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