1/* $KAME: handler.c,v 1.57 2002/01/21 08:45:54 sakane Exp $ */ 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, sysdep_sa_len(iph1->remote)); 172 memcpy(&pd->local, iph1->local, sysdep_sa_len(iph1->local)); 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 265 racoon_free(iph1); 266} 267 268/* 269 * create new isakmp Phase 1 status record to handle isakmp in Phase1 270 */ 271int 272insph1(iph1) 273 struct ph1handle *iph1; 274{ 275 /* validity check */ 276 if (iph1->remote == NULL) { 277 plog(LLV_ERROR, LOCATION, NULL, 278 "invalid isakmp SA handler. no remote address.\n"); 279 return -1; 280 } 281 LIST_INSERT_HEAD(&ph1tree, iph1, chain); 282 283 return 0; 284} 285 286void 287remph1(iph1) 288 struct ph1handle *iph1; 289{ 290 LIST_REMOVE(iph1, chain); 291} 292 293/* 294 * flush isakmp-sa 295 */ 296void 297flushph1() 298{ 299 struct ph1handle *p, *next; 300 301 for (p = LIST_FIRST(&ph1tree); p; p = next) { 302 next = LIST_NEXT(p, chain); 303 304 /* send delete information */ 305 if (p->status == PHASE1ST_ESTABLISHED) 306 isakmp_info_send_d1(p); 307 308 remph1(p); 309 delph1(p); 310 } 311} 312 313void 314initph1tree() 315{ 316 LIST_INIT(&ph1tree); 317} 318 319/* %%% management phase 2 handler */ 320/* 321 * search ph2handle with policy id. 322 */ 323struct ph2handle * 324getph2byspid(spid) 325 u_int32_t spid; 326{ 327 struct ph2handle *p; 328 329 LIST_FOREACH(p, &ph2tree, chain) { 330 /* 331 * there are ph2handle independent on policy 332 * such like informational exchange. 333 */ 334 if (p->spid == spid) 335 return p; 336 } 337 338 return NULL; 339} 340 341/* 342 * search ph2handle with sequence number. 343 */ 344struct ph2handle * 345getph2byseq(seq) 346 u_int32_t seq; 347{ 348 struct ph2handle *p; 349 350 LIST_FOREACH(p, &ph2tree, chain) { 351 if (p->seq == seq) 352 return p; 353 } 354 355 return NULL; 356} 357 358/* 359 * search ph2handle with message id. 360 */ 361struct ph2handle * 362getph2bymsgid(iph1, msgid) 363 struct ph1handle *iph1; 364 u_int32_t msgid; 365{ 366 struct ph2handle *p; 367 368 LIST_FOREACH(p, &ph2tree, chain) { 369 if (p->msgid == msgid) 370 return p; 371 } 372 373 return NULL; 374} 375 376/* 377 * call by pk_recvexpire(). 378 */ 379struct ph2handle * 380getph2bysaidx(src, dst, proto_id, spi) 381 struct sockaddr *src, *dst; 382 u_int proto_id; 383 u_int32_t spi; 384{ 385 struct ph2handle *iph2; 386 struct saproto *pr; 387 388 LIST_FOREACH(iph2, &ph2tree, chain) { 389 if (iph2->proposal == NULL && iph2->approval == NULL) 390 continue; 391 if (iph2->approval != NULL) { 392 for (pr = iph2->approval->head; pr != NULL; 393 pr = pr->next) { 394 if (proto_id != pr->proto_id) 395 break; 396 if (spi == pr->spi || spi == pr->spi_p) 397 return iph2; 398 } 399 } else if (iph2->proposal != NULL) { 400 for (pr = iph2->proposal->head; pr != NULL; 401 pr = pr->next) { 402 if (proto_id != pr->proto_id) 403 break; 404 if (spi == pr->spi) 405 return iph2; 406 } 407 } 408 } 409 410 return NULL; 411} 412 413/* 414 * create new isakmp Phase 2 status record to handle isakmp in Phase2 415 */ 416struct ph2handle * 417newph2() 418{ 419 struct ph2handle *iph2 = NULL; 420 421 /* create new iph2 */ 422 iph2 = racoon_calloc(1, sizeof(*iph2)); 423 if (iph2 == NULL) 424 return NULL; 425 426 iph2->status = PHASE1ST_SPAWN; 427 428 return iph2; 429} 430 431/* 432 * initialize ph2handle 433 * NOTE: don't initialize src/dst. 434 * SPI in the proposal is cleared. 435 */ 436void 437initph2(iph2) 438 struct ph2handle *iph2; 439{ 440 sched_scrub_param(iph2); 441 iph2->sce = NULL; 442 iph2->scr = NULL; 443 444 VPTRINIT(iph2->sendbuf); 445 VPTRINIT(iph2->msg1); 446 447 /* clear spi, keep variables in the proposal */ 448 if (iph2->proposal) { 449 struct saproto *pr; 450 for (pr = iph2->proposal->head; pr != NULL; pr = pr->next) 451 pr->spi = 0; 452 } 453 454 /* clear approval */ 455 if (iph2->approval) { 456 flushsaprop(iph2->approval); 457 iph2->approval = NULL; 458 } 459 460 /* clear the generated policy */ 461 if (iph2->spidx_gen) { 462 delsp_bothdir((struct policyindex *)iph2->spidx_gen); 463 racoon_free(iph2->spidx_gen); 464 iph2->spidx_gen = NULL; 465 } 466 467 if (iph2->pfsgrp) { 468 oakley_dhgrp_free(iph2->pfsgrp); 469 iph2->pfsgrp = NULL; 470 } 471 472 VPTRINIT(iph2->dhpriv); 473 VPTRINIT(iph2->dhpub); 474 VPTRINIT(iph2->dhpub_p); 475 VPTRINIT(iph2->dhgxy); 476 VPTRINIT(iph2->id); 477 VPTRINIT(iph2->id_p); 478 VPTRINIT(iph2->nonce); 479 VPTRINIT(iph2->nonce_p); 480 VPTRINIT(iph2->sa); 481 VPTRINIT(iph2->sa_ret); 482 483 if (iph2->ivm) { 484 oakley_delivm(iph2->ivm); 485 iph2->ivm = NULL; 486 } 487} 488 489/* 490 * delete new isakmp Phase 2 status record to handle isakmp in Phase2 491 */ 492void 493delph2(iph2) 494 struct ph2handle *iph2; 495{ 496 initph2(iph2); 497 498 if (iph2->src) { 499 racoon_free(iph2->src); 500 iph2->src = NULL; 501 } 502 if (iph2->dst) { 503 racoon_free(iph2->dst); 504 iph2->dst = NULL; 505 } 506 if (iph2->src_id) { 507 racoon_free(iph2->src_id); 508 iph2->src_id = NULL; 509 } 510 if (iph2->dst_id) { 511 racoon_free(iph2->dst_id); 512 iph2->dst_id = NULL; 513 } 514 515 if (iph2->proposal) { 516 flushsaprop(iph2->proposal); 517 iph2->proposal = NULL; 518 } 519 520 racoon_free(iph2); 521} 522 523/* 524 * create new isakmp Phase 2 status record to handle isakmp in Phase2 525 */ 526int 527insph2(iph2) 528 struct ph2handle *iph2; 529{ 530 LIST_INSERT_HEAD(&ph2tree, iph2, chain); 531 532 return 0; 533} 534 535void 536remph2(iph2) 537 struct ph2handle *iph2; 538{ 539 LIST_REMOVE(iph2, chain); 540} 541 542void 543initph2tree() 544{ 545 LIST_INIT(&ph2tree); 546} 547 548void 549flushph2() 550{ 551 struct ph2handle *p, *next; 552 553 for (p = LIST_FIRST(&ph2tree); p; p = next) { 554 next = LIST_NEXT(p, chain); 555 556 /* send delete information */ 557 if (p->status == PHASE2ST_ESTABLISHED) 558 isakmp_info_send_d2(p); 559 560 unbindph12(p); 561 remph2(p); 562 delph2(p); 563 } 564} 565 566/* 567 * Delete all Phase 2 handlers for this src/dst/proto. This 568 * is used during INITIAL-CONTACT processing (so no need to 569 * send a message to the peer). 570 */ 571void 572deleteallph2(src, dst, proto_id) 573 struct sockaddr *src, *dst; 574 u_int proto_id; 575{ 576 struct ph2handle *iph2, *next; 577 struct saproto *pr; 578 579 for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) { 580 next = LIST_NEXT(iph2, chain); 581 if (iph2->proposal == NULL && iph2->approval == NULL) 582 continue; 583 if (iph2->approval != NULL) { 584 for (pr = iph2->approval->head; pr != NULL; 585 pr = pr->next) { 586 if (proto_id == pr->proto_id) 587 goto zap_it; 588 } 589 } else if (iph2->proposal != NULL) { 590 for (pr = iph2->proposal->head; pr != NULL; 591 pr = pr->next) { 592 if (proto_id == pr->proto_id) 593 goto zap_it; 594 } 595 } 596 continue; 597 zap_it: 598 unbindph12(iph2); 599 remph2(iph2); 600 delph2(iph2); 601 } 602} 603 604/* %%% */ 605void 606bindph12(iph1, iph2) 607 struct ph1handle *iph1; 608 struct ph2handle *iph2; 609{ 610 iph2->ph1 = iph1; 611 LIST_INSERT_HEAD(&iph1->ph2tree, iph2, ph1bind); 612} 613 614void 615unbindph12(iph2) 616 struct ph2handle *iph2; 617{ 618 if (iph2->ph1 != NULL) { 619 iph2->ph1 = NULL; 620 LIST_REMOVE(iph2, ph1bind); 621 } 622} 623 624/* %%% management contacted list */ 625/* 626 * search contacted list. 627 */ 628struct contacted * 629getcontacted(remote) 630 struct sockaddr *remote; 631{ 632 struct contacted *p; 633 634 LIST_FOREACH(p, &ctdtree, chain) { 635 if (cmpsaddrstrict(remote, p->remote) == 0) 636 return p; 637 } 638 639 return NULL; 640} 641 642/* 643 * create new isakmp Phase 2 status record to handle isakmp in Phase2 644 */ 645int 646inscontacted(remote) 647 struct sockaddr *remote; 648{ 649 struct contacted *new; 650 651 /* create new iph2 */ 652 new = racoon_calloc(1, sizeof(*new)); 653 if (new == NULL) 654 return -1; 655 656 new->remote = dupsaddr(remote); 657 658 LIST_INSERT_HEAD(&ctdtree, new, chain); 659 660 return 0; 661} 662 663void 664initctdtree() 665{ 666 LIST_INIT(&ctdtree); 667} 668 669/* 670 * check the response has been sent to the peer. when not, simply reply 671 * the buffered packet to the peer. 672 * OUT: 673 * 0: the packet is received at the first time. 674 * 1: the packet was processed before. 675 * 2: the packet was processed before, but the address mismatches. 676 * -1: error happened. 677 */ 678int 679check_recvdpkt(remote, local, rbuf) 680 struct sockaddr *remote, *local; 681 vchar_t *rbuf; 682{ 683 vchar_t *hash; 684 struct recvdpkt *r; 685 time_t t; 686 int len, s; 687 688 /* set current time */ 689 t = time(NULL); 690 691 hash = eay_md5_one(rbuf); 692 if (!hash) { 693 plog(LLV_ERROR, LOCATION, NULL, 694 "failed to allocate buffer.\n"); 695 return -1; 696 } 697 698 LIST_FOREACH(r, &rcptree, chain) { 699 if (memcmp(hash->v, r->hash->v, r->hash->l) == 0) 700 break; 701 } 702 vfree(hash); 703 704 /* this is the first time to receive the packet */ 705 if (r == NULL) 706 return 0; 707 708 /* 709 * the packet was processed before, but the remote address mismatches. 710 */ 711 if (cmpsaddrstrict(remote, r->remote) != 0) 712 return 2; 713 714 /* 715 * it should not check the local address because the packet 716 * may arrive at other interface. 717 */ 718 719 /* check the previous time to send */ 720 if (t - r->time_send < 1) { 721 plog(LLV_WARNING, LOCATION, NULL, 722 "the packet retransmitted in a short time from %s\n", 723 saddr2str(remote)); 724 } 725 726 /* select the socket to be sent */ 727 s = getsockmyaddr(r->local); 728 if (s == -1) 729 return -1; 730 731 /* resend the packet if needed */ 732 len = sendfromto(s, r->sendbuf->v, r->sendbuf->l, 733 r->local, r->remote, lcconf->count_persend); 734 if (len == -1) { 735 plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n"); 736 return -1; 737 } 738 739 /* check the retry counter */ 740 r->retry_counter--; 741 if (r->retry_counter <= 0) { 742 rem_recvdpkt(r); 743 del_recvdpkt(r); 744 plog(LLV_DEBUG, LOCATION, NULL, 745 "deleted the retransmission packet to %s.\n", 746 saddr2str(remote)); 747 } else 748 r->time_send = t; 749 750 return 1; 751} 752 753/* 754 * adding a hash of received packet into the received list. 755 */ 756int 757add_recvdpkt(remote, local, sbuf, rbuf) 758 struct sockaddr *remote, *local; 759 vchar_t *sbuf, *rbuf; 760{ 761 struct recvdpkt *new = NULL; 762 763 if (lcconf->retry_counter == 0) { 764 /* no need to add it */ 765 return 0; 766 } 767 768 new = racoon_calloc(1, sizeof(*new)); 769 if (!new) { 770 plog(LLV_ERROR, LOCATION, NULL, 771 "failed to allocate buffer.\n"); 772 return -1; 773 } 774 775 new->hash = eay_md5_one(rbuf); 776 if (!new->hash) { 777 plog(LLV_ERROR, LOCATION, NULL, 778 "failed to allocate buffer.\n"); 779 del_recvdpkt(new); 780 return -1; 781 } 782 new->remote = dupsaddr(remote); 783 if (new->remote == NULL) { 784 plog(LLV_ERROR, LOCATION, NULL, 785 "failed to allocate buffer.\n"); 786 del_recvdpkt(new); 787 return -1; 788 } 789 new->local = dupsaddr(local); 790 if (new->local == NULL) { 791 plog(LLV_ERROR, LOCATION, NULL, 792 "failed to allocate buffer.\n"); 793 del_recvdpkt(new); 794 return -1; 795 } 796 new->sendbuf = vdup(sbuf); 797 if (new->sendbuf == NULL) { 798 plog(LLV_ERROR, LOCATION, NULL, 799 "failed to allocate buffer.\n"); 800 del_recvdpkt(new); 801 return -1; 802 } 803 804 new->retry_counter = lcconf->retry_counter; 805 new->time_send = 0; 806 new->created = time(NULL); 807 808 LIST_INSERT_HEAD(&rcptree, new, chain); 809 810 return 0; 811} 812 813void 814del_recvdpkt(r) 815 struct recvdpkt *r; 816{ 817 if (r->remote) 818 racoon_free(r->remote); 819 if (r->local) 820 racoon_free(r->local); 821 if (r->hash) 822 vfree(r->hash); 823 if (r->sendbuf) 824 vfree(r->sendbuf); 825 racoon_free(r); 826} 827 828void 829rem_recvdpkt(r) 830 struct recvdpkt *r; 831{ 832 LIST_REMOVE(r, chain); 833} 834 835void 836sweep_recvdpkt(dummy) 837 void *dummy; 838{ 839 struct recvdpkt *r, *next; 840 time_t t, lt; 841 842 /* set current time */ 843 t = time(NULL); 844 845 /* set the lifetime of the retransmission */ 846 lt = lcconf->retry_counter * lcconf->retry_interval; 847 848 for (r = LIST_FIRST(&rcptree); r; r = next) { 849 next = LIST_NEXT(r, chain); 850 851 if (t - r->created > lt) { 852 rem_recvdpkt(r); 853 del_recvdpkt(r); 854 } 855 } 856 857 sched_new(lt, sweep_recvdpkt, NULL); 858} 859 860void 861init_recvdpkt() 862{ 863 time_t lt = lcconf->retry_counter * lcconf->retry_interval; 864 865 LIST_INIT(&rcptree); 866 867 sched_new(lt, sweep_recvdpkt, NULL); 868} 869