From: Saverio Proto Date: Mon, 1 Jul 2013 13:36:26 +0000 (+0200) Subject: ndppd: importing ndppd package in openwrt-routing feed X-Git-Url: http://git.openwrt.org/?p=feed%2Frouting.git;a=commitdiff_plain;h=ae1617f296c64b92d8fee75504efbdcc08c1b003;hp=1fb0f3bcfeaafbd1631e769837fb26e3c86669cf ndppd: importing ndppd package in openwrt-routing feed --- diff --git a/README b/README new file mode 100644 index 0000000..72b7fcc --- /dev/null +++ b/README @@ -0,0 +1,18 @@ +This is an OpenWrt package feed containing community maintained routing packages. + +To use these packages, add the following line to the feeds.conf +in the OpenWrt buildroot: + + src-git routing git://github.com/openwrt-routing/packages.git + +Update the feed: + + ./scripts/feeds update routing + +Activate the package: + + ./scripts/feeds install -a -p routing + +The routing packages should now appear in menuconfig. + + diff --git a/batman-adv/Config.in b/batman-adv/Config.in new file mode 100644 index 0000000..1330525 --- /dev/null +++ b/batman-adv/Config.in @@ -0,0 +1,29 @@ + +config KMOD_BATMAN_ADV_DEBUG_LOG + bool "enable verbose debug logging" + depends on PACKAGE_kmod-batman-adv + default n + +config KMOD_BATMAN_ADV_BLA + bool "enable bridge loop avoidance" + depends on PACKAGE_kmod-batman-adv + default y + +config KMOD_BATMAN_ADV_DAT + bool "enable distributed arp table" + depends on PACKAGE_kmod-batman-adv + default y + +config KMOD_BATMAN_ADV_NC + bool "enable network coding [requires promisc mode support]" + depends on PACKAGE_kmod-batman-adv + default n + +config KMOD_BATMAN_ADV_BATCTL + bool "enable batctl" + depends on PACKAGE_kmod-batman-adv + default y + help + batctl is a more intuitive managment utility for B.A.T.M.A.N.-Advanced. + It is an easier method for configuring batman-adv and + provides some additional tools for debugging as well. diff --git a/batman-adv/Makefile b/batman-adv/Makefile new file mode 100644 index 0000000..1e77051 --- /dev/null +++ b/batman-adv/Makefile @@ -0,0 +1,122 @@ +# +# Copyright (C) 2010 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +# $Id: Makefile 5624 2006-11-23 00:29:07Z nbd $ + +include $(TOPDIR)/rules.mk + +PKG_NAME:=batman-adv + +PKG_VERSION:=2013.2.0 +BATCTL_VERSION:=2013.2.0 +PKG_RELEASE:=3 +PKG_MD5SUM:=9ec18300b96df22f0ed21c9f51e4ccef +BATCTL_MD5SUM:=712f86cdd0f9076503fc48acf37e109e + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=http://downloads.open-mesh.org/batman/releases/batman-adv-$(PKG_VERSION) + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(PKG_NAME)-$(PKG_VERSION) +PKG_BATCTL_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/batctl-$(BATCTL_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/kernel.mk + +define KernelPackage/batman-adv + URL:=http://www.open-mesh.org/ + MAINTAINER:=Marek Lindner + SUBMENU:=Network Support + DEPENDS:=+kmod-lib-crc16 +kmod-crypto-core +kmod-crypto-crc32c +kmod-lib-crc32c +libc + TITLE:=B.A.T.M.A.N. Adv + FILES:=$(PKG_BUILD_DIR)/batman-adv.$(LINUX_KMOD_SUFFIX) + AUTOLOAD:=$(call AutoLoad,50,batman-adv) +endef + +define KernelPackage/batman-adv/description +B.A.T.M.A.N. advanced is a kernel module which allows to +build layer 2 mesh networks. This package contains the +version $(PKG_VERSION) of the kernel module plus its user space +configuration & managerment tool batctl. +endef + +define KernelPackage/batman-adv/config + source "$(SOURCE)/Config.in" +endef + +MAKE_BATMAN_ADV_ARGS += \ + CROSS_COMPILE="$(TARGET_CROSS)" \ + KERNELPATH="$(LINUX_DIR)" \ + ARCH="$(LINUX_KARCH)" \ + PATH="$(TARGET_PATH)" \ + SUBDIRS="$(PKG_BUILD_DIR)" \ + PWD="$(PKG_BUILD_DIR)" \ + LINUX_VERSION="$(LINUX_VERSION)" \ + CONFIG_BATMAN_ADV_DEBUG=$(if $(CONFIG_KMOD_BATMAN_ADV_DEBUG_LOG),y,n) \ + CONFIG_BATMAN_ADV_BLA=$(if $(CONFIG_KMOD_BATMAN_ADV_BLA),y,n) \ + CONFIG_BATMAN_ADV_DAT=$(if $(CONFIG_KMOD_BATMAN_ADV_DAT),y,n) \ + CONFIG_BATMAN_ADV_NC=$(if $(CONFIG_KMOD_BATMAN_ADV_NC),y,n) \ + REVISION="" all + +MAKE_BATCTL_ARGS += \ + CFLAGS="$(TARGET_CFLAGS)" \ + CCFLAGS="$(TARGET_CFLAGS)" \ + OFLAGS="$(TARGET_CFLAGS)" \ + REVISION="" \ + CC="$(TARGET_CC)" \ + NODEBUG=1 \ + UNAME="Linux" \ + DESTDIR="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/true" \ + batctl install + +ifneq ($(DEVELOPER)$(CONFIG_KMOD_BATMAN_ADV_BATCTL),) +define Download/batctl + FILE:=batctl-$(BATCTL_VERSION).tar.gz + URL:=$(PKG_SOURCE_URL) + MD5SUM:=$(BATCTL_MD5SUM) +endef +$(eval $(call Download,batctl)) + +BATCTL_EXTRACT = tar xzf "$(DL_DIR)/batctl-$(BATCTL_VERSION).tar.gz" -C "$(BUILD_DIR)/$(PKG_NAME)" +BATCTL_PATCH = $(call Build/DoPatch,"$(PKG_BATCTL_BUILD_DIR)","$(PATCH_DIR)","*batctl*") +BATCTL_BUILD = $(MAKE) -C $(PKG_BATCTL_BUILD_DIR) $(MAKE_BATCTL_ARGS) +BATCTL_INSTALL = $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/local/sbin/batctl $(1)/usr/sbin/ +endif + +KPATCH ?= $(PATCH) +define Build/DoPatch + @if [ -d "$(2)" ]; then \ + if [ "$$$$(ls $(2) | grep -Ec $(3))" -gt 0 ]; then \ + $(KPATCH) "$(1)" "$(2)" "$(3)"; \ + fi; \ + fi +endef + +define Build/Patch + $(call Build/DoPatch,"$(PKG_BUILD_DIR)","$(PATCH_DIR)","*batman*") + $(BATCTL_EXTRACT) + $(BATCTL_PATCH) +endef + +define Build/Compile + $(MAKE) -C "$(PKG_BUILD_DIR)" $(MAKE_BATMAN_ADV_ARGS) + $(BATCTL_BUILD) +endef + +define Build/Clean + rm -rf $(BUILD_DIR)/$(PKG_NAME)/ +endef + +define KernelPackage/batman-adv/install + $(INSTALL_DIR) $(1)/etc/config $(1)/etc/hotplug.d/net $(1)/etc/hotplug.d/iface $(1)/lib/batman-adv $(1)/usr/sbin $(1)/lib/netifd/proto + $(INSTALL_DATA) ./files/etc/config/batman-adv $(1)/etc/config + $(INSTALL_DATA) ./files/lib/batman-adv/config.sh $(1)/lib/batman-adv + $(INSTALL_BIN) ./files/etc/hotplug.d/net/99-batman-adv $(1)/etc/hotplug.d/net + $(INSTALL_BIN) ./files/lib/netifd/proto/batadv.sh $(1)/lib/netifd/proto + $(BATCTL_INSTALL) +endef + +$(eval $(call KernelPackage,batman-adv)) diff --git a/batman-adv/files/etc/config/batman-adv b/batman-adv/files/etc/config/batman-adv new file mode 100644 index 0000000..79d660d --- /dev/null +++ b/batman-adv/files/etc/config/batman-adv @@ -0,0 +1,20 @@ + +config 'mesh' 'bat0' + option 'aggregated_ogms' + option 'ap_isolation' + option 'bonding' + option 'fragmentation' + option 'gw_bandwidth' + option 'gw_mode' + option 'gw_sel_class' + option 'log_level' + option 'orig_interval' + option 'vis_mode' + option 'bridge_loop_avoidance' + option 'distributed_arp_table' + option 'network_coding' + option 'hop_penalty' + +# yet another batX instance +# config 'mesh' 'bat5' +# option 'interfaces' 'second_mesh' diff --git a/batman-adv/files/etc/hotplug.d/net/99-batman-adv b/batman-adv/files/etc/hotplug.d/net/99-batman-adv new file mode 100644 index 0000000..f0c391f --- /dev/null +++ b/batman-adv/files/etc/hotplug.d/net/99-batman-adv @@ -0,0 +1,12 @@ +#!/bin/sh + +. /lib/batman-adv/config.sh + +bat_load_module +config_load batman-adv + +case "$ACTION" in + add) + [ -d /sys/class/net/$INTERFACE/mesh/ ] && bat_config "$INTERFACE" + ;; +esac diff --git a/batman-adv/files/lib/batman-adv/config.sh b/batman-adv/files/lib/batman-adv/config.sh new file mode 100644 index 0000000..471c1f2 --- /dev/null +++ b/batman-adv/files/lib/batman-adv/config.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +bat_load_module() +{ + [ -d "/sys/module/batman_adv/" ] && return + + . /lib/functions.sh + load_modules /etc/modules.d/*-crc16 /etc/modules.d/*-crypto* /etc/modules.d/*-lib-crc* /etc/modules.d/*-batman-adv* +} + +bat_config() +{ + local mesh="$1" + local aggregated_ogms ap_isolation bonding bridge_loop_avoidance distributed_arp_table fragmentation + local gw_bandwidth gw_mode gw_sel_class hop_penalty network_coding log_level orig_interval vis_mode + + config_get aggregated_ogms "$mesh" aggregated_ogms + config_get ap_isolation "$mesh" ap_isolation + config_get bonding "$mesh" bonding + config_get bridge_loop_avoidance "$mesh" bridge_loop_avoidance + config_get distributed_arp_table "$mesh" distributed_arp_table + config_get fragmentation "$mesh" fragmentation + config_get gw_bandwidth "$mesh" gw_bandwidth + config_get gw_mode "$mesh" gw_mode + config_get gw_sel_class "$mesh" gw_sel_class + config_get hop_penalty "$mesh" hop_penalty + config_get network_coding "$mesh" network_coding + config_get log_level "$mesh" log_level + config_get orig_interval "$mesh" orig_interval + config_get vis_mode "$mesh" vis_mode + + [ ! -f "/sys/class/net/$mesh/mesh/orig_interval" ] && echo "batman-adv mesh $mesh does not exist - check your interface configuration" && return 1 + + [ -n "$aggregate_ogms" ] && echo $aggregate_ogms > /sys/class/net/$mesh/mesh/aggregate_ogms + [ -n "$ap_isolation" ] && echo $ap_isolation > /sys/class/net/$mesh/mesh/ap_isolation + [ -n "$bonding" ] && echo $bonding > /sys/class/net/$mesh/mesh/bonding + [ -n "$bridge_loop_avoidance" ] && echo $bridge_loop_avoidance > /sys/class/net/$mesh/mesh/bridge_loop_avoidance 2>&- + [ -n "$distributed_arp_table" ] && echo $distributed_arp_table > /sys/class/net/$mesh/mesh/distributed_arp_table 2>&- + [ -n "$fragmentation" ] && echo $fragmentation > /sys/class/net/$mesh/mesh/fragmentation + [ -n "$gw_bandwidth" ] && echo $gw_bandwidth > /sys/class/net/$mesh/mesh/gw_bandwidth + [ -n "$gw_mode" ] && echo $gw_mode > /sys/class/net/$mesh/mesh/gw_mode + [ -n "$gw_sel_class" ] && echo $gw_sel_class > /sys/class/net/$mesh/mesh/gw_sel_class + [ -n "$hop_penalty" ] && echo $hop_penalty > /sys/class/net/$mesh/mesh/hop_penalty + [ -n "$network_coding" ] && echo $network_coding > /sys/class/net/$mesh/mesh/network_coding 2>&- + [ -n "$log_level" ] && echo $log_level > /sys/class/net/$mesh/mesh/log_level 2>&- + [ -n "$orig_interval" ] && echo $orig_interval > /sys/class/net/$mesh/mesh/orig_interval + [ -n "$vis_mode" ] && echo $vis_mode > /sys/class/net/$mesh/mesh/vis_mode +} diff --git a/batman-adv/files/lib/netifd/proto/batadv.sh b/batman-adv/files/lib/netifd/proto/batadv.sh new file mode 100644 index 0000000..632a209 --- /dev/null +++ b/batman-adv/files/lib/netifd/proto/batadv.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +. /lib/functions.sh +. ../netifd-proto.sh +init_proto "$@" + +proto_batadv_init_config() { + proto_config_add_string "mesh" +} + +proto_batadv_setup() { + local config="$1" + local iface="$2" + + local mesh + json_get_vars mesh + + echo "$mesh" > "/sys/class/net/$iface/batman_adv/mesh_iface" + proto_init_update "$iface" 1 + proto_send_update "$config" +} + +proto_batadv_teardown() { + local config="$1" + local iface="$2" + + echo "none" > "/sys/class/net/$iface/batman_adv/mesh_iface" || true +} + +add_protocol batadv diff --git a/batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch b/batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch new file mode 100644 index 0000000..525dbac --- /dev/null +++ b/batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch @@ -0,0 +1,74 @@ +From 9b96ecbae7295269aaa0320667f646870de65661 Mon Sep 17 00:00:00 2001 +From: Antonio Quartulli +Date: Wed, 3 Apr 2013 10:14:20 +0200 +Subject: [PATCH 01/10] batman-adv: use the proper header len when checking + the TTVN + +Unicast packet might be of type either UNICAST or +UNICAST4ADDR. +In the two cases the header size is different, but the +mechanism checking the TTVN field was assuming it to be +always of the same type (UNICAST), so failing to access the +inner Ethernet header in case of UNICAST4ADDR. + +Fix this by passing the real header length as argument. + +Signed-off-by: Antonio Quartulli +Signed-off-by: Marek Lindner +--- + routing.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/routing.c b/routing.c +index 2f1f889..b27a4d7 100644 +--- a/routing.c ++++ b/routing.c +@@ -939,7 +939,7 @@ out: + } + + static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, +- struct sk_buff *skb) { ++ struct sk_buff *skb, int hdr_len) { + uint8_t curr_ttvn, old_ttvn; + struct batadv_orig_node *orig_node; + struct ethhdr *ethhdr; +@@ -948,7 +948,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, + int is_old_ttvn; + + /* check if there is enough data before accessing it */ +- if (pskb_may_pull(skb, sizeof(*unicast_packet) + ETH_HLEN) < 0) ++ if (pskb_may_pull(skb, hdr_len + ETH_HLEN) < 0) + return 0; + + /* create a copy of the skb (in case of for re-routing) to modify it. */ +@@ -956,7 +956,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, + return 0; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; +- ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet)); ++ ethhdr = (struct ethhdr *)(skb->data + hdr_len); + + /* check if the destination client was served by this node and it is now + * roaming. In this case, it means that the node has got a ROAM_ADV +@@ -1072,8 +1072,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, + + if (check < 0) + return NET_RX_DROP; +- +- if (!batadv_check_unicast_ttvn(bat_priv, skb)) ++ if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) + return NET_RX_DROP; + + /* packet for me */ +@@ -1117,7 +1116,7 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb, + if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) + return NET_RX_DROP; + +- if (!batadv_check_unicast_ttvn(bat_priv, skb)) ++ if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) + return NET_RX_DROP; + + unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; +-- +1.7.10.4 + diff --git a/batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch b/batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch new file mode 100644 index 0000000..608bd43 --- /dev/null +++ b/batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch @@ -0,0 +1,69 @@ +From 82d1a8ebf19a1b9841ee44ce7b2448114be3e772 Mon Sep 17 00:00:00 2001 +From: Patrick McHardy +Date: Wed, 24 Apr 2013 17:42:56 +0200 +Subject: [PATCH 02/10] net: vlan: add protocol argument to packet tagging + functions + +Add a protocol argument to the VLAN packet tagging functions. In case of HW +tagging, we need that protocol available in the ndo_start_xmit functions, +so it is stored in a new field in the skb. The new field fits into a hole +(on 64 bit) and doesn't increase the sks's size. + +Signed-off-by: Patrick McHardy +Signed-off-by: David S. Miller +[siwu@hrz.tu-chemnitz.de: added compat code] +Signed-off-by: Simon Wunderlich +Signed-off-by: Marek Lindner +--- + bridge_loop_avoidance.c | 2 +- + compat.h | 16 +++++++++++++++- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/bridge_loop_avoidance.c b/bridge_loop_avoidance.c +index 6a4f728..379061c 100644 +--- a/bridge_loop_avoidance.c ++++ b/bridge_loop_avoidance.c +@@ -341,7 +341,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, + } + + if (vid != -1) +- skb = vlan_insert_tag(skb, vid); ++ skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), vid); + + skb_reset_mac_header(skb); + skb->protocol = eth_type_trans(skb, soft_iface); +diff --git a/compat.h b/compat.h +index 0663df5..c1dadac 100644 +--- a/compat.h ++++ b/compat.h +@@ -170,7 +170,7 @@ static const struct { \ + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) + + #define kfree_rcu(ptr, rcu_head) call_rcu(&ptr->rcu_head, batadv_free_rcu_##ptr) +-#define vlan_insert_tag(skb, vid) __vlan_put_tag(skb, vid) ++#define vlan_insert_tag(skb, proto, vid) __vlan_put_tag(skb, vid) + + void batadv_free_rcu_gw_node(struct rcu_head *rcu); + void batadv_free_rcu_neigh_node(struct rcu_head *rcu); +@@ -278,4 +278,18 @@ static int __batadv_interface_set_mac_addr(x, y) + + #endif /* < KERNEL_VERSION(3, 9, 0) */ + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) ++ ++#ifndef vlan_insert_tag ++ ++/* include this header early to let the following define ++ * not mess up the original function prototype. ++ */ ++#include ++#define vlan_insert_tag(skb, proto, vid) vlan_insert_tag(skb, vid) ++ ++#endif /* vlan_insert_tag */ ++ ++#endif /* < KERNEL_VERSION(3, 10, 0) */ ++ + #endif /* _NET_BATMAN_ADV_COMPAT_H_ */ +-- +1.7.10.4 + diff --git a/batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch b/batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch new file mode 100644 index 0000000..f6270fa --- /dev/null +++ b/batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch @@ -0,0 +1,33 @@ +From aa7d19a5b97fe48657e075e8e4d130bd6916551e Mon Sep 17 00:00:00 2001 +From: Marek Lindner +Date: Sat, 27 Apr 2013 16:22:28 +0800 +Subject: [PATCH 03/10] batman-adv: check proto length before accessing proto + string buffer + +batadv_param_set_ra() strips the trailing '\n' from the supplied +string buffer without checking the length of the buffer first. This +patches avoids random memory access and associated potential +crashes. + +Reported-by: Sasha Levin +Signed-off-by: Marek Lindner +--- + main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/main.c b/main.c +index 3e30a0f..9c620cd 100644 +--- a/main.c ++++ b/main.c +@@ -475,7 +475,7 @@ static int batadv_param_set_ra(const char *val, const struct kernel_param *kp) + char *algo_name = (char *)val; + size_t name_len = strlen(algo_name); + +- if (algo_name[name_len - 1] == '\n') ++ if (name_len > 0 && algo_name[name_len - 1] == '\n') + algo_name[name_len - 1] = '\0'; + + bat_algo_ops = batadv_algo_get(algo_name); +-- +1.7.10.4 + diff --git a/batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch b/batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch new file mode 100644 index 0000000..e38596b --- /dev/null +++ b/batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch @@ -0,0 +1,44 @@ +From d22ebef1431aab13099370b89afa4ba55eb95c35 Mon Sep 17 00:00:00 2001 +From: Marek Lindner +Date: Tue, 7 May 2013 19:25:02 +0800 +Subject: [PATCH 04/10] batman-adv: check return value of pskb_trim_rcsum() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reported-by: Sven Eckelmann +Signed-off-by: Marek Lindner +Acked-by: Martin Hundebøll +--- + network-coding.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/network-coding.c b/network-coding.c +index f7c5430..e84629e 100644 +--- a/network-coding.c ++++ b/network-coding.c +@@ -1514,6 +1514,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, + struct ethhdr *ethhdr, ethhdr_tmp; + uint8_t *orig_dest, ttl, ttvn; + unsigned int coding_len; ++ int err; + + /* Save headers temporarily */ + memcpy(&coded_packet_tmp, skb->data, sizeof(coded_packet_tmp)); +@@ -1568,8 +1569,11 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, + coding_len); + + /* Resize decoded skb if decoded with larger packet */ +- if (nc_packet->skb->len > coding_len + h_size) +- pskb_trim_rcsum(skb, coding_len + h_size); ++ if (nc_packet->skb->len > coding_len + h_size) { ++ err = pskb_trim_rcsum(skb, coding_len + h_size); ++ if (err) ++ return NULL; ++ } + + /* Create decoded unicast packet */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; +-- +1.7.10.4 + diff --git a/batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch b/batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch new file mode 100644 index 0000000..e617ad3 --- /dev/null +++ b/batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch @@ -0,0 +1,48 @@ +From d6bd8b36fa1f3d72a6fd5942a6e9bde6ddafcd0d Mon Sep 17 00:00:00 2001 +From: Antonio Quartulli +Date: Thu, 9 May 2013 09:35:45 +0200 +Subject: [PATCH 05/10] batman-adv: make DAT drop ARP requests targeting local + clients + +In the outgoing ARP request snooping routine in DAT, ARP +Request sent by local clients which are supposed to be +replied by other local clients can be silently dropped. + +The destination host will reply by itself through the LAN +and therefore there is no need to involve DAT. + +Reported-by: Carlos Quijano +Signed-off-by: Antonio Quartulli +Tested-by: Carlos Quijano +Signed-off-by: Marek Lindner +--- + distributed-arp-table.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/distributed-arp-table.c b/distributed-arp-table.c +index 8e15d96..2399920 100644 +--- a/distributed-arp-table.c ++++ b/distributed-arp-table.c +@@ -837,6 +837,19 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + if (dat_entry) { ++ /* If the ARP request is destined for a local client the local ++ * client will answer itself. DAT would only generate a ++ * duplicate packet. ++ * ++ * Moreover, if the soft-interface is enslaved into a bridge, an ++ * additional DAT answer may trigger kernel warnings about ++ * a packet coming from the wrong port. ++ */ ++ if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) { ++ ret = true; ++ goto out; ++ } ++ + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + bat_priv->soft_iface, ip_dst, hw_src, + dat_entry->mac_addr, hw_src); +-- +1.7.10.4 + diff --git a/batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch b/batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch new file mode 100644 index 0000000..967fb31 --- /dev/null +++ b/batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch @@ -0,0 +1,57 @@ +From 763f413b9c74ccb25cb066408f49f07e5dd78f9b Mon Sep 17 00:00:00 2001 +From: Antonio Quartulli +Date: Tue, 7 May 2013 01:06:18 +0200 +Subject: [PATCH 06/10] batman-adv: reorder clean up routine in order to avoid + race conditions + +nc_worker accesses the originator table during its periodic +work, but since the originator table is freed before +stopping the worker this leads to a global protection fault. + +Fix this by killing the worker (in nc_free) before freeing +the originator table. + +Moreover tidy up the entire clean up routine by running all +the subcomponents freeing procedures first and then killing +the TT and the originator tables at the end. + +Signed-off-by: Antonio Quartulli +Signed-off-by: Marek Lindner +--- + main.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/main.c b/main.c +index 9c620cd..1240f07 100644 +--- a/main.c ++++ b/main.c +@@ -163,14 +163,22 @@ void batadv_mesh_free(struct net_device *soft_iface) + batadv_vis_quit(bat_priv); + + batadv_gw_node_purge(bat_priv); +- batadv_originator_free(bat_priv); + batadv_nc_free(bat_priv); ++ batadv_dat_free(bat_priv); ++ batadv_bla_free(bat_priv); + ++ /* Free the TT and the originator tables only after having terminated ++ * all the other depending components which may use these structures for ++ * their purposes. ++ */ + batadv_tt_free(bat_priv); + +- batadv_bla_free(bat_priv); +- +- batadv_dat_free(bat_priv); ++ /* Since the originator table clean up routine is accessing the TT ++ * tables as well, it has to be invoked after the TT tables have been ++ * freed and marked as empty. This ensures that no cleanup RCU callbacks ++ * accessing the TT data are scheduled for later execution. ++ */ ++ batadv_originator_free(bat_priv); + + free_percpu(bat_priv->bat_counters); + +-- +1.7.10.4 + diff --git a/batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch b/batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch new file mode 100644 index 0000000..d4f3806 --- /dev/null +++ b/batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch @@ -0,0 +1,47 @@ +From a5d79639a76d7a71116abbc369a246bd1fcbf947 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= +Date: Wed, 17 Apr 2013 21:13:16 +0200 +Subject: [PATCH 07/10] batman-adv: Avoid double freeing of bat_counters +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On errors in batadv_mesh_init(), bat_counters will be freed in both +batadv_mesh_free() and batadv_softif_init_late(). This patch fixes this +by returning earlier from batadv_softif_init_late() in case of errors in +batadv_mesh_init() and by setting bat_counters to NULL after freeing. + +Signed-off-by: Martin Hundebøll +Signed-off-by: Marek Lindner +--- + main.c | 1 + + soft-interface.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/main.c b/main.c +index 1240f07..51aafd6 100644 +--- a/main.c ++++ b/main.c +@@ -181,6 +181,7 @@ void batadv_mesh_free(struct net_device *soft_iface) + batadv_originator_free(bat_priv); + + free_percpu(bat_priv->bat_counters); ++ bat_priv->bat_counters = NULL; + + atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); + } +diff --git a/soft-interface.c b/soft-interface.c +index 6f20d33..819dfb0 100644 +--- a/soft-interface.c ++++ b/soft-interface.c +@@ -505,6 +505,7 @@ unreg_debugfs: + batadv_debugfs_del_meshif(dev); + free_bat_counters: + free_percpu(bat_priv->bat_counters); ++ bat_priv->bat_counters = NULL; + + return ret; + } +-- +1.7.10.4 + diff --git a/batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch b/batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch new file mode 100644 index 0000000..87a2940 --- /dev/null +++ b/batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch @@ -0,0 +1,42 @@ +From 96cd7725540f4dccdd6fbb4fde59243e1cc1ad80 Mon Sep 17 00:00:00 2001 +From: Matthias Schiffer +Date: Tue, 28 May 2013 17:32:32 +0200 +Subject: [PATCH 08/10] batman-adv: wait for rtnl in batadv_store_mesh_iface + instead of failing if it is taken + +The rtnl_lock in batadv_store_mesh_iface has been converted to a rtnl_trylock +some time ago to avoid a possible deadlock between rtnl and s_active on removal +of the sysfs nodes. + +The behaviour introduced by that was quite confusing as it could lead to the +sysfs store to fail, making batman-adv setup scripts unreliable. As recently the +sysfs removal was postponed to a worker not running with the rtnl taken, the +deadlock can't occur any more and it is safe to change the trylock back to a +lock to make the sysfs store reliable again. + +Signed-off-by: Matthias Schiffer +Reviewed-by: Simon Wunderlich +Signed-off-by: Marek Lindner +--- + sysfs.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/sysfs.c b/sysfs.c +index 15a22ef..929e304 100644 +--- a/sysfs.c ++++ b/sysfs.c +@@ -582,10 +582,7 @@ static ssize_t batadv_store_mesh_iface(struct kobject *kobj, + (strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0)) + goto out; + +- if (!rtnl_trylock()) { +- ret = -ERESTARTSYS; +- goto out; +- } ++ rtnl_lock(); + + if (status_tmp == BATADV_IF_NOT_IN_USE) { + batadv_hardif_disable_interface(hard_iface, +-- +1.7.10.4 + diff --git a/batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch b/batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch new file mode 100644 index 0000000..52e1acd --- /dev/null +++ b/batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch @@ -0,0 +1,35 @@ +From 518fba156ed911c6183cf5cb34955a6fdf1b4637 Mon Sep 17 00:00:00 2001 +From: Simon Wunderlich +Date: Fri, 7 Jun 2013 16:52:05 +0200 +Subject: [PATCH 09/10] batman-adv: Don't handle address updates when bla is + disabled + +The bridge loop avoidance has a hook to handle address updates of the +originator. These should not be handled when bridge loop avoidance is +disabled - it might send some bridge loop avoidance packets which should +not appear if bla is disabled. + +Signed-off-by: Simon Wunderlich +Signed-off-by: Marek Lindner +--- + bridge_loop_avoidance.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/bridge_loop_avoidance.c b/bridge_loop_avoidance.c +index 379061c..de27b31 100644 +--- a/bridge_loop_avoidance.c ++++ b/bridge_loop_avoidance.c +@@ -1067,6 +1067,10 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv, + group = htons(crc16(0, primary_if->net_dev->dev_addr, ETH_ALEN)); + bat_priv->bla.claim_dest.group = group; + ++ /* purge everything when bridge loop avoidance is turned off */ ++ if (!atomic_read(&bat_priv->bridge_loop_avoidance)) ++ oldif = NULL; ++ + if (!oldif) { + batadv_bla_purge_claims(bat_priv, NULL, 1); + batadv_bla_purge_backbone_gw(bat_priv, 1); +-- +1.7.10.4 + diff --git a/batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch b/batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch new file mode 100644 index 0000000..843ee89 --- /dev/null +++ b/batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch @@ -0,0 +1,226 @@ +From 3d999e5116f44b47c742aa16d6382721c360a6d0 Mon Sep 17 00:00:00 2001 +From: Simon Wunderlich +Date: Thu, 23 May 2013 13:07:42 +0200 +Subject: [PATCH 10/10] batman-adv: forward late OGMs from best next hop + +When a packet is received from another node first and later from the +best next hop, this packet is dropped. However the first OGM was sent +with the BATADV_NOT_BEST_NEXT_HOP flag and thus dropped by neighbors. +The late OGM from the best neighbor is then dropped because it is a +duplicate. + +If this situation happens constantly, a node might end up not forwarding +the "valid" OGMs anymore, and nodes behind will starve from not getting +valid OGMs. + +Fix this by refining the duplicate checking behaviour: The actions +should depend on whether it was a duplicate for a neighbor only or for +the originator. OGMs which are not duplicates for a specific neighbor +will now be considered in batadv_iv_ogm_forward(), but only actually +forwarded for the best next hop. Therefore, late OGMs from the best +next hop are forwarded now and not dropped as duplicates anymore. + +Signed-off-by: Simon Wunderlich +Signed-off-by: Marek Lindner +--- + bat_iv_ogm.c | 86 +++++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 55 insertions(+), 31 deletions(-) + +diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c +index 071f288..f680ee1 100644 +--- a/bat_iv_ogm.c ++++ b/bat_iv_ogm.c +@@ -29,6 +29,21 @@ + #include "bat_algo.h" + #include "network-coding.h" + ++/** ++ * batadv_dup_status - duplicate status ++ * @BATADV_NO_DUP: the packet is a duplicate ++ * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for the ++ * neighbor) ++ * @BATADV_NEIGH_DUP: OGM is a duplicate for the neighbor ++ * @BATADV_PROTECTED: originator is currently protected (after reboot) ++ */ ++enum batadv_dup_status { ++ BATADV_NO_DUP = 0, ++ BATADV_ORIG_DUP, ++ BATADV_NEIGH_DUP, ++ BATADV_PROTECTED, ++}; ++ + static struct batadv_neigh_node * + batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, + const uint8_t *neigh_addr, +@@ -650,7 +665,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, + const struct batadv_ogm_packet *batadv_ogm_packet, + struct batadv_hard_iface *if_incoming, + const unsigned char *tt_buff, +- int is_duplicate) ++ enum batadv_dup_status dup_status) + { + struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + struct batadv_neigh_node *router = NULL; +@@ -676,7 +691,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, + continue; + } + +- if (is_duplicate) ++ if (dup_status != BATADV_NO_DUP) + continue; + + spin_lock_bh(&tmp_neigh_node->lq_update_lock); +@@ -718,7 +733,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, + neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv); + spin_unlock_bh(&neigh_node->lq_update_lock); + +- if (!is_duplicate) { ++ if (dup_status == BATADV_NO_DUP) { + orig_node->last_ttl = batadv_ogm_packet->header.ttl; + neigh_node->last_ttl = batadv_ogm_packet->header.ttl; + } +@@ -902,15 +917,16 @@ out: + return ret; + } + +-/* processes a batman packet for all interfaces, adjusts the sequence number and +- * finds out whether it is a duplicate. +- * returns: +- * 1 the packet is a duplicate +- * 0 the packet has not yet been received +- * -1 the packet is old and has been received while the seqno window +- * was protected. Caller should drop it. ++/** ++ * batadv_iv_ogm_update_seqnos - process a batman packet for all interfaces, ++ * adjust the sequence number and find out whether it is a duplicate ++ * @ethhdr: ethernet header of the packet ++ * @batadv_ogm_packet: OGM packet to be considered ++ * @if_incoming: interface on which the OGM packet was received ++ * ++ * Returns duplicate status as enum batadv_dup_status + */ +-static int ++static enum batadv_dup_status + batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, + const struct batadv_ogm_packet *batadv_ogm_packet, + const struct batadv_hard_iface *if_incoming) +@@ -918,17 +934,18 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, + struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct batadv_orig_node *orig_node; + struct batadv_neigh_node *tmp_neigh_node; +- int is_duplicate = 0; ++ int is_dup; + int32_t seq_diff; + int need_update = 0; +- int set_mark, ret = -1; ++ int set_mark; ++ enum batadv_dup_status ret = BATADV_NO_DUP; + uint32_t seqno = ntohl(batadv_ogm_packet->seqno); + uint8_t *neigh_addr; + uint8_t packet_count; + + orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig); + if (!orig_node) +- return 0; ++ return BATADV_NO_DUP; + + spin_lock_bh(&orig_node->ogm_cnt_lock); + seq_diff = seqno - orig_node->last_real_seqno; +@@ -936,22 +953,29 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, + /* signalize caller that the packet is to be dropped. */ + if (!hlist_empty(&orig_node->neigh_list) && + batadv_window_protected(bat_priv, seq_diff, +- &orig_node->batman_seqno_reset)) ++ &orig_node->batman_seqno_reset)) { ++ ret = BATADV_PROTECTED; + goto out; ++ } + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, + &orig_node->neigh_list, list) { +- is_duplicate |= batadv_test_bit(tmp_neigh_node->real_bits, +- orig_node->last_real_seqno, +- seqno); +- + neigh_addr = tmp_neigh_node->addr; ++ is_dup = batadv_test_bit(tmp_neigh_node->real_bits, ++ orig_node->last_real_seqno, ++ seqno); ++ + if (batadv_compare_eth(neigh_addr, ethhdr->h_source) && +- tmp_neigh_node->if_incoming == if_incoming) ++ tmp_neigh_node->if_incoming == if_incoming) { + set_mark = 1; +- else ++ if (is_dup) ++ ret = BATADV_NEIGH_DUP; ++ } else { + set_mark = 0; ++ if (is_dup && (ret != BATADV_NEIGH_DUP)) ++ ret = BATADV_ORIG_DUP; ++ } + + /* if the window moved, set the update flag. */ + need_update |= batadv_bit_get_packet(bat_priv, +@@ -971,8 +995,6 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, + orig_node->last_real_seqno = seqno; + } + +- ret = is_duplicate; +- + out: + spin_unlock_bh(&orig_node->ogm_cnt_lock); + batadv_orig_node_free_ref(orig_node); +@@ -994,7 +1016,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, + int is_broadcast = 0, is_bidirect; + bool is_single_hop_neigh = false; + bool is_from_best_next_hop = false; +- int is_duplicate, sameseq, simlar_ttl; ++ int sameseq, similar_ttl; ++ enum batadv_dup_status dup_status; + uint32_t if_incoming_seqno; + uint8_t *prev_sender; + +@@ -1138,10 +1161,10 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, + if (!orig_node) + return; + +- is_duplicate = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, +- if_incoming); ++ dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, ++ if_incoming); + +- if (is_duplicate == -1) { ++ if (dup_status == BATADV_PROTECTED) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: packet within seqno protection time (sender: %pM)\n", + ethhdr->h_source); +@@ -1211,11 +1234,12 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, + * seqno and similar ttl as the non-duplicate + */ + sameseq = orig_node->last_real_seqno == ntohl(batadv_ogm_packet->seqno); +- simlar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl; +- if (is_bidirect && (!is_duplicate || (sameseq && simlar_ttl))) ++ similar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl; ++ if (is_bidirect && ((dup_status == BATADV_NO_DUP) || ++ (sameseq && similar_ttl))) + batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr, + batadv_ogm_packet, if_incoming, +- tt_buff, is_duplicate); ++ tt_buff, dup_status); + + /* is single hop (direct) neighbor */ + if (is_single_hop_neigh) { +@@ -1236,7 +1260,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, + goto out_neigh; + } + +- if (is_duplicate) { ++ if (dup_status == BATADV_NEIGH_DUP) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: duplicate packet received\n"); + goto out_neigh; +-- +1.7.10.4 + diff --git a/batmand/Makefile b/batmand/Makefile new file mode 100644 index 0000000..4026496 --- /dev/null +++ b/batmand/Makefile @@ -0,0 +1,152 @@ +# +# Copyright (C) 2008-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:=batmand +PKG_REV:=1439 +PKG_VERSION:=r$(PKG_REV) +PKG_RELEASE:=2 +PKG_EXTRA_CFLAGS=-DDEBUG_MALLOC -DMEMORY_USAGE -DPROFILE_DATA -DREVISION_VERSION=\"\ rv$(PKG_REV)\" + +PKG_SOURCE_PROTO:=svn +PKG_SOURCE_VERSION:=$(PKG_REV) +PKG_SOURCE_SUBDIR:=$(if $(PKG_BRANCH),$(PKG_BRANCH),$(PKG_NAME))-$(PKG_VERSION) +PKG_SOURCE_URL:=http://downloads.open-mesh.org/svn/batman/trunk/ +PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz +PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_SOURCE_SUBDIR) + +PKG_KMOD_BUILD_DIR:=$(PKG_BUILD_DIR)/batman/linux/modules + +include $(INCLUDE_DIR)/package.mk + +define Package/batmand/Default + URL:=http://www.open-mesh.org/ + MAINTAINER:=Marek Lindner +endef + +define Package/batmand +$(call Package/batmand/Default) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + DEPENDS:=+libpthread +kmod-tun + TITLE:=B.A.T.M.A.N. layer 3 routing daemon +endef + +define Package/batmand/description +B.A.T.M.A.N. layer 3 routing daemon +endef + +define Package/vis +$(call Package/batmand/Default) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + DEPENDS:=+libpthread + TITLE:=visualization server for B.A.T.M.A.N. layer 3 +endef + +define Package/vis/description +visualization server for B.A.T.M.A.N. layer 3 +endef + +define KernelPackage/batgat +$(call Package/batmand/Default) + SUBMENU:=Network Support + DEPENDS:=+batmand @BROKEN + TITLE:=B.A.T.M.A.N. gateway module + FILES:=$(PKG_KMOD_BUILD_DIR)/batgat.$(LINUX_KMOD_SUFFIX) + AUTOLOAD:=$(call AutoLoad,50,batgat) +endef + + +define KernelPackage/batgat/description +Kernel gateway module for B.A.T.M.A.N. for better tunnel performance +endef + +MAKE_BATMAND_ARGS += \ + EXTRA_CFLAGS='$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)' \ + CCFLAGS="$(TARGET_CFLAGS)" \ + OFLAGS="$(TARGET_CFLAGS)" \ + REVISION="$(PKG_REV)" \ + CC="$(TARGET_CC)" \ + NODEBUG=1 \ + UNAME="Linux" \ + INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/true" \ + batmand install + +MAKE_VIS_ARGS += \ + EXTRA_CFLAGS='$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)' \ + CCFLAGS="$(TARGET_CFLAGS)" \ + OFLAGS="$(TARGET_CFLAGS)" \ + REVISION="$(PKG_REV)" \ + CC="$(TARGET_CC)" \ + NODEBUG=1 \ + UNAME="Linux" \ + INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/true" \ + vis install + +MAKE_BATGAT_ARGS += \ + CROSS_COMPILE="$(TARGET_CROSS)" \ + ARCH="$(LINUX_KARCH)" \ + PATH="$(TARGET_PATH)" \ + SUBDIRS="$(PKG_KMOD_BUILD_DIR)" \ + LINUX_VERSION="$(LINUX_VERSION)" \ + REVISION="$(PKG_REV)" modules + + +define Build/Configure +endef + +ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_batmand),) + BUILD_BATMAND := $(MAKE) -C $(PKG_BUILD_DIR)/batman $(MAKE_BATMAND_ARGS) +endif + +ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_vis),) + BUILD_VIS := $(MAKE) -C $(PKG_BUILD_DIR)/vis $(MAKE_VIS_ARGS) +endif + +ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_kmod-batgat),) + BUILD_BATGAT := $(MAKE) -C "$(LINUX_DIR)" $(MAKE_BATGAT_ARGS) +endif + +define Build/Compile + $(BUILD_BATMAND) + $(BUILD_VIS) + cp $(PKG_KMOD_BUILD_DIR)/Makefile.kbuild $(PKG_KMOD_BUILD_DIR)/Makefile + $(BUILD_BATGAT) +endef + +define Package/batmand/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/batmand $(1)/usr/sbin/ + $(INSTALL_BIN) ./files/etc/init.d/batmand $(1)/etc/init.d + $(INSTALL_DATA) ./files/etc/config/batmand $(1)/etc/config +endef + +define Package/batmand/conffiles +/etc/config/batmand +endef + +define Package/vis/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/vis $(1)/usr/sbin/ + $(INSTALL_BIN) ./files/etc/init.d/vis $(1)/etc/init.d + $(INSTALL_DATA) ./files/etc/config/vis $(1)/etc/config +endef + +define Package/vis/conffiles +/etc/config/vis +endef + +$(eval $(call BuildPackage,batmand)) +$(eval $(call BuildPackage,vis)) +$(eval $(call KernelPackage,batgat)) diff --git a/batmand/files/etc/config/batmand b/batmand/files/etc/config/batmand new file mode 100644 index 0000000..6d1f3be --- /dev/null +++ b/batmand/files/etc/config/batmand @@ -0,0 +1,12 @@ +config batmand general + option interface ath0 + option hna + option gateway_class + option originator_interval + option preferred_gateway + option routing_class + option visualisation_srv + option policy_routing_script + option disable_client_nat + option disable_aggregation + diff --git a/batmand/files/etc/config/vis b/batmand/files/etc/config/vis new file mode 100644 index 0000000..6d30023 --- /dev/null +++ b/batmand/files/etc/config/vis @@ -0,0 +1,3 @@ +config vis general + option interface ath0 + diff --git a/batmand/files/etc/init.d/batmand b/batmand/files/etc/init.d/batmand new file mode 100644 index 0000000..403e9f3 --- /dev/null +++ b/batmand/files/etc/init.d/batmand @@ -0,0 +1,66 @@ +#!/bin/sh /etc/rc.common +START=90 + +start () { + interface=$(uci get batmand.general.interface) + if [ "$interface" = "" ]; then + echo $1 Error, you must specify at least a network interface + exit + fi + hnas=$(uci get batmand.general.hna) + gateway_class=$(uci get batmand.general.gateway_class) + originator_interval=$(uci get batmand.general.originator_interval) + preferred_gateway=$(uci get batmand.general.preferred_gateway) + routing_class=$(uci get batmand.general.routing_class) + visualisation_srv=$(uci get batmand.general.visualisation_srv) + policy_routing_script=$(uci get batmand.general.policy_routing_script) + disable_client_nat=$(uci get batmand.general.disable_client_nat) + disable_aggregation=$(uci get batmand.general.disable_aggregation) + batman_args="" + + for hna in $hnas + do + batman_args=${batman_args}'-a '$hna' ' + done + + if [ $gateway_class ]; then + batman_args=${batman_args}'-g '$gateway_class' ' + fi + + if [ $originator_interval ]; then + batman_args=${batman_args}'-o '$originator_interval' ' + fi + + if [ $preferred_gateway ]; then + batman_args=${batman_args}'-p '$preferred_gateway' ' + fi + + if [ $routing_class ]; then + batman_args=${batman_args}'-r '$routing_class' ' + fi + + if [ $visualisation_srv ]; then + batman_args=${batman_args}'-s '$visualisation_srv' ' + fi + + if [ $policy_routing_script ]; then + batman_args=${batman_args}'--policy-routing-script '$policy_routing_script' ' + fi + + if [ $disable_client_nat ]; then + batman_args=${batman_args}'--disable-client-nat ' + fi + + if [ $disable_aggregation ]; then + batman_args=${batman_args}'--disable-aggregation ' + fi + + batman_args=${batman_args}$interface + batmand $batman_args >/dev/null 2>&1 +} + +stop () { + killall batmand +} + + diff --git a/batmand/files/etc/init.d/vis b/batmand/files/etc/init.d/vis new file mode 100644 index 0000000..ef18eb7 --- /dev/null +++ b/batmand/files/etc/init.d/vis @@ -0,0 +1,19 @@ +#!/bin/sh /etc/rc.common +START=90 + +start () { + interface=$(uci get vis.general.interface) + if [ "$interface" = "" ]; then + echo $1 Error, you must specify at least a network interface + exit + fi + vis_args=$interface + + vis $vis_args >/dev/null 2>&1 +} + +stop () { + killall vis +} + + diff --git a/batmand/patches/100-2.6.36.patch b/batmand/patches/100-2.6.36.patch new file mode 100644 index 0000000..566c23b --- /dev/null +++ b/batmand/patches/100-2.6.36.patch @@ -0,0 +1,47 @@ +--- + batman/linux/modules/gateway.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +--- batmand-r1439.orig/batman/linux/modules/gateway.c ++++ batmand-r1439/batman/linux/modules/gateway.c +@@ -29,6 +29,7 @@ static struct class *batman_class; + static int batgat_open(struct inode *inode, struct file *filp); + static int batgat_release(struct inode *inode, struct file *file); + static int batgat_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ); ++static long batgat_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg ); + + + static void udp_data_ready(struct sock *sk, int len); +@@ -53,7 +54,11 @@ static int proc_clients_read(char *buf, + static struct file_operations fops = { + .open = batgat_open, + .release = batgat_release, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++ .unlocked_ioctl = batgat_ioctl_unlocked, ++#else + .ioctl = batgat_ioctl, ++#endif + }; + + +@@ -166,6 +171,20 @@ static int batgat_release(struct inode * + } + + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++#include ++static long batgat_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg ) ++{ ++ int ret; ++ ++ lock_kernel(); ++ ret = batgat_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); ++ unlock_kernel(); ++ ++ return ret; ++} ++#endif ++ + static int batgat_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ) + { + uint8_t tmp_ip[4]; diff --git a/bmx6/Makefile b/bmx6/Makefile new file mode 100644 index 0000000..8786a3f --- /dev/null +++ b/bmx6/Makefile @@ -0,0 +1,148 @@ +# Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net +# +# 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. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# Contibutors: +# Axel Neumann, Simó Albert i Beltran, Pau Escrich +# + + +include $(TOPDIR)/rules.mk + +PKG_NAME:=bmx6 + +PKG_SOURCE_PROTO:=git + +PKG_SOURCE_URL:=git://bmx6.net/bmx6.git +#PKG_SOURCE_URL:=git://github.com/axn/bmx6.git + +PKG_REV:=60adcbd4655ccf36a407bae58d66914e33f05a41 +PKG_VERSION:=r2013060803 +PKG_RELEASE:=4 + +PKG_SOURCE_VERSION:=$(PKG_REV) +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) + +include $(INCLUDE_DIR)/package.mk + + +TARGET_CFLAGS += $(FPIC) + +MAKE_ARGS += \ + EXTRA_CFLAGS="$(TARGET_CFLAGS) -I. -I$(STAGING_DIR)/usr/include -DNO_DEBUG_ALL -DNO_DEBUG_DUMP" \ + EXTRA_LDFLAGS="-L$(STAGING_DIR)/usr/lib " \ + REVISION_VERSION="$(PKG_REV)" \ + CC="$(TARGET_CC)" \ + INSTALL_DIR="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/false" \ + build_all + +define Package/bmx6/Default + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + TITLE:=BMX6 layer 3 routing daemon + URL:=http://bmx6.net/ + MAINTAINER:=Axel Neumann + DEPENDS:=+kmod-ip6-tunnel +kmod-iptunnel6 +kmod-tun +endef + +define Package/bmx6/description +BMX6 layer 3 routing daemon supporting IPv4, IPv6, and IPv4 over IPv6 +endef + +define Package/bmx6 + $(call Package/bmx6/Default) + MENU:=1 +endef + +define Package/bmx6-uci-config + $(call Package/bmx6/Default) + DEPENDS:=bmx6 +libuci + TITLE:=configuration plugin based on uci (recommended!) +endef + + +define Package/bmx6-json + $(call Package/bmx6/Default) + DEPENDS:=bmx6 +libjson + TITLE:=json plugin based on jsonc +endef + +define Package/bmx6-sms + $(call Package/bmx6/Default) + DEPENDS:=bmx6 + TITLE:=sms plugin +endef + +define Package/bmx6-quagga + $(call Package/bmx6/Default) + DEPENDS:=bmx6 +qmp-quagga @BROKEN + TITLE:=bmx6 quagga plugin to redistribute/export routes (needs manet/bmx6 patched quagga 0.99.21) +endef + +define Build/Configure + mkdir -p $(PKG_INSTALL_DIR) +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_ARGS) +endef + + +define Package/bmx6/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_BUILD_DIR)/bmx6 $(1)/usr/sbin/bmx6 +endef + + +define Package/bmx6-uci-config/conffiles +/etc/config/bmx6 +endef + + +define Package/bmx6-uci-config/install + $(INSTALL_DIR) $(1)/usr/lib $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_uci_config/bmx6_config.so $(1)/usr/lib/bmx6_config.so + $(INSTALL_BIN) ./files/etc/init.d/bmx6 $(1)/etc/init.d/bmx6 + $(INSTALL_DATA) ./files/etc/config/bmx6 $(1)/etc/config/bmx6 +endef + +define Package/bmx6-json/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_json/bmx6_json.so $(1)/usr/lib/bmx6_json.so +endef + +define Package/bmx6-sms/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_sms/bmx6_sms.so $(1)/usr/lib/bmx6_sms.so +endef + +define Package/bmx6-quagga/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_quagga/bmx6_quagga.so $(1)/usr/lib/bmx6_quagga.so +endef + +$(eval $(call BuildPackage,bmx6)) +$(eval $(call BuildPackage,bmx6-uci-config)) +$(eval $(call BuildPackage,bmx6-json)) +$(eval $(call BuildPackage,bmx6-sms)) +$(eval $(call BuildPackage,bmx6-quagga)) + diff --git a/bmx6/files/etc/config/bmx6 b/bmx6/files/etc/config/bmx6 new file mode 100644 index 0000000..884fd12 --- /dev/null +++ b/bmx6/files/etc/config/bmx6 @@ -0,0 +1,82 @@ + +# for more information: +# http://bmx6.net/projects/bmx6/wiki +# options execute: bmx6 --help + +config 'bmx6' 'general' +# option 'runtimeDir' '/var/run/bmx6' +# option 'tun4Address' '10.202.0.116/32' +# option 'tun4Address' '10.254.10.0/32' +# option 'tun6Address' '2012:0:0:1000::1/64' + +#config 'ipVersion' 'ipVersion' +# option 'ipVersion' '6' # default is 4 +# option 'throwRules' '0' + + +#config 'plugin' +# option 'plugin' 'bmx6_config.so' + + + +#config 'plugin' +# option 'plugin' 'bmx6_json.so' + + + +#config 'plugin' +# option 'plugin' 'bmx6_sms.so' + + +config 'dev' 'mesh_1' + option 'dev' 'eth0.12' + +config 'dev' 'mesh_2' + option 'dev' 'ath0.12' + + + +#config 'hna' 'my_global_prefix' +# option 'hna' '2012:0:0:74:0:0:0:0/64' + + +#config 'tunOut' +# option 'tunOut' 'ip6' +# option 'network' '2012::/16' +# option 'exportDistance' '0' + +#config 'tunOut' +# option 'tunOut' 'ip4' +# option 'network' '10.254.0.0/16' +# option 'exportDistance' '0' # requires quagga plugin ! +# option 'minPrefixLen' '27' + + + +#config 'plugin' +# option 'plugin' 'bmx6_quagga.so' + + + +#config 'redistribute' +# option 'redistribute' 'ospf6' +# option 'network' '10.0.0.0/8' +# option 'minPrefixLen' '10' +# option 'bandwidth' '10000000' +# option 'ospf6' '1' +# option 'aggregatePrefixLen' '16' + +#config 'redistribute' +# option 'redistribute' 'bgp' +# option 'network' '0.0.0.0/0' +# option 'minPrefixLen' '0' +# option 'maxPrefixLen' '24' +# option 'bandwidth' '10000000' +# option 'bgp' '1' +# option 'aggregatePrefixLen' '8' + + + + + + diff --git a/bmx6/files/etc/init.d/bmx6 b/bmx6/files/etc/init.d/bmx6 new file mode 100755 index 0000000..4926e2b --- /dev/null +++ b/bmx6/files/etc/init.d/bmx6 @@ -0,0 +1,40 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net +# +# 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. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". + +START=91 + +BIN=/usr/sbin/bmx6 +CONF=/etc/config/bmx6 +PID=/var/run/bmx6/pid + + +start() { + cd /root/ + ulimit -c 20000 + $BIN -f $CONF -d0 > /dev/null & +} + +stop() { + start-stop-daemon -p $PID -K +} + +restart() { + stop; sleep 3; start +} diff --git a/bmxd/Makefile b/bmxd/Makefile new file mode 100644 index 0000000..5dfd7f9 --- /dev/null +++ b/bmxd/Makefile @@ -0,0 +1,75 @@ +# +# Copyright (C) 2008 Freifunk Leipzig +# 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:=bmxd + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=git://github.com/axn/bmxd.git +PKG_REV:=9c1d12b554dccd2efde249f5e44a7d4de59ce1a8 +PKG_VERSION:=r2012011001 +#PKG_RELEASE:=1 +PKG_SOURCE_VERSION:=$(PKG_REV) +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) + +PKG_EXTRA_CFLAGS:=-DNODEBUGALL + + +include $(INCLUDE_DIR)/package.mk + +define Package/bmxd/Default + URL:=http://www.bmx6.net/ + MAINTAINER:=Axel Neumann +endef + +define Package/bmxd +$(call Package/bmxd/Default) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + DEPENDS:=+kmod-tun + TITLE:=B.a.t.M.a.n. eXperimental (BMX) layer 3 routing daemon +endef + +define Package/bmxd/conffiles +/etc/config/bmxd +endef + + +define Package/bmxd/description +B.a.t.M.a.n. eXperimental (BMX) layer 3 routing daemon +endef + +MAKE_ARGS += \ + EXTRA_CFLAGS="$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)" \ + CCFLAGS="$(TARGET_CFLAGS)" \ + OFLAGS="$(TARGET_CFLAGS)" \ + REVISION="$(PKG_REV)" \ + CC="$(TARGET_CC)" \ + NODEBUG=1 \ + UNAME="Linux" \ + INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/true" \ + bmxd install + +define Build/Compile + mkdir -p $(PKG_INSTALL_DIR)/usr/sbin + $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_ARGS) +endef + +define Package/bmxd/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bmxd $(1)/usr/sbin/bmxd + $(INSTALL_BIN) ./files/etc/init.d/bmxd $(1)/etc/init.d + $(INSTALL_DATA) ./files/etc/config/bmxd $(1)/etc/config +endef + +$(eval $(call BuildPackage,bmxd)) diff --git a/bmxd/files/etc/config/bmxd b/bmxd/files/etc/config/bmxd new file mode 100644 index 0000000..c8619e9 --- /dev/null +++ b/bmxd/files/etc/config/bmxd @@ -0,0 +1,9 @@ +config bmxd general + option interface 'ath0' +# option announce +# option gateway_class +# option originator_interval +# option preferred_gateway +# option routing_class +# option visualisation_srv +# option misc 'base-port=14305' diff --git a/bmxd/files/etc/init.d/bmxd b/bmxd/files/etc/init.d/bmxd new file mode 100644 index 0000000..d23b4c8 --- /dev/null +++ b/bmxd/files/etc/init.d/bmxd @@ -0,0 +1,51 @@ +#!/bin/sh /etc/rc.common +START=91 +. /lib/config/uci.sh +uci_load bmxd +start () { + interface="$(uci get bmxd.general.interface)" + if [ "$interface" = "" ]; then + echo $1 Error, you must specify at least a network interface + exit + fi + announce=$(uci get bmxd.general.announce) + gateway_class=$(uci get bmxd.general.gateway_class) + originator_interval=$(uci get bmxd.general.originator_interval) + preferred_gateway=$(uci get bmxd.general.preferred_gateway) + routing_class=$(uci get bmxd.general.routing_class) + visualisation_srv=$(uci get bmxd.general.visualisation_srv) + misc="$(uci get bmxd.general.misc)" + bmx_args="" + + if [ $announce ]; then + bmx_args=${bmx_args}'-a '$announce' ' + fi + + if [ $gateway_class ]; then + bmx_args=${bmx_args}'-g '$gateway_class' ' + fi + + if [ $originator_interval ]; then + bmx_args=${bmx_args}'-o '$originator_interval' ' + fi + + if [ $preferred_gateway ]; then + bmx_args=${bmx_args}'-p '$preferred_gateway' ' + fi + + if [ $routing_class ]; then + bmx_args=${bmx_args}'-r '$routing_class' ' + fi + + if [ $visualisation_srv ]; then + bmx_args=${bmx_args}'-s '$visualisation_srv' ' + fi + + + bmx_args="$misc ${bmx_args} $interface" + bmxd $bmx_args >/dev/null 2>&1 +} + +stop () { + killall bmxd +} diff --git a/luci-app-bmx6/COPYING b/luci-app-bmx6/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/luci-app-bmx6/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/luci-app-bmx6/Makefile b/luci-app-bmx6/Makefile new file mode 100644 index 0000000..4e8ce1b --- /dev/null +++ b/luci-app-bmx6/Makefile @@ -0,0 +1,61 @@ +# Copyright (C) 2011 Pau Escrich +# +# 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. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-bmx6 +PKG_RELEASE:=2 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-bmx6 + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:= bmx6 configuration, status and visualization module + DEPENDS:=+luci-lib-json +luci-mod-admin-core +luci-lib-httpclient +bmx6 + MAINTAINER:= Pau Escrich +endef + +define Package/luci-app-bmx6/description + bmx6 web module for LuCi web interface +endef + +define Package/luci-app-bmx6/conffiles + /etc/config/luci-bmx6 +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/luci-app-bmx6/install + $(CP) ./files/* $(1)/ + chmod 755 $(1)/www/cgi-bin/bmx6-info +endef + +$(eval $(call BuildPackage,luci-app-bmx6)) + diff --git a/luci-app-bmx6/files/etc/config/luci-bmx6 b/luci-app-bmx6/files/etc/config/luci-bmx6 new file mode 100644 index 0000000..df1e715 --- /dev/null +++ b/luci-app-bmx6/files/etc/config/luci-bmx6 @@ -0,0 +1,7 @@ +config 'bmx6' 'luci' + option ignore '0' + option place 'admin network BMX6' + #option place 'qmp Mesh' + option position '3' + #option json 'http://127.0.0.1/cgi-bin/bmx6-info?' + option json 'exec:/www/cgi-bin/bmx6-info -s' diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua b/luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua new file mode 100644 index 0000000..cb5642e --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua @@ -0,0 +1,329 @@ +--[[ + Copyright (C) 2011 Pau Escrich + Contributors Jo-Philipp Wich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local bmx6json = require("luci.model.bmx6json") + +module("luci.controller.bmx6", package.seeall) + +function index() + local place = {} + local ucim = require "luci.model.uci" + local uci = ucim.cursor() + + -- checking if ignore is on + if uci:get("luci-bmx6","luci","ignore") == "1" then + return nil + end + + -- getting value from uci database + local uci_place = uci:get("luci-bmx6","luci","place") + + -- default values + if uci_place == nil then + place = {"bmx6"} + else + local util = require "luci.util" + place = util.split(uci_place," ") + end + + -- getting position of menu + local uci_position = uci:get("luci-bmx6","luci","position") + + --------------------------- + -- Starting with the pages + --------------------------- + + --- status (default) + entry(place,call("action_nodes_j"),place[#place],tonumber(uci_position)) + + table.insert(place,"Status") + entry(place,call("action_status_j"),"Status",0) + table.remove(place) + + -- not visible + table.insert(place,"nodes_nojs") + entry(place, call("action_nodes"), nil) + table.remove(place) + + --- nodes + table.insert(place,"Nodes") + entry(place,call("action_nodes_j"),"Nodes",1) + table.remove(place) + + --- links + table.insert(place,"Links") + entry(place,call("action_links"),"Links",2).leaf = true + table.remove(place) + + -- Tunnels + table.insert(place,"Tunnels") + entry(place,call("action_tunnels_j"), "Tunnels", 3).leaf = true + table.remove(place) + + -- Gateways (deprecated) + --table.insert(place,"Gateways") + --entry(place,call("action_gateways_j"),"Gateways").leaf = true + --table.remove(place) + + --- Chat + table.insert(place,"Chat") + entry(place,call("action_chat"),"Chat",5) + table.remove(place) + + --- Graph + table.insert(place,"Graph") + entry(place, template("bmx6/graph"), "Graph",4) + table.remove(place) + + --- Topology (hidden) + table.insert(place,"topology") + entry(place, call("action_topology"), nil) + table.remove(place) + + --- configuration (CBI) + table.insert(place,"Configuration") + entry(place, cbi("bmx6/main"), "Configuration",6).dependent=false + + table.insert(place,"General") + entry(place, cbi("bmx6/main"), "General",1) + table.remove(place) + + table.insert(place,"Advanced") + entry(place, cbi("bmx6/advanced"), "Advanced",5) + table.remove(place) + + table.insert(place,"Interfaces") + entry(place, cbi("bmx6/interfaces"), "Interfaces",2) + table.remove(place) + + table.insert(place,"Tunnels") + entry(place, cbi("bmx6/tunnels"), "Tunnels",3) + table.remove(place) + + table.insert(place,"Plugins") + entry(place, cbi("bmx6/plugins"), "Plugins",6) + table.remove(place) + + table.insert(place,"HNAv6") + entry(place, cbi("bmx6/hna"), "HNAv6",4) + table.remove(place) + + table.remove(place) + +end + +function action_status() + local status = bmx6json.get("status").status or nil + local interfaces = bmx6json.get("interfaces").interfaces or nil + + if status == nil or interfaces == nil then + luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"}) + else + luci.template.render("bmx6/status", {status=status,interfaces=interfaces}) + end +end + +function action_status_j() + luci.template.render("bmx6/status_j", {}) +end + + +function action_nodes() + local orig_list = bmx6json.get("originators").originators or nil + + if orig_list == nil then + luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"}) + return nil + end + + local originators = {} + local desc = nil + local orig = nil + local name = "" + local ipv4 = "" + + for _,o in ipairs(orig_list) do + orig = bmx6json.get("originators/"..o.name) or {} + desc = bmx6json.get("descriptions/"..o.name) or {} + + if string.find(o.name,'.') then + name = luci.util.split(o.name,'.')[1] + else + name = o.name + end + + table.insert(originators,{name=name,orig=orig,desc=desc}) + end + + luci.template.render("bmx6/nodes", {originators=originators}) +end + +function action_nodes_j() + local http = require "luci.http" + local link_non_js = "/cgi-bin/luci" .. http.getenv("PATH_INFO") .. '/nodes_nojs' + + luci.template.render("bmx6/nodes_j", {link_non_js=link_non_js}) +end + +function action_gateways_j() + luci.template.render("bmx6/gateways_j", {}) +end + +function action_tunnels_j() + luci.template.render("bmx6/tunnels_j", {}) +end + + +function action_links(host) + local links = bmx6json.get("links", host) + local devlinks = {} + local _,l + + if links ~= nil then + links = links.links + for _,l in ipairs(links) do + devlinks[l.viaDev] = {} + end + for _,l in ipairs(links) do + l.globalId = luci.util.split(l.globalId,'.')[1] + table.insert(devlinks[l.viaDev],l) + end + end + + luci.template.render("bmx6/links", {links=devlinks}) +end + +function action_topology() + local originators = bmx6json.get("originators/all") + local o,i,l,i2 + local first = true + local topology = '[ ' + local cache = '/tmp/bmx6-topology.json' + local offset = 60 + + local cachefd = io.open(cache,r) + local update = false + + if cachefd ~= nil then + local lastupdate = tonumber(cachefd:read("*line")) or 0 + if os.time() >= lastupdate + offset then + update = true + else + topology = cachefd:read("*all") + end + cachefd:close() + end + + if cachefd == nil or update then + for i,o in ipairs(originators) do + local links = bmx6json.get("links",o.primaryIp) + if links then + if first then + first = false + else + topology = topology .. ', ' + end + + topology = topology .. '{ "globalId": "%s", "links": [' %o.globalId:match("^[^%.]+") + + local first2 = true + + for i2,l in ipairs(links.links) do + if first2 then + first2 = false + else + topology = topology .. ', ' + end + + topology = topology .. '{ "globalId": "%s", "rxRate": %s, "txRate": %s }' + %{ l.globalId:match("^[^%.]+"), l.rxRate, l.txRate } + + end + + topology = topology .. ']}' + end + + end + + topology = topology .. ' ]' + + -- Upgrading the content of the cache file + cachefd = io.open(cache,'w+') + cachefd:write(os.time()..'\n') + cachefd:write(topology) + cachefd:close() + end + + luci.http.prepare_content("application/json") + luci.http.write(topology) +end + + +function action_chat() + local sms_dir = "/var/run/bmx6/sms" + local rcvd_dir = sms_dir .. "/rcvdSms" + local send_file = sms_dir .. "/sendSms/chat" + local sms_list = bmx6json.get("rcvdSms") + local sender = "" + local sms_file = "" + local chat = {} + local to_send = nil + local sent = "" + local fd = nil + + if luci.sys.call("test -d " .. sms_dir) ~= 0 then + luci.template.render("bmx6/error", {txt="sms plugin disabled or some problem with directory " .. sms_dir}) + return nil + end + + sms_list = luci.util.split(luci.util.exec("ls "..rcvd_dir.."/*:chat")) + + for _,sms_path in ipairs(sms_list) do + if #sms_path > #rcvd_dir then + sms_file = luci.util.split(sms_path,'/') + sms_file = sms_file[#sms_file] + sender = luci.util.split(sms_file,':')[1] + + -- Trying to clean the name + if string.find(sender,".") ~= nil then + sender = luci.util.split(sender,".")[1] + end + + fd = io.open(sms_path,"r") + chat[sender] = fd:read() + fd:close() + end + end + + to_send = luci.http.formvalue("toSend") + if to_send ~= nil and #to_send > 1 then + fd = io.open(send_file,"w") + fd:write(to_send) + fd:close() + sent = to_send + else + sent = luci.util.exec("cat "..send_file) + end + + luci.template.render("bmx6/chat", {chat=chat,sent=sent}) +end + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua new file mode 100644 index 0000000..dfe9ab1 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua @@ -0,0 +1,219 @@ +--[[ + Copyright (C) 2011 Pau Escrich + Contributors Jo-Philipp Wich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local ltn12 = require("luci.ltn12") +local json = require("luci.json") +local util = require("luci.util") +local uci = require("luci.model.uci") +local sys = require("luci.sys") +local template = require("luci.template") +local http = require("luci.http") +local string = require("string") +local table = require("table") +local nixio = require("nixio") +local nixiofs = require("nixio.fs") +local ipairs = ipairs + +module "luci.model.bmx6json" + +-- Returns a LUA object from bmx6 JSON daemon + +function get(field, host) + local url + if host ~= nil then + if host:match(":") then + url = 'http://[%s]/cgi-bin/bmx6-info?' % host + else + url = 'http://%s/cgi-bin/bmx6-info?' % host + end + else + url = uci.cursor():get("luci-bmx6","luci","json") + end + + if url == nil then + print_error("bmx6 json url not configured, cannot fetch bmx6 daemon data",true) + return nil + end + + local json_url = util.split(url,":") + local raw = "" + + if json_url[1] == "http" then + raw,err = wget(url..field,1000) + else + + if json_url[1] == "exec" then + raw = sys.exec(json_url[2]..' '..field) + else + print_error("bmx6 json url not recognized, cannot fetch bmx6 daemon data. Use http: or exec:",true) + return nil + end + + end + + local data = nil + + if raw and raw:len() > 10 then + local decoder = json.Decoder() + ltn12.pump.all(ltn12.source.string(raw), decoder:sink()) + data = decoder:get() +-- else +-- print_error("Cannot get data from bmx6 daemon",true) +-- return nil + end + + return data +end + +function print_error(txt,popup) + util.perror(txt) + sys.call("logger -t bmx6json " .. txt) + + if popup then + http.write('') + else + http.write("

