| #!/bin/sh |
| # dhclient-script for Linux. Dan Halbert, March, 1997. |
| # Updated for Linux 2.[12] by Brian J. Murrell, January 1999. |
| # No guarantees about this. I'm a novice at the details of Linux |
| # networking. |
| # Conversion to use ip for ipv4 (instead of ifconfig, route) by Peter Marschall |
| # Extended for 4.2.2, and ip output to /dev/null removed, by Ken Moffat |
| |
| # Notes: |
| |
| # 0. This script is based on the netbsd script supplied with dhcp-970306. |
| |
| # 1. ifconfig down apparently deletes all relevant routes and flushes |
| # the arp cache, so this doesn't need to be done explicitly. |
| |
| # 2. The alias address handling here has not been tested AT ALL. |
| # I'm just going by the doc of modern Linux ip aliasing, which uses |
| # notations like eth0:0, eth0:1, for each alias. |
| |
| # 3. I have to calculate the network address, and calculate the broadcast |
| # address if it is not supplied. This might be much more easily done |
| # by the dhclient C code, and passed on. |
| |
| # 4. TIMEOUT not tested. ping has a flag I don't know, and I'm suspicious |
| # of the $1 in its args. |
| |
| printenv |
| |
| . /etc/utils.sh |
| |
| CONMAN_DIR=/tmp/conman |
| mkdir -p "$CONMAN_DIR" |
| mkdir -p /tmp/dnsmasq |
| |
| ip=ip |
| wifi_ifc=eth2 |
| bridge_ifc=br0 |
| |
| log() { |
| echo "$@" >&2 |
| } |
| |
| log_file_contents() { |
| local filename="$1" |
| while read line; do |
| log "$filename: $line" |
| done <$filename |
| } |
| |
| routable_ifc() { |
| [ "$interface" != "$wifi_ifc" ] |
| } |
| |
| is_ipv6() { |
| endswith "$reason" "6" |
| } |
| |
| internal_resolv_conf=" |
| # Note: these settings are hardcoded so that DHCP-provided settings don't |
| # conflict with our log uploader and ACS. For DHCP-provided settings |
| # suitable for end users, use /tmp/resolv.conf.external instead. |
| nameserver 8.8.8.8 |
| nameserver 8.8.4.4 |
| nameserver 2001:4860:4860::8888 |
| nameserver 2001:4860:4860::8844 |
| options single-request-reopen |
| options timeout:2 |
| options attempts:3 |
| " |
| |
| merge_resolv_conf() { |
| local confs="" |
| |
| if [ -e "/tmp/resolv.conf.dhclient" ]; then |
| confs="$confs /tmp/resolv.conf.dhclient" |
| fi |
| |
| if [ -e "/tmp/resolv.conf.dhclient6" ]; then |
| confs="$confs /tmp/resolv.conf.dhclient6" |
| fi |
| |
| if [ -n "$confs" ]; then |
| atomic /tmp/resolv.conf.external "$(cat $confs)" |
| log_file_contents /tmp/resolv.conf.external |
| if [ -e "/config/allowdns" ]; then |
| ln -sf /tmp/resolv.conf.external /tmp/resolv.conf |
| else |
| atomic /tmp/resolv.conf "$internal_resolv_conf" |
| fi |
| log_file_contents /tmp/resolv.conf |
| for conf in /chroot/*/tmp; do |
| if [ -d "$conf" ]; then |
| atomic "$conf/resolv.conf" "$internal_resolv_conf" |
| echo "Updated $conf/resolv.conf" |
| fi |
| done |
| fi |
| } |
| |
| make_resolv_conf() { |
| if [ -n "$new_domain_name_servers" ]; then |
| atomic /tmp/resolv.conf.dhclient "$( |
| if [ -n "$new_domain_search" ]; then |
| echo search $new_domain_search |
| log "dns4 search $new_domain_search" |
| elif [ -n "$new_domain_name" ]; then |
| # Note that the DHCP 'Domain Name Option' is really just a domain |
| # name, and that this practice of using the domain name option as |
| # a search path is both nonstandard and deprecated. |
| echo search $new_domain_name |
| log "dns4 search $new_domain_name" |
| fi |
| for nameserver in $new_domain_name_servers; do |
| echo nameserver $nameserver |
| log "dns4 nameserver $nameserver" |
| done |
| )" |
| merge_resolv_conf |
| elif [ -n "$new_dhcp6_name_servers" ]; then |
| atomic /tmp/resolv.conf.dhclient6 "$( |
| if [ -n "$new_dhcp6_domain_search" ]; then |
| echo search $new_dhcp6_domain_search |
| log "dns6 search $new_dhcp6_domain_search" |
| fi |
| for nameserver in $new_dhcp6_name_servers; do |
| echo nameserver $nameserver |
| log "dns6 nameserver $nameserver" |
| done |
| )" |
| merge_resolv_conf |
| fi |
| } |
| |
| make_dnsmasq_ipv6() { |
| local prefix64="$1" |
| if [ -n "$prefix64" ]; then |
| # Write out a config that will enable dhcpv6 stateless server |
| # and send router advertisements every 10 seconds. |
| atomic /tmp/dnsmasq/ipv6.conf "$( |
| echo "dhcp-range=${prefix64},ra-stateless" |
| echo "ra-param=${bridge_ifc},10" |
| )" |
| else |
| rm -f /tmp/dnsmasq/ipv6.conf |
| fi |
| } |
| |
| make_cwmp_files() { |
| if [ -n "$new_cwmp_acs_url" ]; then |
| log "cwmp dhcp acs '$new_cwmp_acs_url'" |
| if is_ipv6; then |
| set-acs dhcp6 "$new_cwmp_acs_url" |
| else |
| set-acs dhcp "$new_cwmp_acs_url" |
| fi |
| fi |
| if [ -n "$new_cwmp_provisioning_code" ]; then |
| atomic /tmp/cwmp/provisioning_code "$new_cwmp_provisioning_code" |
| log "cwmp provisioning '$new_cwmp_provisioning_code'" |
| fi |
| if [ -n "$new_cwmp_retry_minimum_wait_interval" ]; then |
| atomic /tmp/cwmp/retry_minimum_wait_interval "$new_cwmp_retry_minimum_wait_interval" |
| log "cwmp retry minwait '$new_cwmp_retry_minimum_wait_interval'" |
| fi |
| if [ -n "$new_cwmp_retry_interval_multiplier" ]; then |
| atomic /tmp/cwmp/retry_interval_multiplier "$new_cwmp_retry_interval_multiplier" |
| log "cwmp retry mult '$new_cwmp_retry_interval_multiplier'" |
| fi |
| } |
| |
| make_etc_hosts() { |
| atomic /tmp/hosts "$( |
| echo "$new_ip_address $(hostname)" |
| echo "127.0.0.1 localhost" |
| )" |
| log_file_contents /tmp/hosts |
| } |
| |
| write_ntp_conf_new() |
| { |
| fname=$1 |
| atomic "$fname" "$new_ntp_servers" |
| log_file_contents "$fname" |
| } |
| |
| # Write a JSON encoded blob containing the data needed |
| # to configure ipv4 on the interface. |
| make_v4_dynamic_config() { |
| atomic "/tmp/ip/dynamic/$interface" "$( |
| echo "{ \"$interface\": {" |
| echo " \"ip\": [ {" |
| [ -n "${new_ip_address}" ] && \ |
| echo " \"new_ip_address\": \"$new_ip_address\"," |
| [ -n "${new_subnet_mask}" ] && \ |
| echo " \"new_subnet_mask\": \"$new_subnet_mask\"," |
| [ -n "$new_interface_mtu" ] && \ |
| echo " \"new_interface_mtu\": \"$new_interface_mtu\"," |
| [ -n "$new_routers" ] && echo " \"new_routers\": \"$new_routers\"," |
| echo " \"stuffing\": \"filling\" } ]," |
| echo " \"dynamic_pid\": $PPID" |
| echo "} }" |
| )" |
| } |
| |
| ip6_prefix_to_addr() { |
| if [ -z "$1" ]; then |
| echo "" |
| return |
| fi |
| |
| local prefix=$(echo "$1" | sed -e 's@/..$@@') |
| prefix="${prefix}1/64" |
| echo "$prefix" |
| } |
| |
| ip6_prefix_change() { |
| # If neither the old nor the new prefix is set, don't do anything. |
| if [ -z "$new_ip6_prefix" ] && [ -z "$old_ip6_prefix" ]; then |
| echo 0 |
| return |
| fi |
| |
| local new_addr64=$(ip6_prefix_to_addr "$new_ip6_prefix") |
| local old_addr64=$(ip6_prefix_to_addr "$old_ip6_prefix") |
| |
| # Case 1: Prefix is being removed. |
| if [ -z "$new_addr64" ]; then |
| ip -6 addr del "$old_addr64" dev $bridge_ifc |
| echo 1 |
| return |
| fi |
| |
| # Case 2: Old prefix != newprefix. |
| if [ "$old_addr64" != "$new_addr64" ]; then |
| if [ -n "$old_addr64" ]; then |
| ip -6 addr del "$old_addr64" dev $bridge_ifc |
| fi |
| ip -6 addr add "$new_addr64" dev $bridge_ifc |
| echo 1 |
| return |
| fi |
| |
| # Case 3: old prefix = new prefix. Check if this prefix is already set. |
| local addrs |
| addrs=$(ip -6 addr show dev $bridge_ifc | grep -i "$new_addr64") |
| if [ -n "$addrs" ]; then |
| echo 0 |
| return |
| fi |
| |
| ip -6 addr add "$new_addr64" dev $bridge_ifc |
| echo 1 |
| } |
| |
| remove_ip6_prefix() { |
| # Remove the prefix address that was assigned to the bridge. |
| local prefix="$1" |
| if [ -n "$prefix" ]; then |
| ip -6 addr del $(ip_prefix_to_addr "$prefix") dev $bridge_ifc |
| rm -f /tmp/dnsmasq/ipv6.conf |
| ip -6 addr flush dev $bridge_ifc scope global |
| QUIET=1 restart dnsmasq |
| fi |
| } |
| |
| |
| # Must be used on exit. Invokes the local dhcp client exit hooks, if any. |
| exit_with_hooks() { |
| exit_status=$1 |
| case $reason in |
| BOUND|RENEW|REBIND|REBOOT) |
| write_ntp_conf_new /tmp/ntpd4.servers |
| if [ "$addr_added" = 1 ]; then |
| QUIET=1 restart igmpproxy upnpd ssdpd dialserver |
| runnable update-acs-iptables && update-acs-iptables |
| runnable zebra && QUIET=1 restart quagga |
| fi |
| ;; |
| |
| BOUND6|RENEW6|REBIND6|REBOOT6) |
| local restart_dnsmasq |
| restart_dnsmasq=$(ip6_prefix_change) |
| if [ "$restart_dnsmasq" = 1 ]; then |
| local prefix=$(echo "$new_ip6_prefix" | sed -e 's@/..$@@') |
| make_dnsmasq_ipv6 "$prefix" |
| fi |
| |
| if [ "$restart_dnsmasq" != 0 ]; then |
| QUIET=1 restart dnsmasq |
| fi |
| |
| if runnable rdisc6 && \ |
| [ -n "${new_ip6_address}" ] && [ -n "${interface}" ] && \ |
| [ "${new_ip6_address}" != "${old_ip6_address}" ]; then |
| # If we received a new address, force a router solicitation to |
| # be sent, which will force a router advertisement. We need the |
| # RA for setting our default route. |
| rdisc6 -1 "${interface}" & |
| fi |
| write_ntp_conf_new /tmp/ntpd6.servers |
| ;; |
| |
| EXPIRE|FAIL|RELEASE|STOP) |
| echo "Removing DHCP4 ntp servers on $reason" |
| rm -f /tmp/ntpd4.servers |
| ;; |
| |
| EXPIRE6|FAIL6|RELEASE6|STOP6) |
| echo "Removing DHCP6 ntp servers on $reason" |
| rm -f /tmp/ntpd6.servers |
| ;; |
| esac |
| exit $exit_status |
| } |
| |
| ### |
| ### DHCPv4 Handlers |
| ### |
| |
| if [ x$new_broadcast_address != x ]; then |
| new_broadcast_arg="broadcast $new_broadcast_address" |
| fi |
| if [ x$old_broadcast_address != x ]; then |
| old_broadcast_arg="broadcast $old_broadcast_address" |
| fi |
| if [ x$IF_METRIC != x ]; then |
| metric_arg="metric $IF_METRIC" |
| fi |
| |
| if [ x$reason = xMEDIUM ]; then |
| # Linux doesn't do mediums (ok, ok, media). |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xPREINIT ]; then |
| $ip link set dev ${interface} up |
| |
| # We need to give the kernel some time to get the interface up. |
| sleep 1 |
| |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ |
| [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then |
| |
| if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ |
| [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then |
| |
| make_v4_dynamic_config |
| ipapply ${interface} |
| |
| addr_added=1 |
| fi |
| |
| if [ "$addr_added" = 1 ]; then |
| # Add a network route to the computed network address. |
| if routable_ifc; then |
| if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ]; then |
| # if we've changed routers delete the old and add the new. |
| for router in $old_routers; do |
| $ip -4 route del via $router |
| done |
| fi |
| for router in $new_routers; do |
| if [ "x$new_subnet_mask" = "x255.255.255.255" ]; then |
| $ip -4 route add ${router} dev $interface |
| fi |
| # ipapply will handle the normal router case. |
| done |
| fi |
| fi |
| |
| make_resolv_conf |
| make_cwmp_files |
| make_etc_hosts |
| set-acs bounce |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ |
| || [ x$reason = xSTOP ] || [ x$reason = xEXPIRE ]; then |
| rm -f "/tmp/ip/dynamic/${interface}" |
| ipapply "${interface}" |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xTIMEOUT ]; then |
| rm -f "/tmp/ip/dynamic/${interface}" |
| ipapply "${interface}" |
| exit_with_hooks 1 |
| fi |
| |
| ### |
| ### DHCPv6 Handlers |
| ### |
| |
| if [ x$reason = xPREINIT6 ]; then |
| # Ensure interface is up. |
| $ip link set ${interface} up |
| |
| # Remove any stale addresses from aborted clients. |
| $ip -f inet6 addr flush dev ${interface} scope global permanent |
| |
| exit_with_hooks 0 |
| fi |
| |
| if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ]; then |
| echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix} |
| |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xBOUND6 ]; then |
| if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ]; then |
| exit_with_hooks 2; |
| fi |
| |
| $ip -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \ |
| dev ${interface} scope global |
| |
| # Check for nameserver options. |
| make_resolv_conf |
| make_cwmp_files |
| exit_with_hooks 0 |
| fi |
| |
| has_new_ip6_address() { |
| local output |
| output=$(ip -f inet6 addr show dev "${interface}" | grep "${new_ip6_address}") |
| [ -n "$output" ] |
| } |
| |
| if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ]; then |
| # Part of the DHCPv6 spec says that a RENEW6 can send a new and |
| # different address. |
| if [ -n "${new_ip6_address}" ] && [ "${new_max_life}" != 0 ] && \ |
| ! has_new_ip6_address; then |
| if [ -z "${new_ip6_prefixlen}" ]; then |
| # Should never happen, we got a valid address, but no prefixlen. |
| exit_with_hooks 2; |
| fi |
| $ip -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \ |
| dev ${interface} scope global |
| fi |
| |
| # Make sure nothing has moved around on us. |
| |
| # Nameservers/domains/etc. |
| if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] || |
| [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ]; then |
| make_resolv_conf |
| fi |
| |
| if [ "x${new_cwmp_acs_url}" != "x${old_cwmp_acs_url}" ] || |
| [ "x${new_cwmp_provisioning_code}" != "x${old_cwmp_provisioning_code}" ] || |
| [ "x${new_cwmp_retry_minimum_wait_interval}" != "x${old_cwmp_retry_minimum_wait_interval}" ] || |
| [ "x${new_cwmp_retry_interval_multiplier}" != "x${old_cwmp_retry_interval_multiplier}" ]; then |
| make_cwmp_files |
| fi |
| |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xDEPREF6 ]; then |
| # If cur_ip_prefixlen is not set then call exit_with_hooks with 2 |
| # to indicate an error. |
| if [ x${cur_ip6_prefixlen} = x ]; then |
| exit_with_hooks 2; |
| fi |
| |
| # This section handles removing the delegated prefix. |
| if [ -n "$cur_ip6_prefix" ]; then |
| remove_ip6_prefix $cur_ip6_prefix |
| fi |
| |
| # This section is for the DPREF'd ip6 address. |
| # Just remove the address on depref, setting the preferred_lft to |
| # 0 still leaves the address active. |
| if [ -n "$cur_ip6_address" ]; then |
| $ip -f inet6 addr del ${cur_ip6_address}/${cur_ip6_prefixlen} \ |
| dev ${interface} |
| fi |
| |
| exit_with_hooks 0 |
| fi |
| |
| if [ x$reason = xRELEASE6 -o x$reason = xSTOP6 -o x$reason = xEXPIRE6 ]; then |
| if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ]; then |
| exit_with_hooks 2; |
| fi |
| |
| if [ -n "$old_ip6_prefix" ]; then |
| remove_ip6_prefix $old_ip6_prefix |
| fi |
| |
| if [ -n "$old_ip6_address" ]; then |
| $ip -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \ |
| dev ${interface} |
| fi |
| |
| exit_with_hooks 0 |
| fi |
| |
| exit_with_hooks 0 |