#!/bin/bash # # Copyright (C) 2010 SUSE LINUX Products GmbH / Novell Inc. # # Author: Marius Tomaschewski # # /sbin/dhclient-script for openSUSE / SUSE Linux Enterprise Server # based on the scripts shipped with the ISC DHCP (4.1.1) client. # # Note: # It is used by sysconfig alias netcontrol alias ifup-dhcp, # but not by the NetworkManager that is using an own script. # ## ## check mandatory parameters or ignore & exit ## test "x$reason" = x -o "x$interface" = x && exit 1 # # source sysconfig functions # SYSCONFIG_CFG_DIR="/etc/sysconfig/network" . "$SYSCONFIG_CFG_DIR/scripts/functions" SYSCONFIG_RUN_DIR=${SYSCONFIG_RUN_DIR:-/dev/.sysconfig/network} # # Debugging: # logs entire run of dhclient-script to /var/log/${dhclient}-script.*.log, # when DHCLIENT_DEBUG/DHCLIENT6_DEBUG are "yes" in sysconfig/network/dhcp # eval `grep '^DHCLIENT6\?_DEBUG=' "$SYSCONFIG_CFG_DIR/dhcp" 2>/dev/null` case $reason in *6) DEBUG=$DHCLIENT6_DEBUG ; dhclient=dhclient6 ; ipver=6 ;; *) DEBUG=$DHCLIENT_DEBUG ; dhclient=dhclient ; ipver=4 ;; esac if [ "$DEBUG" = yes ]; then set -a # allexport ( echo '****************' echo "$0 $*" date echo '----------------' env echo '----------------' ) >> /var/log/${dhclient}-script.$interface.log exec 2>> /var/log/${dhclient}-script.$interface.log set +a set -x fi is_ifup_controlled() { test -f "$SYSCONFIG_RUN_DIR/if-${interface}" } # netconfig makes all "additional" modifications as DNS or NTP netconfig_modify() { if test -x /sbin/netconfig -a -n "$interface" ; then { echo "INTERFACE='$interface'" for v in ${!new_*}; do case $ipver in 6) case $v in (new_ip6_address) k='IPADDR' ;; (new_ip6_prefixlen) k='PREFIXLEN' ;; (new_dhcp6_client_id) k='DHCP6CID' ;; (new_dhcp6_server_id) k='DHCP6SID' ;; (new_dhcp6_domain_search) k='DNSSEARCH' ;; (new_dhcp6_name_servers) k='DNSSERVERS' ;; (*) k="dhclient6_${v#new_}" ;; esac ;; 4) case $v in (new_ip_address) k='IPADDR' ;; (new_subnet_mask) k='NETMASK' ;; (new_network_number) k='NETWORK' ;; (new_broadcast_address) k='BROADCAST' ;; (new_interface_mtu) k='MTU' ;; (new_rfc3442_classless_static_routes) k='ROUTES' ;; # (new_static_routes) k='ROUTES' ;; (new_routers) k='GATEWAYS' ;; (new_host_name) k='HOSTNAME' ;; (new_domain_search) k='DNSSEARCH' ;; (new_domain_name) k='DNSDOMAIN' ;; (new_domain_name_servers) k='DNSSERVERS' ;; (new_ntp_servers) k='NTPSERVERS' ;; (new_nis_domain) k='NISDOMAIN' ;; (new_nis_servers) k='NISSERVERS' ;; (new_root_path) k='ROOTPATH' ;; (new_dhcp_server_identifier)k='DHCPSID' ;; (new_lpr_servers) k='LPRSERVER' ;; (new_log_servers) k='LOGSERVER' ;; (new_netbios_dd_server) k='NETBIOSDDSERVER' ;; (new_netbios_name_servers) k='NETBIOSNAMESERVER' ;; (new_netbios_node_type) k='NETBIOSNODETYPE' ;; (new_netbios_scope) k='NETBIOSSCOPE' ;; (*) k="dhclient_${v#new_}" ;; esac esac [ "k${k}" != k ] && echo "${k}='${!v}'" done } | /sbin/netconfig modify -s "dhclient$ipver" -i "$interface" fi } netconfig_remove() { if test -x /sbin/netconfig -a -n "$interface" ; then /sbin/netconfig remove -s "dhclient$ipver" -i "$interface" &1` && return 0 case $err in RTNETLINK*answers:*File*exists) ;; RTNETLINK*answers:*No*such*process) # # The gateway seems to be not reachable via local network # route (implicitly created by ifconfig based on the IP # and netmask provided by dhcp). # Check this, set an explicit host route to the gateway # over the current interface and try again (bnc#266215). # retry=0 for router in ${gate[@]} ; do matches=$(/sbin/ip -f inet -o route list match $router | \ grep -v ^default | grep -c -v "^$" 2>/dev/null) if [ -n "$matches" -a $(($matches)) -eq 0 ] ; then LC_ALL=C /sbin/ip route add $router/32 dev $interface || retry=1 fi done if [ $retry -eq 0 ] ; then LC_ALL=C /sbin/ip route replace ${args[*]} && return 0 fi ;; esac return 1 } get_ipv4_default_gw() { if [ "x$new_rfc3442_classless_static_routes" != x ] ; then local r route=() gw=() for r in $new_rfc3442_classless_static_routes ; do route=(${r//,/ }) case ${route[0]}/${route[1]} in 0.0.0.0/0.0.0.0) [ "x${route[2]}" = "x0.0.0.0" ] || \ gw+=(${route[2]}) ;; esac done echo "${gw[*]}" elif [ "$new_routers" != x ] ; then echo "$new_routers" fi } set_ipv4_routes() { if [ "x$new_rfc3442_classless_static_routes" != x ] ; then local r route=() # TODO: multiple gw's to one dest as in $new_routers? for r in $new_rfc3442_classless_static_routes ; do route=(${r//,/ }) case ${route[0]}/${route[1]} in 0.0.0.0/0.0.0.0) [ "x$DHCLIENT_SET_DEFAULT_ROUTE" = xyes ] || continue ;; esac set_ipv4_route ${route[*]} done elif [ "$new_routers" != x ] ; then if [ "x$DHCLIENT_SET_DEFAULT_ROUTE" = xyes ] ; then set_ipv4_route default "" $new_routers fi fi } set_ipv6_routes() { : TODO } set_hostname() { if [ "$DHCLIENT_SET_HOSTNAME" = yes ] ; then current_hostname=`hostname` if [ "x${current_hostname%%.*}" = "x" ] || \ [ "x${current_hostname%%.*}" = "x(none)" ] || \ [ "x${current_hostname%%.*}" = "xlocalhost" ] || \ [ "x${current_hostname%%.*}" != "x${new_host_name%%.*}" ]; then if [ "x${new_host_name%%.*}" != "x" ]; then hostname "${new_host_name%%.*}" else if [ -x /usr/bin/host ] ; then if out=`host -W 2 "$new_ip_address" 2>/dev/null` ; then _hostname="`echo "$out" | sed 's:^.* ::; s:\..*::'`" if [ "x${_hostname}" != "x" -a \ "x${_hostname}" != "x${current_hostname%%.*}" ]; then hostname "${_hostname}" fi fi fi fi fi fi if is_ifup_controlled ; then # check regardless the DHCLIENT_SET_HOSTNAME setting # and whether we've set it above or not, because when # it changed, we've to handle it anyway... local OLD_HOSTNAME=`read_cached_config_data hostname $interface` local CUR_HOSTNAME=`hostname 2>/dev/null` if test "x$OLD_HOSTNAME" != "x$CUR_HOSTNAME" ; then write_cached_config_data hostname "$CUR_HOSTNAME" $interface commit_cached_config_data $interface # reload syslog so it knows the new hostname /etc/init.d/syslog reload fi fi } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { exit_status=$1 if [ -f /etc/${dhclient}-exit-hooks ]; then . /etc/${dhclient}-exit-hooks fi # probably should do something with exit status of the local script exit $exit_status } # Invoke the local dhcp client enter hooks, if they exist. if [ -f /etc/${dhclient}-enter-hooks ]; then exit_status=0 . /etc/${dhclient}-enter-hooks # allow the local script to abort processing of this state # local script must set exit_status variable to nonzero. if [ $exit_status -ne 0 ]; then exit $exit_status fi fi case $dhclient in dhclient) if is_ifup_controlled ; then # STARTMODE eval `grep --no-filename \ '^[[:space:]]*STARTMODE=' \ "$SYSCONFIG_CFG_DIR/ifcfg-${interface}" 2>/dev/null` tmp_startmode=`read_cached_config_data startmode $INTERFACE` [ -n "$tmp_startmode" ] && STARTMODE="$tmp_startmode" # DHCLIENT_SET_HOSTNAME and DHCLIENT_SET_DEFAULT_ROUTE if grep -qs '^primary=yes' "$SYSCONFIG_RUN_DIR/if-${interface}" 2>/dev/null ; then eval `grep --no-filename \ '^[[:space:]]*DHCLIENT_SET_\(HOSTNAME\|DEFAULT_ROUTE\)=' \ "$SYSCONFIG_CFG_DIR/dhcp" "$SYSCONFIG_CFG_DIR/ifcfg-${interface}" 2>/dev/null` else eval `grep --no-filename \ '^[[:space:]]*DHCLIENT_SET_\(HOSTNAME\|DEFAULT_ROUTE\)=' \ "$SYSCONFIG_CFG_DIR/ifcfg-${interface}" 2>/dev/null` fi else STARTMODE=manual eval `grep --no-filename \ '^[[:space:]]*DHCLIENT_SET_\(HOSTNAME\|DEFAULT_ROUTE\)=' \ "$SYSCONFIG_CFG_DIR/dhcp" 2>/dev/null` fi if [ "x$new_rfc3442_classless_static_routes_raw" != x ] ; then new_rfc3442_classless_static_routes=$( parse_ipv4_classless_routes $new_rfc3442_classless_static_routes_raw ) else unset new_rfc3442_classless_static_routes fi if [ x$new_broadcast_address != x ] ; then new_broadcast_arg="brd $new_broadcast_address" elif [ "x$new_subnet_mask" != "x255.255.255.255" ] ; then new_broadcast_arg="brd +" fi if [ x$new_interface_mtu != x -a \ $(( $new_interface_mtu )) -lt 576 ] ; then # 68 is the minimal legal value, but 576 the real life minimum unset new_interface_mtu fi if [ x$IF_METRIC != x ]; then metric_arg="metric $IF_METRIC" fi ;; dhclient6) if is_ifup_controlled ; then # STARTMODE eval `grep --no-filename \ '^[[:space:]]*STARTMODE=' \ "$SYSCONFIG_CFG_DIR/ifcfg-${interface}" 2>/dev/null` tmp_startmode=`read_cached_config_data startmode $INTERFACE` [ -n "$tmp_startmode" ] && STARTMODE="$tmp_startmode" else STARTMODE=manual fi ;; esac case $reason in ###################################################################### ## DHCPv4 # ###################################################################### MEDIUM) #################################################################### exit_with_hooks 0 ;; PREINIT) #################################################################### if [ "x$alias_ip_address" != x ]; then /sbin/ip addr del $alias_ip_address/$alias_subnet_mask dev $interface fi if [ "x$STARTMODE" != "xnfsroot" ] ; then /sbin/ip -4 addr flush dev $interface fi /sbin/ip link set $interface up # We need to give the kernel some time to get the interface up. sleep 1 exit_with_hooks 0 ;; ARPCHECK|ARPSEND) #################################################################### exit_with_hooks 0 ;; BOUND|RENEW|REBIND|REBOOT) #################################################################### if [ x$old_ip_address != x -a x$alias_ip_address != x ] && \ [ x$alias_ip_address != x$old_ip_address ] ; then # Possible new alias. Remove old alias. /sbin/ip addr del $alias_ip_address/$alias_subnet_mask dev $interface fi if [ x$old_ip_address != x -a x$old_ip_address != x$new_ip_address ]; then # IP address changed. Flush to clear routes and ARP cache. if [ "x$STARTMODE" != "xnfsroot" ] ; then /sbin/ip -4 addr flush dev $interface fi fi if [ x$new_interface_mtu != x ] ; then /sbin/ip link set $interface mtu $new_interface_mtu fi if [ x$new_ip_address != x ] && \ [ x$new_ip_address != x$old_ip_address -o \ x$reason = xBOUND -o x$reason = xREBOOT ]; then /sbin/ip addr add $new_ip_address/${new_subnet_mask:-32} \ ${new_broadcast_arg} \ dev $interface set_ipv4_routes fi if [ x$new_ip_address != x$alias_ip_address -a x$alias_ip_address != x ]; then /sbin/ip addr add $alias_ip_address/$alias_subnet_mask \ dev $interface fi netconfig_modify if [ x$old_ip_address != x -a x$old_ip_address != x$new_ip_address ]; then if is_ifup_controlled ; then ifdown $interface -o dhcp write_cached_config_data dhcp4_state "new" $interface commit_cached_config_data $interface fi else if is_ifup_controlled ; then write_cached_config_data dhcp4_state "up" $interface commit_cached_config_data $interface fi fi set_hostname if is_ifup_controlled ; then # execute ifservice and if-up.d scripts ifup $interface -o dhcp write_cached_config_data dhcp4_state "complete" $interface commit_cached_config_data $interface fi exit_with_hooks 0 ;; EXPIRE|FAIL|RELEASE|STOP) #################################################################### if [ x$old_ip_address != x ]; then if [ "x$STARTMODE" != "xnfsroot" ] ; then /sbin/ip -4 addr flush dev $interface fi fi if [ x$alias_ip_address != x ]; then /sbin/ip addr add $alias_ip_address/$alias_subnet_mask \ dev $interface fi if is_ifup_controlled ; then write_cached_config_data dhcp4_state "down" $interface commit_cached_config_data $interface fi netconfig_remove exit_with_hooks 0 ;; TIMEOUT) #################################################################### if [ x$alias_ip_address != x ]; then /sbin/ip addr del $alias_ip_address/$alias_subnet_mask \ dev $interface fi if [ x$new_ip_address != x -a x$old_ip_address != x$new_ip_address ]; then /sbin/ip addr add $new_ip_address/${new_subnet_mask:-32} \ ${new_broadcast_arg} \ dev $interface set -- $(get_ipv4_default_gw) if [ -n "$1" ] && /sbin/arping -q -c 1 -w 5 -I $interface $1 ; then if [ x$new_ip_address != x$alias_ip_address ] && \ [ x$alias_ip_address != x ]; then /sbin/ip addr add $alias_ip_address/$alias_subnet_arg fi set_ipv4_routes netconfig_modify set_hostname if is_ifup_controlled ; then # execute ifservice and if-up.d scripts ifup $interface -o dhcp write_cached_config_data dhcp4_state "complete" $interface commit_cached_config_data $interface fi exit_with_hooks 0 else if [ x$old_ip_address != x ]; then if [ "x$STARTMODE" != "xnfsroot" ] ; then /sbin/ip -4 addr flush dev $interface fi fi if is_ifup_controlled ; then write_cached_config_data dhcp4_state "down" $interface commit_cached_config_data $interface fi netconfig_remove exit_with_hooks 1 fi fi ;; ###################################################################### ## DHCPv6 # ###################################################################### PREINIT6) #################################################################### if [ "x$STARTMODE" != "xnfsroot" ] ; then /sbin/ip -6 addr flush dev $interface scope global permanent fi /sbin/ip link set $interface up # We need to give the kernel some time to get the interface up. sleep 1 exit_with_hooks 0 ;; BOUND6|RENEW6|REBIND6|REBOOT6) #################################################################### if [ x$old_ip6_address != x -a x$old_ip6_address != x$new_ip6_address ]; then /sbin/ip addr del "$old_ip6_address/$old_ip6_prefixlen" dev $interface if is_ifup_controlled ; then write_cached_config_data dhcp6_state "new" $interface commit_cached_config_data $interface fi else if is_ifup_controlled ; then write_cached_config_data dhcp6_state "up" $interface commit_cached_config_data $interface fi fi if [ "x$new_ip6_address" != x -a "x$new_ip6_prefixlen" != x ] ; then /sbin/ip addr add "$new_ip6_address/$new_ip6_prefixlen" \ dev $interface scope global set_ipv6_routes fi netconfig_modify if [ x$old_ip6_address != x -a x$old_ip6_address != x$new_ip_address ]; then if is_ifup_controlled ; then /sbin/ifdown $interface -o dhcp fi fi #set_hostname if is_ifup_controlled ; then # execute ifservice and if-up.d scripts /sbin/ifup $interface -o dhcp write_cached_config_data dhcp6_state "complete" $interface commit_cached_config_data $interface fi exit_with_hooks 0 ;; DEPREF6) #################################################################### if [ x$new_ip6_address = x -o x$new_ip6_prefixlen = x ] ; then exit_with_hooks 2 fi /sbin/ip addr change "$new_ip6_address/$new_ip6_prefixlen" \ dev $interface scope global preferred_lft 0 exit_with_hooks 0 ;; EXPIRE6|RELEASE6|STOP6) #################################################################### if [ x$old_ip6_address != x -a x$old_ip6_prefixlen != x ] ; then /sbin/ip addr del "$old_ip6_address/$old_ip6_prefixlen" \ dev $interface fi if is_ifup_controlled ; then write_cached_config_data dhcp6_state "down" $interface commit_cached_config_data $interface fi netconfig_remove exit_with_hooks 0 ;; esac exit_with_hooks 0