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