ipv6-support: Second Iteration
[openwrt.git] / package / network / ipv6 / ipv6-support / files / support.sh
1 #!/bin/sh
2 # Copyright (c) 2012 OpenWrt.org
3 . /lib/functions.sh
4 . /lib/functions/service.sh
5 . /lib/functions/network.sh
6
7 config_load network6
8
9
10 conf_get() {
11         local __return="$1"
12         local __device="$2"
13         local __option="$3"
14         local __value=$(cat "/proc/sys/net/ipv6/conf/$device/$option")
15         eval "$__return=$__value"
16 }
17
18
19 conf_set() {
20         local device="$1"
21         local option="$2"
22         local value="$3"
23         echo "$value" > "/proc/sys/net/ipv6/conf/$device/$option"
24 }
25
26
27 stop_service() {
28         local __exe="$1"
29         SERVICE_PID_FILE="$2"
30         local __return="$3"
31
32         service_check "$__exe" && {
33                 service_stop "$__exe"
34                 [ -n "$__return" ] && eval "$__return=1"
35         }
36         rm -f "$SERVICE_PID_FILE"
37 }
38
39
40 start_service() {
41         local cmd="$1"
42         local pidfile="$2"
43
44         SERVICE_DAEMONIZE=1
45         SERVICE_WRITE_PID=1
46         SERVICE_PID_FILE="$pidfile"
47         service_start $cmd
48 }
49
50
51 resolve_network_add() {
52         local __section="$1"
53         local __device="$2"
54         local __return="$3"
55
56         local __cdevice
57         network_get_device __cdevice "$__section"
58         [ "$__cdevice" != "$__device" ] && return
59         
60         eval "$__return"'="'"$__section"'"'
61 }
62
63
64 resolve_network() {
65         local __return="$1"
66         local __device="$2"
67         config_foreach resolve_network_add interface "$__device" "$__return"
68 }
69
70
71 announce_prefix() {
72         local prefix="$1"
73         local network="$2"
74         local cmd="$3"
75
76         local addr=$(echo "$prefix" | cut -d/ -f1)
77         local rem=$(echo "$prefix" | cut -d/ -f2)
78         local length=$(echo "$rem" | cut -d, -f1)
79         local prefer=""
80         local valid=""
81
82         # If preferred / valid provided
83         [ "$rem" != "$length" ] && {
84                 prefer=$(echo "$rem" | cut -d, -f2)
85                 valid=$(echo "$rem" | cut -d, -f3)
86         }
87
88         local msg='{"network": "'"$network"'", "prefix": "'"$addr"'", "length": '"$length"
89         [ -n "$valid" ] && msg="$msg"', "valid": '"$valid"', "preferred": '"$prefer"
90         [ -z "$cmd" ] && cmd=newprefix
91         
92         ubus call 6distributed "$cmd" "$msg}"
93 }
94
95
96 disable_router() {
97         local network="$1"
98
99         # Notify the address distribution daemon
100         ubus call 6distributed deliface '{"network": "'"$network"'"}'
101
102         # Disable advertisement daemon
103         stop_service /usr/sbin/6relayd "/var/run/ipv6-router-$network.pid"
104 }
105
106
107 restart_relay_slave() {
108         local __section="$1"
109         local __master="$2"
110
111         network_is_up "$__section" || return
112
113         local __device=""
114         network_get_device __device "$__section"
115
116         local __cmaster=""
117         config_get __cmaster "$__section" relay_master
118
119         [ "$__master" == "$__cmaster" ] && {
120                 disable_interface "$__section"
121                 enable_interface "$__section" "$__device"
122         }
123 }
124
125
126 add_relay_slave() {
127         local __section="$1"
128         local __return="$2"
129         local __master="$3"
130         local __mode="$4"
131
132         network_is_up "$__section" || return
133
134         # Get device
135         local __device=""
136         network_get_device __device "$__section"
137
138         # Match master network
139         local __cmaster=""
140         config_get __cmaster "$__section" relay_master
141         [ "$__master" == "$__cmaster" ] || return
142         
143         # Test slave  mode
144         local __cmode=""
145         config_get __cmode "$__section" mode
146         [ "$__cmode" == "downstream" ] && __cmode="router"
147
148         # Don't start fallback interfaces if we are in forced-relay mode
149         [ "$__cmode" == "relay" -o "$__mode" == "fallback" ] || return
150
151         # Don't make non-relay or non-router interfaces slaves
152         [ "$__cmode" == "relay" -o "$__cmode" == "router" ] || return
153
154         # Disable any active distribution
155         [ "$__cmode" == "router" ] && disable_router "$__section"
156
157         eval "$__return"'="$'"$__return"' '"$__device"'"'
158 }
159
160
161 stop_relay() {
162         local network="$1"
163         local pid_fallback="/var/run/ipv6-relay-fallback-$network.pid"
164         local pid_forced="/var/run/ipv6-relay-forced-$network.pid"
165         local was_fallback=""
166         
167         stop_service /usr/sbin/6relayd "$pid_fallback" was_fallback
168         stop_service /usr/sbin/6relayd "$pid_forced"
169
170         # Reenable normal distribution on slave interfaces      
171         [ -n "$was_fallback" ] && config_foreach restart_relay_slave interface "$network"
172 }
173
174
175 detect_forced_relay_mode() {
176         local __section="$1"
177         local __mode="$2"
178
179         local __cmode
180         config_get __cmode "$__section" mode
181         [ "$__cmode" == "relay" ] && eval "$__mode=forced"
182 }
183
184
185 restart_relay() {
186         local network="$1"
187         local mode="$2"
188
189         # Stop last active relay
190         stop_relay "$network"
191
192         # Detect if we have a forced-relay
193         [ -z "$mode" ] && config_foreach detect_forced_relay_mode interface mode
194
195         # Don't start without a mode
196         [ -z "$mode" ] && return
197
198         # Detect master device
199         local device=""
200         network_get_device device "$network"
201
202         # Generate command string
203         local cmd="/usr/sbin/6relayd -A $device"
204         local ifaces=""
205         config_foreach add_relay_slave interface ifaces "$network" "$mode"
206
207         # Start relay
208         local pid="/var/run/ipv6-relay-$mode-$network.pid"
209         [ -n "$ifaces" ] && start_service "$cmd $ifaces" "$pid"
210
211         # There are no slave interface, however indicate that we want to relay
212         [ -z "$ifaces" ] && touch "$pid"
213 }
214
215
216 restart_master_relay() {
217         local network="$1"
218         local mode="$2"
219         local pid_fallback="/var/run/ipv6-relay-fallback-$network.pid"
220         local pid_forced="/var/run/ipv6-relay-forced-$network.pid"
221
222         # Disable active relaying to this interface
223         config_get relay_master "$network" relay_master
224         [ -z "$relay_master" ] && return
225         network_is_up "$relay_master" || return
226
227         # Detect running mode
228         [ -z "$mode" && -f "$pid_fallback" ] && mode="fallback"
229         [ -z "$mode" && -f "$pid_forced" ] && mode="forced"
230
231         # Restart relay if running or start requested
232         [ -n "$mode" ] && restart_relay "$relay_master" "$mode"
233 }
234
235
236 disable_interface() {
237         local network="$1"
238
239         # Delete all prefixes routed to this interface
240         ubus call 6distributed delprefix '{"network": "'"$network"'"}'
241
242         # Restart Relay
243         restart_master_relay "$network"
244
245         # Disable distribution
246         disable_router "$network"
247
248         # Disable relay
249         stop_relay "$network"
250
251         # Disable DHCPv6 client if enabled, state script will take care
252         stop_service /usr/sbin/odhcp6c "/var/run/ipv6-dhcpv6-$network.pid"
253 }
254
255
256 enable_static() {
257         local network="$1"
258         local device="$2"
259
260         # Enable global forwarding
261         local global_forward
262         conf_get global_forward all forwarding
263         [ "$global_forward" != "1" ] && conf_set all forwarding 1
264
265         # Configure device
266         conf_set "$device" accept_ra 1
267         conf_set "$device" forwarding 1
268
269         # ULA-integration
270         local ula_prefix=""
271         config_get ula_prefix "$network" ula_prefix
272
273         # ULA auto configuration (first init)
274         [ "$ula_prefix" == "auto" ] && {
275                 local r1=""
276                 local r2=""
277                 local r3=""
278
279                 # Sometimes results are empty, therefore try until it works...          
280                 while [ -z "$r1" -o -z "$r2" -o -z "$r3" ]; do
281                         r1=$(printf "%02x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 256)))
282                         r2=$(printf "%01x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 65536)))
283                         r3=$(printf "%01x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 65536)))
284                 done
285                 
286                 ula_prefix="fd$r1:$r2:$r3::/48"
287
288                 # Save prefix so it will be preserved across reboots
289                 uci set network6.$network.ula_prefix=$ula_prefix
290                 uci commit network6
291         }
292
293         # Announce ULA
294         [ -n "$ula_prefix" ] && announce_prefix $ula_prefix $network
295
296         # Announce all static prefixes
297         config_list_foreach "$network" static_prefix announce_prefix $network
298
299         # start relay if there are forced relay members
300         restart_relay "$network"
301 }
302
303
304 enable_router() {
305         local network="$1"
306         local device="$2"
307
308         # Get IPv6 prefixes
309         local length
310         config_get length "$network" advertise_prefix
311         [ -z "$length" ] && length=64
312         [ "$length" -ne "0" ] && ubus call 6distributed newiface '{"network": "'"$network"'", "iface": "'"$device"'", "length": '"$length"'}'
313
314         # Start RD & DHCPv6 service
315         local pid="/var/run/ipv6-router-$network.pid"
316         start_service "/usr/sbin/6relayd -Rserver -Dserver . $device" "$pid"
317
318         # Try relaying if necessary
319         restart_master_relay "$network"
320 }
321
322
323 enable_dhcpv6() {
324         local network="$1"
325         local device="$2"
326         
327         # Configure device
328         conf_set "$device" accept_ra 2
329         conf_set "$device" forwarding 2
330         
331         # Trigger RS
332         conf_set "$device" disable_ipv6 1
333         conf_set "$device" disable_ipv6 0
334
335         # Configure DHCPv6-client
336         local dhcp6_opts="$device"
337
338         # Configure DHCPv6-client (e.g. requested prefix)
339         local request_prefix
340         config_get request_prefix "$network" request_prefix
341         [ -z "$request_prefix" ] && request_prefix="auto"
342         [ "$request_prefix" != "no" ] && {
343                 [ "$request_prefix" == "auto" ] && request_prefix=0
344                 dhcp6_opts="-P$request_prefix $dhcp6_opts"
345         }
346         
347         # Start DHCPv6 client
348         local pid="/var/run/ipv6-dhcpv6-$network.pid"
349         start_service "/usr/sbin/odhcp6c -s/lib/ipv6/dhcpv6.sh $dhcp6_opts" "$pid"
350
351         # Refresh RA on all interfaces
352         for pid in /var/run/ipv6-router-*.pid; do
353                 kill -SIGUSR1 $(cat "$pid")
354         done
355 }
356
357
358 enable_interface()
359 {
360         local network="$1"
361         local device="$2"
362         local mode=""
363         config_get mode "$network" mode
364
365         # Compatibility with old mode names
366         [ "$mode" == "downstream" ] && mode=router
367         [ "$mode" == "upstream" ] && mode=dhcpv6
368
369         # Run mode startup code
370         [ "$mode" == "dhcpv6" -o "$mode" == "static" ] && enable_static "$network" "$device"
371         [ "$mode" == "dhcpv6" ] && enable_dhcpv6 "$network" "$device"
372         [ "$mode" == "router" ] && enable_router "$network" "$device"
373         [ "$mode" == "relay" ] && restart_master_relay "$network" forced
374 }