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