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