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