xref: /original-bsd/sys/netiso/if_cons.c (revision 95b555db)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)if_cons.c	7.8 (Berkeley) 05/06/91
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 /*
37  * $Header: if_cons.c,v 4.7 88/08/11 15:52:55 nhall Exp $
38  * $Source: /usr/argo/sys/netiso/RCS/if_cons.c,v $
39  *
40  * cons.c - Connection Oriented Network Service:
41  * including support for a) user transport-level service,
42  *	b) COSNS below CLNP, and c) CONS below TP.
43  */
44 
45 #ifdef TPCONS
46 #ifdef KERNEL
47 #ifdef ARGO_DEBUG
48 #define Static
49 unsigned LAST_CALL_PCB;
50 #else ARGO_DEBUG
51 #define Static static
52 #endif ARGO_DEBUG
53 
54 
55 
56 #ifndef SOCK_STREAM
57 #include "param.h"
58 #include "systm.h"
59 #include "mbuf.h"
60 #include "protosw.h"
61 #include "socket.h"
62 #include "socketvar.h"
63 #include "errno.h"
64 #include "ioctl.h"
65 #include "tsleep.h"
66 
67 #include "../net/if.h"
68 #include "../net/netisr.h"
69 #include "../net/route.h"
70 
71 #include "iso_errno.h"
72 #include "argo_debug.h"
73 #include "tp_trace.h"
74 #include "iso.h"
75 #include "cons.h"
76 #include "iso_pcb.h"
77 
78 #include "../netccitt/x25.h"
79 #include "../netccitt/pk.h"
80 #include "../netccitt/pk_var.h"
81 #endif
82 
83 #ifdef ARGO_DEBUG
84 #define MT_XCONN	0x50
85 #define MT_XCLOSE	0x51
86 #define MT_XCONFIRM	0x52
87 #define MT_XDATA	0x53
88 #define MT_XHEADER	0x54
89 #else
90 #define MT_XCONN	MT_DATA
91 #define MT_XCLOSE	MT_DATA
92 #define MT_XCONFIRM	MT_DATA
93 #define MT_XDATA	MT_DATA
94 #define MT_XHEADER	MT_HEADER
95 #endif ARGO_DEBUG
96 
97 #define DONTCLEAR	 -1
98 
99 /*********************************************************************
100  * cons.c - CONS interface to the x.25 layer
101  *
102  * TODO: figure out what resources we might run out of besides mbufs.
103  *  If we run out of any of them (including mbufs) close and recycle
104  *  lru x% of the connections, for some parameter x.
105  *
106  * There are 2 interfaces from above:
107  * 1) from TP0:
108  *    cons CO network service
109  *    TP associates a transport connection with a network connection.
110  * 	  cons_output( isop, m, len, isdgm==0 )
111  *        co_flags == 0
112  * 2) from TP4:
113  *	  It's a datagram service, like clnp is. - even though it calls
114  *			cons_output( isop, m, len, isdgm==1 )
115  *	  it eventually goes through
116  *			cosns_output(ifp, m, dst).
117  *    TP4 permits multiplexing (reuse, possibly simultaneously) of the
118  *	  network connections.
119  *    This means that many sockets (many tpcbs) may be associated with
120  *    this pklcd, hence cannot have a back ptr from pklcd to a tpcb.
121  *        co_flags & CONSF_DGM
122  *    co_socket is null since there may be many sockets that use this pklcd.
123  *
124 NOTE:
125 	streams would really be nice. sigh.
126 NOTE:
127 	PVCs could be handled by config-ing a cons with an address and with the
128 	IFF_POINTTOPOINT flag on.  This code would then have to skip the
129 	connection setup stuff for pt-to-pt links.
130 
131 
132  *********************************************************************/
133 
134 
135 #define CONS_IFQMAXLEN 5
136 
137 
138 /* protosw pointers for getting to higher layer */
139 Static 	struct protosw	*CLNP_proto;
140 Static 	struct protosw	*TP_proto;
141 Static 	struct protosw	*X25_proto;
142 Static 	int				issue_clear_req();
143 
144 #ifndef	PHASEONE
145 extern	struct ifaddr	*ifa_ifwithnet();
146 #endif	PHASEONE
147 
148 extern	struct ifaddr	*ifa_ifwithaddr();
149 
150 Static  struct socket	dummysocket; /* for use by cosns */
151 
152 extern struct	isopcb	tp_isopcb; /* chain of all TP pcbs */
153 struct	isopcb			tp_incoming_pending;  /* incoming connections
154 										for TP, pending */
155 
156 struct isopcb 	*Xpcblist[] =  {
157 	&tp_incoming_pending,
158 	&tp_isopcb,
159 	(struct isopcb *)0
160 };
161 
162 Static 	int parse_facil(), NSAPtoDTE(), make_partial_x25_packet();
163 Static	int FACILtoNSAP(), DTEtoNSAP();
164 Static	struct pklcd *cons_chan_to_pcb();
165 
166 #define HIGH_NIBBLE 1
167 #define LOW_NIBBLE 0
168 
169 /*
170  * NAME:	nibble_copy()
171  * FUNCTION and ARGUMENTS:
172  * 	copies (len) nibbles from (src_octet), high or low nibble
173  *  to (dst_octet), high or low nibble,
174  * src_nibble & dst_nibble should be:
175  * 	HIGH_NIBBLE (1) if leftmost 4 bits/ most significant nibble
176  * 	LOW_NIBBLE (0) if rightmost 4 bits/ least significant nibble
177  * RETURNS: VOID
178  */
179 void
180 nibble_copy(src_octet, src_nibble, dst_octet, dst_nibble, len)
181 	register char  	*src_octet;
182 	register char  	*dst_octet;
183 	register unsigned		src_nibble;
184 	register unsigned 		dst_nibble;
185 	int		len;
186 {
187 
188 	register 	i;
189 	register 	unsigned dshift, sshift;
190 
191 	IFDEBUG(D_CADDR)
192 		printf("nibble_copy ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
193 		 src_octet, src_nibble, dst_octet, dst_nibble, len);
194 	ENDDEBUG
195 #define SHIFT 0x4
196 
197 	dshift = dst_nibble << 2;
198 	sshift = src_nibble << 2;
199 
200 	for (i=0; i<len; i++) {
201 		/* clear dst_nibble  */
202 		*dst_octet 	&= ~(0xf<< dshift);
203 
204 		/* set dst nibble */
205 		*dst_octet 	|= ( 0xf & (*src_octet >> sshift))<< dshift;
206 
207 		dshift		^= SHIFT;
208 		sshift		^= SHIFT;
209 		src_nibble 	= 1-src_nibble;
210 		dst_nibble 	= 1-dst_nibble;
211 		src_octet	+= src_nibble;
212 		dst_octet 	+= dst_nibble;
213 	}
214 	IFDEBUG(D_CADDR)
215 		printf("nibble_copy DONE\n");
216 	ENDDEBUG
217 }
218 
219 /*
220  * NAME:	nibble_match()
221  * FUNCTION and ARGUMENTS:
222  * 	compares src_octet/src_nibble and dst_octet/dst_nibble  for len nibbles.
223  * RETURNS: 0 if they differ, 1 if they are the same.
224  */
225 int
226 nibble_match( src_octet, src_nibble, dst_octet, dst_nibble, len)
227 	register char  	*src_octet;
228 	register char  	*dst_octet;
229 	register unsigned		src_nibble;
230 	register unsigned 		dst_nibble;
231 	int		len;
232 {
233 
234 	register 	i;
235 	register 	unsigned dshift, sshift;
236 	u_char		nibble_a, nibble_b;
237 
238 	IFDEBUG(D_CADDR)
239 		printf("nibble_match ( 0x%x, 0x%x, 0x%x, 0x%x 0x%x)\n",
240 		 src_octet, src_nibble, dst_octet, dst_nibble, len);
241 	ENDDEBUG
242 #define SHIFT 0x4
243 
244 	dshift = dst_nibble << 2;
245 	sshift = src_nibble << 2;
246 
247 	for (i=0; i<len; i++) {
248 		nibble_b = ((*dst_octet)>>dshift) & 0xf;
249 		nibble_a = ( 0xf & (*src_octet >> sshift));
250 		if (nibble_b != nibble_a)
251 			return 0;
252 
253 		dshift		^= SHIFT;
254 		sshift		^= SHIFT;
255 		src_nibble 	= 1-src_nibble;
256 		dst_nibble 	= 1-dst_nibble;
257 		src_octet	+= src_nibble;
258 		dst_octet 	+= dst_nibble;
259 	}
260 	IFDEBUG(D_CADDR)
261 		printf("nibble_match DONE\n");
262 	ENDDEBUG
263 	return 1;
264 }
265 
266 /*
267  **************************** NET PROTOCOL cons ***************************
268  */
269 /*
270  * NAME:	cons_init()
271  * CALLED FROM:
272  *	autoconf
273  * FUNCTION:
274  *	initialize the protocol
275  */
276 cons_init()
277 {
278 	int tp_incoming(), clnp_incoming();
279 
280 
281 	CLNP_proto = pffindproto(AF_ISO, ISOPROTO_CLNP, SOCK_DGRAM);
282 	X25_proto = pffindproto(AF_ISO, ISOPROTO_X25, SOCK_STREAM);
283 	TP_proto = pffindproto(AF_ISO, ISOPROTO_TP0, SOCK_SEQPACKET);
284 	IFDEBUG(D_CCONS)
285 		printf("cons_init end : cnlp_proto 0x%x cons proto 0x%x tp proto 0x%x\n",
286 			CLNP_proto, X25_proto, TP_proto);
287 	ENDDEBUG
288 #ifdef notdef
289 	pk_protolisten(0x81, 0, clnp_incoming);
290 	pk_protolisten(0x82, 0, esis_incoming);
291 	pk_protolisten(0x84, 0, tp8878_A_incoming);
292 	pk_protolisten(0, 0, tp_incoming);
293 #endif
294 }
295 
296 tp_incoming(lcp, m0)
297 struct pklcd *lcp;
298 struct mbuf *m0;
299 {
300 	register struct mbuf *m = m0->m_next; /* m0 has calling sockaddr_x25 */
301 	register struct isopcb *isop;
302 	extern struct isopcb tp_isopcb;
303 	int cons_tpinput();
304 
305 	if (iso_pcballoc((struct socket *)0, &tp_incoming_pending)) {
306 		m_freem(m);
307 		pk_clear(lcp);
308 		return;
309 	}
310 	isop = tp_incoming_pending.isop_next;
311 	pk_output(lcp); /* Confirms call */
312 	lcp->lcd_upper = cons_tpinput;
313 	lcp->lcd_upnext = (caddr_t)isop;
314 	isop->isop_chan = (caddr_t)lcp;
315 	isop->isop_laddr = &isop->isop_sladdr;
316 	isop->isop_faddr = &isop->isop_sfaddr;
317 	DTEtoNSAP(isop->isop_laddr, &lcp->lcd_laddr);
318 	DTEtoNSAP(isop->isop_faddr, &lcp->lcd_faddr);
319 	parse_facil(isop, lcp, &(mtod(m, struct x25_packet *)->packet_data),
320 		m->m_pkthdr.len - PKHEADERLN);
321 	m_freem(m);
322 }
323 
324 cons_tpinput(lcp, m0)
325 struct mbuf *m0;
326 struct pklcd *lcp;
327 {
328 	register struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext;
329 	register struct x25_packet *xp;
330 	int cmd;
331 
332 	if (isop == 0)
333 		return;
334 	if (m0 == 0) {
335 		isop->isop_chan = 0;
336 		isop->isop_refcnt = 0;
337 		lcp->lcd_upnext = 0;
338 		lcp->lcd_upper = 0;
339 		goto dead;
340 	}
341 	switch(m0->m_type) {
342 	case MT_DATA:
343 	case MT_OOBDATA:
344 		tpcons_input(m0, isop->isop_faddr, isop->isop_laddr,
345 			(struct socket *)0, (caddr_t)lcp);
346 
347 	case MT_CONTROL:
348 		switch (pk_decode(mtod(m0, struct x25_packet *))) {
349 		default:
350 			return;
351 
352 		case RR:
353 			cmd = PRC_CONS_SEND_DONE;
354 			break;
355 
356 		case CALL_ACCEPTED:
357 			if (lcp->lcd_sb.sb_mb)
358 				lcp->lcd_send(lcp); /* XXX - fix this */
359 			break;
360 
361 		dead:
362 		case RESET:
363 		case CLEAR:
364 		case CLEAR_CONF:
365 			cmd = PRC_ROUTEDEAD;
366 		}
367 		tpcons_ctlinput(cmd, isop->isop_faddr, isop);
368 	}
369 }
370 
371 /*
372  * NAME:	cons_connect()
373  * CALLED FROM:
374  *	tpcons_pcbconnect() when opening a new connection.
375  * FUNCTION anD ARGUMENTS:
376  *  Figures out which device to use, finding a route if one doesn't
377  *  already exist.
378  * RETURN VALUE:
379  *  returns E*
380  */
381 cons_connect(isop)
382 	register struct isopcb *isop;
383 {
384 	register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
385 	register struct mbuf 	*m;
386 	struct ifaddr 			*ifa;
387 
388 	IFDEBUG(D_CCONN)
389 		printf("cons_connect(0x%x): ", isop);
390 		dump_isoaddr(isop->isop_faddr);
391 		printf("myaddr: ");
392 		dump_isoaddr(isop->isop_laddr);
393 		printf("\n" );
394 	ENDDEBUG
395 	NSAPtoDTE(isop->isop_faddr, &lcp->lcd_faddr);
396 	lcp->lcd_upper = cons_tpinput;
397 	lcp->lcd_upnext = (caddr_t)isop;
398 	IFDEBUG(D_CCONN)
399 		printf(
400 		"calling make_partial_x25_packet( 0x%x, 0x%x, 0x%x)\n",
401 			&lcp->lcd_faddr, &lcp->lcd_laddr,
402 			isop->isop_socket->so_proto->pr_protocol);
403 	ENDDEBUG
404 	return (make_partial_x25_packet(isop, lcp, m) ||
405 		 pk_connect(lcp, &lcp->lcd_faddr));
406 }
407 
408 /*
409  **************************** DEVICE cons ***************************
410  */
411 
412 
413 /*
414  * NAME:	cons_ctlinput()
415  * CALLED FROM:
416  *  lower layer when ECN_CLEAR occurs : this routine is here
417  *  for consistency - cons subnet service calls its higher layer
418  *  through the protosw entry.
419  * FUNCTION & ARGUMENTS:
420  *  cmd is a PRC_* command, list found in ../sys/protosw.h
421  *  copcb is the obvious.
422  *  This serves the higher-layer cons service.
423  * NOTE: this takes 3rd arg. because cons uses it to inform itself
424  *  of things (timeouts, etc) but has a pcb instead of an address.
425  */
426 cons_ctlinput(cmd, sa, copcb)
427 	int cmd;
428 	struct sockaddr *sa;
429 	register struct pklcd *copcb;
430 {
431 }
432 
433 
434 find_error_reason( xp )
435 	register struct x25_packet *xp;
436 {
437 	extern u_char x25_error_stats[];
438 	int error, cause;
439 
440 	if (xp) {
441 		cause = 4[(char *)xp];
442 		switch (cause) {
443 			case 0x00:
444 			case 0x80:
445 				/* DTE originated; look at the diagnostic */
446 				error = (CONL_ERROR_MASK | cause);
447 				goto done;
448 
449 			case 0x01: /* number busy */
450 			case 0x81:
451 			case 0x09: /* Out of order */
452 			case 0x89:
453 			case 0x11: /* Remot Procedure Error */
454 			case 0x91:
455 			case 0x19: /* reverse charging accept not subscribed */
456 			case 0x99:
457 			case 0x21: /* Incampat destination */
458 			case 0xa1:
459 			case 0x29: /* fast select accept not subscribed */
460 			case 0xa9:
461 			case 0x39: /* ship absent */
462 			case 0xb9:
463 			case 0x03: /* invalid facil request */
464 			case 0x83:
465 			case 0x0b: /* access barred */
466 			case 0x8b:
467 			case 0x13: /* local procedure error */
468 			case 0x93:
469 			case 0x05: /* network congestion */
470 			case 0x85:
471 			case 0x8d: /* not obtainable */
472 			case 0x0d:
473 			case 0x95: /* RPOA out of order */
474 			case 0x15:
475 				/* take out bit 8
476 				 * so we don't have to have so many perror entries
477 				 */
478 				error = (CONL_ERROR_MASK | 0x100 | (cause & ~0x80));
479 				goto done;
480 
481 			case 0xc1: /* gateway-detected proc error */
482 			case 0xc3: /* gateway congestion */
483 
484 				error = (CONL_ERROR_MASK | 0x100 | cause);
485 				goto done;
486 		}
487 	}
488 	/* otherwise, a *hopefully* valid perror exists in the e_reason field */
489 	error = xp->packet_data;
490 	if (error = 0) {
491 		printf("Incoming PKT TYPE 0x%x with reason 0x%x\n",
492 			pk_decode(xp),
493 			cause);
494 		error = E_CO_HLI_DISCA;
495 	}
496 
497 done:
498 	return error;
499 }
500 
501 
502 
503 #endif KERNEL
504 
505 /*
506  * NAME:	make_partial_x25_packet()
507  *
508  * FUNCTION and ARGUMENTS:
509  *	Makes part of an X.25 call packet, for use by x25.
510  *  (src) and (dst) are the NSAP-addresses of source and destination.
511  *	(buf) is a ptr to a buffer into which to write this partial header.
512  *
513  *	 0			Facility length (in octets)
514  *	 1			Facility field, which is a set of:
515  *	  m			facil code
516  *	  m+1		facil param len (for >2-byte facilities) in octets
517  *	  m+2..p	facil param field
518  *  q			user data (protocol identification octet)
519  *
520  *
521  * RETURNS:
522  *  0 if OK
523  *  E* if failed.
524  *
525  * SIDE EFFECTS:
526  * Stores facilites mbuf in X.25 control block, where the connect
527  * routine knows where to look for it.
528  */
529 
530 #ifdef X25_1984
531 int cons_use_facils = 1;
532 #else X25_1984
533 int cons_use_facils = 0;
534 #endif X25_1984
535 
536 int cons_use_udata = 1; /* KLUDGE FOR DEBUGGING */
537 
538 Static int
539 make_partial_x25_packet(isop, lcp)
540 	struct isopcb *isop;
541 	struct pklcd *lcp;
542 {
543 	u_int				proto;
544 	int					flag;
545 	caddr_t 			buf;
546 	register caddr_t	ptr;
547 	register int		len	= 0;
548 	int 				buflen	=0;
549 	caddr_t				facil_len;
550 	int 				oddness	= 0;
551 	struct mbuf *m;
552 
553 
554 	MGET(m, MT_DATA, M_WAITOK);
555 	if (m == 0)
556 		return ENOBUFS;
557 	buf = mtod(m, caddr_t);
558 	ptr = buf;
559 	IFDEBUG(D_CCONN)
560 		printf("make_partial_x25_packet(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
561 			isop->isop_laddr, isop->isop_faddr, proto, m, flag);
562 	ENDDEBUG
563 
564 	/* ptr now points to facil length (len of whole facil field in OCTETS */
565 	facil_len = ptr ++;
566 
567 	IFDEBUG(D_CADDR)
568 		printf("make_partial  calling: ptr 0x%x, len 0x%x\n", ptr,
569 				isop->isop_laddr->siso_addr.isoa_len);
570 	ENDDEBUG
571 	if (cons_use_facils) {
572 		*ptr = 0xcb; /* calling facility code */
573 		ptr ++;
574 		ptr ++; /* leave room for facil param len (in OCTETS + 1) */
575 		ptr ++; /* leave room for the facil param len (in nibbles),
576 				* high two bits of which indicate full/partial NSAP
577 				*/
578 		len = isop->isop_laddr->siso_addr.isoa_len;
579 		bcopy( isop->isop_laddr->siso_data, ptr, len);
580 		*(ptr-2) = len+2; /* facil param len in octets */
581 		*(ptr-1) = len<<1; /* facil param len in nibbles */
582 		ptr += len;
583 
584 		IFDEBUG(D_CADDR)
585 			printf("make_partial  called: ptr 0x%x, len 0x%x\n", ptr,
586 					isop->isop_faddr->siso_addr.isoa_len);
587 		ENDDEBUG
588 		*ptr = 0xc9; /* called facility code */
589 		ptr ++;
590 		ptr ++; /* leave room for facil param len (in OCTETS + 1) */
591 		ptr ++; /* leave room for the facil param len (in nibbles),
592 				* high two bits of which indicate full/partial NSAP
593 				*/
594 		len = isop->isop_faddr->siso_nlen;
595 		bcopy(isop->isop_faddr->siso_data, ptr, len);
596 		*(ptr-2) = len+2; /* facil param len = addr len + 1 for each of these
597 						  * two length fields, in octets */
598 		*(ptr-1) = len<<1; /* facil param len in nibbles */
599 		ptr += len;
600 
601 	}
602 	*facil_len = ptr - facil_len - 1;
603 	if (*facil_len > MAX_FACILITIES)
604 		return E_CO_PNA_LONG;
605 
606 	if (cons_use_udata) {
607 		if (isop->isop_x25crud_len > 0) {
608 			/*
609 			 *	The user specified something. Stick it in
610 			 */
611 			bcopy(isop->isop_x25crud, lcp->lcd_faddr.x25_udata,
612 					isop->isop_x25crud_len);
613 			lcp->lcd_faddr.x25_udlen = isop->isop_x25crud_len;
614 		}
615 	}
616 
617 	buflen = (int)(ptr - buf);
618 
619 	IFDEBUG(D_CDUMP_REQ)
620 		register int i;
621 
622 		printf("ECN_CONNECT DATA buf 0x%x len %d (0x%x)\n",
623 			buf, buflen, buflen);
624 		for( i=0; i < buflen; ) {
625 			printf("+%d: %x %x %x %x    %x %x %x %x\n",
626 				i,
627 				*(buf+i), *(buf+i+1), *(buf+i+2), *(buf+i+3),
628 				*(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7));
629 			i+=8;
630 		}
631 	ENDDEBUG
632 	IFDEBUG(D_CADDR)
633 		printf("make_partial returns buf 0x%x size 0x%x bytes\n",
634 			mtod(m, caddr_t), buflen);
635 	ENDDEBUG
636 
637 	if (buflen > MHLEN)
638 		return E_CO_PNA_LONG;
639 
640 	m->m_len = buflen;
641 	lcp->lcd_facilities = m;
642 	return  0;
643 }
644 
645 /*
646  * NAME:	NSAPtoDTE()
647  * CALLED FROM:
648  *  make_partial_x25_packet()
649  * FUNCTION and ARGUMENTS:
650  *  get a DTE address from an NSAP-address (struct sockaddr_iso)
651  *  (dst_octet) is the octet into which to begin stashing the DTE addr
652  *  (dst_nibble) takes 0 or 1.  1 means begin filling in the DTE addr
653  * 		in the high-order nibble of dst_octet.  0 means low-order nibble.
654  *  (addr) is the NSAP-address
655  *  (flag) is true if the transport suffix is to become the
656  *		last two digits of the DTE address
657  *  A DTE address is a series of ASCII digits
658  *
659  *	A DTE address may have leading zeros. The are significant.
660  *		1 digit per nibble, may be an odd number of nibbles.
661  *
662  *  An NSAP-address has the DTE address in the IDI. Leading zeros are
663  *		significant. Trailing hex f indicates the end of the DTE address.
664  *  	The IDI is a series of BCD digits, one per nibble.
665  *
666  * RETURNS
667  *  # significant digits in the DTE address, -1 if error.
668  */
669 
670 Static int
671 NSAPtoDTE(siso, sx25)
672 	register struct sockaddr_iso *siso;
673 	register struct sockaddr_x25 *sx25;
674 {
675 	int		dtelen = -1;
676 
677 	IFDEBUG(D_CADDR)
678 		printf("NSAPtoDTE: nsap: %s\n", clnp_iso_addrp(&siso->siso_addr));
679 	ENDDEBUG
680 
681 	if (siso->siso_data[0] == AFI_37) {
682 		register char *out = sx25->x25_addr;
683 		register char *in = siso->siso_data + 1;
684 		register int nibble;
685 		char *lim = siso->siso_data + siso->siso_nlen;
686 		char *olim = out+15;
687 		int lowNibble = 0;
688 
689 		while (in < lim) {
690 			nibble = ((lowNibble ? *in++ : (*in >> 4)) & 0xf) | 0x30;
691 			lowNibble ^= 1;
692 			if (nibble != 0x3f && out < olim)
693 				*out++ = nibble;
694 		}
695 		dtelen = out - sx25->x25_addr;
696 		*out++ = 0;
697 	} else {
698 		register struct rtentry *rt = rtalloc1(siso, 1);
699 		/* error = iso_8208snparesolve(addr, x121string, &x121strlen);*/
700 
701 		if (rt) {
702 			register struct sockaddr_x25 *sxx =
703 							(struct sockaddr_x25 *)rt->rt_gateway;
704 			register char *in = sxx->x25_addr;
705 
706 			rt->rt_use--;
707 			if (sxx && sxx->x25_family == AF_CCITT) {
708 				bcopy(sx25->x25_addr, sxx->x25_addr, sizeof(sx25->x25_addr));
709 				while (*in++) {}
710 				dtelen = in - sxx->x25_addr;
711 			}
712 		}
713 	}
714 	return dtelen;
715 }
716 
717 /*
718  * NAME:	FACILtoNSAP()
719  * CALLED FROM:
720  *  parse_facil()
721  * FUNCTION and ARGUMENTS:
722  * 	Creates and NSAP in the sockaddr_iso (addr) from the
723  *  x.25 facility found at buf - 1.
724  * RETURNS:
725  *  length of parameter if ok, -1 if error.
726  */
727 
728 Static int
729 FACILtoNSAP(addr, buf)
730 	u_char 		*buf;
731 	register struct sockaddr_iso *addr;
732 {
733 	int len_in_nibbles, param_len = *buf++;
734 	u_char			buf_len; /* in bytes */
735 
736 	IFDEBUG(D_CADDR)
737 		printf("FACILtoNSAP( 0x%x, 0x%x, 0x%x )\n",
738 			buf, buf_len, addr );
739 	ENDDEBUG
740 
741 	len_in_nibbles = *buf & 0x3f;
742 	buf_len = (len_in_nibbles + 1) >> 1;
743 	/* despite the fact that X.25 makes us put a length in nibbles
744 	 * here, the NSAP-addrs are always in full octets
745 	 */
746 	switch (*buf++ & 0xc0) {
747 	case 0:
748 		/* Entire OSI NSAP address */
749 		bcopy((caddr_t)buf, addr->siso_data, addr->siso_nlen = buf_len);
750 		break;
751 
752 	case 40:
753 		/* Partial OSI NSAP address, assume trailing */
754 		if (buf_len + addr->siso_nlen > sizeof(addr->siso_addr))
755 			return -1;
756 		bcopy((caddr_t)buf, TSEL(addr), buf_len);
757 		addr->siso_nlen += buf_len;
758 		break;
759 
760 	default:
761 		/* Rather than blow away the connection, just ignore and use
762 		   NSAP from DTE */;
763 	}
764 	return param_len;
765 }
766 
767 static
768 init_siso(siso)
769 register struct sockaddr_iso *siso;
770 {
771 	siso->siso_len = sizeof (*siso);
772 	siso->siso_family = AF_ISO;
773 	siso->siso_data[0] = AFI_37;
774 	siso->siso_nlen = 8;
775 }
776 
777 /*
778  * NAME:	DTEtoNSAP()
779  * CALLED FROM:
780  *  parse_facil()
781  * FUNCTION and ARGUMENTS:
782  *  Creates a type 37 NSAP in the sockaddr_iso (addr)
783  * 	from a DTE address found in a sockaddr_x25.
784  *
785  * RETURNS:
786  *  0 if ok; E* otherwise.
787  */
788 
789 Static  int
790 DTEtoNSAP(addr, sx)
791 	struct sockaddr_iso *addr;
792 	struct sockaddr_x25 *sx;
793 {
794 	register char		*in, *out;
795 	register int		first;
796 	int					pad_tail = 0;
797 	int 				src_len;
798 
799 
800 	init_siso(addr);
801 	src_len = strlen(sx->x25_addr);
802 	in = sx->x25_addr;
803 	out = addr->siso_data + 1;
804 	if (*in == '0' && (src_len & 1 == 0)) {
805 		pad_tail = 0xf;
806 		src_len++;
807 	}
808 	for (first = 0; src_len > 0; src_len --) {
809 		first |= *in++;
810 		if (src_len & 1) {
811 			*out++ = first;
812 			first = 0;
813 		}
814 		else first <<= 4;
815 	}
816 	if (pad_tail)
817 		out[-1] |= 0xf;
818 	return 0; /* ok */
819 }
820 
821 /*
822  * FUNCTION and ARGUMENTS:
823  *	parses (buf_len) bytes beginning at (buf) and finds
824  *  a called nsap, a calling nsap, and protocol identifier.
825  * RETURNS:
826  *  0 if ok, E* otherwise.
827  */
828 
829 static int
830 parse_facil(lcp, isop, buf, buf_len)
831 	caddr_t 		buf;
832 	u_char			buf_len; /* in bytes */
833 	struct			isopcb *isop;
834 	struct			pklcd *lcp;
835 {
836 	register struct sockaddr_iso *called = isop->isop_laddr;
837 	register struct sockaddr_iso *calling = isop->isop_faddr;
838 	register int 	i;
839 	register u_char 	*ptr = (u_char *)buf;
840 	u_char			*ptr_lim, *facil_lim;
841 	int 			facil_param_len, facil_len;
842 
843 	IFDEBUG(D_CADDR)
844 		printf("parse_facil(0x%x, 0x%x, 0x%x, 0x%x)\n",
845 			buf, buf_len, called, calling);
846 		dump_buf(buf, buf_len);
847 	ENDDEBUG
848 
849 	/* find the beginnings of the facility fields in buf
850 	 * by skipping over the called & calling DTE addresses
851 	 * i <- # nibbles in called + # nibbles in calling
852 	 * i += 1 so that an odd nibble gets rounded up to even
853 	 * before dividing by 2, then divide by two to get # octets
854 	 */
855 	i = (int)(*ptr >> 4) + (int)(*ptr&0xf);
856 	i++;
857 	ptr += i >> 1;
858 	ptr ++; /* plus one for the DTE lengths byte */
859 
860 	/* ptr now is at facil_length field */
861 	facil_len = *ptr++;
862 	facil_lim = ptr + facil_len;
863 	IFDEBUG(D_CADDR)
864 		printf("parse_facils: facil length is  0x%x\n", (int) facil_len);
865 	ENDDEBUG
866 
867 	while (ptr <= facil_lim) {
868 		/* get NSAP addresses from facilities */
869 		switch (*ptr++) {
870 			case 0xcb:
871 				/* calling NSAP */
872 				facil_param_len = FACILtoNSAP(isop->isop_faddr, ptr);
873 				break;
874 			case 0xc9:
875 				/* called NSAP */
876 				facil_param_len = FACILtoNSAP(isop->isop_laddr, ptr);
877 				break;
878 
879 				/* from here to default are legit cases that I ignore */
880 				/* variable length */
881 			case 0xca:  /* end-to-end transit delay negot */
882 			case 0xc6:  /* network user id */
883 			case 0xc5: 	/* charging info : indicating monetary unit */
884 			case 0xc2: 	/* charging info : indicating segment count */
885 			case 0xc1: 	/* charging info : indicating call duration */
886 			case 0xc4: 	/* RPOA extended format */
887 			case 0xc3: 	/* call redirection notification */
888 				facil_param_len = 0;
889 				break;
890 
891 				/* 1 octet */
892 			case 0x0a:  /* min. throughput class negot */
893 			case 0x02:  /* throughput class */
894 			case 0x03:  case 0x47:  /* CUG shit */
895 			case 0x0b:  /* expedited data negot */
896 			case 0x01:  /* Fast select or reverse charging
897 						(example of intelligent protocol design) */
898 			case 0x04: 	/* charging info : requesting service */
899 			case 0x08: 	/* called line addr modified notification */
900 				facil_param_len = 1;
901 				break;
902 
903 				/* any 2 octets */
904 			case 0x42:  /* pkt size */
905 			case 0x43:  /* win size */
906 			case 0x44:  /* RPOA basic format */
907 			case 0x41:  /* bilateral CUG shit */
908 			case 0x49: 	/* transit delay selection and indication */
909 				facil_param_len = 2;
910 				break;
911 
912 				/* don't have any 3 octets */
913 				/*
914 				facil_param_len = 3;
915 				*/
916 			default:
917 				printf(
918 "BOGUS FACILITY CODE facil_len 0x%x *facil_len 0x%x, ptr 0x%x *ptr 0x%x\n",
919 					ptr, facil_len, ptr - 1, ptr[-1]);
920 				/* facil that we don't handle */
921 				return E_CO_HLI_REJI;
922 		}
923 		if (facil_param_len == -1)
924 			return E_CO_REG_ICDA;
925 		if (facil_param_len == 0) /* variable length */
926 			facil_param_len = (int)*ptr; /* 1 + the real facil param */
927 		ptr += facil_param_len;
928 	}
929 	return 0;
930 }
931 
932 #endif TPCONS
933