Subject: bin/10531: dhcp (client and server) don't work on Token-Ring
To: None <gnats-bugs@gnats.netbsd.org>
From: Onno van der Linden <onno@simplex.nl>
List: netbsd-bugs
Date: 07/07/2000 15:23:30
>Number:         10531
>Category:       bin
>Synopsis:       dhcp (client and server) don't work on Token-Ring
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jul 07 15:24:01 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     Onno van der Linden
>Release:        1.5_ALPHA
>Organization:
	
>Environment:
	
System: NetBSD sheep 1.5_ALPHA NetBSD 1.5_ALPHA (SHEEP) #15: Fri Jul 7 22:14:13 MEST 2000 onno@sheep:/usr/src/sys/arch/i386/compile/SHEEP i386


>Description:
	Token-ring support in the ISC dhcp only works for linux.
>How-To-Repeat:
	run the dhcp server or client on tr0
>Fix:
	Below is diff that adds a couple of IEEE802 checks and a
        changed tr.c to work for NetBSD instead of linux. There's
        also a PR "no IEEE802 support for DHCP in the kernel" that
        needs to be applied to the NetBSD kernel in order get dhcp
        really working.

*** /usr/src/usr.sbin/dhcp/common/bpf.c.orig	Sun Jul  2 16:30:35 2000
--- /usr/src/usr.sbin/dhcp/common/bpf.c	Fri Jul  7 23:55:22 2000
***************
*** 139,142 ****
--- 139,145 ----
  	struct interface_info *info;
  {
+ #ifdef HAVE_TR_SUPPORT
+ 	int link_layer;
+ #endif
  	/* If we're using the bpf API for sending and receiving,
  	   we don't need to register this interface twice. */
***************
*** 146,149 ****
--- 149,161 ----
  	info -> wfdesc = info -> rfdesc;
  #endif
+ 
+ #ifdef HAVE_TR_SUPPORT
+ 	if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 &&
+ 	    link_layer == DLT_IEEE802) {
+ 		int flag = 1;
+ 		if (ioctl(info->wfdesc, BIOCSHDRCMPLT, &flag))
+ 			log_fatal("Can't set BIOCSHDRCMPLT: %m");
+ 	}
+ #endif
  	if (!quiet_interface_discovery)
  		log_info ("Sending on   BPF/%s/%s%s%s",
***************
*** 238,244 ****
  	struct bpf_program p;
  	u_int32_t bits;
! #if defined(DEC_FDDI) || defined(NETBSD_FDDI)
! 	int link_layer;
! #endif /* DEC_FDDI || NETBSD_FDDI */
  
  	/* Open a BPF device and hang it on this interface... */
--- 250,256 ----
  	struct bpf_program p;
  	u_int32_t bits;
! #if defined(DEC_FDDI) || defined(NETBSD_FDDI) || defined(HAVE_TR_SUPPORT)
! 	int link_layer = 0;
! #endif /* DEC_FDDI || NETBSD_FDDI || HAVE_TR_SUPPORT */
  
  	/* Open a BPF device and hang it on this interface... */
***************
*** 287,291 ****
  	p.bf_len = dhcp_bpf_filter_len;
  
! #if defined(DEC_FDDI) || defined(NETBSD_FDDI)
  	/* See if this is an FDDI interface, flag it for later. */
  	if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 &&
--- 299,303 ----
  	p.bf_len = dhcp_bpf_filter_len;
  
! #if defined(DEC_FDDI) || defined(NETBSD_FDDI) || defined(HAVE_TR_SUPPORT)
  	/* See if this is an FDDI interface, flag it for later. */
  	if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 &&
***************
*** 310,315 ****
  		}
  		p.bf_insns = bpf_fddi_filter;
  	} else
! #endif /* DEC_FDDI || NETBSD_FDDI */
  	p.bf_insns = dhcp_bpf_filter;
  
--- 322,330 ----
  		}
  		p.bf_insns = bpf_fddi_filter;
+ 	} else if (link_layer == DLT_IEEE802) {
+ 		p.bf_len = dhcp_bpf_tr_filter_len;
+ 		p.bf_insns = dhcp_bpf_tr_filter;
  	} else
! #endif /* DEC_FDDI || NETBSD_FDDI || HAVE_TR_SUPPORT */
  	p.bf_insns = dhcp_bpf_filter;
  
