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