1/* tr.c
2
3   token ring interface support
4   Contributed in May of 1999 by Andrew Chittenden */
5
6/*
7 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1996-2003 by Internet Software Consortium
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 *   Internet Systems Consortium, Inc.
23 *   950 Charter Street
24 *   Redwood City, CA 94063
25 *   <info@isc.org>
26 *   http://www.isc.org/
27 */
28
29#ifndef lint
30static char copyright[] =
31"$Id: tr.c,v 1.3 2005/08/11 17:13:21 drochner Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
32#endif /* not lint */
33
34#include "dhcpd.h"
35
36#if defined (HAVE_TR_SUPPORT) && \
37	(defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING))
38#include "includes/netinet/ip.h"
39#include "includes/netinet/udp.h"
40#include "includes/netinet/if_ether.h"
41#include "netinet/if_tr.h"
42#include <sys/time.h>
43
44/*
45 * token ring device handling subroutines.  These are required as token-ring
46 * does not have a simple on-the-wire header but requires the use of
47 * source routing
48 */
49
50static int insert_source_routing PROTO ((struct trh_hdr *trh, struct interface_info* interface));
51static void save_source_routing PROTO ((struct trh_hdr *trh, struct interface_info* interface));
52static void expire_routes PROTO ((void));
53
54/*
55 * As we keep a list of interesting routing information only, a singly
56 * linked list is all we need
57 */
58struct routing_entry {
59        struct routing_entry *next;
60        unsigned char addr[TR_ALEN];
61        unsigned char iface[5];
62        __u16 rcf;                      /* route control field */
63        __u16 rseg[8];                  /* routing registers */
64        unsigned long access_time;      /* time we last used this entry */
65};
66
67static struct routing_entry *routing_info = NULL;
68
69static int routing_timeout = 10;
70static struct timeval routing_timer;
71
72void assemble_tr_header (interface, buf, bufix, to)
73	struct interface_info *interface;
74	unsigned char *buf;
75	unsigned *bufix;
76	struct hardware *to;
77{
78        struct trh_hdr *trh;
79        int hdr_len;
80        struct trllc *llc;
81
82
83        /* set up the token header */
84        trh = (struct trh_hdr *) &buf[*bufix];
85        if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr))
86                memcpy (trh->saddr, &interface -> hw_address.hbuf [1],
87                                    sizeof (trh->saddr));
88        else
89                memset (trh->saddr, 0x00, sizeof (trh->saddr));
90
91        if (to && to -> hlen == 7) /* XXX */
92                memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr);
93        else
94                memset (trh->daddr, 0xff, sizeof (trh->daddr));
95
96	hdr_len = insert_source_routing (trh, interface);
97
98        trh->ac = AC;
99        trh->fc = LLC_FRAME;
100
101        /* set up the llc header for snap encoding after the tr header */
102        llc = (struct trllc *)(buf + *bufix + hdr_len);
103        llc->dsap = EXTENDED_SAP;
104        llc->ssap = EXTENDED_SAP;
105        llc->llc = UI_CMD;
106        llc->protid[0] = 0;
107        llc->protid[1] = 0;
108        llc->protid[2] = 0;
109        llc->ethertype = htons(ETHERTYPE_IP);
110
111        hdr_len += sizeof(struct trllc);
112
113        *bufix += hdr_len;
114}
115
116
117static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
118
119/*
120 * decoding the token header is a bit complex as you can see here. It is
121 * further complicated by the linux kernel stripping off some valuable
122 * information (see comment below) even though we've asked for the raw
123 * packets.
124 */
125ssize_t decode_tr_header (interface, buf, bufix, from)
126        struct interface_info *interface;
127        unsigned char *buf;
128        unsigned bufix;
129        struct hardware *from;
130{
131        struct trh_hdr *trh = (struct trh_hdr *) buf + bufix;
132        struct trllc *llc;
133	struct ip *ip;
134	struct udphdr *udp;
135        unsigned int route_len = 0;
136        ssize_t hdr_len;
137        struct timeval now;
138
139        /* see whether any source routing information has expired */
140        gettimeofday(&now, NULL);
141
142	if (routing_timer.tv_sec == 0)
143                routing_timer.tv_sec = now.tv_sec + routing_timeout;
144        else if ((now.tv_sec - routing_timer.tv_sec) > 0)
145                expire_routes();
146
147        /* the kernel might have stripped off the source
148         * routing bit. We try a heuristic to determine whether
149         * this is the case and put it back on if so
150         */
151        route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
152        llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len);
153        if (llc->dsap == EXTENDED_SAP
154                        && llc->ssap == EXTENDED_SAP
155                        && llc->llc == UI_CMD
156                        && llc->protid[0] == 0
157                        && llc->protid[1] == 0
158                        && llc->protid[2] == 0) {
159                /* say there is source routing information present */
160                trh->saddr[0] |= TR_RII;
161        }
162
163	if (trh->saddr[0] & TR_RII)
164                route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
165        else
166                route_len = 0;
167
168        hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
169
170        /* now filter out unwanted packets: this is based on the packet
171         * filter code in bpf.c */
172        llc = (struct trllc *)(buf + bufix + hdr_len);
173        ip = (struct ip *) (llc + 1);
174        udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip));
175
176        /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent
177         * to our port */
178        if (llc->dsap != EXTENDED_SAP
179                        || ntohs(llc->ethertype) != ETHERTYPE_IP
180                        || ip->ip_p != IPPROTO_UDP
181                        || (ntohs (ip->ip_off) & IP_OFFMASK) != 0
182                        || udp->uh_dport != local_port)
183                return -1;
184
185        /* only save source routing information for packets from valued hosts */
186        save_source_routing(trh, interface);
187
188        return hdr_len + sizeof (struct trllc);
189}
190
191/* insert_source_routing inserts source route information into the token ring
192 * header
193 */
194static int insert_source_routing (trh, interface)
195        struct trh_hdr *trh;
196        struct interface_info* interface;
197{
198	struct routing_entry *rover;
199        struct timeval now;
200        unsigned int route_len = 0;
201
202        gettimeofday(&now, NULL);
203
204	/* single route broadcasts as per rfc 1042 */
205	if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) {
206		trh->saddr[0] |= TR_RII;
207		trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
208                trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
209		trh->rcf = htons(trh->rcf);
210	} else {
211		/* look for a routing entry */
212                for (rover = routing_info; rover != NULL; rover = rover->next) {
213                        if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0)
214                                break;
215                }
216
217		if (rover != NULL) {
218                        /* success: route that frame */
219                        if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) {
220                                __u16 rcf = rover->rcf;
221				memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg));
222				rcf ^= TR_RCF_DIR_BIT;
223				rcf &= ~TR_RCF_BROADCAST_MASK;
224                                trh->rcf = htons(rcf);
225				trh->saddr[0] |= TR_RII;
226			}
227			rover->access_time = now.tv_sec;
228		} else {
229                        /* we don't have any routing information so send a
230                         * limited broadcast */
231                        trh->saddr[0] |= TR_RII;
232                        trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
233                        trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
234                        trh->rcf = htons(trh->rcf);
235		}
236	}
237
238	/* return how much of the header we've actually used */
239	if (trh->saddr[0] & TR_RII)
240                route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
241        else
242                route_len = 0;
243
244        return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
245}
246
247/*
248 * save any source routing information
249 */
250static void save_source_routing(trh, interface)
251        struct trh_hdr *trh;
252        struct interface_info *interface;
253{
254        struct routing_entry *rover;
255        struct timeval now;
256        unsigned char saddr[TR_ALEN];
257        __u16 rcf = 0;
258
259        gettimeofday(&now, NULL);
260
261        memcpy(saddr, trh->saddr, sizeof(saddr));
262        saddr[0] &= 0x7f;   /* strip off source routing present flag */
263
264        /* scan our table to see if we've got it */
265        for (rover = routing_info; rover != NULL; rover = rover->next) {
266                if (memcmp(&rover->addr[0], &saddr[0], TR_ALEN) == 0)
267                        break;
268        }
269
270        /* found an entry so update it with fresh information */
271        if (rover != NULL) {
272                if ((trh->saddr[0] & TR_RII) &&
273		    ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
274                        rcf = ntohs(trh->rcf);
275                        rcf &= ~TR_RCF_BROADCAST_MASK;
276                        memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
277                }
278                rover->rcf = rcf;
279                rover->access_time = now.tv_sec;
280                return;     /* that's all folks */
281        }
282
283        /* no entry found, so create one */
284        rover = dmalloc (sizeof (struct routing_entry), MDL);
285        if (rover == NULL) {
286                fprintf(stderr,
287			"%s: unable to save source routing information\n",
288			__FILE__);
289                return;
290        }
291
292        memcpy(rover->addr, saddr, sizeof(rover->addr));
293        memcpy(rover->iface, interface->name, 5);
294        rover->access_time = now.tv_sec;
295        if (trh->saddr[0] & TR_RII) {
296                if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
297                        rcf = ntohs(trh->rcf);
298                        rcf &= ~TR_RCF_BROADCAST_MASK;
299                        memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
300                }
301                rover->rcf = rcf;
302        }
303
304        /* insert into list */
305        rover->next = routing_info;
306        routing_info = rover;
307
308        return;
309}
310
311/*
312 * get rid of old routes
313 */
314static void expire_routes()
315{
316        struct routing_entry *rover;
317        struct routing_entry **prover = &routing_info;
318        struct timeval now;
319
320        gettimeofday(&now, NULL);
321
322        while((rover = *prover) != NULL) {
323                if ((now.tv_sec - rover->access_time) > routing_timeout) {
324                        *prover = rover->next;
325                        dfree (rover, MDL);
326                } else
327                        prover = &rover->next;
328        }
329
330        /* Reset the timer */
331        routing_timer.tv_sec = now.tv_sec + routing_timeout;
332        routing_timer.tv_usec = now.tv_usec;
333}
334
335#endif
336