xref: /csrg-svn/sys/netccitt/pk_subr.c (revision 42140)
1 /*
2  * Copyright (c) University of British Columbia, 1984
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Laboratory for Computation Vision and the Computer Science Department
8  * of the University of British Columbia.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)pk_subr.c	7.3 (Berkeley) 05/16/90
13  */
14 
15 #include "param.h"
16 #include "systm.h"
17 #include "mbuf.h"
18 #include "socket.h"
19 #include "protosw.h"
20 #include "socketvar.h"
21 #include "errno.h"
22 #include "time.h"
23 #include "kernel.h"
24 
25 #include "x25.h"
26 #include "pk.h"
27 #include "pk_var.h"
28 #include "x25err.h"
29 
30 int     pk_sendspace = 1024 * 2 + 8;
31 int     pk_recvspace = 1024 * 2 + 8;
32 
33 struct	x25_packet *pk_template ();
34 
35 /*
36  *  Attach X.25 protocol to socket, allocate logical channel descripter
37  *  and buffer space, and enter LISTEN state if we are to accept
38  *  IN-COMMING CALL packets.
39  *
40  */
41 
42 pk_attach (so)
43 struct socket *so;
44 {
45 	register struct pklcd *lcp;
46 	register struct mbuf *m;
47 	register int error;
48 
49 	if (error = soreserve (so, pk_sendspace, pk_recvspace))
50 		return (error);
51 
52 	/* Hopefully we can remove this when SEQ_PKT is available (4.3?) */
53 	so -> so_snd.sb_mbmax = pk_sendspace;
54 
55 	if ((m = m_getclr (M_DONTWAIT, MT_PCB)) == 0)
56 		return (ENOBUFS);
57 	lcp = mtod (m, struct pklcd *);
58 	so -> so_pcb = (caddr_t) lcp;
59 	lcp -> lcd_so = so;
60 
61 	if (so -> so_options & SO_ACCEPTCONN)
62 		lcp -> lcd_state = LISTEN;
63 	else
64 		lcp -> lcd_state = READY;
65 
66 	return (0);
67 }
68 
69 /*
70  *  Disconnect X.25 protocol from socket.
71  */
72 
73 pk_disconnect (lcp)
74 register struct pklcd *lcp;
75 {
76 	register struct socket *so = lcp -> lcd_so;
77 	register struct pklcd *l, *p;
78 
79 	switch (lcp -> lcd_state) {
80 	case LISTEN:
81 		for (p = 0, l = pk_listenhead; l && l != lcp; p = l, l = l -> lcd_listen);
82 		if (p == 0) {
83 			if (l != 0)
84 				pk_listenhead = l -> lcd_listen;
85 		}
86 		else
87 		if (l != 0)
88 			p -> lcd_listen = l -> lcd_listen;
89 		pk_close (lcp);
90 		break;
91 
92 	case READY:
93 		pk_acct (lcp);
94 		pk_close (lcp);
95 		break;
96 
97 	case SENT_CLEAR:
98 	case RECEIVED_CLEAR:
99 		break;
100 
101 	default:
102 		pk_acct (lcp);
103 		if (so) {
104 			soisdisconnecting (so);
105 			sbflush (&so -> so_rcv);
106 		}
107 		pk_clear (lcp);
108 
109 	}
110 }
111 
112 /*
113  *  Close an X.25 Logical Channel. Discard all space held by the
114  *  connection and internal descriptors. Wake up any sleepers.
115  */
116 
117 pk_close (lcp)
118 struct pklcd *lcp;
119 {
120 	register struct socket *so = lcp -> lcd_so;
121 
122 	pk_freelcd (lcp);
123 
124 	if (so == NULL)
125 		return;
126 
127 	so -> so_pcb = 0;
128 	sbflush (&so -> so_snd);
129 	sbflush (&so -> so_rcv);
130 	soisdisconnected (so);
131 	sofree (so);	/* gak!!! you can't do that here */
132 }
133 
134 /*
135  *  Create a template to be used to send X.25 packets on a logical
136  *  channel. It allocates an mbuf and fills in a skeletal packet
137  *  depending on its type. This packet is passed to pk_output where
138  *  the remainer of the packet is filled in.
139 */
140 
141 struct x25_packet *
142 pk_template (lcn, type)
143 int lcn, type;
144 {
145 	register struct mbuf *m;
146 	register struct x25_packet *xp;
147 
148 	MGET (m, M_DONTWAIT, MT_HEADER);
149 	if (m == 0)
150 		panic ("pk_template");
151 	m -> m_act = 0;
152 
153 	/*
154 	 * Efficiency hack: leave a four byte gap at the beginning
155 	 * of the packet level header with the hope that this will
156 	 * be enough room for the link level to insert its header.
157 	 */
158 	m -> m_data += 4;
159 	m -> m_len = PKHEADERLN;
160 
161 	xp = mtod (m, struct x25_packet *);
162 	*(long *)xp = 0;		/* ugly, but fast */
163 /*	xp -> q_bit = 0;*/
164 	xp -> fmt_identifier = 1;
165 /*	xp -> lc_group_number = 0;*/
166 
167 	xp -> logical_channel_number = lcn;
168 	xp -> packet_type = type;
169 
170 	return (xp);
171 }
172 
173 /*
174  *  This routine restarts all the virtual circuits. Actually,
175  *  the virtual circuits are not "restarted" as such. Instead,
176  *  any active switched circuit is simply returned to READY
177  *  state.
178  */
179 
180 pk_restart (pkp, restart_cause)
181 register struct pkcb *pkp;
182 int restart_cause;
183 {
184 	register struct x25_packet *xp;
185 	register struct pklcd *lcp;
186 	register int i;
187 
188 	/* Restart all logical channels. */
189 	if (pkp->pk_chan == 0)
190 		return;
191 	for (i = 1; i <= pkp->pk_maxlcn; ++i)
192 		if ((lcp = pkp->pk_chan[i]) != NULL) {
193 			if (lcp -> lcd_so)
194 				lcp->lcd_so -> so_error = ENETRESET;
195 			pk_close (lcp);
196 		}
197 
198 	if (restart_cause < 0)
199 		return;
200 
201 	pkp->pk_state = DTE_SENT_RESTART;
202 	lcp = pkp->pk_chan[0];
203 	xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESTART);
204 	(dtom (xp)) -> m_len++;
205 	xp -> packet_data = 0;	/* DTE only */
206 	pk_output (lcp);
207 }
208 
209 
210 /*
211  *  This procedure frees up the Logical Channel Descripter.
212  */
213 
214 static
215 pk_freelcd (lcp)
216 register struct pklcd *lcp;
217 {
218 	if (lcp == NULL)
219 		return;
220 
221 	if (lcp -> lcd_template)
222 		m_freem (dtom (lcp -> lcd_template));
223 
224 	if (lcp -> lcd_craddr)
225 		m_freem (dtom (lcp -> lcd_craddr));
226 
227 	if (lcp -> lcd_ceaddr)
228 		m_freem (dtom (lcp -> lcd_ceaddr));
229 
230 	if (lcp -> lcd_lcn > 0)
231 		lcp -> lcd_pkp -> pk_chan[lcp -> lcd_lcn] = NULL;
232 
233 	m_freem (dtom (lcp));
234 }
235 
236 
237 /*
238  *  Bind a address and protocol value to a socket.  The important
239  *  part is the protocol value - the first four characters of the
240  *  Call User Data field.
241  */
242 
243 pk_bind (lcp, nam)
244 struct pklcd *lcp;
245 struct mbuf *nam;
246 {
247 	register struct sockaddr_x25 *sa;
248 	register struct pkcb *pkp;
249 	register struct mbuf *m;
250 	register struct pklcd *pp;
251 
252 	if (nam == NULL)
253 		return (EADDRNOTAVAIL);
254 	if (lcp -> lcd_ceaddr)				/* XXX */
255 		return (EADDRINUSE);
256 	if (checksockaddr (nam))
257 		return (EINVAL);
258 	sa = mtod (nam, struct sockaddr_x25 *);
259 
260 	/*
261 	 * If the user wishes to accept calls only from a particular
262 	 * net (net != 0), make sure the net is known
263 	 */
264 
265 	if (sa -> x25_net)
266 		for (pkp = pkcbhead; ; pkp = pkp -> pk_next) {
267 			if (pkp == 0)
268 				return (ENETUNREACH);
269 			if (pkp -> pk_xcp -> xc_net == sa -> x25_net)
270 				break;
271 		}
272 
273 	for (pp = pk_listenhead; pp; pp = pp -> lcd_listen)
274 		if (bcmp (pp -> lcd_ceaddr -> x25_udata, sa -> x25_udata,
275 			min (pp->lcd_ceaddr->x25_udlen, sa->x25_udlen)) == 0)
276 			return (EADDRINUSE);
277 
278 	if ((m = m_copy (nam, 0, (int)M_COPYALL)) == 0)
279 		return (ENOBUFS);
280 	lcp -> lcd_ceaddr = mtod (m, struct sockaddr_x25 *);
281 	return (0);
282 }
283 
284 /*
285  * Associate a logical channel descriptor with a network.
286  * Fill in the default network specific parameters and then
287  * set any parameters explicitly specified by the user or
288  * by the remote DTE.
289  */
290 
291 pk_assoc (pkp, lcp, sa)
292 register struct pkcb *pkp;
293 register struct pklcd *lcp;
294 register struct sockaddr_x25 *sa;
295 {
296 
297 	lcp -> lcd_pkp = pkp;
298 	lcp -> lcd_packetsize = pkp -> pk_xcp -> xc_psize;
299 	lcp -> lcd_windowsize = pkp -> pk_xcp -> xc_pwsize;
300 	lcp -> lcd_rsn = MODULUS - 1;
301 	pkp -> pk_chan[lcp -> lcd_lcn] = lcp;
302 
303 	if (sa -> x25_opts.op_psize)
304 		lcp -> lcd_packetsize = sa -> x25_opts.op_psize;
305 	else
306 		sa -> x25_opts.op_psize = lcp -> lcd_packetsize;
307 	if (sa -> x25_opts.op_wsize)
308 		lcp -> lcd_windowsize = sa -> x25_opts.op_wsize;
309 	else
310 		sa -> x25_opts.op_wsize = lcp -> lcd_windowsize;
311 	sa -> x25_net = pkp -> pk_xcp -> xc_net;
312 	lcp -> lcd_flags = sa -> x25_opts.op_flags;
313 	lcp -> lcd_stime = time.tv_sec;
314 }
315 
316 pk_connect (lcp, nam)
317 register struct pklcd *lcp;
318 struct mbuf *nam;
319 {
320 	register struct pkcb *pkp;
321 	register struct sockaddr_x25 *sa;
322 	register struct mbuf *m;
323 
324 	if (checksockaddr (nam))
325 		return (EINVAL);
326 	sa = mtod (nam, struct sockaddr_x25 *);
327 	if (sa -> x25_addr[0] == '\0')
328 		return (EDESTADDRREQ);
329 	for (pkp = pkcbhead; ; pkp = pkp->pk_next) {
330 		if (pkp == 0)
331 			return (ENETUNREACH);
332 		/*
333 		 * use first net configured (last in list
334 		 * headed by pkcbhead) if net is zero
335 		 */
336 		if (sa -> x25_net == 0 && pkp -> pk_next == 0)
337 			break;
338 		if (sa -> x25_net == pkp -> pk_xcp -> xc_net)
339 			break;
340 	}
341 
342 	if (pkp -> pk_state != DTE_READY)
343 		return (ENETDOWN);
344 	if ((lcp -> lcd_lcn = pk_getlcn (pkp)) == 0)
345 		return (EMFILE);
346 	if ((m = m_copy (nam, 0, (int)M_COPYALL)) == 0)
347 		return (ENOBUFS);
348 	lcp -> lcd_ceaddr = mtod (m, struct sockaddr_x25 *);
349 	pk_assoc (pkp, lcp, lcp -> lcd_ceaddr);
350 	if (lcp -> so)
351 		soisconnecting (lcp -> lcd_so);
352 	lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL);
353 	pk_callrequest (lcp, m, pkp -> pk_xcp);
354 	pk_output (lcp);
355 	return (0);
356 }
357 
358 /*
359  *  Build the rest of the CALL REQUEST packet. Fill in calling
360  *  address, facilities fields and the user data field.
361  */
362 
363 pk_callrequest (lcp, nam, xcp)
364 struct pklcd *lcp;
365 struct mbuf *nam;
366 register struct x25config *xcp;
367 {
368 	register struct x25_calladdr *a;
369 	register struct sockaddr_x25 *sa = mtod (nam, struct sockaddr_x25 *);
370 	register struct mbuf *m = dtom (lcp -> lcd_template);
371 	unsigned posn = 0;
372 	octet *cp;
373 	char addr[sizeof (xcp -> xc_ntn) * 2];
374 
375 	a = (struct x25_calladdr *) &lcp -> lcd_template -> packet_data;
376 	a -> calling_addrlen = xcp -> xc_ntnlen;
377 	cp = (octet *) xcp -> xc_ntn;
378 	from_bcd (addr, &cp, xcp -> xc_ntnlen);
379 	a -> called_addrlen = strlen (sa -> x25_addr);
380 	cp = (octet *) a -> address_field;
381 	to_bcd (&cp, (int)a -> called_addrlen, sa -> x25_addr, &posn);
382 	to_bcd (&cp, (int)a -> calling_addrlen, addr, &posn);
383 	if (posn & 0x01)
384 		*cp++ &= 0xf0;
385 
386 	build_facilities (&cp, sa, (int)xcp -> xc_type);
387 
388 	bcopy (sa -> x25_udata, (caddr_t)cp, (unsigned)sa -> x25_udlen);
389 	cp += sa -> x25_udlen;
390 
391 	m -> m_len += cp - (octet *) a;
392 
393 #ifdef ANDREW
394 	printf ("call: ");
395 	for (cp = mtod (m, octet *), posn = 0; posn < m->m_len; ++posn)
396 		printf ("%x ", *cp++);
397 	printf ("\n");
398 #endif
399 }
400 
401 build_facilities (cp, sa, type)
402 register octet **cp;
403 struct sockaddr_x25 *sa;
404 {
405 	register octet *fcp;
406 	register int revcharge;
407 
408 	fcp = *cp + 1;
409 	revcharge = sa -> x25_opts.op_flags & X25_REVERSE_CHARGE ? 1 : 0;
410 	/*
411 	 * This is specific to Datapac X.25(1976) DTEs.  International
412 	 * calls must have the "hi priority" bit on.
413 	 */
414 	if (type == X25_1976 && sa -> x25_opts.op_psize == X25_PS128)
415 		revcharge |= 02;
416 	if (revcharge) {
417 		*fcp++ = FACILITIES_REVERSE_CHARGE;
418 		*fcp++ = revcharge;
419 	}
420 	switch (type) {
421 	case X25_1980:
422 	case X25_1984:
423 		*fcp++ = FACILITIES_PACKETSIZE;
424 		*fcp++ = sa -> x25_opts.op_psize;
425 		*fcp++ = sa -> x25_opts.op_psize;
426 
427 		*fcp++ = FACILITIES_WINDOWSIZE;
428 		*fcp++ = sa -> x25_opts.op_wsize;
429 		*fcp++ = sa -> x25_opts.op_wsize;
430 	}
431 	**cp = fcp - *cp - 1;
432 	*cp = fcp;
433 }
434 
435 to_bcd (a, len, x, posn)
436 register octet **a;
437 register char *x;
438 register int len;
439 register unsigned *posn;
440 {
441 	while (--len >= 0)
442 		if ((*posn)++ & 0x01)
443 			*(*a)++ |= *x++ & 0x0F;
444 		else
445 			**a = *x++ << 4;
446 }
447 
448 /*
449  *  This routine gets the  first available logical channel number.  The
450  *  search is from the highest number to lowest number (DTE).
451  */
452 
453 pk_getlcn (pkp)
454 register struct pkcb *pkp;
455 {
456 	register int i;
457 
458 	if (pkp->pk_chan == 0)
459 		return (0);
460 	for (i = pkp -> pk_maxlcn; i > 0; --i)
461 		if (pkp -> pk_chan[i] == NULL)
462 			break;
463 	return (i);
464 
465 }
466 
467 static
468 checksockaddr (m)
469 struct mbuf *m;
470 {
471 	register struct sockaddr_x25 *sa = mtod (m, struct sockaddr_x25 *);
472 	register char *cp;
473 
474 	if (m -> m_len != sizeof (struct sockaddr_x25))
475 		return (1);
476 	if (sa -> x25_family != AF_CCITT || sa -> x25_udlen == 0 ||
477 		sa -> x25_udlen > sizeof (sa -> x25_udata))
478 		return (1);
479 	for (cp = sa -> x25_addr; *cp; cp++) {
480 		if (*cp < '0' || *cp > '9' ||
481 			cp >= &sa -> x25_addr[sizeof (sa -> x25_addr) - 1])
482 			return (1);
483 	}
484 	return (0);
485 }
486 
487 /*
488  *  This procedure sends a CLEAR request packet. The lc state is
489  *  set to "SENT_CLEAR".
490  */
491 
492 pk_clear (lcp)
493 struct pklcd *lcp;
494 {
495 	register struct x25_packet *xp;
496 
497 	xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR);
498 	(dtom (xp)) -> m_len++;
499 	xp -> packet_data = 0;
500 
501 	pk_output (lcp);
502 
503 }
504 
505 /*
506  *  This procedure sends a RESET request packet. It re-intializes
507  *  virtual circuit.
508  */
509 
510 static
511 pk_reset (lcp)
512 register struct pklcd *lcp;
513 {
514 	register struct x25_packet *xp;
515 	register struct socket *so;
516 
517 	if (lcp -> lcd_state != DATA_TRANSFER)
518 		return;
519 
520 	lcp -> lcd_reset_condition = TRUE;
521 
522 	/* Reset all the control variables for the channel. */
523 	lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
524 		lcp -> lcd_intrconf_pending = FALSE;
525 	lcp -> lcd_rsn = MODULUS - 1;
526 	lcp -> lcd_ssn = 0;
527 	lcp -> lcd_output_window = lcp -> lcd_input_window =
528 		lcp -> lcd_last_transmitted_pr = 0;
529 	if (so = lcp -> lcd_so)  {
530 		so -> so_error = ECONNRESET;
531 		sbflush (&so -> so_rcv);
532 		sbflush (&so -> so_snd);
533 	}
534 	xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET);
535 	(dtom (xp)) -> m_len += 2;
536 	xp -> packet_data = 0;
537 	pk_output (lcp);
538 
539 }
540 
541 
542 /*
543  *  This procedure handles all local protocol procedure errors.
544  */
545 
546 pk_procerror (error, lcp, errstr)
547 register struct pklcd *lcp;
548 char *errstr;
549 {
550 
551 	pk_message (lcp -> lcd_lcn, lcp -> lcd_pkp -> pk_xcp, errstr);
552 
553 	switch (error) {
554 	case CLEAR:
555 		if (lcp->lcd_so) {
556 			lcp->lcd_so -> so_error = ECONNABORTED;
557 			soisdisconnecting (lcp->lcd_so);
558 		}
559 		pk_clear (lcp);
560 		break;
561 
562 	case RESET:
563 		pk_reset (lcp);
564 	}
565 }
566 
567 /*
568  *  This procedure is called during the DATA TRANSFER state to check
569  *  and  process  the P(R) values  received  in the DATA,  RR OR RNR
570  *  packets.
571  */
572 
573 pk_ack (lcp, pr)
574 struct pklcd *lcp;
575 unsigned pr;
576 {
577 	register struct socket *so = lcp -> lcd_so;
578 
579 	if (lcp -> lcd_output_window == pr)
580 		return (PACKET_OK);
581 	if (lcp -> lcd_output_window < lcp -> lcd_ssn) {
582 		if (pr < lcp -> lcd_output_window || pr > lcp -> lcd_ssn) {
583 			pk_procerror (RESET, lcp, "p(r) flow control error");
584 			return (ERROR_PACKET);
585 		}
586 	}
587 	else {
588 		if (pr < lcp -> lcd_output_window && pr > lcp -> lcd_ssn) {
589 			pk_procerror (RESET, lcp, "p(r) flow control error");
590 			return (ERROR_PACKET);
591 		}
592 	}
593 
594 	lcp -> lcd_output_window = pr;		/* Rotate window. */
595 	if (lcp -> lcd_window_condition == TRUE)
596 		lcp -> lcd_window_condition = FALSE;
597 
598 	if (so && ((so -> so_snd.sb_flags & SB_WAIT) || so -> so_snd.sb_sel))
599 		sowwakeup (so);
600 	if (lcp -> lcd_downq.pq_unblock)
601 		(*lcp -> lcd_downq.pq_unblock)(lcp);
602 
603 	return (PACKET_OK);
604 }
605 
606 /*
607  *  This procedure decodes the X.25 level 3 packet returning a
608  *  code to be used in switchs or arrays.
609  */
610 
611 pk_decode (xp)
612 register struct x25_packet *xp;
613 {
614 	register int type;
615 
616 	if (xp -> fmt_identifier != 1)
617 		return (INVALID_PACKET);
618 
619 	/*
620 	 *  Make sure that the logical channel group number is 0.
621 	 *  This restriction may be removed at some later date.
622 	 */
623 	if (xp -> lc_group_number != 0)
624 		return (INVALID_PACKET);
625 
626 	/*
627 	 *  Test for data packet first.
628 	 */
629 	if (!(xp -> packet_type & DATA_PACKET_DESIGNATOR))
630 		return (DATA);
631 
632 	/*
633 	 *  Test if flow control packet (RR or RNR).
634 	 */
635 	if (!(xp -> packet_type & RR_OR_RNR_PACKET_DESIGNATOR))
636 		if (!(xp -> packet_type & RR_PACKET_DESIGNATOR))
637 			return (RR);
638 		else
639 			return (RNR);
640 
641 	/*
642 	 *  Determine the rest of the packet types.
643 	 */
644 	switch (xp -> packet_type) {
645 	case X25_CALL:
646 		type = CALL;
647 		break;
648 
649 	case X25_CALL_ACCEPTED:
650 		type = CALL_ACCEPTED;
651 		break;
652 
653 	case X25_CLEAR:
654 		type = CLEAR;
655 		break;
656 
657 	case X25_CLEAR_CONFIRM:
658 		type = CLEAR_CONF;
659 		break;
660 
661 	case X25_INTERRUPT:
662 		type = INTERRUPT;
663 		break;
664 
665 	case X25_INTERRUPT_CONFIRM:
666 		type = INTERRUPT_CONF;
667 		break;
668 
669 	case X25_RESET:
670 		type = RESET;
671 		break;
672 
673 	case X25_RESET_CONFIRM:
674 		type = RESET_CONF;
675 		break;
676 
677 	case X25_RESTART:
678 		type = RESTART;
679 		break;
680 
681 	case X25_RESTART_CONFIRM:
682 		type = RESTART_CONF;
683 		break;
684 
685 	default:
686 		type = INVALID_PACKET;
687 	}
688 	return (type);
689 }
690 
691 /*
692  *  A restart packet has been received. Print out the reason
693  *  for the restart.
694  */
695 
696 pk_restartcause (pkp, xp)
697 struct pkcb *pkp;
698 register struct x25_packet *xp;
699 {
700 	register struct x25config *xcp = pkp -> pk_xcp;
701 	register int lcn = xp -> logical_channel_number;
702 
703 	switch (xp -> packet_data) {
704 	case X25_RESTART_LOCAL_PROCEDURE_ERROR:
705 		pk_message (lcn, xcp, "restart: local procedure error");
706 		break;
707 
708 	case X25_RESTART_NETWORK_CONGESTION:
709 		pk_message (lcn, xcp, "restart: network congestion");
710 		break;
711 
712 	case X25_RESTART_NETWORK_OPERATIONAL:
713 		pk_message (lcn, xcp, "restart: network operational");
714 		break;
715 
716 	default:
717 		pk_message (lcn, xcp, "restart: unknown cause");
718 	}
719 }
720 
721 #define MAXRESETCAUSE	7
722 
723 int     Reset_cause[] = {
724 	EXRESET, EXROUT, 0, EXRRPE, 0, EXRLPE, 0, EXRNCG
725 };
726 
727 /*
728  *  A reset packet has arrived. Return the cause to the user.
729  */
730 
731 pk_resetcause (pkp, xp)
732 struct pkcb *pkp;
733 register struct x25_packet *xp;
734 {
735 	register struct pklcd *lcp = pkp->pk_chan[xp -> logical_channel_number];
736 	register int code = xp -> packet_data;
737 
738 	if (code > MAXRESETCAUSE)
739 		code = 7;	/* EXRNCG */
740 
741 	lcp->lcd_so -> so_error = Reset_cause[code];
742 }
743 
744 #define MAXCLEARCAUSE	25
745 
746 int     Clear_cause[] = {
747 	EXCLEAR, EXCBUSY, 0, EXCINV, 0, EXCNCG, 0,
748 	0, 0, EXCOUT, 0, EXCAB, 0, EXCNOB, 0, 0, 0, EXCRPE,
749 	0, EXCLPE, 0, 0, 0, 0, 0, EXCRRC
750 };
751 
752 /*
753  *  A clear packet has arrived. Return the cause to the user.
754  */
755 
756 pk_clearcause (pkp, xp)
757 struct pkcb *pkp;
758 register struct x25_packet *xp;
759 {
760 	register struct pklcd *lcp = pkp->pk_chan[xp -> logical_channel_number];
761 	register int code = xp -> packet_data;
762 
763 	if (code > MAXCLEARCAUSE)
764 		code = 5;	/* EXRNCG */
765 	lcp->lcd_so -> so_error = Clear_cause[code];
766 }
767 
768 char *
769 format_ntn (xcp)
770 register struct x25config *xcp;
771 {
772 	register int i;
773 	register char *src, *dest;
774 	static char ntn[12];
775 
776 	src = xcp->xc_ntn;
777 	dest = ntn;
778 	for (i = 0; i < xcp->xc_ntnlen / 2; i++) {
779 		*dest++ = ((*src & 0xf0) >> 4) + '0';
780 		*dest++ = (*src++ & 0xf) + '0';
781 	}
782 	if (xcp->xc_ntnlen & 01)
783 		dest[-1] = 0;
784 	else
785 		*dest = 0;
786 	return (ntn);
787 }
788 
789 /* VARARGS1 */
790 pk_message (lcn, xcp, fmt, a1, a2, a3, a4, a5, a6)
791 struct x25config *xcp;
792 char *fmt;
793 {
794 
795 	if (lcn)
796 		if (pkcbhead -> pk_next)
797 			printf ("X.25(%s): lcn %d: ", format_ntn (xcp), lcn);
798 		else
799 			printf ("X.25: lcn %d: ", lcn);
800 	else
801 		if (pkcbhead -> pk_next)
802 			printf ("X.25(%s): ", format_ntn (xcp));
803 		else
804 			printf ("X.25: ");
805 
806 	printf (fmt, a1, a2, a3, a4, a5, a6);
807 	printf ("\n");
808 }
809