1 /*
2  * uhub - A tiny ADC p2p connection hub
3  * Copyright (C) 2007-2014, Jan Vidar Krey
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "uhub.h"
21 
22 static int is_ipv6_supported = -1; /* -1 = CHECK, 0 = NO, 1 = YES */
23 static int net_initialized = 0;
24 
25 static struct net_statistics stats;
26 static struct net_statistics stats_total;
27 
28 #if defined(IPV6_BINDV6ONLY)
29 #define SOCK_DUAL_STACK_OPT IPV6_BINDV6ONLY
30 #elif defined(IPV6_V6ONLY)
31 #define SOCK_DUAL_STACK_OPT IPV6_V6ONLY
32 #endif
33 
34 
net_initialize()35 int net_initialize()
36 {
37 #ifdef WINSOCK
38 	struct WSAData wsa;
39 #endif
40 	if (!net_initialized)
41 	{
42 		LOG_TRACE("Initializing network monitor.");
43 
44 #ifdef WINSOCK
45 		if (WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR)
46 		{
47 			LOG_ERROR("Unable to initialize winsock.");
48 			return -1;
49 		}
50 #endif /* WINSOCK */
51 
52 		if (!net_backend_init()
53 #ifdef SSL_SUPPORT
54 			|| !net_ssl_library_init()
55 #endif
56 			)
57 		{
58 #ifdef WINSOCK
59 			WSACleanup();
60 #endif
61 			return -1;
62 		}
63 
64 		net_dns_initialize();
65 
66 		net_stats_initialize();
67 		net_initialized = 1;
68 		return 0;
69 	}
70 	return -1;
71 }
72 
net_get_max_sockets()73 size_t net_get_max_sockets()
74 {
75 #ifdef HAVE_GETRLIMIT
76 	struct rlimit limits;
77 	if (getrlimit(RLIMIT_NOFILE, &limits) == 0)
78 	{
79 		return MIN(limits.rlim_max, 65536);
80 	}
81 	LOG_ERROR("getrlimit() failed");
82 	return 1024;
83 #else
84 #ifdef WIN32
85 	return FD_SETSIZE;
86 #else
87 	LOG_WARN("System does not have getrlimit(): constrained to 1024 sockets");
88 	return 1024;
89 #endif
90 #endif /* HAVE_GETRLIMIT */
91 }
92 
93 
net_destroy()94 int net_destroy()
95 {
96 	if (net_initialized)
97 	{
98 		LOG_TRACE("Shutting down network monitor");
99 
100 		net_dns_destroy();
101 
102 		net_backend_shutdown();
103 
104 #ifdef SSL_SUPPORT
105 		net_ssl_library_shutdown();
106 #endif /* SSL_SUPPORT */
107 
108 #ifdef WINSOCK
109 		WSACleanup();
110 #endif
111 		net_initialized = 0;
112 		return 0;
113 	}
114 	return -1;
115 }
116 
net_error_out(int fd,const char * func)117 static void net_error_out(int fd, const char* func)
118 {
119 	int err = net_error();
120 	LOG_ERROR("%s, fd=%d: %s (%d)", func, fd, net_error_string(err), err);
121 }
122 
net_error()123 int net_error()
124 {
125 #ifdef WINSOCK
126 	return WSAGetLastError();
127 #else
128 	return errno;
129 #endif
130 }
131 
132 
net_error_string(int code)133 const char* net_error_string(int code)
134 {
135 #ifdef WINSOCK
136 	static char string[32];
137 	snprintf(string, 32, "error code: %d", code);
138 	return string;
139 #else
140 	return strerror(code);
141 #endif
142 }
143 
144 
net_setsockopt(int fd,int level,int opt,const void * optval,socklen_t optlen)145 static int net_setsockopt(int fd, int level, int opt, const void* optval, socklen_t optlen)
146 {
147 	int ret = -1;
148 #ifdef WINSOCK
149 	ret = setsockopt(fd, level, opt, (const char*) optval, optlen);
150 #else
151 	ret = setsockopt(fd, level, opt, optval, optlen);
152 #endif
153 
154 	if (ret == -1)
155 	{
156 		net_error_out(fd, "net_setsockopt");
157 	}
158 
159 	return ret;
160 }
161 
net_getsockopt(int fd,int level,int opt,void * optval,socklen_t * optlen)162 static int net_getsockopt(int fd, int level, int opt, void* optval, socklen_t* optlen)
163 {
164 	int ret = -1;
165 #ifdef WINSOCK
166 	ret = getsockopt(fd, level, opt, (char*) optval, optlen);
167 #else
168 	ret = getsockopt(fd, level, opt, optval, optlen);
169 #endif
170 
171 	if (ret == -1)
172 	{
173 		net_error_out(fd, "net_getsockopt");
174 	}
175 
176 	return ret;
177 }
178 
179 
net_set_nonblocking(int fd,int toggle)180 int net_set_nonblocking(int fd, int toggle)
181 {
182 	int ret = -1;
183 #ifdef WINSOCK
184 	u_long on = toggle ? 1 : 0;
185 	ret = ioctlsocket(fd, FIONBIO, &on);
186 #else
187 #ifdef __sun__
188 	int flags = fcntl(fd, F_GETFL, 0);
189 	if (flags != -1)
190 	{
191 		if (toggle) flags |= O_NONBLOCK;
192 		else        flags &= ~O_NONBLOCK;
193 		ret = fcntl(fd, F_SETFL, flags);
194 	}
195 #else
196 	ret = ioctl(fd, FIONBIO, &toggle);
197 #endif
198 #endif
199 	if (ret == -1)
200 	{
201 		net_error_out(fd, "net_set_nonblocking");
202 	}
203 	return ret;
204 }
205 
206 /* NOTE: Possibly only supported on BSD and OSX? */
net_set_nosigpipe(int fd,int toggle)207 int net_set_nosigpipe(int fd, int toggle)
208 {
209 	int ret = -1;
210 #ifdef SO_NOSIGPIPE
211 	ret = net_setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &toggle, sizeof(toggle));
212 	if (ret == -1)
213 	{
214 		net_error_out(fd, "net_set_nosigpipe");
215 	}
216 #endif
217 	return ret;
218 }
219 
net_set_close_on_exec(int fd,int toggle)220 int net_set_close_on_exec(int fd, int toggle)
221 {
222 #ifdef WINSOCK
223 	return -1; /* FIXME: How is this done on Windows? */
224 #else
225 	return fcntl(fd, F_SETFD, toggle);
226 #endif
227 }
228 
net_set_linger(int fd,int toggle)229 int net_set_linger(int fd, int toggle)
230 {
231 	int ret;
232 	ret = net_setsockopt(fd, SOL_SOCKET, SO_LINGER, &toggle, sizeof(toggle));
233 	if (ret == -1)
234 	{
235 		net_error_out(fd, "net_set_linger");
236 	}
237 	return ret;
238 }
239 
net_set_keepalive(int fd,int toggle)240 int net_set_keepalive(int fd, int toggle)
241 {
242 	int ret;
243 	ret = net_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &toggle, sizeof(toggle));
244 	if (ret == -1)
245 	{
246 		net_error_out(fd, "net_set_keepalive");
247 	}
248 	return ret;
249 }
250 
251 
net_set_reuseaddress(int fd,int toggle)252 int net_set_reuseaddress(int fd, int toggle)
253 {
254 	int ret;
255 	ret = net_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &toggle, sizeof(toggle));
256 	if (ret == -1)
257 	{
258 		net_error_out(fd, "net_set_reuseaddress");
259 	}
260 	return ret;
261 }
262 
net_set_sendbuf_size(int fd,size_t size)263 int net_set_sendbuf_size(int fd, size_t size)
264 {
265 	return net_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
266 }
267 
net_get_sendbuf_size(int fd,size_t * size)268 int net_get_sendbuf_size(int fd, size_t* size)
269 {
270 	socklen_t sz = sizeof(*size);
271 	return net_getsockopt(fd, SOL_SOCKET, SO_SNDBUF, size, &sz);
272 }
273 
net_set_recvbuf_size(int fd,size_t size)274 int net_set_recvbuf_size(int fd, size_t size)
275 {
276 	return net_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
277 }
278 
net_get_recvbuf_size(int fd,size_t * size)279 int net_get_recvbuf_size(int fd, size_t* size)
280 {
281 	socklen_t sz = sizeof(*size);
282 	return net_getsockopt(fd, SOL_SOCKET, SO_RCVBUF, size, &sz);
283 }
284 
285 
286 
net_close(int fd)287 int net_close(int fd)
288 {
289 #ifdef WINSOCK
290 	int ret = closesocket(fd);
291 #else
292 	int ret = close(fd);
293 #endif
294 
295 	if (ret == 0)
296 	{
297 		net_stats_add_close();
298 	}
299 	else
300 	{
301 		if (ret != -1)
302 		{
303 			net_stats_add_error();
304 		}
305 	}
306 	return ret;
307 }
308 
net_shutdown_r(int fd)309 int net_shutdown_r(int fd)
310 {
311 #ifdef WINSOCK
312 	return shutdown(fd, SD_RECEIVE);
313 #else
314 	return shutdown(fd, SHUT_RD);
315 #endif
316 }
317 
net_shutdown_w(int fd)318 int net_shutdown_w(int fd)
319 {
320 #ifdef WINSOCK
321 	return shutdown(fd, SD_SEND);
322 #else
323 	return shutdown(fd, SHUT_WR);
324 #endif
325 }
326 
net_shutdown_rw(int fd)327 int net_shutdown_rw(int fd)
328 {
329 #ifdef WINSOCK
330 	return shutdown(fd, SD_BOTH);
331 #else
332 	return shutdown(fd, SHUT_RDWR);
333 #endif
334 }
335 
net_accept(int fd,struct ip_addr_encap * ipaddr)336 int net_accept(int fd, struct ip_addr_encap* ipaddr)
337 {
338 	struct sockaddr_storage addr;
339         struct sockaddr_in*  addr4;
340         struct sockaddr_in6* addr6;
341 	socklen_t addr_size;
342 	int ret = 0;
343 	addr_size = sizeof(struct sockaddr_storage);
344 
345 	memset(&addr, 0, addr_size);
346 	addr4 = (struct sockaddr_in*) &addr;
347 	addr6 = (struct sockaddr_in6*) &addr;
348 
349 	ret = accept(fd, (struct sockaddr*) &addr, &addr_size);
350 
351 	if (ret == -1)
352 	{
353 		switch (net_error())
354 		{
355 #if defined(__HAIKU__)
356 			case ETIMEDOUT:
357 #endif
358 #if defined(__linux__)
359 			case ENETDOWN:
360 			case EPROTO:
361 			case ENOPROTOOPT:
362 			case EHOSTDOWN:
363 			case ENONET:
364 			case EHOSTUNREACH:
365 			case EOPNOTSUPP:
366 				errno = EWOULDBLOCK;
367 #endif
368 #ifdef WINSOCK
369 			case WSAEWOULDBLOCK:
370 				break;
371 #else
372 			case EWOULDBLOCK:
373 				break;
374 #endif
375 			default:
376 				net_error_out(fd, "net_accept");
377 				net_stats_add_error();
378 				return -1;
379 		}
380 	}
381 	else
382 	{
383 		net_stats_add_accept();
384 
385 		if (ipaddr)
386 		{
387 			memset(ipaddr, 0, sizeof(struct ip_addr_encap));
388 			ipaddr->af = addr.ss_family;;
389 			if (ipaddr->af == AF_INET6)
390 			{
391 				char address[INET6_ADDRSTRLEN+1] = { 0, };
392 				net_address_to_string(AF_INET6, (void*) &addr6->sin6_addr, address, INET6_ADDRSTRLEN+1);
393 				if (strchr(address, '.'))
394 				{
395 					/* Hack to convert IPv6 mapped IPv4 addresses to true IPv4 addresses */
396 					ipaddr->af = AF_INET;
397 					net_string_to_address(AF_INET, address, (void*) &ipaddr->internal_ip_data.in);
398 				}
399 				else
400 				{
401 					memcpy(&ipaddr->internal_ip_data.in6, &addr6->sin6_addr, sizeof(struct in6_addr));
402 				}
403 			}
404 			else
405 			{
406 				memcpy(&ipaddr->internal_ip_data.in, &addr4->sin_addr, sizeof(struct in_addr));
407 			}
408 		}
409 	}
410 
411 	return ret;
412 }
413 
414 
net_connect(int fd,const struct sockaddr * serv_addr,socklen_t addrlen)415 int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen)
416 {
417 	int ret = connect(fd, serv_addr, addrlen);
418 	if (ret == -1)
419 	{
420 #ifdef WINSOCK
421 		if (net_error() != WSAEINPROGRESS)
422 #else
423 		if (net_error() != EINPROGRESS)
424 #endif
425 		{
426 			net_error_out(fd, "net_connect");
427 			net_stats_add_error();
428 		}
429 	}
430 	return ret;
431 }
432 
433 
434 
net_is_ipv6_supported()435 int net_is_ipv6_supported()
436 {
437 	if (is_ipv6_supported == -1)
438 	{
439 		int ret = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
440 		if (ret == -1)
441 		{
442 #ifdef WINSOCK
443 			if (net_error() == WSAEAFNOSUPPORT)
444 #else
445 			if (net_error() == EAFNOSUPPORT)
446 #endif
447 			{
448 				LOG_TRACE("net_is_ipv6_supported(): IPv6 is not supported on this system.");
449 				is_ipv6_supported = 0;
450 				return 0;
451 			}
452 
453 			net_error_out(ret, "net_is_ipv6_supported");
454 		}
455 		else
456 		{
457 #ifdef SOCK_DUAL_STACK_OPT
458 			int off = 0;
459 			if (net_setsockopt(ret, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0)
460 			{
461 				LOG_ERROR("net_socket_create(): Dual stack IPv6/IPv4 is not supported.");
462 				is_ipv6_supported = 0;
463 			}
464 			else
465 			{
466 				is_ipv6_supported = 1;
467 			}
468 #else
469 			is_ipv6_supported = 0;
470 #endif
471 			net_close(ret);
472 		}
473 	}
474 	return is_ipv6_supported;
475 }
476 
477 
net_socket_create(int af,int type,int protocol)478 int net_socket_create(int af, int type, int protocol)
479 {
480 	int sd = socket(af, type, protocol);
481 	if (sd == -1)
482 	{
483 		net_error_out(sd, "net_socket_create");
484 		return -1;
485 	}
486 
487 #ifdef SOCK_DUAL_STACK_OPT
488 	/* BSD style */
489 	if (af == AF_INET6)
490 	{
491 		int off = 0;
492 		if (net_setsockopt(sd, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0)
493 		{
494 			LOG_ERROR("net_socket_create():  Cannot set socket to dual stack mode IPv6/IPv4 (%d - %s).", net_error(), net_error_string(net_error()));
495 		}
496 	}
497 #endif
498 	return sd;
499 }
500 
net_address_to_string(int af,const void * src,char * dst,socklen_t cnt)501 const char* net_address_to_string(int af, const void* src, char* dst, socklen_t cnt)
502 {
503 #ifdef WINSOCK
504 	struct sockaddr_in  sin4;
505 	struct sockaddr_in6 sin6;
506 	struct in_addr*  addr4 = (struct in_addr*)  src;
507 	struct in6_addr* addr6 = (struct in6_addr*) src;
508 	size_t size;
509 	LPSOCKADDR addr;
510 	DWORD len = cnt;
511 
512 	switch (af)
513 	{
514 		case AF_INET:
515 			sin4.sin_family  = AF_INET;
516 			sin4.sin_port    = 0;
517 			sin4.sin_addr    = *addr4;
518 			size             = sizeof(sin4);
519 			addr             = (LPSOCKADDR) &sin4;
520 			break;
521 
522 		case AF_INET6:
523 			sin6.sin6_family = AF_INET6;
524 			sin6.sin6_port   = 0;
525 			sin6.sin6_addr   = *addr6;
526 			sin6.sin6_scope_id = 0;
527 			size             = sizeof(sin6);
528 			addr             = (LPSOCKADDR) &sin6;
529 			break;
530 
531 		default:
532 			return NULL;
533 	}
534 
535 	if (WSAAddressToStringA(addr, size, NULL, dst, &len) == 0)
536 	{
537 		return dst;
538 	}
539 
540 	return NULL;
541 #else
542 	if (inet_ntop(af, src, dst, cnt))
543 	{
544 		if (af == AF_INET6 && strncmp(dst, "::ffff:", 7) == 0) /* IPv6 mapped IPv4 address. */
545 		{
546 			memmove(dst, dst + 7, cnt - 7);
547 		}
548 		return dst;
549 	}
550 	return NULL;
551 #endif
552 }
553 
net_string_to_address(int af,const char * src,void * dst)554 int net_string_to_address(int af, const char* src, void* dst)
555 {
556 #ifdef WINSOCK
557 	int ret, size;
558 	struct sockaddr_in  addr4;
559 	struct sockaddr_in6 addr6;
560 	struct sockaddr* addr = 0;
561 	if (af == AF_INET6)
562 	{
563 		if (net_is_ipv6_supported() != 1) return -1;
564 		size = sizeof(struct sockaddr_in6);
565 		addr = (struct sockaddr*) &addr6;
566 	}
567 	else
568 	{
569 		size = sizeof(struct sockaddr_in);
570 		addr = (struct sockaddr*) &addr4;
571 	}
572 
573 	if (!net_initialized)
574 		net_initialize();
575 
576 	ret = WSAStringToAddressA((char*) src, af, NULL, addr, &size);
577 	if (ret == -1)
578 	{
579 		return -1;
580 	}
581 
582 	if (af == AF_INET6)
583 	{
584 		memcpy(dst, &addr6.sin6_addr, sizeof(addr6.sin6_addr));
585 	}
586 	else
587 	{
588 		memcpy(dst, &addr4.sin_addr, sizeof(addr4.sin_addr));
589 	}
590 
591 	return 1;
592 #else
593 	return inet_pton(af, src, dst);
594 #endif
595 }
596 
net_get_peer_address(int fd)597 const char* net_get_peer_address(int fd)
598 {
599 	static char address[INET6_ADDRSTRLEN+1];
600 	struct sockaddr_storage storage;
601 	struct sockaddr_in6* name6;
602 	struct sockaddr_in*  name4;
603 	struct sockaddr*     name;
604 	socklen_t namelen;
605 
606 	memset(address, 0, INET6_ADDRSTRLEN);
607 	namelen = sizeof(struct sockaddr_storage);
608 	memset(&storage, 0, namelen);
609 
610 	name6 = (struct sockaddr_in6*) &storage;
611 	name4 = (struct sockaddr_in*)  &storage;
612 	name  = (struct sockaddr*)     &storage;
613 
614 	if (getpeername(fd, (struct sockaddr*) name, &namelen) != -1)
615 	{
616 		int af = storage.ss_family;
617 		if (af == AF_INET6)
618 		{
619 			net_address_to_string(af, (void*) &name6->sin6_addr, address, INET6_ADDRSTRLEN);
620 		}
621 		else
622 		{
623 			net_address_to_string(af, (void*) &name4->sin_addr, address, INET6_ADDRSTRLEN);
624 		}
625 		return address;
626 	}
627 	else
628 	{
629 		net_error_out(fd, "net_get_peer_address");
630 		net_stats_add_error();
631 	}
632 
633 	return "0.0.0.0";
634 }
635 
net_get_local_address(int fd)636 const char* net_get_local_address(int fd)
637 {
638 	static char address[INET6_ADDRSTRLEN+1];
639 	struct sockaddr_storage storage;
640 	struct sockaddr_in6* name6;
641 	struct sockaddr_in*  name4;
642 	struct sockaddr*     name;
643 	socklen_t namelen;
644 
645 	memset(address, 0, INET6_ADDRSTRLEN);
646 	namelen = sizeof(struct sockaddr_storage);
647 	memset(&storage, 0, namelen);
648 
649 	name6 = (struct sockaddr_in6*) &storage;
650 	name4 = (struct sockaddr_in*)  &storage;
651 	name  = (struct sockaddr*)     &storage;
652 
653 	if (getsockname(fd, (struct sockaddr*) name, &namelen) != -1)
654 	{
655 #ifndef WINSOCK
656 		int af = storage.ss_family;
657 		if (af == AF_INET6)
658 		{
659 			net_address_to_string(af, (void*) &name6->sin6_addr, address, INET6_ADDRSTRLEN);
660 		}
661 		else
662 #else
663 		int af = AF_INET;
664 #endif
665 		{
666 			net_address_to_string(af, (void*) &name4->sin_addr, address, INET6_ADDRSTRLEN);
667 		}
668 		return address;
669 	}
670 	else
671 	{
672 		net_error_out(fd, "net_get_local_address");
673 		net_stats_add_error();
674 	}
675 
676 	return "0.0.0.0";
677 }
678 
679 
680 
net_recv(int fd,void * buf,size_t len,int flags)681 ssize_t net_recv(int fd, void* buf, size_t len, int flags)
682 {
683 	ssize_t ret = recv(fd, buf, len, flags);
684 	if (ret >= 0)
685 	{
686 		net_stats_add_rx(ret);
687 	}
688 	else
689 	{
690 #ifdef WINSOCK
691 		if (net_error() != WSAEWOULDBLOCK)
692 #else
693 		if (net_error() != EWOULDBLOCK)
694 #endif
695 		{
696 			/* net_error_out(fd, "net_recv"); */
697 			net_stats_add_error();
698 		}
699 	}
700 	return ret;
701 }
702 
703 
net_send(int fd,const void * buf,size_t len,int flags)704 ssize_t net_send(int fd, const void* buf, size_t len, int flags)
705 {
706 	ssize_t ret = send(fd, buf, len, flags);
707 	if (ret >= 0)
708 	{
709 		net_stats_add_tx(ret);
710 	}
711 	else
712 	{
713 #ifdef WINSOCK
714 		if (net_error() != WSAEWOULDBLOCK)
715 #else
716 		if (net_error() != EWOULDBLOCK)
717 #endif
718 		{
719 			/* net_error_out(fd, "net_send"); */
720 			net_stats_add_error();
721 		}
722 	}
723 	return ret;
724 }
725 
726 
net_bind(int fd,const struct sockaddr * my_addr,socklen_t addrlen)727 int net_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen)
728 {
729 	int ret = bind(fd, my_addr, addrlen);
730 	if (ret == -1)
731 	{
732 		net_error_out(fd, "net_bind");
733 		net_stats_add_error();
734 	}
735 	return ret;
736 }
737 
738 
net_listen(int fd,int backlog)739 int net_listen(int fd, int backlog)
740 {
741 	int ret = listen(fd, backlog);
742 	if (ret == -1)
743 	{
744 		net_error_out(fd, "net_listen");
745 		net_stats_add_error();
746 	}
747 	return ret;
748 }
749 
750 
net_stats_initialize()751 void net_stats_initialize()
752 {
753 	memset(&stats_total, 0, sizeof(struct net_statistics));
754 	stats_total.timestamp = time(NULL);
755 
756 	memset(&stats, 0, sizeof(struct net_statistics));
757 	stats.timestamp = time(NULL);
758 }
759 
net_stats_get(struct net_statistics ** intermediate,struct net_statistics ** total)760 void net_stats_get(struct net_statistics** intermediate, struct net_statistics** total)
761 {
762 	*intermediate = &stats;
763 	*total = &stats_total;
764 }
765 
net_stats_reset()766 void net_stats_reset()
767 {
768 	stats_total.tx += stats.tx;
769 	stats_total.rx += stats.rx;
770 	stats_total.accept += stats.accept;
771 	stats_total.errors += stats.errors;
772 	stats_total.closed += stats.closed;
773 
774 	memset(&stats, 0, sizeof(struct net_statistics));
775 	stats.timestamp = time(NULL);
776 }
777 
net_stats_timeout()778 int net_stats_timeout()
779 {
780 	return (difftime(time(NULL), stats.timestamp) > TIMEOUT_STATS) ? 1 : 0;
781 }
782 
net_stats_add_tx(size_t bytes)783 void net_stats_add_tx(size_t bytes)
784 {
785 	stats.tx += bytes;
786 }
787 
net_stats_add_rx(size_t bytes)788 void net_stats_add_rx(size_t bytes)
789 {
790 	stats.rx += bytes;
791 }
792 
net_stats_add_accept()793 void net_stats_add_accept()
794 {
795 	stats.accept++;
796 }
797 
net_stats_add_error()798 void net_stats_add_error()
799 {
800 	stats.errors++;
801 }
802 
net_stats_add_close()803 void net_stats_add_close()
804 {
805 	stats.closed++;
806 }
807 
808 
809