initial working version of test programs
[unix-history] / usr / src / sys / netccitt / pk_input.c
CommitLineData
51386eb2
KS
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 *
1c41f5e9 12 * @(#)pk_input.c 7.6 (Berkeley) %G%
51386eb2 13 */
7f874860 14
4507dea2
KS
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"
7f874860
KS
22
23#include "../net/if.h"
24
4507dea2
KS
25#include "x25.h"
26#include "pk.h"
27#include "pk_var.h"
7f874860 28
7f874860
KS
29/*
30 * This procedure is called by the link level whenever the link
31 * becomes operational, is reset, or when the link goes down.
32 */
33
1c41f5e9
KS
34pk_ctlinput (code, xcp)
35register struct x25config *xcp;
7f874860 36{
7f874860 37
1c41f5e9
KS
38 register struct pkcb *pkp;
39
40 for (pkp = pkcbhead; pkp; pkp = pkp -> pk_next)
41 if (pkp -> pk_xcp == xcp)
42 break;
43
ffababe5 44 if (pkp == 0)
7f874860 45 return (EINVAL);
1c41f5e9 46
7f874860
KS
47 switch (code) {
48 case PRC_LINKUP:
49 if (pkp -> pk_state == DTE_WAITING)
50 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
51 break;
52
53 case PRC_LINKDOWN:
54 pk_restart (pkp, -1); /* Clear all active circuits */
55 pkp -> pk_state = DTE_WAITING;
56 break;
57
58 case PRC_LINKRESET:
59 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
60 break;
61
62 }
63 return (0);
64}
1c41f5e9
KS
65struct ifqueue pkintrq;
66/*
67 * This routine is called if there are semi-smart devices that do HDLC
68 * in hardware and want to queue the packet and call level 3 directly
69 */
70pkintr ()
71{
72 register struct mbuf *m;
73 register struct ifaddr *ifa;
74 register struct ifnet *ifp;
75 register int s;
76
77 for (;;) {
78 s = splimp ();
79 IF_DEQUEUE (&pkintrq, m);
80 splx (s);
81 if (m == 0)
82 break;
83 if (m->m_len < PKHEADERLN) {
84 printf ("pkintr: packet too short (len=%d)\n",
85 m->m_len);
86 m_freem (m);
87 continue;
88 }
89 if ((m->m_flags & M_PKTHDR) == 0)
90 panic("pkintr");
91 ifp = m->m_pkthdr.rcvif;
92 /*
93 * look up the appropriate control block
94 */
95 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
96 if (ifa->ifa_addr->sa_family == AF_CCITT)
97 break;
98 if (ifa == 0)
99 continue;
100 pk_input(m, ((struct x25_ifaddr *)ifa)->ia_xcp);
101 }
102}
103struct mbuf *pk_bad_packet;
7f874860
KS
104/*
105 * X.25 PACKET INPUT
106 *
107 * This procedure is called by a link level procedure whenever
108 * an information frame is received. It decodes the packet and
109 * demultiplexes based on the logical channel number.
110 *
111 */
112
113pk_input (m, xcp)
114register struct mbuf *m;
115struct x25config *xcp;
116{
117 register struct x25_packet *xp;
118 register struct pklcd *lcp;
119 register struct socket *so = 0;
120 register struct pkcb *pkp;
121 int ptype, lcn, lcdstate = LISTEN;
122 static struct x25config *lastxcp;
123 static struct pkcb *lastpkp;
124
125 if (xcp == lastxcp)
126 pkp = lastpkp;
127 else {
128 for (pkp = pkcbhead; ; pkp = pkp -> pk_next) {
129 if (pkp == 0) {
130 pk_message (0, xcp, "pk_input: unknown network");
131 m_freem (m);
132 return;
133 }
134 if (pkp -> pk_xcp == xcp)
135 break;
136 }
137 lastxcp = xcp;
138 lastpkp = pkp;
139 }
140
141 xp = mtod (m, struct x25_packet *);
142 ptype = pk_decode (xp);
143 lcn = xp -> logical_channel_number;
144 lcp = pkp -> pk_chan[lcn];
145
146 /*
147 * If the DTE is in Restart state, then it will ignore data,
148 * interrupt, call setup and clearing, flow control and reset
149 * packets.
150 */
151 if (lcn < 0 || lcn > pkp -> pk_maxlcn) {
152 pk_message (lcn, pkp -> pk_xcp, "illegal lcn");
153 m_freem (m);
154 return;
155 }
156
157 pk_trace (pkp -> pk_xcp, xp, "P-In");
158
159 if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) {
160 m_freem (m);
161 return;
162 }
163 if (lcp) {
164 so = lcp -> lcd_so;
165 lcdstate = lcp -> lcd_state;
166 } else {
167 if (ptype == CLEAR) { /* idle line probe (Datapac specific) */
168 /* send response on lcd 0's output queue */
169 lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM);
170 pk_output (lcp);
171 m_freem (m);
172 return;
173 }
174 if (ptype != CALL)
175 ptype = INVALID_PACKET;
176 }
177
178 if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) {
1c41f5e9
KS
179 pk_message (0, pkp -> pk_xcp, "illegal ptype (%d, %s) on lcn 0",
180 ptype, pk_name[ptype / MAXSTATES]);
181 if (pk_bad_packet)
182 m_freem (pk_bad_packet);
183 pk_bad_packet = m;
7f874860
KS
184 return;
185 }
186
187 switch (ptype + lcdstate) {
188 /*
189 * Incoming Call packet received.
190 */
191 case CALL + LISTEN:
192 incoming_call (pkp, xp, m -> m_len);
193 break;
194
195 /*
196 * Call collision: Just throw this "incoming call" away since
197 * the DCE will ignore it anyway.
198 */
199 case CALL + SENT_CALL:
200 pk_message ((int)xp -> logical_channel_number, pkp -> pk_xcp,
201 "incoming call collision");
202 break;
203
204 /*
205 * Call confirmation packet received. This usually means our
206 * previous connect request is now complete.
207 */
208 case CALL_ACCEPTED + SENT_CALL:
209 call_accepted (lcp, xp, m -> m_len);
210 break;
211
212 /*
213 * This condition can only happen if the previous state was
214 * SENT_CALL. Just ignore the packet, eventually a clear
215 * confirmation should arrive.
216 */
217 case CALL_ACCEPTED + SENT_CLEAR:
218 break;
219
220 /*
221 * Clear packet received. This requires a complete tear down
222 * of the virtual circuit. Free buffers and control blocks.
223 * and send a clear confirmation.
224 */
225 case CLEAR + READY:
226 case CLEAR + RECEIVED_CALL:
227 case CLEAR + SENT_CALL:
228 case CLEAR + DATA_TRANSFER:
229 lcp -> lcd_state = RECEIVED_CLEAR;
230 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM);
231 pk_output (lcp);
232 pk_clearcause (pkp, xp);
233 pk_close (lcp);
234 break;
235
236 /*
237 * Clear collision: Treat this clear packet as a confirmation.
238 */
239 case CLEAR + SENT_CLEAR:
240 pk_close (lcp);
241 break;
242
243 /*
244 * Clear confirmation received. This usually means the virtual
245 * circuit is now completely removed.
246 */
247 case CLEAR_CONF + SENT_CLEAR:
248 pk_close (lcp);
249 break;
250
251 /*
252 * A clear confirmation on an unassigned logical channel - just
253 * ignore it. Note: All other packets on an unassigned channel
254 * results in a clear.
255 */
256 case CLEAR_CONF + READY:
257 break;
258
259 /*
260 * Data packet received. Pass on to next level. Move the Q and M
261 * bits into the data portion for the next level.
262 */
263 case DATA + DATA_TRANSFER:
264 if (lcp -> lcd_reset_condition) {
265 ptype = DELETE_PACKET;
266 break;
267 }
268
269 /*
270 * Process the P(S) flow control information in this Data packet.
271 * Check that the packets arrive in the correct sequence and that
272 * they are within the "lcd_input_window". Input window rotation is
273 * initiated by the receive interface.
274 */
275
276 if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) ||
277 PS(xp) == ((lcp -> lcd_input_window + lcp->lcd_windowsize) % MODULUS)) {
278 m_freem (m);
279 pk_procerror (RESET, lcp, "p(s) flow control error");
280 break;
281 }
282 lcp -> lcd_rsn = PS(xp);
283
284 if (pk_ack (lcp, PR(xp)) != PACKET_OK) {
285 m_freem (m);
286 break;
287 }
1c41f5e9
KS
288 if (so == 0)
289 break;
b84e7ca8 290 m -> m_data += PKHEADERLN;
7f874860
KS
291 m -> m_len -= PKHEADERLN;
292 if (lcp -> lcd_flags & X25_MQBIT) {
293 octet *t;
294
b84e7ca8 295 m -> m_data -= 1;
7f874860
KS
296 m -> m_len += 1;
297 t = mtod (m, octet *);
298 *t = 0x00;
299 if (xp -> q_bit)
300 *t |= 0x80;
301 if (MBIT(xp))
302 *t |= 0x40;
303 }
304
305 /*
306 * Discard Q-BIT packets if the application
307 * doesn't want to be informed of M and Q bit status
308 */
309 if (xp -> q_bit && (lcp -> lcd_flags & X25_MQBIT) == 0) {
310 m_freem (m);
311 lcp -> lcd_rxcnt++;
312 /*
313 * NB. This is dangerous: sending a RR here can
314 * cause sequence number errors if a previous data
315 * packet has not yet been passed up to the application
316 * (RR's are normally generated via PRU_RCVD).
317 */
318 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR);
319 pk_output (lcp);
320 } else {
321#ifdef BSD4_3
322 sbappendrecord (&so -> so_rcv, m);
323#else
324 sbappend (&so -> so_rcv, m);
325#endif
326 sorwakeup (so);
327 }
328 break;
329
330 /*
331 * Interrupt packet received.
332 */
333 case INTERRUPT + DATA_TRANSFER:
334 if (lcp -> lcd_reset_condition)
335 break;
336 lcp -> lcd_intrdata = xp -> packet_data;
7f874860
KS
337 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM);
338 pk_output (lcp);
1c41f5e9
KS
339 MCHTYPE(m, MT_OOBDATA);
340 if (so)
341 sohasoutofband (so);
7f874860
KS
342 break;
343
344 /*
345 * Interrupt confirmation packet received.
346 */
347 case INTERRUPT_CONF + DATA_TRANSFER:
348 if (lcp -> lcd_reset_condition)
349 break;
350 if (lcp -> lcd_intrconf_pending == TRUE)
351 lcp -> lcd_intrconf_pending = FALSE;
352 else
353 pk_procerror (RESET, lcp, "unexpected packet");
1c41f5e9 354 MCHTYPE(m, MT_CONTROL);
7f874860
KS
355 break;
356
357 /*
358 * Receiver ready received. Rotate the output window and output
359 * any data packets waiting transmission.
360 */
361 case RR + DATA_TRANSFER:
1c41f5e9
KS
362 if (lcp -> lcd_reset_condition ||
363 pk_ack (lcp, PR(xp)) != PACKET_OK) {
364 ptype = DELETE_PACKET;
7f874860 365 break;
1c41f5e9 366 }
7f874860
KS
367 if (lcp -> lcd_rnr_condition == TRUE)
368 lcp -> lcd_rnr_condition = FALSE;
369 pk_output (lcp);
1c41f5e9 370 MCHTYPE(m, MT_CONTROL);
7f874860
KS
371 break;
372
373 /*
374 * Receiver Not Ready received. Packets up to the P(R) can be
375 * be sent. Condition is cleared with a RR.
376 */
377 case RNR + DATA_TRANSFER:
1c41f5e9
KS
378 if (lcp -> lcd_reset_condition ||
379 pk_ack (lcp, PR(xp)) != PACKET_OK) {
380 ptype = DELETE_PACKET;
7f874860 381 break;
1c41f5e9 382 }
7f874860 383 lcp -> lcd_rnr_condition = TRUE;
1c41f5e9 384 MCHTYPE(m, MT_CONTROL);
7f874860
KS
385 break;
386
387 /*
388 * Reset packet received. Set state to FLOW_OPEN. The Input and
389 * Output window edges ar set to zero. Both the send and receive
390 * numbers are reset. A confirmation is returned.
391 */
392 case RESET + DATA_TRANSFER:
393 if (lcp -> lcd_reset_condition)
394 /* Reset collision. Just ignore packet. */
395 break;
396
397 pk_resetcause (pkp, xp);
7f874860
KS
398 lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
399 lcp -> lcd_intrconf_pending = FALSE;
400 lcp -> lcd_output_window = lcp -> lcd_input_window =
401 lcp -> lcd_last_transmitted_pr = 0;
402 lcp -> lcd_ssn = 0;
403 lcp -> lcd_rsn = MODULUS - 1;
404
405 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM);
406 pk_output (lcp);
1c41f5e9
KS
407
408 MCHTYPE(m, MT_CONTROL);
409 if (so == 0)
410 break;
411 sbflush (&so -> so_snd);
412 sbflush (&so -> so_rcv);
413 wakeup ((caddr_t) & so -> so_timeo);
414 sorwakeup (so);
415 sowwakeup (so);
7f874860
KS
416 break;
417
418 /*
419 * Reset confirmation received.
420 */
421 case RESET_CONF + DATA_TRANSFER:
422 if (lcp -> lcd_reset_condition) {
423 lcp -> lcd_reset_condition = FALSE;
424 pk_output (lcp);
425 }
426 else
427 pk_procerror (RESET, lcp, "unexpected packet");
1c41f5e9 428 MCHTYPE(m, MT_CONTROL);
7f874860
KS
429 break;
430
431 case DATA + SENT_CLEAR:
432 ptype = DELETE_PACKET;
433 case RR + SENT_CLEAR:
434 case RNR + SENT_CLEAR:
435 case INTERRUPT + SENT_CLEAR:
436 case INTERRUPT_CONF + SENT_CLEAR:
437 case RESET + SENT_CLEAR:
438 case RESET_CONF + SENT_CLEAR:
1c41f5e9 439 /* Just ignore p if we have sent a CLEAR already.
7f874860
KS
440 */
441 break;
442
443 /*
444 * Restart sets all the permanent virtual circuits to the "Data
445 * Transfer" stae and all the switched virtual circuits to the
446 * "Ready" state.
447 */
448 case RESTART + READY:
449 switch (pkp -> pk_state) {
450 case DTE_SENT_RESTART:
451 /* Restart collision. */
452 pkp -> pk_state = DTE_READY;
453 pk_message (0, pkp -> pk_xcp,
454 "Packet level operational");
455 break;
456
457 default:
458 pk_restart (pkp, -1);
459 pk_restartcause (pkp, xp);
460 pkp -> pk_chan[0] -> lcd_template = pk_template (0,
461 X25_RESTART_CONFIRM);
462 pk_output (pkp -> pk_chan[0]);
463 }
464 break;
465
466 /*
467 * Restart confirmation received. All logical channels are set
468 * to READY.
469 */
470 case RESTART_CONF + READY:
471 switch (pkp -> pk_state) {
472 case DTE_SENT_RESTART:
473 pkp -> pk_state = DTE_READY;
474 pk_message (0, pkp -> pk_xcp,
475 "Packet level operational");
476 break;
477
478 default:
479 /* Restart local procedure error. */
480 pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR);
481 pkp -> pk_state = DTE_SENT_RESTART;
482 }
483 break;
484
485 default:
486 if (lcp) {
487 pk_procerror (CLEAR, lcp, "unknown packet error");
488 pk_message (lcn, pkp -> pk_xcp,
489 "\"%s\" unexpected in \"%s\" state",
490 pk_name[ptype/MAXSTATES], pk_state[lcdstate]);
491 }
492 else /* Packets arrived on an unassigned channel.
493 */
494 pk_message ((int)xp->logical_channel_number, pkp -> pk_xcp,
495 "packet arrived on unassigned lcn");
496 break;
497 }
1c41f5e9
KS
498 if (so == 0 && lcdstate == DATA_TRANSFER && lcp -> lcd_upper)
499 lcp -> lcd_upper (lcp, m);
500 else if (ptype != DATA)
7f874860
KS
501 m_freem (m);
502}
503
504
505/*
506 * This routine handles incoming call packets. It matches the protocol
507 * field on the Call User Data field (usually the first four bytes) with
508 * sockets awaiting connections.
509 */
510
511static
512incoming_call (pkp, xp, len)
513struct pkcb *pkp;
514struct x25_packet *xp;
515{
ffababe5 516 register struct pklcd *lcp = 0, *l;
7f874860
KS
517 register struct sockaddr_x25 *sa;
518 register struct x25_calladdr *a;
ffababe5 519 register struct socket *so = 0;
7f874860
KS
520 struct mbuf *m;
521 register int l1, l2;
522 char *e, *errstr = "server unavailable";
523 octet *u;
524 int lcn = xp -> logical_channel_number;
525
526 /* First, copy the data from the incoming call packet to a X25_socket
527 descriptor. */
528
529 a = (struct x25_calladdr *) &xp -> packet_data;
530 l1 = a -> calling_addrlen;
531 l2 = a -> called_addrlen;
1c41f5e9 532 if ((m = m_getclr (M_DONTWAIT, MT_SONAME)) == 0)
7f874860
KS
533 return;
534 sa = mtod (m, struct sockaddr_x25 *);
535 u = (octet *) (a -> address_field + l2 / 2);
536 e = sa -> x25_addr;
537 if (l2 & 0x01) {
538 *e++ = *u++ & 0x0f;
539 l1--;
540 }
541 from_bcd (e, &u, l1);
542 if (l1 & 0x01)
543 u++;
544
545 parse_facilities (u, sa);
546 u += *u + 1;
547 sa -> x25_udlen = min (16, ((octet *)xp) + len - u);
548 if (sa -> x25_udlen < 0)
549 sa -> x25_udlen = 0;
550 bcopy ((caddr_t)u, sa -> x25_udata, (unsigned)sa -> x25_udlen);
551
552 /*
553 * Now, loop through the listen sockets looking for a match on the
554 * PID. That is the first four octets of the user data field. This
555 * is the closest thing to a port number for X.25 packets. What it
556 * does provide is away of multiplexing services at the user level.
557 */
558
559 for (l = pk_listenhead; l; l = l -> lcd_listen) {
560 struct sockaddr_x25 *sxp = l -> lcd_ceaddr;
561
562 if (bcmp (sxp -> x25_udata, sa -> x25_udata, sxp->x25_udlen))
563 continue;
4507dea2
KS
564 if (sxp -> x25_net &&
565 sxp -> x25_net != pkp->pk_xc.xc_addr.x25_net)
7f874860
KS
566 continue;
567 /*
568 * don't accept incoming collect calls unless
569 * the server sets the reverse charging option.
570 */
571 if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 &&
572 sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) {
573 errstr = "incoming collect call refused";
574 break;
575 }
ffababe5 576 if (l -> lcd_so) {
4507dea2 577 if (so = sonewconn (l -> lcd_so, SS_ISCONNECTED))
ffababe5
KS
578 lcp = (struct pklcd *) so -> so_pcb;
579 } else
580 lcp = pk_attach((struct socket *) 0);
581 if (lcp == 0) {
7f874860
KS
582 /*
583 * Insufficient space or too many unaccepted
584 * connections. Just throw the call away.
585 */
586 errstr = "server malfunction";
587 break;
588 }
b84e7ca8
KS
589 lcp -> lcd_upper = l -> lcd_upper;
590 lcp -> lcd_upnext = l -> lcd_upnext;
7f874860
KS
591 lcp -> lcd_lcn = lcn;
592 lcp -> lcd_state = RECEIVED_CALL;
593 lcp -> lcd_craddr = sa;
594 sa -> x25_opts.op_flags |= sxp -> x25_opts.op_flags &
595 ~X25_REVERSE_CHARGE;
596 pk_assoc (pkp, lcp, sa);
597 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED);
b84e7ca8
KS
598 if (so) {
599 pk_output (lcp);
ffababe5 600 soisconnected (so);
b84e7ca8 601 } else if (lcp->lcd_upper)
1c41f5e9 602 (*lcp->lcd_upper)(lcp, m);
7f874860
KS
603 return;
604 }
605
606 /*
607 * If the call fails for whatever reason, we still need to build a
608 * skeleton LCD in order to be able to properly receive the CLEAR
609 * CONFIRMATION.
610 */
611#ifdef WATERLOO /* be explicit */
612 if (l == 0 && bcmp(sa->x25_udata, "ean", 3) == 0)
613 pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s",
614 sa->x25_addr, sa->x25_udata[3] & 0xff, errstr);
615 else if (l == 0 && bcmp(sa->x25_udata, "\1\0\0\0", 4) == 0)
616 pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s",
617 sa->x25_addr, errstr);
618 else
619#endif
620 pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s",
621 sa -> x25_addr, sa -> x25_udata[0] & 0xff,
622 sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff,
623 sa -> x25_udata[3] & 0xff, errstr);
1c41f5e9
KS
624 if ((lcp = pk_attach((struct socket *)0)) == 0) {
625 (void) m_free (m);
7f874860
KS
626 return;
627 }
7f874860
KS
628 lcp -> lcd_lcn = lcn;
629 lcp -> lcd_state = RECEIVED_CALL;
630 pk_assoc (pkp, lcp, sa);
1c41f5e9 631 (void) m_free (m);
7f874860
KS
632 pk_clear (lcp);
633}
634
635static
636call_accepted (lcp, xp, len)
637struct pklcd *lcp;
638struct x25_packet *xp;
639{
640 register struct x25_calladdr *ap;
641 register octet *fcp;
642
643 lcp -> lcd_state = DATA_TRANSFER;
1c41f5e9
KS
644 if (lcp -> lcd_so)
645 soisconnected (lcp -> lcd_so);
7f874860
KS
646 if (len > 3) {
647 ap = (struct x25_calladdr *) &xp -> packet_data;
648 fcp = (octet *) ap -> address_field + (ap -> calling_addrlen +
649 ap -> called_addrlen + 1) / 2;
650 if (fcp + *fcp <= ((octet *)xp) + len)
651 parse_facilities (fcp, lcp -> lcd_ceaddr);
652 }
653 pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr);
654}
655
656static
657parse_facilities (fcp, sa)
658register octet *fcp;
659register struct sockaddr_x25 *sa;
660{
661 register octet *maxfcp;
662
663 maxfcp = fcp + *fcp;
664 fcp++;
665 while (fcp < maxfcp) {
666 /*
667 * Ignore national DCE or DTE facilities
668 */
669 if (*fcp == 0 || *fcp == 0xff)
670 break;
671 switch (*fcp) {
672 case FACILITIES_WINDOWSIZE:
673 sa -> x25_opts.op_wsize = fcp[1];
674 fcp += 3;
675 break;
676
677 case FACILITIES_PACKETSIZE:
678 sa -> x25_opts.op_psize = fcp[1];
679 fcp += 3;
680 break;
681
682 case FACILITIES_THROUGHPUT:
683 sa -> x25_opts.op_speed = fcp[1];
684 fcp += 2;
685 break;
686
687 case FACILITIES_REVERSE_CHARGE:
688 if (fcp[1] & 01)
689 sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE;
690 /*
691 * Datapac specific: for a X.25(1976) DTE, bit 2
692 * indicates a "hi priority" (eg. international) call.
693 */
694 if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0)
695 sa -> x25_opts.op_psize = X25_PS128;
696 fcp += 2;
697 break;
698
699 default:
700/*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/
701 switch ((*fcp & 0xc0) >> 6) {
702 case 0: /* class A */
703 fcp += 2;
704 break;
705
706 case 1:
707 fcp += 3;
708 break;
709
710 case 2:
711 fcp += 4;
712 break;
713
714 case 3:
715 fcp++;
716 fcp += *fcp;
717 }
718 }
719 }
720}
721
722from_bcd (a, x, len)
723register char *a;
724register octet **x;
725register int len;
726{
727 register int posn = 0;
728
729 while (--len >= 0) {
730 if (posn++ & 0x01)
731 *a = *(*x)++ & 0x0f;
732 else
733 *a = (**x >> 4) & 0x0F;
734 *a++ |= 0x30;
735 }
736}