Dammit! some error detected

") + http.write("bmx6-luci: " .. txt) + http.write('

') + end + +end + +function text2html(txt) + txt = string.gsub(txt,"<","{") + txt = string.gsub(txt,">","}") + txt = util.striptags(txt) + return txt +end + + +function wget(url, timeout) + local rfd, wfd = nixio.pipe() + local pid = nixio.fork() + if pid == 0 then + rfd:close() + nixio.dup(wfd, nixio.stdout) + + local candidates = { "/usr/bin/wget", "/bin/wget" } + local _, bin + for _, bin in ipairs(candidates) do + if nixiofs.access(bin, "x") then + nixio.exec(bin, "-q", "-O", "-", url) + end + end + return + else + wfd:close() + rfd:setblocking(false) + + local buffer = { } + local err1, err2 + + while true do + local ready = nixio.poll({{ fd = rfd, events = nixio.poll_flags("in") }}, timeout) + if not ready then + nixio.kill(pid, nixio.const.SIGKILL) + err1 = "timeout" + break + end + + local rv = rfd:read(4096) + if rv then + -- eof + if #rv == 0 then + break + end + + buffer[#buffer+1] = rv + else + -- error + if nixio.errno() ~= nixio.const.EAGAIN and + nixio.errno() ~= nixio.const.EWOULDBLOCK then + err1 = "error" + err2 = nixio.errno() + end + end + end + + nixio.waitpid(pid, "nohang") + if not err1 then + return table.concat(buffer) + else + return nil, err1, err2 + end + end +end + +function getOptions(name) + -- Getting json and Checking if bmx6-json is avaiable + local options = get("options") + if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + return nil + else + options = options.OPTIONS + end + + -- Filtering by the option name + local i,_ + local namedopt = nil + if name ~= nil then + for _,i in ipairs(options) do + if i.name == name and i.CHILD_OPTIONS ~= nil then + namedopt = i.CHILD_OPTIONS + break + end + end + end + + return namedopt +end + +-- Rturns a help string formated to be used in HTML scope +function getHtmlHelp(opt) + if opt == nil then return nil end + + local help = "" + if opt.help ~= nil then + help = text2html(opt.help) + end + if opt.syntax ~= nil then + help = help .. "
Syntax: " .. text2html(opt.syntax) + end + + return help +end + +function testandreload() + local test = sys.call('bmx6 -c --test > /tmp/bmx6-luci.err.tmp') + if test ~= 0 then + return sys.exec("cat /tmp/bmx6-luci.err.tmp") + end + + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + return sys.exec("cat /tmp/bmx6-luci.err.tmp") + end + + return nil +end + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua new file mode 100644 index 0000000..9510214 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua @@ -0,0 +1,74 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +m = Map("bmx6", "bmx6") + +local bmx6json = require("luci.model.bmx6json") +local util = require("luci.util") +local http = require("luci.http") +local sys = require("luci.sys") + +local options = bmx6json.get("options") +if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + options = {} +else + options = options.OPTIONS +end + +local general = m:section(NamedSection,"general","general","General Options") + +local name = "" +local help = "" +local value = nil +local _,o + +for _,o in ipairs(options) do + if o.name ~= nil and o.CHILD_OPTIONS == nil and o.configurable == 1 then + help = "" + name = o.name + + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + if o.def ~= nil then + help = help .. " Default: " .. o.def + end + + value = general:option(Value,name,name,help) + + end +end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua new file mode 100644 index 0000000..db98ae6 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua @@ -0,0 +1,47 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") + +m = Map("bmx6", "bmx6") + +local hna = m:section(TypedSection,"unicastHna","IPv6 HNA") +hna.addremove = true +hna.anonymous = true +local hna_option = hna:option(Value,"unicastHna", "IPv6 Host Network Announcement. Syntax /") + +--function hna_option:validate(value) +-- local err = sys.call('bmx6 -c --test -a ' .. value) +-- if err ~= 0 then +-- return nil +-- end +-- return value +--end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua new file mode 100644 index 0000000..fb1261b --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua @@ -0,0 +1,77 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") +local m = Map("bmx6", "bmx6") + +local eth_int = sys.net.devices() +local interfaces = m:section(TypedSection,"dev","Devices","") +interfaces.addremove = true +interfaces.anonymous = true + +local intlv = interfaces:option(ListValue,"dev","Device") + +for _,i in ipairs(eth_int) do + intlv:value(i,i) +end + +-- Getting json and looking for device section +local json = bmx6json.get("options") + +if json == nil or json.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + json = {} +else + json = json.OPTIONS +end + +local dev = {} +for _,j in ipairs(json) do + if j.name == "dev" and j.CHILD_OPTIONS ~= nil then + dev = j.CHILD_OPTIONS + break + end +end + +local help = "" +local name = "" + +for _,o in ipairs(dev) do + if o.name ~= nil then + help = "" + name = o.name + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + value = interfaces:option(Value,name,name,help) + value.optional = true + end +end + + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua new file mode 100644 index 0000000..8c114bf --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua @@ -0,0 +1,108 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") + +m = Map("bmx6", "bmx6") + +-- Getting json and Checking if bmx6-json is avaiable +local options = bmx6json.get("options") +if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" +else + options = options.OPTIONS +end + +-- Getting a list of interfaces +local eth_int = luci.sys.net.devices() + +-- Getting the most important options from general +local general = m:section(NamedSection,"general","general","General") +general.addremove = false +general:option(Value,"globalPrefix","Global ip prefix","Specify global prefix for interfaces: NETADDR/LENGTH. If you are using IPv6 leave blank to let bmx6 autoassign an ULA IPv6 address.") + +if m:get("ipVersion","ipVersion") == "6" then + general:option(Value,"tun4Address","IPv4 address or range","specify default IPv4 tunnel address and announced range") +end + +-- IP section +-- ipVersion section is important, we are allways showing it +local ipV = m:section(NamedSection,"ipVersion","ipVersion","IP options") +ipV.addremove = false +local lipv = ipV:option(ListValue,"ipVersion","IP version") +lipv:value("4","4") +lipv:value("6","6") +lipv.default = "6" + +-- rest of ip options are optional, getting them from json +local ipoptions = {} +for _,o in ipairs(options) do + if o.name == "ipVersion" and o.CHILD_OPTIONS ~= nil then + ipoptions = o.CHILD_OPTIONS + break + end +end + +local help = "" +local name = "" +local value = nil + +for _,o in ipairs(ipoptions) do + if o.name ~= nil then + help = "" + name = o.name + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + if o.def ~= nil then + help = help .. "
Default: " .. bmx6json.text2html(o.def) + end + + value = ipV:option(Value,name,name,help) + value.optional = true + end +end + +-- Interfaces section +local interfaces = m:section(TypedSection,"dev","Devices","") +interfaces.addremove = true +interfaces.anonymous = true +local intlv = interfaces:option(ListValue,"dev","Device") + +for _,i in ipairs(eth_int) do + intlv:value(i,i) +end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua new file mode 100644 index 0000000..518864e --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua @@ -0,0 +1,50 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] +local sys = require("luci.sys") + +m = Map("bmx6", "bmx6") +plugins_dir = {"/usr/lib/","/var/lib","/lib"} + +plugin = m:section(TypedSection,"plugin","Plugin") +plugin.addremove = true +plugin.anonymous = true +plv = plugin:option(ListValue,"plugin", "Plugin") + +for _,d in ipairs(plugins_dir) do + pl = luci.sys.exec("cd "..d..";ls bmx6_*") + if #pl > 6 then + for _,v in ipairs(luci.util.split(pl,"\n")) do + plv:value(v,v) + end + end +end + + +function m.on_commit(self,map) + local err = sys.call('/etc/init.d/bmx6 restart') + if err ~= 0 then + m.message = sys.exec("Cannot restart bmx6") + end +end + + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua new file mode 100644 index 0000000..7a6bfd3 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua @@ -0,0 +1,75 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") + +m = Map("bmx6", "bmx6") + +-- tunOut +local tunnelsOut = m:section(TypedSection,"tunOut",translate("Networks to fetch"),translate("Tunnel announcements to fetch if possible")) +tunnelsOut.addremove = true +tunnelsOut.anonymous = true +tunnelsOut:option(Value,"tunOut","Name") +tunnelsOut:option(Value,"network", translate("Network to fetch")) + +local tunoptions = bmx6json.getOptions("tunOut") +local _,o +for _,o in ipairs(tunoptions) do + if o.name ~= nil and o.name ~= "network" then + help = bmx6json.getHtmlHelp(o) + value = tunnelsOut:option(Value,o.name,o.name,help) + value.optional = true + end +end + + +--tunIn +local tunnelsIn = m:section(TypedSection,"tunInNet",translate("Networks to offer"),translate("Tunnels to announce in the network")) +tunnelsIn.addremove = true +tunnelsIn.anonymous = true + +local net = tunnelsIn:option(Value,"tunInNet", translate("Network to offer")) +net.default = "10.0.0.0/8" + +local bwd = tunnelsIn:option(Value,"bandwidth",translate("Bandwidth (Bytes)")) +bwd.default = "1000000" + +local tuninoptions = bmx6json.getOptions("tunInNet") +local _,o +for _,o in ipairs(tuninoptions) do + if o.name ~= nil and o.name ~= "tunInNet" and o.name ~= "bandwidth" then + help = bmx6json.getHtmlHelp(o) + value = tunnelsIn:option(Value,o.name,o.name,help) + value.optional = true + end +end + +function m.on_commit(self,map) + --Not working. If test returns error the changes are still commited + local msg = bmx6json.testandreload() + if msg ~= nil then + m.message = msg + end +end + +return m + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm new file mode 100644 index 0000000..97d6e0e --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm @@ -0,0 +1,108 @@ + + +
+ +
+ <%:Mesh nodes%> + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+ + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm new file mode 100644 index 0000000..8e25b11 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm @@ -0,0 +1,35 @@ +<%+header%> + +

