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