network_cmds-245.8.tar.gz
[apple/network_cmds.git] / racoon.tproj / handler.c
CommitLineData
ac2f15b3 1/* $KAME: handler.c,v 1.57 2002/01/21 08:45:54 sakane Exp $ */
7ba0088d
A
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/param.h>
34#include <sys/socket.h>
35
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <time.h>
40#include <errno.h>
41
42#include "var.h"
43#include "misc.h"
44#include "vmbuf.h"
45#include "plog.h"
46#include "sockmisc.h"
47#include "debug.h"
48
49#include "schedule.h"
50#include "grabmyaddr.h"
51#include "algorithm.h"
52#include "crypto_openssl.h"
53#include "policy.h"
54#include "proposal.h"
55#include "isakmp_var.h"
56#include "isakmp.h"
57#include "isakmp_inf.h"
58#include "oakley.h"
59#include "remoteconf.h"
60#include "localconf.h"
61#include "handler.h"
62#include "gcmalloc.h"
63
64#ifdef HAVE_GSSAPI
65#include "gssapi.h"
66#endif
67
68static LIST_HEAD(_ph1tree_, ph1handle) ph1tree;
69static LIST_HEAD(_ph2tree_, ph2handle) ph2tree;
70static LIST_HEAD(_ctdtree_, contacted) ctdtree;
71static LIST_HEAD(_rcptree_, recvdpkt) rcptree;
72
73static void del_recvdpkt __P((struct recvdpkt *));
74static void rem_recvdpkt __P((struct recvdpkt *));
75static void sweep_recvdpkt __P((void *));
76
77/*
78 * functions about management of the isakmp status table
79 */
80/* %%% management phase 1 handler */
81/*
82 * search for isakmpsa handler with isakmp index.
83 */
84
85extern caddr_t val2str(const char *, size_t);
86
87struct ph1handle *
88getph1byindex(index)
89 isakmp_index *index;
90{
91 struct ph1handle *p;
92
93 LIST_FOREACH(p, &ph1tree, chain) {
94 if (p->status == PHASE1ST_EXPIRED)
95 continue;
96 if (memcmp(&p->index, index, sizeof(*index)) == 0)
97 return p;
98 }
99
100 return NULL;
101}
102
103/*
104 * search for isakmp handler by i_ck in index.
105 */
106struct ph1handle *
107getph1byindex0(index)
108 isakmp_index *index;
109{
110 struct ph1handle *p;
111
112 LIST_FOREACH(p, &ph1tree, chain) {
113 if (p->status == PHASE1ST_EXPIRED)
114 continue;
115 if (memcmp(&p->index, index, sizeof(cookie_t)) == 0)
116 return p;
117 }
118
119 return NULL;
120}
121
122/*
123 * search for isakmpsa handler by remote address.
124 * don't use port number to search because this function search
125 * with phase 2's destinaion.
126 */
127struct ph1handle *
128getph1byaddr(local, remote)
129 struct sockaddr *local, *remote;
130{
131 struct ph1handle *p;
132
133 LIST_FOREACH(p, &ph1tree, chain) {
134 if (p->status == PHASE1ST_EXPIRED)
135 continue;
136 if (cmpsaddrwop(local, p->local) == 0
137 && cmpsaddrwop(remote, p->remote) == 0)
138 return p;
139 }
140
141 return NULL;
142}
143
144/*
145 * dump isakmp-sa
146 */
147vchar_t *
148dumpph1()
149{
150 struct ph1handle *iph1;
151 struct ph1dump *pd;
152 int cnt = 0;
153 vchar_t *buf;
154
155 /* get length of buffer */
156 LIST_FOREACH(iph1, &ph1tree, chain)
157 cnt++;
158
159 buf = vmalloc(cnt * sizeof(struct ph1dump));
160 if (buf == NULL) {
161 plog(LLV_ERROR, LOCATION, NULL,
162 "failed to get buffer\n");
163 return NULL;
164 }
165 pd = (struct ph1dump *)buf->v;
166
167 LIST_FOREACH(iph1, &ph1tree, chain) {
168 memcpy(&pd->index, &iph1->index, sizeof(iph1->index));
169 pd->status = iph1->status;
170 pd->side = iph1->side;
171 memcpy(&pd->remote, iph1->remote, iph1->remote->sa_len);
172 memcpy(&pd->local, iph1->local, iph1->local->sa_len);
173 pd->version = iph1->version;
174 pd->etype = iph1->etype;
175 pd->created = iph1->created;
176 pd->ph2cnt = iph1->ph2cnt;
177 pd++;
178 }
179
180 return buf;
181}
182
183/*
184 * create new isakmp Phase 1 status record to handle isakmp in Phase1
185 */
186struct ph1handle *
187newph1()
188{
189 struct ph1handle *iph1;
190
191 /* create new iph1 */
192 iph1 = racoon_calloc(1, sizeof(*iph1));
193 if (iph1 == NULL)
194 return NULL;
195
196 iph1->status = PHASE1ST_SPAWN;
197
198 return iph1;
199}
200
201/*
202 * delete new isakmp Phase 1 status record to handle isakmp in Phase1
203 */
204void
205delph1(iph1)
206 struct ph1handle *iph1;
207{
208 if (iph1->remote) {
209 racoon_free(iph1->remote);
210 iph1->remote = NULL;
211 }
212 if (iph1->local) {
213 racoon_free(iph1->local);
214 iph1->local = NULL;
215 }
216
217 VPTRINIT(iph1->authstr);
218
219 sched_scrub_param(iph1);
220 iph1->sce = NULL;
221 iph1->scr = NULL;
222
223 VPTRINIT(iph1->sendbuf);
224
225 VPTRINIT(iph1->dhpriv);
226 VPTRINIT(iph1->dhpub);
227 VPTRINIT(iph1->dhpub_p);
228 VPTRINIT(iph1->dhgxy);
229 VPTRINIT(iph1->nonce);
230 VPTRINIT(iph1->nonce_p);
231 VPTRINIT(iph1->skeyid);
232 VPTRINIT(iph1->skeyid_d);
233 VPTRINIT(iph1->skeyid_a);
234 VPTRINIT(iph1->skeyid_e);
235 VPTRINIT(iph1->key);
236 VPTRINIT(iph1->hash);
237 VPTRINIT(iph1->sig);
238 VPTRINIT(iph1->sig_p);
239 oakley_delcert(iph1->cert);
240 iph1->cert = NULL;
241 oakley_delcert(iph1->cert_p);
242 iph1->cert_p = NULL;
243 oakley_delcert(iph1->crl_p);
244 iph1->crl_p = NULL;
245 oakley_delcert(iph1->cr_p);
246 iph1->cr_p = NULL;
247 VPTRINIT(iph1->id);
248 VPTRINIT(iph1->id_p);
249
250 if (iph1->ivm) {
251 oakley_delivm(iph1->ivm);
252 iph1->ivm = NULL;
253 }
254
255 VPTRINIT(iph1->sa);
256 VPTRINIT(iph1->sa_ret);
257
258#ifdef HAVE_GSSAPI
259 VPTRINIT(iph1->gi_i);
260 VPTRINIT(iph1->gi_r);
261
262 gssapi_free_state(iph1);
263#endif
264
ac2f15b3
A
265#ifdef IKE_NAT_T
266 VPTRINIT(iph1->local_natd);
267 VPTRINIT(iph1->remote_natd);
268#endif
269
7ba0088d
A
270 racoon_free(iph1);
271}
272
273/*
274 * create new isakmp Phase 1 status record to handle isakmp in Phase1
275 */
276int
277insph1(iph1)
278 struct ph1handle *iph1;
279{
280 /* validity check */
281 if (iph1->remote == NULL) {
282 plog(LLV_ERROR, LOCATION, NULL,
283 "invalid isakmp SA handler. no remote address.\n");
284 return -1;
285 }
286 LIST_INSERT_HEAD(&ph1tree, iph1, chain);
287
288 return 0;
289}
290
291void
292remph1(iph1)
293 struct ph1handle *iph1;
294{
295 LIST_REMOVE(iph1, chain);
296}
297
298/*
299 * flush isakmp-sa
300 */
301void
302flushph1()
303{
304 struct ph1handle *p, *next;
305
306 for (p = LIST_FIRST(&ph1tree); p; p = next) {
307 next = LIST_NEXT(p, chain);
308
309 /* send delete information */
310 if (p->status == PHASE1ST_ESTABLISHED)
311 isakmp_info_send_d1(p);
312
313 remph1(p);
314 delph1(p);
315 }
316}
317
318void
319initph1tree()
320{
321 LIST_INIT(&ph1tree);
322}
323
324/* %%% management phase 2 handler */
325/*
326 * search ph2handle with policy id.
327 */
328struct ph2handle *
329getph2byspid(spid)
330 u_int32_t spid;
331{
332 struct ph2handle *p;
333
334 LIST_FOREACH(p, &ph2tree, chain) {
335 /*
336 * there are ph2handle independent on policy
337 * such like informational exchange.
338 */
339 if (p->spid == spid)
340 return p;
341 }
342
343 return NULL;
344}
345
346/*
347 * search ph2handle with sequence number.
348 */
349struct ph2handle *
350getph2byseq(seq)
351 u_int32_t seq;
352{
353 struct ph2handle *p;
354
355 LIST_FOREACH(p, &ph2tree, chain) {
356 if (p->seq == seq)
357 return p;
358 }
359
360 return NULL;
361}
362
363/*
364 * search ph2handle with message id.
365 */
366struct ph2handle *
367getph2bymsgid(iph1, msgid)
368 struct ph1handle *iph1;
369 u_int32_t msgid;
370{
371 struct ph2handle *p;
372
373 LIST_FOREACH(p, &ph2tree, chain) {
374 if (p->msgid == msgid)
375 return p;
376 }
377
378 return NULL;
379}
380
381/*
382 * call by pk_recvexpire().
383 */
384struct ph2handle *
385getph2bysaidx(src, dst, proto_id, spi)
386 struct sockaddr *src, *dst;
387 u_int proto_id;
388 u_int32_t spi;
389{
390 struct ph2handle *iph2;
391 struct saproto *pr;
392
393 LIST_FOREACH(iph2, &ph2tree, chain) {
394 if (iph2->proposal == NULL && iph2->approval == NULL)
395 continue;
396 if (iph2->approval != NULL) {
397 for (pr = iph2->approval->head; pr != NULL;
398 pr = pr->next) {
399 if (proto_id != pr->proto_id)
400 break;
401 if (spi == pr->spi || spi == pr->spi_p)
402 return iph2;
403 }
404 } else if (iph2->proposal != NULL) {
405 for (pr = iph2->proposal->head; pr != NULL;
406 pr = pr->next) {
407 if (proto_id != pr->proto_id)
408 break;
409 if (spi == pr->spi)
410 return iph2;
411 }
412 }
413 }
414
415 return NULL;
416}
417
418/*
419 * create new isakmp Phase 2 status record to handle isakmp in Phase2
420 */
421struct ph2handle *
422newph2()
423{
424 struct ph2handle *iph2 = NULL;
425
426 /* create new iph2 */
427 iph2 = racoon_calloc(1, sizeof(*iph2));
428 if (iph2 == NULL)
429 return NULL;
430
431 iph2->status = PHASE1ST_SPAWN;
432
433 return iph2;
434}
435
436/*
437 * initialize ph2handle
438 * NOTE: don't initialize src/dst.
439 * SPI in the proposal is cleared.
440 */
441void
442initph2(iph2)
443 struct ph2handle *iph2;
444{
445 sched_scrub_param(iph2);
446 iph2->sce = NULL;
447 iph2->scr = NULL;
448
449 VPTRINIT(iph2->sendbuf);
450 VPTRINIT(iph2->msg1);
451
452 /* clear spi, keep variables in the proposal */
453 if (iph2->proposal) {
454 struct saproto *pr;
455 for (pr = iph2->proposal->head; pr != NULL; pr = pr->next)
456 pr->spi = 0;
457 }
458
459 /* clear approval */
460 if (iph2->approval) {
461 flushsaprop(iph2->approval);
462 iph2->approval = NULL;
463 }
464
465 /* clear the generated policy */
466 if (iph2->spidx_gen) {
467 delsp_bothdir((struct policyindex *)iph2->spidx_gen);
468 racoon_free(iph2->spidx_gen);
469 iph2->spidx_gen = NULL;
470 }
471
472 if (iph2->pfsgrp) {
473 oakley_dhgrp_free(iph2->pfsgrp);
474 iph2->pfsgrp = NULL;
475 }
476
477 VPTRINIT(iph2->dhpriv);
478 VPTRINIT(iph2->dhpub);
479 VPTRINIT(iph2->dhpub_p);
480 VPTRINIT(iph2->dhgxy);
481 VPTRINIT(iph2->id);
482 VPTRINIT(iph2->id_p);
483 VPTRINIT(iph2->nonce);
484 VPTRINIT(iph2->nonce_p);
485 VPTRINIT(iph2->sa);
486 VPTRINIT(iph2->sa_ret);
487
488 if (iph2->ivm) {
489 oakley_delivm(iph2->ivm);
490 iph2->ivm = NULL;
491 }
492}
493
494/*
495 * delete new isakmp Phase 2 status record to handle isakmp in Phase2
496 */
497void
498delph2(iph2)
499 struct ph2handle *iph2;
500{
501 initph2(iph2);
502
503 if (iph2->src) {
504 racoon_free(iph2->src);
505 iph2->src = NULL;
506 }
507 if (iph2->dst) {
508 racoon_free(iph2->dst);
509 iph2->dst = NULL;
510 }
511 if (iph2->src_id) {
512 racoon_free(iph2->src_id);
513 iph2->src_id = NULL;
514 }
515 if (iph2->dst_id) {
516 racoon_free(iph2->dst_id);
517 iph2->dst_id = NULL;
518 }
519
520 if (iph2->proposal) {
521 flushsaprop(iph2->proposal);
522 iph2->proposal = NULL;
523 }
524
525 racoon_free(iph2);
526}
527
528/*
529 * create new isakmp Phase 2 status record to handle isakmp in Phase2
530 */
531int
532insph2(iph2)
533 struct ph2handle *iph2;
534{
535 LIST_INSERT_HEAD(&ph2tree, iph2, chain);
536
537 return 0;
538}
539
540void
541remph2(iph2)
542 struct ph2handle *iph2;
543{
544 LIST_REMOVE(iph2, chain);
545}
546
547void
548initph2tree()
549{
550 LIST_INIT(&ph2tree);
551}
552
553void
554flushph2()
555{
556 struct ph2handle *p, *next;
557
558 for (p = LIST_FIRST(&ph2tree); p; p = next) {
559 next = LIST_NEXT(p, chain);
560
561 /* send delete information */
562 if (p->status == PHASE2ST_ESTABLISHED)
563 isakmp_info_send_d2(p);
564
565 unbindph12(p);
566 remph2(p);
567 delph2(p);
568 }
569}
570
571/*
572 * Delete all Phase 2 handlers for this src/dst/proto. This
573 * is used during INITIAL-CONTACT processing (so no need to
574 * send a message to the peer).
575 */
576void
577deleteallph2(src, dst, proto_id)
578 struct sockaddr *src, *dst;
579 u_int proto_id;
580{
581 struct ph2handle *iph2, *next;
582 struct saproto *pr;
583
584 for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
585 next = LIST_NEXT(iph2, chain);
586 if (iph2->proposal == NULL && iph2->approval == NULL)
587 continue;
588 if (iph2->approval != NULL) {
589 for (pr = iph2->approval->head; pr != NULL;
590 pr = pr->next) {
591 if (proto_id == pr->proto_id)
592 goto zap_it;
593 }
594 } else if (iph2->proposal != NULL) {
595 for (pr = iph2->proposal->head; pr != NULL;
596 pr = pr->next) {
597 if (proto_id == pr->proto_id)
598 goto zap_it;
599 }
600 }
601 continue;
602 zap_it:
603 unbindph12(iph2);
604 remph2(iph2);
605 delph2(iph2);
606 }
607}
608
609/* %%% */
610void
611bindph12(iph1, iph2)
612 struct ph1handle *iph1;
613 struct ph2handle *iph2;
614{
615 iph2->ph1 = iph1;
616 LIST_INSERT_HEAD(&iph1->ph2tree, iph2, ph1bind);
617}
618
619void
620unbindph12(iph2)
621 struct ph2handle *iph2;
622{
623 if (iph2->ph1 != NULL) {
624 iph2->ph1 = NULL;
625 LIST_REMOVE(iph2, ph1bind);
626 }
627}
628
629/* %%% management contacted list */
630/*
631 * search contacted list.
632 */
633struct contacted *
634getcontacted(remote)
635 struct sockaddr *remote;
636{
637 struct contacted *p;
638
639 LIST_FOREACH(p, &ctdtree, chain) {
640 if (cmpsaddrstrict(remote, p->remote) == 0)
641 return p;
642 }
643
644 return NULL;
645}
646
647/*
648 * create new isakmp Phase 2 status record to handle isakmp in Phase2
649 */
650int
651inscontacted(remote)
652 struct sockaddr *remote;
653{
654 struct contacted *new;
655
656 /* create new iph2 */
657 new = racoon_calloc(1, sizeof(*new));
658 if (new == NULL)
659 return -1;
660
661 new->remote = dupsaddr(remote);
662
663 LIST_INSERT_HEAD(&ctdtree, new, chain);
664
665 return 0;
666}
667
668void
669initctdtree()
670{
671 LIST_INIT(&ctdtree);
672}
673
674/*
675 * check the response has been sent to the peer. when not, simply reply
676 * the buffered packet to the peer.
677 * OUT:
678 * 0: the packet is received at the first time.
679 * 1: the packet was processed before.
680 * 2: the packet was processed before, but the address mismatches.
681 * -1: error happened.
682 */
683int
684check_recvdpkt(remote, local, rbuf)
685 struct sockaddr *remote, *local;
686 vchar_t *rbuf;
687{
688 vchar_t *hash;
689 struct recvdpkt *r;
690 time_t t;
691 int len, s;
692
693 /* set current time */
694 t = time(NULL);
695
696 hash = eay_md5_one(rbuf);
697 if (!hash) {
698 plog(LLV_ERROR, LOCATION, NULL,
699 "failed to allocate buffer.\n");
700 return -1;
701 }
702
703 LIST_FOREACH(r, &rcptree, chain) {
704 if (memcmp(hash->v, r->hash->v, r->hash->l) == 0)
705 break;
706 }
707 vfree(hash);
708
709 /* this is the first time to receive the packet */
710 if (r == NULL)
711 return 0;
712
713 /*
714 * the packet was processed before, but the remote address mismatches.
715 */
716 if (cmpsaddrstrict(remote, r->remote) != 0)
717 return 2;
718
719 /*
720 * it should not check the local address because the packet
721 * may arrive at other interface.
722 */
723
724 /* check the previous time to send */
725 if (t - r->time_send < 1) {
726 plog(LLV_WARNING, LOCATION, NULL,
727 "the packet retransmitted in a short time from %s\n",
728 saddr2str(remote));
729 /*XXX should it be error ? */
730 }
731
732 /* select the socket to be sent */
733 s = getsockmyaddr(r->local);
734 if (s == -1)
735 return -1;
736
737 /* resend the packet if needed */
738 len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
739 r->local, r->remote, lcconf->count_persend);
740 if (len == -1) {
741 plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
742 return -1;
743 }
744
745 /* check the retry counter */
746 r->retry_counter--;
747 if (r->retry_counter <= 0) {
748 rem_recvdpkt(r);
749 del_recvdpkt(r);
750 plog(LLV_DEBUG, LOCATION, NULL,
751 "deleted the retransmission packet to %s.\n",
752 saddr2str(remote));
753 } else
754 r->time_send = t;
755
756 return 1;
757}
758
759/*
760 * adding a hash of received packet into the received list.
761 */
762int
763add_recvdpkt(remote, local, sbuf, rbuf)
764 struct sockaddr *remote, *local;
765 vchar_t *sbuf, *rbuf;
766{
767 struct recvdpkt *new = NULL;
768
769 if (lcconf->retry_counter == 0) {
770 /* no need to add it */
771 return 0;
772 }
773
774 new = racoon_calloc(1, sizeof(*new));
775 if (!new) {
776 plog(LLV_ERROR, LOCATION, NULL,
777 "failed to allocate buffer.\n");
778 return -1;
779 }
780
781 new->hash = eay_md5_one(rbuf);
782 if (!new->hash) {
783 plog(LLV_ERROR, LOCATION, NULL,
784 "failed to allocate buffer.\n");
785 del_recvdpkt(new);
786 return -1;
787 }
788 new->remote = dupsaddr(remote);
789 if (new->remote == NULL) {
790 plog(LLV_ERROR, LOCATION, NULL,
791 "failed to allocate buffer.\n");
792 del_recvdpkt(new);
793 return -1;
794 }
795 new->local = dupsaddr(local);
796 if (new->local == NULL) {
797 plog(LLV_ERROR, LOCATION, NULL,
798 "failed to allocate buffer.\n");
799 del_recvdpkt(new);
800 return -1;
801 }
802 new->sendbuf = vdup(sbuf);
803 if (new->sendbuf == NULL) {
804 plog(LLV_ERROR, LOCATION, NULL,
805 "failed to allocate buffer.\n");
806 del_recvdpkt(new);
807 return -1;
808 }
809
810 new->retry_counter = lcconf->retry_counter;
811 new->time_send = 0;
812 new->created = time(NULL);
813
814 LIST_INSERT_HEAD(&rcptree, new, chain);
815
816 return 0;
817}
818
819void
820del_recvdpkt(r)
821 struct recvdpkt *r;
822{
823 if (r->remote)
824 racoon_free(r->remote);
825 if (r->local)
826 racoon_free(r->local);
827 if (r->hash)
828 vfree(r->hash);
829 if (r->sendbuf)
830 vfree(r->sendbuf);
831 racoon_free(r);
832}
833
834void
835rem_recvdpkt(r)
836 struct recvdpkt *r;
837{
838 LIST_REMOVE(r, chain);
839}
840
841void
842sweep_recvdpkt(dummy)
843 void *dummy;
844{
845 struct recvdpkt *r, *next;
846 time_t t, lt;
847
848 /* set current time */
849 t = time(NULL);
850
851 /* set the lifetime of the retransmission */
852 lt = lcconf->retry_counter * lcconf->retry_interval;
853
854 for (r = LIST_FIRST(&rcptree); r; r = next) {
855 next = LIST_NEXT(r, chain);
856
857 if (t - r->created > lt) {
858 rem_recvdpkt(r);
859 del_recvdpkt(r);
860 }
861 }
862
863 sched_new(lt, sweep_recvdpkt, NULL);
864}
865
866void
867init_recvdpkt()
868{
869 time_t lt = lcconf->retry_counter * lcconf->retry_interval;
870
871 LIST_INIT(&rcptree);
872
873 sched_new(lt, sweep_recvdpkt, NULL);
874}