bring manpage up to date
[unix-history] / usr / src / sys / netccitt / hd_input.c
CommitLineData
21e3a7bd
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 *
4507dea2 12 * @(#)hd_input.c 7.4 (Berkeley) %G%
21e3a7bd 13 */
0f9bb9b6 14
4507dea2
KS
15#include "param.h"
16#include "systm.h"
17#include "mbuf.h"
18#include "domain.h"
19#include "socket.h"
20#include "protosw.h"
21#include "errno.h"
22#include "time.h"
23#include "kernel.h"
0f9bb9b6
KS
24
25#include "../net/if.h"
26
4507dea2
KS
27#include "hdlc.h"
28#include "hd_var.h"
29#include "x25.h"
0f9bb9b6
KS
30
31/*
32 * HDLC INPUT INTERFACE
33 *
34 * This routine is called when the HDLC physical device has
35 * completed reading a frame.
36 */
37
38hdintr ()
39{
40 register struct mbuf *m;
41 register struct hdcb *hdp;
42 register struct ifnet *ifp;
43 register int s;
44 static struct ifnet *lastifp;
45 static struct hdcb *lasthdp;
46
47 for (;;) {
48 s = splimp ();
49 IF_DEQUEUEIF(&hdintrq, m, ifp);
50 splx (s);
51 if (m == 0)
52 break;
53 if (m->m_len < HDHEADERLN) {
54 printf ("hdintr: packet too short (len=%d)\n",
55 m->m_len);
56 m_freem (m);
57 continue;
58 }
59
60 /*
61 * look up the appropriate hdlc control block
62 */
63
64 if (ifp == lastifp)
65 hdp = lasthdp;
66 else {
67 for (hdp = hdcbhead; hdp; hdp = hdp->hd_next)
68 if (hdp->hd_ifp == ifp)
69 break;
70 if (hdp == 0) {
71 printf ("hdintr: unknown interface %x\n", ifp);
72 m_freem (m);
73 continue;
74 }
75 lastifp = ifp;
76 lasthdp = hdp;
77 }
78
79 /* Process_rxframe returns FALSE if the frame was NOT queued
80 for the next higher layers. */
81 if (process_rxframe (hdp, m) == FALSE)
82 m_freem (m);
83 }
84}
85
86process_rxframe (hdp, fbuf)
87register struct hdcb *hdp;
88register struct mbuf *fbuf;
89{
90 register int queued = FALSE, frametype, pf;
91 register struct Hdlc_frame *frame;
92
93 frame = mtod (fbuf, struct Hdlc_frame *);
94 pf = ((struct Hdlc_iframe *) frame) -> pf;
95
96 hd_trace (hdp, RX, frame);
97 if (frame -> address != ADDRESS_A && frame -> address != ADDRESS_B)
98 return (queued);
99
100 switch ((frametype = hd_decode (hdp, frame)) + hdp->hd_state) {
101 case DM + DISC_SENT:
102 case UA + DISC_SENT:
103 /*
104 * Link now closed. Leave timer running
105 * so hd_timer() can periodically check the
106 * status of interface driver flag bit IFF_UP.
107 */
108 hdp->hd_state = DISCONNECTED;
109 break;
110
111 case DM + INIT:
112 case UA + INIT:
113 /*
114 * This is a non-standard state change needed for DCEs
115 * that do dynamic link selection. We can't go into the
116 * usual "SEND DM" state because a DM is a SARM in LAP.
117 */
118 hd_writeinternal (hdp, SABM, POLLOFF);
119 hdp->hd_state = SABM_SENT;
120 SET_TIMER (hdp);
121 break;
122
123 case SABM + DM_SENT:
124 case SABM + WAIT_SABM:
125 hd_writeinternal (hdp, UA, pf);
126 case UA + SABM_SENT:
127 case UA + WAIT_UA:
128 KILL_TIMER (hdp);
129 hd_initvars (hdp);
130 hdp->hd_state = ABM;
131 hd_message (hdp, "Link level operational");
132 /* Notify the packet level - to send RESTART. */
133 (void) pk_ctlinput (PRC_LINKUP, hdp->hd_xcp);
134 break;
135
136 case SABM + SABM_SENT:
137 /* Got a SABM collision. Acknowledge the remote's SABM
138 via UA but still wait for UA. */
139 hd_writeinternal (hdp, UA, pf);
140 break;
141
142 case SABM + ABM:
143 /* Request to reset the link from the remote. */
144 KILL_TIMER (hdp);
145 hd_message (hdp, "Link reset");
146#ifdef HDLCDEBUG
147 hd_dumptrace (hdp);
148#endif
149 hd_flush (hdp->hd_ifp);
150 hd_writeinternal (hdp, UA, pf);
151 hd_initvars (hdp);
152 (void) pk_ctlinput (PRC_LINKRESET, hdp->hd_xcp);
153 hdp->hd_resets++;
154 break;
155
156 case SABM + WAIT_UA:
157 hd_writeinternal (hdp, UA, pf);
158 break;
159
160 case DM + ABM:
161 hd_message (hdp, "DM received: link down");
162#ifdef HDLCDEBUG
163 hd_dumptrace (hdp);
164#endif
165 (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
166 hd_flush (hdp->hd_ifp);
167 case DM + DM_SENT:
168 case DM + WAIT_SABM:
169 case DM + WAIT_UA:
170 hd_writeinternal (hdp, SABM, pf);
171 hdp->hd_state = SABM_SENT;
172 SET_TIMER (hdp);
173 break;
174
175 case DISC + INIT:
176 case DISC + DM_SENT:
177 case DISC + SABM_SENT:
178 /* Note: This is a non-standard state change. */
179 hd_writeinternal (hdp, UA, pf);
180 hd_writeinternal (hdp, SABM, POLLOFF);
181 hdp->hd_state = SABM_SENT;
182 SET_TIMER (hdp);
183 break;
184
185 case DISC + WAIT_UA:
186 hd_writeinternal (hdp, DM, pf);
187 SET_TIMER (hdp);
188 hdp->hd_state = DM_SENT;
189 break;
190
191 case DISC + ABM:
192 hd_message (hdp, "DISC received: link down");
193 (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
194 case DISC + WAIT_SABM:
195 hd_writeinternal (hdp, UA, pf);
196 hdp->hd_state = DM_SENT;
197 SET_TIMER (hdp);
198 break;
199
200 case UA + ABM:
201 hd_message (hdp, "UA received: link down");
202 (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
203 case UA + WAIT_SABM:
204 hd_writeinternal (hdp, DM, pf);
205 hdp->hd_state = DM_SENT;
206 SET_TIMER (hdp);
207 break;
208
209 case FRMR + DM_SENT:
210 hd_writeinternal (hdp, SABM, pf);
211 hdp->hd_state = SABM_SENT;
212 SET_TIMER (hdp);
213 break;
214
215 case FRMR + WAIT_SABM:
216 hd_writeinternal (hdp, DM, pf);
217 hdp->hd_state = DM_SENT;
218 SET_TIMER (hdp);
219 break;
220
221 case FRMR + ABM:
222 hd_message (hdp, "FRMR received: link down");
223 (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
224#ifdef HDLCDEBUG
225 hd_dumptrace (hdp);
226#endif
227 hd_flush (hdp->hd_ifp);
228 hd_writeinternal (hdp, SABM, pf);
229 hdp->hd_state = WAIT_UA;
230 SET_TIMER (hdp);
231 break;
232
233 case RR + ABM:
234 case RNR + ABM:
235 case REJ + ABM:
236 process_sframe (hdp, (struct Hdlc_sframe *)frame, frametype);
237 break;
238
239 case IFRAME + ABM:
240 queued = process_iframe (hdp, fbuf, (struct Hdlc_iframe *)frame);
241 break;
242
243 case IFRAME + SABM_SENT:
244 case RR + SABM_SENT:
245 case RNR + SABM_SENT:
246 case REJ + SABM_SENT:
247 hd_writeinternal (hdp, DM, POLLON);
248 hdp->hd_state = DM_SENT;
249 SET_TIMER (hdp);
250 break;
251
252 case IFRAME + WAIT_SABM:
253 case RR + WAIT_SABM:
254 case RNR + WAIT_SABM:
255 case REJ + WAIT_SABM:
256 hd_writeinternal (hdp, FRMR, POLLOFF);
257 SET_TIMER (hdp);
258 break;
259
260 case ILLEGAL + SABM_SENT:
261 hdp->hd_unknown++;
262 hd_writeinternal (hdp, DM, POLLOFF);
263 hdp->hd_state = DM_SENT;
264 SET_TIMER (hdp);
265 break;
266
267 case ILLEGAL + ABM:
268 hd_message (hdp, "Unknown frame received: link down");
269 (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
270 case ILLEGAL + WAIT_SABM:
271 hdp->hd_unknown++;
272#ifdef HDLCDEBUG
273 hd_dumptrace (hdp);
274#endif
275 hd_writeinternal (hdp, FRMR, POLLOFF);
276 hdp->hd_state = WAIT_SABM;
277 SET_TIMER (hdp);
278 break;
279 }
280
281 return (queued);
282}
283
284process_iframe (hdp, fbuf, frame)
285register struct hdcb *hdp;
286struct mbuf *fbuf;
287register struct Hdlc_iframe *frame;
288{
289 register int nr = frame -> nr,
290 ns = frame -> ns,
291 pf = frame -> pf;
292 register int queued = FALSE;
293
294 /*
295 * Validate the iframe's N(R) value. It's N(R) value must be in
296 * sync with our V(S) value and our "last received nr".
297 */
298
299 if (valid_nr (hdp, nr, FALSE) == FALSE) {
300 frame_reject (hdp, Z, frame);
301 return (queued);
302 }
303
304
305 /*
306 * This section tests the IFRAME for proper sequence. That is, it's
307 * sequence number N(S) MUST be equal to V(S).
308 */
309
310 if (ns != hdp->hd_vr) {
311 hdp->hd_invalid_ns++;
312 if (pf || (hdp->hd_condition & REJ_CONDITION) == 0) {
313 hdp->hd_condition |= REJ_CONDITION;
314 /*
315 * Flush the transmit queue. This is ugly but we
316 * have no choice. A reject response must be
317 * immediately sent to the DCE. Failure to do so
318 * may result in another out of sequence iframe
319 * arriving (and thus sending another reject)
320 * before the first reject is transmitted. This
321 * will cause the DCE to receive two or more
322 * rejects back to back, which must never happen.
323 */
324 hd_flush (hdp->hd_ifp);
325 hd_writeinternal (hdp, REJ, pf);
326 }
327 return (queued);
328 }
329 hdp->hd_condition &= ~REJ_CONDITION;
330
331 /*
332 * This section finally tests the IFRAME's sequence number against
333 * the window size (K) and the sequence number of the last frame
334 * we have acknowledged. If the IFRAME is completely correct then
335 * it is queued for the packet level.
336 */
337
338 if (ns != (hdp->hd_lasttxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) {
339 hdp->hd_vr = (hdp->hd_vr + 1) % MODULUS;
340 if (pf == 1) {
341 /* Must generate a RR or RNR with final bit on. */
342 hd_writeinternal (hdp, RR, POLLON);
343 } else
344 /*
345 * Hopefully we can piggyback the RR, if not we will generate
346 * a RR when T3 timer expires.
347 */
348 if (hdp -> hd_rrtimer == 0)
349 hdp->hd_rrtimer = hd_t3;
350
351 /* Forward iframe to packet level of X.25. */
b84e7ca8 352 fbuf -> m_data += HDHEADERLN;
0f9bb9b6
KS
353 fbuf -> m_len -= HDHEADERLN;
354#ifdef BSD4_3
355 fbuf->m_act = 0; /* probably not necessary */
356#else
357 {
358 register struct mbuf *m;
359
360 for (m = fbuf; m -> m_next; m = m -> m_next)
361 m -> m_act = (struct mbuf *) 0;
362 m -> m_act = (struct mbuf *) 1;
363 }
364#endif
365 pk_input (fbuf, hdp->hd_xcp);
366 queued = TRUE;
367 hd_start (hdp);
368 } else {
369 /*
370 * Here if the remote station has transmitted more iframes then
371 * the number which have been acknowledged plus K.
372 */
373 hdp->hd_invalid_ns++;
374 frame_reject (hdp, W, frame);
375 }
376 return (queued);
377}
378
379/*
380 * This routine is used to determine if a value (the middle parameter)
381 * is between two other values. The low value is the first parameter
382 * the high value is the last parameter. The routine checks the middle
383 * value to see if it is within the range of the first and last values.
384 * The reason we need this routine is the values are modulo some base
385 * hence a simple test for greater or less than is not sufficient.
386 */
387
388bool
389range_check (rear, value, front)
390int rear,
391 value,
392 front;
393{
394 register bool result = FALSE;
395
396 if (front > rear)
397 result = (rear <= value) && (value <= front);
398 else
399 result = (rear <= value) || (value <= front);
400
401 return (result);
402}
403
404/*
405 * This routine handles all the frame reject conditions which can
406 * arise as a result of secondary processing. The frame reject
407 * condition Y (frame length error) are handled elsewhere.
408 */
409
410static
411frame_reject (hdp, rejectcode, frame)
412struct hdcb *hdp;
413struct Hdlc_iframe *frame;
414{
415 register struct Frmr_frame *frmr = &hd_frmr;
416
417 frmr -> frmr_control = ((struct Hdlc_frame *) frame) -> control;
418
419 frmr -> frmr_ns = frame -> ns;
420 frmr -> frmr_f1_0 = 0;
421 frmr -> frmr_nr = frame -> nr;
422 frmr -> frmr_f2_0 = 0;
423
424 frmr -> frmr_0000 = 0;
425 frmr -> frmr_w = frmr -> frmr_x = frmr -> frmr_y =
426 frmr -> frmr_z = 0;
427 switch (rejectcode) {
428 case Z:
429 frmr -> frmr_z = 1;/* invalid N(R). */
430 break;
431
432 case Y:
433 frmr -> frmr_y = 1;/* iframe length error. */
434 break;
435
436 case X:
437 frmr -> frmr_x = 1;/* invalid information field. */
438 frmr -> frmr_w = 1;
439 break;
440
441 case W:
442 frmr -> frmr_w = 1;/* invalid N(S). */
443 }
444
445 hd_writeinternal (hdp, FRMR, POLLOFF);
446
447 hdp->hd_state = WAIT_SABM;
448 SET_TIMER (hdp);
449}
450
451/*
452 * This procedure is invoked when ever we receive a supervisor
453 * frame such as RR, RNR and REJ. All processing for these
454 * frames is done here.
455 */
456
457process_sframe (hdp, frame, frametype)
458register struct hdcb *hdp;
459register struct Hdlc_sframe *frame;
460int frametype;
461{
462 register int nr = frame -> nr, pf = frame -> pf, pollbit = 0;
463
464 if (valid_nr (hdp, nr, pf) == TRUE) {
465 switch (frametype) {
466 case RR:
467 hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
468 break;
469
470 case RNR:
471 hdp->hd_condition |= REMOTE_RNR_CONDITION;
472 hdp->hd_retxcnt = 0;
473 break;
474
475 case REJ:
476 hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
477 rej_routine (hdp, nr);
478 }
479
480 if (pf == 1) {
481 hdp->hd_retxcnt = 0;
482 hdp->hd_condition &= ~TIMER_RECOVERY_CONDITION;
483
484 /* If any iframes have been queued because of the
485 timer condition, transmit then now. */
486 if (hdp->hd_condition & REMOTE_RNR_CONDITION) {
487 /* Remote is busy or timer condition, so only
488 send one. */
489 if (hdp->hd_vs != hdp->hd_retxqi)
490 hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], pollbit);
491 }
492 else /* Flush the retransmit list first. */
493 while (hdp->hd_vs != hdp->hd_retxqi)
494 hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF);
495 }
496
497 hd_start (hdp);
498 } else
499 frame_reject (hdp, Z, (struct Hdlc_iframe *)frame); /* Invalid N(R). */
500}
501
502/*
503 * This routine tests the validity of the N(R) which we have received.
504 * If it is ok, then all the iframes which it acknowledges (if any)
505 * will be freed.
506 */
507
508bool
509valid_nr (hdp, nr, finalbit)
510register struct hdcb *hdp;
511register int finalbit;
512{
513 /* Make sure it really does acknowledge something. */
514 if (hdp->hd_lastrxnr == nr)
515 return (TRUE);
516
517 /*
518 * This section validates the frame's N(R) value. It's N(R) value
519 * must be in syncronization with our V(S) value and our "last
520 * received nr" variable. If it is correct then we are able to send
521 * more IFRAME's, else frame reject condition is entered.
522 */
523
524 if (range_check (hdp->hd_lastrxnr, nr, hdp->hd_vs) == FALSE) {
525 if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) &&
526 range_check (hdp->hd_vs, nr, hdp->hd_xx) == TRUE)
527 hdp->hd_vs = nr;
528
529 else {
530 hdp->hd_invalid_nr++;
531 return (FALSE);
532 }
533 }
534
535 /*
536 * If we get to here, we do have a valid frame but it might be out
537 * of sequence. However, we should still accept the receive state
538 * number N(R) since it has already passed our previous test and it
539 * does acknowledge frames which we are sending.
540 */
541
542 KILL_TIMER (hdp);
543 free_iframes (hdp, &nr, finalbit);/* Free all acknowledged iframes */
544 if (nr != hdp->hd_vs)
545 SET_TIMER (hdp);
546
547 return (TRUE);
548}
549
550/*
551 * This routine determines how many iframes need to be retransmitted.
552 * It then resets the Send State Variable V(S) to accomplish this.
553 */
554
555static
556rej_routine (hdp, rejnr)
557register struct hdcb *hdp;
558register int rejnr;
559{
560 register int anchor;
561
562 /*
563 * Flush the output queue. Any iframes queued for
564 * transmission will be out of sequence.
565 */
566
567 hd_flush (hdp->hd_ifp);
568
569 /*
570 * Determine how many frames should be re-transmitted. In the case
571 * of a normal REJ this should be 1 to K. In the case of a timer
572 * recovery REJ (ie. a REJ with the Final Bit on) this could be 0.
573 */
574
575 anchor = hdp->hd_vs;
576 if (hdp->hd_condition & TIMER_RECOVERY_CONDITION)
577 anchor = hdp->hd_xx;
578
579 anchor = (anchor - rejnr + 8) % MODULUS;
580
581 if (anchor > 0) {
582
583 /* There is at least one iframe to retransmit. */
584 KILL_TIMER (hdp);
585 hdp->hd_vs = rejnr;
586
587 while (hdp->hd_vs != hdp->hd_retxqi)
588 hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF);
589
590 }
591 hd_start (hdp);
592}
593
594/*
595 * This routine frees iframes from the retransmit queue. It is called
596 * when a previously written iframe is acknowledged.
597 */
598
599static
600free_iframes (hdp, nr, finalbit)
601register struct hdcb *hdp;
602int *nr;
603register int finalbit;
604
605{
606 register int i, k;
607
608 /*
609 * We need to do the following because of a funny quirk in the
610 * protocol. This case occures when in Timer recovery condition
611 * we get a N(R) which acknowledges all the outstanding iframes
612 * but with the Final Bit off. In this case we need to save the last
613 * iframe for possible retransmission even though it has already been
614 * acknowledged!
615 */
616
617 if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && *nr == hdp->hd_xx && finalbit == 0) {
618 *nr = (*nr - 1 + 8) % MODULUS;
619/* printf ("QUIRK\n"); */
620 }
621
622 k = (*nr - hdp->hd_lastrxnr + 8) % MODULUS;
623
624 /* Loop here freeing all acknowledged iframes. */
625 for (i = 0; i < k; ++i) {
626 m_freem (hdp->hd_retxq[hdp->hd_lastrxnr]);
627 hdp->hd_retxq[hdp->hd_lastrxnr] = 0;
628 hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS;
629 }
630
631}