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