*** /usr/src/usr.sbin/dhcp/common/discover.c.orig	Sun Jun 11 12:15:14 2000
--- /usr/src/usr.sbin/dhcp/common/discover.c	Fri Jul  7 21:57:24 2000
***************
*** 220,223 ****
--- 220,225 ----
  				tmp -> hw_address.hbuf [0] = HTYPE_FDDI;
  #endif
+ 			} else if (foo -> sdl_type == IFT_ISO88025) {
+ 				tmp -> hw_address.hbuf [0] = HTYPE_IEEE802;
  			} else {
  				continue;
*** /dev/null	Fri Jul  7 22:19:09 2000
--- /usr/src/usr.sbin/dhcp/common/tr.c	Fri Jul  7 22:03:26 2000
***************
*** 0 ****
--- 1,325 ----
+ /* tr.c
+ 
+    token ring interface support
+    Contributed in May of 1999 by Andrew Chittenden */
+ 
+ /*
+  * Copyright (c) 1996-2000 Internet Software Consortium.
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice, this list of conditions and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in the
+  *    documentation and/or other materials provided with the distribution.
+  * 3. Neither the name of The Internet Software Consortium nor the names
+  *    of its contributors may be used to endorse or promote products derived
+  *    from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  */
+ 
+ #ifndef lint
+ static char copyright[] =
+ "$Id: tr.c,v 1.1.1.1 2000/04/22 07:11:38 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
+ #endif /* not lint */
+ 
+ #include "dhcpd.h"
+ 
+ #if defined (HAVE_TR_SUPPORT) && \
+ 	(defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING))
+ #include "includes/netinet/ip.h"
+ #include "includes/netinet/udp.h"
+ #include "includes/netinet/if_ether.h"
+ #include <net/if_token.h>
+ #include <net/if_llc.h>
+ #include <sys/time.h>
+ 
+ /*
+  * token ring device handling subroutines.  These are required as token-ring
+  * does not have a simple on-the-wire header but requires the use of
+  * source routing
+  */
+ 
+ static int insert_source_routing PROTO ((struct token_header *trh, struct interface_info *interface));
+ static void save_source_routing PROTO ((struct token_header *trh, struct interface_info *interface));
+ static void expire_routes PROTO ((void));
+ 
+ /*
+  * As we keep a list of interesting routing information only, a singly
+  * linked list is all we need
+  */
+ struct routing_entry {
+         struct routing_entry *next;
+         unsigned char addr[ISO88025_ADDR_LEN];
+         unsigned char iface[5];
+         u_int16_t tr_rcf;               /* route control field */
+         u_int16_t tr_rdf[TOKEN_MAX_BRIDGE]; /* routing registers */
+         unsigned long access_time;      /* time we last used this entry */
+ };
+ 
+ static struct routing_entry *routing_info = NULL;
+ 
+ static int routing_timeout = 10;
+ static struct timeval routing_timer;
+ 
+ void assemble_tr_header (interface, buf, bufix, to)
+ 	struct interface_info *interface;
+ 	unsigned char *buf;
+ 	unsigned *bufix;
+ 	struct hardware *to;
+ {
+         struct token_header *trh;
+         int hdr_len;
+         struct llc *l;
+ 
+ 
+         /* set up the token header */
+         trh = (struct token_header *) &buf[*bufix];
+         if (interface -> hw_address.hlen - 1 == sizeof (trh->token_shost))
+                 memcpy (trh->token_shost, &interface -> hw_address.hbuf [1],
+                                     sizeof (trh->token_shost));
+         else
+                 memset (trh->token_shost, 0x00, sizeof (trh->token_shost));
+ 
+         if (to && to -> hlen == 7) /* XXX */
+                 memcpy (trh->token_dhost, &to -> hbuf [1], sizeof trh->token_dhost);
+         else
+                 memset (trh->token_dhost, 0xff, sizeof (trh->token_dhost));
+ 
+ 	hdr_len = insert_source_routing (trh, interface);
+ 
+         trh->token_ac = TOKEN_AC;
+         trh->token_fc = TOKEN_FC;
+ 
+         /* set up the llc header for snap encoding after the tr header */
+         l = (struct llc *)(buf + *bufix + hdr_len);
+         l->llc_dsap = LLC_SNAP_LSAP;
+         l->llc_ssap = LLC_SNAP_LSAP;
+         l->llc_control = LLC_UI;
+         l->llc_snap.org_code[0] = 0;
+         l->llc_snap.org_code[1] = 0;
+         l->llc_snap.org_code[2] = 0;
+         l->llc_snap.ether_type = htons(ETHERTYPE_IP);
+ 
+         hdr_len += sizeof(struct llc);
+ 
+         *bufix += hdr_len;
+ }
+ 
+ 
+ static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ 
+ ssize_t decode_tr_header (interface, buf, bufix, from)
+         struct interface_info *interface;
+         unsigned char *buf;
+         unsigned bufix;
+         struct hardware *from;
+ {
+         struct token_header *trh = (struct token_header *) (buf + bufix);
+ 	struct token_rif *trrif = TOKEN_RIF(trh);
+         struct llc *l;
+ 	struct ip *ip;
+ 	struct udphdr *udp;
+         unsigned int route_len = 0;
+         ssize_t hdr_len;
+         struct timeval now;
+ 
+         /* see whether any source routing information has expired */
+         gettimeofday(&now, NULL);
+ 
+ 	if (routing_timer.tv_sec == 0)
+                 routing_timer.tv_sec = now.tv_sec + routing_timeout;
+         else if ((now.tv_sec - routing_timer.tv_sec) > 0)
+                 expire_routes();
+ 
+ 	if (trh->token_shost[0] & TOKEN_RI_PRESENT)
+                 route_len = (ntohs(trrif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8;
+         else
+                 route_len = 0;
+ 
+         hdr_len = sizeof (struct token_header) + route_len;
+ 
+         /* now filter out unwanted packets: this is based on the packet
+          * filter code in bpf.c */
+         l = (struct llc *)(buf + bufix + hdr_len);
+         ip = (struct ip *) (l + 1);
+         udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip));
+ 
+         /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent
+          * to our port */
+         if (l->llc_dsap != LLC_SNAP_LSAP
+                         || ntohs(l->llc_snap.ether_type) != ETHERTYPE_IP
+                         || ip->ip_p != IPPROTO_UDP
+                         || (ip->ip_off & IP_OFFMASK) != 0
+                         || udp->uh_dport != local_port)
+                 return -1;
+ 
+         /* only save source routing information for packets from valued hosts */
+         save_source_routing(trh, interface);
+         return hdr_len + sizeof (struct llc);
+ }
+ 
+ /* insert_source_routing inserts source route information into the token ring
+  * header
+  */
+ static int insert_source_routing (trh, interface)
+         struct token_header *trh;
+         struct interface_info* interface;
+ {
+ 	struct routing_entry *rover;
+         struct timeval now;
+         unsigned int route_len = 0;
+ 	struct token_rif *trrif = TOKEN_RIF(trh);
+ 
+         gettimeofday(&now, NULL);
+ 
+ 	/* single route broadcasts as per rfc 1042 */
+ 	if (memcmp(trh->token_dhost, tr_broadcast,ISO88025_ADDR_LEN) == 0) {
+ 		trh->token_shost[0] |= TOKEN_RI_PRESENT;
+ 		trrif->tr_rcf = ((sizeof(trrif->tr_rcf)) << 8) & TOKEN_RCF_LEN_MASK;  
+                 trrif->tr_rcf |= (TOKEN_RCF_FRAME2 | TOKEN_RCF_BROADCAST_SINGLE);
+ 		trrif->tr_rcf = htons(trrif->tr_rcf);
+ 	} else {
+ 		/* look for a routing entry */
+                 for (rover = routing_info; rover != NULL; rover = rover->next) {
+                         if (memcmp(rover->addr, trh->token_dhost, ISO88025_ADDR_LEN) == 0)
+                                 break;
+                 }
+ 
+ 		if (rover != NULL) {
+                         /* success: route that frame */
+                         if ((rover->tr_rcf & TOKEN_RCF_LEN_MASK) >> 8) {
+                                 u_int16_t rcf = rover->tr_rcf;
+ 				memcpy(trrif->tr_rdf,rover->tr_rdf,sizeof(trrif->tr_rdf));
+ 				rcf ^= TOKEN_RCF_DIRECTION;	
+ 				rcf &= ~TOKEN_RCF_BROADCAST_MASK;
+                                 trrif->tr_rcf = htons(rcf);
+ 				trh->token_shost[0] |= TOKEN_RI_PRESENT;
+ 			}
+ 			rover->access_time = now.tv_sec;
+ 		} else {
+                         /* we don't have any routing information so send a
+                          * limited broadcast */
+                         trh->token_shost[0] |= TOKEN_RI_PRESENT;
+                         trrif->tr_rcf = ((sizeof(trrif->tr_rcf)) << 8) & TOKEN_RCF_LEN_MASK;  
+                         trrif->tr_rcf |= (TOKEN_RCF_FRAME2 | TOKEN_RCF_BROADCAST_SINGLE);
+                         trrif->tr_rcf = htons(trrif->tr_rcf);
+ 		}
+ 	}
+ 
+ 	/* return how much of the header we've actually used */
+ 	if (trh->token_shost[0] & TOKEN_RI_PRESENT)
+                 route_len = (ntohs(trrif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8;
+         else
+                 route_len = 0;
+ 
+         return sizeof (struct token_header) + route_len;
+ }
+ 
+ /*
+  * save any source routing information
+  */
+ static void save_source_routing(trh, interface)
+         struct token_header *trh;
+         struct interface_info *interface;
+ {
+         struct routing_entry *rover;
+         struct timeval now;
+         unsigned char saddr[ISO88025_ADDR_LEN];
+         u_int16_t rcf = 0;
+ 	struct token_rif *trrif = TOKEN_RIF(trh);
+ 
+         gettimeofday(&now, NULL);
+ 
+         memcpy(saddr, trh->token_shost, sizeof(saddr));
+         saddr[0] &= 0x7f;   /* strip off source routing present flag */
+ 
+         /* scan our table to see if we've got it */
+         for (rover = routing_info; rover != NULL; rover = rover->next) {
+                 if (memcmp(&rover->addr[0], &saddr[0], ISO88025_ADDR_LEN) == 0)
+                         break;
+         }
+ 
+         /* found an entry so update it with fresh information */
+         if (rover != NULL) {
+                 if ((trh->token_shost[0] & TOKEN_RI_PRESENT) &&
+ 		    ((ntohs(trrif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8) > 2) {
+                         rcf = ntohs(trrif->tr_rcf);
+                         rcf &= ~TOKEN_RCF_BROADCAST_MASK;
+                         memcpy(rover->tr_rdf, trrif->tr_rdf, sizeof(rover->tr_rdf));
+                 }
+                 rover->tr_rcf = rcf;
+                 rover->access_time = now.tv_sec;
+                 return;     /* that's all folks */
+         }
+ 
+         /* no entry found, so create one */
+         rover = dmalloc (sizeof (struct routing_entry), MDL);
+         if (rover == NULL) {
+                 fprintf(stderr,
+ 			"%s: unable to save source routing information\n",
+ 			__FILE__);
+                 return;
+         }
+ 
+         memcpy(rover->addr, saddr, sizeof(rover->addr));
+         memcpy(rover->iface, interface->name, 5);
+         rover->access_time = now.tv_sec;
+         if (trh->token_shost[0] & TOKEN_RI_PRESENT) {
+                 if (((ntohs(trrif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8) > 2) {
+                         rcf = ntohs(trrif->tr_rcf);
+                         rcf &= ~TOKEN_RCF_BROADCAST_MASK;
+                         memcpy(rover->tr_rdf, trrif->tr_rdf, sizeof(rover->tr_rdf));
+                 }
+                 rover->tr_rcf = rcf;
+         }
+ 
+         /* insert into list */
+         rover->next = routing_info;
+         routing_info = rover;
+ 
+         return;
+ }
+ 
+ /*
+  * get rid of old routes
+  */
+ static void expire_routes()
+ {
+         struct routing_entry *rover;
+         struct routing_entry **prover = &routing_info;
+         struct timeval now;
+ 
+         gettimeofday(&now, NULL);
+ 
+         while((rover = *prover) != NULL) {
+                 if ((now.tv_sec - rover->access_time) > routing_timeout) {
+                         *prover = rover->next;
+                         dfree (rover, MDL);
+                 } else
+                         prover = &rover->next;
+         }
+ 
+         /* Reset the timer */
+         routing_timer.tv_sec = now.tv_sec + routing_timeout;
+         routing_timer.tv_usec = now.tv_usec;
+ }
+ 
+ #endif
*** /usr/src/usr.sbin/dhcp/includes/cf/netbsd.h.orig	Sun May 28 12:14:35 2000
--- /usr/src/usr.sbin/dhcp/includes/cf/netbsd.h	Fri Jun 30 23:10:36 2000
***************
*** 94,97 ****
--- 94,99 ----
  #define HAVE_MKSTEMP
  
+ #define HAVE_TR_SUPPORT
+ 
  #if defined (USE_DEFAULT_NETWORK)
  #  define USE_BPF
>Release-Note:
>Audit-Trail:
>Unformatted: