[net.unix-wizards] no way to force local ether traffic onto the wire

cak@Purdue.ARPA (01/09/84)

From:  Christopher A Kent <cak@Purdue.ARPA>

Description:
 If it is desired, for testing or other purposes, to force ethernet traffic
for the local host to go out onto the wire, it is not possible to do so.
Changes to if_ether.c to follow that cause the ethernet traffic to use the
loopback device only if it is marked IFF_UP; thus by "ifconfig lo0 down",
one can force the packets to go onto the wire.

Unfortunately, the ARP code must be changed, as well, since it is designed
to ignore incoming packets from itself. A new type of entry, a "sticky"
entry, is defined. The interface address definition routine calls the new
routine arpinstall() to install a mapping entry for itself; this is a
sticky entry that will not be timed out. Thus the resolution can always
be done without broadcasting a packet. This feature may also be useful for
networks that have simple-minded diskless stations that depend on someone
knowing their IP address, using a modified ARP to do a "reverse query".

Repeat-By:
	On an ethernet host, rlogin `hostname`. Inspecting netstat -i will
show the the lo packet counts are going up, not the ether device.

Fix:
	Apply the following diffs...

*** if_ether.c.old
--- if_ether.c.new
***************
*** 31,36
  /* at_flags field values */
  #define	ATF_INUSE	1		/* entry in use */
  #define ATF_COM		2		/* completed entry (enaddr valid) */
  
  #define	ARPTAB_BSIZ	5		/* bucket size */
  #define	ARPTAB_NB	19		/* number of buckets */

--- 31,37 -----
  /* at_flags field values */
  #define	ATF_INUSE	1		/* entry in use */
  #define ATF_COM		2		/* completed entry (enaddr valid) */
+ #define	ATF_STICKY	4		/* may not be timed out */
  
  #define	ARPTAB_BSIZ	5		/* bucket size */
  #define	ARPTAB_NB	19		/* number of buckets */
***************
*** 124,129
  		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
  			if (at->at_flags == 0)
  				continue;
  			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
  			    ARPT_KILLC : ARPT_KILLI))
  				continue;

--- 125,132 -----
  		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
  			if (at->at_flags == 0)
  				continue;
+ 			if (at->at_flags & ATF_STICKY)
+ 				continue;
  			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
  			    ARPT_KILLC : ARPT_KILLI))
  				continue;
***************
*** 198,206
  		return (1);
  	}
  	ifp = &ac->ac_if;
! 	/* if for us, then use software loopback driver */
! 	if (destip->s_addr ==
! 	    ((struct sockaddr_in *)&ifp->if_addr)-> sin_addr.s_addr) {
  		sin.sin_family = AF_INET;
  		sin.sin_addr = *destip;
  		return (looutput(&loif, m, (struct sockaddr *)&sin));

--- 201,210 -----
  		return (1);
  	}
  	ifp = &ac->ac_if;
! 	/* if for us, then use software loopback driver if available */
! 	if ((destip->s_addr ==
! 	    ((struct sockaddr_in *)&ifp->if_addr)-> sin_addr.s_addr) &&
! 	    ((loif.if_flags & IFF_UP) != 0)) {
  		sin.sin_family = AF_INET;
  		sin.sin_addr = *destip;
  		return (looutput(&loif, m, (struct sockaddr *)&sin));
***************
*** 378,384
  	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
  		if (at->at_flags == 0)
  			goto out;	 /* found an empty entry */
! 		if (at->at_timer > oldest) {
  			oldest = at->at_timer;
  			ato = at;
  		}

--- 382,389 -----
  	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
  		if (at->at_flags == 0)
  			goto out;	 /* found an empty entry */
! 		if ((at->at_timer > oldest) && 
! 		    ((at->at_flags) & ATF_STICKY) == 0) {
  			oldest = at->at_timer;
  			ato = at;
  		}
***************
*** 389,392
  	at->at_iaddr = *addr;
  	at->at_flags = ATF_INUSE;
  	return (at);
  }

--- 394,427 -----
  	at->at_iaddr = *addr;
  	at->at_flags = ATF_INUSE;
  	return (at);
