xref: /csrg-svn/sys/netiso/clnp_input.c (revision 39232)
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.8 (Berkeley) 09/26/89 */
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 iso_addr				src;	/* source address of pkt */
250 	struct iso_addr				dst;	/* destination address of pkt */
251 	caddr_t						hoff;	/* current offset in packet */
252 	caddr_t						hend;	/* address of end of header info */
253 	struct clnp_segment			seg_part; /* segment part of hdr */
254 	int							seg_off=0; /* offset of segment part of hdr */
255 	int							seg_len;/* length of packet data&hdr in bytes */
256 	struct clnp_optidx			oidx, *oidxp = NULL;	/* option index */
257 	extern int 					iso_systype;	/* used by ESIS config resp */
258 	int							need_afrin = 0;
259 										/* true if congestion experienced */
260 										/* which means you need afrin nose */
261 										/* spray. How clever! */
262 
263 	IFDEBUG(D_INPUT)
264 		printf(
265 		   "clnp_input: proccessing dg; First mbuf m_len %d, m_type x%x, %s\n",
266 			m->m_len, m->m_type, IS_CLUSTER(m) ? "cluster" : "normal");
267 	ENDDEBUG
268 	need_afrin = 0;
269 
270 	/*
271 	 *	If no iso addresses have been set, there is nothing
272 	 *	to do with the packet.
273 	 */
274 	if (iso_ifaddr == NULL) {
275 		clnp_discard(m, ADDR_DESTUNREACH);
276 		return;
277 	}
278 
279 	INCSTAT(cns_total);
280 	clnp = mtod(m, struct clnp_fixed *);
281 
282 	IFDEBUG(D_DUMPIN)
283 		struct mbuf *mhead;
284 		int			total_len = 0;
285 		printf("clnp_input: clnp header:\n");
286 		dump_buf(mtod(m, caddr_t), clnp->cnf_hdr_len);
287 		printf("clnp_input: mbuf chain:\n");
288 		for (mhead = m; mhead != NULL; mhead=mhead->m_next) {
289 			printf("m x%x, len %d\n", mhead, mhead->m_len);
290 			total_len += mhead->m_len;
291 		}
292 		printf("clnp_input: total length of mbuf chain %d:\n", total_len);
293 	ENDDEBUG
294 
295 	/*
296 	 *	Compute checksum (if necessary) and drop packet if
297 	 *	checksum does not match
298 	 */
299 	if (CKSUM_REQUIRED(clnp) && iso_check_csum(m, (int)clnp->cnf_hdr_len)) {
300 		INCSTAT(cns_badcsum);
301 		clnp_discard(m, GEN_BADCSUM);
302 		return;
303 	}
304 
305 	if (clnp->cnf_vers != ISO8473_V1) {
306 		INCSTAT(cns_badvers);
307 		clnp_discard(m, DISC_UNSUPPVERS);
308 		return;
309 	}
310 
311 
312  	/* check mbuf data length: clnp_data_ck will free mbuf upon error */
313 	CTOH(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, seg_len);
314 	if ((m = clnp_data_ck(m, seg_len)) == 0)
315 		return;
316 
317 	clnp = mtod(m, struct clnp_fixed *);
318 	hend = (caddr_t)clnp + clnp->cnf_hdr_len;
319 
320 	/*
321 	 *	extract the source and destination address
322 	 *	drop packet on failure
323 	 */
324 	bzero((caddr_t)&src, sizeof(src));
325 	bzero((caddr_t)&dst, sizeof(dst));
326 
327 	hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
328 	CLNP_EXTRACT_ADDR(dst, hoff, hend);
329 	if (hoff == (caddr_t)0) {
330 		INCSTAT(cns_badaddr);
331 		clnp_discard(m, GEN_INCOMPLETE);
332 		return;
333 	}
334 	CLNP_EXTRACT_ADDR(src, hoff, hend);
335 	if (hoff == (caddr_t)0) {
336 		INCSTAT(cns_badaddr);
337 		clnp_discard(m, GEN_INCOMPLETE);
338 		return;
339 	}
340 
341 	IFDEBUG(D_INPUT)
342 		printf("clnp_input: from %s", clnp_iso_addrp(&src));
343 		printf(" to %s\n", clnp_iso_addrp(&dst));
344 	ENDDEBUG
345 
346 	/*
347 	 *	extract the segmentation information, if it is present.
348 	 *	drop packet on failure
349 	 */
350 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
351 		(clnp->cnf_type & CNF_SEG_OK)) {
352 		if (hoff + sizeof(struct clnp_segment) > hend) {
353 			INCSTAT(cns_noseg);
354 			clnp_discard(m, GEN_INCOMPLETE);
355 			return;
356 		} else {
357 			(void) bcopy(hoff, (caddr_t)&seg_part, sizeof(struct clnp_segment));
358 			/* make sure segmentation fields are in host order */
359 			seg_part.cng_id = ntohs(seg_part.cng_id);
360 			seg_part.cng_off = ntohs(seg_part.cng_off);
361 			seg_part.cng_tot_len = ntohs(seg_part.cng_tot_len);
362 			seg_off = hoff - (caddr_t)clnp;
363 			hoff += sizeof(struct clnp_segment);
364 		}
365 	}
366 
367 	/*
368 	 *	process options if present. If clnp_opt_sanity returns
369 	 *	false (indicating an error was found in the options) or
370 	 *	an unsupported option was found
371 	 *	then drop packet and emit an ER.
372 	 */
373 	if (hoff < hend) {
374 		int		errcode;
375 
376 		oidxp = &oidx;
377 		errcode = clnp_opt_sanity(m, hoff, hend-hoff, oidxp);
378 
379 		/* we do not support security */
380 		if ((errcode == 0) && (oidxp->cni_securep))
381 			errcode = DISC_UNSUPPSECURE;
382 
383 		/* the er option is valid with ER pdus only */
384 		if ((errcode == 0) && (oidxp->cni_er_reason != ER_INVALREAS) &&
385 			((clnp->cnf_type & CNF_TYPE) != CLNP_ER))
386 			errcode = DISC_UNSUPPOPT;
387 
388 #ifdef	DECBIT
389 		/* check if the congestion experienced bit is set */
390 		if (oidxp->cni_qos_formatp) {
391 			caddr_t	qosp = CLNP_OFFTOOPT(m, oidxp->cni_qos_formatp);
392 			u_char	qos = *qosp;
393 
394 			need_afrin = ((qos & (CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED)) ==
395 				(CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED));
396 			if (need_afrin)
397 				INCSTAT(cns_congest_rcvd);
398 		}
399 #endif	DECBIT
400 
401 		if (errcode != 0) {
402 			clnp_discard(m, (char)errcode);
403 			IFDEBUG(D_INPUT)
404 				printf("clnp_input: dropped (err x%x) due to bad options\n",
405 					errcode);
406 			ENDDEBUG
407 			return;
408 		}
409 	}
410 
411 	/*
412 	 *	check if this packet is for us. if not, then forward
413 	 */
414 	if (clnp_ours(&dst) == 0) {
415 		IFDEBUG(D_INPUT)
416 			printf("clnp_input: forwarding packet not for us\n");
417 		ENDDEBUG
418  		clnp_forward(m, seg_len, &dst, oidxp, seg_off, shp);
419 		return;
420 	}
421 
422 	/*
423 	 *	ESIS Configuration Response Function
424 	 *
425 	 *	If the packet received was sent to the multicast address
426 	 *	all end systems, then send an esh to the source
427 	 */
428 	if ((shp->snh_flags & M_MCAST) && (iso_systype == SNPA_ES)) {
429 		extern short esis_holding_time;
430 
431 		esis_shoutput(shp->snh_ifp, ESIS_ESH, esis_holding_time,
432 			shp->snh_shost, 6);
433 	}
434 
435 	/*
436 	 *	If this is a fragment, then try to reassemble it. If clnp_reass
437 	 *	returns non NULL, the packet has been reassembled, and should
438 	 *	be give to TP. Otherwise the fragment has been delt with
439 	 *	by the reassembly code (either stored or deleted). In either case
440 	 *	we should have nothing more to do with it.
441 	 */
442 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
443 		(clnp->cnf_type & CNF_SEG_OK) &&
444 		(seg_len != seg_part.cng_tot_len)) {
445 		struct mbuf	*m0;
446 
447 		if ((m0 = clnp_reass(m, &src, &dst, &seg_part)) != NULL) {
448 			m = m0;
449 			clnp = mtod(m, struct clnp_fixed *);
450 			INCSTAT(cns_reassembled);
451 		} else {
452 			return;
453 		}
454 	}
455 
456 	/*
457 	 *	give the packet to the higher layer
458 	 *
459 	 *	Note: the total length of packet
460 	 *	is the total length field of the segmentation part,
461 	 *	or, if absent, the segment length field of the
462 	 *	header.
463 	 */
464 	INCSTAT(cns_delivered);
465 	switch (clnp->cnf_type & CNF_TYPE) {
466 	case CLNP_ER:
467 		/*
468 		 *	This ER must have the er option.
469 		 *	If the option is not present, discard datagram.
470 		 */
471 		if (oidxp == NULL || oidxp->cni_er_reason == ER_INVALREAS) {
472 			clnp_discard(m, GEN_HDRSYNTAX);
473 		} else {
474 			clnp_er_input(m, &src, oidxp->cni_er_reason);
475 		}
476 		break;
477 
478 	case CLNP_DT:
479  		if (need_afrin) {
480  			/* NOTE: do this before TP gets the packet so tp ack can use info*/
481  			IFDEBUG(D_INPUT)
482  				printf("clnp_input: Calling tpclnp_ctlinput(%s)\n",
483  					clnp_iso_addrp(&src));
484  			ENDDEBUG
485  			tpclnp_ctlinput1(PRC_QUENCH2, &src);
486  		}
487 		(*isosw[clnp_protox[ISOPROTO_TP]].pr_input)(m, &src, &dst,
488 			clnp->cnf_hdr_len);
489 		break;
490 
491  	case CLNP_RAW:
492 	case CLNP_ECR:
493 		IFDEBUG(D_INPUT)
494 			printf("clnp_input: raw input of %d bytes\n",
495 				clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len);
496 		ENDDEBUG
497 		(*isosw[clnp_protox[ISOPROTO_RAW]].pr_input)(m, &src, &dst,
498 					clnp->cnf_hdr_len);
499 		break;
500 
501 	case CLNP_EC:
502 		IFDEBUG(D_INPUT)
503 			printf("clnp_input: echoing packet\n");
504 		ENDDEBUG
505 		/*
506 		 *	Switch the source and destination address,
507 		 */
508 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
509 		CLNP_INSERT_ADDR(hoff, src);
510 		CLNP_INSERT_ADDR(hoff, dst);
511 		clnp->cnf_type &= ~CNF_TYPE;
512 		clnp->cnf_type |= CLNP_ECR;
513 
514 		/*
515 		 *	Forward back to sender
516 		 */
517 		clnp_forward(m, (int)
518 			(clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len),
519 			&src, oidxp, seg_off, 0);
520 		break;
521 
522 	default:
523  		printf("clnp_input: unknown clnp pkt type %d\n",
524 			clnp->cnf_type & CNF_TYPE);
525 		clnp_stat.cns_delivered--;
526 		clnp_stat.cns_noproto++;
527 		clnp_discard(m, GEN_HDRSYNTAX);
528  		break;
529 	}
530 }
531 #endif ISO
532