/* $NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $ */
/* tr.c
token ring interface support
Contributed in May of 1999 by Andrew Chittenden */
/*
* Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
* PO Box 360
* Newmarket, NH 03857 USA
* <info@isc.org>
* https://www.isc.org/
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $");
#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 "netinet/if_tr.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 (struct trh_hdr *trh, struct interface_info* interface);
static void save_source_routing (struct trh_hdr *trh, struct interface_info* interface);
static void expire_routes (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[TR_ALEN];
unsigned char iface[5];
u_int16_t rcf; /* route control field */
u_int16_t rseg[8]; /* 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 trh_hdr *trh;
int hdr_len;
struct trllc *llc;
/* set up the token header */
trh = (struct trh_hdr *) &buf[*bufix];
if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr))
memcpy (trh->saddr, &interface -> hw_address.hbuf [1],
sizeof (trh->saddr));
else
memset (trh->saddr, 0x00, sizeof (trh->saddr));
if (to && to -> hlen == 7) /* XXX */
memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr);
else
memset (trh->daddr, 0xff, sizeof (trh->daddr));
hdr_len = insert_source_routing (trh, interface);
trh->ac = AC;
trh->fc = LLC_FRAME;
/* set up the llc header for snap encoding after the tr header */
llc = (struct trllc *)(buf + *bufix + hdr_len);
llc->dsap = EXTENDED_SAP;
llc->ssap = EXTENDED_SAP;
llc->llc = UI_CMD;
llc->protid[0] = 0;
llc->protid[1] = 0;
llc->protid[2] = 0;
llc->ethertype = htons(ETHERTYPE_IP);
hdr_len += sizeof(struct trllc);
*bufix += hdr_len;
}
static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/*
* decoding the token header is a bit complex as you can see here. It is
* further complicated by the linux kernel stripping off some valuable
* information (see comment below) even though we've asked for the raw
* packets.
*/
ssize_t decode_tr_header (interface, buf, bufix, from)
struct interface_info *interface;
unsigned char *buf;
unsigned bufix;
struct hardware *from;
{
struct trh_hdr *trh = (struct trh_hdr *) buf + bufix;
struct trllc *llc;
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();
/* the kernel might have stripped off the source
* routing bit. We try a heuristic to determine whether
* this is the case and put it back on if so
*/
route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len);
if (llc->dsap == EXTENDED_SAP
&& llc->ssap == EXTENDED_SAP
&& llc->llc == UI_CMD
&& llc->protid[0] == 0
&& llc->protid[1] == 0
&& llc->protid[2] == 0) {
/* say there is source routing information present */
trh->saddr[0] |= TR_RII;
}
if (trh->saddr[0] & TR_RII)
route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
else
route_len = 0;
hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
/* now filter out unwanted packets: this is based on the packet
* filter code in bpf.c */
llc = (struct trllc *)(buf + bufix + hdr_len);
ip = (struct ip *) (llc + 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 (llc->dsap != EXTENDED_SAP
|| ntohs(llc->ethertype) != ETHERTYPE_IP
|| ip->ip_p != IPPROTO_UDP
|| (ntohs (ip->ip_off) & IP_OFFMASK) != 0
|| udp->uh_dport != *libdhcp_callbacks.local_port)
return -1;
/* only save source routing information for packets from valued hosts */
save_source_routing(trh, interface);
return hdr_len + sizeof (struct trllc);
}
/* insert_source_routing inserts source route information into the token ring
* header
*/
static int insert_source_routing (trh, interface)
struct trh_hdr *trh;
struct interface_info* interface;
{
struct routing_entry *rover;
struct timeval now;
unsigned int route_len = 0;
gettimeofday(&now, NULL);
/* single route broadcasts as per rfc 1042 */
if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) {
trh->saddr[0] |= TR_RII;
trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
trh->rcf = htons(trh->rcf);
} else {
/* look for a routing entry */
for (rover = routing_info; rover != NULL; rover = rover->next) {
if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0)
break;
}
if (rover != NULL) {
/* success: route that frame */
if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) {
u_int16_t rcf = rover->rcf;
memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg));
rcf ^= TR_RCF_DIR_BIT;
rcf &= ~TR_RCF_BROADCAST_MASK;
trh->rcf = htons(rcf);
trh->saddr[0] |= TR_RII;
}
rover->access_time = now.tv_sec;
} else {
/* we don't have any routing information so send a
* limited broadcast */
trh->saddr[0] |= TR_RII;
trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
trh->rcf = htons(trh->rcf);
}
}
/* return how much of the header we've actually used */
if (trh->saddr[0] & TR_RII)
route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
else
route_len = 0;
return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
}
/*
* save any source routing information
*/
static void save_source_routing(trh, interface)
struct trh_hdr *trh;
struct interface_info *interface;
{
struct routing_entry *rover;
struct timeval now;
unsigned char saddr[TR_ALEN];
u_int16_t rcf = 0;
gettimeofday(&now, NULL);
memcpy(saddr, trh->saddr, 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], TR_ALEN) == 0)
break;
}
/* found an entry so update it with fresh information */
if (rover != NULL) {
if ((trh->saddr[0] & TR_RII) &&
((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
rcf = ntohs(trh->rcf);
rcf &= ~TR_RCF_BROADCAST_MASK;
memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
}
rover->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->saddr[0] & TR_RII) {
if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
rcf = ntohs(trh->rcf);
rcf &= ~TR_RCF_BROADCAST_MASK;
memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
}
rover->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