1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * netspeed: a program to generate network traffic
4 * Copyright (C) 2013 Free Software Foundation, Inc
6 * Author: Nedko Arnaudov
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /* some platforms (i.e. OpenBSD 6.4) don't define SIZE_MAX, but have SIZE_T_MAX */
35 #if !defined(SIZE_MAX) && defined(SIZE_T_MAX)
36 #define SIZE_MAX SIZE_T_MAX
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <netinet/tcp.h>
50 #if !defined(NO_SCHED_FIFO)
54 #define DEFAULT_WORKERS 4
56 #define DEFAULT_DL_RESOURCE "/speedtest/random4000x4000.jpg"
57 //#define DL_RESOURCE "/speedtest/random500x500.jpg"
58 #define DEFAULT_UL_RESOURCE "/speedtest/upload.php"
59 #define DEFAULT_UL_SIZE ((size_t)(3 * 1024 * 1024))
61 #define ABOUT "Generate network traffic. Written by Nedko Arnaudov."
64 #if !defined(MSG_NOSIGNAL)
65 #define MSG_NOSIGNAL 0
68 typedef int (* work_fn
)(void * ctx
, short revents
, int * fd_ptr
, short * events_ptr
);
69 typedef void (* cleanup_fn
)(void * ctx
);
71 #define STATE_NOT_CONNECTED 0
72 #define STATE_CONNECTING 1
73 #define STATE_SENDING_REQUEST_HEADER 2
74 #define STATE_SENDING_REQUEST_BODY 3
75 #define STATE_READING_REPLY_HEADER 4
76 #define STATE_READING_REPLY_BODY 5
77 #define STATE_ERROR -1
86 uint32_t ip
; /* network byte order */
89 char buffer
[1024 * 1024];
93 #define LOGLVL_FORCE 0
94 #define LOGLVL_ERROR 1
95 #define LOGLVL_WARNING 2
97 #define LOGLVL_DEBUG1 4
98 #define LOGLVL_DEBUG2 5
100 #define LOGLVL_DEFAULT_MAX LOGLVL_WARNING
102 static int g_log_max
= LOGLVL_DEFAULT_MAX
;
103 static int g_progress
= 0;
104 static size_t g_workers
= DEFAULT_WORKERS
;
105 static const char * g_dl_resource
= DEFAULT_DL_RESOURCE
;
106 static const char * g_ul_resource
= DEFAULT_UL_RESOURCE
;
107 static size_t g_ul_size
= DEFAULT_UL_SIZE
;
109 void log_msg(int level
, const char * format
, ...) __attribute__((format(printf
, 2, 3)));
110 void log_msg(int level
, const char * format
, ...)
114 if (level
> g_log_max
) return;
116 va_start(ap
, format
);
117 vfprintf(level
> 0 ? stdout
: stderr
, format
, ap
);
121 #define connection_ptr ((struct connection *)ctx)
123 #define LOG_MSG_(level, format, ...) \
124 log_msg(level, format "\n", ##__VA_ARGS__)
125 #define LOG_WORKER(level, no, format, ...) \
126 log_msg(level, "[%d] " format "\n", (int)(no), ##__VA_ARGS__)
127 #define LOG_WORKER_(level, format, ...) \
128 LOG_WORKER(level, connection_ptr->no, format, ##__VA_ARGS__)
130 #define LFRC( format, ...) LOG_MSG(LOGLVL_FORCE, format, ##__VA_ARGS__)
131 #define LERR( format, ...) LOG_MSG(LOGLVL_ERROR, format, ##__VA_ARGS__)
132 #define LWRN( format, ...) LOG_MSG(LOGLVL_WARNING, format, ##__VA_ARGS__)
133 #define LINF( format, ...) LOG_MSG(LOGLVL_INFO, format, ##__VA_ARGS__)
134 #define LDBG1(format, ...) LOG_MSG(LOGLVL_DEBUG1, format, ##__VA_ARGS__)
135 #define LDBG2(format, ...) LOG_MSG(LOGLVL_DEBUG2, format, ##__VA_ARGS__)
137 #define LOG_MSG LOG_WORKER_
139 int worker(void * ctx
, short revents
, int * fd_ptr
, short * events_ptr
)
142 struct sockaddr_in sin
;
150 LDBG2("state=%d", connection_ptr
->state
);
152 switch (connection_ptr
->state
)
154 case STATE_NOT_CONNECTED
:
156 case STATE_CONNECTING
:
157 assert((revents
& POLLOUT
) == POLLOUT
);
158 goto async_connect_done
;
159 case STATE_SENDING_REQUEST_HEADER
:
160 case STATE_SENDING_REQUEST_BODY
:
161 assert((revents
& POLLOUT
) == POLLOUT
);
162 if ((revents
& (POLLERR
| POLLHUP
)) != 0)
164 LERR("async send fd error. revents=%#hx", revents
);
167 ret
= getsockopt(connection_ptr
->socket
, SOL_SOCKET
, SO_ERROR
, &val
, &len
);
170 LERR("getsockopt() failed to get socket send error. %d (%s)", errno
, strerror(errno
));
174 LERR("async send() error %d (%s)", val
, strerror(val
));
178 goto send_request_continue
;
179 case STATE_READING_REPLY_HEADER
:
180 assert((revents
& POLLIN
) == POLLIN
);
181 if ((revents
& (POLLERR
| POLLHUP
)) != 0)
183 LERR("async reply header recv fd error. revents=%#hx", revents
);
186 ret
= getsockopt(connection_ptr
->socket
, SOL_SOCKET
, SO_ERROR
, &val
, &len
);
189 LERR("getsockopt() failed to get socket recv error. %d (%s)", errno
, strerror(errno
));
193 LERR("async recv() error %d (%s)", val
, strerror(val
));
197 goto read_reply_header
;
198 case STATE_READING_REPLY_BODY
:
199 assert((revents
& POLLIN
) == POLLIN
);
200 if ((revents
& (POLLERR
| POLLHUP
)) != 0)
202 LERR("async reply body recv fd error. revents=%#hx", revents
);
205 ret
= getsockopt(connection_ptr
->socket
, SOL_SOCKET
, SO_ERROR
, &val
, &len
);
208 LERR("getsockopt() failed to get socket recv error. %d (%s)", errno
, strerror(errno
));
212 LERR("async recv() error %d (%s)", val
, strerror(val
));
216 goto read_reply_body
;
225 connection_ptr
->socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
226 if (connection_ptr
->socket
== -1)
228 LERR("socket() failed. %d (%s)", errno
, strerror(errno
));
232 ret
= fcntl(connection_ptr
->socket
, F_SETFL
, O_NONBLOCK
);
235 LERR("fcntl() failed to set socket non-blocking mode. %d (%s)", errno
, strerror(errno
));
240 sin
.sin_family
= AF_INET
;
241 sin
.sin_port
= htons(80);
242 sin
.sin_addr
.s_addr
= connection_ptr
->ip
;
244 ret
= connect(connection_ptr
->socket
, (struct sockaddr
*)&sin
, sizeof(struct sockaddr_in
));
247 if (errno
== EINPROGRESS
)
249 connection_ptr
->state
= STATE_CONNECTING
;
250 *fd_ptr
= connection_ptr
->socket
;
251 *events_ptr
= POLLOUT
;
255 LERR("connect() failed. %d (%s)", errno
, strerror(errno
));
259 LINF("connect complete.");
263 if ((revents
& (POLLERR
| POLLHUP
)) != 0)
265 LERR("async connect failed. revents=%#hx", revents
);
269 ret
= getsockopt(connection_ptr
->socket
, SOL_SOCKET
, SO_ERROR
, &val
, &len
);
272 LERR("getsockopt() failed to get socket connect error. %d (%s)", errno
, strerror(errno
));
277 LERR("async connect() failed. %d (%s)", val
, strerror(val
));
281 LINF("async connect complete.");
284 LINF("sending request header...");
286 if (connection_ptr
->upload
)
288 snprintf(size_str
, sizeof(size_str
), "%zu", g_ul_size
);
292 connection_ptr
->buffer
,
293 sizeof(connection_ptr
->buffer
),
295 "User-Agent: netspeed/0.0\r\n"
300 connection_ptr
->upload
? "POST" : "GET",
301 connection_ptr
->upload
? g_ul_resource
: g_dl_resource
,
302 connection_ptr
->host
,
303 connection_ptr
->upload
? "Content-Length: " : "",
304 connection_ptr
->upload
? size_str
: "",
305 connection_ptr
->upload
? "\r\n" : "");
306 if (ret
< -1 || ret
>= (int)sizeof(connection_ptr
->buffer
))
308 LERR("snprintf() failed compose request. %d", ret
);
312 LDBG1("request-header:\n%s", connection_ptr
->buffer
);
314 connection_ptr
->state
= STATE_SENDING_REQUEST_HEADER
;
315 connection_ptr
->offset
= 0;
316 connection_ptr
->size
= (size_t)ret
;
318 send_request_continue
:
319 while (connection_ptr
->size
> 0)
321 if (connection_ptr
->state
== STATE_SENDING_REQUEST_BODY
&&
322 connection_ptr
->size
>= sizeof(connection_ptr
->buffer
))
324 size
= sizeof(connection_ptr
->buffer
);
328 size
= connection_ptr
->size
;
332 connection_ptr
->socket
,
333 connection_ptr
->buffer
+ connection_ptr
->offset
,
343 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
345 *fd_ptr
= connection_ptr
->socket
;
346 *events_ptr
= POLLOUT
;
350 LERR("send() failed. %d (%s)", errno
, strerror(errno
));
354 if (connection_ptr
->state
== STATE_SENDING_REQUEST_HEADER
)
356 connection_ptr
->offset
+= sret
;
359 connection_ptr
->size
-= sret
;
362 if (connection_ptr
->state
== STATE_SENDING_REQUEST_HEADER
)
364 LINF("request header sent");
366 if (connection_ptr
->upload
)
368 connection_ptr
->state
= STATE_SENDING_REQUEST_BODY
;
369 connection_ptr
->offset
= 0;
370 connection_ptr
->size
= g_ul_size
;
371 LINF("sending request body...");
372 goto send_request_continue
;
377 LINF("request body sent");
380 connection_ptr
->state
= STATE_READING_REPLY_HEADER
;
381 connection_ptr
->offset
= 0; /* parsed size */
382 connection_ptr
->size
= 0; /* read size */
385 if (connection_ptr
->size
>= sizeof(connection_ptr
->buffer
))
387 LERR("HTTP reply header too big");
392 connection_ptr
->socket
,
393 connection_ptr
->buffer
+ connection_ptr
->size
,
394 sizeof(connection_ptr
->buffer
) - connection_ptr
->size
,
400 goto read_reply_header
;
403 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
405 *fd_ptr
= connection_ptr
->socket
;
406 *events_ptr
= POLLIN
;
410 LERR("recv() failed. %d (%s)", errno
, strerror(errno
));
414 connection_ptr
->size
+= sret
;
416 for (i
= connection_ptr
->offset
; i
+ 3 < connection_ptr
->size
; i
++)
418 if (connection_ptr
->buffer
[i
] == '\r' &&
419 connection_ptr
->buffer
[i
+ 1] == '\n' &&
420 connection_ptr
->buffer
[i
+ 2] == '\r' &&
421 connection_ptr
->buffer
[i
+ 3] == '\n')
423 connection_ptr
->offset
= i
+ 4;
424 LINF("header size is %zu bytes", connection_ptr
->offset
);
425 for (i
= 0; i
< connection_ptr
->offset
; i
++)
427 if ((signed char)connection_ptr
->buffer
[i
] < 0)
429 LERR("invalid char in HTTP reply header");
433 connection_ptr
->buffer
[i
] = tolower(connection_ptr
->buffer
[i
]);
436 connection_ptr
->buffer
[connection_ptr
->offset
] = 0;
437 LDBG1("reply-header:\n%s", connection_ptr
->buffer
);
439 /* calculate the size of body bytes we already read */
440 i
= connection_ptr
->size
- connection_ptr
->offset
;
442 ptr
= strstr(connection_ptr
->buffer
, "content-length");
448 ptr
+= sizeof("content-length") - 1;
450 while (*ptr
== ' ') ptr
++;
458 while (*ptr
== ' ') ptr
++;
464 LINF("total body size is %d bytes", val
);
468 LERR("body bigger than announced");
472 /* substract the already received body bytes */
473 connection_ptr
->size
= (size_t)val
- i
;
478 /* server didnt provide body size,
479 assume body end will be marked by connection close */
480 LWRN("unknown body size");
482 connection_ptr
->size
= SIZE_MAX
;
485 connection_ptr
->state
= STATE_READING_REPLY_BODY
;
486 connection_ptr
->offset
= i
;
487 goto read_reply_body
;
493 /* next time don't parse the bytes already parsed */
494 connection_ptr
->offset
= i
- 4;
497 goto read_reply_header
;
500 while (connection_ptr
->size
> 0)
503 connection_ptr
->socket
,
504 connection_ptr
->buffer
,
505 sizeof(connection_ptr
->buffer
),
511 goto read_reply_header
;
514 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
516 *fd_ptr
= connection_ptr
->socket
;
517 *events_ptr
= POLLIN
;
521 LERR("recv() failed. %d (%s)", errno
, strerror(errno
));
525 connection_ptr
->size
-= sret
;
526 connection_ptr
->offset
+= sret
;
535 printf("(%zd)", sret
);
542 LINF("%zu body bytes read", connection_ptr
->offset
);
544 //return 0; /* done */
547 connection_ptr
->state
= STATE_ERROR
;
551 void connection_cleanup(void * ctx
)
553 if (connection_ptr
->socket
!= -1)
555 LINF("closing socket...");
556 close(connection_ptr
->socket
);
560 #undef connection_ptr
562 #define LOG_MSG LOG_MSG_
564 uint32_t resolve_host(const char * hostname
)
566 struct hostent
* he_ptr
;
568 he_ptr
= gethostbyname(hostname
);
571 LERR("Cannot resolve \"%s\". h_errno is %d", hostname
, h_errno
);
575 return *(uint32_t *)(he_ptr
->h_addr
);
583 const char * hostname
,
586 cleanup_fn
* cleanup
)
588 struct connection
* connection_ptr
;
591 if (strcmp(type
, "d") == 0)
595 else if (strcmp(type
, "u") == 0)
601 LOG_WORKER(LOGLVL_ERROR
, worker_no
, "unknown type \"%s\".", type
);
605 LOG_WORKER(LOGLVL_INFO
, worker_no
, "connecting to %s for %s", hostname
, upload
? "uploading" : "downloading");
607 connection_ptr
= malloc(sizeof(struct connection
));
608 if (connection_ptr
== NULL
)
610 LOG_WORKER(LOGLVL_ERROR
, worker_no
, "memory allocation failed.");
614 connection_ptr
->no
= worker_no
;
615 connection_ptr
->upload
= upload
;
616 connection_ptr
->host
= hostname
;
617 connection_ptr
->state
= STATE_NOT_CONNECTED
;
618 connection_ptr
->socket
= -1;
619 connection_ptr
->ip
= ip
;
621 *ctx
= connection_ptr
;
623 *cleanup
= connection_cleanup
;
628 #if !defined(NO_SCHED_FIFO)
629 static bool disable_preemption(int priority
)
631 struct sched_param sched_param
;
633 sched_param
.sched_priority
= priority
;
634 if (sched_setscheduler(0, SCHED_FIFO
, &sched_param
) != 0)
636 LERR("Cannot set scheduling policy %d (%s)", errno
, strerror(errno
));
644 int main(int argc
, char ** argv
)
653 struct pollfd pollfd
;
655 struct worker
* workers
= NULL
;
656 struct pollfd
* pollfds
= NULL
;
657 int nfds
, poll_index
;
659 bool worker_count_supplied
= false;
660 size_t workers_per_host
;
665 /* process options */
666 while (argc
> 0 && **argv
== '-')
668 if (strcmp(*argv
, "-v") == 0)
672 else if (strcmp(*argv
, "-q") == 0)
676 else if (strcmp(*argv
, "-s") == 0)
680 else if (strcmp(*argv
, "-p") == 0)
684 else if (strcmp(*argv
, "--help") == 0)
688 else if (strcmp(*argv
, "-w") == 0 && argc
>= 2)
693 LERR("Bad value for workers option");
698 worker_count_supplied
= true;
701 else if (strcmp(*argv
, "-u") == 0 && argc
>= 2)
706 LERR("Bad value for upload size option");
715 LWRN("Ignoring unknown option %s", *argv
);
718 //printf("log level max is %d\n", g_log_max);
722 if (argc
== 0 || (argc
% 2) != 0)
726 LFRC("Usage: netspeed [options] <type> <host> [<type> <host>] ...");
729 LFRC(" -v Increase verbosity. May be used more than once.");
730 LFRC(" -q Decrease verbosity. May be used more than once.");
731 LFRC(" -s Be completely silent.");
732 LFRC(" -p Print progress (dots). Use twice for printing chunk sizes.");
733 LFRC(" -w <num> Worker count (per type/host pair).");
734 LFRC(" -u <num> Upload size. Ignored when downloading.");
735 LFRC(" --help Shows this help text");
737 LFRC(" <type> is either 'u' (upload) or 'd' (download)");
738 LFRC(" <host> is a ookla speedtest host");
744 if (g_log_max
!= LOGLVL_DEFAULT_MAX
)
746 LFRC("log level max is %d", g_log_max
);
751 workers_per_host
= worker_count_supplied
? g_workers
: 1;
753 LFRC("%zu worker(s) per host", workers_per_host
);
755 g_workers
= workers_per_host
* (argc
/ 2);
757 LFRC("%zu total worker(s)", g_workers
);
761 workers_per_host
= g_workers
;
762 LFRC("%zu worker(s)", g_workers
);
765 LFRC("POST body size: %zu bytes", g_ul_size
);
766 //LFRC("download resource: %s", g_dl_resource);
767 //LFRC("upload resource: %s", g_ul_resource);
769 workers
= calloc(g_workers
, sizeof(struct worker
));
772 LERR("memory allocation failed. (workers)");
777 pollfds
= calloc(g_workers
, sizeof(struct pollfd
));
780 LERR("memory allocation failed. (pollfds)");
785 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
)
787 LERR("Cannot ignore SIGPIPE. %d (%s)", errno
, strerror(errno
));
795 assert(argc
% 2 == 0);
797 ip
= resolve_host(argv
[1]);
803 for (i
= host_index
* workers_per_host
; i
< (host_index
+ 1) * workers_per_host
; i
++)
812 &workers
[i
].cleanup
))
818 workers
[i
].pollfd
.fd
= -1;
819 workers
[i
].pollfd
.revents
= 0;
827 ret
= mlockall(MCL_CURRENT
| MCL_FUTURE
);
830 LERR("mlockall() failed. %d (%s)", errno
, strerror(errno
));
834 #if !defined(NO_SCHED_FIFO)
835 if (!disable_preemption(10))
843 assert(poll_index
== 0);
844 for (i
= 0; i
< g_workers
; i
++)
846 if (workers
[i
].work
!= NULL
)
848 if (workers
[i
].pollfd
.fd
== -1 || /* first time */
849 workers
[i
].pollfd
.revents
!= 0) /* or when there are pending events */
851 ret
= workers
[i
].work(
853 workers
[i
].pollfd
.revents
,
854 &workers
[i
].pollfd
.fd
,
855 &workers
[i
].pollfd
.events
);
865 workers
[i
].work
= NULL
;
866 LOG_WORKER(LOGLVL_INFO
, i
, "worker done");
870 workers
[i
].pollfd
.revents
= 0;
872 assert(workers
[i
].pollfd
.fd
!= -1);
873 assert(workers
[i
].pollfd
.events
!= 0);
874 LOG_WORKER(LOGLVL_DEBUG2
, i
, "worker waits on %d\n", workers
[i
].pollfd
.fd
);
878 LOG_WORKER(LOGLVL_DEBUG2
, i
, "worker still waits on %d\n", workers
[i
].pollfd
.fd
);
881 pollfds
[poll_index
].fd
= workers
[i
].pollfd
.fd
;
882 pollfds
[poll_index
].events
= workers
[i
].pollfd
.events
;
883 pollfds
[poll_index
].revents
= 0;
891 LINF("no more workers");
896 LDBG2("polling %d fds", nfds
);
897 ret
= poll(pollfds
, nfds
, -1);
898 LDBG2("poll() returns %d", ret
);
901 LERR("poll() failed. %d (%s)", errno
, strerror(errno
));
909 assert(poll_index
< nfds
);
910 if (pollfds
[poll_index
].revents
!= 0)
912 for (i
= 0; i
< g_workers
; i
++)
914 if (workers
[i
].work
!= NULL
&&
915 workers
[i
].pollfd
.fd
== pollfds
[poll_index
].fd
)
917 workers
[i
].pollfd
.revents
= pollfds
[poll_index
].revents
;
918 assert(workers
[i
].pollfd
.revents
!= 0);
922 assert(i
< g_workers
); /* fd/worker not found */
933 for (i
= 0; i
< g_workers
; i
++)
935 if (workers
[i
].cleanup
!= NULL
)
937 workers
[i
].cleanup(workers
[i
].ctx
);