Skip to content
Extraits de code Groupes Projets
Valider e291162b rédigé par Vany Ingenzi's avatar Vany Ingenzi
Parcourir les fichiers

Corrected the test suites in order to work. Added another test file of 512...

Corrected the test suites in order to work. Added another test file of 512 bytes. Removed circular dependency in Makefile and added the LinkSimulator
parent 05d686ea
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
*.o
.vscode/
\ No newline at end of file
.vscode/
unwanted_logs/
\ No newline at end of file
......@@ -16,28 +16,33 @@ RECEIVER_OBJECTS = $(RECEIVER_SOURCES:.c=.o)
SENDER = sender
RECEIVER = receiver
all: debug $(SENDER) $(RECEIVER)
all: $(SENDER) $(RECEIVER)
$(SENDER): $(SENDER_OBJECTS)
$(CC) $(SENDER_OBJECTS) -o $@ $(LDFLAGS)
@$(CC) $(SENDER_OBJECTS) -o $@ $(LDFLAGS)
$(RECEIVER): $(RECEIVER_OBJECTS)
$(CC) $(RECEIVER_OBJECTS) -o $@ $(LDFLAGS)
@$(CC) $(RECEIVER_OBJECTS) -o $@ $(LDFLAGS)
%.o: %.c
$(CC) $(HEADERS_DIR) $(CFLAGS) $< -o $@ $(LDFLAGS)
@$(CC) $(HEADERS_DIR) $(CFLAGS) $< -o $@ $(LDFLAGS)
.PHONY: clean mrproper
.PHONY: clean mrproper cleanlogs
clean: mrproper
rm -f $(SENDER_OBJECTS) $(RECEIVER_OBJECTS) test
@rm -f $(SENDER_OBJECTS) $(RECEIVER_OBJECTS) test
@make clean -C linksimulator/
mrproper:
rm -f $(SENDER) $(RECEIVER)
@rm -f $(SENDER) $(RECEIVER)
# Executes the makefile in the linksimulator folder
makelinksim:
@make -C linksimulator/
# It is likely that you will need to update this
tests: all
./tests/run_tests.sh
tests: all makelinksim
@./tests/run_tests.sh
# By default, logs are disabled. But you can enable them with the debug target.
debug: CFLAGS += -D_DEBUG
......
......@@ -13,7 +13,16 @@ For a 3rd year bachelors Computer Networks given to computer science students at
```
## Execution
In order to execute the code, one must first execute the Makefile. For debugging please run `make debug`.
## Testing
For the testing we have a specific directory that contains our tests. In order to execute our tests we advise to run the folowing Makefile command `make tests -s`. The silent argument is to silence any makefile output and only leave the messages of the tests.
We use the LinkSimulator given on the Moodle of the course for advanced tests. And we use Valgrind for the simple tests.
In order to test the transfer of a file then you have to add a file in the testfiles. The suite cases will automatically load the fle and transfer using our protocol.
### Results
Our results on the test files are ...
## System Architecture
\ No newline at end of file
link_sim
*.o
*.swp
The MIT License (MIT)
Copyright (c) 2015 Olivier Tilmans
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# See gcc/clang manual to understand all flags
CFLAGS += -std=c99 # Define which version of the C standard to use
CFLAGS += -Wall # Enable the 'all' set of warnings
CFLAGS += -Werror # Treat all warnings as error
CFLAGS += -Wshadow # Warn when shadowing variables
CFLAGS += -Wextra # Enable additional warnings
CFLAGS += -O2 -D_FORTIFY_SOURCE=2 # Add canary code, i.e. detect buffer overflows
CFLAGS += -fstack-protector-all # Add canary code to detect stack smashing
CFLAGS += -D_XOPEN_SOURCE -D_POSIX_C_SOURCE=201112L # getopt, clock_getttime
SOURCES=$(wildcard *.c)
OBJECTS=$(SOURCES:.c=.o)
LDFLAGS= -rdynamic
ifneq ($(shell uname -s),Darwin) # Apple does not have clock_gettime
LDFLAGS += -lrt # hence does not need librealtime
endif
all: link_sim
debug: CFLAGS += -g -DDEBUG -Wno-unused-parameter -fno-omit-frame-pointer
debug: LDFLAGS += -lSegFault
debug: link_sim
link_sim: $(OBJECTS)
.PHONY: clean mrproper rebuild
clean:
@rm -f $(OBJECTS)
mrproper:
@rm -f link_sim
rebuild: clean mrproper link_sim
# LINGI1341-linksim
A link simulator for the first networking project (LINGI1341)
This program will proxy UDP traffic (datagrams of max 528 bytes), between
two hosts, simulating the behavior of a lossy link.
Using it is a simple as choosing two UDP ports, one for the proxy, and one for the receiver of the protocol.
Client machine:
```bash
./sender server_address proxy_port
```
Server machine:
```bash
./link_sim -p proxy_port -P server_port &
./receiver :: server_port
```
You can control the direction (i.e. forward, reverse or both ways) of the
traffic which is affected by the program.
Use this to test your programs in the events of losses, delays, truncation, ...
Feel free to hack it and submit pull request for bug fixes, ...
/* vi:ts=4:sw=4:noet
The MIT License (MIT)
Copyright (c) 2015-2016 Olivier Tilmans, olivier.tilmans@uclouvain.be
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdlib.h> /* malloc, free, EXIT_X, ...*/
#include <stdio.h> /* printf, fprintf, recvfrom */
#include <unistd.h> /* getopt */
#include <netinet/in.h> /* sockaddr_in6 */
#include <sys/types.h> /* in6_addr */
#include <sys/socket.h> /* socket, bind, connect */
#include <sys/select.h> /* fd_set, select */
#ifdef __APPLE__
#include <sys/time.h> /* gettimeofday */
#endif
#include <time.h> /* clock_gettime, time */
#include <string.h> /* memcpy, memcmp */
#include <errno.h> /* errno, EAGAIN, ... */
#include <fcntl.h> /* fcntl */
#include <arpa/inet.h> /* inet_ntop */
#include <limits.h> /* INT_MAX, SHRT_MAX */
#include <stdint.h> /* uint8_t */
#include "min_queue.h" /* minq_x */
/* Min packet length in the protocol */
#define MIN_PKT_LEN 10
/* Min packet length of a data packet in the protocol */
#define MIN_PKT_PDATA_LEN 12
/* Position of the length bit in the header */
#define LENGTH_FIELD_LENGTH_BIT_POS 8
/*
* Max packet length in the protocol (packet with max header size +
* max payload size + CRC2 size
*/
#define MAX_PKT_LEN (MIN_PKT_LEN + 2 + 512 + 4)
/* Random number between 0 and 100 */
#define RAND_PERCENT ((unsigned int)(rand() % 101))
/* Link directions*/
#define LINK_FORWARD 1
#define LINK_REVERSE 2
#define LINK_BOTH_WAYS (LINK_FORWARD | LINK_REVERSE)
#define SAME_DIRECTION(x, y) (x & y)
static inline const char* get_link_direction(int x)
{
switch (x) {
case LINK_FORWARD: return "Forward";
case LINK_REVERSE: return "Reverse";
case LINK_BOTH_WAYS: return "Both ways";
default: return "Unknown";
}
}
int forward_port = 12345;
int port = 1341;
unsigned int delay = 0;
unsigned int jitter = 0;
unsigned int err_rate = 0;
unsigned int cut_rate = 0;
unsigned int loss_rate = 0;
int link_direction = LINK_FORWARD;
int sfd = -1; /* socket file des. */
minqueue_t *pkt_queue = NULL; /* Queue for delayed packet */
struct timeval last_clock; /* Cache current timestamp */
struct sockaddr_in6 dest_addr, src_addr; /* The addresses of the 2 parties */
int has_source_addr = 0; /* Have we seen the other party yet */
struct pkt_slot { /* One entry in the packet queue */
struct timeval ts; /* Expiration date */
int direction; /* The direction of the packet */
int size; /* How many bytes are used in buf */
char buf[MAX_PKT_LEN]; /* The packet data */
};
/* Get the human-readable representation of an IPv6 */
static inline const char *sockaddr6_to_human(const struct in6_addr *a)
{
static char b[INET6_ADDRSTRLEN];
/* Can safely ignore return value as we control all parameters */
inet_ntop(AF_INET6, a, b, sizeof(*a));
return b;
}
/* @return: left > right */
static inline int timeval_cmp(const struct timeval *left,
const struct timeval *right)
{
return left->tv_sec == right->tv_sec ?
left->tv_usec > right->tv_usec :
left->tv_sec > right->tv_sec;
}
/* @return: c = a - b */
static void timeval_diff(const struct timeval *a,
const struct timeval *b,
struct timeval *c)
{
c->tv_sec = a->tv_sec - b->tv_sec;
c->tv_usec = a->tv_usec - b->tv_usec;
/* Underflow in usec, compensate through secs */
if (c->tv_usec < 0) {
if (--c->tv_sec)
c->tv_usec += 1000000;
}
}
/* Log an action on a processed packet */
#define LOG_PKT_FMT(buf, fmt, ...) \
fprintf(stderr,"[%s %3hhu] " fmt, ((uint8_t)buf[0] & 0xC0) == 0x00 ? "FEC" : "SEQ" , (((uint8_t)buf[0] & 0xC0) <= 0x40) ? buf[3] : buf[1], ##__VA_ARGS__)
#define LOG_PKT(buf, msg) LOG_PKT_FMT(buf, msg "\n")
/* Send a packet to the host we're proxying */
static int write_out(const char *buf, int len, int direction)
{
struct sockaddr_in6 *addr;
switch (direction) {
case LINK_FORWARD: addr = &dest_addr;
break;
case LINK_REVERSE: addr = &src_addr;
break;
default: addr = NULL;
break;
};
LOG_PKT_FMT(buf, "Sent packet (%s).\n", get_link_direction(direction));
return sendto(sfd, buf, len, 0, (struct sockaddr*)addr,
sizeof(*addr)) == len ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* Deliver all queued packets whose timestamps have expired */
static int deliver_delayed_pkt()
{
struct pkt_slot *p = (struct pkt_slot*)minq_peek(pkt_queue);
/* We have a packet and its timestamp is < current time */
while (p && timeval_cmp(&last_clock, &p->ts)) {
/* Send it */
if (write_out(p->buf, p->size, p->direction)) {
/* We can try again later for these errors
* (send bunf is full, or ...) */
if (errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN)
return EXIT_SUCCESS;
/* Otherwise propagate error */
perror("Failed to write all delayed bytes");
return EXIT_FAILURE;
}
minq_pop(pkt_queue);
free(p);
p = (struct pkt_slot*)minq_peek(pkt_queue);
}
return EXIT_SUCCESS;
}
/* @return: 1 iff a != b, else 0 */
static inline int sockaddr_cmp(const struct sockaddr_in6 *a,
const struct sockaddr_in6 *b)
{
return memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(a->sin6_addr)) ||
a->sin6_port != b->sin6_port;
}
/* Simulate the effect of a lossy link on a received packet */
static inline int simulate_link(char *buf, int len, int direction)
{
/* Do we drop it? */
if (loss_rate && RAND_PERCENT < loss_rate) {
LOG_PKT(buf, "Dropping packet");
return EXIT_SUCCESS;
}
/* Do we cut it after the header? (only if packet is elligible) */
if (cut_rate && RAND_PERCENT < cut_rate && len > MIN_PKT_PDATA_LEN && ((uint8_t) buf[0]) == 0x40) {
LOG_PKT(buf, "Truncating packet");
len = MIN_PKT_PDATA_LEN;
/* ... and don't forget to mark it as truncated */
buf[0] |= 0x20;
/* or do we corrupt it? */
} else if (err_rate && RAND_PERCENT < err_rate) {
int idx = rand() % len;
LOG_PKT_FMT(buf, "Corrupting packet: inverted byte #%d\n", idx);
buf[idx] = ~buf[idx];
}
/* Do we want to simulate delay? */
if (delay) {
/* Random delay to add is capped to 10s */
unsigned int applied_delay;
if (jitter) {
if (jitter > delay) {
applied_delay = rand() % (delay + jitter);
} else {
applied_delay = (delay + rand() % (2 * jitter)) - jitter;
}
} else {
applied_delay = delay;
}
applied_delay %= 10000;
LOG_PKT_FMT(buf, "Delayed packet by %u ms\n", applied_delay);
/* Create a slot for the packet queue */
struct pkt_slot *slot;
if (!(slot = malloc(sizeof(*slot)))) {
fprintf(stderr,
"Failed to allocate memory for a delayed packet!\n");
return EXIT_FAILURE;
}
slot->direction = direction;
/* Copy the packet in the slot */
memcpy(slot->buf, buf, len);
slot->size = len;
/* Register expiration date: current date + delay */
slot->ts.tv_sec = last_clock.tv_sec + applied_delay / 1000;
/* delay is in ms not us! */
slot->ts.tv_usec = last_clock.tv_usec + (applied_delay % 1000) * 1000;
/* Enqueue the new slot */
if (minq_push(pkt_queue, slot)) {
perror("Failed to enqueue a packet!");
return EXIT_FAILURE;
}
} else {
/* Forward it to the host we're proxying */
if (write_out(buf, len, direction)) {
perror("Failed to write all bytes");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
/* sfd has been marked for reading, handle the read and process the packet */
static int process_incoming_pkt()
{
struct sockaddr_in6 from; /* Whois the one sending us data? */
socklen_t len_from = sizeof(from);
char buf[MAX_PKT_LEN]; /* Max allowed packet size for the protocol */
int len; /* Actual received packet size */
if ((len = recvfrom(sfd, buf, MAX_PKT_LEN, 0,
(struct sockaddr *)&from, &len_from)) < 0) {
/* Ignore if we have been interrupted by a signal,
* or if select marked sfd as ready for reading
* without any no data available. */
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
return EXIT_SUCCESS;
/* Real error, abort mission */
perror("recv failed");
return EXIT_FAILURE;
}
/* Check packet consistency */
if (len < MIN_PKT_LEN) {
fprintf(stderr,"Received malformed data, dropping. "
"(len < %d)\n", MIN_PKT_LEN);
return EXIT_SUCCESS;
}
/* We need to track who is sending us data, so that we can send him the
* reverse traffic coming from the host we're proxying
*/
if (!has_source_addr) {
memcpy(&src_addr, &from, sizeof(src_addr));
fprintf(stderr, "@@ Remote host is %s [%d]\n",
sockaddr6_to_human(&from.sin6_addr), ntohs(from.sin6_port));
has_source_addr = 1; /* We're logically connected to that guy */
}
int direction = 0;
if (!sockaddr_cmp(&from, &dest_addr))
direction = LINK_REVERSE;
if (!sockaddr_cmp(&from, &src_addr))
direction = LINK_FORWARD;
if (!direction) {
/* We do not know the guy that sent us this data, ignore him */
fprintf(stderr, "@@ Received %d bytes from %s [%d], "
"which is an alien to the connection. Dropping it!\n",
len, sockaddr6_to_human(&from.sin6_addr), ntohs(from.sin6_port));
return EXIT_SUCCESS;
}
/* Simply relay packets from the host we're proxying */
if (!SAME_DIRECTION(direction, link_direction)) {
if (write_out(buf, len, direction)) {
perror("Failed to relay a message without altering it.");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/* We have valid data, simulate the behavior of a lossy link
* before delivery
*/
return simulate_link(buf, len, direction);
}
/* Update last_lock to the current time */
static int update_time()
{
#ifdef __APPLE__
if (gettimeofday(&last_clock, NULL)) {
perror("Cannot get internal clock");
return EXIT_FAILURE;
}
#else /* gettimeofday is deprecated */
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
perror("Cannot internal clock");
return EXIT_FAILURE;
}
last_clock.tv_sec = ts.tv_sec;
last_clock.tv_usec = ts.tv_nsec/1000;
#endif /* __APPLE__ */
return EXIT_SUCCESS;
}
/* If a packet is queue, return how long until it should be delivered,
* otherwise return NULL
*/
static struct timeval* get_queue_timeout()
{
static struct timeval timeout;
/* No queued packet */
if (minq_empty(pkt_queue))
return NULL;
/* Get closest expiration date for the queued packet */
struct timeval *ts = &((struct pkt_slot*)minq_peek(pkt_queue))->ts;
/* timeout = expiration_date - current date */
timeval_diff(ts, &last_clock, &timeout);
/* If we queued the packet for too long, set a 1ms timeout. We cannot set
* 0 as packet queued for too long can be due to the send buffer
* being full, thus packet not being dequeued.
*/
if (timeout.tv_sec < 0 || (!timeout.tv_sec && timeout.tv_usec <= 0)) {
timeout.tv_sec = 0;
timeout.tv_usec = 1000; /* 1ms */
}
return &timeout;
}
/* Loop forever, waiting on packet to process */
static int proxy_loop()
{
fd_set rfds;
FD_ZERO(&rfds);
if (update_time()) return EXIT_FAILURE;
while (1) {
/* Reset sfd in fdset, as timeout expiration would have removed it. */
FD_SET(sfd, &rfds);
/* Wait for incoming data, or end of a delay on a previously received
* packet */
if (select(sfd+1, &rfds, NULL, NULL, get_queue_timeout()) < 0) {
/* Ignore if interruption is due to a signal */
if (errno == EINTR) continue;
else {
/* Bad things do happen ... */
perror("Select failed");
break;
}
}
if (update_time() || /* Update time cache */
deliver_delayed_pkt() || /* Deliver delayed packets */
/* Process incoming packets, applying drop rates etc */
(FD_ISSET(sfd, &rfds) && process_incoming_pkt()))
break;
}
/* Reached only on error */
return EXIT_FAILURE;
}
/* Get a socket,
* bind to all interfaces on specified port,
* connect to localhost on forward_port,
* set as non-blocking,
* @return: -1 on error or a valid file descriptor.
*/
static int get_socket()
{
const char *err_str;
/* Socket creation (IPv6, UDP) */
if ((sfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
err_str = "Cannot create socket";
goto fail;
}
/* Force the socket to use IPv6,
* enable address sharing: multiple processes can consume data for this
* IP/port port combination*/
int enable = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))) {
err_str = "Couldn't enable the re-use of the address ...";
goto fail_socket;
}
if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(enable))) {
err_str = "Cannot force the socket to IPv6";
goto fail_socket;
}
/* Bind the socket to listen on all interfaces (::), on port */
struct sockaddr_in6 addr;
/* Implicitly set address to ::,
* as well as flowinfo-scope as they are unwanted here */
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
if (bind(sfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
err_str = "Cannot bind socket";
goto fail_socket;
}
/* Initialize the dest_addr struct (loopback, forward_port).
* Cannot connect as we will receive/send data from/to multiple hosts */
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(forward_port);
memcpy(&dest_addr.sin6_addr, &in6addr_loopback,
sizeof(dest_addr.sin6_addr));
/* Set the socket to non-blocking,
* as select() indicates that a socket is ready to be read, but not that it
* will not block. */
if (fcntl(sfd, F_SETFL, fcntl(sfd, F_GETFL, 0) | O_NONBLOCK) < 0) {
err_str = "Cannot set the socket to non-blocking mode";
goto fail_socket;
}
return sfd;
fail_socket:
close(sfd);
fail:
perror(err_str);
return -1;
}
/* ? a > b */
static int pkt_slot_cmp(const void *a, const void *b)
{
struct timeval *left = &((struct pkt_slot*)a)->ts;
struct timeval *right = &((struct pkt_slot*)b)->ts;
/* We compare the slots based on their (future) expiration date */
return timeval_cmp(left, right);
}
static int proxy_traffic()
{
#define _DIE(label, msg, ...) do { \
fprintf(stderr, msg, ##__VA_ARGS__); \
rval = EXIT_FAILURE; \
goto label; \
} while (0)
int rval = EXIT_SUCCESS;
if (get_socket() < 0)
_DIE(exit, "Socket initialization failure!\n");
if (!(pkt_queue = minq_new(pkt_slot_cmp)))
_DIE(sfd, "Cannot create priority queue!\n");
/* Process incoming traffic until error (or forever) */
if ((rval = proxy_loop()))
_DIE(queue, "The proxy loop crashed, "
"had %zu element(s) left in pkt_queue\n", minq_size(pkt_queue));
queue:
minq_del(pkt_queue);
sfd:
close(sfd);
exit:
return rval;
#undef _DIE
}
static void usage(const char *prog_name)
{
fprintf(stderr,
"Link sim: A simple lossy link simulator.\n"
"This program will relay all incoming UDP traffic on port `port` to\n"
"the loopback address [::1], on port `forward_port`, simulating \n"
"random losses, transmission errors, ...\n"
"\n"
"Usage: %s [-p port] [-P forward_port] [-d delay] [-j jitter]\n"
" %*s [-e err_rate] [-c cut_rate] [-l loss_rate] [-s seed] [-h]\n"
"-p port The UDP port on which the link simulator operates.\n"
" Defaults to: 1341\n"
"-P forward_port The UDP port on localhost on which the incoming traffic\n"
" should be forwarded.\n"
" Defaults to: 12345\n"
"-d delay The delay (in ms) that should be applied to the traffic.\n"
" Defaults to: 0\n"
"-j jitter The jitter (in ms) that should be applied to the traffic.\n"
" The total delay applied to one packet will be:\n"
" delay + rand[-jitter, jitter].\n"
" Defaults to: 0\n"
" Unused if delay == 0.\n"
"-e err_rate The rate of packet corruption occurrence (in packet/100).\n"
" Defaults to: 0\n"
" A packet that has been corrupted will NOT be cut.\n"
"-c cut_rate The rate of packet being cut after the header to simulate\n"
" router truncation due to high network load (in packet/100).\n"
" Defaults to: 0\n"
" A packet that has been cut will NOT be corrupted.\n"
"-l loss_rate The rate of packets loss (in packet/100).\n"
" Defaults to 0\n"
"-s seed The seed for the random generator, to replay a previous\n"
" session.\n"
" Defaults to: time() casted to int\n"
"-r Simulate the link on the reverse path.\n"
"-R Simulate the link in both ways.\n"
"-h Prints this message and exit.\n",
prog_name,
(int)strlen(prog_name),
"");
}
static long parse_number(const char *val)
{
char *c;
long parsed = strtol(val, &c, 0);
if (*c != '\0')
fprintf(stderr, "!! Parsed %s as %ld\n", val, parsed);
return parsed;
}
int main(int argc, char **argv)
{
int opt;
long seed = -1L;
/* parse option values */
while ((opt = getopt(argc, argv, "p:P:d:j:e:c:s:l:hrR")) != -1) {
switch (opt) {
case 'p':
port = parse_number(optarg) & ((1 << 16) - 1);
break;
case 'P':
forward_port = parse_number(optarg) & ((1 << 16) - 1);
break;
case 'd':
delay = parse_number(optarg);
break;
case 'j':
jitter = parse_number(optarg);
break;
case 'e':
err_rate = parse_number(optarg) % 101;
break;
case 'c':
cut_rate = parse_number(optarg) % 101;
break;
case 'l':
loss_rate = parse_number(optarg) % 101;
break;
case 's':
seed = parse_number(optarg);
break;
case 'r':
link_direction = LINK_REVERSE;
break;
case 'R':
link_direction = LINK_BOTH_WAYS;
break;
case 'h':
/* Fall-through */
default:
usage(argv[0]);
return EXIT_FAILURE;
}
}
if (optind != argc) {
fprintf(stderr, "!! Ignoring positional arguments: ");
for (; optind < argc-1; ++optind)
fprintf(stderr, "%s, ", argv[optind]);
fprintf(stderr, "%s\n", argv[optind]);
}
/* Setup RNG */
if (seed == -1L) {
seed = (int)time(NULL);
fprintf(stderr, "@@ Using random seed: %d\n", (int)seed);
}
srand((int)seed);
fprintf(stderr, "@@ Using parameters:\n"
".. port: %d\n"
".. forward_port: %d\n"
".. delay: %u\n"
".. jitter: %u\n"
".. err_rate: %u\n"
".. cut_rate: %u\n"
".. loss_rate: %u\n"
".. seed: %d\n"
".. link_direction: %s\n",
port, forward_port, delay, jitter, err_rate, cut_rate,
loss_rate, (int)seed, get_link_direction(link_direction));
/* Start proxying UDP traffic according to the specified options */
return proxy_traffic();
}
/* vi:ts=4:sw=4:noet
The MIT License (MIT)
Copyright (c) 2015 Olivier Tilmans, olivier.tilmans@uclouvain.be
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "min_queue.h"
#include <stdlib.h> /* malloc */
#include <string.h> /* memcpy */
/* How many item slots per allocation steps */
#define SLOTS_PER_MALLOC 20
/* We use a heap to store all items,
* this enables us to store it as a complete binary tree where each node
* is a slot in a contiguous array. We can then navigate the tree levels
* by multiplying or dividing by 2 the current level.
* This can also be easily adapted to use k-ary trees.
*
* Invariant to be maintained:
* A node in the tree is always < than its child nodes,
* => The root is the minimal node
*/
/* Index of left child of x */
#define LCHILD(x) ((x) << 1)
/* Index of right child of x */
#define RCHILD(x) (((x) << 1) + 1)
/* Index of parent of x */
#define PARENT(x) ((x) >> 1)
/* The data structure we'll be using to represent the priority queue */
struct minqueue {
minq_key_cmp cmp; /* Compare function for the elements */
size_t size; /* The number of items in the queue */
size_t alloc; /* The number of allocated slots */
void **e; /* The array of slots in the queue */
};
minqueue_t *minq_new(minq_key_cmp cmp)
{
minqueue_t *q;
if (!cmp || !(q = malloc(sizeof(*q))))
return NULL;
/* Allocate multiple elements at once to reduce the calls to realloc */
if (!(q->e = malloc(SLOTS_PER_MALLOC * sizeof(*q->e)))) {
free(q);
return NULL;
}
q->cmp = cmp;
q->size = 0;
q->alloc = SLOTS_PER_MALLOC;
return q;
}
void minq_del(minqueue_t *q)
{
if (!q) return;
free(q->e);
free(q);
}
int minq_push(minqueue_t* q, void *v)
{
if (!q) return -1;
/* Check if we have enough mem. slots */
if (q->size == q->alloc) {
/* We filled all slots, increase by an alloc step */
size_t resize_to = q->size + SLOTS_PER_MALLOC;
/* If we fail, we do not want to lose the previous array of elements */
void **tmp;
if (!(tmp = realloc(q->e, resize_to * sizeof(*q->e))))
/* Failure, exit without changing the queue */
return -1;
/* Bookkeeping */
q->e = tmp;
q->alloc = resize_to;
}
/* Assume insertion at last index */
size_t i = q->size++;
size_t parent = PARENT(i);
/* heapify-up: propagate the new value upwards as long as it is smaller
* than the parent of its insertion point, by swapping it with its parent
*/
while (i && q->cmp(q->e[parent], v)) {
/* move parent down */
q->e[i] = q->e[parent];
/* Insertion point is one level above */
i = parent;
parent = PARENT(i);
}
/* Do the actual insertion */
q->e[i] = v;
return 0;
}
/* Return smallest child available, or the root (0) if none */
static inline size_t has_child(const minqueue_t *q, size_t i)
{
size_t left = LCHILD(i);
/* If we don't have child nodes, return the root of the tree */
if (left >= q->size)
return 0;
size_t right = left+1;
/* Check whether the right child is smaller than the left one */
if (right < q->size && q->cmp(q->e[left], q->e[right]))
/* right < left */
return right;
/* left < right */
return left;
}
void minq_pop(minqueue_t *q)
{
if (minq_empty(q)) return;
/* Swap root with last entry and
* 'forget' about the last one, by decreasing the size*/
q->e[0] = q->e[--q->size];
/* We do not size down the alloc'd slots, the queue could grow again ... */
/* heapify-down: Check that the current node is smaller than both of its
* child nodes, otherwise swap with the minimal child. */
size_t current = 0, min_child;
/* As long as current > min_child (if we have any) */
while ((min_child = has_child(q, current)) &&
q->cmp(q->e[current], q->e[min_child])) {
/* Swap the two elements */
void *tmp;
tmp = q->e[current];
q->e[current] = q->e[min_child];
q->e[min_child] = tmp;
/* Check if we need to push the swapped value further down */
current = min_child;
}
}
void* minq_peek(const minqueue_t *q)
{
if (minq_empty(q)) return NULL;
/* By definition, the minimal element of the queue is the root */
return *q->e;
}
int minq_empty(const minqueue_t *q)
{
return (!q || !q->size);
}
size_t minq_size(const minqueue_t *q)
{
return q ? q->size : 0;
}
/* vi:ts=4:sw=4:noet
The MIT License (MIT)
Copyright (c) 2015 Olivier Tilmans, olivier.tilmans@uclouvain.be
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef __MIN_QUEUE_H_
#define __MIN_QUEUE_H_
#include <stddef.h> /* size_t */
/* Minimal priority queue,
* provides O(log n) on push and pop, O(1) on peek
*/
typedef struct minqueue minqueue_t;
/* Compare two keys
* @return: non-zero if a > b, else 0
*/
typedef int (*minq_key_cmp)(const void *a, const void *b);
/* Create and initialize a new min-queue
* @minq_key_cmp: The key compare function
* @return: NULL on error
*/
minqueue_t *minq_new(minq_key_cmp);
/* Destroy a min-queue instance */
void minq_del(minqueue_t*);
/* Insert a new element in the min-queue
* @minqueue_t: The queue
* @val: the data to insert
* @return: non-zero value on error (queue is then untouched)
*/
int minq_push(minqueue_t*, void *val);
/* Remove the minimal element of the queue */
void minq_pop(minqueue_t*);
/* Get the minimal element of the queue */
void* minq_peek(const minqueue_t*);
/* Check whether the queue is empty or not
* @return: 0 is the queue is non-empty, non-zero otherwise
*/
int minq_empty(const minqueue_t*);
/* How many items in the queue? */
size_t minq_size(const minqueue_t*);
#endif
[ERROR] Receiver has following arguments: stats_filename is (null), listen_ip is ::, listen_port is 12345
[ERROR] This is not an error, now let's code!
......@@ -95,6 +95,7 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p
crc2 = ntohl((uint32_t) crc2);
if (calculate_crc(&data[12], length) != crc2)
return E_CRC;
pkt_set_crc2(pkt, crc2);
}
uint32_t timestamp;
......@@ -106,8 +107,6 @@ pkt_status_code pkt_decode_data_fec(const char *data, const size_t len, pkt_t *p
pkt_set_timestamp(pkt, timestamp);
pkt_set_crc1(pkt, crc1);
pkt_set_payload(pkt, &data[12], length);
pkt_set_crc2(pkt, crc2); // Even if there are no crc2, it doesn't matter
return PKT_OK;
}
......
......@@ -78,6 +78,6 @@ int main(int argc, char **argv) {
receiver_read_write_loop(socket);
close(socket);
free(addr);
return EXIT_SUCCESS;
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
#include <poll.h>
#include "our_utils.h"
#include "receiver.h"
#include <sys/time.h>
int send_if_inneed(struct pollfd * pfd, con_state_t * state)
{
......@@ -185,7 +186,8 @@ int handle_incoming(struct pollfd * pfd, con_state_t * state)
ssize_t read_bytes = read(pfd->fd, (void *) buffer, PKT_MAX_LEN);
if (read_bytes == -1) return 0;
// DEBUG_DUMP(buffer, read_bytes);
DEBUG("Received packet");
DEBUG_DUMP(buffer, read_bytes);
pkt_t * pkt = pkt_new();
if (pkt == NULL)
......@@ -216,23 +218,27 @@ int handle_incoming(struct pollfd * pfd, con_state_t * state)
void reception_loop(struct pollfd * pfd, con_state_t * state)
{
int not_eof = 1;
struct timeval last_recv, curr;
gettimeofday(&last_recv, NULL);
while (not_eof)
{
int ready = poll(pfd, 1, -1);
if (ready == -1)
gettimeofday(&curr, NULL);
if (ready == -1 || (curr.tv_sec - last_recv.tv_sec) > 2)
{
ERROR("Poll failed managing the socket file descriptor.");
DEBUG("Timed out.");
return;
}
if (pfd->revents & POLLIN)
{
gettimeofday(&last_recv, NULL);
if (handle_incoming(pfd, state) != 0) return;
} else if (pfd->revents & POLLOUT)
{
if (send_if_inneed(pfd, state) != 0) return;
} else if ((pfd->revents & POLLHUP) || (pfd->revents & POLLERR)){
ERROR("Occured on the socket");
ERROR("Occured on the socket.");
return;
}
}
......
Fichier déplacé
Hello I'm a good friend, I've seen that you are writing a protocol named TRTP.
How is it going ? Hard ? Good for you. See life is full of hard things just like your
network course. But when you manage to thrive throgh tough times then sweet things are
ahead. So good luck have a nice day of testing and remember : Tough Times Never Last.
PS: Drink a beer when you pass the test. Share your knowledge to others, computer
science isn't a field for selfishness. If you want to switch to Law, Economy, some shit
\ No newline at end of file
# Note that this assumes to be called from the Makefile, you may want to adapt it.
@echo "A very simple test"
./tests/simple_test.sh
# Run the same test, but this time with valgrind
@echo "A very simple test, with Valgrind"
VALGRIND=1 ./tests/simple_test.sh
\ No newline at end of file
FILESIZE=""
TEST_FILES_DIR=./test_files/
# We want our tests to always use valgrind
for FILENAME in "$TEST_FILES_DIR"/*
do
FILESIZE=$(stat -c%s "$FILENAME")
echo "Sending $FILENAME \t [$FILESIZE bytes], \t with Valgrind"
./tests/simple_test.sh $FILENAME
done
\ No newline at end of file
#!/bin/bash
# cleanup d'un test précédent
rm -f received_file input_file
# Fichier au contenu aléatoire de 512 octets
dd if=/dev/urandom of=input_file bs=1 count=512 &> /dev/null
if [ -z "$1" ]; then
echo "Not given the file to send"
exit 1
fi
FILENAME=$1
BASENAME=$(basename $FILENAME)
BASENAME_PREFIX="${BASENAME%.*}"
valgrind_sender=""
valgrind_receiver=""
if [ ! -z "$VALGRIND" ] ; then
valgrind_sender="valgrind --leak-check=full --log-file=valgrind_sender.log"
valgrind_receiver="valgrind --leak-check=full --log-file=valgrind_receiver.log"
fi
# cleanup d'un test précédent
rm -f ./unwanted_logs/${BASENAME_PREFIX}_received_file ./unwanted_logs/${BASENAME_PREFIX}_valgrind_receiver.log ./unwanted_logs/${BASENAME_PREFIX}_valgrind_sender.log
touch ./unwanted_logs/${BASENAME_PREFIX}_received_file ./unwanted_logs/${BASENAME_PREFIX}_valgrind_receiver.log ./unwanted_logs/${BASENAME_PREFIX}_valgrind_sender.log
# On lance le receiver et capture sa sortie standard
$valgrind ./receiver -f received_file :: 2456 2> receiver.log &
valgrind --leak-check=full --log-file=./unwanted_logs/${BASENAME_PREFIX}_valgrind_receiver.log ./receiver ::1 12345 1> ./unwanted_logs/${BASENAME_PREFIX}_received_file 2> ./unwanted_logs/${BASENAME_PREFIX}_valgrind_receiver.log &
receiver_pid=$!
cleanup()
......@@ -26,9 +25,9 @@ cleanup()
trap cleanup SIGINT # Kill les process en arrière plan en cas de ^-C
# On démarre le transfert
if ! $valgrind ./sender ::1 1341 < input_file 2> sender.log ; then
if ! valgrind --leak-check=full --log-file=./unwanted_logs/valgrind_sender.log ./sender -f ${FILENAME} ::1 12345 2> ./unwanted_logs/sender.log ; then
echo "Crash du sender!"
cat sender.log
cat ./unwanted_logs/sender.log
err=1 # On enregistre l'erreur
fi
......@@ -41,16 +40,16 @@ if kill -0 $receiver_pid &> /dev/null ; then
else # On teste la valeur de retour du receiver
if ! wait $receiver_pid ; then
echo "Crash du receiver!"
cat receiver.log
cat ./unwanted_logs/${BASENAME_PREFIX}_valgrind_receiver.log
err=1
fi
fi
# On vérifie que le transfert s'est bien déroulé
if [[ "$(md5sum input_file | awk '{print $1}')" != "$(md5sum received_file | awk '{print $1}')" ]]; then
if [[ "$(md5sum ${FILENAME} | awk '{print $1}')" != "$(md5sum ./unwanted_logs/${BASENAME_PREFIX}_received_file | awk '{print $1}')" ]]; then
echo "Le transfert a corrompu le fichier!"
echo "Diff binaire des deux fichiers: (attendu vs produit)"
diff -C 9 <(od -Ax -t x1z input_file) <(od -Ax -t x1z received_file)
diff -C 9 <(od -Ax -t x1z ${FILENAME}) <(od -Ax -t x1z ./unwanted_logs/${BASENAME_PREFIX}_received_file)
exit 1
else
echo "Le transfert est réussi!"
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter