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