xref: /netbsd/external/mpl/dhcp/dist/common/tr.c (revision 13df4856)
1*13df4856Schristos /*	$NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $	*/
23965be93Schristos 
33965be93Schristos /* tr.c
43965be93Schristos 
53965be93Schristos    token ring interface support
63965be93Schristos    Contributed in May of 1999 by Andrew Chittenden */
73965be93Schristos 
83965be93Schristos /*
9*13df4856Schristos  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
103965be93Schristos  * Copyright (c) 1996-2003 by Internet Software Consortium
113965be93Schristos  *
123965be93Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
133965be93Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
143965be93Schristos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
153965be93Schristos  *
163965be93Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
173965be93Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
183965be93Schristos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
193965be93Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
203965be93Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
213965be93Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
223965be93Schristos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
233965be93Schristos  *
243965be93Schristos  *   Internet Systems Consortium, Inc.
25*13df4856Schristos  *   PO Box 360
26*13df4856Schristos  *   Newmarket, NH 03857 USA
273965be93Schristos  *   <info@isc.org>
283965be93Schristos  *   https://www.isc.org/
293965be93Schristos  */
303965be93Schristos 
313965be93Schristos #include <sys/cdefs.h>
32*13df4856Schristos __RCSID("$NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $");
333965be93Schristos 
343965be93Schristos #include "dhcpd.h"
353965be93Schristos 
363965be93Schristos #if defined (HAVE_TR_SUPPORT) && \
373965be93Schristos 	(defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING))
383965be93Schristos #include "includes/netinet/ip.h"
393965be93Schristos #include "includes/netinet/udp.h"
403965be93Schristos #include "includes/netinet/if_ether.h"
413965be93Schristos #include "netinet/if_tr.h"
423965be93Schristos #include <sys/time.h>
433965be93Schristos 
443965be93Schristos /*
453965be93Schristos  * token ring device handling subroutines.  These are required as token-ring
463965be93Schristos  * does not have a simple on-the-wire header but requires the use of
473965be93Schristos  * source routing
483965be93Schristos  */
493965be93Schristos 
503965be93Schristos static int insert_source_routing (struct trh_hdr *trh, struct interface_info* interface);
513965be93Schristos static void save_source_routing (struct trh_hdr *trh, struct interface_info* interface);
523965be93Schristos static void expire_routes (void);
533965be93Schristos 
543965be93Schristos /*
553965be93Schristos  * As we keep a list of interesting routing information only, a singly
563965be93Schristos  * linked list is all we need
573965be93Schristos  */
583965be93Schristos struct routing_entry {
593965be93Schristos         struct routing_entry *next;
603965be93Schristos         unsigned char addr[TR_ALEN];
613965be93Schristos         unsigned char iface[5];
623965be93Schristos         u_int16_t rcf;			/* route control field */
633965be93Schristos         u_int16_t rseg[8];		/* routing registers */
643965be93Schristos         unsigned long access_time;	/* time we last used this entry */
653965be93Schristos };
663965be93Schristos 
673965be93Schristos static struct routing_entry *routing_info = NULL;
683965be93Schristos 
693965be93Schristos static int routing_timeout = 10;
703965be93Schristos static struct timeval routing_timer;
713965be93Schristos 
assemble_tr_header(interface,buf,bufix,to)723965be93Schristos void assemble_tr_header (interface, buf, bufix, to)
733965be93Schristos 	struct interface_info *interface;
743965be93Schristos 	unsigned char *buf;
753965be93Schristos 	unsigned *bufix;
763965be93Schristos 	struct hardware *to;
773965be93Schristos {
783965be93Schristos         struct trh_hdr *trh;
793965be93Schristos         int hdr_len;
803965be93Schristos         struct trllc *llc;
813965be93Schristos 
823965be93Schristos 
833965be93Schristos         /* set up the token header */
843965be93Schristos         trh = (struct trh_hdr *) &buf[*bufix];
853965be93Schristos         if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr))
863965be93Schristos                 memcpy (trh->saddr, &interface -> hw_address.hbuf [1],
873965be93Schristos                                     sizeof (trh->saddr));
883965be93Schristos         else
893965be93Schristos                 memset (trh->saddr, 0x00, sizeof (trh->saddr));
903965be93Schristos 
913965be93Schristos         if (to && to -> hlen == 7) /* XXX */
923965be93Schristos                 memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr);
933965be93Schristos         else
943965be93Schristos                 memset (trh->daddr, 0xff, sizeof (trh->daddr));
953965be93Schristos 
963965be93Schristos 	hdr_len = insert_source_routing (trh, interface);
973965be93Schristos 
983965be93Schristos         trh->ac = AC;
993965be93Schristos         trh->fc = LLC_FRAME;
1003965be93Schristos 
1013965be93Schristos         /* set up the llc header for snap encoding after the tr header */
1023965be93Schristos         llc = (struct trllc *)(buf + *bufix + hdr_len);
1033965be93Schristos         llc->dsap = EXTENDED_SAP;
1043965be93Schristos         llc->ssap = EXTENDED_SAP;
1053965be93Schristos         llc->llc = UI_CMD;
1063965be93Schristos         llc->protid[0] = 0;
1073965be93Schristos         llc->protid[1] = 0;
1083965be93Schristos         llc->protid[2] = 0;
1093965be93Schristos         llc->ethertype = htons(ETHERTYPE_IP);
1103965be93Schristos 
1113965be93Schristos         hdr_len += sizeof(struct trllc);
1123965be93Schristos 
1133965be93Schristos         *bufix += hdr_len;
1143965be93Schristos }
1153965be93Schristos 
1163965be93Schristos 
1173965be93Schristos static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1183965be93Schristos 
1193965be93Schristos /*
1203965be93Schristos  * decoding the token header is a bit complex as you can see here. It is
1213965be93Schristos  * further complicated by the linux kernel stripping off some valuable
1223965be93Schristos  * information (see comment below) even though we've asked for the raw
1233965be93Schristos  * packets.
1243965be93Schristos  */
decode_tr_header(interface,buf,bufix,from)1253965be93Schristos ssize_t decode_tr_header (interface, buf, bufix, from)
1263965be93Schristos         struct interface_info *interface;
1273965be93Schristos         unsigned char *buf;
1283965be93Schristos         unsigned bufix;
1293965be93Schristos         struct hardware *from;
1303965be93Schristos {
1313965be93Schristos         struct trh_hdr *trh = (struct trh_hdr *) buf + bufix;
1323965be93Schristos         struct trllc *llc;
1333965be93Schristos 	struct ip *ip;
1343965be93Schristos 	struct udphdr *udp;
1353965be93Schristos         unsigned int route_len = 0;
1363965be93Schristos         ssize_t hdr_len;
1373965be93Schristos         struct timeval now;
1383965be93Schristos 
1393965be93Schristos         /* see whether any source routing information has expired */
1403965be93Schristos         gettimeofday(&now, NULL);
1413965be93Schristos 
1423965be93Schristos 	if (routing_timer.tv_sec == 0)
1433965be93Schristos                 routing_timer.tv_sec = now.tv_sec + routing_timeout;
1443965be93Schristos         else if ((now.tv_sec - routing_timer.tv_sec) > 0)
1453965be93Schristos                 expire_routes();
1463965be93Schristos 
1473965be93Schristos         /* the kernel might have stripped off the source
1483965be93Schristos          * routing bit. We try a heuristic to determine whether
1493965be93Schristos          * this is the case and put it back on if so
1503965be93Schristos          */
1513965be93Schristos         route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
1523965be93Schristos         llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len);
1533965be93Schristos         if (llc->dsap == EXTENDED_SAP
1543965be93Schristos                         && llc->ssap == EXTENDED_SAP
1553965be93Schristos                         && llc->llc == UI_CMD
1563965be93Schristos                         && llc->protid[0] == 0
1573965be93Schristos                         && llc->protid[1] == 0
1583965be93Schristos                         && llc->protid[2] == 0) {
1593965be93Schristos                 /* say there is source routing information present */
1603965be93Schristos                 trh->saddr[0] |= TR_RII;
1613965be93Schristos         }
1623965be93Schristos 
1633965be93Schristos 	if (trh->saddr[0] & TR_RII)
1643965be93Schristos                 route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
1653965be93Schristos         else
1663965be93Schristos                 route_len = 0;
1673965be93Schristos 
1683965be93Schristos         hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
1693965be93Schristos 
1703965be93Schristos         /* now filter out unwanted packets: this is based on the packet
1713965be93Schristos          * filter code in bpf.c */
1723965be93Schristos         llc = (struct trllc *)(buf + bufix + hdr_len);
1733965be93Schristos         ip = (struct ip *) (llc + 1);
1743965be93Schristos         udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip));
1753965be93Schristos 
1763965be93Schristos         /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent
1773965be93Schristos          * to our port */
1783965be93Schristos         if (llc->dsap != EXTENDED_SAP
1793965be93Schristos                         || ntohs(llc->ethertype) != ETHERTYPE_IP
1803965be93Schristos                         || ip->ip_p != IPPROTO_UDP
1813965be93Schristos                         || (ntohs (ip->ip_off) & IP_OFFMASK) != 0
18207146da4Schristos                         || udp->uh_dport != *libdhcp_callbacks.local_port)
1833965be93Schristos                 return -1;
1843965be93Schristos 
1853965be93Schristos         /* only save source routing information for packets from valued hosts */
1863965be93Schristos         save_source_routing(trh, interface);
1873965be93Schristos 
1883965be93Schristos         return hdr_len + sizeof (struct trllc);
1893965be93Schristos }
1903965be93Schristos 
1913965be93Schristos /* insert_source_routing inserts source route information into the token ring
1923965be93Schristos  * header
1933965be93Schristos  */
insert_source_routing(trh,interface)1943965be93Schristos static int insert_source_routing (trh, interface)
1953965be93Schristos         struct trh_hdr *trh;
1963965be93Schristos         struct interface_info* interface;
1973965be93Schristos {
1983965be93Schristos 	struct routing_entry *rover;
1993965be93Schristos         struct timeval now;
2003965be93Schristos         unsigned int route_len = 0;
2013965be93Schristos 
2023965be93Schristos         gettimeofday(&now, NULL);
2033965be93Schristos 
2043965be93Schristos 	/* single route broadcasts as per rfc 1042 */
2053965be93Schristos 	if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) {
2063965be93Schristos 		trh->saddr[0] |= TR_RII;
2073965be93Schristos 		trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
2083965be93Schristos                 trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
2093965be93Schristos 		trh->rcf = htons(trh->rcf);
2103965be93Schristos 	} else {
2113965be93Schristos 		/* look for a routing entry */
2123965be93Schristos                 for (rover = routing_info; rover != NULL; rover = rover->next) {
2133965be93Schristos                         if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0)
2143965be93Schristos                                 break;
2153965be93Schristos                 }
2163965be93Schristos 
2173965be93Schristos 		if (rover != NULL) {
2183965be93Schristos                         /* success: route that frame */
2193965be93Schristos                         if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) {
2203965be93Schristos                                 u_int16_t rcf = rover->rcf;
2213965be93Schristos 				memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg));
2223965be93Schristos 				rcf ^= TR_RCF_DIR_BIT;
2233965be93Schristos 				rcf &= ~TR_RCF_BROADCAST_MASK;
2243965be93Schristos                                 trh->rcf = htons(rcf);
2253965be93Schristos 				trh->saddr[0] |= TR_RII;
2263965be93Schristos 			}
2273965be93Schristos 			rover->access_time = now.tv_sec;
2283965be93Schristos 		} else {
2293965be93Schristos                         /* we don't have any routing information so send a
2303965be93Schristos                          * limited broadcast */
2313965be93Schristos                         trh->saddr[0] |= TR_RII;
2323965be93Schristos                         trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
2333965be93Schristos                         trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
2343965be93Schristos                         trh->rcf = htons(trh->rcf);
2353965be93Schristos 		}
2363965be93Schristos 	}
2373965be93Schristos 
2383965be93Schristos 	/* return how much of the header we've actually used */
2393965be93Schristos 	if (trh->saddr[0] & TR_RII)
2403965be93Schristos                 route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
2413965be93Schristos         else
2423965be93Schristos                 route_len = 0;
2433965be93Schristos 
2443965be93Schristos         return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
2453965be93Schristos }
2463965be93Schristos 
2473965be93Schristos /*
2483965be93Schristos  * save any source routing information
2493965be93Schristos  */
save_source_routing(trh,interface)2503965be93Schristos static void save_source_routing(trh, interface)
2513965be93Schristos         struct trh_hdr *trh;
2523965be93Schristos         struct interface_info *interface;
2533965be93Schristos {
2543965be93Schristos         struct routing_entry *rover;
2553965be93Schristos         struct timeval now;
2563965be93Schristos         unsigned char saddr[TR_ALEN];
2573965be93Schristos         u_int16_t rcf = 0;
2583965be93Schristos 
2593965be93Schristos         gettimeofday(&now, NULL);
2603965be93Schristos 
2613965be93Schristos         memcpy(saddr, trh->saddr, sizeof(saddr));
2623965be93Schristos         saddr[0] &= 0x7f;   /* strip off source routing present flag */
2633965be93Schristos 
2643965be93Schristos         /* scan our table to see if we've got it */
2653965be93Schristos         for (rover = routing_info; rover != NULL; rover = rover->next) {
2663965be93Schristos                 if (memcmp(&rover->addr[0], &saddr[0], TR_ALEN) == 0)
2673965be93Schristos                         break;
2683965be93Schristos         }
2693965be93Schristos 
2703965be93Schristos         /* found an entry so update it with fresh information */
2713965be93Schristos         if (rover != NULL) {
2723965be93Schristos                 if ((trh->saddr[0] & TR_RII) &&
2733965be93Schristos 		    ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
2743965be93Schristos                         rcf = ntohs(trh->rcf);
2753965be93Schristos                         rcf &= ~TR_RCF_BROADCAST_MASK;
2763965be93Schristos                         memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
2773965be93Schristos                 }
2783965be93Schristos                 rover->rcf = rcf;
2793965be93Schristos                 rover->access_time = now.tv_sec;
2803965be93Schristos                 return;     /* that's all folks */
2813965be93Schristos         }
2823965be93Schristos 
2833965be93Schristos         /* no entry found, so create one */
2843965be93Schristos         rover = dmalloc (sizeof (struct routing_entry), MDL);
2853965be93Schristos         if (rover == NULL) {
2863965be93Schristos                 fprintf(stderr,
2873965be93Schristos 			"%s: unable to save source routing information\n",
2883965be93Schristos 			__FILE__);
2893965be93Schristos                 return;
2903965be93Schristos         }
2913965be93Schristos 
2923965be93Schristos         memcpy(rover->addr, saddr, sizeof(rover->addr));
2933965be93Schristos         memcpy(rover->iface, interface->name, 5);
2943965be93Schristos         rover->access_time = now.tv_sec;
2953965be93Schristos         if (trh->saddr[0] & TR_RII) {
2963965be93Schristos                 if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
2973965be93Schristos                         rcf = ntohs(trh->rcf);
2983965be93Schristos                         rcf &= ~TR_RCF_BROADCAST_MASK;
2993965be93Schristos                         memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
3003965be93Schristos                 }
3013965be93Schristos                 rover->rcf = rcf;
3023965be93Schristos         }
3033965be93Schristos 
3043965be93Schristos         /* insert into list */
3053965be93Schristos         rover->next = routing_info;
3063965be93Schristos         routing_info = rover;
3073965be93Schristos 
3083965be93Schristos         return;
3093965be93Schristos }
3103965be93Schristos 
3113965be93Schristos /*
3123965be93Schristos  * get rid of old routes
3133965be93Schristos  */
expire_routes()3143965be93Schristos static void expire_routes()
3153965be93Schristos {
3163965be93Schristos         struct routing_entry *rover;
3173965be93Schristos         struct routing_entry **prover = &routing_info;
3183965be93Schristos         struct timeval now;
3193965be93Schristos 
3203965be93Schristos         gettimeofday(&now, NULL);
3213965be93Schristos 
3223965be93Schristos         while((rover = *prover) != NULL) {
3233965be93Schristos                 if ((now.tv_sec - rover->access_time) > routing_timeout) {
3243965be93Schristos                         *prover = rover->next;
3253965be93Schristos                         dfree (rover, MDL);
3263965be93Schristos                 } else
3273965be93Schristos                         prover = &rover->next;
3283965be93Schristos         }
3293965be93Schristos 
3303965be93Schristos         /* Reset the timer */
3313965be93Schristos         routing_timer.tv_sec = now.tv_sec + routing_timeout;
3323965be93Schristos         routing_timer.tv_usec = now.tv_usec;
3333965be93Schristos }
3343965be93Schristos 
3353965be93Schristos #endif
336