File dhclient-script of Package dhcp

#!/bin/bash
#
# Copyright (C) 2010 SUSE LINUX Products GmbH / Novell Inc.
#
# Author: Marius Tomaschewski <mt@suse.de>
#
# /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'          ;;
          (new_dhcp6_sntp_servers)    k='NTPSERVERS'          ;;
          #(new_dhcp6_nis_domain)      k='NISDOMAIN'           ;;
          #(new_dhcp6_nis_servers)     k='NISSERVERS'          ;;
          (*)                         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_formatted)
                                      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" </dev/null
  fi
}
parse_ipv4_classless_routes()
{
  local route_regex='^[0-9]{1,3}([ ][0-9]{1,3}){4,}$'
  local rfc_routes=() _routes=()

  # check whether it is a list of numbers
  [[ $* =~ $route_regex ]] && rfc_routes=($*)

  for (( i=0; i < ${#rfc_routes[@]}; )) ; do
    net_length=${rfc_routes[$i]}
    test $net_length -gt 32 && return 1

    ((i++))
    net_octets=$(($net_length / 8 + ($net_length % 8 ? 1 : 0)))
    test ${#rfc_routes[@]} -lt $(( $i + $net_octets + 4 )) && \
      return 1

    net_netmask=$(pfxlen2mask $net_length)
    net_netmask=(${net_netmask//./ })
    net_address=(0 0 0 0)
    for(( j=0; j < $net_octets; j++, i++)); do
      net_address[$j]=$((${rfc_routes[$i]} & ${net_netmask[$j]}))
    done

    gateway=(0 0 0 0)
    for (( j=0; j < 4; j++, i++ )); do
      gateway[$j]=${rfc_routes[$i]}
    done

    old_IFS=$IFS
    IFS='.'
    _routes+=("${net_address[*]},${net_netmask[*]},${gateway[*]}")
    IFS=$old_IFS
  done

  echo "${_routes[*]}"
  return 0
}
set_ipv4_route()
{
  local dest=$1 ; shift
  local mask=$1 ; shift
  local gate=("$@")
  local hops args

  hops=()
  if [ ${#gate[@]} -gt 1 ] ; then
    for((g=0; g<${#gate[@]}; g++)) ; do
      hops+=(nexthop ${gate[$g]:+via ${gate[$g]}}
             dev $interface weight $((g+1)))
    done
  elif [ ${#gate[@]} -gt 0 ] ; then
    hops+=(${gate[$g]:+via ${gate[$g]}} dev $interface)
  fi
  [ ${#hops[@]} -eq 0 ] && hops=(dev $interface)
  args=("$dest${mask:+/$mask}" $metric_arg ${hops[*]})

  err=`LC_ALL=C /sbin/ip route replace ${args[*]} 2>&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_formatted" != x ] ; then
    local r route=() gw=()
    for r in $new_rfc3442_classless_static_routes_formatted ; 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_formatted" != x ] ; then
    local r route=()
    # TODO: multiple gw's to one dest as in $new_routers?
    for r in $new_rfc3442_classless_static_routes_formatted ; 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()
{
  rx_host='^[[:alnum:]][[:alnum:]_-]{0,62}$'
  if [ "$DHCLIENT_SET_HOSTNAME" = yes ] ; then
    new_host_name="${new_host_name%%.*}"
    [[ ${new_host_name} =~ ${rx_host} ]] || unset new_host_name
    current_hostname=`hostname`
    current_hostname="${current_hostname%%.*}"
    [[ ${current_hostname} =~ ${rx_host} ]] || unset current_hostname

    if [ "x${current_hostname}" = "x" ] || \
       [ "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:\..*::; s:.*[)]::'`"
	    [[ ${_hostname} =~ ${rx_host} ]] || unset _hostname
            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`
    CUR_HOSTNAME="${CUR_HOSTNAME%%.*}"
    if [[ ${CUR_HOSTNAME} =~ ${rx_host} ]] && \
       [ "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" != x ] ; then
    new_rfc3442_classless_static_routes_formatted=$(
      parse_ipv4_classless_routes $new_rfc3442_classless_static_routes
    )
  else
    unset new_rfc3442_classless_static_routes_formatted
  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 )) -le 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$alias_ip_address != x -a x$alias_ip_address != x$old_ip_address -a \
       x$new_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 \
    -a x$new_ip_address != x$old_ip_address ];
  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_ip6_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
openSUSE Build Service is sponsored by