summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packaging/config3
-rw-r--r--toys/pending/ping.c110
-rw-r--r--toys/samsung/ping.c512
3 files changed, 514 insertions, 111 deletions
diff --git a/packaging/config b/packaging/config
index 9e442eb..4d2b88d 100644
--- a/packaging/config
+++ b/packaging/config
@@ -16,6 +16,7 @@ CONFIG_TOYBOX_FORK=y
# Samsung developed commands
#
CONFIG_NSLOOKUP=y
+CONFIG_PING=y
#
# Posix commands
@@ -157,7 +158,7 @@ CONFIG_DUMPLEASES=y
# CONFIG_OPENVT is not set
# CONFIG_DEALLOCVT is not set
# CONFIG_PGREP is not set
-CONFIG_PING=y
+# CONFIG_PING=y
# CONFIG_PS is not set
# CONFIG_ROUTE is not set
# CONFIG_SH is not set
diff --git a/toys/pending/ping.c b/toys/pending/ping.c
deleted file mode 100644
index 629f98b..0000000
--- a/toys/pending/ping.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* ping.c - check network connectivity
- *
- * Copyright 2014 Rob Landley <rob@landley.net>
- *
- * Not in SUSv4.
-
-USE_PING(NEWTOY(ping, "<1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
-
-config PING
- bool "ping"
- default n
- help
- usage: ping [OPTIONS] HOST
-
- Check network connectivity by sending packets to a host and reporting
- its response.
-
- Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
- echo it receives back, with round trip time.
-
- Options:
- -4, -6 Force IPv4 or IPv6
- -c CNT Send CNT many packets
- -I IFACE/IP Source interface or address
- -q Quiet, only displays output at start and when finished
- -s SIZE Packet SIZE in bytes (default 56)
- -t TTL Set Time (number of hops) To Live
- -W SEC Seconds to wait for response after all packets sent (default 10)
- -w SEC Exit after this many seconds
-*/
-
-#define FOR_ping
-#include "toys.h"
-
-#include <ifaddrs.h>
-
-GLOBALS(
- long wait_exit;
- long wait_resp;
- char *iface;
- long size;
- long count;
- long ttl;
-
- int sock;
-)
-
-void ping_main(void)
-{
- int family, protocol;
- union {
- struct in_addr in;
- struct in6_addr in6;
- } src_addr;
- char *host = 0;
-
- // Determine IPv4 vs IPv6 type
-
- if(!(toys.optflags & (FLAG_4|FLAG_6))) {
-// todo getaddrinfo instead?
- if (inet_pton(AF_INET6, toys.optargs[0], (void*)&src_addr))
- toys.optflags |= FLAG_6;
- }
-
- if (toys.optflags & FLAG_6) {
- family = AF_INET6;
- protocol = IPPROTO_ICMPV6;
- } else {
- family = AF_INET;
- protocol = IPPROTO_ICMP;
- }
-
- if (!(toys.optflags & FLAG_s)) TT.size = 56; // 64-PHDR_LEN
-
- if (TT.iface) {
- memset(&src_addr, 0, sizeof(src_addr));
-
- // IP address?
- if (!inet_pton(family, TT.iface, &src_addr)) {
- struct ifaddrs *ifsave, *ifa = 0;
-
- // Interface name?
- if (!getifaddrs(&ifsave)) {
- for (ifa = ifsave; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != family) continue;
- if (!strcmp(ifa->ifa_name, TT.iface)) {
- if (family == AF_INET)
- memcpy(&src_addr,
- &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
- sizeof(struct in_addr));
- else memcpy(&src_addr,
- &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
- sizeof(struct in6_addr));
- break;
- }
- }
- freeifaddrs(ifsave);
- }
- if (!ifa)
- error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.iface);
- }
- inet_ntop(family, &src_addr, toybuf, sizeof(toybuf));
- host = xstrdup(toybuf);
- }
-
-printf("host=%s\n", host);
-
- // Open raw socket
- TT.sock = xsocket(family, SOCK_RAW, protocol);
-}
diff --git a/toys/samsung/ping.c b/toys/samsung/ping.c
new file mode 100644
index 0000000..ca2af9f
--- /dev/null
+++ b/toys/samsung/ping.c
@@ -0,0 +1,512 @@
+/* ping.c - ping program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_PING(NEWTOY(ping, "<1>1Q#<0>255t#<0>255c#<1s#<0>65535I:W#<0w#<0q46", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+
+config PING
+ bool "ping"
+ default y
+ help
+ usage: ping [OPTIONS] HOST
+
+ Send ICMP ECHO_REQUEST packets to network hosts
+
+ Options:
+ -4, -6 Force IP or IPv6 name resolution
+ -c CNT Send only CNT pings
+ -s SIZE Send SIZE data bytes in packets (default:56)
+ -t TTL Set TTL
+ -I IFACE/IP Use interface or IP address as source
+ -W SEC Seconds to wait for the first response (default:10)
+ (after all -c CNT packets are sent)
+ -w SEC Seconds until ping exits (default:infinite)
+ (can exit earlier with -c CNT)
+ -q Quiet, only displays output at start
+ and when finished
+ -Q ToS Set Quality of Service
+*/
+#define FOR_ping
+#include "toys.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+
+GLOBALS(
+ long wait_exit;
+ long wait_resp;
+ char *iface;
+ long size;
+ long count;
+ long ttl;
+ long tos;
+ int ntransmitted;
+ int nreceived;
+ int ident;
+ int nrepeats;
+ int sock;
+)
+
+#define F_QUIET 0x0001 // minimize all output
+#define F_TIMING 0x0002 // room for a timestamp
+#define F_SOURCE_ADDR 0x0004 // set source IP address/interface
+
+u_char rcvd_tbl[2048]; //MAX_DUP_CHK
+#define A(seq) rcvd_tbl[(seq/8)%sizeof(rcvd_tbl)] // byte in array
+#define B(seq) (1 << (seq & 0x07)) // bit in byte
+#define SET(seq) (A(seq) |= B(seq))
+#define CLR(seq) (A(seq) &= (~B(seq)))
+#define TST(seq) (A(seq) & B(seq))
+
+struct tv32 {
+ int32_t tv32_sec;
+ int32_t tv32_usec;
+};
+
+u_char *packet;
+int packlen, pingflags = 0, bufspace = IP_MAXPACKET;
+double tmax = 0.0, tsum = 0.0, tmin = 999999999.0;
+struct timeval now;
+struct sockaddr_in src_addr, send_addr; //from where to Who
+
+#define PHDR_LEN sizeof(struct tv32) // size of timestamp header
+#define MAXHOSTNAMELEN 64
+char hostname[MAXHOSTNAMELEN + 1];
+static struct {
+ union {
+ u_char u_buf[(IP_MAXPACKET-60-8)+offsetof(struct icmp, icmp_data)];
+ struct icmp u_icmp;
+ } o_u;
+} out_pack;
+#define opack_icmp out_pack.o_u.u_icmp
+
+/* Compute the IP checksum
+ * This assumes the packet is less than 32K long.
+ */
+static u_int16_t in_cksum(u_int16_t *p, u_int len)
+{
+ u_int32_t sum = 0;
+ int nwords = len >> 1;
+
+ while (nwords--) sum += *p++;
+ if (len & 1) {
+ union {
+ u_int16_t w;
+ u_int8_t c[2];
+ } u;
+ u.c[0] = *(u_char *)p;
+ u.c[1] = 0;
+ sum += u.w;
+ }
+
+ // end-around-carry
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ return (~sum);
+}
+
+/*
+ * Print statistics.
+ * Heavily buffered STDIO is used here, so that all the statistics
+ * will be written with 1 sys-write call. This is nice when more
+ * than one copy of the program is running on a terminal; it prevents
+ * the statistics output from becomming intermingled.
+ */
+static void summary(int header)
+{
+ if (header) xprintf("\n--- %s PING Statistics ---\n", hostname);
+ xprintf("%d packets transmitted, ", TT.ntransmitted);
+ xprintf("%d packets received, ", TT.nreceived);
+ if (TT.nrepeats) xprintf("+%d duplicates, ", TT.nrepeats);
+ if (TT.ntransmitted) {
+ if (TT.nreceived > TT.ntransmitted) xprintf("-- somebody's duplicating packets!");
+ else xprintf("%.1f%% packet loss", (((TT.ntransmitted-TT.nreceived)*100.0)/TT.ntransmitted));
+ }
+ xputc('\n');
+ if (TT.nreceived && (pingflags & F_TIMING)) {
+ double n = TT.nreceived + TT.nrepeats;
+ double avg = (tsum / n);
+
+ xprintf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
+ tmin * 1000.0, avg * 1000.0, tmax * 1000.0);
+ }
+}
+
+// Print statistics when SIGINFO is received.
+static void prtsig(int dummy)
+{
+ summary(0);
+}
+
+// Print statistics and give up.
+static void finish(int dummy)
+{
+ signal(SIGQUIT, SIG_DFL);
+ summary(1);
+ exit(TT.nreceived > 0 ? 0 : 2);
+}
+
+// Print a descriptive string about an ICMP header other than an echo reply.
+static int pr_icmph(struct icmp *icp)
+{
+ switch (icp->icmp_type ) {
+ case ICMP_UNREACH:
+ xprintf("Destination Unreachable");
+ break;
+ case ICMP_SOURCEQUENCH:
+ xprintf("Source Quench");
+ break;
+ case ICMP_REDIRECT:
+ xprintf("Redirect (change route)");
+ break;
+ case ICMP_ECHO:
+ xprintf("Echo Request");
+ break;
+ case ICMP_ECHOREPLY:
+ // displaying other's pings is too noisy
+ return 0;
+ case ICMP_TIME_EXCEEDED:
+ xprintf("Time Exceeded");
+ break;
+ case ICMP_PARAMETERPROB:
+ xprintf("Parameter Problem");
+ break;
+ case ICMP_TIMESTAMP:
+ xprintf("Timestamp Request");
+ break;
+ case ICMP_TIMESTAMPREPLY:
+ xprintf("Timestamp Reply");
+ break;
+ case ICMP_INFO_REQUEST:
+ xprintf("Information Request");
+ break;
+ case ICMP_INFO_REPLY:
+ xprintf("Information Reply");
+ break;
+ case ICMP_ADDRESS:
+ xprintf("Address Mask Request");
+ break;
+ case ICMP_ADDRESSREPLY:
+ xprintf("Address Mask Reply");
+ break;
+ default:
+ xprintf("Bad ICMP type: %d", icp->icmp_type);
+ break;
+ }
+ return 1;
+}
+
+static void get_ifaddr(char *addrname, const char* name)
+{
+ struct ifaddrs *ifaddr_list, *ifa_item;
+ int family, s;
+ char host[NI_MAXHOST];
+
+ if (getifaddrs(&ifaddr_list) == -1) perror_exit("getifaddrs");
+
+ for (ifa_item = ifaddr_list; ifa_item; ifa_item = ifa_item->ifa_next) {
+ if (!ifa_item->ifa_addr) continue;
+
+ family = ifa_item->ifa_addr->sa_family;
+ if ((family == AF_INET) && !(strcmp(ifa_item->ifa_name,name))) {
+ s = getnameinfo(ifa_item->ifa_addr, sizeof(struct sockaddr_in),
+ host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ if (s) error_exit("getnameinfo() failed: %s\n", gai_strerror(s));
+ strncpy(addrname, host, MAXHOSTNAMELEN);
+ break;
+ }
+ }
+ freeifaddrs(ifaddr_list);
+}
+
+static void gethost(const char *arg, const char *name, struct sockaddr_in *sa,
+ char *realname, int realname_len)
+{
+ struct hostent *hp;
+ unsigned int if_idx = 0, len = strlen(arg);
+ char addrname[MAXHOSTNAMELEN+1];
+
+ memset(sa, 0, sizeof(*sa));
+ sa->sin_family = AF_INET;
+
+ if (inet_aton(name, &sa->sin_addr)) {
+ if (realname) strncpy(realname, name, realname_len -1);
+ if (len) {
+ pingflags |= F_SOURCE_ADDR;
+ TT.iface = NULL;
+ }
+ return;
+ }
+
+ if (len) {
+ if ((if_idx = if_nametoindex(name))) get_ifaddr(addrname, name);
+ else perror_exit("unknown interface '%s'",name);
+ } else strncpy(addrname,name, MAXHOSTNAMELEN);
+
+ addrname[MAXHOSTNAMELEN] = '\0';
+ hp = gethostbyname(addrname);
+ if (!hp) error_exit("Cannot resolve \"%s\" (%s)",name,hstrerror(h_errno));
+ if (hp->h_addrtype != AF_INET) error_exit("%s only supported with IP", arg);
+ memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
+ if (realname) strncpy(realname, hp->h_name, realname_len -1);
+}
+
+/*
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+static void pr_pack(u_char *buf, int tot_len, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ struct icmp *icp;
+ int net_len, hlen, dupflag = 0;
+ double triptime = 0.0;
+
+ // Check the IP header
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (tot_len < TT.size + ICMP_MINLEN) return;
+
+ // Now the ICMP part
+ net_len = tot_len - hlen;
+ icp = (struct icmp *)(buf + hlen);
+ if (ntohs(icp->icmp_id) != TT.ident) return;
+ if (icp->icmp_type == ICMP_ECHOREPLY) {
+ TT.nreceived++;
+ if (pingflags & F_TIMING) {
+ struct timeval tv;
+ struct tv32 tv32;
+
+ memcpy(&tv32, icp->icmp_data, sizeof(tv32));
+ tv.tv_sec = ntohl(tv32.tv32_sec);
+ tv.tv_usec = ntohl(tv32.tv32_usec);
+ triptime = ((now.tv_sec - tv.tv_sec)*1.0
+ + (now.tv_usec - tv.tv_usec)/1000000.0);
+ tsum += triptime;
+ if (triptime < tmin) tmin = triptime;
+ if (triptime > tmax) tmax = triptime;
+ }
+
+ if (TST(ntohs((u_int16_t)icp->icmp_seq))) {
+ TT.nrepeats++, TT.nreceived--;
+ dupflag = 1;
+ } else SET(ntohs((u_int16_t)icp->icmp_seq));
+
+ if (!dupflag) {
+ static u_int16_t last_seqno = 0xffff;
+ u_int16_t seqno = ntohs((u_int16_t)icp->icmp_seq);
+ u_int16_t gap = seqno - (last_seqno + 1);
+
+ if (gap < 0x8000) last_seqno = seqno;
+ }
+
+ if (pingflags & F_QUIET) return;
+
+ xprintf("%d bytes from %s: seq=%u ttl=%d", net_len, inet_ntoa(from->sin_addr),
+ ntohs((u_int16_t)icp->icmp_seq), ip->ip_ttl);
+ if (pingflags & F_TIMING) xprintf(" time=%.3f ms", triptime*1000.0);
+ if (dupflag) xprintf(" (DUP!)");
+ } else if (icp->icmp_type != ICMP_ECHO) pr_icmph(icp);
+ else return;
+ xputc('\n');
+}
+
+/*
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first PHDR_LEN bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+static void pinger(void)
+{
+ struct tv32 tv32;
+ int i, cc;
+
+ gettimeofday(&now,0);
+ opack_icmp.icmp_code = 0;
+ opack_icmp.icmp_seq = htons((u_int16_t)(TT.ntransmitted));
+ opack_icmp.icmp_type = ICMP_ECHO;
+ opack_icmp.icmp_id = htons(TT.ident);
+ tv32.tv32_sec = htonl(now.tv_sec);
+ tv32.tv32_usec = htonl(now.tv_usec);
+ if (pingflags & F_TIMING) memcpy(&opack_icmp.icmp_data[0], &tv32, sizeof(tv32));
+ cc = TT.size + PHDR_LEN;
+ opack_icmp.icmp_cksum = 0;//reset before Checksum computation
+ opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp, cc);
+
+ i = sendto(TT.sock, (char *) &opack_icmp, cc, 0,
+ (struct sockaddr *)&send_addr, sizeof(struct sockaddr_in));
+ if (i != cc) {
+ if (i < 0) perror_exit("sendto");
+ else error_msg("wrote %s %d chars, ret=%d", hostname, cc, i);
+ }
+ CLR(TT.ntransmitted);
+ TT.ntransmitted++;
+}
+
+static void send_ping(int dummy)
+{
+ struct itimerval itimer;
+ if (TT.wait_exit && (TT.wait_exit == TT.ntransmitted)) finish(0);
+ if (TT.count && (TT.ntransmitted >= TT.count)) {
+ //waiting for the last response.
+ if (TT.nreceived) {
+ itimer.it_value.tv_sec = 2 * tmax/1000;
+ if (itimer.it_value.tv_sec == 0) itimer.it_value.tv_sec = 1;
+ } else itimer.it_value.tv_sec = (TT.wait_resp) ? TT.wait_resp : 10; //default 10 secs
+ itimer.it_value.tv_usec = 0;
+ itimer.it_interval.tv_sec = itimer.it_interval.tv_usec = 0;
+ signal(SIGALRM, finish);
+ setitimer(ITIMER_REAL, &itimer, NULL);
+ } else pinger();
+}
+
+static void doit(void)
+{
+ int cc;
+ struct sockaddr_in from;
+ socklen_t fromlen;
+
+ for(;;) {
+ fromlen = sizeof(from);
+ cc = recvfrom(TT.sock, (char *) packet, packlen,
+ 0, (struct sockaddr *)&from, &fromlen);
+ if (cc < 0) {
+ if (errno != EINTR) perror_msg("recvfrom");
+ continue;
+ }
+ gettimeofday(&now, 0);
+ pr_pack(packet, cc, &from);
+ if(TT.count && (TT.nreceived >= TT.count)) break;
+ }
+ finish(0);
+}
+
+void ping_main(void)
+{
+ int const_int_1 = 1, i;
+ struct in6_addr in6;
+ struct itimerval itimer;
+ struct timeval interval_tv = {1, 0}; //1sec interval
+
+ if(!(toys.optflags & FLAG_4) && (inet_pton(AF_INET6, toys.optargs[0], (void*)&in6)))
+ toys.optflags |= FLAG_6;
+ if (toys.optflags & FLAG_6) {
+ //ping6 support 4 options
+ //1 for cmdname 1 for NULL and toys.optc
+ int cnt = 0, opt = 0;
+ char **argv6 = xzalloc((6 + toys.optc) * sizeof(char*));
+ argv6[cnt++] = "ping6";
+ if (toys.optflags & FLAG_c) argv6[cnt++] = xmprintf("-c%d",TT.count);
+ if (toys.optflags & FLAG_s) argv6[cnt++] = xmprintf("-s%d",TT.size);
+ if (toys.optflags & FLAG_I) argv6[cnt++] = xmprintf("-I%s",TT.iface);
+ if (toys.optflags & FLAG_q) argv6[cnt++] = "-q";
+
+ while(opt < toys.optc) argv6[cnt++] = toys.optargs[opt++];
+ xexec(argv6);
+ }
+
+ TT.sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (!(toys.optflags & FLAG_s)) TT.size = 64 - PHDR_LEN;
+ if (toys.optflags & FLAG_I) gethost("-I", TT.iface, &src_addr, 0, 0);
+ if (toys.optflags & FLAG_q) pingflags |= F_QUIET;
+
+ gethost("", toys.optargs[0], &send_addr, hostname, sizeof(hostname));
+ if (TT.size >= PHDR_LEN) pingflags |= F_TIMING;
+ packlen = TT.size + 60 + 76; /* MAXIP + MAXICMP */
+ packet = xmalloc(packlen);
+
+ TT.ident = rand() & 0xFFFF;
+ for (i = PHDR_LEN; i < (int)TT.size; i++) opack_icmp.icmp_data[i] = i;
+
+ if (TT.ttl) {
+ if (setsockopt(TT.sock, IPPROTO_IP, IP_TTL, &TT.ttl, sizeof(TT.ttl)) < 0)
+ perror_exit("Can't set time-to-live");
+ if (setsockopt(TT.sock, IPPROTO_IP, IP_MULTICAST_TTL, &TT.ttl, sizeof(TT.ttl)) < 0)
+ perror_exit("Can't set multicast time-to-live");
+ }
+ if ((toys.optflags & FLAG_Q) &&
+ setsockopt(TT.sock, IPPROTO_IP, IP_TOS, &TT.tos, sizeof(TT.tos)) < 0)
+ perror_exit("IP_TOS %d failed ", TT.tos);
+
+ if (pingflags & F_SOURCE_ADDR) {
+ if (setsockopt(TT.sock, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *) &src_addr.sin_addr,
+ sizeof(src_addr.sin_addr)) < 0)
+ perror_exit("Can't set source interface/address");
+ if (bind(TT.sock, (struct sockaddr*)&src_addr, sizeof(src_addr)))
+ perror_exit("bind");
+ }
+ if (TT.iface && memcmp(&src_addr, &send_addr, sizeof(send_addr))) {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ xstrncpy(ifr.ifr_name, TT.iface, IFNAMSIZ);
+ if (setsockopt(TT.sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
+ perror_msg("can't bind to interface %s", TT.iface);
+ }
+ //enable PING to Broadcast address
+ setsockopt(TT.sock, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+ xprintf("PING %s (%s):", hostname, inet_ntoa(send_addr.sin_addr));
+ if (toys.optflags & FLAG_I) xprintf(" from %s:", inet_ntoa(src_addr.sin_addr));
+ xprintf(" %d data bytes.\n", (int)TT.size);
+
+ /* When pinging the broadcast address, you can get a lot
+ * of answers. Doing something so evil is useful if you
+ * are trying to stress the ethernet, or just want to
+ * fill the arp cache to get some stuff for /etc/ethers.
+ */
+ while (0 > setsockopt(TT.sock, SOL_SOCKET, SO_RCVBUF,
+ (char*)&bufspace, sizeof(bufspace)))
+ if ((bufspace -= 4096) <= 0)
+ perror_exit("Cannot set the receive buffer size");
+
+ signal(SIGINT, finish);
+ signal(SIGQUIT, prtsig);
+ signal(SIGCONT, prtsig);
+ itimer.it_interval = interval_tv;
+ itimer.it_value = interval_tv;
+ signal(SIGALRM, send_ping);
+ setitimer(ITIMER_REAL, &itimer, NULL); //interval timer between ping
+ send_ping(0);
+ doit();
+}
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */