xref: /original-bsd/sys/netiso/clnp_input.c (revision f43fc9d7)
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.12 (Berkeley) 06/20/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 || m->m_pkthdr.rcvif == 0) {
145 		m_freem(m);
146 		goto next;
147 	} else {
148 		register struct ifaddr *ifa;
149 		for (ifa = m->m_pkthdr.rcvif->if_addrlist; ifa; ifa = ifa->ifa_next)
150 			if (ifa->ifa_addr->sa_family == AF_ISO)
151 				break;
152 		if (ifa == 0) {
153 			m_freem(m);
154 			goto next;
155 		}
156 	}
157 	bzero((caddr_t)&sh, sizeof(sh));
158 	sh.snh_flags = m->m_flags & (M_MCAST|M_BCAST);
159 	switch((sh.snh_ifp = m->m_pkthdr.rcvif)->if_type) {
160 		extern int ether_output();
161 	case IFT_EON:
162 		bcopy(mtod(m, caddr_t), (caddr_t)sh.snh_dhost, sizeof(u_long));
163 		bcopy(sizeof(u_long) + mtod(m, caddr_t),
164 					(caddr_t)sh.snh_shost, sizeof(u_long));
165 		sh.snh_dhost[4] = mtod(m, u_char *)[sizeof(struct ip) +
166 								_offsetof(struct eon_hdr, eonh_class)];
167 		m->m_data += EONIPLEN;
168 		m->m_len -= EONIPLEN;
169 		m->m_pkthdr.len -= EONIPLEN;
170 		break;
171 
172 	default:
173 		if (sh.snh_ifp->if_output == ether_output) {
174 			bcopy((caddr_t)(mtod(m, struct ether_header *)->ether_dhost),
175 				(caddr_t)sh.snh_dhost, 2*sizeof(sh.snh_dhost));
176 			m->m_data += sizeof (struct ether_header);
177 			m->m_len -= sizeof (struct ether_header);
178 			m->m_pkthdr.len -= sizeof (struct ether_header);
179 		}
180 	}
181 	IFDEBUG(D_INPUT)
182 		int i;
183 		printf("clnlintr: src:");
184 		for (i=0; i<6; i++)
185 			printf("%x%c", sh.snh_shost[i] & 0xff, (i<5) ? ':' : ' ');
186 		printf(" dst:");
187 		for (i=0; i<6; i++)
188 			printf("%x%c", sh.snh_dhost[i] & 0xff, (i<5) ? ':' : ' ');
189 		printf("\n");
190 	ENDDEBUG
191 
192 	/*
193 	 *	Get the fixed part of the clnl header into the first mbuf.
194 	 *	Drop the packet if this fails.
195 	 *	Do not call m_pullup if we have a cluster mbuf or the
196 	 *	data is not there.
197 	 */
198 	if ((IS_CLUSTER(m) || (m->m_len < sizeof(struct clnl_fixed))) &&
199 		((m = m_pullup(m, sizeof(struct clnl_fixed))) == 0)) {
200 		INCSTAT(cns_toosmall);	/* TODO: use clnl stats */
201 		goto next;				/* m_pullup discards mbuf */
202 	}
203 
204 	clnl = mtod(m, struct clnl_fixed *);
205 
206 	/*
207 	 *	Drop packet if the length of the header is not reasonable.
208 	 */
209 	if ((clnl->cnf_hdr_len < CLNP_HDR_MIN) ||
210 		(clnl->cnf_hdr_len > CLNP_HDR_MAX)) {
211 		INCSTAT(cns_badhlen);	/* TODO: use clnl stats */
212 		m_freem(m);
213 		goto next;
214 	}
215 
216 	/*
217 	 *	If the header is not contained in this mbuf, make it so.
218 	 *	Drop packet if this fails.
219 	 *	Note: m_pullup will allocate a cluster mbuf if necessary
220 	 */
221 	if (clnl->cnf_hdr_len > m->m_len) {
222 		if ((m = m_pullup(m, (int)clnl->cnf_hdr_len)) == 0) {
223 			INCSTAT(cns_badhlen);	/* TODO: use clnl stats */
224 			goto next;	/* m_pullup discards mbuf */
225 		}
226 		clnl = mtod(m, struct clnl_fixed *);
227 	}
228 
229 	clnlsw = &clnl_protox[clnl->cnf_proto_id];
230 
231 
232 	if (clnlsw->clnl_input)
233 		(*clnlsw->clnl_input) (m, &sh);
234 	else
235 		m_freem(m);
236 
237 	goto next;
238 }
239 
240 /*
241  * FUNCTION:		clnp_input
242  *
243  * PURPOSE:			process an incoming clnp packet
244  *
245  * RETURNS:			nothing
246  *
247  * SIDE EFFECTS:	increments fields of clnp_stat structure.
248  *
249  * NOTES:
250  *	TODO: I would like to make seg_part a pointer into the mbuf, but
251  *	will it be correctly aligned?
252  */
253 clnp_input(m, shp)
254 struct mbuf		*m;		/* ptr to first mbuf of pkt */
255 struct snpa_hdr	*shp;	/* subnetwork header */
256 {
257 	register struct clnp_fixed	*clnp;	/* ptr to fixed part of header */
258 	struct sockaddr_iso			source; /* source address of pkt */
259 	struct sockaddr_iso			target; /* destination address of pkt */
260 #define src	source.siso_addr
261 #define dst	target.siso_addr
262 	caddr_t						hoff;	/* current offset in packet */
263 	caddr_t						hend;	/* address of end of header info */
264 	struct clnp_segment			seg_part; /* segment part of hdr */
265 	int							seg_off=0; /* offset of segment part of hdr */
266 	int							seg_len;/* length of packet data&hdr in bytes */
267 	struct clnp_optidx			oidx, *oidxp = NULL;	/* option index */
268 	extern int 					iso_systype;	/* used by ESIS config resp */
269 	extern struct sockaddr_iso	blank_siso;		/* used for initializing */
270 	int							need_afrin = 0;
271 										/* true if congestion experienced */
272 										/* which means you need afrin nose */
273 										/* spray. How clever! */
274 
275 	IFDEBUG(D_INPUT)
276 		printf(
277 		   "clnp_input: proccessing dg; First mbuf m_len %d, m_type x%x, %s\n",
278 			m->m_len, m->m_type, IS_CLUSTER(m) ? "cluster" : "normal");
279 	ENDDEBUG
280 	need_afrin = 0;
281 
282 	/*
283 	 *	If no iso addresses have been set, there is nothing
284 	 *	to do with the packet.
285 	 */
286 	if (iso_ifaddr == NULL) {
287 		clnp_discard(m, ADDR_DESTUNREACH);
288 		return;
289 	}
290 
291 	INCSTAT(cns_total);
292 	clnp = mtod(m, struct clnp_fixed *);
293 
294 	IFDEBUG(D_DUMPIN)
295 		struct mbuf *mhead;
296 		int			total_len = 0;
297 		printf("clnp_input: clnp header:\n");
298 		dump_buf(mtod(m, caddr_t), clnp->cnf_hdr_len);
299 		printf("clnp_input: mbuf chain:\n");
300 		for (mhead = m; mhead != NULL; mhead=mhead->m_next) {
301 			printf("m x%x, len %d\n", mhead, mhead->m_len);
302 			total_len += mhead->m_len;
303 		}
304 		printf("clnp_input: total length of mbuf chain %d:\n", total_len);
305 	ENDDEBUG
306 
307 	/*
308 	 *	Compute checksum (if necessary) and drop packet if
309 	 *	checksum does not match
310 	 */
311 	if (CKSUM_REQUIRED(clnp) && iso_check_csum(m, (int)clnp->cnf_hdr_len)) {
312 		INCSTAT(cns_badcsum);
313 		clnp_discard(m, GEN_BADCSUM);
314 		return;
315 	}
316 
317 	if (clnp->cnf_vers != ISO8473_V1) {
318 		INCSTAT(cns_badvers);
319 		clnp_discard(m, DISC_UNSUPPVERS);
320 		return;
321 	}
322 
323 
324  	/* check mbuf data length: clnp_data_ck will free mbuf upon error */
325 	CTOH(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, seg_len);
326 	if ((m = clnp_data_ck(m, seg_len)) == 0)
327 		return;
328 
329 	clnp = mtod(m, struct clnp_fixed *);
330 	hend = (caddr_t)clnp + clnp->cnf_hdr_len;
331 
332 	/*
333 	 *	extract the source and destination address
334 	 *	drop packet on failure
335 	 */
336 	source = target = blank_siso;
337 
338 	hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
339 	CLNP_EXTRACT_ADDR(dst, hoff, hend);
340 	if (hoff == (caddr_t)0) {
341 		INCSTAT(cns_badaddr);
342 		clnp_discard(m, GEN_INCOMPLETE);
343 		return;
344 	}
345 	CLNP_EXTRACT_ADDR(src, hoff, hend);
346 	if (hoff == (caddr_t)0) {
347 		INCSTAT(cns_badaddr);
348 		clnp_discard(m, GEN_INCOMPLETE);
349 		return;
350 	}
351 
352 	IFDEBUG(D_INPUT)
353 		printf("clnp_input: from %s", clnp_iso_addrp(&src));
354 		printf(" to %s\n", clnp_iso_addrp(&dst));
355 	ENDDEBUG
356 
357 	/*
358 	 *	extract the segmentation information, if it is present.
359 	 *	drop packet on failure
360 	 */
361 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
362 		(clnp->cnf_type & CNF_SEG_OK)) {
363 		if (hoff + sizeof(struct clnp_segment) > hend) {
364 			INCSTAT(cns_noseg);
365 			clnp_discard(m, GEN_INCOMPLETE);
366 			return;
367 		} else {
368 			(void) bcopy(hoff, (caddr_t)&seg_part, sizeof(struct clnp_segment));
369 			/* make sure segmentation fields are in host order */
370 			seg_part.cng_id = ntohs(seg_part.cng_id);
371 			seg_part.cng_off = ntohs(seg_part.cng_off);
372 			seg_part.cng_tot_len = ntohs(seg_part.cng_tot_len);
373 			seg_off = hoff - (caddr_t)clnp;
374 			hoff += sizeof(struct clnp_segment);
375 		}
376 	}
377 
378 	/*
379 	 *	process options if present. If clnp_opt_sanity returns
380 	 *	false (indicating an error was found in the options) or
381 	 *	an unsupported option was found
382 	 *	then drop packet and emit an ER.
383 	 */
384 	if (hoff < hend) {
385 		int		errcode;
386 
387 		oidxp = &oidx;
388 		errcode = clnp_opt_sanity(m, hoff, hend-hoff, oidxp);
389 
390 		/* we do not support security */
391 		if ((errcode == 0) && (oidxp->cni_securep))
392 			errcode = DISC_UNSUPPSECURE;
393 
394 		/* the er option is valid with ER pdus only */
395 		if ((errcode == 0) && (oidxp->cni_er_reason != ER_INVALREAS) &&
396 			((clnp->cnf_type & CNF_TYPE) != CLNP_ER))
397 			errcode = DISC_UNSUPPOPT;
398 
399 #ifdef	DECBIT
400 		/* check if the congestion experienced bit is set */
401 		if (oidxp->cni_qos_formatp) {
402 			caddr_t	qosp = CLNP_OFFTOOPT(m, oidxp->cni_qos_formatp);
403 			u_char	qos = *qosp;
404 
405 			need_afrin = ((qos & (CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED)) ==
406 				(CLNPOVAL_GLOBAL|CLNPOVAL_CONGESTED));
407 			if (need_afrin)
408 				INCSTAT(cns_congest_rcvd);
409 		}
410 #endif	DECBIT
411 
412 		if (errcode != 0) {
413 			clnp_discard(m, (char)errcode);
414 			IFDEBUG(D_INPUT)
415 				printf("clnp_input: dropped (err x%x) due to bad options\n",
416 					errcode);
417 			ENDDEBUG
418 			return;
419 		}
420 	}
421 
422 	/*
423 	 *	check if this packet is for us. if not, then forward
424 	 */
425 	if (clnp_ours(&dst) == 0) {
426 		IFDEBUG(D_INPUT)
427 			printf("clnp_input: forwarding packet not for us\n");
428 		ENDDEBUG
429  		clnp_forward(m, seg_len, &dst, oidxp, seg_off, shp);
430 		return;
431 	}
432 
433 	/*
434 	 *	ESIS Configuration Response Function
435 	 *
436 	 *	If the packet received was sent to the multicast address
437 	 *	all end systems, then send an esh to the source
438 	 */
439 	if ((shp->snh_flags & M_MCAST) && (iso_systype == SNPA_ES)) {
440 		extern short esis_holding_time;
441 
442 		esis_shoutput(shp->snh_ifp, ESIS_ESH, esis_holding_time,
443 			shp->snh_shost, 6, &dst);
444 	}
445 
446 	/*
447 	 *	If this is a fragment, then try to reassemble it. If clnp_reass
448 	 *	returns non NULL, the packet has been reassembled, and should
449 	 *	be give to TP. Otherwise the fragment has been delt with
450 	 *	by the reassembly code (either stored or deleted). In either case
451 	 *	we should have nothing more to do with it.
452 	 */
453 	if (((clnp->cnf_type & CNF_TYPE) != CLNP_ER) &&
454 		(clnp->cnf_type & CNF_SEG_OK) &&
455 		(seg_len != seg_part.cng_tot_len)) {
456 		struct mbuf	*m0;
457 
458 		if ((m0 = clnp_reass(m, &src, &dst, &seg_part)) != NULL) {
459 			m = m0;
460 			clnp = mtod(m, struct clnp_fixed *);
461 			INCSTAT(cns_reassembled);
462 		} else {
463 			return;
464 		}
465 	}
466 
467 	/*
468 	 *	give the packet to the higher layer
469 	 *
470 	 *	Note: the total length of packet
471 	 *	is the total length field of the segmentation part,
472 	 *	or, if absent, the segment length field of the
473 	 *	header.
474 	 */
475 	INCSTAT(cns_delivered);
476 	switch (clnp->cnf_type & CNF_TYPE) {
477 	case CLNP_ER:
478 		/*
479 		 *	This ER must have the er option.
480 		 *	If the option is not present, discard datagram.
481 		 */
482 		if (oidxp == NULL || oidxp->cni_er_reason == ER_INVALREAS) {
483 			clnp_discard(m, GEN_HDRSYNTAX);
484 		} else {
485 			clnp_er_input(m, &src, oidxp->cni_er_reason);
486 		}
487 		break;
488 
489 	case CLNP_DT:
490 		(*isosw[clnp_protox[ISOPROTO_TP]].pr_input)(m, &source, &target,
491 			clnp->cnf_hdr_len, need_afrin);
492 		break;
493 
494  	case CLNP_RAW:
495 	case CLNP_ECR:
496 		IFDEBUG(D_INPUT)
497 			printf("clnp_input: raw input of %d bytes\n",
498 				clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len);
499 		ENDDEBUG
500 		(*isosw[clnp_protox[ISOPROTO_RAW]].pr_input)(m, &source, &target,
501 					clnp->cnf_hdr_len);
502 		break;
503 
504 	case CLNP_EC:
505 		IFDEBUG(D_INPUT)
506 			printf("clnp_input: echoing packet\n");
507 		ENDDEBUG
508 		/*
509 		 *	Switch the source and destination address,
510 		 */
511 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
512 		CLNP_INSERT_ADDR(hoff, src);
513 		CLNP_INSERT_ADDR(hoff, dst);
514 		clnp->cnf_type &= ~CNF_TYPE;
515 		clnp->cnf_type |= CLNP_ECR;
516 
517 		/*
518 		 *	Forward back to sender
519 		 */
520 		clnp_forward(m, (int)
521 			(clnp->cnf_type & CNF_SEG_OK ? seg_part.cng_tot_len : seg_len),
522 			&src, oidxp, seg_off, 0);
523 		break;
524 
525 	default:
526  		printf("clnp_input: unknown clnp pkt type %d\n",
527 			clnp->cnf_type & CNF_TYPE);
528 		clnp_stat.cns_delivered--;
529 		clnp_stat.cns_noproto++;
530 		clnp_discard(m, GEN_HDRSYNTAX);
531  		break;
532 	}
533 }
534 #endif ISO
535