Bug 75570 - Bounding a network interface config to a HWADDR does not work
Bounding a network interface config to a HWADDR does not work
Status: CLOSED CURRENTRELEASE
Product: Red Hat Linux
Classification: Retired
Component: initscripts (Show other bugs)
8.0
i686 Linux
medium Severity medium
: ---
: ---
Assigned To: Bill Nottingham
Brock Organ
:
: 76598 76599 (view as bug list)
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2002-10-09 18:16 EDT by diego.santacruz
Modified: 2014-03-16 22:31 EDT (History)
5 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2003-10-30 00:25:10 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)
bash replacement for nameif(8) (6.87 KB, text/plain)
2003-04-29 10:06 EDT, Ole Craig
no flags Details
drop-in replacement for nameif(8) (3.84 KB, text/plain)
2003-04-29 10:32 EDT, Ole Craig
no flags Details

  None (edit)
Description diego.santacruz 2002-10-09 18:16:37 EDT
From Bugzilla Helper:
User-Agent: Mozilla/5.0 Galeon/1.2.5 (X11; Linux i686; U;) Gecko/20020809

Description of problem:
If one sets a HWADDR (with the HWADDR variable in the ifcfg-eth0 file) for an
interface and inserts a network card with another HWADDR ifup starts it up
anyways. Furthermore ifdown refuses to shut it down since the HWADDR does not match.

Version-Release number of selected component (if applicable):
initscripts-6.95-1

How reproducible:
Always

Steps to Reproduce:
1. Configure a network interface with redhat-config-network
2. Select the "Bind to MAC address" option in the Hardware Device tab of the
configuration dialog.
3. Save the network configuration
4. Insert a different network card (with a different MAC address)
	

Actual Results:  The network interface is started up despite a mismatch in the
MAC address.
Since the MAC address does not match, ifdown refuses to bring down the network
interface (which is correct behaviour).

Expected Results:  The network interface should not be started up.

Additional info:

The problem is that nameif returns with a zero code when no interface with the
given MAC is found. Therefore ifup believes that the rename succeded and
proceeds bringing up the device when it should not.

However, I think the handling of HWADDR in ifup is bogus with the ifname command
as it is. As I understand:

1. First is_available() is used to ensure that $REALDEVICE exists. Let's say it
is eth0. If is_available() succeds then eth0 exists, otherwise ifup exits with
non-zero code.
2. Then HWADDR is compared with the MAC address of eth0. If they don't match
nameif is used to attempt to rename an interface which has HWADDR as the MAC
address as eth0. However this will *always* fail since eth0 is already in use.

I think nameif, with a special option flag, should exchange the interface names
if the new name is being used but not up. For example if both eth0 and eth1 are
present and down and we do an 'ifup eth0', but HWADDR matches eth1, then nameif
renames eth0 to some temporary name, eth1 to eth0 and the temporary name to
eth1. Since the original eth0 is not up it should not really matter if it is
renamed. This same functionality can also be achieved by using the 'ip' command
in a somewhat complex shell function.
Comment 1 diego.santacruz 2002-10-09 18:26:21 EDT
nameif bug reported in bug #75572
Comment 2 Mike McLean 2002-10-18 21:44:31 EDT
There is a bug in ifdown (I looking at initscripts-6.95-1).  When the interface
macaddr does not match the one in the config, ifdown looks for another config
file *with the same nonmatching macaddr*.  The following patch will fix this
little problem, but not the larger one.

--- ifdown.orig 2002-10-18 18:35:46.000000000 -0700
+++ ifdown      2002-10-18 18:37:57.000000000 -0700
@@ -52,7 +52,7 @@
     FOUNDMACADDR=`LC_ALL= LANG= ip -o link show ${REALDEVICE} | \
        sed 's/.*link\/ether \([[:alnum:]:]*\).*/\1/'`
     if [ "${FOUNDMACADDR}" != "${HWADDR}" ]; then
-        NEWCONFIG=`fgrep -l "HWADDR=${HWADDR}"
/etc/sysconfig/network-scripts/ifcfg-*`
+        NEWCONFIG=`fgrep -l "HWADDR=${FOUNDMACADDR}"
/etc/sysconfig/network-scripts/ifcfg-*`
        if [ -n "${NEWCONFIG}" -a "${NEWCONFIG}" != "${CONFIG}" ]; then
           exec /sbin/ifdown ${NEWCONFIG}
        else
Comment 3 Mike McLean 2002-10-18 22:06:18 EDT
Also we should probably decide on a standard for the case of HWADDR.  /sbin/ip
reports them lower case, but neat writes them uppercase in the config file. 
Optionally, we could just make the test case insensitive with something like

