[comp.protocols.tcp-ip] 4.3BSD routed/gateways bug & fix

beau@ultra.UUCP (Beau James {Manager - SW Development - Ultra Networks}) (09/22/89)

This is a followup to a message I posted two weeks ago regarding
a problem with the 4.3-tahoe BSD route daemon (and also SunOS through
4.0.3).  We've identified the bug and at least a partial fix.

Symptom recap:
	Remote network gateways identified to routed as "active"
	gateways in the /etc/gateways file are entered on the local
	machine as gateways to destination net "0.0.0.0" instead of
	the destination network named in the /etc/gateways file.
	That invalid information is then broadcast by the local
	system's route daemon.

Problem identified:
	The routine "gwkludge()" in routed/startup.c processes the
	entries from the /etc/gateways file.  For active gateways,
	it ends up adding the route by calling the subroutine
	"addrouteforif()", in the same module.  "addrouteforif()"
	is also called by "ifinit()" when the hardware interfaces
	found by the kernel are added to the route daemon's tables.

	It appears that when "ifinit()" and "addrouteforif()"
	were extended to handle subnetted networks, several
	new fields were added to the (struct interface).
	However, "gwkludge()" was not modified to correctly
	initialize those fields before calling "addrouteforif()".

Partial (?) bugfix:
	The following change to the bottom of "gwkludge()" will
	result in the proper destination network address being
	entered into the routing tables:

                /* assume no duplicate entries */
                externalinterfaces++;
                ifp = (struct interface *)malloc(sizeof (*ifp));
                bzero((char *)ifp, sizeof (*ifp));
                ifp->int_flags = IFF_REMOTE;
                /* can't identify broadcast capability */
                ifp->int_net = inet_netof(dst.sin_addr);
	ADD-->  ifp->int_subnet = ifp->int_net;
                if (strcmp(type, "host") == 0) {
                        ifp->int_flags |= IFF_POINTOPOINT;
                        ifp->int_dstaddr = *((struct sockaddr *)&dst);
                }
                ifp->int_addr = *((struct sockaddr *)&gate);
                ifp->int_metric = metric;
                ifp->int_next = ifnet;
                ifnet = ifp;
                addrouteforif(ifp);

	This change has been tested on a non-subnetted Ethernet
	environment.  There are two additional fields in the
	(struct interface) that probably should be initialized
	to non-zero values, but it is not clear to me what the
	proper initialization is, for remote gateways.  The fields
	are:

		ifp->int_netmask
		ifp->int_subnetmask

	For interfaces found by the kernel, "ifinit()" uses an
	ioctl(SIOCGIFNETMASK) and a series of tests to set these
	fields:

                if (ioctl(s, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
                        syslog(LOG_ERR, "%s: ioctl (get netmask)",
                            ifr->ifr_name);
                        continue;
                }
                sin = (struct sockaddr_in *)&ifreq.ifr_addr;
                ifs.int_subnetmask = ntohl(sin->sin_addr.s_addr);
                sin = (struct sockaddr_in *)&ifs.int_addr;
                i = ntohl(sin->sin_addr.s_addr);
                if (IN_CLASSA(i))
                        ifs.int_netmask = IN_CLASSA_NET;
                else if (IN_CLASSB(i))
                        ifs.int_netmask = IN_CLASSB_NET;
                else
                        ifs.int_netmask = IN_CLASSC_NET;
                ifs.int_net = i & ifs.int_netmask;
                ifs.int_subnet = i & ifs.int_subnetmask;
                if (ifs.int_subnetmask != ifs.int_netmask)
                        ifs.int_flags |= IFF_SUBNET;

	If anyone out there has better insight as to the proper way
	to initialize these netmasks for the case of remote gateways,
	please pass it along.

Thanks,

Beau James				beau@Ultra.COM
Ultra Network Technologies		{sun,ames}!ultra.com!beau