| 1 | #!/bin/sh |
| 2 | # Copyright (C) 2006-2011 OpenWrt.org |
| 3 | |
| 4 | # DEBUG="echo" |
| 5 | |
| 6 | do_sysctl() { |
| 7 | [ -n "$2" ] && \ |
| 8 | sysctl -n -e -w "$1=$2" >/dev/null || \ |
| 9 | sysctl -n -e "$1" |
| 10 | } |
| 11 | |
| 12 | map_sysctls() { |
| 13 | local cfg="$1" |
| 14 | local ifn="$2" |
| 15 | |
| 16 | local fam |
| 17 | for fam in ipv4 ipv6; do |
| 18 | if [ -d /proc/sys/net/$fam ]; then |
| 19 | local key |
| 20 | for key in /proc/sys/net/$fam/*/$ifn/*; do |
| 21 | local val |
| 22 | config_get val "$cfg" "${fam}_${key##*/}" |
| 23 | [ -n "$val" ] && echo -n "$val" > "$key" |
| 24 | done |
| 25 | fi |
| 26 | done |
| 27 | } |
| 28 | |
| 29 | find_config() { |
| 30 | local iftype device iface ifaces ifn |
| 31 | for ifn in $interfaces; do |
| 32 | config_get iftype "$ifn" type |
| 33 | config_get iface "$ifn" ifname |
| 34 | case "$iftype" in |
| 35 | bridge) config_get ifaces "$ifn" ifnames;; |
| 36 | esac |
| 37 | config_get device "$ifn" device |
| 38 | for ifc in $device $iface $ifaces; do |
| 39 | [ ."$ifc" = ."$1" ] && { |
| 40 | echo "$ifn" |
| 41 | return 0 |
| 42 | } |
| 43 | done |
| 44 | done |
| 45 | |
| 46 | return 1; |
| 47 | } |
| 48 | |
| 49 | scan_interfaces() { |
| 50 | local cfgfile="${1:-network}" |
| 51 | interfaces= |
| 52 | config_cb() { |
| 53 | case "$1" in |
| 54 | interface) |
| 55 | config_set "$2" auto 1 |
| 56 | ;; |
| 57 | esac |
| 58 | local iftype ifname device proto |
| 59 | config_get iftype "$CONFIG_SECTION" TYPE |
| 60 | case "$iftype" in |
| 61 | interface) |
| 62 | append interfaces "$CONFIG_SECTION" |
| 63 | config_get proto "$CONFIG_SECTION" proto |
| 64 | config_get iftype "$CONFIG_SECTION" type |
| 65 | config_get ifname "$CONFIG_SECTION" ifname |
| 66 | config_get device "$CONFIG_SECTION" device "$ifname" |
| 67 | config_set "$CONFIG_SECTION" device "$device" |
| 68 | case "$iftype" in |
| 69 | bridge) |
| 70 | config_set "$CONFIG_SECTION" ifnames "$device" |
| 71 | config_set "$CONFIG_SECTION" ifname br-"$CONFIG_SECTION" |
| 72 | ;; |
| 73 | esac |
| 74 | ( type "scan_$proto" ) >/dev/null 2>/dev/null && eval "scan_$proto '$CONFIG_SECTION'" |
| 75 | ;; |
| 76 | esac |
| 77 | } |
| 78 | config_load "${cfgfile}" |
| 79 | } |
| 80 | |
| 81 | add_vlan() { |
| 82 | local vif="${1%\.*}" |
| 83 | |
| 84 | [ "$1" = "$vif" ] || ifconfig "$1" >/dev/null 2>/dev/null || { |
| 85 | ifconfig "$vif" up 2>/dev/null >/dev/null || add_vlan "$vif" |
| 86 | $DEBUG vconfig add "$vif" "${1##*\.}" |
| 87 | return 0 |
| 88 | } |
| 89 | return 1 |
| 90 | } |
| 91 | |
| 92 | # add dns entries if they are not in resolv.conf yet |
| 93 | add_dns() { |
| 94 | local cfg="$1"; shift |
| 95 | |
| 96 | remove_dns "$cfg" |
| 97 | |
| 98 | # We may be called by pppd's ip-up which has a nonstandard umask set. |
| 99 | # Create an empty file here and force its permission to 0644, otherwise |
| 100 | # dnsmasq will not be able to re-read the resolv.conf.auto . |
| 101 | [ ! -f /tmp/resolv.conf.auto ] && { |
| 102 | touch /tmp/resolv.conf.auto |
| 103 | chmod 0644 /tmp/resolv.conf.auto |
| 104 | } |
| 105 | |
| 106 | local dns |
| 107 | local add |
| 108 | for dns in "$@"; do |
| 109 | grep -qsE "^nameserver ${dns//./\\.}$" /tmp/resolv.conf.auto || { |
| 110 | add="${add:+$add }$dns" |
| 111 | echo "nameserver $dns" >> /tmp/resolv.conf.auto |
| 112 | } |
| 113 | done |
| 114 | |
| 115 | [ -n "$cfg" ] && { |
| 116 | uci_toggle_state network "$cfg" dns "$add" |
| 117 | uci_toggle_state network "$cfg" resolv_dns "$add" |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | # remove dns entries of the given iface |
| 122 | remove_dns() { |
| 123 | local cfg="$1" |
| 124 | |
| 125 | [ -n "$cfg" ] && { |
| 126 | [ -f /tmp/resolv.conf.auto ] && { |
| 127 | local dns=$(uci_get_state network "$cfg" resolv_dns) |
| 128 | for dns in $dns; do |
| 129 | sed -i -e "/^nameserver ${dns//./\\.}$/d" /tmp/resolv.conf.auto |
| 130 | done |
| 131 | } |
| 132 | |
| 133 | uci_revert_state network "$cfg" dns |
| 134 | uci_revert_state network "$cfg" resolv_dns |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | # sort the device list, drop duplicates |
| 139 | sort_list() { |
| 140 | local arg="$*" |
| 141 | ( |
| 142 | for item in $arg; do |
| 143 | echo "$item" |
| 144 | done |
| 145 | ) | sort -u |
| 146 | } |
| 147 | |
| 148 | prepare_interface_bridge() { |
| 149 | return 0 |
| 150 | } |
| 151 | |
| 152 | # Create the interface, if necessary. |
| 153 | # Return status 0 indicates that the setup_interface() call should continue |
| 154 | # Return status 1 means that everything is set up already. |
| 155 | |
| 156 | prepare_interface() { |
| 157 | local iface="$1" |
| 158 | local config="$2" |
| 159 | local macaddr="$3" |
| 160 | |
| 161 | # if we're called for the bridge interface itself, don't bother trying |
| 162 | # to create any interfaces here. The scripts have already done that, otherwise |
| 163 | # the bridge interface wouldn't exist. |
| 164 | [ "br-$config" = "$iface" -o -e "$iface" ] && return 0; |
| 165 | |
| 166 | ifconfig "$iface" 2>/dev/null >/dev/null && { |
| 167 | local proto |
| 168 | config_get proto "$config" proto |
| 169 | |
| 170 | # make sure the interface is removed from any existing bridge and deconfigured, |
| 171 | # (deconfigured only if the interface is not set to proto=none) |
| 172 | unbridge "$iface" |
| 173 | |
| 174 | local mtu macaddr txqueuelen |
| 175 | config_get mtu "$config" mtu |
| 176 | [ -n "$macaddr" ] || config_get macaddr "$config" macaddr |
| 177 | config_get txqueuelen "$config" txqueuelen |
| 178 | [ -n "$macaddr" ] && $DEBUG ifconfig "$iface" down |
| 179 | $DEBUG ifconfig "$iface" ${macaddr:+hw ether "$macaddr"} ${mtu:+mtu $mtu} ${txqueuelen:+txqueuelen $txqueuelen} up |
| 180 | |
| 181 | [ "$proto" = none ] || ifconfig "$iface" 0.0.0.0 |
| 182 | |
| 183 | # Apply sysctl settings |
| 184 | map_sysctls "$config" "$iface" |
| 185 | } |
| 186 | |
| 187 | # Setup VLAN interfaces |
| 188 | add_vlan "$iface" && return 1 |
| 189 | ifconfig "$iface" 2>/dev/null >/dev/null || return 0 |
| 190 | |
| 191 | # Setup bridging |
| 192 | local iftype |
| 193 | config_get iftype "$config" type |
| 194 | case "$iftype" in |
| 195 | bridge) |
| 196 | local macaddr |
| 197 | config_get macaddr "$config" macaddr |
| 198 | [ -x /usr/sbin/brctl ] && { |
| 199 | ifconfig "br-$config" 2>/dev/null >/dev/null && { |
| 200 | local newdevs devices |
| 201 | config_get devices "$config" device |
| 202 | for dev in $(sort_list "$devices" "$iface"); do |
| 203 | append newdevs "$dev" |
| 204 | done |
| 205 | uci_toggle_state network "$config" device "$newdevs" |
| 206 | $DEBUG ifconfig "$iface" 0.0.0.0 |
| 207 | $DEBUG do_sysctl "net.ipv6.conf.$iface.disable_ipv6" 1 |
| 208 | $DEBUG brctl addif "br-$config" "$iface" |
| 209 | # Bridge existed already. No further processing necesary |
| 210 | } || { |
| 211 | local stp igmp_snooping |
| 212 | config_get_bool stp "$config" stp 0 |
| 213 | config_get_bool igmp_snooping "$config" igmp_snooping 1 |
| 214 | $DEBUG brctl addbr "br-$config" |
| 215 | $DEBUG brctl setfd "br-$config" 0 |
| 216 | $DEBUG ifconfig "$iface" 0.0.0.0 |
| 217 | $DEBUG do_sysctl "net.ipv6.conf.$iface.disable_ipv6" 1 |
| 218 | $DEBUG brctl addif "br-$config" "$iface" |
| 219 | $DEBUG brctl stp "br-$config" $stp |
| 220 | [ -z "$macaddr" ] && macaddr="$(cat /sys/class/net/$iface/address)" |
| 221 | echo $igmp_snooping > /sys/devices/virtual/net/br-$config/bridge/multicast_snooping 2>/dev/null |
| 222 | $DEBUG ifconfig "br-$config" hw ether $macaddr up |
| 223 | # Creating the bridge here will have triggered a hotplug event, which will |
| 224 | # result in another setup_interface() call, so we simply stop processing |
| 225 | # the current event at this point. |
| 226 | } |
| 227 | ifconfig "$iface" ${macaddr:+hw ether "${macaddr}"} up 2>/dev/null >/dev/null |
| 228 | return 1 |
| 229 | } |
| 230 | ;; |
| 231 | esac |
| 232 | return 0 |
| 233 | } |
| 234 | |
| 235 | set_interface_ifname() { |
| 236 | local config="$1" |
| 237 | local ifname="$2" |
| 238 | |
| 239 | local device |
| 240 | config_get device "$1" device |
| 241 | uci_toggle_state network "$config" ifname "$ifname" |
| 242 | uci_toggle_state network "$config" device "$device" |
| 243 | } |
| 244 | |
| 245 | setup_interface_none() { |
| 246 | env -i ACTION="ifup" INTERFACE="$2" DEVICE="$1" PROTO=none /sbin/hotplug-call "iface" & |
| 247 | } |
| 248 | |
| 249 | setup_interface_static() { |
| 250 | local iface="$1" |
| 251 | local config="$2" |
| 252 | |
| 253 | local ipaddr netmask ip6addr |
| 254 | config_get ipaddr "$config" ipaddr |
| 255 | config_get netmask "$config" netmask |
| 256 | config_get ip6addr "$config" ip6addr |
| 257 | [ -z "$ipaddr" -o -z "$netmask" ] && [ -z "$ip6addr" ] && return 1 |
| 258 | |
| 259 | local gateway ip6gw dns bcast metric |
| 260 | config_get gateway "$config" gateway |
| 261 | config_get ip6gw "$config" ip6gw |
| 262 | config_get dns "$config" dns |
| 263 | config_get bcast "$config" broadcast |
| 264 | config_get metric "$config" metric |
| 265 | |
| 266 | case "$ip6addr" in |
| 267 | */*) ;; |
| 268 | *:*) ip6addr="$ip6addr/64" ;; |
| 269 | esac |
| 270 | |
| 271 | [ -z "$ipaddr" ] || $DEBUG ifconfig "$iface" "$ipaddr" netmask "$netmask" broadcast "${bcast:-+}" |
| 272 | [ -z "$ip6addr" ] || $DEBUG ifconfig "${iface%:*}" add "$ip6addr" |
| 273 | [ -z "$gateway" ] || $DEBUG route add default gw "$gateway" ${metric:+metric $metric} dev "$iface" |
| 274 | [ -z "$ip6gw" ] || $DEBUG route -A inet6 add default gw "$ip6gw" ${metric:+metric $metric} dev "${iface%:*}" |
| 275 | [ -z "$dns" ] || add_dns "$config" $dns |
| 276 | |
| 277 | config_get type "$config" TYPE |
| 278 | [ "$type" = "alias" ] && return 0 |
| 279 | |
| 280 | env -i ACTION="ifup" INTERFACE="$config" DEVICE="$iface" PROTO=static /sbin/hotplug-call "iface" & |
| 281 | } |
| 282 | |
| 283 | setup_interface_alias() { |
| 284 | local config="$1" |
| 285 | local parent="$2" |
| 286 | local iface="$3" |
| 287 | |
| 288 | local cfg |
| 289 | config_get cfg "$config" interface |
| 290 | [ "$parent" == "$cfg" ] || return 0 |
| 291 | |
| 292 | # parent device and ifname |
| 293 | local p_device p_type |
| 294 | config_get p_device "$cfg" device |
| 295 | config_get p_type "$cfg" type |
| 296 | |
| 297 | # select alias ifname |
| 298 | local layer use_iface |
| 299 | config_get layer "$config" layer 2 |
| 300 | case "$layer:$p_type" in |
| 301 | # layer 3: e.g. pppoe-wan or pptp-vpn |
| 302 | 3:*) use_iface="$iface" ;; |
| 303 | |
| 304 | # layer 2 and parent is bridge: e.g. br-wan |
| 305 | 2:bridge) use_iface="br-$cfg" ;; |
| 306 | |
| 307 | # layer 1: e.g. eth0 or ath0 |
| 308 | *) use_iface="$p_device" ;; |
| 309 | esac |
| 310 | |
| 311 | # alias counter |
| 312 | local ctr |
| 313 | config_get ctr "$parent" alias_count 0 |
| 314 | ctr="$(($ctr + 1))" |
| 315 | config_set "$parent" alias_count "$ctr" |
| 316 | |
| 317 | # alias list |
| 318 | local list |
| 319 | config_get list "$parent" aliases |
| 320 | append list "$config" |
| 321 | config_set "$parent" aliases "$list" |
| 322 | |
| 323 | use_iface="$use_iface:$ctr" |
| 324 | set_interface_ifname "$config" "$use_iface" |
| 325 | |
| 326 | local proto |
| 327 | config_get proto "$config" proto "static" |
| 328 | case "${proto}" in |
| 329 | static) |
| 330 | setup_interface_static "$use_iface" "$config" |
| 331 | ;; |
| 332 | *) |
| 333 | echo "Unsupported type '$proto' for alias config '$config'" |
| 334 | return 1 |
| 335 | ;; |
| 336 | esac |
| 337 | } |
| 338 | |
| 339 | setup_interface() { |
| 340 | local iface="$1" |
| 341 | local config="$2" |
| 342 | local proto="$3" |
| 343 | local vifmac="$4" |
| 344 | |
| 345 | [ -n "$config" ] || { |
| 346 | config=$(find_config "$iface") |
| 347 | [ "$?" = 0 ] || return 1 |
| 348 | } |
| 349 | |
| 350 | prepare_interface "$iface" "$config" "$vifmac" || return 0 |
| 351 | |
| 352 | [ "$iface" = "br-$config" ] && { |
| 353 | # need to bring up the bridge and wait a second for |
| 354 | # it to switch to the 'forwarding' state, otherwise |
| 355 | # it will lose its routes... |
| 356 | ifconfig "$iface" up |
| 357 | sleep 1 |
| 358 | } |
| 359 | |
| 360 | # Interface settings |
| 361 | set_interface_ifname "$config" "$iface" |
| 362 | |
| 363 | [ -n "$proto" ] || config_get proto "$config" proto |
| 364 | case "$proto" in |
| 365 | static) |
| 366 | setup_interface_static "$iface" "$config" |
| 367 | ;; |
| 368 | dhcp) |
| 369 | # kill running udhcpc instance |
| 370 | local pidfile="/var/run/dhcp-${iface}.pid" |
| 371 | |
| 372 | SERVICE_PID_FILE="$pidfile" \ |
| 373 | service_stop /sbin/udhcpc |
| 374 | |
| 375 | local ipaddr netmask hostname proto1 clientid vendorid broadcast reqopts |
| 376 | config_get ipaddr "$config" ipaddr |
| 377 | config_get netmask "$config" netmask |
| 378 | config_get hostname "$config" hostname |
| 379 | config_get proto1 "$config" proto |
| 380 | config_get clientid "$config" clientid |
| 381 | config_get vendorid "$config" vendorid |
| 382 | config_get_bool broadcast "$config" broadcast 0 |
| 383 | config_get reqopts "$config" reqopts |
| 384 | |
| 385 | [ -z "$ipaddr" ] || \ |
| 386 | $DEBUG ifconfig "$iface" "$ipaddr" ${netmask:+netmask "$netmask"} |
| 387 | |
| 388 | # additional request options |
| 389 | local opt dhcpopts daemonize |
| 390 | for opt in $reqopts; do |
| 391 | append dhcpopts "-O $opt" |
| 392 | done |
| 393 | |
| 394 | # don't stay running in background if dhcp is not the main proto on the interface (e.g. when using pptp) |
| 395 | [ "$proto1" != "$proto" ] && { |
| 396 | append dhcpopts "-n -q" |
| 397 | } || { |
| 398 | append dhcpopts "-O rootpath -R" |
| 399 | daemonize=1 |
| 400 | } |
| 401 | [ "$broadcast" = 1 ] && broadcast="-O broadcast" || broadcast= |
| 402 | |
| 403 | SERVICE_DAEMONIZE=$daemonize \ |
| 404 | SERVICE_PID_FILE="$pidfile" \ |
| 405 | service_start /sbin/udhcpc -t 0 -i "$iface" \ |
| 406 | ${ipaddr:+-r $ipaddr} \ |
| 407 | ${hostname:+-H $hostname} \ |
| 408 | ${clientid:+-c $clientid} \ |
| 409 | ${vendorid:+-V $vendorid} \ |
| 410 | -b -p "$pidfile" $broadcast \ |
| 411 | ${dhcpopts} |
| 412 | ;; |
| 413 | none) |
| 414 | setup_interface_none "$iface" "$config" |
| 415 | ;; |
| 416 | *) |
| 417 | if ( eval "type setup_interface_$proto" ) >/dev/null 2>/dev/null; then |
| 418 | eval "setup_interface_$proto '$iface' '$config' '$proto'" |
| 419 | else |
| 420 | echo "Interface type $proto not supported." |
| 421 | return 1 |
| 422 | fi |
| 423 | ;; |
| 424 | esac |
| 425 | } |
| 426 | |
| 427 | stop_interface_dhcp() { |
| 428 | local config="$1" |
| 429 | |
| 430 | local ifname |
| 431 | config_get ifname "$config" ifname |
| 432 | |
| 433 | local lock="/var/lock/dhcp-${ifname}" |
| 434 | [ -f "$lock" ] && lock -u "$lock" |
| 435 | |
| 436 | remove_dns "$config" |
| 437 | |
| 438 | SERVICE_PID_FILE="/var/run/dhcp-${ifname}.pid" \ |
| 439 | service_stop /sbin/udhcpc |
| 440 | |
| 441 | uci -P /var/state revert "network.$config" |
| 442 | } |
| 443 | |
| 444 | unbridge() { |
| 445 | local dev="$1" |
| 446 | local brdev |
| 447 | |
| 448 | [ -x /usr/sbin/brctl ] || return 0 |
| 449 | brctl show 2>/dev/null | grep "$dev" >/dev/null && { |
| 450 | # interface is still part of a bridge, correct that |
| 451 | |
| 452 | for brdev in $(brctl show | awk '$2 ~ /^[0-9].*\./ { print $1 }'); do |
| 453 | brctl delif "$brdev" "$dev" 2>/dev/null >/dev/null |
| 454 | do_sysctl "net.ipv6.conf.$dev.disable_ipv6" 0 |
| 455 | done |
| 456 | } |
| 457 | } |
| 458 | |