<%:Chat%>

+

This is sms a chat where all bmx6 nodes can participate. The data is replayed using routing packets, so there is a limit of 2040 bytes. Use it only to send short messages.

+

Each participant can only send one sms at same time.

+
+ +Received SMS +
+
+<% for orig,sms in pairs(chat) do %>
+         <%=orig%>:<%=sms%>
+<% end %>
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
Your last sms:
<%=sent%>
+<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm new file mode 100644 index 0000000..78de7b9 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm @@ -0,0 +1,10 @@ +<%+header%> +

<%:ERROR%>

+Some error has occurred +
+
+	<%=txt%>
+
+
+<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm new file mode 100644 index 0000000..12bfcd4 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm @@ -0,0 +1,120 @@ +<%+header%> + + + + + +
+ +

Originators

+
+
+ <%:Mesh gateways%> + + + + + + + + + + +
<%:Node%><%:Network%><%:Bandwidth%>

<%:Collecting data...%>
+
+ +
+ +<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm new file mode 100644 index 0000000..a4dabb7 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm @@ -0,0 +1,110 @@ +<%# +Copyright (C) 2011 Pau Escrich +Contributors Jo-Philip + +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. + +The full GNU General Public License is included in this distribution in +the file called "COPYING". +-%> + +<% + luci.http.prepare_content("text/html") + + local location = { unpack(luci.dispatcher.context.path) } + location[#location] = "topology" +%> + +<%+header%> + + + + + + + + +
+

+ +<%:Collecting data...%> + +
+ +
+ + + + +<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm new file mode 100644 index 0000000..70935ea --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm @@ -0,0 +1,59 @@ +<%+header%> + + + +

<%:Interfaces%>

+Interfaces where bmx6 is running +
+
+ + + + + + + + + + + +<% for i,v in ipairs(data) do %> + + + + + + + + + + +<%end%> +
NameStateTypeRate (Min/Max)Local IPGlobal IPMulticast IPPrimary
<%=v.devName%><%=v.state%><%=v.type%><%=v.rateMin%>/<%=v.rateMax%><%=v.llocalIp%><%=v.globalIp%><%=v.multicastIp%><%=v.primary%>
+ +
+<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm new file mode 100644 index 0000000..65e62d7 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm @@ -0,0 +1,55 @@ +<%+header%> + +

<%:Links%>

+
+ + +
+
+<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm new file mode 100644 index 0000000..6474116 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm @@ -0,0 +1,89 @@ +<%+header%> + + +

<%:Neighbours%>

+ + + + + + + + + + + + + +<% for i,o in ipairs(originators) do %> + + + + + + + + + + + +<%end%> +
NameIPv4IPv6Via DevVia IPRoutesMetricLast DescLast Ref
<%=o.name%><%=o.ipv4%><%=o.orig.primaryIp%><%=o.orig.viaDev%><%=o.orig.viaIp%><%=o.orig.routes%><%=o.orig.metric%><%=o.orig.lastDesc%><%=o.orig.lastRef%>
+ + + + + + + +<% for i,o in ipairs(originators) do %> + + + + +<% end %> +
NodeAnnounced networks
<%=o.name%> + <% if o.desc.DESC_ADV ~= nil then %> + <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %> + <%=h.address%>     + <% end %> + <% end %> +
+ +
+<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm new file mode 100644 index 0000000..14f5597 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm @@ -0,0 +1,188 @@ +<%+header%> + + + +
+ +

Originators

+
+
+
+ Click to the icon to see individual node information +
+
+ <%:Mesh nodes%> + + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+Go to non JavaScript view + + +<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm new file mode 100644 index 0000000..18e5cc9 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm @@ -0,0 +1,87 @@ +<%+header%> + + +

<%:Nodes%>

+ + + + + + + + + + + + +<% for i,o in ipairs(originators) do %> + + + + + + + + + + +<%end%> +
NameIPv6Via DevVia IPRoutesMetricLast DescLast Ref
<%=o.name%><%=o.orig.primaryIp%><%=o.orig.viaDev%><%=o.orig.viaIp%><%=o.orig.routes%><%=o.orig.metric%><%=o.orig.lastDesc%><%=o.orig.lastRef%>
+ + + + + + + +<% for i,o in ipairs(originators) do %> + + + + +<% end %> +
NodeAnnounced networks
<%=o.name%> + <% if o.desc.DESC_ADV ~= nil then %> + <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %> + <%=h.address%>     + <% end %> + <% end %> +
+ +
+<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm new file mode 100644 index 0000000..0435655 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm @@ -0,0 +1,193 @@ +<%# + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + +<%+header%> + + + + + +
+ +

Node originators

+
+
+
+
+ Click icon to see individual node information +
+
+
+ <%:Mesh nodes%> + + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+ +Go to non JavaScript view + + + +<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm new file mode 100644 index 0000000..11e9682 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm @@ -0,0 +1,69 @@ +<%+header%> + + + + +Bmx6 is a routing protocol for Linux based operating systems. Visit bmx6.net for more info. + +
+
+ +

Status of bmx6

+ + + + + + + + + + + + + + + + + + + + + + + + +
VersionCompatibilityCodeVersionGlobal IdPrimary IpLocal IdUptimeCPUNodes
<%=status.version%><%=status.compatibility%><%=status.codeVersion%><%=status.globalId%>/<%=status.rateMax%><%=status.primaryIp%><%=status.myLocalId%><%=status.uptime%><%=status.cpu%><%=status.nodes%>
+ +
+
+ +

Status of interfaces

+ + + + + + + + + + + +<% for i,v in ipairs(interfaces) do %> + + + + + + + + + + +<%end%> +
NameStateTypeRate (Min/Max)Local IPGlobal IPMulticast IPPrimary
<%=v.devName%><%=v.state%><%=v.type%><%=v.rateMin%>/<%=v.rateMax%><%=v.llocalIp%><%=v.globalIp%><%=v.multicastIp%><%=v.primary%>
+ +
+ +<%+footer%> diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm new file mode 100644 index 0000000..5e51f09 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm @@ -0,0 +1,114 @@ +<%+header%> + + + + + +
+
+ +
+
+a mesh routing protocol for Linux devices.
+Visit bmx6.net for more info. +
+
+
+ +

status

+
+
+ <%:status%> + + + + + + + + + + + + + + + + + +
<%:Version%><%:Compat%><%:Code Version%><%:Global ID%><%:Primary IP%><%:Tun6Address%><%:Tun4Address%><%:Local ID%><%:Uptime%><%:Cpu load%><%:Nodes seen%>

<%:Collecting data...%>
+
+ +
+ + <%:Network devices%> + + + + + + + + + + + + + + +
<%:Name%><%:State%><%:Type%><%:Rate%><%:Local IP%><%:Global IP%><%:Multicast IP%><%:is Primary%>

<%:Collecting data...%>
+
+ +
+ + + + +<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm new file mode 100644 index 0000000..1b7ce42 --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm @@ -0,0 +1,107 @@ +<%# + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + + +<%+header%> + + + + + +
+ +

Gateways tunnel announcements

+
+
+ <%:Mesh gateways%> + + + + + + + + + + + + + + + + + + +
<%:Tunnel%><%:Node%><%:Network%><%:Bandwidth%><%:SearchNet%><%:Type%><%:Path Metric%><%:IP metric%><%:Tun metric%><%:Bonus%><%:search id%>

<%:Collecting data...%>
+
+ +
+ + + +<%+footer%> + diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm new file mode 100644 index 0000000..aa7e19d --- /dev/null +++ b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm @@ -0,0 +1,7 @@ +<%+header%> +

<%:Wireless%>

+
+

Wireless