+ }
+ 
+ /* 
+  * Install an ARP mapping. Called by device address routines to force
+  * a mapping in for themselves.
+  */
+ 
+ arpinstall(ipaddr, enaddr, sticky)
+ 	register struct in_addr *ipaddr;
+ 	u_char enaddr[6];
+ {
+ 	struct arptab *at = 0;
+ 	int s = splimp();
+ 
+ 	ARPTAB_LOOK(at, ipaddr->s_addr);
+ 	if(at){	/* already there! */
+ 		printf("duplicate IP address 0x%X!! overwriting ");
+ 		printf("%x %x %x %x %x %x with %x %x %x %x %x %x\n",
+ 		    at->at_enaddr[0]&0xff, at->at_enaddr[1]&0xff,
+ 		    at->at_enaddr[2]&0xff, at->at_enaddr[3]&0xff,
+ 		    at->at_enaddr[4]&0xff, at->at_enaddr[5]&0xff,
+ 		    enaddr[0]&0xff, enaddr[1]&0xff,
+ 		    enaddr[2]&0xff, enaddr[3]&0xff,
+ 		    enaddr[4]&0xff, enaddr[5]&0xff);
+ 	}else
+ 		at = arptnew(ipaddr);
+ 
+ 	bcopy((caddr_t)enaddr, (caddr_t)at->at_enaddr, sizeof(at->at_enaddr));
+ 	at->at_flags |= (ATF_COM | (sticky?ATF_STICKY:0));
+ 	splx(s);
  }

It is also necessary to modify the setaddr routine in the appropriate
ethernet interface drivers. NOTE THAT SOME INTERFACES CAN'T HEAR THEIR OWN
PACKETS, so this whole thing is useless for them. Notable among these are
the 3Com 10Mb and the Xerox 3Mb ethers. We have Interlan hardware; the
change for this follows:

*** if_il.c.old
--- if_il.c.new
***************
*** 642,647
  	register struct ifnet *ifp;
  	register struct sockaddr_in *sin;
  {
  
  	ifp->if_addr = *(struct sockaddr *)sin;
  	ifp->if_net = in_netof(sin->sin_addr);

--- 644,650 -----
  	register struct ifnet *ifp;
  	register struct sockaddr_in *sin;
  {
+ 	register struct il_softc *is = &il_softc[ifp->if_unit];
  
  	ifp->if_addr = *(struct sockaddr *)sin;
  	ifp->if_net = in_netof(sin->sin_addr);
***************
*** 646,651
  	ifp->if_addr = *(struct sockaddr *)sin;
  	ifp->if_net = in_netof(sin->sin_addr);
  	ifp->if_host[0] = in_lnaof(sin->sin_addr);
  	sin = (struct sockaddr_in *)&ifp->if_broadaddr;
  	sin->sin_family = AF_INET;
  	sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);

--- 649,655 -----
  	ifp->if_addr = *(struct sockaddr *)sin;
  	ifp->if_net = in_netof(sin->sin_addr);
  	ifp->if_host[0] = in_lnaof(sin->sin_addr);
+ 	arpinstall(&sin->sin_addr, is->is_stats.ils_addr, 1);
  	sin = (struct sockaddr_in *)&ifp->if_broadaddr;
  	sin->sin_family = AF_INET;
  	sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);

----------

croft%Safe%su-score@sri-unix.UUCP (01/13/84)

From:  Bill Croft <croft%Safe@su-score>

Index: sys/netinet/if_ether.c 4.2BSD unfix

Description:
	Chris, is your fix here really necessary?  For testing
	ethernet interfaces (the reason you claimed for your mod),
	I think it would be more conclusive to run Mike Muess's
	ICMP echo sender and bounce packets off of another host.
	If this test fails it's time to swap the xcvr/board or
	run a more exhaustive diagnostic.  Using your test
	method (machine talking to itself on the ether)
	would still be an intermediate step before running
	the ICMP test, since I can imagine certain ether
	failure modes (weak transmitter) that would pass
	your test but fail the ICMP echo.

Repeat-By:
	NA

Fix:
	Unnecessary?

cak@Purdue.ARPA (01/13/84)

From:  Christopher A Kent <cak@Purdue.ARPA>

Bill,

When you have a network with only one host on it, you can't very well
bounce packets from another host. At the time I developed the mods, we
had cable problems in our main cable as well as hardware problems, so I
was running around connecting a transceiver with 2.5 meters of
terminated cable to each machine and trying to test for bad hardware.
It was immensely helpful.

It's a nice efficiency hack to go through loopback whenever possible,
but there should be a way to turn it off.

Cheers,
chris
----------