echo "$HWADDR" |fgrep -qix "$FOUNDMACADDR"
Comment 4 Jeremy Portzer 2002-10-19 12:16:22 EDT
I ran into the same problem with the case of the HWADDR.  Fixing this solves the
problem for me.  I used this patch to make sure both are uppercase (allowing for
the fact that the config file might be lowercase too if hand-edited).

--- ifdown      2002-10-19 11:32:47.000000000 -0400
+++ ifdown.good 2002-10-19 11:31:43.000000000 -0400
@@ -51,6 +51,9 @@
 if [ -n "${HWADDR}" ]; then
     FOUNDMACADDR=`LC_ALL= LANG= ip -o link show ${REALDEVICE} | \
        sed 's/.*link\/ether \([[:alnum:]:]*\).*/\1/'`
+    # uppercase the mac address variables
+    FOUNDMACADDR=`echo ${FOUNDMACADDR} | tr a-f A-F`
+    HWADDR=`echo ${HWADDR} | tr a-f A-F`
     if [ "${FOUNDMACADDR}" != "${HWADDR}" ]; then
         NEWCONFIG=`fgrep -l "HWADDR=${HWADDR}"
/etc/sysconfig/network-scripts/ifcfg-*`
        if [ -n "${NEWCONFIG}" -a "${NEWCONFIG}" != "${CONFIG}" ]; then
Comment 5 Bill Nottingham 2002-10-23 21:09:51 EDT
*** Bug 76599 has been marked as a duplicate of this bug. ***
Comment 6 Bill Nottingham 2002-10-23 21:10:11 EDT
*** Bug 76598 has been marked as a duplicate of this bug. ***
Comment 7 Ole Craig 2003-04-29 10:06:25 EDT
Created attachment 91383 [details]
bash replacement for nameif(8)
Comment 8 Ole Craig 2003-04-29 10:07:29 EDT
This bug has bitten me on both RH8 and RH9 systems, and is particularly egregious
when using multiple interfaces driven by the same card -- since all interfaces get
named as soon as the module loads, nameif(8) refuses to rename (e.g.) eth2 to eth0
because eth0 already exists. I swore at this for some time and finally ended up
writing a more-or-less drop-in replacement for nameif in bash, using "ip link"
commands. This simplifies things greatly for me, and so far it works like a charm.
Attached; you can also find a copy at http://www.cs.umass.edu/~olc/pub/nameif.
Comment 9 Ole Craig 2003-04-29 10:13:15 EDT
Comment on attachment 91383 [details]
bash replacement for nameif(8)


#!/bin/bash

# Copyright (c) 2003 Ole Craig <olc@cs.umass.edu>, all rights
# reserved. This software may be freely redistributed under the terms
# of the GNU general public license. See (e.g.)
# http://www.gnu.org/licenses/gpl.txt for details. Although it is not
# required by the license terms, if you improve this script in any way
# the author would deeply appreciate seeing (and perhaps learning
# from) your changes.

# we use this script because regular nameif doesn't handle the case
# where we want to rename preexisting devices (e.g. swap eth0 <-->
# eth1) and sometimes segfaults anyway.

# quick'n'dirty method for dealing with errout (overrideable with -s)
logger="echo `basename $0[$$]`: "

function usage() {
    echo "usage: `basename $0` [-c configurationfile] [-s] {ifname macaddress}"
    exit -1
}

function loggit() {
    $logger "$*"
}

function die () {
    loggit $*
    exit -1
}
function namer() {
# this is where we do the work on a given IF
# expect 2 inputs: current name targetname
    err=`ip link set $1 name $2`
    status=$?
    if [ "$err" != "" ]; then
	$logger $err
    fi
    return $status
}

function isup() {
    # check to see if interface is active
    ip link show $1 2>&1 | cut -f3 -d" " | grep -qw UP
    return $?
}