+<%=data%> +
+<%+footer%> diff --git a/luci-app-bmx6/files/www/cgi-bin/bmx6-info b/luci-app-bmx6/files/www/cgi-bin/bmx6-info new file mode 100644 index 0000000..931cbf6 --- /dev/null +++ b/luci-app-bmx6/files/www/cgi-bin/bmx6-info @@ -0,0 +1,112 @@ +#!/bin/sh +# Copyright (C) 2011 Pau Escrich +# Contributors Jo-Philipp Wich +# +# 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. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# This script gives information about bmx6 +# Can be executed from a linux shell: ./bmx6-info -s links +# Or from web interfae (with cgi enabled): http://host/cgi-bin/bmx6-info?links +# If you ask for a directory you wil get the directory contents in JSON forman + +BMX6_DIR="$(uci get bmx6.general.runtimeDir 2>/dev/null)" || BMX6_DIR="/var/run/bmx6/json" + +#Checking if shell mode or cgi-bin mode +if [ "$1" == "-s" ]; then + QUERY="$2" +else + QUERY="${QUERY_STRING%%=*}" + echo "Content-type: application/json" + echo "" + +fi + +check_path() { + [ -d "$1" ] && path=$(cd $1; pwd) + [ -f "$1" ] && path=$(cd $1/..; pwd) + [ $(echo "$path" | grep -c "^$BMX6_DIR") -ne 1 ] && exit 1 +} + +print_query() { + # If the query is a directory + [ -d "$BMX6_DIR/$1" ] && + { + # If /all has not been specified + [ -z "$QALL" ] && + { + total=$(ls $BMX6_DIR/$1 | wc -w) + i=1 + echo -n "{ \"$1\": [ " + for f in $(ls $BMX6_DIR/$1); do + echo -n "{ \"name\": \"$f\" }" + [ $i -lt $total ] && echo -n ',' + i=$(( $i + 1 )) + done + echo -n " ] }" + + # If /all has been specified, printing all the files together + } || { + comma="" + echo -n "[ " + for entry in "$BMX6_DIR/$1/"*; do + [ -f "$entry" ] && + { + ${comma:+echo "$comma"} + tr -d '\n' < "$entry" + comma="," + } + done + echo -n " ]" + } + } + + # If the query is a file, just printing the file + [ -f "$BMX6_DIR/$QUERY" ] && cat "$BMX6_DIR/$QUERY"; +} + +if [ "${QUERY##*/}" == "all" ]; then + QUERY="${QUERY%/all}" + QALL=1 +fi + + +if [ "$QUERY" == '$neighbours' ]; then + QALL=1 + echo '{ "neighbours": [ ' + echo '{ "originators": ' + print_query originators + echo '}, ' + echo '{ "descriptions": ' + print_query descriptions + echo "} ] }" + exit 0 + +else if [ "$QUERY" == '$tunnels' ]; then + bmx6 -c --jshow tunnels + exit 0 + +else + check_path "$BMX6_DIR/$QUERY" + print_query $QUERY + exit 0 +fi +fi + +ls -1F "$BMX6_DIR" +exit 0 + diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png new file mode 100644 index 0000000..12f7526 Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png differ diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js new file mode 100644 index 0000000..f5aa0f9 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js @@ -0,0 +1,29 @@ +/** + * Curry - Function currying + * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) + * Date: 10/4/2008 + * + * @author Ariel Flesler + * @version 1.0.1 + */ + +function curry( fn ){ + return function(){ + var args = curry.args(arguments), + master = arguments.callee, + self = this; + + return args.length >= fn.length ? fn.apply(self,args) : function(){ + return master.apply( self, args.concat(curry.args(arguments)) ); + }; + }; +}; + +curry.args = function( args ){ + return Array.prototype.slice.call(args); +}; + +Function.prototype.curry = function(){ + return curry(this); +}; \ No newline at end of file diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js new file mode 100644 index 0000000..0fbb085 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js @@ -0,0 +1,599 @@ +/* + * Various algorithms and data structures, licensed under the MIT-license. + * (c) 2010 by Johann Philipp Strathausen + * http://strathausen.eu + * + */ + + + +/* + Bellman-Ford + + Path-finding algorithm, finds the shortest paths from one node to all nodes. + + + Complexity + + O( |E| · |V| ), where E = edges and V = vertices (nodes) + + + Constraints + + Can run on graphs with negative edge weights as long as they do not have + any negative weight cycles. + + */ +function bellman_ford(g, source) { + + /* STEP 1: initialisation */ + for(var n in g.nodes) + g.nodes[n].distance = Infinity; + /* predecessors are implicitly null */ + source.distance = 0; + + step("Initially, all distances are infinite and all predecessors are null."); + + /* STEP 2: relax each edge (this is at the heart of Bellman-Ford) */ + /* repeat this for the number of nodes minus one */ + for(var i = 1; i < g.nodes.length; i++) + /* for each edge */ + for(var e in g.edges) { + var edge = g.edges[e]; + if(edge.source.distance + edge.weight < edge.target.distance) { + step("Relax edge between " + edge.source.id + " and " + edge.target.id + "."); + edge.target.distance = edge.source.distance + edge.weight; + edge.target.predecessor = edge.source; + } + //Added by Jake Stothard (Needs to be tested) + if(!edge.style.directed) { + if(edge.target.distance + edge.weight < edge.source.distance) { + g.snapShot("Relax edge between "+edge.target.id+" and "+edge.source.id+"."); + edge.source.distance = edge.target.distance + edge.weight; + edge.source.predecessor = edge.target; + } + } + } + step("Ready."); + + /* STEP 3: TODO Check for negative cycles */ + /* For now we assume here that the graph does not contain any negative + weights cycles. (this is left as an excercise to the reader[tm]) */ +} + + + +/* + Path-finding algorithm Dijkstra + + - worst-case running time is O((|E| + |V|) · log |V| ) thus better than + Bellman-Ford for sparse graphs (with less edges), but cannot handle + negative edge weights + */ +function dijkstra(g, source) { + + /* initially, all distances are infinite and all predecessors are null */ + for(var n in g.nodes) + g.nodes[n].distance = Infinity; + /* predecessors are implicitly null */ + + g.snapShot("Initially, all distances are infinite and all predecessors are null."); + + source.distance = 0; + /* set of unoptimized nodes, sorted by their distance (but a Fibonacci heap + would be better) */ + var q = new BinaryMinHeap(g.nodes, "distance"); + + /* pointer to the node in focus */ + var node; + + /* get the node with the smallest distance + as long as we have unoptimized nodes. q.min() can have O(log n). */ + while(q.min() != undefined) { + /* remove the latest */ + node = q.extractMin(); + node.optimized = true; + + /* no nodes accessible from this one, should not happen */ + if(node.distance == Infinity) + throw "Orphaned node!"; + + /* for each neighbour of node */ + for(e in node.edges) { + var other = (node == node.edges[e].target) ? node.edges[e].source : node.edges[e].target; + + if(other.optimized) + continue; + + /* look for an alternative route */ + var alt = node.distance + node.edges[e].weight; + + /* update distance and route if a better one has been found */ + if (alt < other.distance) { + + /* update distance of neighbour */ + other.distance = alt; + + /* update priority queue */ + q.heapify(); + + /* update path */ + other.predecessor = node; + g.snapShot("Enhancing node.") + } + } + } +} + + +/* All-Pairs-Shortest-Paths */ +/* Runs at worst in O(|V|³) and at best in Omega(|V|³) :-) + complexity Sigma(|V|²) */ +/* This implementation is not yet ready for general use, but works with the + Dracula graph library. */ +function floyd_warshall(g, source) { + + /* Step 1: initialising empty path matrix (second dimension is implicit) */ + var path = []; + var next = []; + var n = g.nodes.length; + + /* construct path matrix, initialize with Infinity */ + for(j in g.nodes) { + path[j] = []; + next[j] = []; + for(i in g.nodes) + path[j][i] = j == i ? 0 : Infinity; + } + + /* initialize path with edge weights */ + for(e in g.edges) + path[g.edges[e].source.id][g.edges[e].target.id] = g.edges[e].weight; + + /* Note: Usually, the initialisation is done by getting the edge weights + from a node matrix representation of the graph, not by iterating through + a list of edges as done here. */ + + /* Step 2: find best distances (the heart of Floyd-Warshall) */ + for(k in g.nodes){ + for(i in g.nodes) { + for(j in g.nodes) + if(path[i][j] > path[i][k] + path[k][j]) { + path[i][j] = path[i][k] + path[k][j]; + /* Step 2.b: remember the path */ + next[i][j] = k; + } + } + } + + /* Step 3: Path reconstruction, get shortest path */ + function getPath(i, j) { + if(path[i][j] == Infinity) + throw "There is no path."; + var intermediate = next[i][j]; + if(intermediate == undefined) + return null; + else + return getPath(i, intermediate) + .concat([intermediate]) + .concat(getPath(intermediate, j)); + } + + /* TODO use the knowledge, e.g. mark path in graph */ +} + +/* + Ford-Fulkerson + + Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed + graph from source to sink. + + + Complexity + + O(E * max(f)), max(f) being the maximum flow + + + Description + + As long as there is an open path through the residual graph, send the + minimum of the residual capacities on the path. + + + Constraints + + The algorithm works only if all weights are integers. Otherwise it is + possible that the Ford–Fulkerson algorithm will not converge to the maximum + value. + + + Input + + g - Graph object + s - Source ID + t - Target (sink) ID + + + Output + + Maximum flow from Source s to Target t + + */ +/* + Edmonds-Karp + + Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed + graph from source to sink. An implementation of the Ford-Fulkerson + algorithm. + + + Complexity + + O(|V|*|E|²) + + + Input + + g - Graph object (with node and edge lists, capacity is a property of edge) + s - source ID + t - sink ID + + */ +function edmonds_karp(g, s, t) { + +} + +/* + A simple binary min-heap serving as a priority queue + - takes an array as the input, with elements having a key property + - elements will look like this: + { + key: "... key property ...", + value: "... element content ..." + } + - provides insert(), min(), extractMin() and heapify() + - example usage (e.g. via the Firebug or Chromium console): + var x = {foo: 20, hui: "bla"}; + var a = new BinaryMinHeap([x,{foo:3},{foo:10},{foo:20},{foo:30},{foo:6},{foo:1},{foo:3}],"foo"); + console.log(a.extractMin()); + console.log(a.extractMin()); + x.foo = 0; // update key + a.heapify(); // call this always after having a key updated + console.log(a.extractMin()); + console.log(a.extractMin()); + - can also be used on a simple array, like [9,7,8,5] + */ +function BinaryMinHeap(array, key) { + + /* Binary tree stored in an array, no need for a complicated data structure */ + var tree = []; + + var key = key || 'key'; + + /* Calculate the index of the parent or a child */ + var parent = function(index) { return Math.floor((index - 1)/2); }; + var right = function(index) { return 2 * index + 2; }; + var left = function(index) { return 2 * index + 1; }; + + /* Helper function to swap elements with their parent + as long as the parent is bigger */ + function bubble_up(i) { + var p = parent(i); + while((p >= 0) && (tree[i][key] < tree[p][key])) { + /* swap with parent */ + tree[i] = tree.splice(p, 1, tree[i])[0]; + /* go up one level */ + i = p; + p = parent(i); + } + } + + /* Helper function to swap elements with the smaller of their children + as long as there is one */ + function bubble_down(i) { + var l = left(i); + var r = right(i); + + /* as long as there are smaller children */ + while(tree[l] && (tree[i][key] > tree[l][key]) || tree[r] && (tree[i][key] > tree[r][key])) { + + /* find smaller child */ + var child = tree[l] ? tree[r] ? tree[l][key] > tree[r][key] ? r : l : l : l; + + /* swap with smaller child with current element */ + tree[i] = tree.splice(child, 1, tree[i])[0]; + + /* go up one level */ + i = child; + l = left(i); + r = right(i); + } + } + + /* Insert a new element with respect to the heap property + 1. Insert the element at the end + 2. Bubble it up until it is smaller than its parent */ + this.insert = function(element) { + + /* make sure there's a key property */ + (element[key] == undefined) && (element = {key:element}); + + /* insert element at the end */ + tree.push(element); + + /* bubble up the element */ + bubble_up(tree.length - 1); + } + + /* Only show us the minimum */ + this.min = function() { + return tree.length == 1 ? undefined : tree[0]; + } + + /* Return and remove the minimum + 1. Take the root as the minimum that we are looking for + 2. Move the last element to the root (thereby deleting the root) + 3. Compare the new root with both of its children, swap it with the + smaller child and then check again from there (bubble down) + */ + this.extractMin = function() { + var result = this.min(); + + /* move the last element to the root or empty the tree completely */ + /* bubble down the new root if necessary */ + (tree.length == 1) && (tree = []) || (tree[0] = tree.pop()) && bubble_down(0); + + return result; + } + + /* currently unused, TODO implement */ + this.changeKey = function(index, key) { + throw "function not implemented"; + } + + this.heapify = function() { + for(var start = Math.floor((tree.length - 2) / 2); start >= 0; start--) { + bubble_down(start); + } + } + + /* insert the input elements one by one only when we don't have a key property (TODO can be done more elegant) */ + for(i in (array || [])) + this.insert(array[i]); +} + + + +/* + Quick Sort: + 1. Select some random value from the array, the median. + 2. Divide the array in three smaller arrays according to the elements + being less, equal or greater than the median. + 3. Recursively sort the array containg the elements less than the + median and the one containing elements greater than the median. + 4. Concatenate the three arrays (less, equal and greater). + 5. One or no element is always sorted. + TODO: This could be implemented more efficiently by using only one array object and several pointers. +*/ +function quickSort(arr) { + /* recursion anchor: one element is always sorted */ + if(arr.length <= 1) return arr; + /* randomly selecting some value */ + var median = arr[Math.floor(Math.random() * arr.length)]; + var arr1 = [], arr2 = [], arr3 = []; + for(var i in arr) { + arr[i] < median && arr1.push(arr[i]); + arr[i] == median && arr2.push(arr[i]); + arr[i] > median && arr3.push(arr[i]); + } + /* recursive sorting and assembling final result */ + return quickSort(arr1).concat(arr2).concat(quickSort(arr3)); +} + +/* + Selection Sort: + 1. Select the minimum and remove it from the array + 2. Sort the rest recursively + 3. Return the minimum plus the sorted rest + 4. An array with only one element is already sorted +*/ +function selectionSort(arr) { + /* recursion anchor: one element is always sorted */ + if(arr.length == 1) return arr; + var minimum = Infinity; + var index; + for(var i in arr) { + if(arr[i] < minimum) { + minimum = arr[i]; + index = i; /* remember the minimum index for later removal */ + } + } + /* remove the minimum */ + arr.splice(index, 1); + /* assemble result and sort recursively (could be easily done iteratively as well)*/ + return [minimum].concat(selectionSort(arr)); +} + +/* + Merge Sort: + 1. Cut the array in half + 2. Sort each of them recursively + 3. Merge the two sorted arrays + 4. An array with only one element is considered sorted + +*/ +function mergeSort(arr) { + /* merges two sorted arrays into one sorted array */ + function merge(a, b) { + /* result set */ + var c = []; + /* as long as there are elements in the arrays to be merged */ + while(a.length > 0 || b.length > 0){ + /* are there elements to be merged, if yes, compare them and merge */ + var n = a.length > 0 && b.length > 0 ? a[0] < b[0] ? a.shift() : b.shift() : b.length > 0 ? b.shift() : a.length > 0 ? a.shift() : null; + /* always push the smaller one onto the result set */ + n != null && c.push(n); + } + return c; + } + /* this mergeSort implementation cuts the array in half, wich should be fine with randomized arrays, but introduces the risk of a worst-case scenario */ + median = Math.floor(arr.length / 2); + var part1 = arr.slice(0, median); /* for some reason it doesn't work if inserted directly in the return statement (tried so with firefox) */ + var part2 = arr.slice(median - arr.length); + return arr.length <= 1 ? arr : merge( + mergeSort(part1), /* first half */ + mergeSort(part2) /* second half */ + ); +} + +/* Balanced Red-Black-Tree */ +function RedBlackTree(arr) { + +} + +function BTree(arr) { + +} + +function NaryTree(n, arr) { + +} + +/** + * Knuth-Morris-Pratt string matching algorithm - finds a pattern in a text. + * FIXME: Doesn't work correctly yet. + */ +function kmp(p, t) { + + /** + * PREFIX, OVERLAP or FALIURE function for KMP. Computes how many iterations + * the algorithm can skip after a mismatch. + * + * @input p - pattern (string) + * @result array of skippable iterations + */ + function prefix(p) { + /* pi contains the computed skip marks */ + var pi = [0], k = 0; + for(q = 1; q < p.length; q++) { + while(k > 0 && (p.charAt(k) != p.charAt(q))) + k = pi[k-1]; + + (p.charAt(k) == p.charAt(q)) && k++; + + pi[q] = k; + } + return pi; + } + + /* The actual KMP algorithm starts here. */ + + var pi = prefix(p), q = 0, result = []; + + for(var i = 0; i < t.length; i++) { + /* jump forward as long as the character doesn't match */ + while((q > 0) && (p.charAt(q) != t.charAt(i))) + q = pi[q]; + + (p.charAt(q) == t.charAt(i)) && q++; + + (q == p.length) && result.push(i - p.length) && (q = pi[q]); + } + + return result; +} + +/* step for algorithm visualisation */ +function step(comment, funct) { + //wait for input + //display comment (before or after waiting) +// next.wait(); + /* execute callback function */ + funct(); +} + +/** + * Curry - Function currying + * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) + * Date: 10/4/2008 + * + * @author Ariel Flesler + * @version 1.0.1 + */ +function curry( fn ){ + return function(){ + var args = curry.args(arguments), + master = arguments.callee, + self = this; + + return args.length >= fn.length ? fn.apply(self,args) : function(){ + return master.apply( self, args.concat(curry.args(arguments)) ); + }; + }; +}; + +curry.args = function( args ){ + return Array.prototype.slice.call(args); +}; + +Function.prototype.curry = function(){ + return curry(this); +}; + +/** + * Topological Sort + * + * Sort a directed graph based on incoming edges + * + * Coded by Jake Stothard + */ +function topological_sort(g) { + //Mark nodes as "deleted" instead of actually deleting them + //That way we don't have to copy g + + for(i in g.nodes) + g.nodes[i].deleted = false; + + var ret = topological_sort_helper(g); + + //Cleanup: Remove the deleted property + for(i in g.nodes) + delete g.nodes[i].deleted + + return ret; +} +function topological_sort_helper(g) { + //Find node with no incoming edges + var node; + for(i in g.nodes) { + if(g.nodes[i].deleted) + continue; //Bad style, meh + + var incoming = false; + for(j in g.nodes[i].edges) { + if(g.nodes[i].edges[j].target == g.nodes[i] + && g.nodes[i].edges[j].source.deleted == false) { + incoming = true; + break; + } + } + if(!incoming) { + node = g.nodes[i]; + break; + } + } + + // Either unsortable or done. Either way, GTFO + if(node == undefined) + return []; + + //"Delete" node from g + node.deleted = true; + + var tail = topological_sort_helper(g); + + tail.unshift(node); + + return tail; +} diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js new file mode 100644 index 0000000..ddf171d --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js @@ -0,0 +1,106 @@ +/** + * Originally grabbed from the official RaphaelJS Documentation + * http://raphaeljs.com/graffle.html + * Adopted (arrows) and commented by Philipp Strathausen http://blog.ameisenbar.de + * Licenced under the MIT licence. + */ + +/** + * Usage: + * connect two shapes + * parameters: + * source shape [or connection for redrawing], + * target shape, + * style with { fg : linecolor, bg : background color, directed: boolean } + * returns: + * connection { draw = function() } + */ +Raphael.fn.connection = function (obj1, obj2, style) { + var selfRef = this; + /* create and return new connection */ + var edge = {/* + from : obj1, + to : obj2, + style : style,*/ + draw : function() { + /* get bounding boxes of target and source */ + var bb1 = obj1.getBBox(); + var bb2 = obj2.getBBox(); + var off1 = 0; + var off2 = 0; + /* coordinates for potential connection coordinates from/to the objects */ + var p = [ + {x: bb1.x + bb1.width / 2, y: bb1.y - off1}, /* NORTH 1 */ + {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */ + {x: bb1.x - off1, y: bb1.y + bb1.height / 2}, /* WEST 1 */ + {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST 1 */ + {x: bb2.x + bb2.width / 2, y: bb2.y - off2}, /* NORTH 2 */ + {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */ + {x: bb2.x - off2, y: bb2.y + bb2.height / 2}, /* WEST 2 */ + {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2} /* EAST 2 */ + ]; + + /* distances between objects and according coordinates connection */ + var d = {}, dis = []; + + /* + * find out the best connection coordinates by trying all possible ways + */ + /* loop the first object's connection coordinates */ + for (var i = 0; i < 4; i++) { + /* loop the seond object's connection coordinates */ + for (var j = 4; j < 8; j++) { + var dx = Math.abs(p[i].x - p[j].x), + dy = Math.abs(p[i].y - p[j].y); + if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) { + dis.push(dx + dy); + d[dis[dis.length - 1].toFixed(3)] = [i, j]; + } + } + } + var res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis).toFixed(3)]; + /* bezier path */ + var x1 = p[res[0]].x, + y1 = p[res[0]].y, + x4 = p[res[1]].x, + y4 = p[res[1]].y, + dx = Math.max(Math.abs(x1 - x4) / 2, 10), + dy = Math.max(Math.abs(y1 - y4) / 2, 10), + x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3), + y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3), + x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3), + y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3); + /* assemble path and arrow */ + var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(","); + /* arrow */ + if(style && style.directed) { + /* magnitude, length of the last path vector */ + var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3)); + /* vector normalisation to specified length */ + var norm = function(x,l){return (-x*(l||5)/mag);}; + /* calculate array coordinates (two lines orthogonal to the path vector) */ + var arr = [ + {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)}, + {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)} + ]; + path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y; + } + /* function to be used for moving existent path(s), e.g. animate() or attr() */ + var move = "attr"; + /* applying path(s) */ + edge.fg && edge.fg[move]({path:path}) + || (edge.fg = selfRef.path(path).attr({stroke: style && style.stroke || "#000", fill: "none"}).toBack()); + edge.bg && edge.bg[move]({path:path}) + || style && style.fill && (edge.bg = style.fill.split && selfRef.path(path).attr({stroke: style.fill.split("|")[0], fill: "none", "stroke-width": style.fill.split("|")[1] || 3}).toBack()); + /* setting label */ + style && style.label + && (edge.label && edge.label.attr({x:(x1+x4)/2, y:(y1+y4)/2}) + || (edge.label = selfRef.text((x1+x4)/2, (y1+y4)/2, style.label).attr({fill: "#000", "font-size": style["font-size"] || "12px"}))); + style && style.label && style["label-style"] && edge.label && edge.label.attr(style["label-style"]); + style && style.callback && style.callback(edge); + } + } + edge.draw(); + return edge; +}; +//Raphael.prototype.set.prototype.dodo=function(){console.log("works");}; diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js new file mode 100644 index 0000000..f3e43e1 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js @@ -0,0 +1,527 @@ +/* + * Dracula Graph Layout and Drawing Framework 0.0.3alpha + * (c) 2010 Philipp Strathausen , http://strathausen.eu + * Contributions by Jake Stothard . + * + * based on the Graph JavaScript framework, version 0.0.1 + * (c) 2006 Aslak Hellesoy + * (c) 2006 Dave Hoover + * + * Ported from Graph::Layouter::Spring in + * http://search.cpan.org/~pasky/Graph-Layderer-0.02/ + * The algorithm is based on a spring-style layouter of a Java-based social + * network tracker PieSpy written by Paul Mutton . + * + * This code is freely distributable under the MIT license. Commercial use is + * hereby granted without any cost or restriction. + * + * Links: + * + * Graph Dracula JavaScript Framework: + * http://graphdracula.net + * + /*--------------------------------------------------------------------------*/ + +/* + * Edge Factory + */ +var AbstractEdge = function() { +} +AbstractEdge.prototype = { + hide: function() { + this.connection.fg.hide(); + this.connection.bg && this.bg.connection.hide(); + } +}; +var EdgeFactory = function() { + this.template = new AbstractEdge(); + this.template.style = new Object(); + this.template.style.directed = false; + this.template.weight = 1; +}; +EdgeFactory.prototype = { + build: function(source, target) { + var e = jQuery.extend(true, {}, this.template); + e.source = source; + e.target = target; + return e; + } +}; + +/* + * Graph + */ +var Graph = function() { + this.nodes = {}; + this.edges = []; + this.snapshots = []; // previous graph states TODO to be implemented + this.edgeFactory = new EdgeFactory(); +}; +Graph.prototype = { + /* + * add a node + * @id the node's ID (string or number) + * @content (optional, dictionary) can contain any information that is + * being interpreted by the layout algorithm or the graph + * representation + */ + addNode: function(id, content) { + /* testing if node is already existing in the graph */ + if(this.nodes[id] == undefined) { + this.nodes[id] = new Graph.Node(id, content); + } + return this.nodes[id]; + }, + + addEdge: function(source, target, style) { + var s = this.addNode(source); + var t = this.addNode(target); + var edge = this.edgeFactory.build(s, t); + jQuery.extend(edge.style,style); + s.edges.push(edge); + this.edges.push(edge); + // NOTE: Even directed edges are added to both nodes. + t.edges.push(edge); + }, + + /* TODO to be implemented + * Preserve a copy of the graph state (nodes, positions, ...) + * @comment a comment describing the state + */ + snapShot: function(comment) { + /* FIXME + var graph = new Graph(); + graph.nodes = jQuery.extend(true, {}, this.nodes); + graph.edges = jQuery.extend(true, {}, this.edges); + this.snapshots.push({comment: comment, graph: graph}); + */ + }, + removeNode: function(id) { + delete this.nodes[id]; + for(var i = 0; i < this.edges.length; i++) { + if (this.edges[i].source.id == id || this.edges[i].target.id == id) { + this.edges.splice(i, 1); + i--; + } + } + } +}; + +/* + * Node + */ +Graph.Node = function(id, node){ + node = node || {}; + node.id = id; + node.edges = []; + node.hide = function() { + this.hidden = true; + this.shape && this.shape.hide(); /* FIXME this is representation specific code and should be elsewhere */ + for(i in this.edges) + (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].hide && this.edges[i].hide(); + }; + node.show = function() { + this.hidden = false; + this.shape && this.shape.show(); + for(i in this.edges) + (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].show && this.edges[i].show(); + }; + return node; +}; +Graph.Node.prototype = { +}; + +/* + * Renderer base class + */ +Graph.Renderer = {}; + +/* + * Renderer implementation using RaphaelJS + */ +Graph.Renderer.Raphael = function(element, graph, width, height) { + this.width = width || 400; + this.height = height || 400; + var selfRef = this; + this.r = Raphael(element, this.width, this.height); + this.radius = 40; /* max dimension of a node */ + this.graph = graph; + this.mouse_in = false; + + /* TODO default node rendering function */ + if(!this.graph.render) { + this.graph.render = function() { + return; + } + } + + /* + * Dragging + */ + this.isDrag = false; + this.dragger = function (e) { + this.dx = e.clientX; + this.dy = e.clientY; + selfRef.isDrag = this; + this.set && this.set.animate({"fill-opacity": .1}, 200) && this.set.toFront(); + e.preventDefault && e.preventDefault(); + }; + + var d = document.getElementById(element); + d.onmousemove = function (e) { + e = e || window.event; + if (selfRef.isDrag) { + var bBox = selfRef.isDrag.set.getBBox(); + // TODO round the coordinates here (eg. for proper image representation) + var newX = e.clientX - selfRef.isDrag.dx + (bBox.x + bBox.width / 2); + var newY = e.clientY - selfRef.isDrag.dy + (bBox.y + bBox.height / 2); + /* prevent shapes from being dragged out of the canvas */ + var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0); + var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0); + selfRef.isDrag.set.translate(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy)); + // console.log(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy)); + for (var i in selfRef.graph.edges) { + selfRef.graph.edges[i].connection && selfRef.graph.edges[i].connection.draw(); + } + //selfRef.r.safari(); + selfRef.isDrag.dx = clientX; + selfRef.isDrag.dy = clientY; + } + }; + d.onmouseup = function () { + selfRef.isDrag && selfRef.isDrag.set.animate({"fill-opacity": .6}, 500); + selfRef.isDrag = false; + }; + this.draw(); +}; +Graph.Renderer.Raphael.prototype = { + translate: function(point) { + return [ + (point[0] - this.graph.layoutMinX) * this.factorX + this.radius, + (point[1] - this.graph.layoutMinY) * this.factorY + this.radius + ]; + }, + + rotate: function(point, length, angle) { + var dx = length * Math.cos(angle); + var dy = length * Math.sin(angle); + return [point[0]+dx, point[1]+dy]; + }, + + draw: function() { + this.factorX = (this.width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX); + this.factorY = (this.height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY); + for (i in this.graph.nodes) { + this.drawNode(this.graph.nodes[i]); + } + for (var i = 0; i < this.graph.edges.length; i++) { + this.drawEdge(this.graph.edges[i]); + } + }, + + drawNode: function(node) { + var point = this.translate([node.layoutPosX, node.layoutPosY]); + node.point = point; + + /* if node has already been drawn, move the nodes */ + if(node.shape) { + var oBBox = node.shape.getBBox(); + var opoint = { x: oBBox.x + oBBox.width / 2, y: oBBox.y + oBBox.height / 2}; + node.shape.translate(Math.round(point[0] - opoint.x), Math.round(point[1] - opoint.y)); + this.r.safari(); + return node; + }/* else, draw new nodes */ + + var shape; + + /* if a node renderer function is provided by the user, then use it + or the default render function instead */ + if(!node.render) { + node.render = function(r, node) { + /* the default node drawing */ + var color = Raphael.getColor(); + var ellipse = r.ellipse(0, 0, 30, 20).attr({fill: color, stroke: color, "stroke-width": 2}); + /* set DOM node ID */ + ellipse.node.id = node.label || node.id; + shape = r.set(). + push(ellipse). + push(r.text(0, 30, node.label || node.id)); + return shape; + } + } + /* or check for an ajax representation of the nodes */ + if(node.shapes) { + // TODO ajax representation evaluation + } + + shape = node.render(this.r, node).hide(); + + shape.attr({"fill-opacity": .6}); + /* re-reference to the node an element belongs to, needed for dragging all elements of a node */ + shape.items.forEach(function(item){ item.set = shape; item.node.style.cursor = "move"; }); + shape.mousedown(this.dragger); + + var box = shape.getBBox(); + shape.translate(Math.round(point[0]-(box.x+box.width/2)),Math.round(point[1]-(box.y+box.height/2))) + //console.log(box,point); + node.hidden || shape.show(); + node.shape = shape; + }, + drawEdge: function(edge) { + /* if this edge already exists the other way around and is undirected */ + if(edge.backedge) + return; + if(edge.source.hidden || edge.target.hidden) { + edge.connection && edge.connection.fg.hide() | edge.connection.bg && edge.connection.bg.hide(); + return; + } + /* if edge already has been drawn, only refresh the edge */ + if(!edge.connection) { + edge.style && edge.style.callback && edge.style.callback(edge); // TODO move this somewhere else + edge.connection = this.r.connection(edge.source.shape, edge.target.shape, edge.style); + return; + } + //FIXME showing doesn't work well + edge.connection.fg.show(); + edge.connection.bg && edge.connection.bg.show(); + edge.connection.draw(); + } +}; +Graph.Layout = {}; +Graph.Layout.Spring = function(graph) { + this.graph = graph; + this.iterations = 500; + this.maxRepulsiveForceDistance = 6; + this.k = 2; + this.c = 0.01; + this.maxVertexMovement = 0.5; + this.layout(); +}; +Graph.Layout.Spring.prototype = { + layout: function() { + this.layoutPrepare(); + for (var i = 0; i < this.iterations; i++) { + this.layoutIteration(); + } + this.layoutCalcBounds(); + }, + + layoutPrepare: function() { + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + node.layoutPosX = 0; + node.layoutPosY = 0; + node.layoutForceX = 0; + node.layoutForceY = 0; + } + + }, + + layoutCalcBounds: function() { + var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity; + + for (i in this.graph.nodes) { + var x = this.graph.nodes[i].layoutPosX; + var y = this.graph.nodes[i].layoutPosY; + + if(x > maxx) maxx = x; + if(x < minx) minx = x; + if(y > maxy) maxy = y; + if(y < miny) miny = y; + } + + this.graph.layoutMinX = minx; + this.graph.layoutMaxX = maxx; + this.graph.layoutMinY = miny; + this.graph.layoutMaxY = maxy; + }, + + layoutIteration: function() { + // Forces on nodes due to node-node repulsions + + var prev = new Array(); + for(var c in this.graph.nodes) { + var node1 = this.graph.nodes[c]; + for (var d in prev) { + var node2 = this.graph.nodes[prev[d]]; + this.layoutRepulsive(node1, node2); + + } + prev.push(c); + } + + // Forces on nodes due to edge attractions + for (var i = 0; i < this.graph.edges.length; i++) { + var edge = this.graph.edges[i]; + this.layoutAttractive(edge); + } + + // Move by the given force + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + var xmove = this.c * node.layoutForceX; + var ymove = this.c * node.layoutForceY; + + var max = this.maxVertexMovement; + if(xmove > max) xmove = max; + if(xmove < -max) xmove = -max; + if(ymove > max) ymove = max; + if(ymove < -max) ymove = -max; + + node.layoutPosX += xmove; + node.layoutPosY += ymove; + node.layoutForceX = 0; + node.layoutForceY = 0; + } + }, + + layoutRepulsive: function(node1, node2) { + if (typeof node1 == 'undefined' || typeof node2 == 'undefined') + return; + var dx = node2.layoutPosX - node1.layoutPosX; + var dy = node2.layoutPosY - node1.layoutPosY; + var d2 = dx * dx + dy * dy; + if(d2 < 0.01) { + dx = 0.1 * Math.random() + 0.1; + dy = 0.1 * Math.random() + 0.1; + var d2 = dx * dx + dy * dy; + } + var d = Math.sqrt(d2); + if(d < this.maxRepulsiveForceDistance) { + var repulsiveForce = this.k * this.k / d; + node2.layoutForceX += repulsiveForce * dx / d; + node2.layoutForceY += repulsiveForce * dy / d; + node1.layoutForceX -= repulsiveForce * dx / d; + node1.layoutForceY -= repulsiveForce * dy / d; + } + }, + + layoutAttractive: function(edge) { + var node1 = edge.source; + var node2 = edge.target; + + var dx = node2.layoutPosX - node1.layoutPosX; + var dy = node2.layoutPosY - node1.layoutPosY; + var d2 = dx * dx + dy * dy; + if(d2 < 0.01) { + dx = 0.1 * Math.random() + 0.1; + dy = 0.1 * Math.random() + 0.1; + var d2 = dx * dx + dy * dy; + } + var d = Math.sqrt(d2); + if(d > this.maxRepulsiveForceDistance) { + d = this.maxRepulsiveForceDistance; + d2 = d * d; + } + var attractiveForce = (d2 - this.k * this.k) / this.k; + if(edge.attraction == undefined) edge.attraction = 1; + attractiveForce *= Math.log(edge.attraction) * 0.5 + 1; + + node2.layoutForceX -= attractiveForce * dx / d; + node2.layoutForceY -= attractiveForce * dy / d; + node1.layoutForceX += attractiveForce * dx / d; + node1.layoutForceY += attractiveForce * dy / d; + } +}; + +Graph.Layout.Ordered = function(graph, order) { + this.graph = graph; + this.order = order; + this.layout(); +}; +Graph.Layout.Ordered.prototype = { + layout: function() { + this.layoutPrepare(); + this.layoutCalcBounds(); + }, + + layoutPrepare: function(order) { + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + node.layoutPosX = 0; + node.layoutPosY = 0; + } + var counter = 0; + for (i in this.order) { + var node = this.order[i]; + node.layoutPosX = counter; + node.layoutPosY = Math.random(); + counter++; + } + }, + + layoutCalcBounds: function() { + var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity; + + for (i in this.graph.nodes) { + var x = this.graph.nodes[i].layoutPosX; + var y = this.graph.nodes[i].layoutPosY; + + if(x > maxx) maxx = x; + if(x < minx) minx = x; + if(y > maxy) maxy = y; + if(y < miny) miny = y; + } + + this.graph.layoutMinX = minx; + this.graph.layoutMaxX = maxx; + + this.graph.layoutMinY = miny; + this.graph.layoutMaxY = maxy; + } +}; + +/* + * usefull JavaScript extensions, + */ + +function log(a) {console.log&&console.log(a);} + +/* + * Raphael Tooltip Plugin + * - attaches an element as a tooltip to another element + * + * Usage example, adding a rectangle as a tooltip to a circle: + * + * paper.circle(100,100,10).tooltip(paper.rect(0,0,20,30)); + * + * If you want to use more shapes, you'll have to put them into a set. + * + */ +Raphael.el.tooltip = function (tp) { + this.tp = tp; + this.tp.o = {x: 0, y: 0}; + this.tp.hide(); + this.hover( + function(event){ + this.mousemove(function(event){ + this.tp.translate(event.clientX - + this.tp.o.x,event.clientY - this.tp.o.y); + this.tp.o = {x: event.clientX, y: event.clientY}; + }); + this.tp.show().toFront(); + }, + function(event){ + this.tp.hide(); + this.unmousemove(); + }); + return this; +}; + +/* For IE */ +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fun /*, thisp*/) + { + var len = this.length; + if (typeof fun != "function") + throw new TypeError(); + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in this) + fun.call(thisp, this[i], i, this); + } + }; +} diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js new file mode 100644 index 0000000..8f81443 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js @@ -0,0 +1,81 @@ +/* + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +*/ + + +/* + Table pooler is a function to easy call XHR poller. + + new TablePooler(5,"/cgi-bin/bmx6-info", {'status':''}, "status_table", function(st){ + var table = Array() + table.push(st.first,st.second) + return table + } + Parameters are: + polling_time: time between pollings + json_url: the json url to fetch the data + json_call: the json call + output_table_id: the table where javascript will put the data + callback_function: the function that will be executed each polling_time + + The callback_function must return an array of arrays (matrix). + In the code st is the data obtained from the json call +*/ + + function TablePooler (time, jsonurl, getparams, table_id, callback) { + this.table = document.getElementById(table_id); + this.callback = callback; + this.jsonurl = jsonurl; + this.getparams = getparams; + this.time = time; + + this.clear = function(){ + /* clear all rows */ + while( this.table.rows.length > 1 ) this.table.deleteRow(1); + } + this.start = function(){ + XHR.poll(this.time, this.jsonurl, this.getparams, function(x, st){ + var data = this.callback(st); + var content, tr, td; + this.clear(); + for (var i = 0; i < data.length; i++){ + tr = this.table.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1); + + for (var j = 0; j < data[i].length; j++){ + td = tr.insertCell(-1); + if (data[i][j].length == 2) { + td.colSpan = data[i][j][1]; + content = data[i][j][0]; + } + else content = data[i][j]; + td.innerHTML = content; + } + } + }.bind(this)); + } + + + this.start(); + } + + + diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js new file mode 100644 index 0000000..8718b5b --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js @@ -0,0 +1,7 @@ +/* + * Raphael 1.3.1 - JavaScript Vector Library + * + * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +Raphael=(function(){var a=/[, ]+/,aO=/^(circle|rect|path|ellipse|text|image)$/,L=document,au=window,l={was:"Raphael" in au,is:au.Raphael},an=function(){if(an.is(arguments[0],"array")){var d=arguments[0],e=w[aW](an,d.splice(0,3+an.is(d[0],al))),S=e.set();for(var R=0,a0=d[m];R

