Monday, May 3, 2010

Solaris NIC speed and duplex settings

The following is a Bourne shell script I wrote to determine the speed and duplex settings for all active network interfaces on a Solaris system.


#!/bin/sh

# $Id: speed_duplex,v 1.4 2007/07/10 16:04:42 hutch Exp $

PATH=/bin:/usr/bin:/usr/sbin

# Print column header information
echo "Interface\tSpeed\t\tDuplex"
echo "---------\t-----\t\t------"

# Determine the speed and duplex for each live NIC on the system
for INTERFACE in `netstat -i | egrep -v "^Name|^lo0" \
   | awk '{ print $1 }' | cut -d: -f1 | sort | uniq`
do
   # Only gather information for active interfaces
   # Note: "ce" interfaces can be "UP" in "ifconfig" but have link down
   ifconfig $INTERFACE | grep "^$INTERFACE:.* /dev/null 2>&1 || continue
   # Skip "cip" ATM interfaces
   echo $INTERFACE | grep "^cip" > /dev/null 2>&1 && continue
   # "ce" interfaces
   if [ "`echo $INTERFACE | awk '/^ce[0-9]+/ { print }'`" ] ; then
      kstat > /dev/null 2>&1
      if [ $? -ne 0 ] ; then
         echo "The \"kstat\" command failed for interface $INTERFACE."
         continue
      fi
      # Determine the ce interface number
      INSTANCE=`echo $INTERFACE | cut -c 3-`
      DUPLEX=`kstat ce:$INSTANCE | grep link_duplex | awk '{ print $2 }'`
      case "$DUPLEX" in
         0) DUPLEX="link down" ;;
         1) DUPLEX="half" ;;
         2) DUPLEX="full" ;;
      esac
      SPEED=`kstat ce:$INSTANCE | grep link_speed | awk '{ print $2 }'`
      case "$SPEED" in
         0) SPEED="link down" ;;
         10) SPEED="10 Mbit/s" ;;
         100) SPEED="100 Mbit/s" ;;
         1000) SPEED="1 Gbit/s" ;;
      esac
   # "dmfe" interfaces
   elif [ "`echo $INTERFACE | awk '/^dmfe[0-9]+/ { print }'`" ] ; then
      # Only the root user should run "ndd"
      if [ "`id | cut -c1-5`" != "uid=0" ] ; then
         echo "You must be the root user to determine \
${INTERFACE_TYPE}${INSTANCE} speed and duplex information."
  continue
      fi
      DUPLEX=`ndd /dev/${INTERFACE} link_mode`
      case "$DUPLEX" in
         0) DUPLEX="half" ;;
         1) DUPLEX="full" ;;
      esac
      SPEED=`ndd /dev/${INTERFACE} link_speed`
      case "$SPEED" in
         10) SPEED="10 Mbit/s" ;;
         100) SPEED="100 Mbit/s" ;;
         1000) SPEED="1 Gbit/s" ;;
      esac
   # "bge" and "iprb" interfaces
   elif [ "`echo $INTERFACE | awk '/^iprb[0-9]+|bge[0-9]+/ { print }'`" ] ; then
      kstat > /dev/null 2>&1
      if [ $? -ne 0 ] ; then
         DUPLEX="The \"kstat\" command failed for interface $INTERFACE."
         continue
      fi
      # Determine the bge|iprb interface number
      INSTANCE=`echo $INTERFACE | tr -d '[a-z]'`
      INTERFACE=`echo $INTERFACE | tr -d '[0-9]'`
      DUPLEX=`kstat $INTERFACE:$INSTANCE | grep duplex | awk '{ print $2 }'`
      SPEED=`kstat $INTERFACE:$INSTANCE | grep ifspeed | awk '{ print $2 }'`
      case "$SPEED" in
         10000000) SPEED="10 Mbit/s" ;;
         100000000) SPEED="100 Mbit/s" ;;
         1000000000) SPEED="1 Gbit/s" ;;
      esac
   elif [ "`echo $INTERFACE | awk '/^e1000g[0-9]+/ { print }'`" ] ; then
      INSTANCE=`echo $INTERFACE | cut -c7-`
      # The duplex for e1000g devices can only be found with "dladm"
      DUPLEX=`dladm show-dev $INTERFACE | awk '{ print $NF }'`
      SPEED=`kstat e1000g:$INSTANCE | grep ifspeed | awk '{ print $2 }'`
      case "$SPEED" in
         10000000) SPEED="10 Mbit/s" ;;
         100000000) SPEED="100 Mbit/s" ;;
         1000000000) SPEED="1 Gbit/s" ;;
      esac
   # le interfaces are always 10 Mbit half-duplex
   elif [ "`echo $INTERFACE | awk '/^le[0-9]+/ { print }'`" ] ; then
      DUPLEX="half"
      SPEED="10 Mbit/s"
   # All other interfaces
   else
      INTERFACE_TYPE=`echo $INTERFACE | sed -e "s/[0-9]*$//"`
      INSTANCE=`echo $INTERFACE | sed -e "s/^[a-z]*//"`
      # Only the root user should run "ndd"
      if [ "`id | cut -c1-5`" != "uid=0" ] ; then
         echo "You must be the root user to determine \
${INTERFACE_TYPE}${INSTANCE} speed and duplex information."
  continue
      fi
      ndd -set /dev/$INTERFACE_TYPE instance $INSTANCE
      SPEED=`ndd -get /dev/$INTERFACE_TYPE link_speed`
      case "$SPEED" in
         0) SPEED="10 Mbit/s" ;;
         1) SPEED="100 Mbit/s" ;;
         1000) SPEED="1 Gbit/s" ;;
      esac
      DUPLEX=`ndd -get /dev/$INTERFACE_TYPE link_mode`
      case "$DUPLEX" in
         0) DUPLEX="half" ;;
         1) DUPLEX="full" ;;
         *) DUPLEX="" ;;
      esac
   fi
   echo "$INTERFACE\t\t$SPEED\t$DUPLEX"
