| 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 | local NAT="ip6tables -t nat" |
| 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 | local __cdevice |
| 56 | network_get_device __cdevice "$__section" |
| 57 | [ "$__cdevice" != "$__device" ] && return |
| 58 | |
| 59 | eval "$__return"'="'"$__section"'"' |
| 60 | } |
| 61 | |
| 62 | |
| 63 | resolve_network() { |
| 64 | local __return="$1" |
| 65 | local __device="$2" |
| 66 | config_foreach resolve_network_add interface "$__device" "$__return" |
| 67 | } |
| 68 | |
| 69 | |
| 70 | setup_masquerading() { |
| 71 | local cmd="$1" |
| 72 | local chain="network6_masquerade_$2" |
| 73 | local device="$3" |
| 74 | |
| 75 | $NAT -D POSTROUTING -j "$chain" 2>/dev/null && { |
| 76 | $NAT -F "$chain" 2>/dev/null |
| 77 | $NAT -X "$chain" 2>/dev/null |
| 78 | } |
| 79 | |
| 80 | [ "$cmd" != "stop" ] && { |
| 81 | $NAT -N "$chain" |
| 82 | $NAT -A "$chain" -o "$device" -j MASQUERADE |
| 83 | $NAT -A POSTROUTING -j "$chain" |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | |
| 88 | setup_npt_chain() { |
| 89 | local cmd="$1" |
| 90 | local network="$2" |
| 91 | local chain="network6_npt_$network" |
| 92 | |
| 93 | [ "$cmd" != "start" ] && { |
| 94 | $NAT -D POSTROUTING -j "$chain" 2>/dev/null && { |
| 95 | $NAT -D PREROUTING -j "$chain" 2>/dev/null |
| 96 | $NAT -F "$chain" 2>/dev/null |
| 97 | $NAT -X "$chain" 2>/dev/null |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | [ "$cmd" != "stop" ] && { |
| 102 | $NAT -N "$chain" 2>/dev/null && { |
| 103 | $NAT -A PREROUTING -j "$chain" |
| 104 | $NAT -A POSTROUTING -j "$chain" |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | |
| 110 | announce_prefix() { |
| 111 | local prefix="$1" |
| 112 | local network="$2" |
| 113 | local device="$3" |
| 114 | local cmd="$4" |
| 115 | local type="$5" |
| 116 | |
| 117 | local addr=$(echo "$prefix" | cut -d/ -f1) |
| 118 | local rem=$(echo "$prefix" | cut -d/ -f2) |
| 119 | local length=$(echo "$rem" | cut -d, -f1) |
| 120 | local prefer="" |
| 121 | local valid="" |
| 122 | |
| 123 | # If preferred / valid provided |
| 124 | [ "$rem" != "$length" ] && { |
| 125 | prefer=$(echo "$rem" | cut -d, -f2) |
| 126 | valid=$(echo "$rem" | cut -d, -f3) |
| 127 | } |
| 128 | |
| 129 | # Get prefix configuration |
| 130 | local ula="" |
| 131 | local prefix_action="" |
| 132 | config_get ula global ula_prefix |
| 133 | config_get prefix_action "$network" prefix_action |
| 134 | [ -z "$prefix_action" ] && prefix_action="distribute" |
| 135 | |
| 136 | # Always announce the ULA when doing NPT |
| 137 | [ "$prefix" == "$ula" -a "$prefix_action" == "npt" ] && prefix_action="distribute" |
| 138 | |
| 139 | [ "$prefix_action" == "distribute" -o "$prefix_action" == "npt" ] && { |
| 140 | local msg='{"network": "'"$network"'", "prefix": "'"$addr"'", "length": '"$length" |
| 141 | [ -n "$valid" ] && msg="$msg"', "valid": '"$valid"', "preferred": '"$prefer" |
| 142 | [ -z "$cmd" ] && cmd=newprefix |
| 143 | |
| 144 | [ "$prefix_action" == "npt" ] && msg="$msg"', "npt": 1' |
| 145 | [ "$type" == "secondary" ] && msg="$msg"', "secondary": 1' |
| 146 | |
| 147 | # Detect MTU |
| 148 | local mtu |
| 149 | conf_get mtu "$device" mtu |
| 150 | msg="$msg"', "mtu": '"$mtu" |
| 151 | |
| 152 | ubus call 6distributed "$cmd" "$msg}" |
| 153 | } |
| 154 | |
| 155 | [ "$prefix_action" == "npt" ] && { |
| 156 | local chain="network6_npt_$network" |
| 157 | local ula_addr=$(echo "$ula" | cut -d/ -f1) |
| 158 | local ula_rem=$(echo "$ula" | cut -d/ -f2) |
| 159 | local ula_length=$(echo "$ula_rem" | cut -d, -f1) |
| 160 | local device="" |
| 161 | |
| 162 | network_get_device device "$network" |
| 163 | [ "$length" -lt "$ula_length" ] && length="$ula_length" |
| 164 | [ "$cmd" == "delprefix" ] && cmd="-D $chain" || cmd="-A $chain" |
| 165 | |
| 166 | local in="-i $device -d $addr/$length -j NETMAP --to $ula_addr/$ula_length" |
| 167 | local out="-o $device -s $ula_addr/$ula_length -j NETMAP --to $addr/$length" |
| 168 | |
| 169 | setup_npt_chain start "$network" |
| 170 | $NAT $cmd $in |
| 171 | $NAT $cmd $out |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | |
| 176 | disable_router() { |
| 177 | local network="$1" |
| 178 | |
| 179 | # Notify the address distribution daemon |
| 180 | ubus call 6distributed deliface '{"network": "'"$network"'"}' |
| 181 | |
| 182 | |
| 183 | # Start RD & DHCPv6 service |
| 184 | local router_service |
| 185 | config_get router_service global router_service |
| 186 | |
| 187 | if [ "$router_service" == "dnsmasq" ]; then |
| 188 | rm -f "/var/etc/dnsmasq.d/ipv6-router-$network.conf" |
| 189 | /etc/init.d/dnsmasq restart |
| 190 | else |
| 191 | stop_service /usr/sbin/6relayd "/var/run/ipv6-router-$network.pid" |
| 192 | fi |
| 193 | } |
| 194 | |
| 195 | |
| 196 | restart_relay_slave() { |
| 197 | local __section="$1" |
| 198 | local __master="$2" |
| 199 | |
| 200 | network_is_up "$__section" || return |
| 201 | |
| 202 | local __device="" |
| 203 | network_get_device __device "$__section" |
| 204 | |
| 205 | local __cmaster="" |
| 206 | config_get __cmaster "$__section" relay_master |
| 207 | |
| 208 | [ "$__master" == "$__cmaster" ] && { |
| 209 | disable_interface "$__section" |
| 210 | enable_interface "$__section" "$__device" |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | |
| 215 | add_relay_slave() { |
| 216 | local __section="$1" |
| 217 | local __return="$2" |
| 218 | local __master="$3" |
| 219 | local __mode="$4" |
| 220 | |
| 221 | network_is_up "$__section" || return |
| 222 | |
| 223 | # Get device |
| 224 | local __device="" |
| 225 | network_get_device __device "$__section" |
| 226 | |
| 227 | # Match master network |
| 228 | local __cmaster="" |
| 229 | config_get __cmaster "$__section" relay_master |
| 230 | [ "$__master" == "$__cmaster" ] || return |
| 231 | |
| 232 | # Test slave mode |
| 233 | local __cmode="" |
| 234 | config_get __cmode "$__section" mode |
| 235 | [ "$__cmode" == "downstream" ] && __cmode="router" |
| 236 | |
| 237 | # Don't start fallback interfaces if we are in forced-relay mode |
| 238 | [ "$__cmode" == "relay" -o "$__mode" == "fallback" ] || return |
| 239 | |
| 240 | # Don't make non-relay or non-router interfaces slaves |
| 241 | [ "$__cmode" == "relay" -o "$__cmode" == "router" ] || return |
| 242 | |
| 243 | # Disable any active distribution |
| 244 | [ "$__cmode" == "router" ] && disable_router "$__section" |
| 245 | |
| 246 | # Configure interface to accept RA and send RS |
| 247 | conf_set "$__device" accept_ra 2 |
| 248 | conf_set "$__device" forwarding 2 |
| 249 | |
| 250 | eval "$__return"'="$'"$__return"' '"$__device"'"' |
| 251 | } |
| 252 | |
| 253 | |
| 254 | stop_relay() { |
| 255 | local network="$1" |
| 256 | local pid_fallback="/var/run/ipv6-relay-fallback-$network.pid" |
| 257 | local pid_forced="/var/run/ipv6-relay-forced-$network.pid" |
| 258 | local was_fallback="" |
| 259 | |
| 260 | stop_service /usr/sbin/6relayd "$pid_fallback" was_fallback |
| 261 | stop_service /usr/sbin/6relayd "$pid_forced" |
| 262 | |
| 263 | # Reenable normal distribution on slave interfaces |
| 264 | [ -n "$was_fallback" ] && config_foreach restart_relay_slave interface "$network" |
| 265 | } |
| 266 | |
| 267 | |
| 268 | detect_forced_relay_mode() { |
| 269 | local __section="$1" |
| 270 | local __mode="$2" |
| 271 | |
| 272 | local __cmode |
| 273 | config_get __cmode "$__section" mode |
| 274 | [ "$__cmode" == "relay" ] && eval "$__mode=forced" |
| 275 | } |
| 276 | |
| 277 | |
| 278 | restart_relay() { |
| 279 | local network="$1" |
| 280 | local mode="$2" |
| 281 | |
| 282 | # Stop last active relay |
| 283 | stop_relay "$network" |
| 284 | |
| 285 | # Detect if we have a forced-relay |
| 286 | [ -z "$mode" ] && config_foreach detect_forced_relay_mode interface mode |
| 287 | |
| 288 | # Don't start without a mode |
| 289 | [ -z "$mode" ] && return |
| 290 | |
| 291 | # Detect master device |
| 292 | local device="" |
| 293 | network_get_device device "$network" |
| 294 | |
| 295 | # Generate command string |
| 296 | local cmd="/usr/sbin/6relayd -A $device" |
| 297 | local ifaces="" |
| 298 | config_foreach add_relay_slave interface ifaces "$network" "$mode" |
| 299 | |
| 300 | # Start relay |
| 301 | local pid="/var/run/ipv6-relay-$mode-$network.pid" |
| 302 | [ -n "$ifaces" ] && start_service "$cmd $ifaces" "$pid" |
| 303 | |
| 304 | # There are no slave interface, however indicate that we want to relay |
| 305 | [ -z "$ifaces" ] && touch "$pid" |
| 306 | } |
| 307 | |
| 308 | |
| 309 | setup_prefix_fallback() { |
| 310 | local cmd="$1" |
| 311 | local network="$2" |
| 312 | local device="$3" |
| 313 | |
| 314 | stop_relay "$network" |
| 315 | restart_relay "$network" |
| 316 | |
| 317 | setup_masquerading stop "$network" |
| 318 | |
| 319 | [ "$cmd" != "stop" ] && { |
| 320 | local fallback="" |
| 321 | config_get fallback "$network" prefix_fallback |
| 322 | |
| 323 | [ "$fallback" == "relay" ] && restart_relay "$network" fallback |
| 324 | [ "$fallback" == "masquerade" ] && setup_masquerading start "$network" "$device" |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | |
| 329 | restart_master_relay() { |
| 330 | local network="$1" |
| 331 | local mode="$2" |
| 332 | local pid_fallback="/var/run/ipv6-relay-fallback-$network.pid" |
| 333 | local pid_forced="/var/run/ipv6-relay-forced-$network.pid" |
| 334 | |
| 335 | # Disable active relaying to this interface |
| 336 | config_get relay_master "$network" relay_master |
| 337 | [ -z "$relay_master" ] && return |
| 338 | network_is_up "$relay_master" || return |
| 339 | |
| 340 | # Detect running mode |
| 341 | [ -z "$mode" && -f "$pid_fallback" ] && mode="fallback" |
| 342 | [ -z "$mode" && -f "$pid_forced" ] && mode="forced" |
| 343 | |
| 344 | # Restart relay if running or start requested |
| 345 | [ -n "$mode" ] && restart_relay "$relay_master" "$mode" |
| 346 | } |
| 347 | |
| 348 | |
| 349 | disable_interface() { |
| 350 | local network="$1" |
| 351 | |
| 352 | # Delete all prefixes routed to this interface |
| 353 | ubus call 6distributed delprefix '{"network": "'"$network"'"}' |
| 354 | |
| 355 | # Restart Relay |
| 356 | restart_master_relay "$network" |
| 357 | |
| 358 | # Disable distribution |
| 359 | disable_router "$network" |
| 360 | |
| 361 | # Disable any active relays, masquerading rules and NPT rules |
| 362 | stop_relay "$network" |
| 363 | setup_masquerading stop "$network" |
| 364 | setup_npt_chain stop "$network" |
| 365 | |
| 366 | # Disable DHCPv6 client if enabled, state script will take care |
| 367 | stop_service /usr/sbin/odhcp6c "/var/run/ipv6-dhcpv6-$network.pid" |
| 368 | } |
| 369 | |
| 370 | |
| 371 | enable_ula_prefix() { |
| 372 | local network="$1" |
| 373 | local ula="$2" |
| 374 | local device="$3" |
| 375 | [ -z "$ula" ] && ula="global" |
| 376 | |
| 377 | # ULA-integration |
| 378 | local ula_prefix="" |
| 379 | config_get ula_prefix "$ula" ula_prefix |
| 380 | |
| 381 | # ULA auto configuration (first init) |
| 382 | [ "$ula_prefix" == "auto" ] && { |
| 383 | local r1="" |
| 384 | local r2="" |
| 385 | local r3="" |
| 386 | |
| 387 | # Sometimes results are empty, therefore try until it works... |
| 388 | while [ -z "$r1" -o -z "$r2" -o -z "$r3" ]; do |
| 389 | r1=$(printf "%02x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 256))) |
| 390 | r2=$(printf "%01x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 65536))) |
| 391 | r3=$(printf "%01x" $(($(</dev/urandom tr -dc 0-9 | dd bs=9 count=1) % 65536))) |
| 392 | done |
| 393 | |
| 394 | ula_prefix="fd$r1:$r2:$r3::/48" |
| 395 | |
| 396 | # Save prefix so it will be preserved across reboots |
| 397 | config_set "$ula" ula_prefix "$ula_prefix" |
| 398 | uci_set network6 "$ula" ula_prefix "$ula_prefix" |
| 399 | uci_commit network6 |
| 400 | } |
| 401 | |
| 402 | # Announce ULA |
| 403 | [ -n "$ula_prefix" ] && announce_prefix "$ula_prefix" "$network" "$device" newprefix secondary |
| 404 | } |
| 405 | |
| 406 | |
| 407 | enable_static() { |
| 408 | local network="$1" |
| 409 | local device="$2" |
| 410 | |
| 411 | # Enable global forwarding |
| 412 | local global_forward |
| 413 | conf_get global_forward all forwarding |
| 414 | [ "$global_forward" != "1" ] && conf_set all forwarding 1 |
| 415 | |
| 416 | # Configure device |
| 417 | conf_set "$device" accept_ra 1 |
| 418 | conf_set "$device" forwarding 1 |
| 419 | |
| 420 | # Enable ULA |
| 421 | enable_ula_prefix "$network" global "$device" |
| 422 | # Compatibility (deprecated) |
| 423 | enable_ula_prefix "$network" "$network" "$device" |
| 424 | |
| 425 | # Announce all static prefixes |
| 426 | config_list_foreach "$network" static_prefix announce_prefix "$network" "$device" |
| 427 | |
| 428 | # start relay if there are forced relay members |
| 429 | restart_relay "$network" |
| 430 | } |
| 431 | |
| 432 | |
| 433 | enable_router() { |
| 434 | local network="$1" |
| 435 | local device="$2" |
| 436 | |
| 437 | # Get IPv6 prefixes |
| 438 | local length |
| 439 | config_get length "$network" advertise_prefix |
| 440 | [ -z "$length" ] && length=64 |
| 441 | [ "$length" -ne "0" ] && ubus call 6distributed newiface '{"network": "'"$network"'", "iface": "'"$device"'", "length": '"$length"'}' |
| 442 | |
| 443 | # Start RD & DHCPv6 service |
| 444 | local router_service |
| 445 | config_get router_service global router_service |
| 446 | |
| 447 | if [ "$router_service" == "dnsmasq" ]; then |
| 448 | local dnsmasq_opts |
| 449 | config_get dnsmasq_opts "$network" dnsmasq_opts |
| 450 | [ -z "$dnsmasq_opts" ] && dnsmasq_opts="ra-names,24h" |
| 451 | |
| 452 | local conf="/var/etc/dnsmasq.d/ipv6-router-$network.conf" |
| 453 | mkdir -p $(dirname $conf) |
| 454 | echo "dhcp-range=::00ff,::ffff,constructor:$device,$dnsmasq_opts" > $conf |
| 455 | echo "enable-ra" >> $conf |
| 456 | /etc/init.d/dnsmasq restart |
| 457 | else |
| 458 | local pid="/var/run/ipv6-router-$network.pid" |
| 459 | start_service "/usr/sbin/6relayd -S . $device" "$pid" |
| 460 | fi |
| 461 | |
| 462 | # Try relaying if necessary |
| 463 | restart_master_relay "$network" |
| 464 | } |
| 465 | |
| 466 | |
| 467 | enable_dhcpv6() { |
| 468 | local network="$1" |
| 469 | local device="$2" |
| 470 | |
| 471 | # Configure device |
| 472 | conf_set "$device" accept_ra 2 |
| 473 | conf_set "$device" forwarding 2 |
| 474 | |
| 475 | # Trigger RS |
| 476 | conf_set "$device" disable_ipv6 1 |
| 477 | conf_set "$device" disable_ipv6 0 |
| 478 | |
| 479 | # Configure DHCPv6-client |
| 480 | local dhcp6_opts="$device" |
| 481 | |
| 482 | # Configure DHCPv6-client (e.g. requested prefix) |
| 483 | local request_prefix |
| 484 | config_get request_prefix "$network" request_prefix |
| 485 | [ -z "$request_prefix" ] && request_prefix="auto" |
| 486 | [ "$request_prefix" != "no" ] && { |
| 487 | [ "$request_prefix" == "auto" ] && request_prefix=0 |
| 488 | dhcp6_opts="-P$request_prefix $dhcp6_opts" |
| 489 | } |
| 490 | |
| 491 | # Start DHCPv6 client |
| 492 | local pid="/var/run/ipv6-dhcpv6-$network.pid" |
| 493 | start_service "/usr/sbin/odhcp6c -s/lib/ipv6/dhcpv6.sh $dhcp6_opts" "$pid" |
| 494 | |
| 495 | # Refresh RA on all interfaces |
| 496 | for pid in /var/run/ipv6-router-*.pid; do |
| 497 | kill -SIGUSR1 $(cat "$pid") |
| 498 | done |
| 499 | } |
| 500 | |
| 501 | |
| 502 | enable_6to4() { |
| 503 | local network="$1" |
| 504 | local device="$2" |
| 505 | local mode="$3" |
| 506 | |
| 507 | local prefixlen="48" |
| 508 | [ "$mode" == "6rd" ] && { |
| 509 | local ip4prefix=$(uci_get network "$network" ip4prefixlen 0) |
| 510 | local ip6prefix=$(uci_get network "$network" ip6prefixlen 32) |
| 511 | prefixlen=$(($ip6prefix + 32 - $ip4prefix)) |
| 512 | } |
| 513 | |
| 514 | local prefix="" |
| 515 | network_get_ipaddr6 prefix "$network" |
| 516 | |
| 517 | announce_prefix "$prefix/$prefixlen" "$network" "$device" |
| 518 | } |
| 519 | |
| 520 | |
| 521 | enable_interface() |
| 522 | { |
| 523 | local network="$1" |
| 524 | local device="$2" |
| 525 | local mode="" |
| 526 | |
| 527 | config_get mode "$network" mode |
| 528 | [ -n "$mode" -a "$mode" != "none" ] || return |
| 529 | |
| 530 | # Compatibility with old mode names |
| 531 | [ "$mode" == "downstream" ] && mode=router |
| 532 | [ "$mode" == "upstream" ] && mode=dhcpv6 |
| 533 | |
| 534 | # Run mode startup code |
| 535 | enable_static "$network" "$device" |
| 536 | [ "$mode" == "dhcpv6" ] && enable_dhcpv6 "$network" "$device" |
| 537 | [ "$mode" == "router" ] && enable_router "$network" "$device" |
| 538 | [ "$mode" == "6to4" -o "$mode" == "6rd" ] && enable_6to4 "$network" "$device" "$mode" |
| 539 | [ "$mode" == "relay" ] && restart_master_relay "$network" forced |
| 540 | } |
| 541 | |