";if(ag.childNodes[m]!=2){return null;}}an.svg=!(an.vml=an.type=="VML");aT[aY]=an[aY];an._id=0;an._oid=0;an.fn={};an.is=function(e,d){d=aZ.call(d);return((d=="object"||d=="undefined")&&typeof e==d)||(e==null&&d=="null")||aZ.call(aw.call(e).slice(8,-1))==d;};an.setWindow=function(d){au=d;L=au.document;};var aD=function(e){if(an.vml){var d=/^\s+|\s+$/g;aD=aj(function(R){var S;R=(R+at)[aP](d,at);try{var a0=new ActiveXObject("htmlfile");a0.write("");a0.close();S=a0.body;}catch(a2){S=createPopup().document.body;}var i=S.createTextRange();try{S.style.color=R;var a1=i.queryCommandValue("ForeColor");a1=((a1&255)<<16)|(a1&65280)|((a1&16711680)>>>16);return"#"+("000000"+a1[aA](16)).slice(-6);}catch(a2){return"none";}});}else{var E=L.createElement("i");E.title="Rapha\xebl Colour Picker";E.style.display="none";L.body[aL](E);aD=aj(function(i){E.style.color=i;return L.defaultView.getComputedStyle(E,at).getPropertyValue("color");});}return aD(e);};an.hsb2rgb=aj(function(a3,a1,a7){if(an.is(a3,"object")&&"h" in a3&&"s" in a3&&"b" in a3){a7=a3.b;a1=a3.s;a3=a3.h;}var R,S,a8;if(a7==0){return{r:0,g:0,b:0,hex:"#000"};}if(a3>1||a1>1||a7>1){a3/=255;a1/=255;a7/=255;}var a0=~~(a3*6),a4=(a3*6)-a0,E=a7*(1-a1),e=a7*(1-(a1*a4)),a9=a7*(1-(a1*(1-a4)));R=[a7,e,E,E,a9,a7,a7][a0];S=[a9,a7,a7,e,E,E,a9][a0];a8=[E,E,a9,a7,a7,e,E][a0];R*=255;S*=255;a8*=255;var a5={r:R,g:S,b:a8},d=(~~R)[aA](16),a2=(~~S)[aA](16),a6=(~~a8)[aA](16);d=d[aP](aU,"0");a2=a2[aP](aU,"0");a6=a6[aP](aU,"0");a5.hex="#"+d+a2+a6;return a5;},an);an.rgb2hsb=aj(function(d,e,a1){if(an.is(d,"object")&&"r" in d&&"g" in d&&"b" in d){a1=d.b;e=d.g;d=d.r;}if(an.is(d,"string")){var a3=an.getRGB(d);d=a3.r;e=a3.g;a1=a3.b;}if(d>1||e>1||a1>1){d/=255;e/=255;a1/=255;}var a0=g(d,e,a1),i=aI(d,e,a1),R,E,S=a0;if(i==a0){return{h:0,s:0,b:a0};}else{var a2=(a0-i);E=a2/a0;if(d==a0){R=(e-a1)/a2;}else{if(e==a0){R=2+((a1-d)/a2);}else{R=4+((d-e)/a2);}}R/=6;R<0&&R++;R>1&&R--;}return{h:R,s:E,b:S};},an);var aE=/,?([achlmqrstvxz]),?/gi;an._path2string=function(){return this.join(",")[aP](aE,"$1");};function aj(E,e,d){function i(){var R=Array[aY].slice.call(arguments,0),a0=R[az]("\u25ba"),S=i.cache=i.cache||{},a1=i.count=i.count||[];if(S[Q](a0)){return d?d(S[a0]):S[a0];}a1[m]>=1000&&delete S[a1.shift()];a1[f](a0);S[a0]=E[aW](e,R);return d?d(S[a0]):S[a0];}return i;}an.getRGB=aj(function(d){if(!d||!!((d=d+at).indexOf("-")+1)){return{r:-1,g:-1,b:-1,hex:"none",error:1};}if(d=="none"){return{r:-1,g:-1,b:-1,hex:"none"};}!(({hs:1,rg:1})[Q](d.substring(0,2))||d.charAt()=="#")&&(d=aD(d));var S,i,E,a2,a3,a0=d.match(x);if(a0){if(a0[2]){a2=G(a0[2].substring(5),16);E=G(a0[2].substring(3,5),16);i=G(a0[2].substring(1,3),16);}if(a0[3]){a2=G((a3=a0[3].charAt(3))+a3,16);E=G((a3=a0[3].charAt(2))+a3,16);i=G((a3=a0[3].charAt(1))+a3,16);}if(a0[4]){a0=a0[4][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);}if(a0[5]){a0=a0[5][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;}if(a0[6]){a0=a0[6][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);return an.hsb2rgb(i,E,a2);}if(a0[7]){a0=a0[7][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;return an.hsb2rgb(i,E,a2);}a0={r:i,g:E,b:a2};var e=(~~i)[aA](16),R=(~~E)[aA](16),a1=(~~a2)[aA](16);e=e[aP](aU,"0");R=R[aP](aU,"0");a1=a1[aP](aU,"0");a0.hex="#"+e+R+a1;return a0;}return{r:-1,g:-1,b:-1,hex:"none",error:1};},an);an.getColor=function(e){var i=this.getColor.start=this.getColor.start||{h:0,s:1,b:e||0.75},d=this.hsb2rgb(i.h,i.s,i.b);i.h+=0.075;if(i.h>1){i.h=0;i.s-=0.2;i.s<=0&&(this.getColor.start={h:0,s:1,b:i.b});}return d.hex;};an.getColor.reset=function(){delete this.start;};an.parsePathString=aj(function(d){if(!d){return null;}var i={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},e=[];if(an.is(d,"array")&&an.is(d[0],"array")){e=av(d);}if(!e[m]){(d+at)[aP](/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,function(R,E,a1){var a0=[],S=aZ.call(E);a1[aP](/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,function(a3,a2){a2&&a0[f](+a2);});while(a0[m]>=i[S]){e[f]([E][aS](a0.splice(0,i[S])));if(!i[S]){break;}}});}e[aA]=an._path2string;return e;});an.findDotsAtSegment=function(e,d,be,bc,a0,R,a2,a1,a8){var a6=1-a8,a5=aM(a6,3)*e+aM(a6,2)*3*a8*be+a6*3*a8*a8*a0+aM(a8,3)*a2,a3=aM(a6,3)*d+aM(a6,2)*3*a8*bc+a6*3*a8*a8*R+aM(a8,3)*a1,ba=e+2*a8*(be-e)+a8*a8*(a0-2*be+e),a9=d+2*a8*(bc-d)+a8*a8*(R-2*bc+d),bd=be+2*a8*(a0-be)+a8*a8*(a2-2*a0+be),bb=bc+2*a8*(R-bc)+a8*a8*(a1-2*R+bc),a7=(1-a8)*e+a8*be,a4=(1-a8)*d+a8*bc,E=(1-a8)*a0+a8*a2,i=(1-a8)*R+a8*a1,S=(90-ab.atan((ba-bd)/(a9-bb))*180/ab.PI);(ba>bd||a91){bi=ab.sqrt(by)*bi;bg=ab.sqrt(by)*bg;}var E=bi*bi,br=bg*bg,bt=(a4==S?-1:1)*ab.sqrt(ab.abs((E*br-E*bn*bn-br*bo*bo)/(E*bn*bn+br*bo*bo))),bd=bt*bi*bn/bg+(a9+a8)/2,bc=bt*-bg*bo/bi+(bE+bD)/2,a3=ab.asin(((bE-bc)/bg).toFixed(7)),a2=ab.asin(((bD-bc)/bg).toFixed(7));a3=a9a2){a3=a3-R*2;}if(!S&&a2>a3){a2=a2-R*2;}}else{a3=bb[0];a2=bb[1];bd=bb[2];bc=bb[3];}var a7=a2-a3;if(ab.abs(a7)>bf){var be=a2,bh=a8,a5=bD;a2=a3+bf*(S&&a2>a3?1:-1);a8=bd+bi*ab.cos(a2);bD=bc+bg*ab.sin(a2);bm=K(a8,bD,bi,bg,ba,0,S,bh,a5,[a2,be,bd,bc]);}a7=a2-a3;var a1=ab.cos(a3),bC=ab.sin(a3),a0=ab.cos(a2),bB=ab.sin(a2),bp=ab.tan(a7/4),bs=4/3*bi*bp,bq=4/3*bg*bp,bz=[a9,bE],bx=[a9+bs*bC,bE-bq*a1],bw=[a8+bs*bB,bD-bq*a0],bu=[a8,bD];bx[0]=2*bz[0]-bx[0];bx[1]=2*bz[1]-bx[1];if(bb){return[bx,bw,bu][aS](bm);}else{bm=[bx,bw,bu][aS](bm)[az]()[z](",");var bk=[];for(var bv=0,bl=bm[m];bv1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}a7=(a8-2*E+d)-(a2-2*a8+E);a4=2*(E-d)-2*(a8-E);a1=d-E;a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7;S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}return{min:{x:aI[aW](0,a6),y:aI[aW](0,a3)},max:{x:g[aW](0,a6),y:g[aW](0,a3)}};}),H=aj(function(a9,a4){var R=r(a9),a5=a4&&r(a4),a6={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},d={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},a0=function(ba,bb){var i,bc;if(!ba){return["C",bb.x,bb.y,bb.x,bb.y,bb.x,bb.y];}!(ba[0] in {T:1,Q:1})&&(bb.qx=bb.qy=null);switch(ba[0]){case"M":bb.X=ba[1];bb.Y=ba[2];break;case"A":ba=["C"][aS](K[aW](0,[bb.x,bb.y][aS](ba.slice(1))));break;case"S":i=bb.x+(bb.x-(bb.bx||bb.x));bc=bb.y+(bb.y-(bb.by||bb.y));ba=["C",i,bc][aS](ba.slice(1));break;case"T":bb.qx=bb.x+(bb.x-(bb.qx||bb.x));bb.qy=bb.y+(bb.y-(bb.qy||bb.y));ba=["C"][aS](aK(bb.x,bb.y,bb.qx,bb.qy,ba[1],ba[2]));break;case"Q":bb.qx=ba[1];bb.qy=ba[2];ba=["C"][aS](aK(bb.x,bb.y,ba[1],ba[2],ba[3],ba[4]));break;case"L":ba=["C"][aS](aX(bb.x,bb.y,ba[1],ba[2]));break;case"H":ba=["C"][aS](aX(bb.x,bb.y,ba[1],bb.y));break;case"V":ba=["C"][aS](aX(bb.x,bb.y,bb.x,ba[1]));break;case"Z":ba=["C"][aS](aX(bb.x,bb.y,bb.X,bb.Y));break;}return ba;},e=function(ba,bb){if(ba[bb][m]>7){ba[bb].shift();var bc=ba[bb];while(bc[m]){ba.splice(bb++,0,["C"][aS](bc.splice(0,6)));}ba.splice(bb,1);a7=g(R[m],a5&&a5[m]||0);}},E=function(be,bd,bb,ba,bc){if(be&&bd&&be[bc][0]=="M"&&bd[bc][0]!="M"){bd.splice(bc,0,["M",ba.x,ba.y]);bb.bx=0;bb.by=0;bb.x=be[bc][1];bb.y=be[bc][2];a7=g(R[m],a5&&a5[m]||0);}};for(var a2=0,a7=g(R[m],a5&&a5[m]||0);a23){return{container:1,x:arguments[0],y:arguments[1],width:arguments[2],height:arguments[3]};}}},aG=function(d,i){var e=this;for(var E in i){if(i[Q](E)&&!(E in d)){switch(typeof i[E]){case"function":(function(R){d[E]=d===e?R:function(){return R[aW](e,arguments);};})(i[E]);break;case"object":d[E]=d[E]||{};aG.call(this,d[E],i[E]);break;default:d[E]=i[E];break;}}}},ak=function(d,e){d==e.top&&(e.top=d.prev);d==e.bottom&&(e.bottom=d.next);d.next&&(d.next.prev=d.prev);d.prev&&(d.prev.next=d.next);},Y=function(d,e){if(e.top===d){return;}ak(d,e);d.next=null;d.prev=e.top;e.top.next=d;e.top=d;},k=function(d,e){if(e.bottom===d){return;}ak(d,e);d.next=e.bottom;d.prev=null;e.bottom.prev=d;e.bottom=d;},A=function(e,d,i){ak(e,i);d==i.top&&(i.top=e);d.next&&(d.next.prev=e);e.next=d.next;e.prev=d;d.next=e;},aq=function(e,d,i){ak(e,i);d==i.bottom&&(i.bottom=e);d.prev&&(d.prev.next=e);e.prev=d.prev;d.prev=e;e.next=d;},s=function(d){return function(){throw new Error("Rapha\xebl: you are calling to method \u201c"+d+"\u201d of removed object");};},ar=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;if(an.svg){aT[aY].svgns="http://www.w3.org/2000/svg";aT[aY].xlink="http://www.w3.org/1999/xlink";var O=function(d){return +d+(~~d===d)*0.5;},V=function(S){for(var e=0,E=S[m];e0.5)*2-1);aM(a1-0.5,2)+aM(S-0.5,2)>0.25&&(S=ab.sqrt(0.25-aM(a1-0.5,2))*ba+0.5)&&S!=0.5&&(S=S.toFixed(5)-0.00001*ba);}return at;});a7=a7[z](/\s*\-\s*/);if(a4=="linear"){var a0=a7.shift();a0=-W(a0);if(isNaN(a0)){return null;}var R=[0,0,ab.cos(a0*ab.PI/180),ab.sin(a0*ab.PI/180)],a6=1/(g(ab.abs(R[2]),ab.abs(R[3]))||1);R[2]*=a6;R[3]*=a6;if(R[2]<0){R[0]=-R[2];R[2]=0;}if(R[3]<0){R[1]=-R[3];R[3]=0;}}var a3=p(a7);if(!a3){return null;}var e=aJ(a4+"Gradient");e.id="r"+(an._id++)[aA](36);aJ(e,a4=="radial"?{fx:a1,fy:S}:{x1:R[0],y1:R[1],x2:R[2],y2:R[3]});d.defs[aL](e);for(var a2=0,a8=a3[m];a2a1.height)&&(a1.height=a0.y+a0.height-a1.y);(a0.x+a0.width-a1.x>a1.width)&&(a1.width=a0.x+a0.width-a1.x);}}E&&this.hide();return a1;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var R={};for(var E in this.attrs){if(this.attrs[Q](E)){R[E]=this.attrs[E];}}this._.rt.deg&&(R.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(R.scale=this.scale());R.gradient&&R.fill=="none"&&(R.fill=R.gradient)&&delete R.gradient;return R;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e in arguments[0]){if(arguments[0][Q](e)){d[arguments[0][e]]=this.attrs[arguments[0][e]];}}return d;}if(arguments[m]==2){var S={};S[arguments[0]]=arguments[1];aa(this,S);}else{if(arguments[m]==1&&an.is(arguments[0],"object")){aa(this,arguments[0]);}}return this;};ax[aY].toFront=function(){if(this.removed){return this;}this.node.parentNode[aL](this.node);var d=this.paper;d.top!=this&&Y(this,d);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);k(this,this.paper);var d=this.paper;}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}var e=d.node;if(e.nextSibling){e.parentNode.insertBefore(this.node,e.nextSibling);}else{e.parentNode[aL](this.node);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}var e=d.node;e.parentNode.insertBefore(this.node,e);aq(this,d,this.paper);return this;};var P=function(e,d,S,R){d=O(d);S=O(S);var E=aJ("circle");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:S,r:R,fill:"none",stroke:"#000"};i.type="circle";aJ(E,i.attrs);return i;};var aF=function(i,d,a1,e,S,a0){d=O(d);a1=O(a1);var R=aJ("rect");i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,r:a0||0,rx:a0||0,ry:a0||0,fill:"none",stroke:"#000"};E.type="rect";aJ(R,E.attrs);return E;};var ai=function(e,d,a0,S,R){d=O(d);a0=O(a0);var E=aJ("ellipse");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:a0,rx:S,ry:R,fill:"none",stroke:"#000"};i.type="ellipse";aJ(E,i.attrs);return i;};var o=function(i,a0,d,a1,e,S){var R=aJ("image");aJ(R,{x:d,y:a1,width:e,height:S,preserveAspectRatio:"none"});R.setAttributeNS(i.xlink,"href",a0);i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,src:a0};E.type="image";return E;};var X=function(e,d,S,R){var E=aJ("text");aJ(E,{x:d,y:S,"text-anchor":"middle"});e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={x:d,y:S,"text-anchor":"middle",text:R,font:j.font,stroke:"none",fill:"#000"};i.type="text";aa(i,i.attrs);return i;};var aV=function(e,d){this.width=e||this.width;this.height=d||this.height;this.canvas[v]("width",this.width);this.canvas[v]("height",this.height);return this;};var w=function(){var E=ao[aW](null,arguments),i=E&&E.container,e=E.x,a0=E.y,R=E.width,d=E.height;if(!i){throw new Error("SVG container not found.");}var S=aJ("svg");R=R||512;d=d||342;aJ(S,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:R,height:d});if(i==1){S.style.cssText="position:absolute;left:"+e+"px;top:"+a0+"px";L.body[aL](S);}else{if(i.firstChild){i.insertBefore(S,i.firstChild);}else{i[aL](S);}}i=new aT;i.width=R;i.height=d;i.canvas=S;aG.call(i,i,an.fn);i.clear();return i;};aT[aY].clear=function(){var d=this.canvas;while(d.firstChild){d.removeChild(d.firstChild);}this.bottom=this.top=null;(this.desc=aJ("desc"))[aL](L.createTextNode("Created with Rapha\xebl"));d[aL](this.desc);d[aL](this.defs=aJ("defs"));};aT[aY].remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if(an.vml){var aH=function(a8){var a5=/[ahqstv]/ig,a0=r;(a8+at).match(a5)&&(a0=H);a5=/[clmz]/g;if(a0==r&&!(a8+at).match(a5)){var e={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},R=/([clmz]),?([^clmz]*)/gi,S=/-?[^,\s-]+/g;var a4=(a8+at)[aP](R,function(a9,bb,i){var ba=[];i[aP](S,function(bc){ba[f](O(bc));});return e[bb]+ba;});return a4;}var a6=a0(a8),E,a4=[],d;for(var a2=0,a7=a6[m];a21&&(e=1);a7.opacity=e;}a8.fill&&(a7.on=true);if(a7.on==null||a8.fill=="none"){a7.on=false;}if(a7.on&&a8.fill){var i=a8.fill.match(c);if(i){a7.src=i[1];a7.type="tile";}else{a7.color=an.getRGB(a8.fill).hex;a7.src=at;a7.type="solid";if(an.getRGB(a8.fill).error&&(bd.type in {circle:1,ellipse:1}||(a8.fill+at).charAt()!="r")&&b(bd,a8.fill)){a9.fill="none";a9.gradient=a8.fill;}}}ba&&a6[aL](a7);var R=(a6.getElementsByTagName("stroke")&&a6.getElementsByTagName("stroke")[0]),bb=false;!R&&(bb=R=ah("stroke"));if((a8.stroke&&a8.stroke!="none")||a8["stroke-width"]||a8["stroke-opacity"]!=null||a8["stroke-dasharray"]||a8["stroke-miterlimit"]||a8["stroke-linejoin"]||a8["stroke-linecap"]){R.on=true;}(a8.stroke=="none"||R.on==null||a8.stroke==0||a8["stroke-width"]==0)&&(R.on=false);R.on&&a8.stroke&&(R.color=an.getRGB(a8.stroke).hex);var e=((+a9["stroke-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1),a4=(W(a8["stroke-width"])||1)*0.75;e<0&&(e=0);e>1&&(e=1);a8["stroke-width"]==null&&(a4=a9["stroke-width"]);a8["stroke-width"]&&(R.weight=a4);a4&&a4<1&&(e*=a4)&&(R.weight=1);R.opacity=e;a8["stroke-linejoin"]&&(R.joinstyle=a8["stroke-linejoin"]||"miter");R.miterlimit=a8["stroke-miterlimit"]||8;a8["stroke-linecap"]&&(R.endcap=a8["stroke-linecap"]=="butt"?"flat":a8["stroke-linecap"]=="square"?"square":"round");if(a8["stroke-dasharray"]){var a5={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};R.dashstyle=a5[Q](a8["stroke-dasharray"])?a5[a8["stroke-dasharray"]]:at;}bb&&a6[aL](R);}if(bd.type=="text"){var a0=bd.paper.span.style;a9.font&&(a0.font=a9.font);a9["font-family"]&&(a0.fontFamily=a9["font-family"]);a9["font-size"]&&(a0.fontSize=a9["font-size"]);a9["font-weight"]&&(a0.fontWeight=a9["font-weight"]);a9["font-style"]&&(a0.fontStyle=a9["font-style"]);bd.node.string&&(bd.paper.span.innerHTML=(bd.node.string+at)[aP](/"));bd.W=a9.w=bd.paper.span.offsetWidth;bd.H=a9.h=bd.paper.span.offsetHeight;bd.X=a9.x;bd.Y=a9.y+O(bd.H/2);switch(a9["text-anchor"]){case"start":bd.node.style["v-text-align"]="left";bd.bbx=O(bd.W/2);break;case"end":bd.node.style["v-text-align"]="right";bd.bbx=-O(bd.W/2);break;default:bd.node.style["v-text-align"]="center";break;}}};var b=function(d,a1){d.attrs=d.attrs||{};var a2=d.attrs,a4=d.node.getElementsByTagName("fill"),S="linear",a0=".5 .5";d.attrs.gradient=a1;a1=(a1+at)[aP](ar,function(a6,a7,i){S="radial";if(a7&&i){a7=W(a7);i=W(i);aM(a7-0.5,2)+aM(i-0.5,2)>0.25&&(i=ab.sqrt(0.25-aM(a7-0.5,2))*((i>0.5)*2-1)+0.5);a0=a7+am+i;}return at;});a1=a1[z](/\s*\-\s*/);if(S=="linear"){var e=a1.shift();e=-W(e);if(isNaN(e)){return null;}}var R=p(a1);if(!R){return null;}d=d.shape||d.node;a4=a4[0]||ah("fill");if(R[m]){a4.on=true;a4.method="none";a4.type=(S=="radial")?"gradientradial":"gradient";a4.color=R[0].color;a4.color2=R[R[m]-1].color;var a5=[];for(var E=0,a3=R[m];E');};}catch(af){ah=function(d){return L.createElement("<"+d+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');};}var w=function(){var i=ao[aW](null,arguments),d=i.container,a2=i.height,a3,e=i.width,a1=i.x,a0=i.y;if(!d){throw new Error("VML container not found.");}var R=new aT,S=R.canvas=L.createElement("div"),E=S.style;e=e||512;a2=a2||342;e==+e&&(e+="px");a2==+a2&&(a2+="px");R.width=1000;R.height=1000;R.coordsize="1000 1000";R.coordorigin="0 0";R.span=L.createElement("span");R.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";S[aL](R.span);E.cssText=an.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden",e,a2);if(d==1){L.body[aL](S);E.left=a1+"px";E.top=a0+"px";}else{d.style.width=e;d.style.height=a2;if(d.firstChild){d.insertBefore(S,d.firstChild);}else{d[aL](S);}}aG.call(R,R,an.fn);return R;};aT[aY].clear=function(){this.canvas.innerHTML=at;this.span=L.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[aL](this.span);this.bottom=this.top=null;};aT[aY].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if((/^Apple|^Google/).test(navigator.vendor)&&!(navigator.userAgent.indexOf("Version/4.0")+1)){aT[aY].safari=function(){var d=this.rect(-99,-99,this.width+99,this.height+99);setTimeout(function(){d.remove();});};}else{aT[aY].safari=function(){};}var ae=(function(){if(L.addEventListener){return function(R,i,e,d){var E=function(S){return e.call(d,S);};R.addEventListener(i,E,false);return function(){R.removeEventListener(i,E,false);return true;};};}else{if(L.attachEvent){return function(S,E,i,e){var R=function(a0){return i.call(e,a0||au.event);};S.attachEvent("on"+E,R);var d=function(){S.detachEvent("on"+E,R);return true;};return d;};}}})();for(var ac=F[m];ac--;){(function(d){ax[aY][d]=function(e){if(an.is(e,"function")){this.events=this.events||[];this.events.push({name:d,f:e,unbind:ae(this.shape||this.node,d,e,this)});}return this;};ax[aY]["un"+d]=function(E){var i=this.events,e=i[m];while(e--){if(i[e].name==d&&i[e].f==E){i[e].unbind();i.splice(e,1);!i.length&&delete this.events;return this;}}return this;};})(F[ac]);}ax[aY].hover=function(e,d){return this.mouseover(e).mouseout(d);};ax[aY].unhover=function(e,d){return this.unmouseover(e).unmouseout(d);};aT[aY].circle=function(d,i,e){return P(this,d||0,i||0,e||0);};aT[aY].rect=function(d,R,e,i,E){return aF(this,d||0,R||0,e||0,i||0,E||0);};aT[aY].ellipse=function(d,E,i,e){return ai(this,d||0,E||0,i||0,e||0);};aT[aY].path=function(d){d&&!an.is(d,"string")&&!an.is(d[0],"array")&&(d+=at);return q(an.format[aW](an,arguments),this);};aT[aY].image=function(E,d,R,e,i){return o(this,E||"about:blank",d||0,R||0,e||0,i||0);};aT[aY].text=function(d,i,e){return X(this,d||0,i||0,e||at);};aT[aY].set=function(d){arguments[m]>1&&(d=Array[aY].splice.call(arguments,0,arguments[m]));return new T(d);};aT[aY].setSize=aV;aT[aY].top=aT[aY].bottom=null;aT[aY].raphael=an;function u(){return this.x+am+this.y;}ax[aY].scale=function(a6,a5,E,e){if(a6==null&&a5==null){return{x:this._.sx,y:this._.sy,toString:u};}a5=a5||a6;!+a5&&(a5=a6);var ba,a8,a9,a7,bm=this.attrs;if(a6!=0){var a4=this.getBBox(),a1=a4.x+a4.width/2,R=a4.y+a4.height/2,bl=a6/this._.sx,bk=a5/this._.sy;E=(+E||E==0)?E:a1;e=(+e||e==0)?e:R;var a3=~~(a6/ab.abs(a6)),a0=~~(a5/ab.abs(a5)),be=this.node.style,bo=E+(a1-E)*bl,bn=e+(R-e)*bk;switch(this.type){case"rect":case"image":var a2=bm.width*a3*bl,bd=bm.height*a0*bk;this.attr({height:bd,r:bm.r*aI(a3*bl,a0*bk),width:a2,x:bo-a2/2,y:bn-bd/2});break;case"circle":case"ellipse":this.attr({rx:bm.rx*a3*bl,ry:bm.ry*a0*bk,r:bm.r*aI(a3*bl,a0*bk),cx:bo,cy:bn});break;case"path":var bg=ad(bm.path),bh=true;for(var bj=0,bc=bg[m];bjS){if(e&&!a8.start){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);R+=["C",a6.start.x,a6.start.y,a6.m.x,a6.m.y,a6.x,a6.y];if(a0){return R;}a8.start=R;R=["M",a6.x,a6.y+"C",a6.n.x,a6.n.y,a6.end.x,a6.end.y,E[5],E[6]][az]();a3+=a1;a5=+E[5];a4=+E[6];continue;}if(!d&&!e){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);return{x:a6.x,y:a6.y,alpha:a6.alpha};}}a3+=a1;a5=+E[5];a4=+E[6];}R+=E;}a8.end=R;a6=d?a3:e?a8:an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],1);a6.alpha&&(a6={x:a6.x,y:a6.y,alpha:a6.alpha});return a6;};},n=aj(function(E,d,a0,S,a6,a5,a4,a3){var R={x:0,y:0},a2=0;for(var a1=0;a1<1.01;a1+=0.01){var e=M(E,d,a0,S,a6,a5,a4,a3,a1);a1&&(a2+=ab.sqrt(aM(R.x-e.x,2)+aM(R.y-e.y,2)));R=e;}return a2;});var ap=aB(1),C=aB(),J=aB(0,1);ax[aY].getTotalLength=function(){if(this.type!="path"){return;}return ap(this.attrs.path);};ax[aY].getPointAtLength=function(d){if(this.type!="path"){return;}return C(this.attrs.path,d);};ax[aY].getSubpath=function(i,e){if(this.type!="path"){return;}if(ab.abs(this.getTotalLength()-e)<0.000001){return J(this.attrs.path,i).end;}var d=J(this.attrs.path,e,1);return i?J(d,i).end:d;};an.easing_formulas={linear:function(d){return d;},"<":function(d){return aM(d,3);},">":function(d){return aM(d-1,3)+1;},"<>":function(d){d=d*2;if(d<1){return aM(d,3)/2;}d-=2;return(aM(d,3)+2)/2;},backIn:function(e){var d=1.70158;return e*e*((d+1)*e-d);},backOut:function(e){e=e-1;var d=1.70158;return e*e*((d+1)*e+d)+1;},elastic:function(i){if(i==0||i==1){return i;}var e=0.3,d=e/4;return aM(2,-10*i)*ab.sin((i-d)*(2*ab.PI)/e)+1;},bounce:function(E){var e=7.5625,i=2.75,d;if(E<(1/i)){d=e*E*E;}else{if(E<(2/i)){E-=(1.5/i);d=e*E*E+0.75;}else{if(E<(2.5/i)){E-=(2.25/i);d=e*E*E+0.9375;}else{E-=(2.625/i);d=e*E*E+0.984375;}}}return d;}};var I={length:0},aR=function(){var a2=+new Date;for(var be in I){if(be!="length"&&I[Q](be)){var bj=I[be];if(bj.stop){delete I[be];I[m]--;continue;}var a0=a2-bj.start,bb=bj.ms,ba=bj.easing,bf=bj.from,a7=bj.diff,E=bj.to,a6=bj.t,a9=bj.prev||0,a1=bj.el,R=bj.callback,a8={},d;if(a0255?255:(d<0?0:d);},t=function(d,i){if(d==null){return{x:this._.tx,y:this._.ty,toString:u};}this._.tx+=+d;this._.ty+=+i;switch(this.type){case"circle":case"ellipse":this.attr({cx:+d+this.attrs.cx,cy:+i+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+d+this.attrs.x,y:+i+this.attrs.y});break;case"path":var e=ad(this.attrs.path);e[0][1]+=+d;e[0][2]+=+i;this.attr({path:e});break;}return this;};ax[aY].animateWith=function(e,i,d,R,E){I[e.id]&&(i.start=I[e.id].start);return this.animate(i,d,R,E);};ax[aY].animateAlong=ay();ax[aY].animateAlongBack=ay(1);function ay(d){return function(E,i,e,S){var R={back:d};an.is(e,"function")?(S=e):(R.rot=e);E&&E.constructor==ax&&(E=E.attrs.path);E&&(R.along=E);return this.animate(R,i,S);};}ax[aY].onAnimation=function(d){this._run=d||0;return this;};ax[aY].animate=function(be,a5,a4,E){if(an.is(a4,"function")||!a4){E=a4||null;}var a9={},e={},a2={};for(var a6 in be){if(be[Q](a6)){if(Z[Q](a6)){a9[a6]=this.attr(a6);(a9[a6]==null)&&(a9[a6]=j[a6]);e[a6]=be[a6];switch(Z[a6]){case"along":var bc=ap(be[a6]),a7=C(be[a6],bc*!!be.back),R=this.getBBox();a2[a6]=bc/a5;a2.tx=R.x;a2.ty=R.y;a2.sx=a7.x;a2.sy=a7.y;e.rot=be.rot;e.back=be.back;e.len=bc;be.rot&&(a2.r=W(this.rotate())||0);break;case"number":a2[a6]=(e[a6]-a9[a6])/a5;break;case"colour":a9[a6]=an.getRGB(a9[a6]);var a8=an.getRGB(e[a6]);a2[a6]={r:(a8.r-a9[a6].r)/a5,g:(a8.g-a9[a6].g)/a5,b:(a8.b-a9[a6].b)/a5};break;case"path":var S=H(a9[a6],e[a6]);a9[a6]=S[0];var a3=S[1];a2[a6]=[];for(var bb=0,a1=a9[a6][m];bb +// +// Math.seedrandom('yipee'); Sets Math.random to a function that is +// initialized using the given explicit seed. +// +// Math.seedrandom(); Sets Math.random to a function that is +// seeded using the current time, dom state, +// and other accumulated local entropy. +// The generated seed string is returned. +// +// Math.seedrandom('yowza', true); +// Seeds using the given explicit seed mixed +// together with accumulated entropy. +// +// +// Seeds using physical random bits downloaded +// from random.org. +// +// Examples: +// +// Math.seedrandom("hello"); // Use "hello" as the seed. +// document.write(Math.random()); // Always 0.5463663768140734 +// document.write(Math.random()); // Always 0.43973793770592234 +// var rng1 = Math.random; // Remember the current prng. +// +// var autoseed = Math.seedrandom(); // New prng with an automatic seed. +// document.write(Math.random()); // Pretty much unpredictable. +// +// Math.random = rng1; // Continue "hello" prng sequence. +// document.write(Math.random()); // Always 0.554769432473455 +// +// Math.seedrandom(autoseed); // Restart at the previous seed. +// document.write(Math.random()); // Repeat the 'unpredictable' value. +// +// Notes: +// +// Each time seedrandom('arg') is called, entropy from the passed seed +// is accumulated in a pool to help generate future seeds for the +// zero-argument form of Math.seedrandom, so entropy can be injected over +// time by calling seedrandom with explicit data repeatedly. +// +// On speed - This javascript implementation of Math.random() is about +// 3-10x slower than the built-in Math.random() because it is not native +// code, but this is typically fast enough anyway. Seeding is more expensive, +// especially if you use auto-seeding. Some details (timings on Chrome 4): +// +// Our Math.random() - avg less than 0.002 milliseconds per call +// seedrandom('explicit') - avg less than 0.5 milliseconds per call +// seedrandom('explicit', true) - avg less than 2 milliseconds per call +// seedrandom() - avg about 38 milliseconds per call +// +// LICENSE (BSD): +// +// Copyright 2010 David Bau, all rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of this module nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/** + * All code is in an anonymous closure to keep the global namespace clean. + * + * @param {number=} overflow + * @param {number=} startdenom + */ +(function (pool, math, width, chunks, significance, overflow, startdenom) { + + +// +// seedrandom() +// This is the seedrandom function described above. +// +math['seedrandom'] = function seedrandom(seed, use_entropy) { + var key = []; + var arc4; + + // Flatten the seed string or build one from local entropy if needed. + seed = mixkey(flatten( + use_entropy ? [seed, pool] : + arguments.length ? seed : + [new Date().getTime(), pool, window], 3), key); + + // Use the seed to initialize an ARC4 generator. + arc4 = new ARC4(key); + + // Mix the randomness into accumulated entropy. + mixkey(arc4.S, pool); + + // Override Math.random + + // This function returns a random double in [0, 1) that contains + // randomness in every bit of the mantissa of the IEEE 754 value. + + math['random'] = function random() { // Closure to return a random double: + var n = arc4.g(chunks); // Start with a numerator n < 2 ^ 48 + var d = startdenom; // and denominator d = 2 ^ 48. + var x = 0; // and no 'extra last byte'. + while (n < significance) { // Fill up all significant digits by + n = (n + x) * width; // shifting numerator and + d *= width; // denominator and generating a + x = arc4.g(1); // new least-significant-byte. + } + while (n >= overflow) { // To avoid rounding up, before adding + n /= 2; // last byte, shift everything + d /= 2; // right using integer math until + x >>>= 1; // we have exactly the desired bits. + } + return (n + x) / d; // Form the number within [0, 1). + }; + + // Return the seed that was used + return seed; +}; + +// +// ARC4 +// +// An ARC4 implementation. The constructor takes a key in the form of +// an array of at most (width) integers that should be 0 <= x < (width). +// +// The g(count) method returns a pseudorandom integer that concatenates +// the next (count) outputs from ARC4. Its return value is a number x +// that is in the range 0 <= x < (width ^ count). +// +/** @constructor */ +function ARC4(key) { + var t, u, me = this, keylen = key.length; + var i = 0, j = me.i = me.j = me.m = 0; + me.S = []; + me.c = []; + + // The empty key [] is treated as [0]. + if (!keylen) { key = [keylen++]; } + + // Set up S using the standard key scheduling algorithm. + while (i < width) { me.S[i] = i++; } + for (i = 0; i < width; i++) { + t = me.S[i]; + j = lowbits(j + t + key[i % keylen]); + u = me.S[j]; + me.S[i] = u; + me.S[j] = t; + } + + // The "g" method returns the next (count) outputs as one number. + me.g = function getnext(count) { + var s = me.S; + var i = lowbits(me.i + 1); var t = s[i]; + var j = lowbits(me.j + t); var u = s[j]; + s[i] = u; + s[j] = t; + var r = s[lowbits(t + u)]; + while (--count) { + i = lowbits(i + 1); t = s[i]; + j = lowbits(j + t); u = s[j]; + s[i] = u; + s[j] = t; + r = r * width + s[lowbits(t + u)]; + } + me.i = i; + me.j = j; + return r; + }; + // For robust unpredictability discard an initial batch of values. + // See http://www.rsa.com/rsalabs/node.asp?id=2009 + me.g(width); +} + +// +// flatten() +// Converts an object tree to nested arrays of strings. +// +/** @param {Object=} result + * @param {string=} prop */ +function flatten(obj, depth, result, prop) { + result = []; + if (depth && typeof(obj) == 'object') { + for (prop in obj) { + if (prop.indexOf('S') < 5) { // Avoid FF3 bug (local/sessionStorage) + try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} + } + } + } + return result.length ? result : '' + obj; +} + +// +// mixkey() +// Mixes a string seed into a key that is an array of integers, and +// returns a shortened string seed that is equivalent to the result key. +// +/** @param {number=} smear + * @param {number=} j */ +function mixkey(seed, key, smear, j) { + seed += ''; // Ensure the seed is a string + smear = 0; + for (j = 0; j < seed.length; j++) { + key[lowbits(j)] = + lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j)); + } + seed = ''; + for (j in key) { seed += String.fromCharCode(key[j]); } + return seed; +} + +// +// lowbits() +// A quick "n mod width" for width a power of 2. +// +function lowbits(n) { return n & (width - 1); } + +// +// The following constants are related to IEEE 754 limits. +// +startdenom = math.pow(width, chunks); +significance = math.pow(2, significance); +overflow = significance * 2; + +// +// When seedrandom.js is loaded, we immediately mix a few bits +// from the built-in RNG into the entropy pool. Because we do +// not want to intefere with determinstic PRNG state later, +// seedrandom will not call math.random on its own again after +// initialization. +// +mixkey(math.random(), pool); + +// End anonymous scope, and pass initial values. +})( + [], // pool: entropy pool starts empty + Math, // math: package containing random, pow, and seedrandom + 256, // width: each RC4 output is 0 <= x < 256 + 6, // chunks: at least six RC4 outputs for each double + 52 // significance: there are 52 significant digits in a double +); diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png new file mode 100644 index 0000000..58977ff Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png differ diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css b/luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css new file mode 100644 index 0000000..d8191a7 --- /dev/null +++ b/luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css @@ -0,0 +1,22 @@ + table { + width:90%; + border-top:1px solid #e5eaf8; + border-right:1px solid #e5eaf8; + margin:1em auto; + border-collapse:collapse; + } + + td { + color:#678197; + border-bottom:1px solid #e6eff8; + border-left:1px solid #e6eff8; + padding:.3em 1em; + text-align:center; + } + th { + background:#f4f9fe; + text-align:center; + font:bold 1.2em/2em Century Gothic,Trebuchet MS,Arial,Helvetica,sans-serif; + color:#66a3d3; + } + diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png new file mode 100644 index 0000000..4195b08 Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png differ diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png new file mode 100644 index 0000000..29b53c9 Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png differ diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png new file mode 100644 index 0000000..f5f3105 Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png differ diff --git a/nodogsplash/Makefile b/nodogsplash/Makefile new file mode 100644 index 0000000..d1d9212 --- /dev/null +++ b/nodogsplash/Makefile @@ -0,0 +1,56 @@ +# +# Copyright (C) 2013 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:=nodogsplash +PKG_FIXUP:=autoreconf +PKG_VERSION:=0.9_beta9.9.9-pre +PKG_RELEASE:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/ +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=git://github.com/nodogsplash/nodogsplash.git +PKG_SOURCE_VERSION:=master +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + + +define Package/nodogsplash + SUBMENU:=Captive Portals + SECTION:=net + CATEGORY:=Network + DEPENDS:=+libpthread +iptables-mod-ipopt + TITLE:=Open public network gateway daemon + URL:=https://github.com/nodogsplash/nodogsplash +endef + +define Package/nodogsplash/description + Nodogsplash offers a simple way to open a free hotspot providing + restricted access to an internet connection. +endef + +define Package/nodogsplash/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/nodogsplash $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/ndsctl $(1)/usr/bin/ + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) files/nodogsplash.init $(1)/etc/init.d/$(PKG_NAME) + + $(INSTALL_DIR) $(1)/etc/$(PKG_NAME)/htdocs/images + $(CP) $(PKG_BUILD_DIR)/resources/nodogsplash.conf $(1)/etc/$(PKG_NAME)/ + $(CP) $(PKG_BUILD_DIR)/resources/splash.html $(1)/etc/$(PKG_NAME)/htdocs/ + $(CP) $(PKG_BUILD_DIR)/resources/infoskel.html $(1)/etc/$(PKG_NAME)/htdocs/ + $(CP) $(PKG_BUILD_DIR)/resources/splash.jpg $(1)/etc/$(PKG_NAME)/htdocs/images/ +endef + + +$(eval $(call BuildPackage,nodogsplash)) + diff --git a/nodogsplash/files/nodogsplash.init b/nodogsplash/files/nodogsplash.init new file mode 100644 index 0000000..7eba60b --- /dev/null +++ b/nodogsplash/files/nodogsplash.init @@ -0,0 +1,176 @@ +#!/bin/sh /etc/rc.common +# +# description: Startup/shutdown script for nodogsplash captive portal +# +# P. Kube 2007 +# +# (Based on wifidog startup script +# Date : 2004-08-25 +# Version : 1.0 +# Comment by that author: Could be better, but it's working as expected) +# + + +IPT=/usr/sbin/iptables +WD_DIR=/usr/bin +OPTIONS="" +START=65 +STOP=65 +# -s -d 5 runs in background, with level 5 (not so verbose) messages to syslog +# -f -d 7 runs in foreground, with level 7 (verbose) debug messages to terminal +# N.B.: -f will fail if starting at boot from rcS +#OPTIONS="-s -d 5" + +start() { + echo "Starting nodogsplash ... " + if $WD_DIR/ndsctl status 2> /dev/null; then + echo "FAILED: nodogsplash already running" + else + if test_module && $WD_DIR/nodogsplash $OPTIONS; then + echo "OK: nodogsplash started" + else + echo "FAILED: nodogsplash exited with non 0 status" + fi + fi +} + +stop() { + echo "Stopping nodogsplash ... " + if $WD_DIR/ndsctl status 2> /dev/null; then + if $WD_DIR/ndsctl stop; then + echo "OK: nodogsplash stopped" + else + echo "FAILED: ndsctl stop exited with non 0 status" + fi + else + echo "FAILED: nodogsplash was not running" + fi +} + +status() { + $WD_DIR/ndsctl status +} + +test_module() { + + ### Test ipt_mark with iptables + test_ipt_mark () { + ($IPT -A FORWARD -m mark --mark 2 -j ACCEPT 2>&1) > /dev/null + IPTABLES_OK=$? + if [ "$IPTABLES_OK" -eq 0 ]; then + ($IPT -D FORWARD -m mark --mark 2 -j ACCEPT 2>&1) > /dev/null + return 0 + else + return 1 + fi + } + ### Test ipt_mac with iptables + test_ipt_mac () { + ($IPT -A INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT 2>&1) > /dev/null + IPTABLES_OK=$? + if [ "$IPTABLES_OK" -eq 0 ]; then + ($IPT -D INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT 2>&1) > /dev/null + return 0 + else + return 1 + fi + } + + ### Test ipt_IMQ with iptables + test_ipt_IMQ () { + ($IPT -t mangle -A PREROUTING -j IMQ --todev 0 2>&1) > /dev/null + IPTABLES_OK=$? + if [ "$IPTABLES_OK" -eq 0 ]; then + ($IPT -t mangle -D PREROUTING -j IMQ --todev 0 2>&1) > /dev/null + return 0 + else + return 1 + fi + } + + ### Test imq with ip + test_imq () { + (ip link set imq0 up 2>&1) > /dev/null + IMQ0_OK=$? + (ip link set imq1 up 2>&1) > /dev/null + IMQ1_OK=$? + if [ "$IMQ0_OK" -eq 0 -a "$IMQ1_OK" -eq 0 ]; then + (ip link set imq0 down 2>&1) > /dev/null + (ip link set imq1 down 2>&1) > /dev/null + return 0 + else + return 1 + fi + } + + ### Test sch_htb with tc; requires imq0 + test_sch_htb () { + (tc qdisc del dev imq0 root 2>&1) > /dev/null + (tc qdisc add dev imq0 root htb 2>&1) > /dev/null + TC_OK=$? + if [ "$TC_OK" -eq 0 ]; then + (tc qdisc del dev imq0 root 2>&1) > /dev/null + return 0 + else + return 1 + fi + } + + + ### Find a module on disk + module_exists () { + EXIST=$(find /lib/modules/`uname -r` -name $1.*o 2> /dev/null) + if [ -n "$EXIST" ]; then + return 0 + else + return 1 + fi + } + + ### Test if a module is in memory + module_in_memory () { + MODULE=$(lsmod | grep $1 | awk '{print $1}') + if [ "$MODULE" = "$1" ]; then + return 0 + else + return 1 + fi + } + + ### Test functionality of a module; load if necessary + do_module_tests () { + echo " Testing module $1 $2" + "test_$1" + if [ $? -ne 0 ]; then + echo " Module $1 $2 needed" + echo " Scanning disk for $1 module" + module_exists $1 + if [ $? -ne 0 ]; then + echo " $1 module missing: please install it" + exit 1 + else + echo " $1 exists, trying to load" + insmod $1 $2 > /dev/null + if [ $? -ne 0 ]; then + echo " Error: insmod $1 $2 failed" + exit 1 + else + echo " $1 $2 loaded successfully" + fi + fi + else + echo " $1 is working" + fi + + } + + echo " Testing required modules" + + do_module_tests "ipt_mac" + do_module_tests "ipt_mark" + # if not using traffic control, + # you can comment out the following 3 lines: + do_module_tests "imq" "numdevs=2" + do_module_tests "ipt_IMQ" + do_module_tests "sch_htb" +} diff --git a/olsrd/Makefile b/olsrd/Makefile new file mode 100644 index 0000000..c1ca224 --- /dev/null +++ b/olsrd/Makefile @@ -0,0 +1,290 @@ +# +# Copyright (C) 2009-2012 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:=olsrd +PKG_VERSION:=0.6.5.4 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 +PKG_SOURCE_URL:=http://www.olsr.org/releases/0.6 + +PKG_MD5SUM:=44689e790359f9363e5ebb924c548730 +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +TARGET_CFLAGS += $(FPIC) + +define Package/olsrd/template + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + MAINTAINER:=Saverio Proto + TITLE:=OLSR (Optimized Link State Routing) daemon + URL:=http://www.olsr.org/ +endef + +define Package/olsrd + $(call Package/olsrd/template) + MENU:=1 + DEPENDS:=+libpthread +endef + +define Package/olsrd/conffiles +/etc/config/olsrd +endef + +define Package/olsrd-mod-arprefresh + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Kernel ARP cache refresh plugin +endef + +define Package/olsrd-mod-dot-draw + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Dot topology information plugin +endef + +define Package/olsrd-mod-bmf + $(call Package/olsrd/template) + DEPENDS:=olsrd +kmod-tun + TITLE:=Basic multicast forwarding plugin +endef + +define Package/olsrd-mod-dyn-gw + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Dynamic internet gateway plugin +endef + +define Package/olsrd-mod-dyn-gw-plain + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Dynamic internet gateway plain plugin +endef + +define Package/olsrd-mod-httpinfo + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Small informative web server plugin +endef + +define Package/olsrd-mod-jsoninfo + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Small informative plugin with JSON output +endef + +define Package/olsrd-mod-mdns + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=MDNS/Zeroconf/Bonjour packet distribution +endef + +define Package/olsrd-mod-nameservice + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Lightweight hostname resolver plugin +endef + +define Package/olsrd-mod-p2pd + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Peer to Peer Discovery plugin +endef + + +define Package/olsrd-mod-pgraph + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=output network topology for pgraph +endef + +define Package/olsrd-mod-pud + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Position Update Distribution plugin +endef + +define Package/olsrd-mod-quagga + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Quagga plugin +endef + +define Package/olsrd-mod-secure + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Message signing plugin to secure routing domain +endef + +define Package/olsrd-mod-sgwdynspeed + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Smart Gateway dynamic speed plugin +endef + +define Package/olsrd-mod-txtinfo + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Small informative web server plugin +endef + +define Package/olsrd-mod-watchdog + $(call Package/olsrd/template) + DEPENDS:=olsrd + TITLE:=Watchdog plugin +endef + +define Package/olsrd-mod-pud/conffiles +/etc/olsrd.d/olsrd.pud.position.conf +endef + +define Package/olsrd-mod-secure/conffiles +/etc/olsrd.d/olsrd_secure_key +endef + +define Package/olsrd-mod-sgwdynspeed/conffiles +/etc/olsrd.d/olsrd.sgw.speed.conf +endef + +MAKE_FLAGS+= \ + NO_DEBUG_MESSAGES=1 \ + OS="linux" \ + DESTDIR="$(PKG_INSTALL_DIR)" \ + STRIP="true" \ + INSTALL_LIB="true" \ + SUBDIRS="arprefresh bmf dot_draw dyn_gw dyn_gw_plain httpinfo jsoninfo mdns nameservice p2pd pgraph pud quagga secure sgwdynspeed txtinfo watchdog" + +define Build/Compile + $(call Build/Compile/Default,all) + $(call Build/Compile/Default,libs) +endef + +define Package/olsrd/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DATA) ./files/olsrd.config $(1)/etc/config/olsrd + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsrd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/olsrd.init $(1)/etc/init.d/olsrd +endef + +define Package/olsrd-mod-arprefresh/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/arprefresh/olsrd_arprefresh.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-dot-draw/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dot_draw/olsrd_dot_draw.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-bmf/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmf/olsrd_bmf.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-dyn-gw/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dyn_gw/olsrd_dyn_gw.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-dyn-gw-plain/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dyn_gw_plain/olsrd_dyn_gw_plain.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-httpinfo/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/httpinfo/olsrd_httpinfo.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-jsoninfo/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/jsoninfo/olsrd_jsoninfo.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-mdns/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/mdns/olsrd_mdns.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-nameservice/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/nameservice/olsrd_nameservice.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-p2pd/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/p2pd/olsrd_p2pd.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-pgraph/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pgraph/olsrd_pgraph.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-pud/install + $(INSTALL_DIR) $(1)/etc/olsrd.d + $(CP) ./files/olsrd.pud.position.conf $(1)/etc/olsrd.d/ + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/nmealib/lib/libnmea.so $(1)/usr/lib/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/wireformat/lib/libOlsrdPudWireFormat.so $(1)/usr/lib/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/olsrd_pud.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-quagga/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/quagga/olsrd_quagga.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-secure/install + $(INSTALL_DIR) $(1)/etc/olsrd.d + $(CP) ./files/olsrd_secure_key $(1)/etc/olsrd.d/ + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/secure/olsrd_secure.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-sgwdynspeed/install + $(INSTALL_DIR) $(1)/etc/olsrd.d + $(CP) ./files/olsrd.sgw.speed.conf $(1)/etc/olsrd.d/ + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/sgwdynspeed/olsrd_sgwdynspeed.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-txtinfo/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/txtinfo/olsrd_txtinfo.so.* $(1)/usr/lib/ +endef + +define Package/olsrd-mod-watchdog/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/watchdog/olsrd_watchdog.so.* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,olsrd)) +$(eval $(call BuildPackage,olsrd-mod-arprefresh)) +$(eval $(call BuildPackage,olsrd-mod-dot-draw)) +$(eval $(call BuildPackage,olsrd-mod-bmf)) +$(eval $(call BuildPackage,olsrd-mod-dyn-gw)) +$(eval $(call BuildPackage,olsrd-mod-dyn-gw-plain)) +$(eval $(call BuildPackage,olsrd-mod-httpinfo)) +$(eval $(call BuildPackage,olsrd-mod-jsoninfo)) +$(eval $(call BuildPackage,olsrd-mod-mdns)) +$(eval $(call BuildPackage,olsrd-mod-nameservice)) +$(eval $(call BuildPackage,olsrd-mod-p2pd)) +$(eval $(call BuildPackage,olsrd-mod-pgraph)) +$(eval $(call BuildPackage,olsrd-mod-pud)) +$(eval $(call BuildPackage,olsrd-mod-quagga)) +$(eval $(call BuildPackage,olsrd-mod-secure)) +$(eval $(call BuildPackage,olsrd-mod-sgwdynspeed)) +$(eval $(call BuildPackage,olsrd-mod-txtinfo)) +$(eval $(call BuildPackage,olsrd-mod-watchdog)) diff --git a/olsrd/files/olsrd.config b/olsrd/files/olsrd.config new file mode 100644 index 0000000..9a19eb9 --- /dev/null +++ b/olsrd/files/olsrd.config @@ -0,0 +1,26 @@ +config olsrd + # uncomment the following line to use a custom config file instead: + #option config_file '/etc/olsrd.conf' + + option IpVersion '4' + +config LoadPlugin + option library 'olsrd_arprefresh.so.0.1' + +config LoadPlugin + option library 'olsrd_dyn_gw.so.0.5' + +config LoadPlugin + option library 'olsrd_httpinfo.so.0.1' + option port '1978' + list Net '0.0.0.0 0.0.0.0' + +config LoadPlugin + option library 'olsrd_nameservice.so.0.3' + +config LoadPlugin + option library 'olsrd_txtinfo.so.0.1' + option accept '0.0.0.0' + +config Interface + list interface 'wlan' diff --git a/olsrd/files/olsrd.init b/olsrd/files/olsrd.init new file mode 100644 index 0000000..f4641aa --- /dev/null +++ b/olsrd/files/olsrd.init @@ -0,0 +1,837 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2008-2013 OpenWrt.org + +START=65 + +SERVICE_DAEMONIZE=1 +SERVICE_WRITE_PID=1 + +CONF=/var/etc/olsrd.conf +PID=/var/run/olsrd.pid +PID6=/var/run/olsrd.ipv6.pid + +OLSRD_OLSRD_SCHEMA='ignore:internal config_file:internal DebugLevel=0 AllowNoInt=yes' +OLSRD_IPCCONNECT_SCHEMA='ignore:internal Host:list Net:list2' +OLSRD_LOADPLUGIN_SCHEMA='ignore:internal library:internal Host4:list Net4:list2 Host:list Net:list2 Host6:list Net6:list2 Ping:list redistribute:list NonOlsrIf:list name:list lat lon latlon_infile HNA:list2 hosts:list2' +OLSRD_INTERFACE_SCHEMA='ignore:internal interface:internal AutoDetectChanges:bool' +OLSRD_INTERFACE_DEFAULTS_SCHEMA='AutoDetectChanges:bool' + +T=' ' +N=' +' + +log() { + logger -t olsrd -p daemon.info -s "${initscript}: $@" +} + +error() { + log "ERROR: $@" +} + +validate_varname() { + local varname="$1" + [ -z "$varname" -o "$varname" != "${varname%%[!A-Za-z0-9_]*}" ] && return 1 + return 0 +} + +validate_olsrd_option() { + local str="$1" + [ -z "$str" -o "$str" != "${str%%[! 0-9A-Za-z./|:_-]*}" ] && return 1 + return 0 +} + +system_config() { + local cfg="$1" + local cfgt hostname latlon oldIFS + + config_get cfgt "$cfg" TYPE + + if [ "$cfgt" = "system" ]; then + config_get hostname "$cfg" hostname + hostname="${hostname:-OpenWrt}" + SYSTEM_HOSTNAME="$hostname" + fi + + if [ -z "$SYSTEM_LAT" -o -z "$SYSTEM_LON" ]; then + config_get latlon "$cfg" latlon + oldIFS="$IFS"; IFS=" ${T}${N},"; set -- $latlon; IFS="$oldIFS" + SYSTEM_LAT="$1" + SYSTEM_LON="$2" + fi + + if [ -z "$SYSTEM_LAT" -o -z "$SYSTEM_LON" ]; then + config_get latlon "$cfg" latitude + SYSTEM_LAT="$latlon" + config_get latlon "$cfg" longitude + SYSTEM_LON="$latlon" + fi +} + +olsrd_find_config_file() { + local cfg="$1" + validate_varname "$cfg" || return 0 + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + config_get OLSRD_CONFIG_FILE "$cfg" config_file + + return 0 +} + +warning_invalid_value() { + local funcname="warning_invalid_value" + local package="$1" + validate_varname "$package" || package= + local config="$2" + validate_varname "$config" || config= + local option="$3" + validate_varname "$option" || option= + + if [ -n "$package" -a -n "$config" ]; then + log "$funcname() in option '$package.$config${option:+.}$option', skipped" + else + log "$funcname() skipped" + fi + + return 0 +} + +olsrd_write_option() { + local param="$1" + local cfg="$2" + validate_varname "$cfg" || return 1 + local option="$3" + validate_varname "$option" || return 1 + local value="$4" + local option_type="$5" + + if [ "$option_type" = bool ]; then + case "$value" in + 1|on|true|enabled|yes) value=yes;; + 0|off|false|disabled|no) value=no;; + *) warning_invalid_value olsrd "$cfg" "$option"; return 1;; + esac + fi + + if ! validate_olsrd_option "$value"; then + warning_invalid_value olsrd "$cfg" "$option" + return 1 + fi + + if [ "$value" != "${value%%[G-Zg-z_-]*}" ]; then + if [ "$option" != "Ip6AddrType" -a "$option" != "LinkQualityMult" -a "$value" != "yes" -a "$value" != "no" ]; then + value="\"$value\"" + fi + fi + + echo -n "${N}$param$option $value" + + return 0 +} + +olsrd_write_plparam() { + local funcname="olsrd_write_plparam" + local param="$1" + local cfg="$2" + validate_varname "$cfg" || return 1 + local option="$3" + validate_varname "$option" || return 1 + local value="$4" + local option_type="$5" + local _option oldIFS + + if [ "$option_type" = bool ]; then + case "$value" in + 1|on|true|enabled|yes) value=yes;; + 0|off|false|disabled|no) value=no;; + *) warning_invalid_value olsrd "$cfg" "$option"; return 1;; + esac + fi + + if ! validate_olsrd_option "$value"; then + warning_invalid_value olsrd "$cfg" "$option" + return 1 + fi + + oldIFS="$IFS" + IFS='-_' + set -- $option + option="$*" + IFS="$oldIFS" + _option="$option" + + if [ "$option" = 'hosts' ]; then + set -- $value + option="$1" + shift + value="$*" + fi + + if [ "$option" = 'NonOlsrIf' ]; then + if validate_varname "$value"; then + if network_get_device ifname "$value"; then + log "$funcname() Info: mdns Interface '$value' ifname '$ifname' found" + else + log "$funcname() Warning: mdns Interface '$value' not found, skipped" + fi + else + warning_invalid_value olsrd "$cfg" "NonOlsrIf" + fi + [ -z "$ifname" ] || value=$ifname + fi + + echo -n "${N}${param}PlParam \"$option\" \"$value\"" + + return 0 +} + +config_update_schema() { + local schema_varname="$1" + validate_varname "$schema_varname" || return 1 + local command="$2" + validate_varname "$command" || return 1 + local option="$3" + validate_varname "$option" || return 1 + local value="$4" + local schema + local cur_option + + case "$varname" in + *_LENGTH) return 0;; + *_ITEM*) return 0;; + esac + + eval "export -n -- \"schema=\${$schema_varname}\"" + + for cur_option in $schema; do + [ "${cur_option%%[:=]*}" = "$option" ] && return 0 + done + + if [ "$command" = list ]; then + set -- $value + if [ "$#" -ge "3" ]; then + schema_entry="$option:list3" + elif [ "$#" -ge "2" ]; then + schema_entry="$option:list2" + else + schema_entry="$option:list" + fi + else + schema_entry="$option" + fi + + append "$schema_varname" "$schema_entry" + + return 0 +} + +config_write_options() { + local funcname="config_write_options" + local schema="$1" + local cfg="$2" + validate_varname "$cfg" || return 1 + local write_func="$3" + [ -z "$write_func" ] && output_func=echo + local write_param="$4" + + local schema_entry option option_length option_type default value list_size list_item list_value i position speed oldIFS + local list_speed_vars="HelloInterval HelloValidityTime TcInterval TcValidityTime MidInterval MidValidityTime HnaInterval HnaValidityTime" + + get_value_for_entry() + { + local schema_entry="$1" + + default="${schema_entry#*[=]}" + [ "$default" = "$schema_entry" ] && default= + option="${schema_entry%%[=]*}" + + oldIFS="$IFS"; IFS=':'; set -- $option; IFS="$oldIFS" + option="$1" + option_type="$2" + + validate_varname "$option" || return 1 + [ -z "$option_type" ] || validate_varname "$option_type" || return 1 + [ "$option_type" = internal ] && return 1 + + config_get value "$cfg" "$option" + [ "$option" = "speed" ] && return 1 + + return 0 + } + + already_in_schema() + { + case " $schema " in + *" $1 "*) + return 0 + ;; + *) + return 1 + ;; + esac + } + + already_in_schema "speed" && { + get_value_for_entry "speed" + + if [ 2>/dev/null $value -gt 0 -a $value -le 20 ]; then + speed="$value" + else + log "$funcname() Warning: invalid speed-value: '$value' - allowed integers: 1...20, fallback to 6" + speed=6 + fi + + for schema_entry in $list_speed_vars; do { + already_in_schema "$schema_entry" || schema="$schema $schema_entry" + } done + } + + for schema_entry in $schema; do + if [ -n "$speed" ]; then # like sven-ola freifunk firmware fff-1.7.4 + case "$schema_entry" in + HelloInterval) + value="$(( $speed / 2 + 1 )).0" + ;; + HelloValidityTime) + value="$(( $speed * 25 )).0" + ;; + TcInterval) # todo: not fisheye? -> $(( $speed * 2 )) + value=$(( $speed / 2 )) + [ $value -eq 0 ] && value=1 + value="$value.0" + ;; + TcValidityTime) + value="$(( $speed * 100 )).0" + ;; + MidInterval) + value="$(( $speed * 5 )).0" + ;; + MidValidityTime) + value="$(( $speed * 100 )).0" + ;; + HnaInterval) + value="$(( $speed * 2 )).0" + ;; + HnaValidityTime) + value="$(( $speed * 25 )).0" + ;; + *) + get_value_for_entry "$schema_entry" || continue + ;; + esac + + is_speed_var() + { + case " $list_speed_vars " in + *" $1 "*) + return 0 + ;; + *) + return 1 + ;; + esac + } + + is_speed_var "$schema_entry" && option="$schema_entry" + else + get_value_for_entry "$schema_entry" || continue + fi + + if [ -z "$value" ]; then + oldIFS="$IFS"; IFS='+'; set -- $default; IFS="$oldIFS" + value=$* + elif [ "$value" = '-' -a -n "$default" ]; then + continue + fi + + [ -z "$value" ] && continue + + case "$option_type" in + list) list_size=1;; + list2) list_size=2;; + list3) list_size=3;; + *) list_size=0;; + esac + + if [ "$list_size" -gt 0 ]; then + config_get option_length "$cfg" "${option}_LENGTH" + if [ -n "$option_length" ]; then + i=1 + while [ "$i" -le "$option_length" ]; do + config_get list_value "$cfg" "${option}_ITEM$i" + "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type" || break + i=$((i + 1)) + done + else + list_value= + i=0 + for list_item in $value; do + append "list_value" "$list_item" + i=$((i + 1)) + position=$((i % list_size)) + if [ "$position" -eq 0 ]; then + "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type" || break + list_value= + fi + done + [ "$position" -ne 0 ] && "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type" + fi + else + "$write_func" "$write_param" "$cfg" "$option" "$value" "$option_type" + fi + done + + return 0 +} + +olsrd_write_olsrd() { + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + [ "$OLSRD_COUNT" -gt 0 ] && return 0 + + config_get ipversion "$cfg" IpVersion + if [ "$ipversion" = "6and4" ]; then + OLSRD_IPVERSION_6AND4=1 + config_set "$cfg" IpVersion '6' + fi + config_get smartgateway "$cfg" SmartGateway + config_get smartgatewayuplink "$cfg" SmartGatewayUplink + + config_write_options "$OLSRD_OLSRD_SCHEMA" "$cfg" olsrd_write_option + echo + OLSRD_COUNT=$((OLSRD_COUNT + 1)) + return 0 +} + +olsrd_write_ipcconnect() { + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + [ "$IPCCONNECT_COUNT" -gt 0 ] && return 0 + + echo -n "${N}IpcConnect${N}{" + config_write_options "$OLSRD_IPCCONNECT_SCHEMA" "$cfg" olsrd_write_option "${T}" + echo "${N}}" + IPCCONNECT_COUNT=$((IPCCONNECT_COUNT + 1)) + + return 0 +} + +olsrd_write_hna4() { + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + config_get netaddr "$cfg" netaddr + if ! validate_olsrd_option "$netaddr"; then + warning_invalid_value olsrd "$cfg" "netaddr" + return 0 + fi + + config_get netmask "$cfg" netmask + if ! validate_olsrd_option "$netmask"; then + warning_invalid_value olsrd "$cfg" "netmask" + return 0 + fi + + [ "$HNA4_COUNT" -le 0 ] && echo -n "${N}Hna4${N}{" + echo -n "${N}${T}${T}$netaddr $netmask" + HNA4_COUNT=$((HNA4_COUNT + 1)) + + return 0 +} + +olsrd_write_hna6() { + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + config_get netaddr "$cfg" netaddr + if ! validate_olsrd_option "$netaddr"; then + warning_invalid_value olsrd "$cfg" "netaddr" + return 0 + fi + + config_get prefix "$cfg" prefix + if ! validate_olsrd_option "$prefix"; then + warning_invalid_value olsrd "$cfg" "prefix" + return 0 + fi + + [ "$HNA6_COUNT" -le 0 ] && echo -n "${N}Hna6${N}{" + echo -n "${N}${T}${T}$netaddr $prefix" + HNA6_COUNT=$((HNA6_COUNT + 1)) + + return 0 +} + +olsrd_write_loadplugin() { + local funcname="olsrd_write_loadplugin" + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + local name + local suffix + local lat + local lon + local latlon_infile + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + config_get library "$cfg" library + if ! validate_olsrd_option "$library"; then + warning_invalid_value olsrd "$cfg" "library" + return 0 + fi + if ! [ -x "/lib/$library" -o -x "/usr/lib/$library" -o -x "/usr/local/lib/$library" ]; then + log "$funcname() Warning: Plugin library '$library' not found, skipped" + return 0 + fi + + case "$library" in + olsrd_nameservice.*) + config_get name "$cfg" name + [ -z "$name" ] && config_set "$cfg" name $SYSTEM_HOSTNAME + + config_get suffix "$cfg" suffix + [ -z "$suffix" ] && config_set "$cfg" suffix '.olsr' + + config_get lat "$cfg" lat + config_get lon "$cfg" lon + config_get latlon_infile "$cfg" latlon_infile + if [ \( -z "$lat" -o -z "$lat" \) -a -z "$latlon_infile" ]; then + if [ -f '/var/run/latlon.txt' ]; then + config_set "$cfg" lat '' + config_set "$cfg" lon '' + config_set "$cfg" latlon_infile '/var/run/latlon.txt' + else + config_set "$cfg" lat "$SYSTEM_LAT" + config_set "$cfg" lon "$SYSTEM_LON" + fi + fi + + for f in latlon_file hosts_file services_file resolv_file macs_file; do + config_get $f "$cfg" $f + done + + [ -z "$latlon_file" ] && config_set "$cfg" latlon_file '/var/run/latlon.js' + ;; + olsrd_watchdog.*) + config_get wd_file "$cfg" file + ;; + esac + + echo -n "${N}LoadPlugin \"$library\"${N}{" + config_write_options "$OLSRD_LOADPLUGIN_SCHEMA" "$cfg" olsrd_write_plparam "${T}" + echo "${N}}" + + return 0 +} + +olsrd_write_interface() { + local funcname="olsrd_write_interface" + local cfg="$1" + validate_varname "$cfg" || return 0 + local ignore + local interfaces + local interface + local ifnames + + config_get_bool ignore "$cfg" ignore 0 + [ "$ignore" -ne 0 ] && return 0 + + ifnames= + config_get interfaces "$cfg" interface + + for interface in $interfaces; do + if validate_varname "$interface"; then + if network_get_device IFNAME "$interface"; then + ifnames="$ifnames \"$IFNAME\"" + ifsglobal="$ifsglobal $IFNAME" + else + log "$funcname() Warning: Interface '$interface' not found, skipped" + fi + else + warning_invalid_value olsrd "$cfg" "interface" + fi + done + + [ -z "$ifnames" ] && return 0 + + echo -n "${N}Interface$ifnames${N}{" + config_write_options "$OLSRD_INTERFACE_SCHEMA" "$cfg" olsrd_write_option "${T}" + echo "${N}}" + INTERFACES_COUNT=$((INTERFACES_COUNT + 1)) + + return 0 +} + +olsrd_write_interface_defaults() { + local cfg="$1" + validate_varname "$cfg" || return 0 + + echo -n "${N}InterfaceDefaults$ifnames${N}{" + config_write_options "$OLSRD_INTERFACE_DEFAULTS_SCHEMA" "$cfg" olsrd_write_option "${T}" + echo "${N}}" + + return 1 +} + +olsrd_update_schema() { + local command="$1" + validate_varname "$command" || return 0 + local varname="$2" + validate_varname "$varname" || return 0 + local value="$3" + local cfg="$CONFIG_SECTION" + local cfgt + local cur_varname + + config_get cfgt "$cfg" TYPE + case "$cfgt" in + olsrd) config_update_schema OLSRD_OLSRD_SCHEMA "$command" "$varname" "$value";; + IpcConnect) config_update_schema OLSRD_IPCCONNECT_SCHEMA "$command" "$varname" "$value";; + LoadPlugin) config_update_schema OLSRD_LOADPLUGIN_SCHEMA "$command" "$varname" "$value";; + Interface) config_update_schema OLSRD_INTERFACE_SCHEMA "$command" "$varname" "$value";; + InterfaceDefaults) config_update_schema OLSRD_INTERFACE_DEFAULTS_SCHEMA "$command" "$varname" "$value";; + esac + + return 0 +} + +olsrd_write_config() { + OLSRD_IPVERSION_6AND4=0 + OLSRD_COUNT=0 + config_foreach olsrd_write_olsrd olsrd + IPCCONNECT_COUNT=0 + config_foreach olsrd_write_ipcconnect IpcConnect + HNA4_COUNT=0 + config_foreach olsrd_write_hna4 Hna4 + [ "$HNA4_COUNT" -gt 0 ] && echo "${N}}" + HNA6_COUNT=0 + config_foreach olsrd_write_hna6 Hna6 + [ "$HNA6_COUNT" -gt 0 ] && echo "${N}}" + config_foreach olsrd_write_loadplugin LoadPlugin + INTERFACES_COUNT=0 + config_foreach olsrd_write_interface_defaults InterfaceDefaults + config_foreach olsrd_write_interface Interface + echo + + return 0 +} + +get_wan_ifnames() +{ + local wanifnames word catch_next + + which ip >/dev/null || return 1 + + set -- $( ip route list exact 0.0.0.0/0 table all ) + for word in $*; do + case "$word" in + dev) + catch_next="true" + ;; + *) + [ -n "$catch_next" ] && { + case "$wanifnames" in + *" $word "*) + ;; + *) + wanifnames="$wanifnames $word " + ;; + esac + + catch_next= + } + ;; + esac + done + + echo "$wanifnames" +} + +olsrd_setup_smartgw_rules() { + local funcname="olsrd_setup_smartgw_rules" + # Check if ipip is installed + [ -e /etc/modules.d/[0-9]*-ipip ] || { + log "$funcname() Warning: kmod-ipip is missing. SmartGateway will not work until you install it." + return 1 + } + + local wanifnames="$( get_wan_ifnames )" + + if [ -z "$wanifnames" ]; then + nowan=1 + else + nowan=0 + fi + + IP4T=$(which iptables) + IP6T=$(which ip6tables) + + # Delete smartgw firewall rules first + for IPT in $IP4T $IP6T; do + while $IPT -D forwarding_rule -o tnl_+ -j ACCEPT 2> /dev/null; do :;done + for IFACE in $wanifnames; do + while $IPT -D forwarding_rule -i tunl0 -o $IFACE -j ACCEPT 2> /dev/null; do :; done + done + for IFACE in $ifsglobal; do + while $IPT -D input_rule -i $IFACE -p 4 -j ACCEPT 2> /dev/null; do :; done + done + done + while $IP4T -t nat -D postrouting_rule -o tnl_+ -j MASQUERADE 2> /dev/null; do :;done + + if [ "$smartgateway" == "yes" ]; then + log "$funcname() Notice: Inserting firewall rules for SmartGateway" + if [ ! "$smartgatewayuplink" == "none" ]; then + if [ "$smartgatewayuplink" == "ipv4" ]; then + # Allow everything to be forwarded to tnl_+ and use NAT for it + $IP4T -I forwarding_rule -o tnl_+ -j ACCEPT + $IP4T -t nat -I postrouting_rule -o tnl_+ -j MASQUERADE + # Allow forwarding from tunl0 to (all) wan-interfaces + if [ "$nowan"="0" ]; then + for IFACE in $wanifnames; do + $IP4T -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT + done + fi + # Allow incoming ipip on all olsr-interfaces + for IFACE in $ifsglobal; do + $IP4T -I input_rule -i $IFACE -p 4 -j ACCEPT + done + elif [ "$smartgatewayuplink" == "ipv6" ]; then + $IP6T -I forwarding_rule -o tnl_+ -j ACCEPT + if [ "$nowan"="0" ]; then + for IFACE in $wanifnames; do + $IP6T -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT + done + fi + for IFACE in $ifsglobal; do + $IP6T -I input_rule -i $IFACE -p 4 -j ACCEPT + done + else + for IPT in $IP4T $IP6T; do + $IPT -I forwarding_rule -o tnl_+ -j ACCEPT + $IPT -t nat -I postrouting_rule -o tnl_+ -j MASQUERADE + if [ "$nowan"="0" ]; then + for IFACE in $wanifnames; do + $IPT -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT + done + fi + for IFACE in $ifsglobal; do + $IPT -I input_rule -i $IFACE -p 4 -j ACCEPT + done + done + fi + fi + fi +} + +start() { + SYSTEM_HOSTNAME= + SYSTEM_LAT= + SYSTEM_LON= + config_load system + config_foreach system_config system + + option_cb() { + olsrd_update_schema "option" "$@" + } + + list_cb() { + olsrd_update_schema "list" "$@" + } + + . /lib/functions/network.sh + + config_load olsrd + reset_cb + + OLSRD_CONFIG_FILE= + config_foreach olsrd_find_config_file olsrd + + if [ -z "$OLSRD_CONFIG_FILE" ]; then + mkdir -p -- /var/etc/ + olsrd_write_config > /var/etc/olsrd.conf + if [ "$INTERFACES_COUNT" -gt 0 -a "$OLSRD_COUNT" -gt 0 ]; then + OLSRD_CONFIG_FILE=/var/etc/olsrd.conf + fi + fi + + [ -z "$OLSRD_CONFIG_FILE" ] && return 1 + + local bindv6only='0' + if [ "$OLSRD_IPVERSION_6AND4" -ne 0 ]; then + bindv6only="$(sysctl -n net.ipv6.bindv6only)" + sysctl -w net.ipv6.bindv6only=1 > /dev/null + sed -e '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/d' < "$OLSRD_CONFIG_FILE" > /var/etc/olsrd.conf.ipv6 + sed -e 's/^IpVersion[ ][ ]*6$/IpVersion 4/' -e 's/^\t\t[A-Fa-f0-9.:]*[:][A-Fa-f0-9.:]*[ ][0-9]*$//' < "$OLSRD_CONFIG_FILE" > /var/etc/olsrd.conf.ipv4 + rm $OLSRD_CONFIG_FILE + + # some filenames should get the suffix .ipv6 + for file in $latlon_file $hosts_file $services_file $resolv_file $macs_file $wd_file;do + f=$(echo $file|sed 's/\//\\\//g') + sed -i "s/$f/$f.ipv6/g" /var/etc/olsrd.conf.ipv6 + done + + SERVICE_PID_FILE="$PID6" + if service_check /usr/sbin/olsrd; then + error "there is already an IPv6 instance of olsrd running (pid: '$(cat $PID6)'), not starting." + else + service_start /usr/sbin/olsrd -f /var/etc/olsrd.conf.ipv6 -nofork + fi + + SERVICE_PID_FILE="$PID" + if service_check /usr/sbin/olsrd; then + error "there is already an IPv4 instance of olsrd running (pid: '$(cat $PID)'), not starting." + else + service_start /usr/sbin/olsrd -f /var/etc/olsrd.conf.ipv4 -nofork + fi + + sleep 3 + sysctl -w net.ipv6.bindv6only="$bindv6only" > /dev/null + + else + + if [ "$ipversion" = "6" ]; then + sed -i '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/d' "$OLSRD_CONFIG_FILE" + fi + + SERVICE_PID_FILE="$PID" + if service_check /usr/sbin/olsrd; then + error "there is already an IPv4 instance of olsrd running (pid: '$(cat $PID)'), not starting." + return 1 + else + service_start /usr/sbin/olsrd -f "$OLSRD_CONFIG_FILE" -nofork + service_check /usr/sbin/olsrd || { + log "startup-error: check via: '/usr/sbin/olsrd -f \"$OLSRD_CONFIG_FILE\" -nofork'" + } + fi + fi + + olsrd_setup_smartgw_rules +} + +stop() { + SERVICE_PID_FILE="$PID" + service_stop /usr/sbin/olsrd + + SERVICE_PID_FILE="$PID6" + service_stop /usr/sbin/olsrd +} diff --git a/olsrd/files/olsrd.pud.position.conf b/olsrd/files/olsrd.pud.position.conf new file mode 100644 index 0000000..0ab0fb7 --- /dev/null +++ b/olsrd/files/olsrd.pud.position.conf @@ -0,0 +1,51 @@ +# +# OLSrd PUD plugin position configuration file +# + +# GPS operating mode. +# Values : bad, 2d, 3d +# Default: bad +# Note : a value of 'bad' will make the resulting position information invalid +# (the mask will indicate that all other information is not present) +#fix = bad + +# GPS quality indicator. +# Values : bad, low, mid, high +# Default: high +#sig = high + +# HDOP (Horizontal Dilution Of Precision) +# Values : floating point value +# Default: 0.0 +# Note : From HDOP, VDOP and PDOP are determined: VDOP = HDOP, and PDOP = sqrt(2) * HDOP. +#hdop = 0.0 + +# Latitude +# Values : floating point value +# Unit is NMEA like latitude: ddmm.sssss (d=degrees, m=minutes, s=seconds) +# Default: 0000.00000 +#lat = 0000.00000 + +# Longitude +# Values: floating point value +# Unit is NMEA like longitude: dddmm.sssss (d=degrees, m=minutes, s=seconds) +# Default: 00000.00000 +#lon = 00000.00000 + +# Elevation +# Values: floating point value +# Unit is meters above the mean sea level (geoid) +# Default: 0.0 +#elv = 0.0 + +# Speed +# Values: floating point value +# Unit is over the ground in kph +# Default: 0.0 +#speed = 0.0 + +# Direction/Track +# Values: floating point value +# Unit is (compass) degrees +# Default: 0.0 +#direction = 0.0 diff --git a/olsrd/files/olsrd.sgw.speed.conf b/olsrd/files/olsrd.sgw.speed.conf new file mode 100644 index 0000000..ae795d8 --- /dev/null +++ b/olsrd/files/olsrd.sgw.speed.conf @@ -0,0 +1,14 @@ +# +# OLSrd Smart Gateway uplink speed configuration file +# + + +# +# Upstream speed in kilobit/s +# Default: 128 +#upstream=128 + +# +# Downstream speed in kilobit/s +# Default: 1024 +#downstream=1024 diff --git a/olsrd/files/olsrd_secure_key b/olsrd/files/olsrd_secure_key new file mode 100644 index 0000000..4a7d725 --- /dev/null +++ b/olsrd/files/olsrd_secure_key @@ -0,0 +1 @@ +1234567890123456 \ No newline at end of file diff --git a/quagga/Makefile b/quagga/Makefile new file mode 100644 index 0000000..fac45c2 --- /dev/null +++ b/quagga/Makefile @@ -0,0 +1,304 @@ +# +# Copyright (C) 2006-2013 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:=quagga +PKG_VERSION:=0.99.22 +PKG_RELEASE:=6 +PKG_MD5SUM:=3057bf3a91116a1017dd0df7e5e8ef93 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=http://download.savannah.gnu.org/releases/quagga/ +PKG_CONFIG_DEPENDS:= \ + CONFIG_IPV6 \ + CONFIG_PACKAGE_quagga-watchquagga \ + CONFIG_PACKAGE_quagga-zebra \ + CONFIG_PACKAGE_quagga-libzebra \ + CONFIG_PACKAGE_quagga-libospf \ + CONFIG_PACKAGE_quagga-bgpd \ + CONFIG_PACKAGE_quagga-isisd \ + CONFIG_PACKAGE_quagga-ospf6d \ + CONFIG_PACKAGE_quagga-ripd \ + CONFIG_PACKAGE_quagga-ripngd \ + CONFIG_PACKAGE_quagga-babeld \ + CONFIG_PACKAGE_quagga-vtysh +PKG_BUILD_PARALLEL:=1 +PKG_FIXUP:=autoreconf +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/quagga/Default + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + DEPENDS:=quagga + TITLE:=The Quagga Software Routing Suite + URL:=http://www.quagga.net + MAINTAINER:=Vasilis Tsiligiannis +endef + +define Package/quagga + $(call Package/quagga/Default) + DEPENDS:=+librt + MENU:=1 +endef + +define Package/quagga/description + A routing software package that provides TCP/IP based routing services + with routing protocols support such as RIPv1, RIPv2, RIPng, OSPFv2, + OSPFv3, BGP-4, and BGP-4+ +endef + +define Package/quagga-watchquagga + $(call Package/quagga/Default) + TITLE:=Quagga watchdog + DEPENDS+=+quagga-libzebra + DEFAULT:=y if PACKAGE_quagga +endef + +define Package/quagga-zebra + $(call Package/quagga/Default) + TITLE:=Zebra daemon + DEPENDS+=+quagga-libzebra + DEFAULT:=y if PACKAGE_quagga +endef + +define Package/quagga-libzebra + $(call Package/quagga/Default) + TITLE:=zebra library +endef + +define Package/quagga-libospf + $(call Package/quagga/Default) + TITLE:=OSPF library +endef + +define Package/quagga-bgpd + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra + TITLE:=BGPv4, BGPv4+, BGPv4- routing engine +endef + +define Package/quagga-isisd + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra + TITLE:=IS-IS routing engine +endef + +define Package/quagga-ospfd + $(call Package/quagga/Default) + DEPENDS+=+quagga-libospf +quagga-libzebra + TITLE:=OSPFv2 routing engine +endef + +define Package/quagga-ospf6d + $(call Package/quagga/Default) + DEPENDS+=+quagga-libospf +quagga-libzebra @IPV6 + TITLE:=OSPFv3 routing engine +endef + +define Package/quagga-ripd + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra + TITLE:=RIP routing engine +endef + +define Package/quagga-ripngd + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra @IPV6 + TITLE:=RIPNG routing engine +endef + +define Package/quagga-babeld + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra @IPV6 + TITLE:=Babel routing engine +endef + +define Package/quagga-vtysh + $(call Package/quagga/Default) + DEPENDS+=+quagga-libzebra +libreadline +libncurses + TITLE:=integrated shell for Quagga routing software +endef + +define Package/quagga-zebra/conffiles +/etc/quagga/zebra.conf +endef + +define Package/quagga-bgpd/conffiles +/etc/quagga/bgpd.conf +endef + +define Package/quagga-isisd/conffiles +/etc/quagga/isisd.conf +endef + +define Package/quagga-ospfd/conffiles +/etc/quagga/ospfd.conf +endef + +define Package/quagga-ospf6d/conffiles +/etc/quagga/ospf6d.conf +endef + +define Package/quagga-ripd/conffiles +/etc/quagga/ripd.conf +endef + +define Package/quagga-ripngd/conffiles +/etc/quagga/ripngd.conf +endef + +define Package/quagga-babeld/conffiles +/etc/quagga/babeld.conf +endef + +ifneq ($(SDK),) +CONFIG_PACKAGE_quagga-libzebra:=m +CONFIG_PACKAGE_quagga-libospf:=m +CONFIG_PACKAGE_quagga-watchquagga:=m +CONFIG_PACKAGE_quagga-zebra:=m +CONFIG_PACKAGE_quagga-bgpd:=m +CONFIG_PACKAGE_quagga-isisd:=m +CONFIG_PACKAGE_quagga-ospf6d:=m +CONFIG_PACKAGE_quagga-ripd:=m +CONFIG_PACKAGE_quagga-ripngd:=m +CONFIG_PACKAGE_quagga-babeld:=m +CONFIG_PACKAGE_quagga-vtysh:=m +endif + +CONFIGURE_ARGS+= \ + --localstatedir=/var/run/quagga \ + --sysconfdir=/etc/quagga/ \ + --enable-shared \ + --disable-static \ + --enable-user=network \ + --enable-group=network \ + --enable-pie=no \ + --enable-multipath=8 \ + --disable-ospfclient \ + --disable-capabilities \ + --disable-doc \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-libzebra,zebra) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-libospf,ospfd) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-bgpd,bgpd) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-isisd,isisd) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-ospf6d,ospf6d) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-ripd,ripd) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-ripngd,ripngd) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-babeld,babeld) \ + $(call autoconf_bool,CONFIG_PACKAGE_quagga-vtysh,vtysh) \ + +MAKE_FLAGS += \ + CFLAGS="$(TARGET_CFLAGS) -std=gnu99" + +define Package/quagga/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/quagga $(1)/usr/sbin/quagga.init + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/quagga.init $(1)/etc/init.d/quagga +endef + +define Package/quagga-watchquagga/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/watchquagga $(1)/usr/sbin/ +endef + +define Package/quagga-zebra/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/zebra $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/zebra.conf +endef + +define Package/quagga-bgpd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bgpd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/bgpd.conf +endef + +define Package/quagga-isisd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/isisd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/isisd.conf +endef + +define Package/quagga-ospfd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospfd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospfd.conf +endef + +define Package/quagga-ospf6d/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospf6d $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospf6d.conf +endef + +define Package/quagga-ripd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripd.conf +endef + +define Package/quagga-ripngd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripngd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripngd.conf +endef + +define Package/quagga-babeld/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/babeld $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/babeld.conf +endef + +define Package/quagga-vtysh/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/vtysh $(1)/usr/bin/ +endef + +define Package/quagga-libospf/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libospf.so.* $(1)/usr/lib/ +endef + +define Package/quagga-libzebra/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libzebra.so.* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,quagga)) +$(eval $(call BuildPackage,quagga-libzebra)) +$(eval $(call BuildPackage,quagga-libospf)) +$(eval $(call BuildPackage,quagga-watchquagga)) +$(eval $(call BuildPackage,quagga-zebra)) +$(eval $(call BuildPackage,quagga-bgpd)) +$(eval $(call BuildPackage,quagga-isisd)) +$(eval $(call BuildPackage,quagga-ospfd)) +$(eval $(call BuildPackage,quagga-ospf6d)) +$(eval $(call BuildPackage,quagga-ripd)) +$(eval $(call BuildPackage,quagga-ripngd)) +$(eval $(call BuildPackage,quagga-babeld)) +$(eval $(call BuildPackage,quagga-vtysh)) diff --git a/quagga/files/quagga b/quagga/files/quagga new file mode 100644 index 0000000..b172090 --- /dev/null +++ b/quagga/files/quagga @@ -0,0 +1,335 @@ +#!/bin/sh +# +# quagga Starts/stops quagga daemons and watchquagga. +# Create a daemon.conf file to have that routing daemon +# started/stopped automagically when using this script +# without any daemon names as args. +# If watchquagga is available, it will also be +# started/stopped if the script is called without +# any daemon names. +# + +ME=$(basename $0) + +usage() { + echo "Usage: ${ME} {start|stop|restart} [daemon ...]" + exit 2 +} + +if [ -z "$1" ] +then + usage +else + COMMAND=$1 +fi +shift +ARG_DAEMONS=$* +BINDIR=/usr/sbin +CONFDIR=/etc/quagga +STATEDIR=/var/run/quagga +RUNUSER=network +RUNGROUP=$RUNUSER +DAEMONS="zebra ripd ripngd ospfd ospf6d bgpd" +DAEMON_FLAGS=-d +WATCHQUAGGA_FLAGS="-d -z -T 60 -R" +WATCHQUAGGA_CMD="$0 watchrestart" +if [ ${COMMAND} != "watchrestart" -a -x "${BINDIR}/watchquagga" ] +then + DAEMONS="${DAEMONS} watchquagga" +fi +DAEMONS_STARTSEQ=${DAEMONS} + +reverse() +{ + local revlist r + revlist= + for r + do + revlist="$r $revlist" + done + echo $revlist +} + +DAEMONS_STOPSEQ=$(reverse ${DAEMONS_STARTSEQ}) + +#pidof() { +# ps ax | awk 'match($5, "(^|/)'"$1"'$") > 0 { printf " %s", $1 }' +#} + +quit() { + echo "${ME}: $1" + exit 0 +} + +die() { + echo "${ME}: $1" + exit 1 +} + +is_in() { + local i + for i in $2 + do + [ "$1" = "$i" ] && return 0 + done + return 1 +} + +select_subset() { + local unknown i j + unknown= + RESULT= + for i in $1 + do + is_in $i "$2" || unknown="$unknown $i" + done + if [ -n "$unknown" ] + then + RESULT=$unknown + return 1 + else + for j in $2 + do + is_in $j "$1" && RESULT="$RESULT $j" + done + return 0 + fi +} + +# check command + +case ${COMMAND} +in + start|stop|restart) + ;; + watchrestart) + if [ -n "$ARG_DAEMONS" ] + then + echo "${ME}: watchrestart mode is only for use by watchquagga" + exit 2 + fi + ;; + *) + usage + ;; +esac + +# select daemons to start + +case ${COMMAND} +in + start|restart|watchrestart) + START_DAEMONS= + for d in ${DAEMONS_STARTSEQ} + do + [ -x "${BINDIR}/${d}" -a -f "${CONFDIR}/${d}.conf" ] \ + && START_DAEMONS="${START_DAEMONS}${d} " + done + WATCHQUAGGA_DAEMONS=${START_DAEMONS} + if is_in watchquagga "${DAEMONS_STARTSEQ}" + then + START_DAEMONS="${START_DAEMONS} watchquagga" + fi + if [ -n "${ARG_DAEMONS}" ] + then + if select_subset "${ARG_DAEMONS}" "${DAEMONS}" + then + if select_subset "${ARG_DAEMONS}" "${START_DAEMONS}" + then + START_DAEMONS=${RESULT} + else + die "these daemons are not startable:${RESULT}." + fi + else + die "unknown daemons:${RESULT}; choose from: ${DAEMONS}." + fi + fi + ;; +esac + +# select daemons to stop + +case ${COMMAND} +in + stop|restart|watchrestart) + STOP_DAEMONS=${DAEMONS_STOPSEQ} + if [ -n "${ARG_DAEMONS}" ] + then + if select_subset "${ARG_DAEMONS}" "${STOP_DAEMONS}" + then + STOP_DAEMONS=${RESULT} + else + die "unknown daemons:${RESULT}; choose from: ${DAEMONS}." + fi + fi + stop_daemons= + for d in ${STOP_DAEMONS} + do + pidfile=${STATEDIR}/${d}.pid + if [ -f "${pidfile}" -o -n "$(pidof ${d})" ] + then + stop_daemons="${stop_daemons}${d} " + elif [ -n "${ARG_DAEMONS}" ] + then + echo "${ME}: found no ${d} process running." + fi + done + STOP_DAEMONS=${stop_daemons} + ;; +esac + +# stop daemons + +for d in $STOP_DAEMONS +do + echo -n "${ME}: Stopping ${d} ... " + pidfile=${STATEDIR}/${d}.pid + if [ -f "${pidfile}" ] + then + file_pid=$(cat ${pidfile}) + if [ -z "${file_pid}" ] + then + echo -n "no pid file entry found ... " + fi + else + file_pid= + echo -n "no pid file found ... " + fi + proc_pid=$(pidof ${d}) + if [ -z "${proc_pid}" ] + then + echo -n "found no ${d} process running ... " + else + count=0 + notinpidfile= + for p in ${proc_pid} + do + count=$((${count}+1)) + if kill ${p} + then + echo -n "killed ${p} ... " + else + echo -n "failed to kill ${p} ... " + fi + [ "${p}" = "${file_pid}" ] \ + || notinpidfile="${notinpidfile} ${p}" + done + [ ${count} -le 1 ] \ + || echo -n "WARNING: ${count} ${d} processes were found running ... " + for n in ${notinpidfile} + do + echo -n "WARNING: process ${n} was not in pid file ... " + done + fi + count=0 + survivors=$(pidof ${d}) + while [ -n "${survivors}" ] + do + sleep 1 + count=$((${count}+1)) + survivors=$(pidof ${d}) + [ -z "${survivors}" -o ${count} -gt 5 ] && break + for p in ${survivors} + do + sleep 1 + echo -n "${p} " + kill ${p} + done + done + survivors=$(pidof ${d}) + [ -n "${survivors}" ] && \ + if kill -KILL ${survivors} + then + echo -n "KILLed ${survivors} ... " + else + echo -n "failed to KILL ${survivors} ... " + fi + sleep 1 + survivors=$(pidof ${d}) + if [ -z "${survivors}" ] + then + echo -n "done." + if [ -f "${pidfile}" ] + then + rm -f ${pidfile} \ + || echo -n " Failed to remove pidfile." + fi + else + echo -n "failed to stop ${survivors} - giving up." + if [ "${survivors}" != "${file_pid}" ] + then + if echo "${survivors}" > ${pidfile} + then + chown ${RUNUSER}:${RUNGROUP} ${pidfile} + echo -n " Wrote ${survivors} to pidfile." + else + echo -n " Failed to write ${survivors} to pidfile." + fi + fi + fi + echo +done + +# start daemons + +if [ -n "$START_DAEMONS" ] +then + [ -d ${CONFDIR} ] \ + || quit "${ME}: no config directory ${CONFDIR} - exiting." + chown -R ${RUNUSER}:${RUNGROUP} ${CONFDIR} + [ -d ${STATEDIR} ] || mkdir -p ${STATEDIR} \ + || die "${ME}: could not create state directory ${STATEDIR} - exiting." + chown -R ${RUNUSER}:${RUNGROUP} ${STATEDIR} + + for d in $START_DAEMONS + do + echo -n "${ME}: Starting ${d} ... " + proc_pid=$(pidof ${d}) + pidfile=${STATEDIR}/${d}.pid + file_pid= + if [ -f "${pidfile}" ] + then + file_pid=$(cat ${pidfile}) + if [ -n "${file_pid}" ] + then + echo -n "found old pid file entry ${file_pid} ... " + fi + fi + if [ -n "${proc_pid}" ] + then + echo -n "found ${d} running (${proc_pid}) - skipping ${d}." + if [ "${proc_pid}" != "${file_pid}" ] + then + if echo "${proc_pid}" > ${pidfile} + then + chown ${RUNUSER}:${RUNGROUP} ${pidfile} + echo -n " Wrote ${proc_pid} to pidfile." + else + echo -n " Failed to write ${proc_pid} to pidfile." + fi + fi + elif rm -f "${pidfile}" + then + if [ "${d}" = "watchquagga" ] + then + "${BINDIR}/${d}" \ + ${WATCHQUAGGA_FLAGS} \ + "${WATCHQUAGGA_CMD}" \ + ${WATCHQUAGGA_DAEMONS} + status=$? + else + "${BINDIR}/${d}" ${DAEMON_FLAGS} + status=$? + fi + if [ $status -eq 0 ] + then + echo -n "done." + else + echo -n "failed." + fi + else + echo -n " failed to remove pidfile." + fi + echo + done +fi diff --git a/quagga/files/quagga.conf b/quagga/files/quagga.conf new file mode 100644 index 0000000..fb7a54e --- /dev/null +++ b/quagga/files/quagga.conf @@ -0,0 +1,7 @@ +password zebra +! +access-list vty permit 127.0.0.0/8 +access-list vty deny any +! +line vty + access-class vty diff --git a/quagga/files/quagga.init b/quagga/files/quagga.init new file mode 100644 index 0000000..21fbf2c --- /dev/null +++ b/quagga/files/quagga.init @@ -0,0 +1,11 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=60 +start() { + /usr/sbin/quagga.init start +} + +stop() { + /usr/sbin/quagga.init stop +} diff --git a/quagga/patches/120-quagga_manet.patch b/quagga/patches/120-quagga_manet.patch new file mode 100644 index 0000000..684a27d --- /dev/null +++ b/quagga/patches/120-quagga_manet.patch @@ -0,0 +1,243 @@ +--- a/lib/log.c ++++ b/lib/log.c +@@ -925,13 +925,19 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_STATIC; + else if (strncmp (s, "r", 1) == 0) + return ZEBRA_ROUTE_RIP; +- else if (strncmp (s, "o", 1) == 0) ++ else if (strncmp (s, "os", 2) == 0) + return ZEBRA_ROUTE_OSPF; + else if (strncmp (s, "i", 1) == 0) + return ZEBRA_ROUTE_ISIS; + else if (strncmp (s, "bg", 2) == 0) + return ZEBRA_ROUTE_BGP; +- else if (strncmp (s, "ba", 2) == 0) ++ else if (strncmp (s, "h", 1) == 0) ++ return ZEBRA_ROUTE_HSLS; ++ else if (strncmp (s, "ol", 2) == 0) ++ return ZEBRA_ROUTE_OLSR; ++ else if (strncmp (s, "bat", 3) == 0) ++ return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } + if (afi == AFI_IP6) +@@ -944,13 +950,19 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_STATIC; + else if (strncmp (s, "r", 1) == 0) + return ZEBRA_ROUTE_RIPNG; +- else if (strncmp (s, "o", 1) == 0) ++ else if (strncmp (s, "os", 2) == 0) + return ZEBRA_ROUTE_OSPF6; + else if (strncmp (s, "i", 1) == 0) + return ZEBRA_ROUTE_ISIS; + else if (strncmp (s, "bg", 2) == 0) + return ZEBRA_ROUTE_BGP; +- else if (strncmp (s, "ba", 2) == 0) ++ else if (strncmp (s, "h", 1) == 0) ++ return ZEBRA_ROUTE_HSLS; ++ else if (strncmp (s, "ol", 2) == 0) ++ return ZEBRA_ROUTE_OLSR; ++ else if (strncmp (s, "bat", 3) == 0) ++ return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } + return -1; +--- a/lib/route_types.txt ++++ b/lib/route_types.txt +@@ -51,13 +51,9 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd + ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" + ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" + ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" +-# HSLS and OLSR both are AFI independent (so: 1, 1), however +-# we want to disable for them for general Quagga distribution. +-# This at least makes it trivial for users of these protocols +-# to 'switch on' redist support (direct numeric entry remaining +-# possible). +-ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" +-ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" ++ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 1, 1, "HSLS" ++ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR" ++ZEBRA_ROUTE_BATMAN, batman, batmand,'b', 1, 1, "BATMAN" + ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" + + ## help strings +@@ -72,5 +68,6 @@ ZEBRA_ROUTE_OSPF6, "Open Shortest Path + ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" + ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" + ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" +-ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ++ZEBRA_ROUTE_OLSR, "Optimized Link State Routing (OLSR)" ++ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)" + ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" +--- a/ripd/rip_zebra.c ++++ b/ripd/rip_zebra.c +@@ -206,9 +206,12 @@ static struct { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, +- {ZEBRA_ROUTE_OSPF, 1, "ospf"}, ++ {ZEBRA_ROUTE_OSPF, 2, "ospf"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, +- {ZEBRA_ROUTE_BABEL, 2, "babel"}, ++ {ZEBRA_ROUTE_HSLS, 1, "hsls"}, ++ {ZEBRA_ROUTE_OLSR, 2, "olsr"}, ++ {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; + +--- a/ripngd/ripng_zebra.c ++++ b/ripngd/ripng_zebra.c +@@ -216,9 +216,12 @@ static struct { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, +- {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, ++ {ZEBRA_ROUTE_OSPF6, 2, "ospf6"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, +- {ZEBRA_ROUTE_BABEL, 2, "babel"}, ++ {ZEBRA_ROUTE_HSLS, 1, "hsls"}, ++ {ZEBRA_ROUTE_OLSR, 2, "olsr"}, ++ {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; + +--- a/zebra/rt_netlink.c ++++ b/zebra/rt_netlink.c +@@ -1609,6 +1609,9 @@ netlink_route_multipath (int cmd, struct + addattr_l (&req.n, sizeof req, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + ++ if (rib->type == ZEBRA_ROUTE_OLSR) ++ req.r.rtm_scope = RT_SCOPE_LINK; ++ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (single hop): " + "nexthop via if %u", nexthop->ifindex); +--- a/zebra/zebra_rib.c ++++ b/zebra/zebra_rib.c +@@ -68,6 +68,9 @@ static const struct + [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110}, + [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, + [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, ++ [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 0}, ++ [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 0}, ++ [ZEBRA_ROUTE_BATMAN] = {ZEBRA_ROUTE_BATMAN, 0}, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, + /* no entry/default: 150 */ + }; +@@ -456,6 +459,18 @@ nexthop_active_ipv4 (struct rib *rib, st + } + return 0; + } ++ else if (match->type == ZEBRA_ROUTE_OLSR) ++ { ++ for (newhop = match->nexthop; newhop; newhop = newhop->next) ++ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) ++ && newhop->type == NEXTHOP_TYPE_IFINDEX) ++ { ++ if (nexthop->type == NEXTHOP_TYPE_IPV4) ++ nexthop->ifindex = newhop->ifindex; ++ return 1; ++ } ++ return 0; ++ } + else + { + return 0; +@@ -560,6 +575,18 @@ nexthop_active_ipv6 (struct rib *rib, st + } + return 0; + } ++ else if (match->type == ZEBRA_ROUTE_OLSR) ++ { ++ for (newhop = match->nexthop; newhop; newhop = newhop->next) ++ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) ++ && newhop->type == NEXTHOP_TYPE_IFINDEX) ++ { ++ if (nexthop->type == NEXTHOP_TYPE_IPV6) ++ nexthop->ifindex = newhop->ifindex; ++ return 1; ++ } ++ return 0; ++ } + else + { + return 0; +@@ -1376,6 +1403,8 @@ static const u_char meta_queue_map[ZEBRA + [ZEBRA_ROUTE_ISIS] = 2, + [ZEBRA_ROUTE_BGP] = 3, + [ZEBRA_ROUTE_HSLS] = 4, ++ [ZEBRA_ROUTE_OLSR] = 4, ++ [ZEBRA_ROUTE_BATMAN] = 4, + [ZEBRA_ROUTE_BABEL] = 2, + }; + +--- a/zebra/zebra_snmp.c ++++ b/zebra/zebra_snmp.c +@@ -245,6 +245,12 @@ proto_trans(int type) + return 1; /* shouldn't happen */ + case ZEBRA_ROUTE_BGP: + return 14; /* bgp */ ++ case ZEBRA_ROUTE_HSLS: ++ return 1; /* other */ ++ case ZEBRA_ROUTE_OLSR: ++ return 1; /* other */ ++ case ZEBRA_ROUTE_BATMAN: ++ return 1; /* other */ + default: + return 1; /* other */ + } +--- a/zebra/zebra_vty.c ++++ b/zebra/zebra_vty.c +@@ -558,7 +558,10 @@ vty_show_ip_route_detail (struct vty *vt + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -777,7 +780,10 @@ vty_show_ip_route (struct vty *vty, stru + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -1570,7 +1576,10 @@ vty_show_ipv6_route_detail (struct vty * + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -1750,7 +1759,10 @@ vty_show_ipv6_route (struct vty *vty, st + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; diff --git a/quagga/patches/140-holdtimer-set.patch b/quagga/patches/140-holdtimer-set.patch new file mode 100644 index 0000000..b699775 --- /dev/null +++ b/quagga/patches/140-holdtimer-set.patch @@ -0,0 +1,22 @@ +--- a/bgpd/bgp_network.c ++++ b/bgpd/bgp_network.c +@@ -193,8 +193,7 @@ bgp_accept (struct thread *thread) + peer->fd = bgp_sock; + peer->status = Active; + peer->local_id = peer1->local_id; +- peer->v_holdtime = peer1->v_holdtime; +- peer->v_keepalive = peer1->v_keepalive; ++ peer->v_holdtime = BGP_LARGE_HOLDTIME; + + /* Make peer's address string. */ + sockunion2str (&su, buf, SU_ADDRSTRLEN); +--- a/bgpd/bgpd.h ++++ b/bgpd/bgpd.h +@@ -732,6 +732,7 @@ struct bgp_nlri + /* BGP timers default value. */ + #define BGP_INIT_START_TIMER 5 + #define BGP_ERROR_START_TIMER 30 ++#define BGP_LARGE_HOLDTIME 240 + #define BGP_DEFAULT_HOLDTIME 180 + #define BGP_DEFAULT_KEEPALIVE 60 + #define BGP_DEFAULT_ASORIGINATE 15 diff --git a/quagga/patches/150-no-cross-fs-link.patch b/quagga/patches/150-no-cross-fs-link.patch new file mode 100644 index 0000000..32c1208 --- /dev/null +++ b/quagga/patches/150-no-cross-fs-link.patch @@ -0,0 +1,40 @@ +--- a/lib/command.c ++++ b/lib/command.c +@@ -2527,6 +2527,13 @@ DEFUN (config_write_file, + VTY_NEWLINE); + goto finished; + } ++ ++#if 0 ++ /* This code fails on UNION MOUNTs and similar filesystems if the ++ * config file is still on the RO layer. Hardlinks across layers ++ * will not work and cause quagga to fail saving the configuration... ++ * should use rename() to move files around... ++ */ + if (link (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, +@@ -2540,7 +2547,23 @@ DEFUN (config_write_file, + VTY_NEWLINE); + goto finished; + } ++#else ++ /* And this is the code that hopefully does work */ ++ if (rename (config_file, config_file_sav) != 0) ++ { ++ vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, ++ VTY_NEWLINE); ++ goto finished; ++ } ++ sync (); ++#endif ++ ++#if 0 ++ /* same here. Please no cross-filesystem hardlinks... */ + if (link (config_file_tmp, config_file) != 0) ++#else ++ if (rename (config_file_tmp, config_file) != 0) ++#endif + { + vty_out (vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); diff --git a/quagga/patches/170-use-supported-pagers.patch b/quagga/patches/170-use-supported-pagers.patch new file mode 100644 index 0000000..15595a7 --- /dev/null +++ b/quagga/patches/170-use-supported-pagers.patch @@ -0,0 +1,29 @@ +--- a/vtysh/vtysh.c ++++ b/vtysh/vtysh.c +@@ -268,7 +268,7 @@ vtysh_pager_init (void) + if (pager_defined) + vtysh_pager_name = strdup (pager_defined); + else +- vtysh_pager_name = strdup ("more"); ++ vtysh_pager_name = strdup ("cat"); + } + + /* Command execution over the vty interface. */ +@@ -1884,7 +1884,7 @@ DEFUN (vtysh_terminal_length, + { + int lines; + char *endptr = NULL; +- char default_pager[10]; ++ char default_pager[12]; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') +@@ -1901,7 +1901,7 @@ DEFUN (vtysh_terminal_length, + + if (lines != 0) + { +- snprintf(default_pager, 10, "more -%i", lines); ++ snprintf(default_pager, 12, "head -n %i", lines); + vtysh_pager_name = strdup (default_pager); + } +