done


Example output:

Interface Speed Duplex
--------- ----- ------
hme0 100 Mbit/s full
hme1 100 Mbit/s full
hme2 100 Mbit/s full


Setting NIC speed and duplex
Solaris is often unable to correctly auto-negotiate duplex settings with a link partner (e.g. switch), especially when the switch is set to 100Mbit full-duplex. You can force the NIC into 100Mbit full-duplex by disabling auto-negotiation and 100Mbit half-duplex capability.

Example with hme0:

1. Make the changes to the running system.

# ndd -set /dev/hme adv_100hdx_cap 0
# ndd -set /dev/hme adv_100fdx_cap 1
# ndd -set /dev/hme adv_autoneg_cap 0

2. Make kernel parameter changes to preserve the speed and duplex settings after a reboot.
# vi /etc/system

Add:

# set hme:hme_adv_autoneg_cap=0
# set hme:hme_adv_100hdx_cap=0
# set hme:hme_adv_100fdx_cap=1

Note: the /etc/system change affects all hme interfaces if multiple NICs are present (e.g. hme0, hme1).



The /etc/system settings listed above are not supported for configuring ce Ethernet adapters during system startup; you may either use ndd commands in an /etc/rc?.d script or create a /platform/sun4u/kernel/drv/ce.conf file with appropriate settings.

Example: /etc/init.d/nddconfig

#!/bin/sh

ndd -set /dev/ce instance 0
ndd -set /dev/ce adv_1000fdx_cap 0
ndd -set /dev/ce adv_1000hdx_cap 0
ndd -set /dev/ce adv_100fdx_cap 1
ndd -set /dev/ce adv_100hdx_cap 0
ndd -set /dev/ce adv_10fdx_cap 0
ndd -set /dev/ce adv_10hdx_cap 0
ndd -set /dev/ce adv_autoneg_cap 0

# ln -s /etc/init.d/nddconfig /etc/rc2.d/S31nddconfig

$ dmesg | grep ce0
Jan 20 11:05:01 crmmdb22 genunix: [ID 611667 kern.info] NOTICE: ce0: xcvr addr:0x01 - link up 100 Mbps half duplex
Jan 20 11:05:15 crmmdb22 genunix: [ID 408822 kern.info] NOTICE: ce0: no fault external to device; service available
Jan 20 11:05:15 crmmdb22 genunix: [ID 611667 kern.info] NOTICE: ce0: xcvr addr:0x01 - link up 100 Mbps full duplex


Manually determining NIC speed and duplex

If you have ce or bge interfaces, use kstat ce and kstat bge, respectively, to return NIC settings. All other interfaces may use ndd to determine NIC settings.

ndd example with hme0, assuming "instance" is 0:

# ndd -get /dev/hme link_mode
Interpretation:
0 -- half-duplex
1 -- full-duplex

# ndd -get /dev/hme link_speed
Interpretation:
0 -- 10 Mbit
1 -- 100 Mbit
1000 -- 1 Gbit

To query a different NIC, such as hme1, set the "instance" to 1, and then perform the link_mode and link_speed queries above.

# ndd -set /dev/hme instance 1

Note: the ndd commands above must be run as root. Otherwise, you will receive errors such as "couldn't push module 'hme0', No such device or address."


ce Ethernet adapters

Older versions of the Sun GigaSwift Ethernet 1.0 driver do not support the ndd link_mode and link_speed parameters. You may either install the latest Sun GigaSwift Ethernet adapter patch (111883) or you may use kstat ce ce_device to get speed and duplex information for ce Ethernet adapters.



$ netstat -k ce | egrep 'link_speed|link_status|link_duplex'

Interpretation:
link_up - 0 down, 1 up
link_speed - speed in Mbit/s
link_duplex - 1 half duplex, 2 full duplex, 0 down


Host/link partner mismatch example
A large number of output errors or collisions may indicate a host and link partner mismatch. The following is netstat -in output from a system configured for 100 half-duplex while the switch was configured for 100 full-duplex.

Name Mtu Net/Dest Address Ipkts Ierrs Opkts Oerrs Collis Queue
ce0 1500 192.168.1.0 192.168.1.1 2707133478 25 2895422910 142310052 182856975 0

In this example, the switch was configured for 100 half-duplex and the system was configured for 100 full-duplex. Note the percentage of Ierrs/Ipkts (~ 1.7%).

Name Mtu Net/Dest Address Ipkts Ierrs Opkts Oerrs Collis Queue
qfe1 1500 10.0.0.0 10.0.0.3 1430247 24663 1779341 0 0 0


CHANGELOG
2007/07/10 -- Added e1000g interfaces
2006/11/29 -- Added dmfe interfaces, fixed bge and ce interfaces, many other modifications and fixes.
2006/04/27 -- Solaris 7 and earlier include subinterface information with netstat -i. Patch by Royce Williams to only query physical NIC interfaces.
2006/03/09 -- Fixed bge interfaces, added support for iprb interfaces, added "support" for le interfaces, modified script to only require root access if using /usr/sbin/ndd to determine NIC speed and duplex settings
2005/05/25 -- Fixed ce interface handling.
2004/09/15 -- Added support for ce and bge interfaces.