示例#1
0
文件: ipl2tp.c 项目: AllardJ/Tomato
static int do_show(int argc, char **argv)
{
	struct l2tp_data data;
	struct l2tp_parm *p = &data.config;

	if (parse_args(argc, argv, L2TP_GET, p) < 0)
		return -1;

	if (!p->tunnel && !p->session)
		missarg("tunnel or session");

	if (p->session)
		get_session(&data);
	else
		get_tunnel(&data);

	return 0;
}
示例#2
0
文件: network.c 项目: ff94315/rt-n56u
void network_thread ()
{
    /*
     * We loop forever waiting on either data from the ppp drivers or from
     * our network socket.  Control handling is no longer done here.
     */
    struct sockaddr_in from;
    struct in_pktinfo to;
    unsigned int fromlen;
    int tunnel, call;           /* Tunnel and call */
    int recvsize;               /* Length of data received */
    struct buffer *buf;         /* Payload buffer */
    struct call *c, *sc;        /* Call to send this off to */
    struct tunnel *st;          /* Tunnel */
    fd_set readfds;             /* Descriptors to watch for reading */
    int max;                    /* Highest fd */
    struct timeval tv, *ptv;    /* Timeout for select */
    struct msghdr msgh;
    struct iovec iov;
    char cbuf[256];
    unsigned int refme, refhim;
    int * currentfd;
    int server_socket_processed;

#ifdef HIGH_PRIO
    /* set high priority */
    if (setpriority(PRIO_PROCESS, 0, -20) < 0)
	l2tp_log (LOG_INFO, "xl2tpd: can't set priority to high: %m");
#endif

    /* This one buffer can be recycled for everything except control packets */
    buf = new_buf (MAX_RECV_SIZE);

    tunnel = 0;
    call = 0;

    for (;;)
    {
        int ret;
        process_signal();
        max = build_fdset (&readfds);
        ptv = process_schedule(&tv);
        ret = select (max + 1, &readfds, NULL, NULL, ptv);

        if (ret <= 0)
        {
#ifdef DEBUG_MORE
            if (ret == 0)
            {
                if (gconfig.debug_network)
                {
                    l2tp_log (LOG_DEBUG, "%s: select timeout\n", __FUNCTION__);
                }
            }
            else
            {
                if (gconfig.debug_network)
                {
                    l2tp_log (LOG_DEBUG,
                        "%s: select returned error %d (%s)\n",
                        __FUNCTION__, errno, strerror (errno));
                }
            }
#endif
            continue;
        }

        if (FD_ISSET (control_fd, &readfds))
        {
            do_control ();
        }
        server_socket_processed = 0;
        currentfd = NULL;
        st = tunnels.head;
        while (st || !server_socket_processed) {
            if (st && (st->udp_fd == -1)) {
                st=st->next;
                continue;
            }
            if (st) {
                currentfd = &st->udp_fd;
            } else {
                currentfd = &server_socket;
                server_socket_processed = 1;
            }
            if (FD_ISSET (*currentfd, &readfds))
        {
            /*
             * Okay, now we're ready for reading and processing new data.
             */
            recycle_buf (buf);

            /* Reserve space for expanding payload packet headers */
            buf->start += PAYLOAD_BUF;
            buf->len -= PAYLOAD_BUF;

	    memset(&from, 0, sizeof(from));
	    memset(&to,   0, sizeof(to));
	    
	    fromlen = sizeof(from);
	    
	    memset(&msgh, 0, sizeof(struct msghdr));
	    iov.iov_base = buf->start;
	    iov.iov_len  = buf->len;
	    msgh.msg_control = cbuf;
	    msgh.msg_controllen = sizeof(cbuf);
	    msgh.msg_name = &from;
	    msgh.msg_namelen = fromlen;
	    msgh.msg_iov  = &iov;
	    msgh.msg_iovlen = 1;
	    msgh.msg_flags = 0;
	    
	    /* Receive one packet. */
	    recvsize = recvmsg(*currentfd, &msgh, 0);

            if (recvsize < MIN_PAYLOAD_HDR_LEN)
            {
                if (recvsize < 0)
                {
                    if (errno == ECONNREFUSED) {
                        close(*currentfd);
                    }
                    if ((errno == ECONNREFUSED) ||
                        (errno == EBADF)) {
                        *currentfd = -1;
                    }
                    if (errno != EAGAIN)
                        l2tp_log (LOG_WARNING,
                             "%s: recvfrom returned error %d (%s)\n",
                             __FUNCTION__, errno, strerror (errno));
                }
                else
                {
                    l2tp_log (LOG_WARNING, "%s: received too small a packet\n",
                         __FUNCTION__);
                }
                if (st) st=st->next;
		continue;
            }


	    refme=refhim=0;


		struct cmsghdr *cmsg;
		/* Process auxiliary received data in msgh */
		for (cmsg = CMSG_FIRSTHDR(&msgh);
			cmsg != NULL;
			cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
			/* extract destination(our) addr */
			if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
				struct in_pktinfo* pktInfo = ((struct in_pktinfo*)CMSG_DATA(cmsg));
				to = *pktInfo;
			}
			/* extract IPsec info out */
			else if (gconfig.ipsecsaref && cmsg->cmsg_level == IPPROTO_IP
			&& cmsg->cmsg_type == gconfig.sarefnum) {
				unsigned int *refp;
				
				refp = (unsigned int *)CMSG_DATA(cmsg);
				refme =refp[0];
				refhim=refp[1];
			}
		}

	    /*
	     * some logic could be added here to verify that we only
	     * get L2TP packets inside of IPsec, or to provide different
	     * classes of service to packets not inside of IPsec.
	     */
	    buf->len = recvsize;
	    fix_hdr (buf->start);
	    extract (buf->start, &tunnel, &call);

	    if (gconfig.debug_network)
	    {
		l2tp_log(LOG_DEBUG, "%s: recv packet from %s, size = %d, "
			 "tunnel = %d, call = %d ref=%u refhim=%u\n",
			 __FUNCTION__, inet_ntoa (from.sin_addr),
			 recvsize, tunnel, call, refme, refhim);
	    }

	    if (gconfig.packet_dump)
	    {
		do_packet_dump (buf);
	    }
			if (!(c = get_call (tunnel, call, from.sin_addr,
			       from.sin_port, refme, refhim)))
	    {
				if ((c = get_tunnel (tunnel, from.sin_addr.s_addr, from.sin_port)))
		{
		    /*
		     * It is theoretically possible that we could be sent
		     * a control message (say a StopCCN) on a call that we
		     * have already closed or some such nonsense.  To
		     * prevent this from closing the tunnel, if we get a
		     * call on a valid tunnel, but not with a valid CID,
		     * we'll just send a ZLB to ack receiving the packet.
		     */
		    if (gconfig.debug_tunnel)
			l2tp_log (LOG_DEBUG,
				  "%s: no such call %d on tunnel %d. Sending special ZLB\n",
				  __FUNCTION__, call, tunnel);
		    if (handle_special (buf, c, call) == 0)
			/* get a new buffer */
			buf = new_buf (MAX_RECV_SIZE);
		}
#ifdef DEBUG_MORE
		else{
		    l2tp_log (LOG_DEBUG,
			      "%s: unable to find call or tunnel to handle packet.  call = %d, tunnel = %d Dumping.\n",
			      __FUNCTION__, call, tunnel);
		    }
#endif
	    }
	    else
	    {
		if (c->container) {
			c->container->my_addr = to;
		}

		buf->peer = from;
		/* Handle the packet */
		c->container->chal_us.vector = NULL;
		if (handle_packet (buf, c->container, c))
		{
		    if (gconfig.debug_tunnel)
			l2tp_log (LOG_DEBUG, "%s: bad packet\n", __FUNCTION__);
		}
		if (c->cnu)
		{
		    /* Send Zero Byte Packet */
		    control_zlb (buf, c->container, c);
		    c->cnu = 0;
		}
		}
	}
	if (st) st=st->next;
	}

	/*
	 * finished obvious sources, look for data from PPP connections.
	 */
	st = tunnels.head;
        while (st)
        {
            sc = st->call_head;
            while (sc)
            {
                if ((sc->fd >= 0) && FD_ISSET (sc->fd, &readfds))
                {
                    /* Got some payload to send */
                    int result;

                    while ((result = read_packet (sc)) > 0)
                    {
                        add_payload_hdr (sc->container, sc, sc->ppp_buf);
                        if (gconfig.packet_dump)
                        {
                            do_packet_dump (sc->ppp_buf);
                        }


                        sc->prx = sc->data_rec_seq_num;
                        if (sc->zlb_xmit)
                        {
                            deschedule (sc->zlb_xmit);
                            sc->zlb_xmit = NULL;
                        }
                        sc->tx_bytes += sc->ppp_buf->len;
                        sc->tx_pkts++;
                        udp_xmit (sc->ppp_buf, st);
                        recycle_payload (sc->ppp_buf, sc->container->peer);
                    }
                    if (result != 0)
                    {
                        l2tp_log (LOG_WARNING,
                             "%s: tossing read packet, error = %s (%d).  Closing call.\n",
                             __FUNCTION__, strerror (-result), -result);
                        strcpy (sc->errormsg, strerror (-result));
                        sc->needclose = -1;
                    }
                }
                sc = sc->next;
            }
            st = st->next;
        }
    }

}
示例#3
0
int main(int argc, char **argv)
{
	if (argc < 5) {
		printf("Usage: %s <tunN> <port> <secret> options...\n"
				"\n"
				"Options:\n"
				"  -m <MTU> for the maximum transmission unit\n"
				"  -a <address/prefix-length> for the private address\n"
				"  -r <address/prefix-length> for the forwarding route\n"
				"  -d <address> for the domain name server\n"
				"  -s <domain> for the search domain\n"
				"\n"
				"Note that TUN interface needs to be configured properly\n"
				"BEFORE running this program. For more information, please\n"
				"read the comments in the source code.\n\n", argv[0]);
		exit(1);
	}

	strcpy(_hi_secret, argv[3]);
	memcpy(_ll_argv, argv, argc * sizeof(argv[0]));
	_ll_argc = argc;

	// Wait for a tunnel.
	int tunnel;
	int dirty = 0;
	time_t lastup = time(NULL);

	// Get TUN interface.
	int interface = get_interface(argv[1]);

	do {
		int maxfd;
		int count;
		fd_set readfds;
		struct timeval timeout;

		dirty = 0;
		lastup = time(NULL);
		tunnel = get_tunnel(argv[2]);

		maxfd = (tunnel > interface? tunnel: interface);
		for (; ; ) {
			FD_ZERO(&readfds);
			FD_SET(tunnel, &readfds);
			FD_SET(interface, &readfds);

			timeout.tv_sec = 1;
			timeout.tv_usec = 0;
			count = select(maxfd + 1, &readfds, NULL, NULL, &timeout);

			if (count == -1) {
				fprintf(stderr, "select error %s\n", strerror(errno));
				exit(-1);
			}

			if (count > 0) {
				int length;
				int tunnel_prepare;
				int interface_prepare;

				struct sockaddr from;
				unsigned char packet[2048];
				socklen_t fromlen = sizeof(from);

				tunnel_prepare = FD_ISSET(tunnel, &readfds);
				interface_prepare = FD_ISSET(interface, &readfds);

				do {
					if (tunnel_prepare) {
						length = recvfrom(tunnel, packet, sizeof(packet), MSG_DONTWAIT, &from, &fromlen);

						tunnel_prepare = 0;
						if (length > 0) {
							tunnel_prepare = 1;
							if (length > LEN_PADDING + (int)sizeof(struct ipv4_info) && packet[LEN_PADDING]) {
								int len = length - LEN_PADDING;
								const unsigned char *adj = packet + LEN_PADDING;

								if (is_same_network(adj, len)) {
									/* route packet to other device. */
									dispatch_packet(tunnel, adj, len, &from, fromlen);
								} else {
									/* dispatch to tun device. */
									write(interface, adj, len);
								}

								lastup = time(NULL);
								dirty = 1;
							} else if (length > LEN_PADDING) {
								int len = length - LEN_PADDING;
								const unsigned char *adj = packet + LEN_PADDING;
								fprintf(stderr, "recvfrom %d %d %d\n", length, fromlen, from.sa_family);
								packet[length] = 0;
								handshake_packet(tunnel, adj, len, &from, fromlen);
								lastup = time(NULL);
								dirty = 1;
							}
						}
					}

					if (interface_prepare) {
						length = read(interface, packet, sizeof(packet));

						interface_prepare = 0;
						if (length > (int)sizeof(struct ipv4_info)) {
							interface_prepare = 1;
							dispatch_packet(tunnel, packet, length, NULL, 0);
						}
					}

				} while (tunnel_prepare || interface_prepare);

				continue;
			}

			if (dirty && lastup + 60 < time(NULL)) {
				fprintf(stderr, "idle for long time, try to recreate interface\n");
				break;
			}
		}

		close(tunnel);

	} while (true);

	close(interface);

	return 0;
}
示例#4
0
void network_thread ()
{
    /*
     * We loop forever waiting on either data from the ppp drivers or from
     * our network socket.  Control handling is no longer done here.
     */
    struct sockaddr_in from, to;
    unsigned int fromlen, tolen;
    int tunnel, call;           /* Tunnel and call */
    int recvsize;               /* Length of data received */
    struct buffer *buf;         /* Payload buffer */
    struct call *c, *sc;        /* Call to send this off to */
    struct tunnel *st;          /* Tunnel */
    fd_set readfds;             /* Descriptors to watch for reading */
    int max;                    /* Highest fd */
    struct timeval tv, *ptv;    /* Timeout for select */
    struct msghdr msgh;
    struct iovec iov;
    char cbuf[256];
    unsigned int refme, refhim;

    /* This one buffer can be recycled for everything except control packets */
    buf = new_buf (MAX_RECV_SIZE);

    tunnel = 0;
    call = 0;

    for (;;)
    {
        int ret;
        process_signal();
        max = build_fdset (&readfds);
        ptv = process_schedule(&tv);
        ret = select (max + 1, &readfds, NULL, NULL, ptv);
        if (ret <= 0)
        {
            if (ret == 0)
            {
                if (gconfig.debug_network)
                {
                    l2tp_log (LOG_DEBUG, "%s: select timeout\n", __FUNCTION__);
                }
            }
            else
            {
                if (gconfig.debug_network)
                {
                    l2tp_log (LOG_DEBUG,
                        "%s: select returned error %d (%s)\n",
                        __FUNCTION__, errno, strerror (errno));
                }
            }
            continue;
        }
        if (FD_ISSET (control_fd, &readfds))
        {
            do_control ();
        }
        if (FD_ISSET (server_socket, &readfds))
        {
            /*
             * Okay, now we're ready for reading and processing new data.
             */
            recycle_buf (buf);

            /* Reserve space for expanding payload packet headers */
            buf->start += PAYLOAD_BUF;
            buf->len -= PAYLOAD_BUF;

	    memset(&from, 0, sizeof(from));
	    memset(&to,   0, sizeof(to));
	    
	    fromlen = sizeof(from);
	    tolen   = sizeof(to);
	    
	    memset(&msgh, 0, sizeof(struct msghdr));
	    iov.iov_base = buf->start;
	    iov.iov_len  = buf->len;
	    msgh.msg_control = cbuf;
	    msgh.msg_controllen = sizeof(cbuf);
	    msgh.msg_name = &from;
	    msgh.msg_namelen = fromlen;
	    msgh.msg_iov  = &iov;
	    msgh.msg_iovlen = 1;
	    msgh.msg_flags = 0;
	    
	    /* Receive one packet. */
	    recvsize = recvmsg(server_socket, &msgh, 0);

            if (recvsize < MIN_PAYLOAD_HDR_LEN)
            {
                if (recvsize < 0)
                {
                    if (errno != EAGAIN)
                        l2tp_log (LOG_WARNING,
                             "%s: recvfrom returned error %d (%s)\n",
                             __FUNCTION__, errno, strerror (errno));
                }
                else
                {
                    l2tp_log (LOG_WARNING, "%s: received too small a packet\n",
                         __FUNCTION__);
                }
		continue;
            }


	    refme=refhim=0;

	    /* extract IPsec info out */
	    if(gconfig.ipsecsaref) {
		    struct cmsghdr *cmsg;
		    /* Process auxiliary received data in msgh */
		    for (cmsg = CMSG_FIRSTHDR(&msgh);
			 cmsg != NULL;
			 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
			    if (cmsg->cmsg_level == IPPROTO_IP
				&& cmsg->cmsg_type == IP_IPSEC_REFINFO) {
				    unsigned int *refp;
				    
				    refp = (unsigned int *)CMSG_DATA(cmsg);
				    refme =refp[0];
				    refhim=refp[1];
			    }
		    }
	    }

	    /*
	     * some logic could be added here to verify that we only
	     * get L2TP packets inside of IPsec, or to provide different
	     * classes of service to packets not inside of IPsec.
	     */
	    buf->len = recvsize;
	    fix_hdr (buf->start);
	    extract (buf->start, &tunnel, &call);

	    if (gconfig.debug_network)
	    {
		l2tp_log(LOG_DEBUG, "%s: recv packet from %s, size = %d, "
			 "tunnel = %d, call = %d ref=%u refhim=%u\n",
			 __FUNCTION__, inet_ntoa (from.sin_addr),
			 recvsize, tunnel, call, refme, refhim);
	    }

	    if (gconfig.packet_dump)
	    {
		do_packet_dump (buf);
	    }
	    if (!
		(c = get_call (tunnel, call, from.sin_addr.s_addr,
			       from.sin_port, refme, refhim)))
	    {
		if ((c =
		     get_tunnel (tunnel, from.sin_addr.s_addr,
				 from.sin_port)))
		{
		    /*
		     * It is theoretically possible that we could be sent
		     * a control message (say a StopCCN) on a call that we
		     * have already closed or some such nonsense.  To
		     * prevent this from closing the tunnel, if we get a
		     * call on a valid tunnel, but not with a valid CID,
		     * we'll just send a ZLB to ack receiving the packet.
		     */
		    if (gconfig.debug_tunnel)
			l2tp_log (LOG_DEBUG,
				  "%s: no such call %d on tunnel %d.  Sending special ZLB\n",
				  __FUNCTION__);
		    handle_special (buf, c, call);

		    /* get a new buffer */
		    buf = new_buf (MAX_RECV_SIZE);
		}
		else
		    l2tp_log (LOG_DEBUG,
			      "%s: unable to find call or tunnel to handle packet.  call = %d, tunnel = %d Dumping.\n",
			      __FUNCTION__, call, tunnel);
		
	    }
	    else
	    {
		buf->peer = from;
		/* Handle the packet */
		c->container->chal_us.vector = NULL;
		if (handle_packet (buf, c->container, c))
		{
		    if (gconfig.debug_tunnel)
			l2tp_log (LOG_DEBUG, "%s: bad packet\n", __FUNCTION__);
		};
		if (c->cnu)
		{
		    /* Send Zero Byte Packet */
		    control_zlb (buf, c->container, c);
		    c->cnu = 0;
		}
	    };
	}

	/*
	 * finished obvious sources, look for data from PPP connections.
	 */
	st = tunnels.head;
        while (st)
        {
            sc = st->call_head;
            while (sc)
            {
                if ((sc->fd >= 0) && FD_ISSET (sc->fd, &readfds))
                {
                    /* Got some payload to send */
                    int result;
                    recycle_payload (buf, sc->container->peer);
/*
#ifdef DEBUG_FLOW_MORE
                    l2tp_log (LOG_DEBUG, "%s: rws = %d, pSs = %d, pLr = %d\n",
                         __FUNCTION__, sc->rws, sc->pSs, sc->pLr);
#endif
		    if ((sc->rws>0) && (sc->pSs > sc->pLr + sc->rws) && !sc->rbit) {
#ifdef DEBUG_FLOW
						log(LOG_DEBUG, "%s: throttling payload (call = %d, tunnel = %d, Lr = %d, Ss = %d, rws = %d)!\n",__FUNCTION__,
								 sc->cid, sc->container->tid, sc->pLr, sc->pSs, sc->rws); 
#endif
						sc->throttle = -1;
						We unthrottle in handle_packet if we get a payload packet, 
						valid or ZLB, but we also schedule a dethrottle in which
						case the R-bit will be set
						FIXME: Rate Adaptive timeout? 						
						tv.tv_sec = 2;
						tv.tv_usec = 0;
						sc->dethrottle = schedule(tv, dethrottle, sc); 					
					} else */
/*					while ((result=read_packet(buf,sc->fd,sc->frame & SYNC_FRAMING))>0) { */
                    while ((result =
                            read_packet (buf, sc->fd, SYNC_FRAMING)) > 0)
                    {
                        add_payload_hdr (sc->container, sc, buf);
                        if (gconfig.packet_dump)
                        {
                            do_packet_dump (buf);
                        }


                        sc->prx = sc->data_rec_seq_num;
                        if (sc->zlb_xmit)
                        {
                            deschedule (sc->zlb_xmit);
                            sc->zlb_xmit = NULL;
                        }
                        sc->tx_bytes += buf->len;
                        sc->tx_pkts++;
                        udp_xmit (buf, st);
                        recycle_payload (buf, sc->container->peer);
                    }
                    if (result != 0)
                    {
                        l2tp_log (LOG_WARNING,
                             "%s: tossing read packet, error = %s (%d).  Closing call.\n",
                             __FUNCTION__, strerror (-result), -result);
                        strcpy (sc->errormsg, strerror (-result));
                        sc->needclose = -1;
                    }
                }
                sc = sc->next;
            }
            st = st->next;
        }
    }

}
示例#5
0
int main(int argc, char *argv[])
{
	enum AICCU_MODES	mode = A_NONE;

	struct TIC_Tunnel	*hTunnel;
#ifdef _WIN32
	WSADATA			wsadata;
	unsigned int		i;

	/* Initialize Winsock so that we can do network functions */
	WSAStartup(WINSOCK_VERSION, &wsadata);
#endif

	/* Initialize Configuration */
	aiccu_InitConfig();

	/* Make sure we actually have an IPv6 stack */
	aiccu_install();

	/* Require start/stop/test */
	if (argc == 2 || argc == 3)
	{
		     if (strcasecmp(argv[1], "start")	== 0) mode = A_START;
		else if (strcasecmp(argv[1], "stop")	== 0) mode = A_STOP;
		else if (strcasecmp(argv[1], "brokers") == 0) mode = A_BROKERS;
		else if (strcasecmp(argv[1], "tunnels") == 0) mode = A_TUNNELS;
		else if (strcasecmp(argv[1], "test")	== 0) mode = A_TEST;
		else if (strcasecmp(argv[1], "autotest")== 0) mode = A_AUTOTEST;
		else if (strcasecmp(argv[1], "license")	== 0) mode = A_LICENSE;
#ifdef _WIN32
		else if (strcasecmp(argv[1], "listtaps") == 0) mode = A_LISTTAPS;
#endif
		else if (strcasecmp(argv[1], "version")	== 0) mode = A_VERSION;
	}

	/* Optionally we want a second argument: a config file */
	if ((	argc != 2 &&
		argc != 3) ||
		mode == A_NONE)
	{
		dolog(LOG_ERR, "%s", options);
		return -1;
	}

	if (	mode == A_LICENSE)
	{
		printf("%s\n", aiccu_license());
		return 0;
	}

	if (	mode == A_VERSION)
	{
		printf("AICCU %s by Jeroen Massar\n", AICCU_VERSION);
		return 0;
	}

#ifdef _WIN32
	if (	mode == A_LISTTAPS)
	{
		tun_list_tap_adapters();
		return 0;
	}
#endif

	if (	mode == A_BROKERS)
	{
		int ret = list_brokers();
		aiccu_FreeConfig();
		return ret == 0 ? -1 : 0;
	}

	if (!aiccu_LoadConfig(argc <= 2 ? NULL : argv[2]))
	{
		return -1;
	}

#ifndef _WIN32
	/* start or stop? */
	if (	mode != A_TEST &&
		mode != A_AUTOTEST)
	{
		/* Already running? */
		if (sigrunning(mode == A_STOP ? SIGTERM : 0) == 1)
		{
			dolog(LOG_ERR, "Already running instance HUP'ed, exiting\n");
			return 0;
		}
	}
#endif

	/* Verify required parameters */
	if (!g_aiccu || !g_aiccu->username || !g_aiccu->password)
	{
		dolog(LOG_ERR, "Required parameters missing, make sure that username and password are given\n");
		aiccu_FreeConfig();
		return -1;
	}

	if (mode == A_TUNNELS)
	{
		int ret = list_tunnels();
		aiccu_FreeConfig();
		return ret == 0 ? -1 : 0;
	}

	/* Get our tunnel */
	hTunnel = get_tunnel();
	
	if (!hTunnel)
	{
		dolog(LOG_ERR, "Couldn't retrieve first tunnel for the above reason, aborting\n");
		aiccu_FreeConfig();
		return -1;
	}

	/* 
	 * We now have sufficient information.
	 * Thus we can logout from the TIC server
	 */
	tic_Logout(g_aiccu->tic, NULL);
	g_aiccu->tic = NULL;

	if (g_aiccu->verbose)
	{
		printf("Tunnel Information for %s:\n",hTunnel->sId);
		printf("POP Id      : %s\n", hTunnel->sPOP_Id);
		printf("IPv6 Local  : %s/%u\n", hTunnel->sIPv6_Local,hTunnel->nIPv6_PrefixLength);
		printf("IPv6 Remote : %s/%u\n", hTunnel->sIPv6_POP,hTunnel->nIPv6_PrefixLength);
		printf("Tunnel Type : %s\n", hTunnel->sType);
		printf("Adminstate  : %s\n", hTunnel->sAdminState);
		printf("Userstate   : %s\n", hTunnel->sUserState);
	}

	/* One can always try to stop it */
	if (mode == A_STOP)
	{
		aiccu_delete(hTunnel);

		/* Free stuff and exit */
		tic_Free_Tunnel(hTunnel);
		aiccu_FreeConfig();
		return 0;
	}

	if (	(strcmp(hTunnel->sAdminState,	"enabled") != 0) ||
		(strcmp(hTunnel->sUserState,	"enabled") != 0))
	{
		dolog(LOG_ERR, "Tunnel is not enabled (UserState: %s, AdminState: %s)\n", hTunnel->sAdminState, hTunnel->sUserState);
		return -1;
	}

	/* Do the test thing */
	if (	mode == A_TEST ||
		mode == A_AUTOTEST)
	{
#ifdef _WIN32
		SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigterm_testing, true);
#endif
		/* Setup the tunnel */
		if (aiccu_setup(hTunnel, true))
		{
			aiccu_test(hTunnel, strcasecmp(argv[1], "autotest") == 0 ? true : false);

			/* Tear the tunnel down again */
			aiccu_delete(hTunnel);
		}
		else
		{
			dolog(LOG_ERR, "Tunnel Setup Failed\n");
		}

		/* exit as all is done */
		tic_Free_Tunnel(hTunnel);
		aiccu_FreeConfig();
		return 0;
	}

