xref: /original-bsd/sys/netiso/clnp_input.c (revision dbe44373)
1 /***********************************************************
2 		Copyright IBM Corporation 1987
3 
4                       All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
13 
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 ******************************************************************/
23 
24 /*
25  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26  */
27 /* $Header: /var/src/sys/netiso/RCS/clnp_input.c,v 5.1 89/02/09 16:20:32 hagens Exp $ */
28 /* $Source: /var/src/sys/netiso/RCS/clnp_input.c,v $ */
29 /*	@(#)clnp_input.c	7.10 (Berkeley) 01/16/90 */
30 
31 #ifndef lint
32 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_input.c,v 5.1 89/02/09 16:20:32 hagens Exp $";
33 #endif lint
34 
35 #include "types.h"
36 #include "param.h"
37 #include "mbuf.h"
38 #include "domain.h"
39 #include "protosw.h"
40 #include "socket.h"
41 #include "socketvar.h"
42 #include "errno.h"
43 #include "time.h"
44 
45 #include "../net/if.h"
46 #include "../net/if_types.h"
47 #include "../net/route.h"
48 
49 #include "iso.h"
50 #include "iso_var.h"
51 #include "iso_snpac.h"
52 #include "clnp.h"
53 #include "clnl.h"
54 #include "esis.h"
55 #include "../netinet/in_systm.h"
56 #include "../netinet/ip.h"
57 #include "../netinet/if_ether.h"
58 #include "eonvar.h"
59 #include "clnp_stat.h"
60 #include "argo_debug.h"
61 
62 #ifdef ISO
63 u_char		clnp_protox[ISOPROTO_MAX];
64 struct clnl_protosw clnl_protox[256];
65 int			clnpqmaxlen = IFQ_MAXLEN;	/* RAH? why is this a variable */
66 struct mbuf	*clnp_data_ck();
67 
68 int	clnp_input();
69 
70 int	esis_input();
71 
72 #ifdef	ISO_X25ESIS
73 int	x25esis_input();
74 #endif	ISO_X25ESIS
75 
76 /*
77  * FUNCTION:		clnp_init
78  *
79  * PURPOSE:			clnp initialization. Fill in clnp switch tables.
80  *
81  * RETURNS:			none
82  *
83  * SIDE EFFECTS:	fills in clnp_protox table with correct offsets into
84  *					the isosw table.
85  *
86  * NOTES:
87  */
88 clnp_init()
89 {
90 	register struct protosw *pr;
91 
92 	/*
93 	 *	CLNP protox initialization
94 	 */
95 	if ((pr = pffindproto(PF_ISO, ISOPROTO_RAW, SOCK_RAW)) == 0)
96 		printf("clnl_init: no raw CLNP\n");
97 	else
98 		clnp_protox[ISOPROTO_RAW] = pr - isosw;
99 
100 	if ((pr = pffindproto(PF_ISO, ISOPROTO_TP, SOCK_SEQPACKET)) == 0)
101 		printf("clnl_init: no tp/clnp\n");
102 	else
103 		clnp_protox[ISOPROTO_TP] = pr - isosw;
104 
105 	/*
106 	 *	CLNL protox initialization
107 	 */
108 	clnl_protox[ISO8473_CLNP].clnl_input = clnp_input;
109 
110 	clnlintrq.ifq_maxlen = clnpqmaxlen;
111 }
112 
113 /*
114  * FUNCTION:		clnlintr
115  *
116  * PURPOSE:			Process a packet on the clnl input queue
117  *
118  * RETURNS:			nothing.
119  *
120  * SIDE EFFECTS:
121  *
122  * NOTES:
123  */
124 clnlintr()
125 {
126 	register struct mbuf		*m;		/* ptr to first mbuf of pkt */
127 	register struct clnl_fixed	*clnl;	/* ptr to fixed part of clnl hdr */
128 	int							s;		/* save and restore priority */
129 	struct clnl_protosw			*clnlsw;/* ptr to protocol switch */
130 	struct snpa_hdr				sh;		/* subnetwork hdr */
131 
132 	/*
133 	 *	Get next datagram off clnl input queue
134 	 */
135 next:
136 	s = splimp();
137 	/* IF_DEQUEUESNPAHDR(&clnlintrq, m, sh);*/
138 	IF_DEQUEUE(&clnlintrq, m);
139 	splx(s);
140 
141 
142 	if (m == 0)		/* nothing to do */
143 		return;
144 	if ((m->m_flags & M_PKTHDR) == 0) {
145 		m_freem(m);
146 		goto next;
147 	}
148 	bzero((caddr_t)&sh, sizeof(sh));
149 	sh.snh_flags = m->m_flags & (M_MCAST|M_BCAST);
150 	switch((sh.snh_ifp = m->m_pkthdr.rcvif)->if_type) {
151 		extern int ether_output();
152 	case IFT_EON:
153 		bcopy(mtod(m, caddr_t), (caddr_t)sh.snh_dhost, sizeof(u_long));
154 		bcopy(sizeof(u_long) + mtod(m, caddr_t),
155 					(caddr_t)sh.snh_shost, sizeof(u_long));
156 		sh.snh_dhost[4] = mtod(m, u_char *)[sizeof(struct ip) +
157 								_offsetof(struct eon_hdr, eonh_class)];
158 		m->m_data += EONIPLEN;
159 		m->m_len -= EONIPLEN;
160 		m->m_pkthdr.len -= EONIPLEN;
161 		break;
162 
163 	default:
164 		if (sh.snh_ifp->if_output == ether_output) {
165 			bcopy((caddr_t)(mtod(m, struct ether_header *)->ether_dhost),
166 				(caddr_t)sh.snh_dhost, 2*sizeof(sh.snh_dhost));
167 			m->m_data += sizeof (struct ether_header);
168 			m->m_len -= sizeof (struct ether_header);
169 			m->m_pkthdr.len -= sizeof (struct ether_header);
170 		}
171 	}
172 	IFDEBUG(D_INPUT)
173 		int i;
174 		printf("clnlintr: src:");
175 		for (i=0; i<6; i++)
176 			printf("%x%c", sh.snh_shost[i] & 0xff, (i<5) ? ':' : ' ');
177 		printf(" dst:");
178 		for (i=0; i<6; i++)
179 			printf("%x%c", sh.snh_dhost[i] & 0xff, (i<5) ? ':' : ' ');
180 		printf("\n");
181 	ENDDEBUG
182 
183 	/*
184 	 *	Get the fixed part of the clnl header into the first mbuf.
185 	 *	Drop the packet if this fails.
186 	 *	Do not call m_pullup if we have a cluster mbuf or the
187 	 *	data is not there.
188 	 */
189 	if ((IS_CLUSTER(m) || (m->m_len < sizeof(struct clnl_fixed))) &&
190 		((m = m_pullup(m, sizeof(struct clnl_fixed))) == 0)) {
191 		INCSTAT(cns_toosmall);	/* TODO: use clnl stats */
192 		goto next;				/* m_pullup discards mbuf */
193 	}
194 
195 	clnl = mtod(m, struct clnl_fixed *);
196 
197 	/*
198 	 *	Drop packet if the length of the header is not reasonable.
199 	 */
200 	if ((clnl->cnf_hdr_len < CLNP_HDR_MIN) ||
201 		(clnl->cnf_hdr_len > CLNP_HDR_MAX)) {
202 		INCSTAT(cns_badhlen);	/* TODO: use clnl stats */
203 		m_freem(m);
204 		goto next;
205 	}
206 
207 	/*
208 	 *	If the header is not contained in this mbuf, make it so.
209 	 *	Drop packet if this fails.
210 	 *	Note: m_pullup will allocate a cluster mbuf if necessary
211 	 */
212 	if (clnl->cnf_hdr_len > m->m_len) {
213 		if ((m = m_pullup(m, (int)clnl->cnf_hdr_len)) == 0) {
214 			INCSTAT(cns_badhlen);	/* TODO: use clnl stats */
215 			goto next;	/* m_pullup discards mbuf */
216 		}
217 		clnl = mtod(m, struct clnl_fixed *);
218 	}
219 
220 	clnlsw = &clnl_protox[clnl->cnf_proto_id];
221 
222 
223 	if (clnlsw->clnl_input)
224 		(*clnlsw->clnl_input) (m, &sh);
225 	else
226 		m_freem(m);
227 
228 	goto next;
229 }
230 
231 /*
232  * FUNCTION:		clnp_input
233  *
234  * PURPOSE:			process an incoming clnp packet
235  *
236  * RETURNS:			nothing
237  *
238  * SIDE EFFECTS:	increments fields of clnp_stat structure.
239  *
240  * NOTES:
241  *	TODO: I would like to make seg_part a pointer into the mbuf, but
242  *	will it be correctly aligned?
243  */
244 clnp_input(m, shp)
245 struct mbuf		*m;		/* ptr to first mbuf of pkt */
246 struct snpa_hdr	*shp;	/* subnetwork header */
247 {
248 	register struct clnp_fixed	*clnp;	/* ptr to fixed part of header */
249 	struct sockaddr_iso			source; /* source address of pkt */
250 	struct sockaddr_iso			target; /* destination address of pkt */
251 #define src	source.siso_addr
252 #define dst	target.siso_addr
253 	caddr_t						hoff;	/* current offset in packet */
254 	caddr_t						hend;	/* address of end of header info */
255 	struct clnp_segment			seg_part; /* segment part of hdr */
256 	int							seg_off=0; /* offset of segment part of hdr */
257 	int							seg_len;/* length of packet data&hdr in bytes */
258 	struct clnp_optidx			oidx, *oidxp = NULL;	/* option index */
259 	extern int 					iso_systype;	/* used by ESIS config resp */
260 	extern struct sockaddr_iso	blank_siso;		/* used for initializing */
261 	int							need_afrin = 0;
262 										/* true if congestion experienced */
263 										/* which means you need afrin nose */
264 										/* spray. How clever! */
265 
266 	IFDEBUG(D_INPUT)
267 		printf(
268 		   "clnp_input: proccessing dg; First mbuf m_len %d, m_type x%x, %s\n",
269 			m->m_len, m->m_type, IS_CLUSTER(m) ? "cluster" : "normal");
270 	ENDDEBUG
271 	need_afrin = 0;
272 
273 	/*
274 	 *	If no iso addresses have been set, there is nothing
275 	 *	to do with the packet.
276 	 */
277 	if (iso_ifaddr == NULL) {
278 		clnp_discard(m, ADDR_DESTUNREACH);
279 		return;
280 	}
281 
282 	INCSTAT(cns_total);
283 	clnp = mtod(m, struct clnp_fixed *);
284 
285 	IFDEBUG(D_DUMPIN)
286 		struct mbuf *mhead;
287 		int			total_len = 0;
288 		printf("clnp_input: clnp header:\n");
289 		dump_buf(mtod(m, caddr_t), clnp->cnf_hdr_len);
290 		printf("clnp_input: mbuf chain:\n");
291 		for (mhead = m; mhead != NULL; mhead=mhead->m_next) {
292 			printf("m x%x, len %d\n", mhead, mhead->m_len);
293 			total_len += mhead->m_len;
294 		}
295 		printf("clnp_input: total length of mbuf chain %d:\n", total_len);
296 	ENDDEBUG
297 
298 	/*
299 	 *	Compute checksum (if necessary) and drop packet if
300 	 *	checksum does not match
301 	 */
302 	if (CKSUM_REQUIRED(clnp) && iso_check_csum(m, (int)clnp->cnf_hdr_len)) {
303 		INCSTAT(cns_badcsum);
304 		clnp_discard(m, GEN_BADCSUM);
305 		return;
306 	}
307 
308 	if (clnp->cnf_vers != ISO8473_V1) {
309 		INCSTAT(cns_badvers);
310 		clnp_discard(m, DISC_UNSUPPVERS);
311 		return;
312 	}
313 
314 
315  	/* check mbuf data length: clnp_data_ck will free mbuf upon error */
316 	CTOH(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, seg_len);
317 	if ((m = clnp_data_ck(m, seg_len)) == 0)
318 		return;
319 
320 	clnp = mtod(m, struct clnp_fixed *);
321 	hend = (caddr_t)clnp + clnp->cnf_hdr_len;
322 
323 	/*
324 	 *	extract the source and destination address
325 	 *	drop packet on failure
326 	 */
327 	source = target = blank_siso;
328 
329 	hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
330 	CLNP_EXTRACT_ADDR(dst, hoff, hend);
331 	if (hoff == (caddr_t)0) {
332 		INCSTAT(cns_badaddr);
333 		clnp_discard(m, GEN_INCOMPLETE);
334 		return;
335 	}
336 	CLNP_EXTRACT_ADDR(src, hoff, hend);
337 	if (hoff == (caddr_t)0) {
338 		INCSTAT(cns_badaddr);
339 		clnp_discard(m, GEN_INCOMPLETE);
340 		return;
341 	}
342 
343 	IFDEBUG(D_INPUT)
344 		printf("clnp_input: from %s", clnp_iso_addrp(&src));
345 		printf(" to %s\n", clnp_iso_addrp(&dst));
346 	ENDDEBUG
347 
348 	/*
349 	 *	extract the segmentation information, if it is present.
350 	 *	drop packet on failure
351 	 */
352 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
353 		(clnp->cnf_type & CNF_SEG_OK)) {
354 		if (hoff + sizeof(struct clnp_segment) > hend) {
355 			INCSTAT(cns_noseg);
356 			clnp_discard(m, GEN_INCOMPLETE);
357 			return;
358 		} else {
359 			(void) bcopy(hoff, (caddr_t)&seg_part, sizeof(struct clnp_segment));
360 			/* make sure segmentation fields are in host order */
361 			seg_part.cng_id = ntohs(seg_part.cng_id);
362 			seg_part.cng_off = ntohs(seg_part.cng_off);
363 			seg_part.cng_tot_len = ntohs(seg_part.cng_tot_len);
364 			seg_off = hoff - (caddr_t)clnp;
365 			hoff += sizeof(struct clnp_segment);
366 		}
367 	}
368 
369 	/*
370 	 *	process options if present. If clnp_opt_sanity returns
371 	 *	false (indicating an error was found in the options) or
372 	 *	an unsupported option was found
373 	 *	then drop packet and emit an ER.
374 	 */
375 	if (hoff < hend) {
376 		int		errcode;
377 
378 		oidxp = &oidx;
379 		errcode = clnp_opt_sanity(m, hoff, hend-hoff, oidxp);
380 
381 		/* we do not support security */
382 		if ((errcode == 0) && (oidxp->cni_securep))
383 			errcode = DISC_UNSUPPSECURE;
384 
385 		/* the er option is valid with ER pdus only */
386 		if ((errcode == 0) && (oidxp->cni_er_reason != ER_INVALREAS) &&
387 			((clnp->cnf_type & CNF_TYPE) != CLNP_ER))
388 			errcode = DISC_UNSUPPOPT;
389 
390 #ifdef	DECBIT
391 		/* check if the congestion experienced bit is set */
392 		if (oidxp->cni_qos_formatp) {
393 			caddr_t	qosp = CLNP_OFFTOOPT(m, oidxp->cni_qos_formatp);
394 			u_char	qos = *qosp;
395 
396 			need_afrin = ((qos & (CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED)) ==
397 				(CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED));
398 			if (need_afrin)
399 				INCSTAT(cns_congest_rcvd);
400 		}
401 #endif	DECBIT
402 
403 		if (errcode != 0) {
404 			clnp_discard(m, (char)errcode);
405 			IFDEBUG(D_INPUT)
406 				printf("clnp_input: dropped (err x%x) due to bad options\n",
407 					errcode);
408 			ENDDEBUG
409 			return;
410 		}
411 	}
412 
413 	/*
414 	 *	check if this packet is for us. if not, then forward
415 	 */
416 	if (clnp_ours(&dst) == 0) {
417 		IFDEBUG(D_INPUT)
418 			printf("clnp_input: forwarding packet not for us\n");
419 		ENDDEBUG
420  		clnp_forward(m, seg_len, &dst, oidxp, seg_off, shp);
421 		return;
422 	}
423 
424 	/*
425 	 *	ESIS Configuration Response Function
426 	 *
427 	 *	If the packet received was sent to the multicast address
428 	 *	all end systems, then send an esh to the source
429 	 */
430 	if ((shp->snh_flags & M_MCAST) && (iso_systype == SNPA_ES)) {
431 		extern short esis_holding_time;
432 
433 		esis_shoutput(shp->snh_ifp, ESIS_ESH, esis_holding_time,
434 			shp->snh_shost, 6);
435 	}
436 
437 	/*
438 	 *	If this is a fragment, then try to reassemble it. If clnp_reass
439 	 *	returns non NULL, the packet has been reassembled, and should
440 	 *	be give to TP. Otherwise the fragment has been delt with
441 	 *	by the reassembly code (either stored or deleted). In either case
442 	 *	we should have nothing more to do with it.
443 	 */
444 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
445 		(clnp->cnf_type & CNF_SEG_OK) &&
446 		(seg_len != seg_part.cng_tot_len)) {
447 		struct mbuf	*m0;
448 
449 		if ((m0 = clnp_reass(m, &src, &dst, &seg_part)) != NULL) {
450 			m = m0;
451 			clnp = mtod(m, struct clnp_fixed *);
452 			INCSTAT(cns_reassembled);
453 		} else {
454 			return;
455 		}
456 	}
457 
458 	/*
459 	 *	give the packet to the higher layer
460 	 *
461 	 *	Note: the total length of packet
462 	 *	is the total length field of the segmentation part,
463 	 *	or, if absent, the segment length field of the
464 	 *	header.
465 	 */
466 	INCSTAT(cns_delivered);
467 	switch (clnp->cnf_type & CNF_TYPE) {
468 	case CLNP_ER:
469 		/*
470 		 *	This ER must have the er option.
471 		 *	If the option is not present, discard datagram.
472 		 */
473 		if (oidxp == NULL || oidxp->cni_er_reason == ER_INVALREAS) {
474 			clnp_discard(m, GEN_HDRSYNTAX);
475 		} else {
476 			clnp_er_input(m, &src, oidxp->cni_er_reason);
477 		}
478 		break;
479 
480 	case CLNP_DT:
481 		(*isosw[clnp_protox[ISOPROTO_TP]].pr_input)(m, &source, &target,
482 			clnp->cnf_hdr_len, need_afrin);
483 		break;
484 
485  	case CLNP_RAW:
486 	case CLNP_ECR:
487 		IFDEBUG(D_INPUT)
488 			printf("clnp_input: raw input of %d bytes\n",
489 				clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len);
490 		ENDDEBUG
491 		(*isosw[clnp_protox[ISOPROTO_RAW]].pr_input)(m, &src, &dst,
492 					clnp->cnf_hdr_len);
493 		break;
494 
495 	case CLNP_EC:
496 		IFDEBUG(D_INPUT)
497 			printf("clnp_input: echoing packet\n");
498 		ENDDEBUG
499 		/*
500 		 *	Switch the source and destination address,
501 		 */
502 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
503 		CLNP_INSERT_ADDR(hoff, src);
504 		CLNP_INSERT_ADDR(hoff, dst);
505 		clnp->cnf_type &= ~CNF_TYPE;
506 		clnp->cnf_type |= CLNP_ECR;
507 
508 		/*
509 		 *	Forward back to sender
510 		 */
511 		clnp_forward(m, (int)
512 			(clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len),
513 			&src, oidxp, seg_off, 0);
514 		break;
515 
516 	default:
517  		printf("clnp_input: unknown clnp pkt type %d\n",
518 			clnp->cnf_type & CNF_TYPE);
519 		clnp_stat.cns_delivered--;
520 		clnp_stat.cns_noproto++;
521 		clnp_discard(m, GEN_HDRSYNTAX);
522  		break;
523 	}
524 }
525 #endif ISO
526