]> git.saurik.com Git - apple/network_cmds.git/blobdiff - racoon.tproj/handler.c
network_cmds-115.tar.gz
[apple/network_cmds.git] / racoon.tproj / handler.c
diff --git a/racoon.tproj/handler.c b/racoon.tproj/handler.c
new file mode 100644 (file)
index 0000000..1ca5b5c
--- /dev/null
@@ -0,0 +1,869 @@
+/*     $KAME: handler.c,v 1.56 2002/01/02 09:05:25 jinmei Exp $        */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "var.h"
+#include "misc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "sockmisc.h"
+#include "debug.h"
+
+#include "schedule.h"
+#include "grabmyaddr.h"
+#include "algorithm.h"
+#include "crypto_openssl.h"
+#include "policy.h"
+#include "proposal.h"
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "isakmp_inf.h"
+#include "oakley.h"
+#include "remoteconf.h"
+#include "localconf.h"
+#include "handler.h"
+#include "gcmalloc.h"
+
+#ifdef HAVE_GSSAPI
+#include "gssapi.h"
+#endif
+
+static LIST_HEAD(_ph1tree_, ph1handle) ph1tree;
+static LIST_HEAD(_ph2tree_, ph2handle) ph2tree;
+static LIST_HEAD(_ctdtree_, contacted) ctdtree;
+static LIST_HEAD(_rcptree_, recvdpkt) rcptree;
+
+static void del_recvdpkt __P((struct recvdpkt *));
+static void rem_recvdpkt __P((struct recvdpkt *));
+static void sweep_recvdpkt __P((void *));
+
+/*
+ * functions about management of the isakmp status table
+ */
+/* %%% management phase 1 handler */
+/*
+ * search for isakmpsa handler with isakmp index.
+ */
+
+extern caddr_t val2str(const char *, size_t);
+
+struct ph1handle *
+getph1byindex(index)
+       isakmp_index *index;
+{
+       struct ph1handle *p;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->status == PHASE1ST_EXPIRED)
+                       continue;
+               if (memcmp(&p->index, index, sizeof(*index)) == 0)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * search for isakmp handler by i_ck in index.
+ */
+struct ph1handle *
+getph1byindex0(index)
+       isakmp_index *index;
+{
+       struct ph1handle *p;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->status == PHASE1ST_EXPIRED)
+                       continue;
+               if (memcmp(&p->index, index, sizeof(cookie_t)) == 0)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * search for isakmpsa handler by remote address.
+ * don't use port number to search because this function search
+ * with phase 2's destinaion.
+ */
+struct ph1handle *
+getph1byaddr(local, remote)
+       struct sockaddr *local, *remote;
+{
+       struct ph1handle *p;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->status == PHASE1ST_EXPIRED)
+                       continue;
+               if (cmpsaddrwop(local, p->local) == 0
+                && cmpsaddrwop(remote, p->remote) == 0)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * dump isakmp-sa
+ */
+vchar_t *
+dumpph1()
+{
+       struct ph1handle *iph1;
+       struct ph1dump *pd;
+       int cnt = 0;
+       vchar_t *buf;
+
+       /* get length of buffer */
+       LIST_FOREACH(iph1, &ph1tree, chain)
+               cnt++;
+
+       buf = vmalloc(cnt * sizeof(struct ph1dump));
+       if (buf == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to get buffer\n");
+               return NULL;
+       }
+       pd = (struct ph1dump *)buf->v;
+
+       LIST_FOREACH(iph1, &ph1tree, chain) {
+               memcpy(&pd->index, &iph1->index, sizeof(iph1->index));
+               pd->status = iph1->status;
+               pd->side = iph1->side;
+               memcpy(&pd->remote, iph1->remote, iph1->remote->sa_len);
+               memcpy(&pd->local, iph1->local, iph1->local->sa_len);
+               pd->version = iph1->version;
+               pd->etype = iph1->etype;
+               pd->created = iph1->created;
+               pd->ph2cnt = iph1->ph2cnt;
+               pd++;
+       }
+
+       return buf;
+}
+
+/*
+ * create new isakmp Phase 1 status record to handle isakmp in Phase1
+ */
+struct ph1handle *
+newph1()
+{
+       struct ph1handle *iph1;
+
+       /* create new iph1 */
+       iph1 = racoon_calloc(1, sizeof(*iph1));
+       if (iph1 == NULL)
+               return NULL;
+
+       iph1->status = PHASE1ST_SPAWN;
+
+       return iph1;
+}
+
+/*
+ * delete new isakmp Phase 1 status record to handle isakmp in Phase1
+ */
+void
+delph1(iph1)
+       struct ph1handle *iph1;
+{
+       if (iph1->remote) {
+               racoon_free(iph1->remote);
+               iph1->remote = NULL;
+       }
+       if (iph1->local) {
+               racoon_free(iph1->local);
+               iph1->local = NULL;
+       }
+
+       VPTRINIT(iph1->authstr);
+
+       sched_scrub_param(iph1);
+       iph1->sce = NULL;
+       iph1->scr = NULL;
+
+       VPTRINIT(iph1->sendbuf);
+
+       VPTRINIT(iph1->dhpriv);
+       VPTRINIT(iph1->dhpub);
+       VPTRINIT(iph1->dhpub_p);
+       VPTRINIT(iph1->dhgxy);
+       VPTRINIT(iph1->nonce);
+       VPTRINIT(iph1->nonce_p);
+       VPTRINIT(iph1->skeyid);
+       VPTRINIT(iph1->skeyid_d);
+       VPTRINIT(iph1->skeyid_a);
+       VPTRINIT(iph1->skeyid_e);
+       VPTRINIT(iph1->key);
+       VPTRINIT(iph1->hash);
+       VPTRINIT(iph1->sig);
+       VPTRINIT(iph1->sig_p);
+       oakley_delcert(iph1->cert);
+       iph1->cert = NULL;
+       oakley_delcert(iph1->cert_p);
+       iph1->cert_p = NULL;
+       oakley_delcert(iph1->crl_p);
+       iph1->crl_p = NULL;
+       oakley_delcert(iph1->cr_p);
+       iph1->cr_p = NULL;
+       VPTRINIT(iph1->id);
+       VPTRINIT(iph1->id_p);
+
+       if (iph1->ivm) {
+               oakley_delivm(iph1->ivm);
+               iph1->ivm = NULL;
+       }
+
+       VPTRINIT(iph1->sa);
+       VPTRINIT(iph1->sa_ret);
+
+#ifdef HAVE_GSSAPI
+       VPTRINIT(iph1->gi_i);
+       VPTRINIT(iph1->gi_r);
+
+       gssapi_free_state(iph1);
+#endif
+
+       racoon_free(iph1);
+}
+
+/*
+ * create new isakmp Phase 1 status record to handle isakmp in Phase1
+ */
+int
+insph1(iph1)
+       struct ph1handle *iph1;
+{
+       /* validity check */
+       if (iph1->remote == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "invalid isakmp SA handler. no remote address.\n");
+               return -1;
+       }
+       LIST_INSERT_HEAD(&ph1tree, iph1, chain);
+
+       return 0;
+}
+
+void
+remph1(iph1)
+       struct ph1handle *iph1;
+{
+       LIST_REMOVE(iph1, chain);
+}
+
+/*
+ * flush isakmp-sa
+ */
+void
+flushph1()
+{
+       struct ph1handle *p, *next;
+
+       for (p = LIST_FIRST(&ph1tree); p; p = next) {
+               next = LIST_NEXT(p, chain);
+
+               /* send delete information */
+               if (p->status == PHASE1ST_ESTABLISHED) 
+                       isakmp_info_send_d1(p);
+
+               remph1(p);
+               delph1(p);
+       }
+}
+
+void
+initph1tree()
+{
+       LIST_INIT(&ph1tree);
+}
+
+/* %%% management phase 2 handler */
+/*
+ * search ph2handle with policy id.
+ */
+struct ph2handle *
+getph2byspid(spid)
+      u_int32_t spid;
+{
+       struct ph2handle *p;
+
+       LIST_FOREACH(p, &ph2tree, chain) {
+               /*
+                * there are ph2handle independent on policy
+                * such like informational exchange.
+                */
+               if (p->spid == spid)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * search ph2handle with sequence number.
+ */
+struct ph2handle *
+getph2byseq(seq)
+       u_int32_t seq;
+{
+       struct ph2handle *p;
+
+       LIST_FOREACH(p, &ph2tree, chain) {
+               if (p->seq == seq)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * search ph2handle with message id.
+ */
+struct ph2handle *
+getph2bymsgid(iph1, msgid)
+       struct ph1handle *iph1;
+       u_int32_t msgid;
+{
+       struct ph2handle *p;
+
+       LIST_FOREACH(p, &ph2tree, chain) {
+               if (p->msgid == msgid)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * call by pk_recvexpire().
+ */
+struct ph2handle *
+getph2bysaidx(src, dst, proto_id, spi)
+       struct sockaddr *src, *dst;
+       u_int proto_id;
+       u_int32_t spi;
+{
+       struct ph2handle *iph2;
+       struct saproto *pr;
+
+       LIST_FOREACH(iph2, &ph2tree, chain) {
+               if (iph2->proposal == NULL && iph2->approval == NULL)
+                       continue;
+               if (iph2->approval != NULL) {
+                       for (pr = iph2->approval->head; pr != NULL;
+                            pr = pr->next) {
+                               if (proto_id != pr->proto_id)
+                                       break;
+                               if (spi == pr->spi || spi == pr->spi_p)
+                                       return iph2;
+                       }
+               } else if (iph2->proposal != NULL) {
+                       for (pr = iph2->proposal->head; pr != NULL;
+                            pr = pr->next) {
+                               if (proto_id != pr->proto_id)
+                                       break;
+                               if (spi == pr->spi)
+                                       return iph2;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * create new isakmp Phase 2 status record to handle isakmp in Phase2
+ */
+struct ph2handle *
+newph2()
+{
+       struct ph2handle *iph2 = NULL;
+
+       /* create new iph2 */
+       iph2 = racoon_calloc(1, sizeof(*iph2));
+       if (iph2 == NULL)
+               return NULL;
+
+       iph2->status = PHASE1ST_SPAWN;
+
+       return iph2;
+}
+
+/*
+ * initialize ph2handle
+ * NOTE: don't initialize src/dst.
+ *       SPI in the proposal is cleared.
+ */
+void
+initph2(iph2)
+       struct ph2handle *iph2;
+{
+       sched_scrub_param(iph2);
+       iph2->sce = NULL;
+       iph2->scr = NULL;
+
+       VPTRINIT(iph2->sendbuf);
+       VPTRINIT(iph2->msg1);
+
+       /* clear spi, keep variables in the proposal */
+       if (iph2->proposal) {
+               struct saproto *pr;
+               for (pr = iph2->proposal->head; pr != NULL; pr = pr->next)
+                       pr->spi = 0;
+       }
+
+       /* clear approval */
+       if (iph2->approval) {
+               flushsaprop(iph2->approval);
+               iph2->approval = NULL;
+       }
+
+       /* clear the generated policy */
+       if (iph2->spidx_gen) {
+               delsp_bothdir((struct policyindex *)iph2->spidx_gen);
+               racoon_free(iph2->spidx_gen);
+               iph2->spidx_gen = NULL;
+       }
+
+       if (iph2->pfsgrp) {
+               oakley_dhgrp_free(iph2->pfsgrp);
+               iph2->pfsgrp = NULL;
+       }
+
+       VPTRINIT(iph2->dhpriv);
+       VPTRINIT(iph2->dhpub);
+       VPTRINIT(iph2->dhpub_p);
+       VPTRINIT(iph2->dhgxy);
+       VPTRINIT(iph2->id);
+       VPTRINIT(iph2->id_p);
+       VPTRINIT(iph2->nonce);
+       VPTRINIT(iph2->nonce_p);
+       VPTRINIT(iph2->sa);
+       VPTRINIT(iph2->sa_ret);
+
+       if (iph2->ivm) {
+               oakley_delivm(iph2->ivm);
+               iph2->ivm = NULL;
+       }
+}
+
+/*
+ * delete new isakmp Phase 2 status record to handle isakmp in Phase2
+ */
+void
+delph2(iph2)
+       struct ph2handle *iph2;
+{
+       initph2(iph2);
+
+       if (iph2->src) {
+               racoon_free(iph2->src);
+               iph2->src = NULL;
+       }
+       if (iph2->dst) {
+               racoon_free(iph2->dst);
+               iph2->dst = NULL;
+       }
+       if (iph2->src_id) {
+             racoon_free(iph2->src_id);
+             iph2->src_id = NULL;
+       }
+       if (iph2->dst_id) {
+             racoon_free(iph2->dst_id);
+             iph2->dst_id = NULL;
+       }
+
+       if (iph2->proposal) {
+               flushsaprop(iph2->proposal);
+               iph2->proposal = NULL;
+       }
+
+       racoon_free(iph2);
+}
+
+/*
+ * create new isakmp Phase 2 status record to handle isakmp in Phase2
+ */
+int
+insph2(iph2)
+       struct ph2handle *iph2;
+{
+       LIST_INSERT_HEAD(&ph2tree, iph2, chain);
+
+       return 0;
+}
+
+void
+remph2(iph2)
+       struct ph2handle *iph2;
+{
+       LIST_REMOVE(iph2, chain);
+}
+
+void
+initph2tree()
+{
+       LIST_INIT(&ph2tree);
+}
+
+void
+flushph2()
+{
+       struct ph2handle *p, *next;
+
+       for (p = LIST_FIRST(&ph2tree); p; p = next) {
+               next = LIST_NEXT(p, chain);
+
+               /* send delete information */
+               if (p->status == PHASE2ST_ESTABLISHED) 
+                       isakmp_info_send_d2(p);
+
+               unbindph12(p);
+               remph2(p);
+               delph2(p);
+       }
+}
+
+/*
+ * Delete all Phase 2 handlers for this src/dst/proto.  This
+ * is used during INITIAL-CONTACT processing (so no need to
+ * send a message to the peer).
+ */
+void
+deleteallph2(src, dst, proto_id)
+       struct sockaddr *src, *dst;
+       u_int proto_id;
+{
+       struct ph2handle *iph2, *next;
+       struct saproto *pr;
+
+       for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
+               next = LIST_NEXT(iph2, chain);
+               if (iph2->proposal == NULL && iph2->approval == NULL)
+                       continue;
+               if (iph2->approval != NULL) {
+                       for (pr = iph2->approval->head; pr != NULL;
+                            pr = pr->next) {
+                               if (proto_id == pr->proto_id)
+                                       goto zap_it;
+                       }
+               } else if (iph2->proposal != NULL) {
+                       for (pr = iph2->proposal->head; pr != NULL;
+                            pr = pr->next) {
+                               if (proto_id == pr->proto_id)
+                                       goto zap_it;
+                       }
+               }
+               continue;
+ zap_it:
+               unbindph12(iph2);
+               remph2(iph2);
+               delph2(iph2);
+       }
+}
+
+/* %%% */
+void
+bindph12(iph1, iph2)
+       struct ph1handle *iph1;
+       struct ph2handle *iph2;
+{
+       iph2->ph1 = iph1;
+       LIST_INSERT_HEAD(&iph1->ph2tree, iph2, ph1bind);
+}
+
+void
+unbindph12(iph2)
+       struct ph2handle *iph2;
+{
+       if (iph2->ph1 != NULL) {
+               iph2->ph1 = NULL;
+               LIST_REMOVE(iph2, ph1bind);
+       }
+}
+
+/* %%% management contacted list */
+/*
+ * search contacted list.
+ */
+struct contacted *
+getcontacted(remote)
+       struct sockaddr *remote;
+{
+       struct contacted *p;
+
+       LIST_FOREACH(p, &ctdtree, chain) {
+               if (cmpsaddrstrict(remote, p->remote) == 0)
+                       return p;
+       }
+
+       return NULL;
+}
+
+/*
+ * create new isakmp Phase 2 status record to handle isakmp in Phase2
+ */
+int
+inscontacted(remote)
+       struct sockaddr *remote;
+{
+       struct contacted *new;
+
+       /* create new iph2 */
+       new = racoon_calloc(1, sizeof(*new));
+       if (new == NULL)
+               return -1;
+
+       new->remote = dupsaddr(remote);
+
+       LIST_INSERT_HEAD(&ctdtree, new, chain);
+
+       return 0;
+}
+
+void
+initctdtree()
+{
+       LIST_INIT(&ctdtree);
+}
+
+/*
+ * check the response has been sent to the peer.  when not, simply reply
+ * the buffered packet to the peer.
+ * OUT:
+ *      0:     the packet is received at the first time.
+ *      1:     the packet was processed before.
+ *      2:     the packet was processed before, but the address mismatches.
+ *     -1:     error happened.
+ */
+int
+check_recvdpkt(remote, local, rbuf)
+       struct sockaddr *remote, *local;
+       vchar_t *rbuf;
+{
+       vchar_t *hash;
+       struct recvdpkt *r;
+       time_t t;
+       int len, s;
+
+       /* set current time */
+       t = time(NULL);
+
+       hash = eay_md5_one(rbuf);
+       if (!hash) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               return -1;
+       }
+
+       LIST_FOREACH(r, &rcptree, chain) {
+               if (memcmp(hash->v, r->hash->v, r->hash->l) == 0)
+                       break;
+       }
+       vfree(hash);
+
+       /* this is the first time to receive the packet */
+       if (r == NULL)
+               return 0;
+
+       /*
+        * the packet was processed before, but the remote address mismatches.
+        */
+       if (cmpsaddrstrict(remote, r->remote) != 0)
+               return 2;
+
+       /*
+        * it should not check the local address because the packet
+        * may arrive at other interface.
+        */
+
+       /* check the previous time to send */
+       if (t - r->time_send < 1) {
+               plog(LLV_WARNING, LOCATION, NULL,
+                       "the packet retransmitted in a short time from %s\n",
+                       saddr2str(remote));
+               /*XXX should it be error ? */
+       }
+
+       /* select the socket to be sent */
+       s = getsockmyaddr(r->local);
+       if (s == -1)
+               return -1;
+
+       /* resend the packet if needed */
+       len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
+                       r->local, r->remote, lcconf->count_persend);
+       if (len == -1) {
+               plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
+               return -1;
+       }
+
+       /* check the retry counter */
+       r->retry_counter--;
+       if (r->retry_counter <= 0) {
+               rem_recvdpkt(r);
+               del_recvdpkt(r);
+               plog(LLV_DEBUG, LOCATION, NULL,
+                       "deleted the retransmission packet to %s.\n",
+                       saddr2str(remote));
+       } else
+               r->time_send = t;
+
+       return 1;
+}
+
+/*
+ * adding a hash of received packet into the received list.
+ */
+int
+add_recvdpkt(remote, local, sbuf, rbuf)
+       struct sockaddr *remote, *local;
+       vchar_t *sbuf, *rbuf;
+{
+       struct recvdpkt *new = NULL;
+
+       if (lcconf->retry_counter == 0) {
+               /* no need to add it */
+               return 0;
+       }
+
+       new = racoon_calloc(1, sizeof(*new));
+       if (!new) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               return -1;
+       }
+
+       new->hash = eay_md5_one(rbuf);
+       if (!new->hash) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               del_recvdpkt(new);
+               return -1;
+       }
+       new->remote = dupsaddr(remote);
+       if (new->remote == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               del_recvdpkt(new);
+               return -1;
+       }
+       new->local = dupsaddr(local);
+       if (new->local == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               del_recvdpkt(new);
+               return -1;
+       }
+       new->sendbuf = vdup(sbuf);
+       if (new->sendbuf == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               del_recvdpkt(new);
+               return -1;
+       }
+
+       new->retry_counter = lcconf->retry_counter;
+       new->time_send = 0;
+       new->created = time(NULL);
+
+       LIST_INSERT_HEAD(&rcptree, new, chain);
+
+       return 0;
+}
+
+void
+del_recvdpkt(r)
+       struct recvdpkt *r;
+{
+       if (r->remote)
+               racoon_free(r->remote);
+       if (r->local)
+               racoon_free(r->local);
+       if (r->hash)
+               vfree(r->hash);
+       if (r->sendbuf)
+               vfree(r->sendbuf);
+       racoon_free(r);
+}
+
+void
+rem_recvdpkt(r)
+       struct recvdpkt *r;
+{
+       LIST_REMOVE(r, chain);
+}
+
+void
+sweep_recvdpkt(dummy)
+       void *dummy;
+{
+       struct recvdpkt *r, *next;
+       time_t t, lt;
+
+       /* set current time */
+       t = time(NULL);
+
+       /* set the lifetime of the retransmission */
+       lt = lcconf->retry_counter * lcconf->retry_interval;
+
+       for (r = LIST_FIRST(&rcptree); r; r = next) {
+               next = LIST_NEXT(r, chain);
+
+               if (t - r->created > lt) {
+                       rem_recvdpkt(r);
+                       del_recvdpkt(r);
+               }
+       }
+
+       sched_new(lt, sweep_recvdpkt, NULL);
+}
+
+void
+init_recvdpkt()
+{
+       time_t lt = lcconf->retry_counter * lcconf->retry_interval;
+
+       LIST_INIT(&rcptree);
+
+       sched_new(lt, sweep_recvdpkt, NULL);
+}