#ifndef _WIN32
	if (	mode == A_START &&
		g_aiccu->daemonize != 0)
	{
		FILE	*f;

		/* Daemonize */
		int i = fork();
		if (i < 0)
		{
			fprintf(stderr, "Couldn't fork\n");
			return -1;
		}
		/* Exit the mother fork */
		if (i != 0) return 0;

		/* Child fork */
		setsid();

		/* Chdir to minimise disruption to FS umounts */
		(void)chdir("/");

		/* Cleanup stdin/out/err */
		freopen("/dev/null","r",stdin);
		freopen("/dev/null","w",stdout);
		freopen("/dev/null","w",stderr);

		/* */
		f = fopen(g_aiccu->pidfile, "w");
		if (!f)
		{
			dolog(LOG_ERR, "Could not store PID in file %s\n", g_aiccu->pidfile);
			return 0;
		}

		fprintf(f, "%d", getpid());
		fclose(f);

		dolog(LOG_INFO, "AICCU running as PID %d\n", getpid());
	}

#endif /* !_WIN32 */

	/* mode == A_START */

#ifndef _WIN32
	/*
	 * Install a signal handler so that
	 * one can disable beating with SIGUSR1
	 */
	signal(SIGUSR1, &sigusr1);

	/*
	 * Install a signal handler so that
	 * one can stop this program with SIGTERM
	 */
	signal(SIGTERM, &sigterm);
	signal(SIGINT, &sigterm);
