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