xref: /netbsd-src/external/mpl/dhcp/dist/common/tr.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* tr.c
4 
5    token ring interface support
6    Contributed in May of 1999 by Andrew Chittenden */
7 
8 /*
9  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
10  * Copyright (c) 1996-2003 by Internet Software Consortium
11  *
12  * This Source Code Form is subject to the terms of the Mozilla Public
13  * License, v. 2.0. If a copy of the MPL was not distributed with this
14  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
22  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  *   Internet Systems Consortium, Inc.
25  *   PO Box 360
26  *   Newmarket, NH 03857 USA
27  *   <info@isc.org>
28  *   https://www.isc.org/
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: tr.c,v 1.3 2022/04/03 01:10:58 christos Exp $");
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 
50 static int insert_source_routing (struct trh_hdr *trh, struct interface_info* interface);
51 static void save_source_routing (struct trh_hdr *trh, struct interface_info* interface);
52 static void expire_routes (void);
53 
54 /*
55  * As we keep a list of interesting routing information only, a singly
56  * linked list is all we need
57  */
58 struct routing_entry {
59         struct routing_entry *next;
60         unsigned char addr[TR_ALEN];
61         unsigned char iface[5];
62         u_int16_t rcf;			/* route control field */
63         u_int16_t rseg[8];		/* routing registers */
64         unsigned long access_time;	/* time we last used this entry */
65 };
66 
67 static struct routing_entry *routing_info = NULL;
68 
69 static int routing_timeout = 10;
70 static struct timeval routing_timer;
71 
assemble_tr_header(interface,buf,bufix,to)72 void 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 
117 static 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  */
decode_tr_header(interface,buf,bufix,from)125 ssize_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 != *libdhcp_callbacks.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  */
insert_source_routing(trh,interface)194 static 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                                 u_int16_t 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  */
save_source_routing(trh,interface)250 static 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         u_int16_t 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  */
expire_routes()314 static 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