# main body
if [ ${#*} == 0 ]; then
    # we get our parameters from /etc/mactab unless otherwise specified
    CONFFILE=/etc/mactab
else
    while [ ${#*} -gt 0 ]; do
	case $1 in
	    -s)
	    logger="logger -i -t `basename $0`"
	    shift
	    ;;
	    -c)
		if [ "$2" == "" -o ! -r $2 ]; then
		    usage
		else
		    CONFFILE=$2
		    shift
		    shift
		fi
		;;
	    *)
# we assume anything else is an interface name
		if [ "$2" == "" -o "$3" != "" ]; then
		    usage
		else
		    DES_IFNAME=$1
		    # attempt to sanitize input a bit
		    DES_MAC=`echo $2 | tr 'a-f-' 'A-F:'`
		    shift
		    shift
		fi


	esac
    done

    i=0
    BLOCKED=0
fi
if [ "$DES_IFNAME" = "" ]; then
# if we're processing a table (/etc/mactab by default) then we iterate
# through...

    itab=0
    for q in `cat $CONFFILE`; do
	MACTABLE[$itab]=$q
	let itab++
    done

# ...and recurse
    for (( q=0 ; $q < $itab ; q++)); do
	$0 ${MACTABLE[((q++))]} ${MACTABLE[$q]}
# note nasty increment of counter within loop. Ugh.
    done
else # we were given an argument, use it

# Build table of current IFs and their associated MAC addresses. While
# we're at it, grab current values for IF name and IF/MAC of blocking
# name, if any.  If we only have one MAC address as input, then we
# scan the table of available IFs for a match. If that MAC is
# currently in use by another IF name, we swap. Otherwise, we just
# name it. (Or leave it alone, as may be the case.)

# DES_IFNAME is the desired name for the mac address supplied on commandline.

    i=0
    GETMAC=0
    for q in `ifconfig -a | grep -E ^eth | awk '{ print $1" "$5 }'`; do
	MACDESK[$i]=$q
	if [ "$GETMAC" != "0" ]; then
	    CUR_MAC=$q
	    GETMAC=0
	fi
	if [ "$q" == "$DES_MAC" ]; then
	    CUR_IFNAME=${MACDESK[$i-1]}
	fi
	if [ "$q" == "$DES_IFNAME" ]; then
	    BLOCKED=1
	    GETMAC=1
	fi
	let i++
    done

# CUR_IFNAME is the current name for the mac address supplied on commandline.
# if $CUR_IFNAME is empty at this point, then we don't have an
# interface with this MAC address

if [ "$CUR_IFNAME" == "" ]; then
    die "no interface found with requested MAC address \"$DES_MAC\""
fi

# swap "blocked" names
    isup $DES_IFNAME && die "$DES_IFNAME ($CUR_MAC) already active"
    isup $CUR_IFNAME && die "$CUR_IFNAME ($DES_MAC) already active"
    if [ "$CUR_IFNAME" == "$DES_IFNAME" ]; then
	exit 0 # everything's jake, current and desired IF names are the same
    fi
    if [ $BLOCKED == 1 ]; then
	namer $DES_IFNAME nameif_tmp$DES_IFNAME
	namer $CUR_IFNAME $DES_IFNAME
	namer nameif_tmp$DES_IFNAME $CUR_IFNAME
    else
	namer $CUR_IFNAME $DES_IFNAME
    fi

fi
Comment 10 Ole Craig 2003-04-29 10:14:24 EDT
Comment on attachment 91383 [details]
bash replacement for nameif(8)


#!/bin/bash

# Copyright (c) 2003 Ole Craig <olc@cs.umass.edu>, all rights
# reserved. This software may be freely redistributed under the terms
# of the GNU general public license. See (e.g.)
# http://www.gnu.org/licenses/gpl.txt for details. Although it is not
# required by the license terms, if you improve this script in any way
# the author would deeply appreciate seeing (and perhaps learning
# from) your changes.

# we use this script because regular nameif doesn't handle the case
# where we want to rename preexisting devices (e.g. swap eth0 <-->
# eth1) and sometimes segfaults anyway.

# quick'n'dirty method for dealing with errout (overrideable with -s)
logger="echo `basename $0[$$]`: "

function usage() {
    echo "usage: `basename $0` [-c configurationfile] [-s] {ifname macaddress}"
    exit -1
}

function loggit() {
    $logger "$*"
}

function die () {
    loggit $*
    exit -1
}
function namer() {
# this is where we do the work on a given IF
# expect 2 inputs: current name targetname
    err=`ip link set $1 name $2`
    status=$?
    if [ "$err" != "" ]; then
	$logger $err
    fi
    return $status
}

function isup() {
    # check to see if interface is active
    ip link show $1 2>&1 | cut -f3 -d" " | grep -qw UP
    return $?
}

# main body
if [ ${#*} == 0 ]; then
    # we get our parameters from /etc/mactab unless otherwise specified
    CONFFILE=/etc/mactab
else
    while [ ${#*} -gt 0 ]; do
	case $1 in
	    -s)
	    logger="logger -i -t `basename $0`"
	    shift
	    ;;
	    -c)
		if [ "$2" == "" -o ! -r $2 ]; then
		    usage
		else
		    CONFFILE=$2
		    shift
		    shift
		fi
		;;
	    *)
# we assume anything else is an interface name
		if [ "$2" == "" -o "$3" != "" ]; then
		    usage
		else
		    DES_IFNAME=$1
		    # attempt to sanitize input a bit
		    DES_MAC=`echo $2 | tr 'a-f-' 'A-F:'`
		    shift
		    shift
		fi


	esac
    done

    i=0
    BLOCKED=0
fi
if [ "$DES_IFNAME" = "" ]; then
# if we're processing a table (/etc/mactab by default) then we iterate
# through...

    itab=0
    for q in `cat $CONFFILE`; do
	MACTABLE[$itab]=$q
	let itab++
    done

# ...and recurse
    for (( q=0 ; $q < $itab ; q++)); do
	$0 ${MACTABLE[((q++))]} ${MACTABLE[$q]}
# note nasty increment of counter within loop. Ugh.
    done
else # we were given an argument, use it

# Build table of current IFs and their associated MAC addresses. While
# we're at it, grab current values for IF name and IF/MAC of blocking
# name, if any.  If we only have one MAC address as input, then we
# scan the table of available IFs for a match. If that MAC is
# currently in use by another IF name, we swap. Otherwise, we just
# name it. (Or leave it alone, as may be the case.)

# DES_IFNAME is the desired name for the mac address supplied on commandline.

    i=0
    GETMAC=0
    for q in `ifconfig -a | grep -E ^eth | awk '{ print $1" "$5 }'`; do
	MACDESK[$i]=$q
	if [ "$GETMAC" != "0" ]; then
	    CUR_MAC=$q
	    GETMAC=0
	fi
	if [ "$q" == "$DES_MAC" ]; then
	    CUR_IFNAME=${MACDESK[$i-1]}
	fi
	if [ "$q" == "$DES_IFNAME" ]; then
	    BLOCKED=1
	    GETMAC=1
	fi
	let i++
    done

# CUR_IFNAME is the current name for the mac address supplied on commandline.
# if $CUR_IFNAME is empty at this point, then we don't have an
# interface with this MAC address

if [ "$CUR_IFNAME" == "" ]; then
    die "no interface found with requested MAC address \"$DES_MAC\""
fi

# swap "blocked" names
    isup $DES_IFNAME && die "$DES_IFNAME ($CUR_MAC) already active"
    isup $CUR_IFNAME && die "$CUR_IFNAME ($DES_MAC) already active"
    if [ "$CUR_IFNAME" == "$DES_IFNAME" ]; then
	exit 0 # everything's jake, current and desired IF names are the same
    fi
    if [ $BLOCKED == 1 ]; then
	namer $DES_IFNAME nameif_tmp$DES_IFNAME
	namer $CUR_IFNAME $DES_IFNAME
	namer nameif_tmp$DES_IFNAME $CUR_IFNAME
    else
	namer $CUR_IFNAME $DES_IFNAME
    fi

fi
Comment 11 Ole Craig 2003-04-29 10:18:45 EDT
Aaaah!

Sorry about that. The original attachment was bogus -- I grabbed the old nameif
instead of my replacement. Then, misunderstanding some bugzilla functionality
that I don't often use, I tried to rectify my mistake by replacing the attachment
with the correct file... Sigh. Compound that with a cached POST operation, and 

Anyway, there it is. Sorry for the extra pagefiller.
Comment 12 Ole Craig 2003-04-29 10:32:38 EDT
Created attachment 91386 [details]
drop-in replacement for nameif(8)

The real bashful nameif...
Comment 13 Craig Lawson 2003-10-29 18:54:04 EST
I was burned by this bug, too. The source of my problem was the configuration 
files /etc/sysconfig/network-scripts/ifcfg-eth? and /etc/sysconfig/networking.

I upgraded my system, replacing a NIC with an ethernet driver built into the 
motherboard. This made the MAC addresses in the configuration files incorrect. 
Also, redhat-config-network put DEVICE=eth1 in my eth0 file and vice versa.

After upgrading, the MAC confusion failed the weak FOUNDMACADDR test, and the 
swapped device assignments assured that the script would invoke itself forever 
through exec.

Any of the suggestions in this bug report would have caught my problems.
Comment 14 Bill Nottingham 2003-10-30 00:25:10 EST
Something similar to this (but different) is in both RHEL3 and Fedora Core.

Note You need to log in before you can comment on or make changes to this bug.