BCM WL 6.30.102.9 (r366174)
[tomato.git] / release / src / router / xl2tpd / network.c
blob70b460d80a6cdf16b6866e0505322dd9e643ac00
1 /*
2 * Layer Two Tunnelling Protocol Daemon
3 * Copyright (C) 1998 Adtran, Inc.
4 * Copyright (C) 2002 Jeff McAdams
6 * Mark Spencer
8 * This software is distributed under the terms
9 * of the GPL, which you should have received
10 * along with this source.
12 * Network routines for UDP handling
14 #include <stdio.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <sys/ioctl.h>
25 #include <sys/wait.h>
26 #ifndef LINUX
27 # include <sys/uio.h>
28 #endif
29 #include "l2tp.h"
30 #include "ipsecmast.h"
31 #include "misc.h" /* for IPADDY macro */
33 char hostname[256];
34 struct sockaddr_in server, from; /* Server and transmitter structs */
35 int server_socket; /* Server socket */
36 #ifdef USE_KERNEL
37 int kernel_support; /* Kernel Support there or not? */
38 #endif
40 #ifdef USE_KERNEL
41 void modprobe() {
42 char * modules[] = { "l2tp_ppp", "pppol2tp", NULL };
43 char ** module;
44 char buf[256], *tok;
45 int pid, exit_status, fd;
47 FILE * fmod = fopen("/proc/modules", "r");
49 if (fmod == NULL)
50 return;
52 while (fgets(buf, 255, fmod) != NULL) {
53 if ((tok = strtok(buf, " ")) != NULL) {
54 for (module = modules; *module != NULL; ++module) {
55 if (!strcmp(*module, tok)) {
56 fclose(fmod);
57 return;
63 fclose(fmod);
65 for (module = modules; *module != NULL; ++module) {
66 if ((pid = fork()) >= 0) {
67 if (pid == 0) {
68 setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
69 if ((fd = open("/dev/null", O_RDWR)) > -1) {
70 dup2(fd, 1);
71 dup2(fd, 2);
73 execlp("modprobe", "modprobe", "-q", *module, (char *)NULL);
74 exit(1);
75 } else {
76 if ((pid = waitpid(pid, &exit_status, 0)) != -1 && WIFEXITED(exit_status)) {
77 if (WEXITSTATUS(exit_status) == 0)
78 return;
84 #endif
86 int init_network (void)
88 long arg;
89 unsigned int length = sizeof (server);
90 gethostname (hostname, sizeof (hostname));
91 server.sin_family = AF_INET;
92 server.sin_addr.s_addr = gconfig.listenaddr;
93 server.sin_port = htons (gconfig.port);
94 int flags;
95 if ((server_socket = socket (PF_INET, SOCK_DGRAM, 0)) < 0)
97 l2tp_log (LOG_CRIT, "%s: Unable to allocate socket. Terminating.\n",
98 __FUNCTION__);
99 return -EINVAL;
102 flags = 1;
103 setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
104 setsockopt(server_socket, SOL_SOCKET, SO_NO_CHECK, &flags, sizeof(flags));
106 if (bind (server_socket, (struct sockaddr *) &server, sizeof (server)))
108 close (server_socket);
109 l2tp_log (LOG_CRIT, "%s: Unable to bind socket: %s. Terminating.\n",
110 __FUNCTION__, strerror(errno), errno);
111 return -EINVAL;
113 if (getsockname (server_socket, (struct sockaddr *) &server, &length))
115 l2tp_log (LOG_CRIT, "%s: Unable to read socket name.Terminating.\n",
116 __FUNCTION__);
117 return -EINVAL;
120 #ifdef LINUX
122 * For L2TP/IPsec with KLIPSng, set the socket to receive IPsec REFINFO
123 * values.
125 arg=1;
126 if(setsockopt(server_socket, IPPROTO_IP, gconfig.sarefnum,
127 &arg, sizeof(arg)) != 0) {
128 l2tp_log(LOG_CRIT, "setsockopt recvref[%d]: %s\n", gconfig.sarefnum, strerror(errno));
130 gconfig.ipsecsaref=0;
132 #else
133 l2tp_log(LOG_INFO, "No attempt being made to use IPsec SAref's since we're not on a Linux machine.\n");
135 #endif
137 #ifdef USE_KERNEL
138 if (gconfig.forceuserspace)
140 l2tp_log (LOG_INFO, "Not looking for kernel support.\n");
141 kernel_support = 0;
143 else
145 modprobe();
146 int kernel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
147 if (kernel_fd < 0)
149 l2tp_log (LOG_INFO, "L2TP kernel support not detected.\n");
150 kernel_support = 0;
152 else
154 close(kernel_fd);
155 l2tp_log (LOG_INFO, "Using l2tp kernel support.\n");
156 kernel_support = -1;
159 #else
160 l2tp_log (LOG_INFO, "This binary does not support kernel L2TP.\n");
161 #endif
162 arg = fcntl (server_socket, F_GETFL);
163 arg |= O_NONBLOCK;
164 fcntl (server_socket, F_SETFL, arg);
165 gconfig.port = ntohs (server.sin_port);
166 return 0;
169 inline void extract (void *buf, int *tunnel, int *call)
172 * Extract the tunnel and call #'s, and fix the order of the
173 * version
176 struct payload_hdr *p = (struct payload_hdr *) buf;
177 if (PLBIT (p->ver))
179 *tunnel = p->tid;
180 *call = p->cid;
182 else
184 *tunnel = p->length;
185 *call = p->tid;
189 inline void fix_hdr (void *buf)
192 * Fix the byte order of the header
195 struct payload_hdr *p = (struct payload_hdr *) buf;
196 _u16 ver = ntohs (p->ver);
197 if (CTBIT (p->ver))
200 * Control headers are always
201 * exactly 12 bytes big.
203 swaps (buf, 12);
205 else
207 int len = 6;
208 if (PSBIT (ver))
209 len += 2;
210 if (PLBIT (ver))
211 len += 2;
212 if (PFBIT (ver))
213 len += 4;
214 swaps (buf, len);
218 void dethrottle (void *call)
220 /* struct call *c = (struct call *)call; */
221 /* if (c->throttle) {
222 #ifdef DEBUG_FLOW
223 log(LOG_DEBUG, "%s: dethrottling call %d, and setting R-bit\n",__FUNCTION__,c->ourcid);
224 #endif c->rbit = RBIT;
225 c->throttle = 0;
226 } else {
227 log(LOG_DEBUG, "%s: call %d already dethrottled?\n",__FUNCTION__,c->ourcid);
228 } */
231 void control_xmit (void *b)
233 struct buffer *buf = (struct buffer *) b;
234 struct tunnel *t;
235 struct timeval tv;
236 int ns;
238 if (!buf)
240 l2tp_log (LOG_WARNING, "%s: called on NULL buffer!\n", __FUNCTION__);
241 return;
244 t = buf->tunnel;
245 #ifdef DEBUG_CONTROL_XMIT
246 if(t) {
247 l2tp_log (LOG_DEBUG,
248 "trying to send control packet to %d\n",
249 t->ourtid);
251 #endif
253 buf->retries++;
254 ns = ntohs (((struct control_hdr *) (buf->start))->Ns);
255 if (t)
257 if (ns < t->cLr)
259 #ifdef DEBUG_CONTROL_XMIT
260 l2tp_log (LOG_DEBUG, "%s: Tossing packet %d\n", __FUNCTION__, ns);
261 #endif
262 /* Okay, it's been received. Let's toss it now */
263 toss (buf);
264 return;
267 if (buf->retries > DEFAULT_MAX_RETRIES)
270 * Too many retries. Either kill the tunnel, or
271 * if there is no tunnel, just stop retransmitting.
273 if (t)
275 if (t->self->needclose)
277 l2tp_log (LOG_DEBUG,
278 "Unable to deliver closing message for tunnel %d. Destroying anyway.\n",
279 t->ourtid);
280 t->self->needclose = 0;
281 t->self->closing = -1;
283 else
285 l2tp_log (LOG_NOTICE,
286 "Maximum retries exceeded for tunnel %d. Closing.\n",
287 t->ourtid);
288 strcpy (t->self->errormsg, "Timeout");
289 t->self->needclose = -1;
291 call_close(t->self);
293 toss (buf);
295 else
298 * FIXME: How about adaptive timeouts?
300 tv.tv_sec = 1;
301 tv.tv_usec = 0;
302 schedule (tv, control_xmit, buf);
303 #ifdef DEBUG_CONTROL_XMIT
304 l2tp_log (LOG_DEBUG, "%s: Scheduling and transmitting packet %d\n",
305 __FUNCTION__, ns);
306 #endif
307 udp_xmit (buf, t);
311 void udp_xmit (struct buffer *buf, struct tunnel *t)
313 struct cmsghdr *cmsg;
314 char cbuf[CMSG_SPACE(sizeof (unsigned int))];
315 unsigned int *refp;
316 struct msghdr msgh;
317 int err;
318 struct iovec iov;
321 * OKAY, now send a packet with the right SAref values.
323 memset(&msgh, 0, sizeof(struct msghdr));
325 msgh.msg_control = cbuf;
326 msgh.msg_controllen = 0;
328 if(gconfig.ipsecsaref && t->refhim != IPSEC_SAREF_NULL) {
329 msgh.msg_controllen = sizeof(cbuf);
331 cmsg = CMSG_FIRSTHDR(&msgh);
332 cmsg->cmsg_level = IPPROTO_IP;
333 cmsg->cmsg_type = gconfig.sarefnum;
334 cmsg->cmsg_len = CMSG_LEN(sizeof(unsigned int));
336 if(gconfig.debug_network) {
337 l2tp_log(LOG_DEBUG,"sending with saref=%d using sarefnum=%d\n", t->refhim, gconfig.sarefnum);
339 refp = (unsigned int *)CMSG_DATA(cmsg);
340 *refp = t->refhim;
342 msgh.msg_controllen = cmsg->cmsg_len;
345 iov.iov_base = buf->start;
346 iov.iov_len = buf->len;
348 /* return packet from whence it came */
349 msgh.msg_name = &buf->peer;
350 msgh.msg_namelen = sizeof(buf->peer);
352 msgh.msg_iov = &iov;
353 msgh.msg_iovlen = 1;
354 msgh.msg_flags = 0;
357 /* Receive one packet. */
358 if ((err = sendmsg(server_socket, &msgh, 0)) < 0) {
359 l2tp_log(LOG_ERR, "udp_xmit failed to %s:%d with err=%d:%s\n",
360 IPADDY(t->peer.sin_addr), ntohs(t->peer.sin_port),
361 err,strerror(errno));
365 int build_fdset (fd_set *readfds)
367 struct tunnel *tun;
368 struct call *call;
369 int max = 0;
371 tun = tunnels.head;
372 FD_ZERO (readfds);
374 while (tun)
376 if (tun->udp_fd > -1) {
377 if (tun->udp_fd > max)
378 max = tun->udp_fd;
379 FD_SET (tun->udp_fd, readfds);
381 call = tun->call_head;
382 while (call)
384 if (call->needclose ^ call->closing)
386 call_close (call);
387 call = tun->call_head;
388 if (!call)
389 break;
390 continue;
392 if (call->fd > -1)
394 if (!call->needclose && !call->closing)
396 if (call->fd > max)
397 max = call->fd;
398 FD_SET (call->fd, readfds);
401 call = call->next;
403 /* Now that call fds have been collected, and checked for
404 * closing, check if the tunnel needs to be closed too
406 if (tun->self->needclose ^ tun->self->closing)
408 if (gconfig.debug_tunnel)
409 l2tp_log (LOG_DEBUG, "%s: closing down tunnel %d\n",
410 __FUNCTION__, tun->ourtid);
411 call_close (tun->self);
412 /* Reset the while loop
413 * and check for NULL */
414 tun = tunnels.head;
415 if (!tun)
416 break;
417 continue;
419 tun = tun->next;
421 FD_SET (server_socket, readfds);
422 if (server_socket > max)
423 max = server_socket;
424 FD_SET (control_fd, readfds);
425 if (control_fd > max)
426 max = control_fd;
427 return max;
430 void network_thread ()
433 * We loop forever waiting on either data from the ppp drivers or from
434 * our network socket. Control handling is no longer done here.
436 struct sockaddr_in from, to;
437 unsigned int fromlen;
438 int tunnel, call; /* Tunnel and call */
439 int recvsize; /* Length of data received */
440 struct buffer *buf; /* Payload buffer */
441 struct call *c, *sc; /* Call to send this off to */
442 struct tunnel *st; /* Tunnel */
443 fd_set readfds; /* Descriptors to watch for reading */
444 int max; /* Highest fd */
445 struct timeval tv, *ptv; /* Timeout for select */
446 struct msghdr msgh;
447 struct iovec iov;
448 char cbuf[256];
449 unsigned int refme, refhim;
450 int * currentfd;
451 int server_socket_processed;
453 /* This one buffer can be recycled for everything except control packets */
454 buf = new_buf (MAX_RECV_SIZE);
456 tunnel = 0;
457 call = 0;
459 for (;;)
461 int ret;
462 process_signal();
463 max = build_fdset (&readfds);
464 ptv = process_schedule(&tv);
465 ret = select (max + 1, &readfds, NULL, NULL, ptv);
466 if (ret <= 0)
468 if (ret == 0)
470 if (gconfig.debug_network)
472 l2tp_log (LOG_DEBUG, "%s: select timeout\n", __FUNCTION__);
475 else
477 if (gconfig.debug_network)
479 l2tp_log (LOG_DEBUG,
480 "%s: select returned error %d (%s)\n",
481 __FUNCTION__, errno, strerror (errno));
484 continue;
486 if (FD_ISSET (control_fd, &readfds))
488 do_control ();
490 server_socket_processed = 0;
491 currentfd = NULL;
492 st = tunnels.head;
493 while (st || !server_socket_processed) {
494 if (st && (st->udp_fd == -1)) {
495 st=st->next;
496 continue;
498 if (st) {
499 currentfd = &st->udp_fd;
500 } else {
501 currentfd = &server_socket;
502 server_socket_processed = 1;
504 if (FD_ISSET (*currentfd, &readfds))
507 * Okay, now we're ready for reading and processing new data.
509 recycle_buf (buf);
511 /* Reserve space for expanding payload packet headers */
512 buf->start += PAYLOAD_BUF;
513 buf->len -= PAYLOAD_BUF;
515 memset(&from, 0, sizeof(from));
516 memset(&to, 0, sizeof(to));
518 fromlen = sizeof(from);
520 memset(&msgh, 0, sizeof(struct msghdr));
521 iov.iov_base = buf->start;
522 iov.iov_len = buf->len;
523 msgh.msg_control = cbuf;
524 msgh.msg_controllen = sizeof(cbuf);
525 msgh.msg_name = &from;
526 msgh.msg_namelen = fromlen;
527 msgh.msg_iov = &iov;
528 msgh.msg_iovlen = 1;
529 msgh.msg_flags = 0;
531 /* Receive one packet. */
532 recvsize = recvmsg(*currentfd, &msgh, 0);
534 if (recvsize < MIN_PAYLOAD_HDR_LEN)
536 if (recvsize < 0)
538 if (errno == ECONNREFUSED) {
539 close(*currentfd);
541 if ((errno == ECONNREFUSED) ||
542 (errno == EBADF)) {
543 *currentfd = -1;
545 if (errno != EAGAIN)
546 l2tp_log (LOG_WARNING,
547 "%s: recvfrom returned error %d (%s)\n",
548 __FUNCTION__, errno, strerror (errno));
550 else
552 l2tp_log (LOG_WARNING, "%s: received too small a packet\n",
553 __FUNCTION__);
555 continue;
559 refme=refhim=0;
561 /* extract IPsec info out */
562 if(gconfig.ipsecsaref) {
563 struct cmsghdr *cmsg;
564 /* Process auxiliary received data in msgh */
565 for (cmsg = CMSG_FIRSTHDR(&msgh);
566 cmsg != NULL;
567 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
568 if (cmsg->cmsg_level == IPPROTO_IP
569 && cmsg->cmsg_type == gconfig.sarefnum) {
570 unsigned int *refp;
572 refp = (unsigned int *)CMSG_DATA(cmsg);
573 refme =refp[0];
574 refhim=refp[1];
580 * some logic could be added here to verify that we only
581 * get L2TP packets inside of IPsec, or to provide different
582 * classes of service to packets not inside of IPsec.
584 buf->len = recvsize;
585 fix_hdr (buf->start);
586 extract (buf->start, &tunnel, &call);
588 if (gconfig.debug_network)
590 l2tp_log(LOG_DEBUG, "%s: recv packet from %s, size = %d, "
591 "tunnel = %d, call = %d ref=%u refhim=%u\n",
592 __FUNCTION__, inet_ntoa (from.sin_addr),
593 recvsize, tunnel, call, refme, refhim);
596 if (gconfig.packet_dump)
598 do_packet_dump (buf);
600 if (!
601 (c = get_call (tunnel, call, from.sin_addr,
602 from.sin_port, refme, refhim)))
604 if ((c =
605 get_tunnel (tunnel, from.sin_addr.s_addr,
606 from.sin_port)))
609 * It is theoretically possible that we could be sent
610 * a control message (say a StopCCN) on a call that we
611 * have already closed or some such nonsense. To
612 * prevent this from closing the tunnel, if we get a
613 * call on a valid tunnel, but not with a valid CID,
614 * we'll just send a ZLB to ack receiving the packet.
616 if (gconfig.debug_tunnel)
617 l2tp_log (LOG_DEBUG,
618 "%s: no such call %d on tunnel %d. Sending special ZLB\n",
619 __FUNCTION__);
620 handle_special (buf, c, call);
622 /* get a new buffer */
623 buf = new_buf (MAX_RECV_SIZE);
625 else
626 l2tp_log (LOG_DEBUG,
627 "%s: unable to find call or tunnel to handle packet. call = %d, tunnel = %d Dumping.\n",
628 __FUNCTION__, call, tunnel);
631 else
633 buf->peer = from;
634 /* Handle the packet */
635 c->container->chal_us.vector = NULL;
636 if (handle_packet (buf, c->container, c))
638 if (gconfig.debug_tunnel)
639 l2tp_log (LOG_DEBUG, "%s: bad packet\n", __FUNCTION__);
641 if (c->cnu)
643 /* Send Zero Byte Packet */
644 control_zlb (buf, c->container, c);
645 c->cnu = 0;
649 if (st) st=st->next;
653 * finished obvious sources, look for data from PPP connections.
655 st = tunnels.head;
656 while (st)
658 sc = st->call_head;
659 while (sc)
661 if ((sc->fd >= 0) && FD_ISSET (sc->fd, &readfds))
663 /* Got some payload to send */
664 int result;
665 recycle_payload (buf, sc->container->peer);
667 #ifdef DEBUG_FLOW_MORE
668 l2tp_log (LOG_DEBUG, "%s: rws = %d, pSs = %d, pLr = %d\n",
669 __FUNCTION__, sc->rws, sc->pSs, sc->pLr);
670 #endif
671 if ((sc->rws>0) && (sc->pSs > sc->pLr + sc->rws) && !sc->rbit) {
672 #ifdef DEBUG_FLOW
673 log(LOG_DEBUG, "%s: throttling payload (call = %d, tunnel = %d, Lr = %d, Ss = %d, rws = %d)!\n",__FUNCTION__,
674 sc->cid, sc->container->tid, sc->pLr, sc->pSs, sc->rws);
675 #endif
676 sc->throttle = -1;
677 We unthrottle in handle_packet if we get a payload packet,
678 valid or ZLB, but we also schedule a dethrottle in which
679 case the R-bit will be set
680 FIXME: Rate Adaptive timeout?
681 tv.tv_sec = 2;
682 tv.tv_usec = 0;
683 sc->dethrottle = schedule(tv, dethrottle, sc);
684 } else */
685 /* while ((result=read_packet(buf,sc->fd,sc->frame & SYNC_FRAMING))>0) { */
686 while ((result =
687 read_packet (buf, sc->fd, SYNC_FRAMING)) > 0)
689 add_payload_hdr (sc->container, sc, buf);
690 if (gconfig.packet_dump)
692 do_packet_dump (buf);
696 sc->prx = sc->data_rec_seq_num;
697 if (sc->zlb_xmit)
699 deschedule (sc->zlb_xmit);
700 sc->zlb_xmit = NULL;
702 sc->tx_bytes += buf->len;
703 sc->tx_pkts++;
704 udp_xmit (buf, st);
705 recycle_payload (buf, sc->container->peer);
707 if (result != 0)
709 l2tp_log (LOG_WARNING,
710 "%s: tossing read packet, error = %s (%d). Closing call.\n",
711 __FUNCTION__, strerror (-result), -result);
712 strcpy (sc->errormsg, strerror (-result));
713 sc->needclose = -1;
716 sc = sc->next;
718 st = st->next;
724 int connect_pppol2tp(struct tunnel *t) {
725 #ifdef USE_KERNEL
726 if (kernel_support) {
727 int ufd = -1, fd2 = -1;
728 int flags;
729 struct sockaddr_pppol2tp sax;
731 struct sockaddr_in server;
732 server.sin_family = AF_INET;
733 server.sin_addr.s_addr = gconfig.listenaddr;
734 server.sin_port = htons (gconfig.port);
735 if ((ufd = socket (PF_INET, SOCK_DGRAM, 0)) < 0)
737 l2tp_log (LOG_CRIT, "%s: Unable to allocate UDP socket. Terminating.\n",
738 __FUNCTION__);
739 return -EINVAL;
742 flags=1;
743 setsockopt(ufd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
744 setsockopt(ufd, SOL_SOCKET, SO_NO_CHECK, &flags, sizeof(flags));
746 if (bind (ufd, (struct sockaddr *) &server, sizeof (server)))
748 close (ufd);
749 l2tp_log (LOG_CRIT, "%s: Unable to bind UDP socket: %s. Terminating.\n",
750 __FUNCTION__, strerror(errno), errno);
751 return -EINVAL;
753 server = t->peer;
754 flags = fcntl(ufd, F_GETFL);
755 if (flags == -1 || fcntl(ufd, F_SETFL, flags | O_NONBLOCK) == -1) {
756 l2tp_log (LOG_WARNING, "%s: Unable to set UDP socket nonblock.\n",
757 __FUNCTION__);
758 return -EINVAL;
760 if (connect (ufd, (struct sockaddr *) &server, sizeof(server)) < 0) {
761 l2tp_log (LOG_CRIT, "%s: Unable to connect UDP peer. Terminating.\n",
762 __FUNCTION__);
763 return -EINVAL;
766 t->udp_fd=ufd;
768 fd2 = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
769 if (fd2 < 0) {
770 l2tp_log (LOG_WARNING, "%s: Unable to allocate PPPoL2TP socket.\n",
771 __FUNCTION__);
772 return -EINVAL;
774 flags = fcntl(fd2, F_GETFL);
775 if (flags == -1 || fcntl(fd2, F_SETFL, flags | O_NONBLOCK) == -1) {
776 l2tp_log (LOG_WARNING, "%s: Unable to set PPPoL2TP socket nonblock.\n",
777 __FUNCTION__);
778 return -EINVAL;
780 sax.sa_family = AF_PPPOX;
781 sax.sa_protocol = PX_PROTO_OL2TP;
782 sax.pppol2tp.pid = 0;
783 sax.pppol2tp.fd = t->udp_fd;
784 sax.pppol2tp.addr.sin_addr.s_addr = t->peer.sin_addr.s_addr;
785 sax.pppol2tp.addr.sin_port = t->peer.sin_port;
786 sax.pppol2tp.addr.sin_family = AF_INET;
787 sax.pppol2tp.s_tunnel = t->ourtid;
788 sax.pppol2tp.s_session = 0;
789 sax.pppol2tp.d_tunnel = t->tid;
790 sax.pppol2tp.d_session = 0;
791 if ((connect(fd2, (struct sockaddr *)&sax, sizeof(sax))) < 0) {
792 l2tp_log (LOG_WARNING, "%s: Unable to connect PPPoL2TP socket. %d %s\n",
793 __FUNCTION__, errno, strerror(errno));
794 close(fd2);
795 return -EINVAL;
797 t->pppox_fd = fd2;
799 #endif
800 return 0;