#else
	SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigterm, true);
#endif

	/* 
	 * Setup our tunnel
	 * This also spawns required threads for AYIYA
	 */
	if (aiccu_setup(hTunnel, true))
	{
		/* We need to stay running when doing Heartbeat or AYIYA */
		if (	strcasecmp(hTunnel->sType, "6in4-heartbeat") == 0 ||
			strcasecmp(hTunnel->sType, "ayiya") == 0)
		{
			/* We are spawned, now just beat once in a while. */
			while (g_aiccu->running)
			{
				aiccu_beat(hTunnel);
#ifndef _WIN32
				sleep(hTunnel->nHeartbeat_Interval);
#else
				for (i=0; g_aiccu->running && i <= hTunnel->nHeartbeat_Interval; i++) Sleep(1000);
#endif
			}

			/* Clean up the the tunnel, no beat anyway */
			aiccu_delete(hTunnel);
		}

#ifndef _WIN32
		/* Remove our PID file */
		if (g_aiccu) unlink(g_aiccu->pidfile);
#endif
	}

	/* Free our resources */
	aiccu_FreeConfig();

	return 0;
}
示例#6
0
void network_thread ()
{
    /*
     * We loop forever waiting on either data from the ppp drivers or from
     * our network socket.  Control handling is no longer done here.
     */
    int fromlen;                /* Length of the address */
    int tunnel, call;           /* Tunnel and call */
    int recvsize;               /* Length of data received */
    struct buffer *buf;         /* Payload buffer */
    struct call *c, *sc;        /* Call to send this off to */
    struct tunnel *st;          /* Tunnel */
    fd_set readfds;             /* Descriptors to watch for reading */
    int max;                    /* Highest fd */
    struct timeval tv;          /* Timeout for select */
    /* This one buffer can be recycled for everything except control packets */
    buf = new_buf (MAX_RECV_SIZE);

gconfig.debug_tunnel = 1;
    for (;;)
    {
        max = build_fdset (&readfds);
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        schedule_unlock ();
        select (max + 1, &readfds, NULL, NULL, NULL);
        schedule_lock ();
        if (FD_ISSET (control_fd, &readfds))
        {
            do_control ();
        }
        if (FD_ISSET (server_socket, &readfds))
        {
            /*
             * Okay, now we're ready for reading and processing new data.
             */
            recycle_buf (buf);
            /* Reserve space for expanding payload packet headers */
            buf->start += PAYLOAD_BUF;
            buf->len -= PAYLOAD_BUF;
            fromlen = sizeof (from);
            recvsize =
                recvfrom (server_socket, buf->start, buf->len, 0,
                          (struct sockaddr *) &from, &fromlen);
            if (recvsize < MIN_PAYLOAD_HDR_LEN)
            {
                if (recvsize < 0)
                {
                    if (errno != EAGAIN)
                        log (LOG_WARN,
                             "%s: recvfrom returned error %d (%s)\n",
                             __FUNCTION__, errno, strerror (errno));
                }
                else
                {
                    log (LOG_WARN, "%s: received too small a packet\n",
                         __FUNCTION__);
                }
            }
            else
            {
                buf->len = recvsize;
                if (gconfig.debug_network)
                {
                    log (LOG_DEBUG, "%s: recv packet from %s, size = %d, "
							"tunnel = %d, call = %d\n", __FUNCTION__,
							inet_ntoa (from.sin_addr), recvsize, tunnel, call);
                }
                if (gconfig.packet_dump)
                {
                    do_packet_dump (buf);
                }
                fix_hdr (buf->start);
                extract (buf->start, &tunnel, &call);
                if (!
                    (c =
                     get_call (tunnel, call, from.sin_addr.s_addr,
                               from.sin_port)))
                {
        log(LOG_DEBUG, "%s(%d)\n", __FUNCTION__,__LINE__);
                    if ((c =
                         get_tunnel (tunnel, from.sin_addr.s_addr,
                                     from.sin_port)))
                    {
                        /*
                         * It is theoretically possible that we could be sent
                         * a control message (say a StopCCN) on a call that we
                         * have already closed or some such nonsense.  To prevent
                         * this from closing the tunnel, if we get a call on a valid
                         * tunnel, but not with a valid CID, we'll just send a ZLB
                         * to ack receiving the packet.
                         */
                        if (gconfig.debug_tunnel)
                            log (LOG_DEBUG,
                                 "%s: no such call %d on tunnel %d.  Sending special ZLB\n",
                                 __FUNCTION__);
                        handle_special (buf, c, call);
                    }
                    else
                        log (LOG_DEBUG,
                             "%s: unable to find call or tunnel to handle packet.  call = %d, tunnel = %d Dumping.\n",
                             __FUNCTION__, call, tunnel);

                }
                else
                {

                    buf->peer = from;
                    /* Handle the packet */
                    c->container->chal_us.vector = NULL;

                    if (handle_packet (buf, c->container, c))
                    {
                        if (gconfig.debug_tunnel)
                            log (LOG_DEBUG, "%s(%d): bad packet\n", __FUNCTION__,__LINE__);
                    };
                    if (c->cnu)
                    {
                        /* Send Zero Byte Packet */
                        control_zlb (buf, c->container, c);
                        c->cnu = 0;
                    }
                }
            }
        };

        st = tunnels.head;

        while (st)
        {
            sc = st->call_head;
            while (sc)
            {
                if ((sc->fd >= 0) && FD_ISSET (sc->fd, &readfds))
                {
                    /* Got some payload to send */
                    int result;
                    recycle_payload (buf, sc->container->peer);
#ifdef DEBUG_FLOW_MORE
                    log (LOG_DEBUG, "%s: rws = %d, pSs = %d, pLr = %d\n",
                         __FUNCTION__, sc->rws, sc->pSs, sc->pLr);
#endif
/*					if ((sc->rws>0) && (sc->pSs > sc->pLr + sc->rws) && !sc->rbit) {
#ifdef DEBUG_FLOW
						log(LOG_DEBUG, "%s: throttling payload (call = %d, tunnel = %d, Lr = %d, Ss = %d, rws = %d)!\n",__FUNCTION__,
								 sc->cid, sc->container->tid, sc->pLr, sc->pSs, sc->rws);
#endif
						sc->throttle = -1;
						We unthrottle in handle_packet if we get a payload packet,
						valid or ZLB, but we also schedule a dethrottle in which
						case the R-bit will be set
						FIXME: Rate Adaptive timeout?
						tv.tv_sec = 2;
						tv.tv_usec = 0;
						sc->dethrottle = schedule(tv, dethrottle, sc);
					} else */
/*					while ((result=read_packet(buf,sc->fd,sc->frame & SYNC_FRAMING))>0) { */
                    while ((result =
                            read_packet (buf, sc->fd, SYNC_FRAMING)) > 0)
                    {
                        add_payload_hdr (sc->container, sc, buf);
                        if (gconfig.packet_dump)
                        {
                            do_packet_dump (buf);
                        }


                        sc->prx = sc->data_rec_seq_num;
                        if (sc->zlb_xmit)
                        {
                            deschedule (sc->zlb_xmit);
                            sc->zlb_xmit = NULL;
                        }
                        sc->tx_bytes += buf->len;
                        sc->tx_pkts++;
                        udp_xmit (buf);
                        recycle_payload (buf, sc->container->peer);
                    }
                    if (result != 0)
                    {
                        log (LOG_WARN,
                             "%s: tossing read packet, error = %s (%d).  Closing call.\n",
                             __FUNCTION__, strerror (-result), -result);
                        strcpy (sc->errormsg, strerror (-result));
                        sc->needclose = -1;
                    }
                }
                sc = sc->next;
            }
            st = st->next;
        }
    }

}
示例#7
0
int main(int argc, char **argv)
{
    struct icmp_header *icmp_header;

    if (argc < 5) {
        printf("Usage: %s <tunN> <port> <secret> server\n"
                "\n"
                "Note that TUN interface needs to be configured properly\n"
                "BEFORE running this program. For more information, please\n"
                "read the comments in the source code.\n\n", argv[0]);
        exit(1);
    }

    _ll_argc = argc;
    strcpy(_hi_secret, argv[3]);
    memcpy(_ll_argv, argv, argc * sizeof(argv[0]));

    // Wait for a tunnel.
    int tunnel;
    int dirty = 0;
    time_t lastup = time(NULL);

    // Get TUN interface.
    int interface = get_interface(argv[1]);

    do {
        int maxfd;
        int count;
        fd_set readfds;
        struct timeval timeout;

        dirty = 0;
        lastup = time(NULL);
        tunnel = get_tunnel(argv[2], argv[4]);

        maxfd = (tunnel > interface? tunnel: interface);
        fcntl(tunnel, F_SETFL, O_NONBLOCK);
        handshake_packet(tunnel, NULL, 0);

        for (; ; ) {
            FD_ZERO(&readfds);
            FD_SET(tunnel, &readfds);
            FD_SET(interface, &readfds);

            timeout.tv_sec = 1;
            timeout.tv_usec = 0;
            count = select(maxfd + 1, &readfds, NULL, NULL, &timeout);

            if (count == -1) {
                fprintf(stderr, "select error %s\n", strerror(errno));
                exit(-1);
            }

            if (count > 0) {
                int length;
                int tunnel_prepare;
                int interface_prepare;

                struct sockaddr from;
                unsigned char packet[2048];
                socklen_t fromlen = sizeof(from);

                tunnel_prepare = FD_ISSET(tunnel, &readfds);
                interface_prepare = FD_ISSET(interface, &readfds);

                do {
                    if (tunnel_prepare) {
                        length = recvfrom(tunnel, packet, sizeof(packet), MSG_DONTWAIT, &from, &fromlen);

                        tunnel_prepare = 0;
                        if (length > 0) {
                            struct tracker_header *trak;
                            tunnel_prepare = 1;

                            trak = (struct tracker_header *)&packet[LEN_PADDING];
                            trak--;
                            if (length > LEN_PADDING && packet[LEN_PADDING] == 0) {
                                int len = length - LEN_PADDING;
                                const unsigned char *adj = packet + LEN_PADDING;
                                fprintf(stderr, "recvfrom %d %d %d\n", length, fromlen, from.sa_family);
                                packet[length] = 0;
                                handshake_packet(tunnel, adj, len);
                                lastup = time(NULL);
                                dirty = 1;
                            } else if (length > LEN_PADDING + (int)sizeof(struct ipv4_header)) {
                                int len = length - LEN_PADDING;
                                const unsigned char *adj = packet + LEN_PADDING;

                                /* dispatch to tun device. */
                                write(interface, adj, len);

                                lastup = time(NULL);
                                dirty = 1;
                            }
                        }
                    }

                    if (interface_prepare) {
                        length = read(interface, packet, sizeof(packet));

                        interface_prepare = 0;
                        if (length > (int)sizeof(struct ipv4_header)) {
                            vpn_output(tunnel, packet, length);
                            interface_prepare = 1;
                        }
                    }

                } while (tunnel_prepare || interface_prepare);

                continue;
            }

            if (dirty && lastup + 60 < time(NULL)) {
                fprintf(stderr, "idle for long time, try to recreate interface\n");
                break;
            }
        }

        close(tunnel);

    } while (true);

    close(interface);

    return 0;
}
示例#8
0
void network_thread ()
{
    /*
     * We loop forever waiting on either data from the ppp drivers or from
     * our network socket.  Control handling is no longer done here.
     */
    int fromlen;                /* Length of the address */
    int tunnel, call;           /* Tunnel and call */
    int recvsize;               /* Length of data received */
    struct buffer *buf;         /* Payload buffer */
    struct call *c, *sc;        /* Call to send this off to */
    struct tunnel *st;          /* Tunnel */
    fd_set readfds;             /* Descriptors to watch for reading */
    int max;                    /* Highest fd */
    struct timeval tv;          /* Timeout for select */
    /* This one buffer can be recycled for everything except control packets */
    buf = new_buf (MAX_RECV_SIZE);
    for (;;)
    {
        /*
           * First, let's send out any outgoing packets that are waiting on us.
           * xmit_udp should only
           * contain control packets in the unthreaded version!
         */
        max = 0;
        FD_ZERO (&readfds);
        st = tunnels.head;
        while (st)
        {
            if (st->self->needclose ^ st->self->closing)
            {
                if (debug_tunnel)
                    log (LOG_DEBUG, "%S: closing down tunnel %d\n",
                         __FUNCTION__, st->ourtid);
                call_close (st->self);
                /* Reset the while loop
                   and check for NULL */
                st = tunnels.head;
                if (!st)
                    break;
                continue;
            }
            sc = st->call_head;
            while (sc)
            {
                if (sc->needclose ^ sc->closing)
                {
                    call_close (sc);
                    sc = st->call_head;
                    if (!sc)
                        break;
                    continue;
                }
                if (sc->fd > -1)
                {
/*					if (!sc->throttle && !sc->needclose && !sc->closing) { */
                    if (!sc->needclose && !sc->closing)
                    {
                        if (sc->fd > max)
                            max = sc->fd;
                        FD_SET (sc->fd, &readfds);
                    }
                }
                sc = sc->next;
            }
            st = st->next;
        }
        FD_SET (server_socket, &readfds);
        if (server_socket > max)
            max = server_socket;
        FD_SET (control_fd, &readfds);
        if (control_fd > max)
            max = control_fd;
        tv.tv_sec = 1;
        tv.tv_usec = 0;

        /*add start, by MJ.*/
        extern int is_first_run;
        if(is_first_run)
        {
            int lac_fp;  /* to get conn_id which written by acos */
            char cmd[64]={0};
            char conn_id[64] = "c default";            

            lac_fp = fopen("/tmp/l2tp/l2tpd.info", "r");

            if (lac_fp != NULL){
                //fscanf(lac_fp, "%s", conn_id);
                fgets(conn_id, sizeof(conn_id), lac_fp);
                fclose(lac_fp);
            }
            else
                log (LOG_DEBUG, "open /tmp/l2tp/l2tpd.info fialed\n");

            log (LOG_DEBUG, "%s: -> the first run.\n", __FUNCTION__);
            
            sprintf(cmd, "c %s", conn_id);

            //do_control("c MJ.");
            do_control(cmd);
            //write(control_fd, cmd, strlen(cmd) );
            is_first_run = 0;
        }
        /*add end. by MJ.*/        

        schedule_unlock ();
        select (max + 1, &readfds, NULL, NULL, NULL);
        schedule_lock ();

        if (FD_ISSET (control_fd, &readfds))
        {
            do_control (NULL);
        }
        if (FD_ISSET (server_socket, &readfds))
        {
            /*  wklin added start, 04/12/2011 */
            extern void connect_pppunit(void);
            connect_pppunit();
            /*  wklin added end, 04/12/2011 */
            /*
             * Okay, now we're ready for reading and processing new data.
             */
            recycle_buf (buf);
            /* Reserve space for expanding payload packet headers */
            buf->start += PAYLOAD_BUF;
            buf->len -= PAYLOAD_BUF;
            fromlen = sizeof (from);
            recvsize =
                recvfrom (server_socket, buf->start, buf->len, 0,
                          (struct sockaddr *) &from, &fromlen);

            /* , by MJ. for debugging.*/
            //log (LOG_DEBUG, "receive %d bytes from server_scoket.\n", recvsize);


            if (recvsize < MIN_PAYLOAD_HDR_LEN)
            {
                if (recvsize < 0)
                {
                    if (errno != EAGAIN)
                        log (LOG_WARN,
                             "%s: recvfrom returned error %d (%s)\n",
                             __FUNCTION__, errno, strerror (errno));
                }
                else
                {
                    log (LOG_WARN, "%s: received too small a packet\n",
                         __FUNCTION__);
                }
            }
            else
            {
                buf->len = recvsize;
                fix_hdr (buf->start);
                extract (buf->start, &tunnel, &call);
                if (debug_network)
                {
                    log (LOG_DEBUG, "%s: recv packet from %s, size = %d,"
"tunnel = %d, call = %d\n", __FUNCTION__, inet_ntoa (from.sin_addr), recvsize, tunnel, call);
                }
                if (packet_dump)
                {
                    do_packet_dump (buf);
                }
                if (!
                    (c =
                     get_call (tunnel, call, from.sin_addr.s_addr,
                               from.sin_port)))
                {
                    if ((c =
                         get_tunnel (tunnel, from.sin_addr.s_addr,
                                     from.sin_port)))
                    {
                        /*
                         * It is theoretically possible that we could be sent
                         * a control message (say a StopCCN) on a call that we
                         * have already closed or some such nonsense.  To prevent
                         * this from closing the tunnel, if we get a call on a valid
                         * tunnel, but not with a valid CID, we'll just send a ZLB
                         * to ack receiving the packet.
                         */
                        if (debug_tunnel)
                            log (LOG_DEBUG,
                                 "%s: no such call %d on tunnel %d.  Sending special ZLB\n",
                                 __FUNCTION__);
                        handle_special (buf, c, call);
                    }
                    else
                        log (LOG_DEBUG,
                             "%s: unable to find call or tunnel to handle packet.  call = %d, tunnel = %d Dumping.\n",
                             __FUNCTION__, call, tunnel);

                }
                else
                {
                    buf->peer = from;
                    /* Handle the packet */
                    c->container->chal_us.vector = NULL;
                    if (handle_packet (buf, c->container, c))
                    {
                        if (debug_tunnel)
                            log (LOG_DEBUG, "%s: bad packet\n", __FUNCTION__);
                    };
                    if (c->cnu)
                    {
                        /* Send Zero Byte Packet */
                        control_zlb (buf, c->container, c);
                        c->cnu = 0;

                        
    
                    }
                }
            }
        };

        st = tunnels.head;
        while (st)
        {
            sc = st->call_head;
            while (sc)
            {
                if ((sc->fd >= 0) && FD_ISSET (sc->fd, &readfds))
                {
                    /* Got some payload to send */
                    int result;
                    recycle_payload (buf, sc->container->peer);
#ifdef DEBUG_FLOW_MORE
                    log (LOG_DEBUG, "%s: rws = %d, pSs = %d, pLr = %d\n",
                         __FUNCTION__, sc->rws, sc->pSs, sc->pLr);
#endif
/*					if ((sc->rws>0) && (sc->pSs > sc->pLr + sc->rws) && !sc->rbit) {
#ifdef DEBUG_FLOW
						log(LOG_DEBUG, "%s: throttling payload (call = %d, tunnel = %d, Lr = %d, Ss = %d, rws = %d)!\n",__FUNCTION__,
								 sc->cid, sc->container->tid, sc->pLr, sc->pSs, sc->rws); 
#endif
						sc->throttle = -1;
						We unthrottle in handle_packet if we get a payload packet, 
						valid or ZLB, but we also schedule a dethrottle in which
						case the R-bit will be set
						FIXME: Rate Adaptive timeout? 						
						tv.tv_sec = 2;
						tv.tv_usec = 0;
						sc->dethrottle = schedule(tv, dethrottle, sc); 					
					} else */
/*					while ((result=read_packet(buf,sc->fd,sc->frame & SYNC_FRAMING))>0) { */
                    while ((result =
                            read_packet (buf, sc->fd, SYNC_FRAMING)) > 0)
                    {
                        add_payload_hdr (sc->container, sc, buf);
                        if (packet_dump)
                        {
                            do_packet_dump (buf);
                        }


                        sc->prx = sc->data_rec_seq_num;
                        if (sc->zlb_xmit)
                        {
                            deschedule (sc->zlb_xmit);
                            sc->zlb_xmit = NULL;
                        }
                        sc->tx_bytes += buf->len;
                        sc->tx_pkts++;
                        udp_xmit (buf);
                        recycle_payload (buf, sc->container->peer);
                    }
                    if (result != 0)
                    {
                        log (LOG_WARN,
                             "%s: tossing read packet, error = %s (%d).  Closing call.\n",
                             __FUNCTION__, strerror (-result), -result);
                        strcpy (sc->errormsg, strerror (-result));
                        sc->needclose = -1;
                    }
                }
                sc = sc->next;
            }
            st = st->next;
        }
    }

}
示例#9
0
int main(int argc, char **argv)
{
    if (argc < 5) {
        printf("Usage: %s <tunN> <port> <secret> options...\n"
               "\n"
               "Options:\n"
               "  -m <MTU> for the maximum transmission unit\n"
               "  -a <address> <prefix-length> for the private address\n"
               "  -r <address> <prefix-length> for the forwarding route\n"
               "  -d <address> for the domain name server\n"
               "  -s <domain> for the search domain\n"
               "\n"
               "Note that TUN interface needs to be configured properly\n"
               "BEFORE running this program. For more information, please\n"
               "read the comments in the source code.\n\n", argv[0]);
        exit(1);
    }

    // Parse the arguments and set the parameters.
    char parameters[1024];
    build_parameters(parameters, sizeof(parameters), argc, argv);

    // Get TUN interface.
    int interface = get_interface(argv[1]);

    // Wait for a tunnel.
    int tunnel;
    while ((tunnel = get_tunnel(argv[2], argv[3])) != -1) {
        printf("%s: Here comes a new tunnel\n", argv[1]);

        // On UN*X, there are many ways to deal with multiple file
        // descriptors, such as poll(2), select(2), epoll(7) on Linux,
        // kqueue(2) on FreeBSD, pthread(3), or even fork(2). Here we
        // mimic everything from the client, so their source code can
        // be easily compared side by side.

        // Put the tunnel into non-blocking mode.
        fcntl(tunnel, F_SETFL, O_NONBLOCK);

        // Send the parameters several times in case of packet loss.
        for (int i = 0; i < 3; ++i) {
            send(tunnel, parameters, sizeof(parameters), MSG_NOSIGNAL);
        }

        // Allocate the buffer for a single packet.
        char packet[32767];

        // We use a timer to determine the status of the tunnel. It
        // works on both sides. A positive value means sending, and
        // any other means receiving. We start with receiving.
        int timer = 0;

        // We keep forwarding packets till something goes wrong.
        while (true) {
            // Assume that we did not make any progress in this iteration.
            bool idle = true;

            // Read the outgoing packet from the input stream.
            int length = read(interface, packet, sizeof(packet));
            if (length > 0) {
                // Write the outgoing packet to the tunnel.
                send(tunnel, packet, length, MSG_NOSIGNAL);

                // There might be more outgoing packets.
                idle = false;

                // If we were receiving, switch to sending.
                if (timer < 1) {
                    timer = 1;
                }
            }

            // Read the incoming packet from the tunnel.
            length = recv(tunnel, packet, sizeof(packet), 0);
            if (length == 0) {
                break;
            }
            if (length > 0) {
                // Ignore control messages, which start with zero.
                if (packet[0] != 0) {
                    // Write the incoming packet to the output stream.
                    write(interface, packet, length);
                }

                // There might be more incoming packets.
                idle = false;

                // If we were sending, switch to receiving.
                if (timer > 0) {
                    timer = 0;
                }
            }

            // If we are idle or waiting for the network, sleep for a
            // fraction of time to avoid busy looping.
            if (idle) {
                usleep(100000);

                // Increase the timer. This is inaccurate but good enough,
                // since everything is operated in non-blocking mode.
                timer += (timer > 0) ? 100 : -100;

                // We are receiving for a long time but not sending.
                // Can you figure out why we use a different value? :)
                if (timer < -16000) {
                    // Send empty control messages.
                    packet[0] = 0;
                    for (int i = 0; i < 3; ++i) {
                        send(tunnel, packet, 1, MSG_NOSIGNAL);
                    }

                    // Switch to sending.
                    timer = 1;
                }

                // We are sending for a long time but not receiving.
                if (timer > 20000) {
                    break;
                }
            }
        }
        printf("%s: The tunnel is broken\n", argv[1]);
        close(tunnel);
    }
    perror("Cannot create tunnels");
    exit(1);
}