SHOW:
|
|
- or go back to the newest paste.
| 1 | #!/bin/sh | |
| 2 | - | # version: 1.0.0, 06-nov-2021, by eibgrad |
| 2 | + | #DEBUG= # uncomment/comment to enable/disable debug mode |
| 3 | - | # href: https://tinyurl.com/???????? |
| 3 | + | |
| 4 | - | # known compatibility: rt-ac68u |
| 4 | + | # name: merlin-ac68u-add-networks.sh |
| 5 | # version: 2.0.1, 27-jun-2024, by eibgrad | |
| 6 | - | unset DEBUG INCLUDE_DNSMASQ INCLUDE_FIREWALL |
| 6 | + | # purpose: add ip networks using vlans, aps/vaps, bridges, etc. |
| 7 | - | unset ALLOW_PRIVATE_TO_ANY ALLOW_OVPN_ACCESS |
| 7 | + | # type(s): dnsmasq.postconf (optional), firewall-start (optional), |
| 8 | # service-event-end, services-start | |
| 9 | - | # ---------------------- DO NOT CHANGE ABOVE THIS LINE ----------------------- # |
| 9 | + | # href: https://tinyurl.com/kumyymcw (version 1.x.x) |
| 10 | # href: https://tinyurl.com/ycxxmw6d (version 2.x.x) | |
| 11 | # installation: | |
| 12 | # 1. enable jffs custom scripts and configs (administration->system) | |
| 13 | - | DEBUG= # uncomment/comment to enable/disable debug mode |
| 13 | + | # 2. ssh to router and copy/paste the following command: |
| 14 | # curl -kLs bit.ly/merlin-installer|tr -d '\r'|sh -s hvHHic1V | |
| 15 | # 3. use nano editor to modify script w/ your preferred options: | |
| 16 | # nano /jffs/configs/merlin-ac68u-add-networks.options | |
| 17 | # 4. reboot | |
| 18 | ||
| 19 | # compatibility checks | |
| 20 | if [ "$(nvram get sw_mode)" != '1' ]; then | |
| 21 | - | # only bridged configurations have port 0 availble for assignment |
| 21 | + | echo 'error: script only supports a routed configuration' |
| 22 | - | #VLAN_PORTS='1/0/1/2/3 3/4' # vlan1 ports 0 1 2 3, vlan3 port 4 |
| 22 | + | exit 1 |
| 23 | elif ! which robocfg &>/dev/null; then | |
| 24 | echo 'error: script is NOT compatible w/ this firmware; requires robocfg' | |
| 25 | exit 1 | |
| 26 | fi | |
| 27 | ||
| 28 | CONFIGS_DIR='/jffs/configs' | |
| 29 | CONFIG="$CONFIGS_DIR/merlin-ac68u-add-networks.options" | |
| 30 | ||
| 31 | SCRIPTS_DIR='/jffs/scripts' | |
| 32 | SCRIPT1="$SCRIPTS_DIR/merlin-ac68u-add-networks.dnsmasq" | |
| 33 | SCRIPT2="$SCRIPTS_DIR/merlin-ac68u-add-networks.firewall" | |
| 34 | SCRIPT3="$SCRIPTS_DIR/merlin-ac68u-add-networks.service-event-end" | |
| 35 | - | #VLANS_PORTS=''; VLANS_WL='' # only intended for cleanup purposes |
| 35 | + | SCRIPT4="$SCRIPTS_DIR/merlin-ac68u-add-networks.services-start" |
| 36 | SCRIPT5="$SCRIPTS_DIR/dnsmasq.postconf" | |
| 37 | SCRIPT6="$SCRIPTS_DIR/firewall-start" | |
| 38 | - | #IPNET_PFX='10.99.' # first two dotted octets only |
| 38 | + | SCRIPT7="$SCRIPTS_DIR/service-event-end" |
| 39 | SCRIPT8="$SCRIPTS_DIR/services-start" | |
| 40 | ||
| 41 | mkdir -p $CONFIGS_DIR $SCRIPTS_DIR | |
| 42 | ||
| 43 | # ----------------- begin merlin-ac68u-add-networks.options ------------------ # | |
| 44 | cat << 'EOF' > $CONFIG | |
| 45 | ||
| 46 | # ------------------------------ BEGIN OPTIONS ------------------------------- # | |
| 47 | ||
| 48 | # VLANS_PORTS='[<vlan-id>[/<port>...] ...]' | |
| 49 | VLANS_PORTS='1/1/2/3 3/4' # vlan1 ports 1 2 3, vlan3 port 4 | |
| 50 | #VLANS_PORTS='1/1/2 3/3/4' # vlan1 ports 1 2, vlan3 ports 3 4 | |
| 51 | #VLANS_PORTS='1 10/1/2/3/4' # vlan1 no ports, vlan10 ports 1 2 3 4 | |
| 52 | #VLANS_PORTS='1/1 10/2 11/3 12/4' # vlan1/vlan10/vlan11/vlan12, one port each | |
| 53 | #VLANS_PORTS='1/1/2/3/4t 3/4t' # vlan1/vlan3 port 4 trunk | |
| 54 | ||
| 55 | # VLANS_WL='[<vlan-id>[/<wireless-if>...] ...]' | |
| 56 | #VLANS_WL='' # no wireless changes required | |
| 57 | #VLANS_WL='3/eth1' # bridge vlan3 w/ 2.4ghz | |
| 58 | VLANS_WL='3/eth2' # bridge vlan3 w/ 5ghz | |
| 59 | #VLANS_WL='3/wl0.1/wl1.1' # bridge vlan3 w/ guest 1 (2.4+5ghz) | |
| 60 | - | if ! which robocfg &>/dev/null; then |
| 60 | + | |
| 61 | - | echo 'error: script is NOT compatible w/ this firmware; requires robocfg' |
| 61 | + | |
| 62 | #VLANS_WL='11/wl0.2/wl1.2' # bridge vlan11 w/ guest 2 (2.4+5ghz) | |
| 63 | #VLANS_WL='12/wl0.3/wl1.3' # bridge vlan12 w/ guest 3 (2.4+5ghz) | |
| 64 | # bridge vlans 10/11/12 /w guests 1/2/3 respectively | |
| 65 | - | # function router_is_bridged() |
| 65 | + | |
| 66 | - | router_is_bridged() { ! ifconfig vlan2 2>/dev/null | grep -q 'UP'; }
|
| 66 | + | |
| 67 | #VLANS_PORTS=''; VLANS_WL='' # for cleanup purposes only | |
| 68 | - | CONFIGS_DIR='/jffs/configs'; mkdir -p $CONFIGS_DIR |
| 68 | + | |
| 69 | - | SCRIPTS_DIR='/jffs/scripts'; mkdir -p $SCRIPTS_DIR |
| 69 | + | |
| 70 | #IP_PFX='10.99.' # first two dotted octets only | |
| 71 | - | if [ ! "$IPNET_PFX" ]; then |
| 71 | + | |
| 72 | - | IPNET_PFX="$(nvram get lan_ipaddr | egrep -o '^(.{1,3}\.){2}')"
|
| 72 | + | |
| 73 | # warning: any change requires reinstallation w/ this script! | |
| 74 | INCLUDE_DNSMASQ= | |
| 75 | - | MIN_VID=3; MAX_VID=255 |
| 75 | + | |
| 76 | - | MIN_PORT=$(router_is_bridged && echo 0 || echo 1); MAX_PORT=4 |
| 76 | + | |
| 77 | DNS_SERVERS='8.8.8.8,8.8.4.4' # comma-separated | |
| 78 | - | LOG="$([ ${DEBUG+x} ] && echo '/tmp/$(basename $0).$$.log' || echo '/dev/null')"
|
| 78 | + | |
| 79 | # uncomment/comment to include/exclude pre-defined firewall rules | |
| 80 | - | # -------------------------- begin dnsmasq.conf.add ------------------------- # |
| 80 | + | # warning: any change requires reinstallation w/ this script! |
| 81 | - | CONFIG="$CONFIGS_DIR/dnsmasq.conf.add" |
| 81 | + | |
| 82 | ||
| 83 | - | if [ "${INCLUDE_DNSMASQ+x}" ]; then
|
| 83 | + | |
| 84 | - | ( |
| 84 | + | |
| 85 | - | create_config() {
|
| 85 | + | |
| 86 | # uncomment/comment to allow/deny access to/from openvpn clients/servers | |
| 87 | #ALLOW_OVPN_ACCESS= | |
| 88 | ||
| 89 | # these are just examples; uncomment and modify as you see fit | |
| 90 | DNSMASQ_ADDITIONS=' | |
| 91 | - | if ! echo $vlan_id | egrep -q '^[0-9]+$'; then |
| 91 | + | |
| 92 | #dhcp-host=br3,47:b5:1d:54:5c:fb,192.168.3.100,desktop,24h | |
| 93 | #dhcp-host=br3,64:67:16:cd:c7:c0,59:21:2d:99:28:70,192.168.3.101 | |
| 94 | # per-network static leases for device w/ multiple network adapters | |
| 95 | #dhcp-host=br0,b8:70:f4:b3:4d:6a,38:59:f9:14:1f:d3,192.168.1.99,laptop | |
| 96 | #dhcp-host=br10,b8:70:f4:b3:4d:6a,38:59:f9:14:1f:d3,192.168.10.99,laptop | |
| 97 | #dhcp-host=br11,b8:70:f4:b3:4d:6a,38:59:f9:14:1f:d3,192.168.11.99,laptop | |
| 98 | #dhcp-host=br12,b8:70:f4:b3:4d:6a,38:59:f9:14:1f:d3,192.168.12.99,laptop | |
| 99 | # hostnames (dynamic leases only) | |
| 100 | #dhcp-host=a3:fa:ca:ba:07:2c,somehostname1 | |
| 101 | #dhcp-host=43:f3:52:dd:67:d9,somehostname2 | |
| 102 | ' | |
| 103 | - | cat << EOF >> $CONFIG |
| 103 | + | # function firewall_additions( vlan-id ) |
| 104 | - | interface=br${1}
|
| 104 | + | firewall_additions() {
|
| 105 | - | dhcp-range=br${1},${IPNET_PFX}${1}.100,${IPNET_PFX}${1}.254,255.255.255.0,24h
|
| 105 | + | |
| 106 | - | dhcp-option=br${1},3,${IPNET_PFX}${1}.1
|
| 106 | + | local brx=br${1} # generalize reference to current bridge under examination
|
| 107 | - | $([ "$DNS_SERVERS" ] && echo "dhcp-option=br${1},6,$DNS_SERVERS")
|
| 107 | + | |
| 108 | # useful constants | |
| 109 | local WAN_IF="$WAN_IF" | |
| 110 | local LAN_IP="$(nvram get lan_ipaddr)" # (e.g., 192.168.1.1) | |
| 111 | - | # function add_user_defined_directives() |
| 111 | + | local LAN_NET="$LAN_IP/$(nvram get lan_netmask)" # (e.g., 192.168.1.0/24) |
| 112 | - | add_user_defined_directives() {
|
| 112 | + | local LAN_PFX="$(echo $LAN_IP | grep -o '^.*\.')" # (e.g., 192.168.1.) |
| 113 | ||
| 114 | - | cat << "EOF" >> $CONFIG |
| 114 | + | case "$1" in |
| 115 | - | # >>>>>>>>>>>>>>>>>>>>>> begin user-defined directives <<<<<<<<<<<<<<<<<<<<<<< # |
| 115 | + | |
| 116 | # these are just examples; uncomment and modify as you see fit | |
| 117 | ||
| 118 | 3) ### vlan3/br3 rules go here ### | |
| 119 | # allow access to printer hosted on router | |
| 120 | #iptables -I INPUT -p tcp -i $brx --dport 9100 -j ACCEPT | |
| 121 | :;; # DO NOT DISTURB | |
| 122 | ||
| 123 | 4) ### vlan4/br4 rules go here ### | |
| 124 | # deny routing to internet based on source ip range | |
| 125 | #iptables -I FORWARD -i $brx -o $WAN_IF -m iprange \ | |
| 126 | # --src-range "${IP_PFX}${1}.110-${IP_PFX}${1}.119" -j REJECT
| |
| 127 | - | # >>>>>>>>>>>>>>>>>>>>>>> end user-defined directives <<<<<<<<<<<<<<<<<<<<<<<< # |
| 127 | + | # deny routing to internet based on source mac address |
| 128 | #iptables -I FORWARD -i $brx -o $WAN_IF -m mac --mac-source \ | |
| 129 | # 0a:32:13:75:7d:95 -j REJECT | |
| 130 | :;; # DO NOT DISTURB | |
| 131 | - | > $CONFIG |
| 131 | + | |
| 132 | 5) ### vlan5/br5 rules go here ### | |
| 133 | # deny routing from 10:00PM to 6:00AM, Sunday->Friday (student schedule) | |
| 134 | #iptables -I FORWARD -i $brx -m time --timestart 22:00 --timestop 00:00 \ | |
| 135 | # --weekdays Sun,Mon,Tue,Wed,Thu --kerneltz -j REJECT | |
| 136 | #iptables -I FORWARD -i $brx -m time --timestart 00:00 --timestop 06:00 \ | |
| 137 | # --weekdays Mon,Tue,Wed,Thu,Fri --kerneltz -j REJECT | |
| 138 | :;; # DO NOT DISTURB | |
| 139 | ||
| 140 | # repeat as necessary for additional vlans/bridges | |
| 141 | ||
| 142 | *) ### rules that apply across all vlans/bridges (br+) go here ### | |
| 143 | # allow access to printer hosted on private network (other than router) | |
| 144 | - | add_user_defined_directives |
| 144 | + | |
| 145 | # --dport 9100 -j ACCEPT | |
| 146 | :;; # DO NOT DISTURB | |
| 147 | - | if [ -f $CONFIG ]; then |
| 147 | + | |
| 148 | - | echo "error: $CONFIG already exists; requires manual installation" |
| 148 | + | |
| 149 | } | |
| 150 | - | create_config |
| 150 | + | |
| 151 | - | echo "installed: $CONFIG" |
| 151 | + | |
| 152 | ||
| 153 | - | ) |
| 153 | + | |
| 154 | ||
| 155 | - | # --------------------------- end dnsmasq.conf.add --------------------------- # |
| 155 | + | # shared/global functions and constants (do NOT touch!) |
| 156 | ||
| 157 | debug_enabled() { set -o | grep -Eq '^xtrace\s+on$'; }
| |
| 158 | - | SCRIPT="$SCRIPTS_DIR/firewall-start" |
| 158 | + | |
| 159 | BASENAME="$(basename $0)" | |
| 160 | - | if [ "${INCLUDE_FIREWALL+x}" ]; then
|
| 160 | + | NOW="$(date +'%Y-%m-%d-%H%M%S')" |
| 161 | LOG="$(debug_enabled && echo /tmp/${BASENAME}_${NOW}_$$.log || echo /dev/null)"
| |
| 162 | MIN_VID=3 MAX_VID=255 MIN_PORT=1 MAX_PORT=4 | |
| 163 | - | cat << "EOF" > $SCRIPT |
| 163 | + | [ "$IP_PFX" ] || IP_PFX="$(nvram get lan_ipaddr | grep -Eo '^(.{1,3}\.){2}')"
|
| 164 | ||
| 165 | - | set -x # uncomment/comment to enable/disable debug mode |
| 165 | + | |
| 166 | echo "installed: $CONFIG" | |
| 167 | # ------------------ end merlin-ac68u-add-networks.options ------------------- # | |
| 168 | ||
| 169 | - | LAN_IP="$(nvram get lan_ipaddr)" # (e.g., 192.168.1.1) |
| 169 | + | # ----------------- begin merlin-ac68u-add-networks.dnsmasq ------------------ # |
| 170 | - | LAN_NET="$LAN_IP/$(nvram get lan_netmask)" # (e.g., 192.168.1.0/24) |
| 170 | + | if grep -q '^INCLUDE_DNSMASQ=' $CONFIG; then |
| 171 | - | LAN_PFX="$(echo $LAN_IP | grep -o '^.*\.')" # (e.g., 192.168.1.) |
| 171 | + | cat << 'EOF' > $SCRIPT1 |
| 172 | #!/bin/sh | |
| 173 | - | # function router_is_bridged() |
| 173 | + | #set -x # comment/uncomment to disable/enable debug mode |
| 174 | - | router_is_bridged() { ! ifconfig vlan2 2>/dev/null | grep -q 'UP'; }
|
| 174 | + | . $CONFIG |
| 175 | {
| |
| 176 | . /usr/sbin/helper.sh | |
| 177 | ||
| 178 | DNSMASQ_CONFIG="$1" | |
| 179 | ||
| 180 | - | if ! echo $vlan_id | egrep -q '^[0-9]+$'; then |
| 180 | + | # function write( string ) |
| 181 | write() { pc_append "$1" $DNSMASQ_CONFIG; }
| |
| 182 | ||
| 183 | # function validate_vlan_id( vlan-id ) | |
| 184 | validate_vlan_id() {
| |
| 185 | local vlan_id=$1 | |
| 186 | ||
| 187 | if ! echo $vlan_id | grep -Eq '^[0-9]+$'; then | |
| 188 | return 1 | |
| 189 | elif [[ $vlan_id -lt $MIN_VID || $vlan_id -gt $MAX_VID ]]; then | |
| 190 | return 1 | |
| 191 | fi | |
| 192 | - | # limit new bridge to essential router services (dhcp, dns, ping) |
| 192 | + | |
| 193 | - | iptables -I INPUT -i br${1} -j REJECT
|
| 193 | + | |
| 194 | - | iptables -I INPUT -i br${1} -d ${IPNET_PFX}${1}.1 -p icmp -j ACCEPT
|
| 194 | + | |
| 195 | - | if [ ! "$DNS_SERVERS" ]; then |
| 195 | + | |
| 196 | - | iptables -I INPUT -i br${1} -d ${IPNET_PFX}${1}.1 -p tcp --dport 53 -j ACCEPT
|
| 196 | + | |
| 197 | - | iptables -I INPUT -i br${1} -d ${IPNET_PFX}${1}.1 -p udp --dport 53 -j ACCEPT
|
| 197 | + | |
| 198 | write "interface=br${1}"
| |
| 199 | - | iptables -I INPUT -i br${1} -p udp --dport 67 -j ACCEPT
|
| 199 | + | write "dhcp-range=br${1},${IP_PFX}${1}.100,${IP_PFX}${1}.254,255.255.255.0,24h"
|
| 200 | - | if router_is_bridged; then |
| 200 | + | write "dhcp-option=br${1},3,${IP_PFX}${1}.1"
|
| 201 | - | iptables -I INPUT -i lo -j ACCEPT |
| 201 | + | [ "$DNS_SERVERS" ] && write "dhcp-option=br${1},6,$DNS_SERVERS"
|
| 202 | - | iptables -I INPUT -i br0 -j ACCEPT |
| 202 | + | |
| 203 | - | iptables -I INPUT -m state --state INVALID -j DROP |
| 203 | + | |
| 204 | - | iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT |
| 204 | + | write "# --- begin additions by $BASENAME --- #" |
| 205 | ||
| 206 | # add dhcp server configuration for each new bridge | |
| 207 | - | # define routing limits of new bridge (default is internet only) |
| 207 | + | |
| 208 | - | iptables -I FORWARD -i br${1} -j REJECT
|
| 208 | + | |
| 209 | - | if [ ! "$ALLOW_PRIVATE_TO_ANY" ]; then |
| 209 | + | |
| 210 | - | iptables -I FORWARD -i br0 -o br${1} -j REJECT
|
| 210 | + | |
| 211 | [ $vlan_id ] || continue | |
| 212 | - | if router_is_bridged; then |
| 212 | + | |
| 213 | - | iptables -I FORWARD -i br${1} -o br0 -j ACCEPT
|
| 213 | + | |
| 214 | - | #iptables -I FORWARD -i br${1} -o br0 ! -d $LAN_NET -j ACCEPT
|
| 214 | + | |
| 215 | - | iptables -I FORWARD -i br${1} -o br0 -d 10.0.0.0/8 -j REJECT
|
| 215 | + | |
| 216 | - | iptables -I FORWARD -i br${1} -o br0 -d 172.16.0.0/12 -j REJECT
|
| 216 | + | |
| 217 | - | iptables -I FORWARD -i br${1} -o br0 -d 192.168.0.0/16 -j REJECT
|
| 217 | + | # add user-defined directives |
| 218 | - | iptables -I FORWARD -m state --state INVALID -j DROP |
| 218 | + | OIFS="$IFS"; IFS=$'\n' |
| 219 | - | iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT |
| 219 | + | for line in $DNSMASQ_ADDITIONS; do |
| 220 | - | iptables -t nat -I POSTROUTING -o br0 -j SNAT --to $LAN_IP |
| 220 | + | echo $line | grep -Eq '^[[:space:]]*(#|$)' || write "$line" |
| 221 | done | |
| 222 | - | if [ "$ALLOW_OVPN_ACCESS" ]; then |
| 222 | + | IFS="$OIFS" |
| 223 | - | iptables -I FORWARD -i tun2+ -o br${1} -j ACCEPT
|
| 223 | + | |
| 224 | - | iptables -I FORWARD -i br${1} -o tun1+ -j ACCEPT
|
| 224 | + | write "# ---- end additions by $BASENAME ---- #" |
| 225 | - | fi |
| 225 | + | |
| 226 | - | iptables -I FORWARD -i br${1} -m conntrack --ctstate DNAT -j ACCEPT
|
| 226 | + | |
| 227 | - | iptables -I FORWARD -i br${1} -o $WAN_IF -j ACCEPT
|
| 227 | + | } 2>&1 | tee $LOG | logger -t $BASENAME[$$] |
| 228 | EOF | |
| 229 | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT1
| |
| 230 | sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT1 | |
| 231 | - | # function add_user_defined_rules( bridge-index ) |
| 231 | + | chmod +x $SCRIPT1 |
| 232 | - | add_user_defined_rules() {
|
| 232 | + | echo "installed: $SCRIPT1" |
| 233 | fi | |
| 234 | - | brx=br${1} # generalize reference to current bridge under examination
|
| 234 | + | # ------------------ end merlin-ac68u-add-networks.dnsmasq ------------------- # |
| 235 | ||
| 236 | - | case $1 in |
| 236 | + | # ----------------- begin merlin-ac68u-add-networks.firewall ----------------- # |
| 237 | - | # >>>>>>>>>>>>>>>>>>>>>>>>> begin user-defined rules <<<<<<<<<<<<<<<<<<<<<<<<< # |
| 237 | + | if grep -q '^INCLUDE_FIREWALL=' $CONFIG; then |
| 238 | cat << 'EOF' > $SCRIPT2 | |
| 239 | #!/bin/sh | |
| 240 | #set -x # comment/uncomment to disable/enable debug mode | |
| 241 | . $CONFIG | |
| 242 | {
| |
| 243 | WAN_IF="$([ $1 ] && echo $1 || echo $(nvram get wan0_ifname))" | |
| 244 | ||
| 245 | - | # deny routing based on ip range |
| 245 | + | |
| 246 | - | #iptables -I FORWARD -i $brx -m iprange \ |
| 246 | + | |
| 247 | - | # --src-range "${IPNET_PFX}${1}.110-${IPNET_PFX}${1}.119" -j REJECT
|
| 247 | + | |
| 248 | ||
| 249 | - | # deny routing based on mac address |
| 249 | + | if ! echo $vlan_id | grep -Eq '^[0-9]+$'; then |
| 250 | - | #iptables -I FORWARD -i $brx -m mac --mac-source 0a:32:13:75:7d:95 -j REJECT |
| 250 | + | |
| 251 | elif [[ $vlan_id -lt $MIN_VID || $vlan_id -gt $MAX_VID ]]; then | |
| 252 | return 1 | |
| 253 | fi | |
| 254 | - | # deny routing from 10:00PM to 6:00AM, Sunday thru Friday (student scheduling) |
| 254 | + | |
| 255 | return 0 | |
| 256 | } | |
| 257 | ||
| 258 | # function add_rules( bridge-index ) | |
| 259 | add_rules() {
| |
| 260 | # limit new bridge to essential router services (dhcp, dns, ping) | |
| 261 | iptables -I INPUT -i br${1} -j REJECT
| |
| 262 | iptables -I INPUT -i br${1} -d ${IP_PFX}${1}.1 -p icmp -j ACCEPT
| |
| 263 | if [ ! "$DNS_SERVERS" ]; then | |
| 264 | iptables -I INPUT -i br${1} -d ${IP_PFX}${1}.1 -p tcp --dport 53 -j ACCEPT
| |
| 265 | iptables -I INPUT -i br${1} -d ${IP_PFX}${1}.1 -p udp --dport 53 -j ACCEPT
| |
| 266 | fi | |
| 267 | iptables -I INPUT -i br${1} -p udp --dport 67 -j ACCEPT
| |
| 268 | ||
| 269 | - | # >>>>>>>>>>>>>>>>>>>>>>>>>> end user-defined rules <<<<<<<<<<<<<<<<<<<<<<<<<< # |
| 269 | + | # define routing limits of new bridge (default is internet only) |
| 270 | iptables -I FORWARD -i br${1} -j REJECT
| |
| 271 | if [ ! ${ALLOW_PRIVATE_TO_ANY+x} ]; then
| |
| 272 | iptables -I FORWARD -i br0 -o br${1} -j REJECT
| |
| 273 | fi | |
| 274 | if [ ${ALLOW_OVPN_ACCESS+x} ]; then
| |
| 275 | iptables -I FORWARD -i tun2+ -o br${1} -j ACCEPT
| |
| 276 | iptables -I FORWARD -i br${1} -o tun1+ -j ACCEPT
| |
| 277 | fi | |
| 278 | iptables -I FORWARD -i br${1} -m conntrack --ctstate DNAT -j ACCEPT
| |
| 279 | iptables -I FORWARD -i br${1} -o $WAN_IF -j ACCEPT
| |
| 280 | } | |
| 281 | ||
| 282 | # add firewall rules for each new bridge | |
| 283 | for vp in $VLANS_PORTS; do | |
| 284 | vlan_id="$(echo $vp | cut -d/ -f1)" | |
| 285 | ||
| 286 | - | add_user_defined_rules $vlan_id |
| 286 | + | |
| 287 | [ $vlan_id ] || continue | |
| 288 | ||
| 289 | validate_vlan_id $vlan_id || continue | |
| 290 | - | add_user_defined_rules '+' |
| 290 | + | |
| 291 | # add rules for this vlan-id | |
| 292 | add_rules $vlan_id | |
| 293 | - | } 2>&1 | tee $LOG | logger -t $(basename $0)[$$] |
| 293 | + | |
| 294 | # add user-defined rules (if any) for this vlan-id | |
| 295 | - | [ ${DEBUG+x} ] || sed -ri 's/^(set -x)/#\1/g' $SCRIPT
|
| 295 | + | firewall_additions $vlan_id |
| 296 | - | sed -e "s:\$LOG:$LOG:g" \ |
| 296 | + | |
| 297 | - | -e "s:\$MIN_VID:$MIN_VID:g" \ |
| 297 | + | |
| 298 | - | -e "s:\$MAX_VID:$MAX_VID:g" \ |
| 298 | + | |
| 299 | - | -e "s:\$VLANS_PORTS:$(echo $VLANS_PORTS):g" \ |
| 299 | + | firewall_additions '+' |
| 300 | - | -e "s:\${IPNET_PFX}:$IPNET_PFX:g" \
|
| 300 | + | |
| 301 | - | -e "s:\$DNS_SERVERS:$DNS_SERVERS:g" \ |
| 301 | + | |
| 302 | - | -e "s:\$ALLOW_PRIVATE_TO_ANY:${ALLOW_PRIVATE_TO_ANY+x}:g" \
|
| 302 | + | } 2>&1 | tee $LOG | logger -t $BASENAME[$$] |
| 303 | - | -e "s:\$ALLOW_OVPN_ACCESS:${ALLOW_OVPN_ACCESS+x}:g" \
|
| 303 | + | |
| 304 | - | -i $SCRIPT |
| 304 | + | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT2
|
| 305 | - | chmod +x $SCRIPT |
| 305 | + | sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT2 |
| 306 | chmod +x $SCRIPT2 | |
| 307 | echo "installed: $SCRIPT2" | |
| 308 | - | if [ -f $SCRIPT ]; then |
| 308 | + | |
| 309 | - | echo "error: $SCRIPT already exists; requires manual installation" |
| 309 | + | # ------------------ end merlin-ac68u-add-networks.firewall ------------------ # |
| 310 | ||
| 311 | # -------------- begin merlin-ac68u-add-networks.services-start -------------- # | |
| 312 | - | echo "installed: $SCRIPT" |
| 312 | + | cat << 'EOF' > $SCRIPT3 |
| 313 | #!/bin/sh | |
| 314 | #set -x # comment/uncomment to disable/enable debug mode | |
| 315 | . $CONFIG | |
| 316 | {
| |
| 317 | CPU_PORT="$(robocfg show | awk '/vlan1:/{print $NF}')"
| |
| 318 | ||
| 319 | - | SCRIPT="$SCRIPTS_DIR/service-event-end" |
| 319 | + | |
| 320 | validate_vlan_id() {
| |
| 321 | local vlan_id=$1 | |
| 322 | - | cat << "EOF" > $SCRIPT |
| 322 | + | |
| 323 | if [ $vlan_id != '1' ]; then | |
| 324 | - | set -x # uncomment/comment to enable/disable debug mode |
| 324 | + | |
| 325 | echo "error: changes to vlan2 (wan) NOT supported" | |
| 326 | return 1 | |
| 327 | elif ! echo $vlan_id | grep -Eq '^[0-9]+$'; then | |
| 328 | echo "error: vlan-id ${vlan_id} not numeric"
| |
| 329 | return 1 | |
| 330 | elif [[ $vlan_id -lt $MIN_VID || $vlan_id -gt $MAX_VID ]]; then | |
| 331 | echo "error: vlan${vlan_id} out of range ($MIN_VID-$MAX_VID)"
| |
| 332 | return 1 | |
| 333 | fi | |
| 334 | fi | |
| 335 | ||
| 336 | - | elif ! echo $vlan_id | egrep -q '^[0-9]+$'; then |
| 336 | + | |
| 337 | } | |
| 338 | ||
| 339 | # function add_vlan_and_bridge( vlan-id ports ) | |
| 340 | add_vlan_and_bridge() {
| |
| 341 | local vlan_id=$1 | |
| 342 | local ports="$2" | |
| 343 | ||
| 344 | # create new vlan w/ specified port(s) | |
| 345 | robocfg vlan $vlan_id ports "$ports" | |
| 346 | ||
| 347 | # add new vlan to eth0 (cpu) network interface | |
| 348 | vconfig add eth0 $vlan_id | |
| 349 | ||
| 350 | # bring up new vlan | |
| 351 | ifconfig vlan${vlan_id} up
| |
| 352 | ||
| 353 | # create new bridge and add new vlan | |
| 354 | brctl addbr br${vlan_id}
| |
| 355 | brctl addif br${vlan_id} vlan${vlan_id}
| |
| 356 | ||
| 357 | # configure new bridge w/ preferred settings | |
| 358 | stp=$([ "$(nvram get lan_stp)" == '1' ] && echo 'on' || echo 'off') | |
| 359 | brctl stp br${vlan_id} $stp # stp to prevent bridge loops
| |
| 360 | brctl setfd br${vlan_id} 2 # stp forward delay (2 secs)
| |
| 361 | ||
| 362 | # config ip on new bridge and bring up network | |
| 363 | ifconfig br${vlan_id} ${IP_PFX}${vlan_id}.1 netmask 255.255.255.0 up
| |
| 364 | } | |
| 365 | ||
| 366 | # respond to *all* wireless events (start/restart/stop) | |
| 367 | - | stp="$([ "$(nvram get lan_stp)" == '1' ] && echo 'on' || echo 'off')" |
| 367 | + | |
| 368 | ||
| 369 | # cleanup any previous vlan/bridge configurations | |
| 370 | n=$MIN_VID | |
| 371 | while [ "$(nvram get br${n}_ifname)" ]; do
| |
| 372 | - | ifconfig br${vlan_id} ${IPNET_PFX}${vlan_id}.1 netmask 255.255.255.0 up
|
| 372 | + | |
| 373 | vl="$(nvram get br${n}_ifnames | cut -d' ' -f1)"
| |
| 374 | ||
| 375 | ifconfig $br down 2>/dev/null && brctl delbr $br && vconfig rem $vl | |
| 376 | ||
| 377 | nvram unset br${n}_ifname
| |
| 378 | nvram unset br${n}_ifnames
| |
| 379 | nvram unset lan${n}_ifname
| |
| 380 | nvram unset lan${n}_ifnames
| |
| 381 | ||
| 382 | [ $((++n)) -le $MAX_VID ] || break | |
| 383 | done | |
| 384 | ||
| 385 | # commit changes from cleanup if no other actions requested/required | |
| 386 | [[ $((n)) -gt $MIN_VID && ! "$VLANS_PORTS" && ! "$VLANS_WL" ]] && nvram commit | |
| 387 | ||
| 388 | # assign ports to vlans | |
| 389 | for vp in $VLANS_PORTS; do | |
| 390 | vlan_id="$(echo $vp | cut -d/ -f1)" | |
| 391 | ||
| 392 | # ignore bad/missing input | |
| 393 | [ $vlan_id ] || continue | |
| 394 | ||
| 395 | validate_vlan_id $vlan_id || continue | |
| 396 | ||
| 397 | # isolate ports from vlan-id | |
| 398 | vlan_ports="$(echo $vp | awk -F/ '{$1=""; print $0}')"
| |
| 399 | ||
| 400 | # validate ports | |
| 401 | for p in $vlan_ports; do | |
| 402 | if ! echo $p | grep -Eq '^[0-9]+[tu*]{0,1}$'; then
| |
| 403 | echo "error: port $p specification not valid" | |
| 404 | continue 2 | |
| 405 | fi | |
| 406 | _p="$(echo $p | grep -Eo '^[0-9]*')" | |
| 407 | if [[ $_p -lt $MIN_PORT || $_p -gt $MAX_PORT ]]; then | |
| 408 | echo "error: port $p out of range ($MIN_PORT-$MAX_PORT)" | |
| 409 | continue 2 | |
| 410 | fi | |
| 411 | - | if ! echo $p | egrep -q '^[0-9]+[tu*]{0,1}$'; then
|
| 411 | + | |
| 412 | ||
| 413 | # add cpu port to ports | |
| 414 | vlan_ports="$(echo $vlan_ports $CPU_PORT)" | |
| 415 | - | _p="$(echo $p | egrep -o '^[0-9]*')" |
| 415 | + | |
| 416 | if [ $vlan_id == '1' ]; then | |
| 417 | robocfg vlan 1 ports "$vlan_ports" | |
| 418 | continue | |
| 419 | fi | |
| 420 | ||
| 421 | add_vlan_and_bridge $vlan_id "$vlan_ports" | |
| 422 | ||
| 423 | # determine next available bridge/lan index | |
| 424 | #n=$vlan_id # doesn't work; must be assigned sequentially | |
| 425 | n=$MIN_VID; while [ "$(nvram get lan${n}_ifname)" ]; do let n++; done
| |
| 426 | ||
| 427 | # add and initialize network interface names | |
| 428 | nvram set br${n}_ifname=br${vlan_id}
| |
| 429 | nvram set br${n}_ifnames=vlan${vlan_id}
| |
| 430 | nvram set lan${n}_ifname=br${vlan_id}
| |
| 431 | nvram set lan${n}_ifnames=vlan${vlan_id}
| |
| 432 | done | |
| 433 | ||
| 434 | # bridge wireless to vlans | |
| 435 | for vw in $VLANS_WL; do | |
| 436 | vlan_id="$(echo $vw | cut -d/ -f1)" | |
| 437 | ||
| 438 | # ignore bad/missing input | |
| 439 | [ $vlan_id ] || continue | |
| 440 | ||
| 441 | validate_vlan_id $vlan_id || continue | |
| 442 | ||
| 443 | # validate vlan-id usage | |
| 444 | if ! ifconfig vlan${vlan_id} &>/dev/null; then
| |
| 445 | echo "error: vlan${vlan_id} not found"
| |
| 446 | continue | |
| 447 | fi | |
| 448 | ||
| 449 | # isolate wireless network interfaces from vlan-id | |
| 450 | vlan_wl="$(echo $(echo $vw | awk -F/ '{$1=""; print $0}'))"
| |
| 451 | ||
| 452 | # move wireless network interfaces to new bridge | |
| 453 | for wl in $vlan_wl; do | |
| 454 | # validate wireless network interface | |
| 455 | case $wl in | |
| 456 | 'eth1'|'eth2'|'wl0.1'|'wl1.1'|'wl0.2'|'wl1.2'|'wl0.3'|'wl1.3') :;; | |
| 457 | *) echo "error: unknown wireless network interface: ${wl}"; continue 2;;
| |
| 458 | esac | |
| 459 | ||
| 460 | # wireless network interface must be up and running | |
| 461 | if ! ifconfig $wl &>/dev/null; then | |
| 462 | echo "error: wireless network interface not available: ${wl}"
| |
| 463 | continue 2 | |
| 464 | fi | |
| 465 | ||
| 466 | # delete wireless network interface from current bridge | |
| 467 | n=0 | |
| 468 | if brctl show | grep -q "\s${wl}\$"; then
| |
| 469 | while ! brctl delif br${n} $wl 2>/dev/null; do
| |
| 470 | if [ $((++n)) -gt $MAX_VID ]; then | |
| 471 | echo "program error: wireless network interface not found: ${wl}"
| |
| 472 | break | |
| 473 | fi | |
| 474 | done | |
| 475 | fi | |
| 476 | ||
| 477 | # add wireless network interface to new bridge | |
| 478 | brctl addif br${vlan_id} $wl
| |
| 479 | done | |
| 480 | ||
| 481 | # find bridge/lan index for this vlan | |
| 482 | n=$MIN_VID | |
| 483 | while ! nvram get br${n}_ifnames | grep -Eq "^vlan${vlan_id}( |$)"; do
| |
| 484 | if [ $((++n)) -gt $MAX_VID ]; then | |
| 485 | echo "program error: bridge/lan index not found: ($n)" | |
| 486 | continue 2 | |
| 487 | fi | |
| 488 | done | |
| 489 | ||
| 490 | # add wireless network interface names to corresponding bridge/lan | |
| 491 | nvram set br${n}_ifnames="$(nvram get br${n}_ifnames) $vlan_wl"
| |
| 492 | - | while ! nvram get br${n}_ifnames | egrep -q "^vlan${vlan_id}( |$)"; do
|
| 492 | + | |
| 493 | ||
| 494 | # convert wireless network interface names to sed search mask | |
| 495 | mask=''; for wl in $vlan_wl; do mask="${mask}${wl}(\s|\$)|"; done
| |
| 496 | ||
| 497 | # remove wireless network interface names from system bridges/lans | |
| 498 | for i in 0 1 2; do | |
| 499 | [ "$(nvram get br${i}_ifnames)" ] && \
| |
| 500 | nvram set br${i}_ifnames="$(echo $(nvram get br${i}_ifnames | \
| |
| 501 | sed -r s/$mask//g))" | |
| 502 | [ "$i" == '0' ] && j='' || j="$i" | |
| 503 | [ "$(nvram get lan${j}_ifnames)" ] && \
| |
| 504 | nvram set lan${j}_ifnames="$(echo $(nvram get lan${j}_ifnames | \
| |
| 505 | sed -r s/$mask//g))" | |
| 506 | done | |
| 507 | done | |
| 508 | ||
| 509 | # force system to recognize any changes | |
| 510 | eapd | |
| 511 | ||
| 512 | exit 0 | |
| 513 | } 2>&1 | tee $LOG | logger -t $BASENAME[$$] | |
| 514 | EOF | |
| 515 | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT3
| |
| 516 | sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT3 | |
| 517 | chmod +x $SCRIPT3 | |
| 518 | echo "installed: $SCRIPT3" | |
| 519 | # --------------- end merlin-ac68u-add-networks.services-start --------------- # | |
| 520 | ||
| 521 | # ------------ begin merlin-ac68u-add-networks.service-event-end ------------- # | |
| 522 | - | } 2>&1 | tee $LOG | logger -t $(basename $0)[$$] |
| 522 | + | cat << 'EOF' > $SCRIPT4 |
| 523 | #!/bin/sh | |
| 524 | - | [ ${DEBUG+x} ] || sed -ri 's/^(set -x)/#\1/g' $SCRIPT
|
| 524 | + | #set -x # comment/uncomment to disable/enable debug mode |
| 525 | - | sed -e "s:\$LOG:$LOG:g" \ |
| 525 | + | . $CONFIG |
| 526 | - | -e "s:\$MIN_VID:$MIN_VID:g" \ |
| 526 | + | |
| 527 | - | -e "s:\$MAX_VID:$MAX_VID:g" \ |
| 527 | + | |
| 528 | - | -e "s:\$MIN_PORT:$MIN_PORT:g" \ |
| 528 | + | |
| 529 | - | -e "s:\$MAX_PORT:$MAX_PORT:g" \ |
| 529 | + | |
| 530 | - | -e "s:\$VLANS_PORTS:$(echo $VLANS_PORTS):g" \ |
| 530 | + | /jffs/scripts/service-event-end 'start' 'wireless' |
| 531 | - | -e "s:\$VLANS_WL:$(echo $VLANS_WL):g" \ |
| 531 | + | |
| 532 | - | -e "s:\${IPNET_PFX}:$IPNET_PFX:g" \
|
| 532 | + | |
| 533 | - | -i $SCRIPT |
| 533 | + | } 2>&1 | tee $LOG | logger -t $BASENAME[$$] |
| 534 | - | chmod +x $SCRIPT |
| 534 | + | |
| 535 | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT4
| |
| 536 | sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT4 | |
| 537 | - | if [ -f $SCRIPT ]; then |
| 537 | + | chmod +x $SCRIPT4 |
| 538 | - | echo "error: $SCRIPT already exists; requires manual installation" |
| 538 | + | echo "installed: $SCRIPT4" |
| 539 | # ------------- end merlin-ac68u-add-networks.service-event-end -------------- # | |
| 540 | ||
| 541 | - | echo "installed: $SCRIPT" |
| 541 | + | # -------------------------- begin dnsmasq.postconf -------------------------- # |
| 542 | if grep -q '^INCLUDE_DNSMASQ=' $CONFIG; then | |
| 543 | create_script() {
| |
| 544 | cat << 'EOF' > $SCRIPT5 | |
| 545 | #!/bin/sh | |
| 546 | - | SCRIPT="$SCRIPTS_DIR/services-start" |
| 546 | + | #set -x # comment/uncomment to disable/enable debug mode |
| 547 | {
| |
| 548 | $SCRIPT1 "$1" | |
| 549 | - | cat << "EOF" > $SCRIPT |
| 549 | + | } 2>&1 | logger -t $(basename $0)[$$] |
| 550 | EOF | |
| 551 | - | set -x # uncomment/comment to enable/disable debug mode |
| 551 | + | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT5
|
| 552 | sed "s:\$SCRIPT1:$SCRIPT1:g" -i $SCRIPT5 | |
| 553 | - | # function router_is_bridged() |
| 553 | + | chmod +x $SCRIPT5 |
| 554 | - | router_is_bridged() { ! ifconfig vlan2 2>/dev/null | grep -q 'UP'; }
|
| 554 | + | |
| 555 | ||
| 556 | - | # in a bridged configuration (e.g., AP mode), the firewall is not activated, |
| 556 | + | if [ -f $SCRIPT5 ]; then |
| 557 | - | # so we have to manually start the firewall-start script |
| 557 | + | echo "error: $SCRIPT5 already exists; requires manual installation" |
| 558 | else | |
| 559 | - | if [ "$INCLUDE_FIREWALL" ] && router_is_bridged; then |
| 559 | + | |
| 560 | - | "$SCRIPTS_DIR/firewall-start" |
| 560 | + | echo "installed: $SCRIPT5" |
| 561 | fi | |
| 562 | - | # enable ip routing for bridged router configurations |
| 562 | + | |
| 563 | - | echo '1' > /proc/sys/net/ipv4/ip_forward |
| 563 | + | # ------------------------ end begin dnsmasq.postconf ------------------------ # |
| 564 | ||
| 565 | # --------------------------- begin firewall-start --------------------------- # | |
| 566 | if grep -q '^INCLUDE_FIREWALL=' $CONFIG; then | |
| 567 | create_script() {
| |
| 568 | cat << 'EOF' > $SCRIPT6 | |
| 569 | #!/bin/sh | |
| 570 | - | "$SCRIPTS_DIR/service-event-end" 'start' 'wireless' |
| 570 | + | #set -x # comment/uncomment to disable/enable debug mode |
| 571 | {
| |
| 572 | $SCRIPT2 "$1" | |
| 573 | - | } 2>&1 | tee $LOG | logger -t $(basename $0)[$$] |
| 573 | + | } 2>&1 | logger -t $(basename $0)[$$] |
| 574 | EOF | |
| 575 | - | [ ${DEBUG+x} ] || sed -ri 's/^(set -x)/#\1/g' $SCRIPT
|
| 575 | + | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT6
|
| 576 | - | sed -e "s:\$LOG:$LOG:g" \ |
| 576 | + | sed "s:\$SCRIPT2:$SCRIPT2:g" -i $SCRIPT6 |
| 577 | - | -e "s:\$SCRIPTS_DIR:$SCRIPTS_DIR:g" \ |
| 577 | + | chmod +x $SCRIPT6 |
| 578 | - | -e "s:\$INCLUDE_FIREWALL:${INCLUDE_FIREWALL+x}:g" \
|
| 578 | + | |
| 579 | - | -i $SCRIPT |
| 579 | + | |
| 580 | - | chmod +x $SCRIPT |
| 580 | + | if [ -f $SCRIPT6 ]; then |
| 581 | echo "error: $SCRIPT6 already exists; requires manual installation" | |
| 582 | else | |
| 583 | - | if [ -f $SCRIPT ]; then |
| 583 | + | |
| 584 | - | echo "error: $SCRIPT already exists; requires manual installation" |
| 584 | + | echo "installed: $SCRIPT6" |
| 585 | fi | |
| 586 | fi | |
| 587 | - | echo "installed: $SCRIPT" |
| 587 | + | |
| 588 | ||
| 589 | - | # ---------------------------- end services-start ---------------------------- # |
| 589 | + | |
| 590 | create_script() {
| |
| 591 | - | fi |
| 591 | + | cat << 'EOF' > $SCRIPT7 |
| 592 | #!/bin/sh | |
| 593 | #set -x # comment/uncomment to disable/enable debug mode | |
| 594 | {
| |
| 595 | $SCRIPT3 "$1" "$2" | |
| 596 | } 2>&1 | logger -t $(basename $0)[$$] | |
| 597 | EOF | |
| 598 | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT7
| |
| 599 | sed "s:\$SCRIPT3:$SCRIPT3:g" -i $SCRIPT7 | |
| 600 | chmod +x $SCRIPT7 | |
| 601 | } | |
| 602 | ||
| 603 | if [ -f $SCRIPT7 ]; then | |
| 604 | echo "error: $SCRIPT7 already exists; requires manual installation" | |
| 605 | else | |
| 606 | create_script | |
| 607 | echo "installed: $SCRIPT7" | |
| 608 | fi | |
| 609 | # -------------------------- end service-event-end --------------------------- # | |
| 610 | ||
| 611 | # --------------------------- begin services-start --------------------------- # | |
| 612 | create_script() {
| |
| 613 | cat << 'EOF' > $SCRIPT8 | |
| 614 | #!/bin/sh | |
| 615 | #set -x # comment/uncomment to disable/enable debug mode | |
| 616 | {
| |
| 617 | $SCRIPT4 | |
| 618 | } 2>&1 | logger -t $(basename $0)[$$] | |
| 619 | EOF | |
| 620 | [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT8
| |
| 621 | sed "s:\$SCRIPT4:$SCRIPT4:g" -i $SCRIPT8 | |
| 622 | chmod +x $SCRIPT8 | |
| 623 | } | |
| 624 | ||
| 625 | if [ -f $SCRIPT8 ]; then | |
| 626 | echo "error: $SCRIPT8 already exists; requires manual installation" | |
| 627 | else | |
| 628 | create_script | |
| 629 | echo "installed: $SCRIPT8" | |
| 630 | fi | |
| 631 | # ---------------------------- end services-start ---------------------------- # |