better TCP and started using libtuntap
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
*.pcapng
|
*.pcapng
|
||||||
loop
|
loop
|
||||||
|
loop2
|
||||||
test_arp
|
test_arp
|
||||||
test_arp_cov
|
test_arp_cov
|
||||||
report_arp_cov
|
report_arp_cov
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#ifndef TUNTAP_EXPORT_H
|
||||||
|
#define TUNTAP_EXPORT_H
|
||||||
|
|
||||||
|
#ifdef TUNTAP_STATIC_DEFINE
|
||||||
|
# define TUNTAP_EXPORT
|
||||||
|
# define TUNTAP_NO_EXPORT
|
||||||
|
#else
|
||||||
|
# ifndef TUNTAP_EXPORT
|
||||||
|
# ifdef tuntap_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define TUNTAP_EXPORT
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define TUNTAP_EXPORT
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef TUNTAP_NO_EXPORT
|
||||||
|
# define TUNTAP_NO_EXPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TUNTAP_DEPRECATED
|
||||||
|
# define TUNTAP_DEPRECATED __attribute__ ((__deprecated__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TUNTAP_DEPRECATED_EXPORT
|
||||||
|
# define TUNTAP_DEPRECATED_EXPORT TUNTAP_EXPORT TUNTAP_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TUNTAP_DEPRECATED_NO_EXPORT
|
||||||
|
# define TUNTAP_DEPRECATED_NO_EXPORT TUNTAP_NO_EXPORT TUNTAP_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 /* DEFINE_NO_DEPRECATED */
|
||||||
|
# ifndef TUNTAP_NO_DEPRECATED
|
||||||
|
# define TUNTAP_NO_DEPRECATED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* TUNTAP_EXPORT_H */
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Tristan Le Guern <leguern AT medu DOT se>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <tuntap-export.h>
|
||||||
|
|
||||||
|
#ifndef LIBTUNTAP_H_
|
||||||
|
# define LIBTUNTAP_H_
|
||||||
|
|
||||||
|
# define TUNTAP_ID_MAX 256
|
||||||
|
# define TUNTAP_ID_ANY 257
|
||||||
|
|
||||||
|
# define TUNTAP_MODE_ETHERNET 0x0001
|
||||||
|
# define TUNTAP_MODE_TUNNEL 0x0002
|
||||||
|
# define TUNTAP_MODE_PERSIST 0x0004
|
||||||
|
|
||||||
|
# define TUNTAP_LOG_NONE 0x0000
|
||||||
|
# define TUNTAP_LOG_DEBUG 0x0001
|
||||||
|
# define TUNTAP_LOG_INFO 0x0002
|
||||||
|
# define TUNTAP_LOG_NOTICE 0x0004
|
||||||
|
# define TUNTAP_LOG_WARN 0x0008
|
||||||
|
# define TUNTAP_LOG_ERR 0x0016
|
||||||
|
|
||||||
|
/* Versioning: 0xMMmm, with 'M' for major and 'm' for minor */
|
||||||
|
# define TUNTAP_VERSION_MAJOR 0
|
||||||
|
# define TUNTAP_VERSION_MINOR 4
|
||||||
|
# define TUNTAP_VERSION ((TUNTAP_VERSION_MAJOR<<8)|TUNTAP_VERSION_MINOR)
|
||||||
|
|
||||||
|
# ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* Forward declaration of struct device */
|
||||||
|
struct device;
|
||||||
|
|
||||||
|
# if defined Windows
|
||||||
|
typedef void* t_tun;
|
||||||
|
# else /* Unix */
|
||||||
|
typedef int t_tun;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* User definable log callback */
|
||||||
|
typedef void (*t_tuntap_log)(int, const char *);
|
||||||
|
|
||||||
|
/* Portable "public" functions */
|
||||||
|
TUNTAP_EXPORT struct device *tuntap_init(void);
|
||||||
|
TUNTAP_EXPORT int tuntap_version(void);
|
||||||
|
TUNTAP_EXPORT void tuntap_destroy(struct device *);
|
||||||
|
TUNTAP_EXPORT void tuntap_release(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_start(struct device *, int, int);
|
||||||
|
TUNTAP_EXPORT char *tuntap_get_ifname(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_ifname(struct device *, const char *);
|
||||||
|
TUNTAP_EXPORT char *tuntap_get_hwaddr(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_hwaddr(struct device *, const char *);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_descr(struct device *, const char *);
|
||||||
|
TUNTAP_EXPORT char *tuntap_get_descr(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_up(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_down(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_get_mtu(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_mtu(struct device *, int);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_ip(struct device *, const char *, int);
|
||||||
|
TUNTAP_EXPORT int tuntap_read(struct device *, void *, size_t);
|
||||||
|
TUNTAP_EXPORT int tuntap_write(struct device *, void *, size_t);
|
||||||
|
TUNTAP_EXPORT int tuntap_get_readable(struct device *);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_nonblocking(struct device *dev, int);
|
||||||
|
TUNTAP_EXPORT int tuntap_set_debug(struct device *dev, int);
|
||||||
|
TUNTAP_EXPORT t_tun tuntap_get_fd(struct device *);
|
||||||
|
|
||||||
|
/* Logging functions */
|
||||||
|
TUNTAP_EXPORT void tuntap_log_set_cb(t_tuntap_log cb);
|
||||||
|
|
||||||
|
# ifdef __cplusplus
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
Submodule
+1
Submodule 3p/libtuntap added at ef7ed7b444
+8
-11
@@ -10,12 +10,6 @@ typedef struct microtcp_socket_t microtcp_socket_t;
|
|||||||
#define MICROTCP_MAX_BUFFERS 8
|
#define MICROTCP_MAX_BUFFERS 8
|
||||||
#define MICROTCP_MAX_SOCKETS 32
|
#define MICROTCP_MAX_SOCKETS 32
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t data[6];
|
|
||||||
} microtcp_mac_t;
|
|
||||||
|
|
||||||
typedef uint32_t microtcp_ip_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
MICROTCP_ERRCODE_NONE = 0,
|
MICROTCP_ERRCODE_NONE = 0,
|
||||||
@@ -30,7 +24,6 @@ typedef enum {
|
|||||||
// Returned by microtcp_accept
|
// Returned by microtcp_accept
|
||||||
MICROTCP_ERRCODE_NOTLISTENER,
|
MICROTCP_ERRCODE_NOTLISTENER,
|
||||||
MICROTCP_ERRCODE_CANTBLOCK,
|
MICROTCP_ERRCODE_CANTBLOCK,
|
||||||
MICROTCP_ERRCODE_NOTHINGTOACCEPT,
|
|
||||||
|
|
||||||
// Returned by microtcp_recv and microtcp_send
|
// Returned by microtcp_recv and microtcp_send
|
||||||
MICROTCP_ERRCODE_NOTCONNECTION,
|
MICROTCP_ERRCODE_NOTCONNECTION,
|
||||||
@@ -44,14 +37,18 @@ typedef struct {
|
|||||||
int (*recv)(void *data, void *dst, size_t len);
|
int (*recv)(void *data, void *dst, size_t len);
|
||||||
} microtcp_callbacks_t;
|
} microtcp_callbacks_t;
|
||||||
|
|
||||||
microtcp_t *microtcp_create();
|
#ifdef MICROTCP_USING_TAP
|
||||||
microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac, microtcp_callbacks_t callbacks);
|
bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks);
|
||||||
|
microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks);
|
||||||
void microtcp_destroy(microtcp_t *mtcp);
|
void microtcp_destroy(microtcp_t *mtcp);
|
||||||
const char *microtcp_strerror(microtcp_errcode_t errcode);
|
const char *microtcp_strerror(microtcp_errcode_t errcode);
|
||||||
microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_errcode_t *errcode);
|
microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_errcode_t *errcode);
|
||||||
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode);
|
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode);
|
||||||
void microtcp_close(microtcp_socket_t *socket);
|
void microtcp_close(microtcp_socket_t *socket);
|
||||||
size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, microtcp_errcode_t *errcode);
|
size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, bool no_block, microtcp_errcode_t *errcode);
|
||||||
size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, microtcp_errcode_t *errcode);
|
size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, bool no_block, microtcp_errcode_t *errcode);
|
||||||
void microtcp_step(microtcp_t *mtcp);
|
void microtcp_step(microtcp_t *mtcp);
|
||||||
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
|
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
all: loop test_net test_arp report_arp_cov
|
all: loop2
|
||||||
|
|
||||||
|
loop2:
|
||||||
|
gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c test/loop2.c -o loop2 -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -pthread -ltuntap -Iinclude/ -I3p/include/ -L3p/lib/
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c src/microtcp_linux.c test/loop.c -o loop -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_LINUX -pthread -Iinclude/ -fsanitize=thread
|
gcc src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c src/microtcp_linux.c test/loop.c -o loop -Wall -Wextra -g -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_LINUX -pthread -Iinclude/ # -fsanitize=thread
|
||||||
|
|
||||||
test_arp:
|
test_arp:
|
||||||
gcc src/arp.c test/test_arp.c test/test_arp_util.c -o test_arp -Wall -Wextra -g -Iinclude/
|
gcc src/arp.c test/test_arp.c test/test_arp_util.c -o test_arp -Wall -Wextra -g -Iinclude/
|
||||||
@@ -16,7 +19,4 @@ report_arp_cov: test_arp_cov
|
|||||||
rm *.gcda *.gcno coverage.info
|
rm *.gcda *.gcno coverage.info
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.gcda *.gcno coverage.info
|
|
||||||
rm -f test_arp test_arp_cov
|
|
||||||
rm -f loop
|
rm -f loop
|
||||||
rm -rf report_arp_cov
|
|
||||||
@@ -16,5 +16,6 @@ static_assert(sizeof(ip_address_t) == 4);
|
|||||||
#define MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}
|
#define MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}
|
||||||
|
|
||||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||||
|
|
||||||
#endif /* MICROTCP_DEFS_H */
|
#endif /* MICROTCP_DEFS_H */
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <endian.h>
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "icmp.h"
|
#include "icmp.h"
|
||||||
|
|
||||||
#define IP_PLUGGED_PROTOCOLS_MAX 4
|
#define IP_PLUGGED_PROTOCOLS_MAX 4
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
uint8_t header_length: 4;
|
uint8_t header_length: 4;
|
||||||
uint8_t version: 4;
|
uint8_t version: 4;
|
||||||
#endif
|
#else
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
uint8_t version: 4;
|
uint8_t version: 4;
|
||||||
uint8_t header_length: 4;
|
uint8_t header_length: 4;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+249
-69
@@ -4,12 +4,16 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h> // inet_pton
|
||||||
#include "ip.h"
|
#include "ip.h"
|
||||||
#include "arp.h"
|
#include "arp.h"
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
#include <microtcp.h>
|
#include <microtcp.h>
|
||||||
|
|
||||||
|
#ifdef MICROTCP_USING_TAP
|
||||||
|
#include <tuntap.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -22,23 +26,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
#define LOCK_WHEN_THREADED(mtcp) do { \
|
#define LOCK_WHEN_THREADED(mtcp) do { pthread_mutex_lock(&(mtcp)->lock); } while (0);
|
||||||
fprintf(stderr, "--- %s before lock\n", __func__); \
|
#define UNLOCK_WHEN_THREADED(mtcp) do { pthread_mutex_unlock(&(mtcp)->lock); } while (0);
|
||||||
fflush(stderr); \
|
|
||||||
pthread_mutex_lock(&(mtcp)->lock); \
|
|
||||||
fprintf(stderr, "--- %s after lock\n", __func__); \
|
|
||||||
fflush(stderr); \
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
#define UNLOCK_WHEN_THREADED(mtcp) do { \
|
|
||||||
fprintf(stderr, "--- %s before unlock\n", __func__); \
|
|
||||||
fflush(stderr); \
|
|
||||||
pthread_mutex_unlock(&(mtcp)->lock); \
|
|
||||||
fprintf(stderr, "--- %s after unlock\n", __func__); \
|
|
||||||
fflush(stderr); \
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
//#define UNLOCK_WHEN_THREADED(mtcp) do { pthread_mutex_unlock(&(mtcp)->lock); } while (0);
|
|
||||||
#else
|
#else
|
||||||
#define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
|
#define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
|
||||||
#define UNLOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
|
#define UNLOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
|
||||||
@@ -68,7 +57,13 @@ struct microtcp_socket_t {
|
|||||||
tcp_connection_t *connection;
|
tcp_connection_t *connection;
|
||||||
};
|
};
|
||||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
pthread_cond_t something_to_accept;
|
union {
|
||||||
|
pthread_cond_t something_to_accept;
|
||||||
|
struct {
|
||||||
|
pthread_cond_t something_to_recv;
|
||||||
|
pthread_cond_t something_to_send;
|
||||||
|
};
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,7 +105,6 @@ const char *microtcp_strerror(microtcp_errcode_t errcode)
|
|||||||
case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error";
|
case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error";
|
||||||
case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket";
|
case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket";
|
||||||
case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function";
|
case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function";
|
||||||
case MICROTCP_ERRCODE_NOTHINGTOACCEPT: return "Accept queue is empty";
|
|
||||||
case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket";
|
case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket";
|
||||||
}
|
}
|
||||||
return "???";
|
return "???";
|
||||||
@@ -353,7 +347,7 @@ process_packet(microtcp_t *mtcp, const void *packet, size_t len)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// Unsupported ethertype
|
// Unsupported ethertype
|
||||||
MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto);
|
//MICROTCP_DEBUG_LOG("Ignoring packet with ethertype %4x", frame->proto);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +384,6 @@ void microtcp_step(microtcp_t *mtcp)
|
|||||||
mtcp->last_update_time = current_time;
|
mtcp->last_update_time = current_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unlock_and_exit:
|
|
||||||
UNLOCK_WHEN_THREADED(mtcp);
|
UNLOCK_WHEN_THREADED(mtcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,19 +397,107 @@ static void *loop(void *data)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac,
|
static bool is_hex_digit(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9')
|
||||||
|
|| (c >= 'a' && c <= 'f')
|
||||||
|
|| (c >= 'A' && c <= 'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int int_from_hex_digit(char c)
|
||||||
|
{
|
||||||
|
assert(is_hex_digit(c));
|
||||||
|
if (c >= 'A' || c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' || c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_mac(const char *src, size_t len,
|
||||||
|
mac_address_t *mac)
|
||||||
|
{
|
||||||
|
if (src == NULL || len != 17
|
||||||
|
|| !is_hex_digit(src[0])
|
||||||
|
|| !is_hex_digit(src[1])
|
||||||
|
|| src[2] != ':'
|
||||||
|
|| !is_hex_digit(src[3])
|
||||||
|
|| !is_hex_digit(src[4])
|
||||||
|
|| src[5] != ':'
|
||||||
|
|| !is_hex_digit(src[6])
|
||||||
|
|| !is_hex_digit(src[7])
|
||||||
|
|| src[8] != ':'
|
||||||
|
|| !is_hex_digit(src[9])
|
||||||
|
|| !is_hex_digit(src[10])
|
||||||
|
|| src[11] != ':'
|
||||||
|
|| !is_hex_digit(src[12])
|
||||||
|
|| !is_hex_digit(src[13])
|
||||||
|
|| src[14] != ':'
|
||||||
|
|| !is_hex_digit(src[15])
|
||||||
|
|| !is_hex_digit(src[16]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
static const char max_char_map[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
if (mac) {
|
||||||
|
mac->data[0] = max_char_map[int_from_hex_digit(src[ 0])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[ 1])];
|
||||||
|
mac->data[1] = max_char_map[int_from_hex_digit(src[ 3])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[ 4])];
|
||||||
|
mac->data[2] = max_char_map[int_from_hex_digit(src[ 6])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[ 7])];
|
||||||
|
mac->data[3] = max_char_map[int_from_hex_digit(src[ 9])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[10])];
|
||||||
|
mac->data[4] = max_char_map[int_from_hex_digit(src[12])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[13])];
|
||||||
|
mac->data[5] = max_char_map[int_from_hex_digit(src[15])] << 4
|
||||||
|
| max_char_map[int_from_hex_digit(src[16])];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mac_address_t generate_random_mac()
|
||||||
|
{
|
||||||
|
mac_address_t mac = {
|
||||||
|
.data = {
|
||||||
|
rand() & 0xff,
|
||||||
|
rand() & 0xff,
|
||||||
|
rand() & 0xff,
|
||||||
|
rand() & 0xff,
|
||||||
|
rand() & 0xff,
|
||||||
|
rand() & 0xff,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_ip(const char *ip, ip_address_t *parsed_ip)
|
||||||
|
{
|
||||||
|
return inet_pton(AF_INET, ip, parsed_ip) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
|
||||||
microtcp_callbacks_t callbacks)
|
microtcp_callbacks_t callbacks)
|
||||||
{
|
{
|
||||||
|
mac_address_t parsed_mac;
|
||||||
|
if (mac == NULL) {
|
||||||
|
// Generate a random MAC
|
||||||
|
parsed_mac = generate_random_mac();
|
||||||
|
} else {
|
||||||
|
if (!parse_mac(mac, mac ? strlen(mac) : 0, &parsed_mac))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip_address_t parsed_ip;
|
||||||
|
if (!parse_ip(ip, &parsed_ip))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
microtcp_t *mtcp = malloc(sizeof(microtcp_t));
|
microtcp_t *mtcp = malloc(sizeof(microtcp_t));
|
||||||
if (mtcp == NULL)
|
if (mtcp == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
mac_address_t mac2;
|
mtcp->ip = parsed_ip;
|
||||||
static_assert(sizeof(mac2) == sizeof(mac));
|
mtcp->mac = parsed_mac;
|
||||||
memcpy(&mac2, &mac, sizeof(mac));
|
|
||||||
|
|
||||||
mtcp->ip = ip;
|
|
||||||
mtcp->mac = mac2;
|
|
||||||
mtcp->callbacks = callbacks;
|
mtcp->callbacks = callbacks;
|
||||||
mtcp->last_update_time = time(NULL);
|
mtcp->last_update_time = time(NULL);
|
||||||
|
|
||||||
@@ -443,15 +524,15 @@ microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac
|
|||||||
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].prev = NULL;
|
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].prev = NULL;
|
||||||
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = NULL;
|
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = NULL;
|
||||||
|
|
||||||
ip_init(&mtcp->ip_state, ip, mtcp, send_ip_packet);
|
ip_init(&mtcp->ip_state, parsed_ip, mtcp, send_ip_packet);
|
||||||
if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) {
|
if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) {
|
||||||
free(mtcp);
|
free(mtcp);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
arp_init(&mtcp->arp_state, ip, mac2, mtcp, send_arp_packet);
|
arp_init(&mtcp->arp_state, parsed_ip, parsed_mac, mtcp, send_arp_packet);
|
||||||
|
|
||||||
tcp_init(&mtcp->tcp_state, (tcp_callbacks_t) {
|
tcp_init(&mtcp->tcp_state, parsed_ip, (tcp_callbacks_t) {
|
||||||
.data = mtcp,
|
.data = mtcp,
|
||||||
.send = send_tcp_segment,
|
.send = send_tcp_segment,
|
||||||
});
|
});
|
||||||
@@ -496,6 +577,51 @@ microtcp_t *microtcp_create_using_callbacks(microtcp_ip_t ip, microtcp_mac_t mac
|
|||||||
return mtcp;
|
return mtcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MICROTCP_USING_TAP
|
||||||
|
|
||||||
|
bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac,
|
||||||
|
microtcp_callbacks_t *callbacks)
|
||||||
|
{
|
||||||
|
assert(ip);
|
||||||
|
|
||||||
|
struct device *dev = tuntap_init();
|
||||||
|
if (!dev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int netmask = 24; // TODO: Make this configurable
|
||||||
|
|
||||||
|
if (tuntap_start(dev, TUNTAP_MODE_ETHERNET, TUNTAP_ID_ANY) ||
|
||||||
|
tuntap_set_ip(dev, ip, netmask) ||
|
||||||
|
tuntap_set_hwaddr(dev, mac ? mac : "random") ||
|
||||||
|
tuntap_up(dev)) {
|
||||||
|
tuntap_release(dev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*callbacks = (microtcp_callbacks_t) {
|
||||||
|
.data = dev,
|
||||||
|
.free = tuntap_release,
|
||||||
|
.recv = tuntap_read,
|
||||||
|
.send = tuntap_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip,
|
||||||
|
const char *tap_mac, const char *stack_mac)
|
||||||
|
{
|
||||||
|
microtcp_callbacks_t callbacks;
|
||||||
|
if (!microtcp_callbacks_create_for_tap(tap_ip, tap_mac, &callbacks))
|
||||||
|
return NULL;
|
||||||
|
microtcp_t *mtcp = microtcp_create_using_callbacks(stack_ip, stack_mac, callbacks);
|
||||||
|
if (!mtcp)
|
||||||
|
callbacks.free(callbacks.data);
|
||||||
|
return mtcp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void microtcp_destroy(microtcp_t *mtcp)
|
void microtcp_destroy(microtcp_t *mtcp)
|
||||||
{
|
{
|
||||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
@@ -522,12 +648,6 @@ pop_socket_struct_from_free_list(microtcp_t *mtcp)
|
|||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
have_unused_socket_structs(microtcp_t *mtcp)
|
|
||||||
{
|
|
||||||
return mtcp->free_socket_list != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
push_unlinked_socket_into_used_list(microtcp_socket_t *socket)
|
push_unlinked_socket_into_used_list(microtcp_socket_t *socket)
|
||||||
{
|
{
|
||||||
@@ -647,6 +767,26 @@ void microtcp_close(microtcp_socket_t *socket)
|
|||||||
UNLOCK_WHEN_THREADED(mtcp);
|
UNLOCK_WHEN_THREADED(mtcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ready_to_recv(void *data)
|
||||||
|
{
|
||||||
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
microtcp_socket_t *socket = data;
|
||||||
|
pthread_cond_signal(&socket->something_to_recv);
|
||||||
|
#else
|
||||||
|
(void) data;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ready_to_send(void *data)
|
||||||
|
{
|
||||||
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
microtcp_socket_t *socket = data;
|
||||||
|
pthread_cond_signal(&socket->something_to_send);
|
||||||
|
#else
|
||||||
|
(void) data;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
||||||
bool no_block,
|
bool no_block,
|
||||||
microtcp_errcode_t *errcode)
|
microtcp_errcode_t *errcode)
|
||||||
@@ -662,40 +802,26 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
|||||||
goto unlock_and_exit; // Can't accept from a non-listening socket
|
goto unlock_and_exit; // Can't accept from a non-listening socket
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!have_unused_socket_structs(mtcp)) {
|
socket2 = pop_socket_struct_from_free_list(mtcp);
|
||||||
|
if (!socket2) {
|
||||||
errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT;
|
errcode2 = MICROTCP_ERRCODE_SOCKETLIMIT;
|
||||||
goto unlock_and_exit; // Socket limit reached
|
goto unlock_and_exit; // Socket limit reached
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_connection_t *connection = tcp_listener_accept(socket->listener);
|
tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send);
|
||||||
|
|
||||||
if (!connection) {
|
|
||||||
|
|
||||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
while (!connection && !no_block) {
|
||||||
if (no_block) {
|
pthread_cond_wait(&socket->something_to_accept, &mtcp->lock);
|
||||||
errcode2 = MICROTCP_ERRCODE_NOTHINGTOACCEPT;
|
connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send);
|
||||||
goto unlock_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
pthread_cond_wait(&socket->something_to_accept, &mtcp->lock);
|
|
||||||
connection = tcp_listener_accept(socket->listener);
|
|
||||||
} while (!connection);
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
if (!no_block)
|
|
||||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
|
||||||
else
|
|
||||||
errcode2 = MICROTCP_ERRCODE_NOTHINGTOACCEPT;
|
|
||||||
goto unlock_and_exit;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
socket2 = pop_socket_struct_from_free_list(mtcp);
|
if (!connection && !no_block) {
|
||||||
assert(socket2); // Because we checked at the start
|
push_unlinked_socket_into_free_list(mtcp, socket2);
|
||||||
|
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
socket2->mtcp = mtcp;
|
socket2->mtcp = mtcp;
|
||||||
socket2->prev = NULL;
|
socket2->prev = NULL;
|
||||||
@@ -703,6 +829,21 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
|||||||
socket2->type = SOCKET_CONNECTION;
|
socket2->type = SOCKET_CONNECTION;
|
||||||
socket2->connection = connection;
|
socket2->connection = connection;
|
||||||
|
|
||||||
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
if (pthread_cond_init(&socket2->something_to_recv, NULL)) {
|
||||||
|
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||||
|
push_unlinked_socket_into_free_list(mtcp, socket2);
|
||||||
|
tcp_connection_destroy(connection);
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
if (pthread_cond_init(&socket2->something_to_send, NULL)) {
|
||||||
|
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||||
|
push_unlinked_socket_into_free_list(mtcp, socket2);
|
||||||
|
tcp_connection_destroy(connection);
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
push_unlinked_socket_into_used_list(socket2);
|
push_unlinked_socket_into_used_list(socket2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,6 +858,7 @@ unlock_and_exit:
|
|||||||
|
|
||||||
size_t microtcp_recv(microtcp_socket_t *socket,
|
size_t microtcp_recv(microtcp_socket_t *socket,
|
||||||
void *dst, size_t len,
|
void *dst, size_t len,
|
||||||
|
bool no_block,
|
||||||
microtcp_errcode_t *errcode)
|
microtcp_errcode_t *errcode)
|
||||||
{
|
{
|
||||||
if (!socket || socket->type != SOCKET_CONNECTION) {
|
if (!socket || socket->type != SOCKET_CONNECTION) {
|
||||||
@@ -725,19 +867,37 @@ size_t microtcp_recv(microtcp_socket_t *socket,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t num;
|
||||||
microtcp_t *mtcp = socket->mtcp;
|
microtcp_t *mtcp = socket->mtcp;
|
||||||
|
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
|
||||||
|
|
||||||
LOCK_WHEN_THREADED(mtcp);
|
LOCK_WHEN_THREADED(mtcp);
|
||||||
size_t num = tcp_connection_recv(socket->connection, dst, len);
|
{
|
||||||
|
num = tcp_connection_recv(socket->connection, dst, len);
|
||||||
|
|
||||||
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
while (num == 0 && !no_block) {
|
||||||
|
pthread_cond_wait(&socket->something_to_recv, &mtcp->lock);
|
||||||
|
num = tcp_connection_recv(socket->connection, dst, len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (num == 0 && !no_block) {
|
||||||
|
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
unlock_and_exit:
|
||||||
UNLOCK_WHEN_THREADED(mtcp);
|
UNLOCK_WHEN_THREADED(mtcp);
|
||||||
|
|
||||||
if (errcode)
|
if (errcode)
|
||||||
*errcode = MICROTCP_ERRCODE_NONE;
|
*errcode = errcode2;
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t microtcp_send(microtcp_socket_t *socket,
|
size_t microtcp_send(microtcp_socket_t *socket,
|
||||||
const void *src, size_t len,
|
const void *src, size_t len,
|
||||||
|
bool no_block,
|
||||||
microtcp_errcode_t *errcode)
|
microtcp_errcode_t *errcode)
|
||||||
{
|
{
|
||||||
if (!socket || socket->type != SOCKET_CONNECTION) {
|
if (!socket || socket->type != SOCKET_CONNECTION) {
|
||||||
@@ -746,13 +906,33 @@ size_t microtcp_send(microtcp_socket_t *socket,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t num;
|
||||||
microtcp_t *mtcp = socket->mtcp;
|
microtcp_t *mtcp = socket->mtcp;
|
||||||
|
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
|
||||||
|
|
||||||
LOCK_WHEN_THREADED(mtcp);
|
LOCK_WHEN_THREADED(mtcp);
|
||||||
size_t num = tcp_connection_send(socket->connection, src, len);
|
{
|
||||||
|
num = tcp_connection_send(socket->connection, src, len);
|
||||||
|
|
||||||
|
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||||
|
while (num == 0 && !no_block) {
|
||||||
|
pthread_cond_wait(&socket->something_to_send, &mtcp->lock);
|
||||||
|
num = tcp_connection_send(socket->connection, src, len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (num == 0 && !no_block) {
|
||||||
|
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_exit:
|
||||||
UNLOCK_WHEN_THREADED(mtcp);
|
UNLOCK_WHEN_THREADED(mtcp);
|
||||||
|
|
||||||
if (errcode)
|
if (errcode)
|
||||||
*errcode = MICROTCP_ERRCODE_NONE;
|
*errcode = errcode2;
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
#ifdef MICROTCP_LINUX
|
#ifdef MICROTCP_LINUX
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
@@ -85,7 +86,7 @@ static void free_callback(void *data)
|
|||||||
close(tap_fd);
|
close(tap_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
static bool get_ip_address(const char *dev, microtcp_ip_t *ip)
|
static bool get_ip_address(const char *dev, microtcp_ip_t *ip)
|
||||||
{
|
{
|
||||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
@@ -104,7 +105,7 @@ static bool get_ip_address(const char *dev, microtcp_ip_t *ip)
|
|||||||
close(fd);
|
close(fd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
static bool get_mac_address(const char *dev, microtcp_mac_t *mac)
|
static bool get_mac_address(const char *dev, microtcp_mac_t *mac)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
|
|
||||||
@@ -15,8 +16,9 @@ static int tcp_send(tcp_state_t *tcp_state, ip_address_t ip,
|
|||||||
return tcp_state->callbacks.send(tcp_state->callbacks.data, ip, src, len);
|
return tcp_state->callbacks.send(tcp_state->callbacks.data, ip, src, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_init(tcp_state_t *tcp_state, tcp_callbacks_t callbacks)
|
void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks)
|
||||||
{
|
{
|
||||||
|
tcp_state->ip = ip;
|
||||||
tcp_state->callbacks = callbacks;
|
tcp_state->callbacks = callbacks;
|
||||||
|
|
||||||
for (size_t i = 0; i < TCP_MAX_SOCKETS-1; i++)
|
for (size_t i = 0; i < TCP_MAX_SOCKETS-1; i++)
|
||||||
@@ -53,30 +55,47 @@ connection_create_waiting_for_ack(tcp_listener_t *listener,
|
|||||||
tcp_state_t *state = listener->state;
|
tcp_state_t *state = listener->state;
|
||||||
|
|
||||||
// Pop a connection structure from the free list
|
// Pop a connection structure from the free list
|
||||||
if (state->free_connection_list == NULL)
|
tcp_connection_t *connection;
|
||||||
// ERROR: Reached connection limit
|
{
|
||||||
return NULL;
|
if (state->free_connection_list == NULL)
|
||||||
tcp_connection_t *connection = state->free_connection_list;
|
// ERROR: Reached connection limit
|
||||||
state->free_connection_list = connection->next;
|
return NULL;
|
||||||
|
connection = state->free_connection_list;
|
||||||
|
state->free_connection_list = connection->next;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize connection structure
|
// Initialize connection structure
|
||||||
connection->listener = listener;
|
{
|
||||||
connection->seq_no = seq_no;
|
connection->listener = listener;
|
||||||
connection->ack_no = ack_no;
|
|
||||||
connection->peer_port = peer_port;
|
|
||||||
connection->peer_ip = peer_ip;
|
|
||||||
connection->in_used = 0;
|
|
||||||
connection->out_used = 0;
|
|
||||||
connection->prev = NULL;
|
|
||||||
connection->next = NULL;
|
|
||||||
|
|
||||||
|
connection->callback_data = NULL;
|
||||||
|
connection->callback_ready_to_recv = NULL;
|
||||||
|
connection->callback_ready_to_send = NULL;
|
||||||
|
|
||||||
|
connection->peer_port = peer_port;
|
||||||
|
connection->peer_ip = peer_ip;
|
||||||
|
|
||||||
|
connection->rcv_unread = ack_no;
|
||||||
|
connection->rcv_nxt = ack_no;
|
||||||
|
connection->rcv_wnd = TCP_INPUT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
connection->snd_una = seq_no;
|
||||||
|
connection->snd_wnd = 0;
|
||||||
|
connection->snd_nxt = 0;
|
||||||
|
|
||||||
|
connection->prev = NULL;
|
||||||
|
connection->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Append the connection to the list of connections
|
// Append the connection to the list of connections
|
||||||
// waiting for the ACK message
|
// waiting for the ACK message
|
||||||
if (listener->connections_waiting_for_ack)
|
{
|
||||||
listener->connections_waiting_for_ack->prev = connection;
|
if (listener->connections_waiting_for_ack)
|
||||||
connection->next = listener->connections_waiting_for_ack;
|
listener->connections_waiting_for_ack->prev = connection;
|
||||||
listener->connections_waiting_for_ack = connection;
|
connection->prev = NULL;
|
||||||
|
connection->next = listener->connections_waiting_for_ack;
|
||||||
|
listener->connections_waiting_for_ack = connection;
|
||||||
|
}
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@@ -84,11 +103,8 @@ connection_create_waiting_for_ack(tcp_listener_t *listener,
|
|||||||
static tcp_listener_t*
|
static tcp_listener_t*
|
||||||
find_listener_with_port(tcp_state_t *state, uint16_t port)
|
find_listener_with_port(tcp_state_t *state, uint16_t port)
|
||||||
{
|
{
|
||||||
TCP_DEBUG_LOG("Looking for listener with port %d", port);
|
|
||||||
|
|
||||||
tcp_listener_t *cursor = state->used_listener_list;
|
tcp_listener_t *cursor = state->used_listener_list;
|
||||||
while (cursor) {
|
while (cursor) {
|
||||||
TCP_DEBUG_LOG("port=%d, seeking=%d", cursor->port, port);
|
|
||||||
if (cursor->port == port)
|
if (cursor->port == port)
|
||||||
return cursor;
|
return cursor;
|
||||||
cursor = cursor->next;
|
cursor = cursor->next;
|
||||||
@@ -121,31 +137,182 @@ static connection_state_t find_connection_associated_to(tcp_listener_t *listener
|
|||||||
{
|
{
|
||||||
tcp_connection_t *connection2 = find_connection(listener->connections, peer_ip, peer_port);
|
tcp_connection_t *connection2 = find_connection(listener->connections, peer_ip, peer_port);
|
||||||
if (connection2) {
|
if (connection2) {
|
||||||
*connection = connection2;
|
if (connection)
|
||||||
|
*connection = connection2;
|
||||||
return SOCKET_IDLE;
|
return SOCKET_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection2 = find_connection(listener->connections_waiting_for_accept_head, peer_ip, peer_port);
|
connection2 = find_connection(listener->connections_waiting_for_accept_head, peer_ip, peer_port);
|
||||||
if (connection2) {
|
if (connection2) {
|
||||||
*connection = connection2;
|
if (connection)
|
||||||
|
*connection = connection2;
|
||||||
return SOCKET_IDLE;
|
return SOCKET_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection2 = find_connection(listener->connections_waiting_for_ack, peer_ip, peer_port);
|
connection2 = find_connection(listener->connections_waiting_for_ack, peer_ip, peer_port);
|
||||||
if (connection2) {
|
if (connection2) {
|
||||||
*connection = connection2;
|
if (connection)
|
||||||
|
*connection = connection2;
|
||||||
return SOCKET_WAIT;
|
return SOCKET_WAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
*connection = NULL;
|
if (connection)
|
||||||
|
*connection = NULL;
|
||||||
return SOCKET_NONE;
|
return SOCKET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t choose_ack()
|
static uint32_t choose_sequence_no()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ip_address_t src_addr;
|
||||||
|
ip_address_t dst_addr;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t protocol;
|
||||||
|
uint16_t tcp_length;
|
||||||
|
} tcp_pseudoheader_t;
|
||||||
|
|
||||||
|
static uint16_t
|
||||||
|
calculate_checksum_tcp(const void *a, const void *b,
|
||||||
|
size_t a_len, size_t b_len)
|
||||||
|
{
|
||||||
|
assert((a_len & 1) == 0
|
||||||
|
&& (b_len & 1) == 0);
|
||||||
|
|
||||||
|
const uint16_t *a2 = a;
|
||||||
|
const uint16_t *b2 = b;
|
||||||
|
|
||||||
|
uint32_t sum = 0xffff;
|
||||||
|
for (size_t i = 0; i < a_len/2; i++) {
|
||||||
|
sum += ntohs(a2[i]);
|
||||||
|
if (sum > 0xffff)
|
||||||
|
sum -= 0xffff;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < b_len/2; i++) {
|
||||||
|
sum += ntohs(b2[i]);
|
||||||
|
if (sum > 0xffff)
|
||||||
|
sum -= 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return htons(~sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
move_connection_from_wait_for_ack_to_wait_for_accept(tcp_connection_t *connection)
|
||||||
|
{
|
||||||
|
tcp_listener_t *listener = connection->listener;
|
||||||
|
|
||||||
|
// Unlink it from the current list
|
||||||
|
if (connection->prev)
|
||||||
|
connection->prev->next = connection->next;
|
||||||
|
else
|
||||||
|
listener->connections_waiting_for_ack = connection->next;
|
||||||
|
|
||||||
|
if (connection->next)
|
||||||
|
connection->next->prev = connection->prev;
|
||||||
|
|
||||||
|
|
||||||
|
// Push it to the new one
|
||||||
|
connection->prev = NULL;
|
||||||
|
connection->next = listener->connections_waiting_for_accept_head;
|
||||||
|
|
||||||
|
if (listener->connections_waiting_for_accept_head)
|
||||||
|
// Accept queue isn't empty
|
||||||
|
listener->connections_waiting_for_accept_head->prev = connection;
|
||||||
|
else
|
||||||
|
// Accept queue is empty
|
||||||
|
listener->connections_waiting_for_accept_tail = connection;
|
||||||
|
listener->connections_waiting_for_accept_head = connection;
|
||||||
|
|
||||||
|
|
||||||
|
if (listener->callback_ready_to_accept)
|
||||||
|
listener->callback_ready_to_accept(listener->callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void emit_segment(tcp_connection_t *connection, bool ack, bool syn, size_t payload)
|
||||||
|
{
|
||||||
|
tcp_listener_t *listener = connection->listener;
|
||||||
|
tcp_state_t *state = listener->state;
|
||||||
|
|
||||||
|
size_t payload_being_sent = MIN(payload, connection->snd_wnd);
|
||||||
|
size_t total_segment_size = sizeof(tcp_segment_t) + payload_being_sent;
|
||||||
|
|
||||||
|
uint8_t flags = 0;
|
||||||
|
uint32_t ack_no = 0;
|
||||||
|
if (ack) {
|
||||||
|
flags |= TCP_FLAG_ACK;
|
||||||
|
ack_no = connection->rcv_nxt;
|
||||||
|
}
|
||||||
|
if (syn)
|
||||||
|
flags |= TCP_FLAG_SYN;
|
||||||
|
|
||||||
|
uint32_t seq_no = connection->snd_una;
|
||||||
|
//if (payload_being_sent > 0)
|
||||||
|
// seq_no++;
|
||||||
|
|
||||||
|
connection->out_header = (tcp_segment_t) {
|
||||||
|
.src_port = htons(listener->port),
|
||||||
|
.dst_port = htons(connection->peer_port),
|
||||||
|
.flags = flags,
|
||||||
|
.seq_no = htonl(seq_no),
|
||||||
|
.ack_no = htonl(ack_no),
|
||||||
|
.offset = 5, // No options
|
||||||
|
.unused = 0,
|
||||||
|
.window = htons(connection->rcv_wnd),
|
||||||
|
.checksum = 0, // Will be calculated later
|
||||||
|
.urgent_pointer = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
tcp_pseudoheader_t pseudo_header = {
|
||||||
|
.src_addr = state->ip,
|
||||||
|
.dst_addr = connection->peer_ip,
|
||||||
|
.reserved = 0,
|
||||||
|
.protocol = 6, // TCP
|
||||||
|
.tcp_length = htons(total_segment_size),
|
||||||
|
};
|
||||||
|
|
||||||
|
connection->out_header.checksum = calculate_checksum_tcp(&pseudo_header, &connection->out_header, sizeof(pseudo_header), total_segment_size);
|
||||||
|
|
||||||
|
int result = tcp_send(state, connection->peer_ip, &connection->out_header, total_segment_size);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
// It wasn't possible to send out bytes. We'll try again later!
|
||||||
|
} else {
|
||||||
|
size_t actually_sent_bytes = (size_t) result;
|
||||||
|
|
||||||
|
if (actually_sent_bytes < sizeof(tcp_segment_t)) {
|
||||||
|
// Not even the TCP header was sent. I hope this
|
||||||
|
// doesn't ever happen!
|
||||||
|
assert(0);
|
||||||
|
} else {
|
||||||
|
size_t actually_sent_payload_bytes = actually_sent_bytes - sizeof(tcp_segment_t);
|
||||||
|
connection->snd_nxt = MAX(connection->snd_nxt, connection->snd_una + actually_sent_payload_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_received_data(tcp_connection_t *connection,
|
||||||
|
const void *data, size_t size)
|
||||||
|
{
|
||||||
|
size_t considered = MIN(size, connection->rcv_wnd);
|
||||||
|
|
||||||
|
if (considered > 0) {
|
||||||
|
size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd;
|
||||||
|
memcpy(connection->in_buffer + input_buffer_usage, data, considered);
|
||||||
|
connection->rcv_wnd -= considered;
|
||||||
|
connection->rcv_nxt += considered;
|
||||||
|
|
||||||
|
emit_segment(connection, true, false, SIZE_MAX);
|
||||||
|
|
||||||
|
// Data is ready to be received by the parent application
|
||||||
|
if (connection->callback_ready_to_recv)
|
||||||
|
connection->callback_ready_to_recv(connection->callback_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
|
void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
|
||||||
tcp_segment_t *segment, size_t len)
|
tcp_segment_t *segment, size_t len)
|
||||||
{
|
{
|
||||||
@@ -172,118 +339,95 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender,
|
|||||||
tcp_connection_t *connection;
|
tcp_connection_t *connection;
|
||||||
connection_state_t connection_state = find_connection_associated_to(listener, sender, reordered_src_port, &connection);
|
connection_state_t connection_state = find_connection_associated_to(listener, sender, reordered_src_port, &connection);
|
||||||
|
|
||||||
if ((segment->flags & TCP_FLAG_SYN) && !(segment->flags & TCP_FLAG_ACK)) {
|
if (segment->flags & TCP_FLAG_SYN) {
|
||||||
|
|
||||||
/* Connection request */
|
if (segment->flags & TCP_FLAG_ACK) {
|
||||||
|
|
||||||
if (connection_state != SOCKET_NONE) {
|
// Drop the packet. We only do servers for now!
|
||||||
// Peer wants to connect, but a connection was already created..
|
#warning "TODO: Handle TCP second message of three way handshake"
|
||||||
// What to do?
|
|
||||||
#warning "TODO: Handle case where an existing connection recieved the SYN message"
|
} else {
|
||||||
return;
|
|
||||||
|
/* Connection request */
|
||||||
|
|
||||||
|
if (connection_state != SOCKET_NONE) {
|
||||||
|
// Peer wants to connect, but a connection was already created..
|
||||||
|
// What to do?
|
||||||
|
#warning "TODO: Handle case where an existing connection recieved the SYN message"
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary
|
||||||
|
uint32_t seq_no = choose_sequence_no();
|
||||||
|
uint32_t ack_no = ntohl(segment->seq_no)+1;
|
||||||
|
|
||||||
|
tcp_connection_t *connection = connection_create_waiting_for_ack(listener, seq_no, ack_no, sender, reordered_src_port);
|
||||||
|
if (connection == NULL) {
|
||||||
|
// ERROR: Socket limit reached. Drop the connection silently
|
||||||
|
#warning "TODO: Handle connection limit reached (RST?)"
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_segment(connection, true, true, 0);
|
||||||
|
connection->snd_una++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary
|
|
||||||
uint32_t seq_no = ntohs(segment->seq_no);
|
|
||||||
uint32_t ack_no = choose_ack();
|
|
||||||
|
|
||||||
tcp_connection_t *connection = connection_create_waiting_for_ack(listener, seq_no, ack_no, sender, reordered_dst_port);
|
|
||||||
if (connection == NULL) {
|
|
||||||
// ERROR: Socket limit reached. Drop the connection silently
|
|
||||||
#warning "TODO: Handle connection limit reached (RST?)"
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp_segment_t segment2 = {
|
|
||||||
.src_port = segment->dst_port,
|
|
||||||
.dst_port = segment->src_port,
|
|
||||||
.flags = TCP_FLAG_SYN | TCP_FLAG_ACK, // No need for fixing endianess, it's just one byte!
|
|
||||||
.seq_no = htons(ack_no),
|
|
||||||
.ack_no = htons(seq_no),
|
|
||||||
.offset = 5,
|
|
||||||
.unused = 0,
|
|
||||||
.window = htons(TCP_INPUT_BUFFER_SIZE - connection->in_used),
|
|
||||||
.checksum = 0,
|
|
||||||
.urgent_pointer = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
#warning "TODO: Calculare checksum"
|
|
||||||
|
|
||||||
tcp_send(state, sender, &segment2, sizeof(segment2));
|
|
||||||
|
|
||||||
#warning "TODO: Handle TCP connection creation"
|
|
||||||
|
|
||||||
} else if ((segment->flags & TCP_FLAG_SYN) && (segment->flags & TCP_FLAG_ACK)) {
|
|
||||||
|
|
||||||
// Drop the packet. We only do servers for now!
|
|
||||||
#warning "TODO: Handle TCP second message of three way handshake"
|
|
||||||
|
|
||||||
} else if (!(segment->flags & TCP_FLAG_SYN) && (segment->flags & TCP_FLAG_ACK)) {
|
|
||||||
|
|
||||||
if (connection_state != SOCKET_WAIT) {
|
|
||||||
// Either there is no connection or the connection wasn't
|
|
||||||
// waiting for an ACK segment. What to do?
|
|
||||||
#warning "TODO: Handle case where an existing connection recieved the SYN message"
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move connection from the waiting-for-ack list
|
|
||||||
// to the waiting-for-accept queue.
|
|
||||||
|
|
||||||
// Unlink it from the current list
|
|
||||||
{
|
|
||||||
if (connection->prev)
|
|
||||||
connection->prev->next = connection->next;
|
|
||||||
else
|
|
||||||
listener->connections_waiting_for_ack = connection->next;
|
|
||||||
|
|
||||||
if (connection->next)
|
|
||||||
connection->next->prev = connection->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push it to the new one
|
|
||||||
{
|
|
||||||
connection->prev = NULL;
|
|
||||||
connection->next = listener->connections_waiting_for_accept_head;
|
|
||||||
|
|
||||||
if (listener->connections_waiting_for_accept_head)
|
|
||||||
// Accept queue isn't empty
|
|
||||||
listener->connections_waiting_for_accept_head->prev = connection;
|
|
||||||
else
|
|
||||||
// Accept queue is empty
|
|
||||||
listener->connections_waiting_for_accept_tail = connection;
|
|
||||||
listener->connections_waiting_for_accept_head = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener->callback)
|
|
||||||
listener->callback(listener->data);
|
|
||||||
|
|
||||||
// TODO: What about the payload?
|
|
||||||
#warning "TODO: Handle payload of TCP ACK message"
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (connection_state != SOCKET_IDLE) {
|
if (segment->flags & TCP_FLAG_ACK) {
|
||||||
// Either there is no connection associated to this peer
|
|
||||||
// at this port, or the connection is waiting for an ACK.
|
if (connection_state == SOCKET_WAIT)
|
||||||
// What to do?
|
move_connection_from_wait_for_ack_to_wait_for_accept(connection);
|
||||||
#warning "TODO: Handle case where unexpected TCP data message is received"
|
else if (connection_state == SOCKET_IDLE) {
|
||||||
return;
|
|
||||||
|
} else {
|
||||||
|
#warning "TODO: Handle case where no connection exists"
|
||||||
|
assert(connection_state == SOCKET_NONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ack_no = ntohl(segment->ack_no);
|
||||||
|
|
||||||
|
if (ack_no <= connection->snd_una)
|
||||||
|
TCP_DEBUG_LOG("Received segment acknowledged again %d", ack_no);
|
||||||
|
else {
|
||||||
|
if (ack_no > connection->snd_nxt) {
|
||||||
|
TCP_DEBUG_LOG("Received segment acknowledged unsent data with sequence number %d, but %d still wasn't sent", ack_no, connection->snd_nxt);
|
||||||
|
return; // Acknowledged unsent data
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t newly_acked_bytes = ack_no - connection->snd_una;
|
||||||
|
memmove(connection->out_buffer, connection->out_buffer + newly_acked_bytes, connection->snd_wnd - newly_acked_bytes);
|
||||||
|
connection->snd_wnd -= newly_acked_bytes;
|
||||||
|
connection->snd_una = ack_no;
|
||||||
|
|
||||||
|
// Now there's space available in the output buffer
|
||||||
|
if (connection->callback_ready_to_send)
|
||||||
|
connection->callback_ready_to_send(connection->callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (connection_state != SOCKET_IDLE) {
|
||||||
|
// Either there is no connection associated to this peer
|
||||||
|
// at this port, or the connection is waiting for an ACK.
|
||||||
|
// What to do?
|
||||||
|
#warning "TODO: Handle case where unexpected TCP data message is received"
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
handle_received_data(connection, segment->payload,
|
||||||
|
len - sizeof(tcp_segment_t));
|
||||||
|
|
||||||
char *payload = segment->payload;
|
if (segment->flags & TCP_FLAG_FIN) {
|
||||||
size_t payload_arrived = len - sizeof(tcp_segment_t);
|
#warning "TODO: Handle FIN segment"
|
||||||
size_t payload_capacity = TCP_INPUT_BUFFER_SIZE - connection->in_used;
|
}
|
||||||
size_t payload_considered = MIN(payload_arrived, payload_capacity);
|
|
||||||
|
|
||||||
memcpy(connection->in_buffer + connection->in_used, payload, payload_considered);
|
|
||||||
connection->in_used += payload_considered;
|
|
||||||
connection->ack_no += payload_considered; // Is this right?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_listener_t*
|
tcp_listener_t*
|
||||||
tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*))
|
tcp_listener_create(tcp_state_t *state, uint16_t port, void *callback_data,
|
||||||
|
void (*callback_ready_to_accept)(void*))
|
||||||
{
|
{
|
||||||
if (find_listener_with_port(state, port)) {
|
if (find_listener_with_port(state, port)) {
|
||||||
// ERROR: A connection is already listening on this port
|
// ERROR: A connection is already listening on this port
|
||||||
@@ -307,8 +451,8 @@ tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callba
|
|||||||
listener->connections_waiting_for_ack = NULL;
|
listener->connections_waiting_for_ack = NULL;
|
||||||
listener->connections_waiting_for_accept_head = NULL;
|
listener->connections_waiting_for_accept_head = NULL;
|
||||||
listener->connections_waiting_for_accept_tail = NULL;
|
listener->connections_waiting_for_accept_tail = NULL;
|
||||||
listener->data = data;
|
listener->callback_data = callback_data;
|
||||||
listener->callback = callback;
|
listener->callback_ready_to_accept = callback_ready_to_accept;
|
||||||
|
|
||||||
// Push listener connection structure to the used list
|
// Push listener connection structure to the used list
|
||||||
listener->prev = NULL;
|
listener->prev = NULL;
|
||||||
@@ -346,7 +490,9 @@ void tcp_listener_destroy(tcp_listener_t *listener)
|
|||||||
state->free_listener_list = listener;
|
state->free_listener_list = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener)
|
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data,
|
||||||
|
void (*callback_ready_to_recv)(void*),
|
||||||
|
void (*callback_ready_to_send)(void*))
|
||||||
{
|
{
|
||||||
(void) listener;
|
(void) listener;
|
||||||
|
|
||||||
@@ -374,6 +520,11 @@ tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener)
|
|||||||
listener->connections->prev = connection;
|
listener->connections->prev = connection;
|
||||||
listener->connections = connection;
|
listener->connections = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection->callback_data = callback_data;
|
||||||
|
connection->callback_ready_to_recv = callback_ready_to_recv;
|
||||||
|
connection->callback_ready_to_send = callback_ready_to_send;
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,13 +550,20 @@ void tcp_connection_destroy(tcp_connection_t *connection)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t tcp_connection_recv(tcp_connection_t *connection,
|
size_t tcp_connection_recv(tcp_connection_t *connection,
|
||||||
void *dst, size_t len)
|
void *dst, size_t len)
|
||||||
{
|
{
|
||||||
size_t num = MIN(len, connection->in_used);
|
size_t unread = connection->rcv_nxt - connection->rcv_unread;
|
||||||
|
size_t num = MIN(len, unread);
|
||||||
memcpy(dst, connection->in_buffer, num);
|
memcpy(dst, connection->in_buffer, num);
|
||||||
|
|
||||||
memmove(connection->in_buffer, connection->in_buffer + num, connection->in_used - num);
|
|
||||||
connection->in_used -= num;
|
size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd;
|
||||||
|
|
||||||
|
memmove(connection->in_buffer, connection->in_buffer + num, input_buffer_usage - num);
|
||||||
|
connection->rcv_unread += num;
|
||||||
|
connection->rcv_wnd += num;
|
||||||
|
|
||||||
|
assert(connection->rcv_wnd <= TCP_INPUT_BUFFER_SIZE);
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
@@ -414,55 +572,18 @@ static size_t
|
|||||||
append_to_output_buffer(tcp_connection_t *connection,
|
append_to_output_buffer(tcp_connection_t *connection,
|
||||||
const void *src, size_t len)
|
const void *src, size_t len)
|
||||||
{
|
{
|
||||||
size_t num = MIN(len, TCP_OUTPUT_BUFFER_SIZE - connection->out_used);
|
size_t capacity = TCP_OUTPUT_BUFFER_SIZE - connection->snd_wnd;
|
||||||
|
size_t num = MIN(len, capacity);
|
||||||
|
|
||||||
memcpy(connection->out_buffer + connection->out_used, src, num);
|
memcpy(connection->out_buffer + connection->snd_wnd, src, num);
|
||||||
connection->out_used += len;
|
connection->snd_wnd += num;
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t calculate_checksum(const void *data, size_t size)
|
|
||||||
{
|
|
||||||
(void) data,
|
|
||||||
(void) size;
|
|
||||||
|
|
||||||
#warning "TODO: Calculate TCP checksum"
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
try_flushing_output_buffer(tcp_connection_t *connection)
|
|
||||||
{
|
|
||||||
tcp_state_t *tcp_state = connection->listener->state;
|
|
||||||
|
|
||||||
tcp_segment_t *segment = &connection->out_header;
|
|
||||||
segment->src_port = htons(connection->listener->port);
|
|
||||||
segment->dst_port = htons(connection->peer_port);
|
|
||||||
segment->seq_no = htons(connection->seq_no); // Should this be increased by the segment size?
|
|
||||||
segment->ack_no = htons(connection->ack_no);
|
|
||||||
segment->unused = 0;
|
|
||||||
segment->offset = 5; // No options
|
|
||||||
segment->flags = 0;
|
|
||||||
segment->window = htons(TCP_INPUT_BUFFER_SIZE - connection->in_used);
|
|
||||||
segment->checksum = 0; // Temporary value
|
|
||||||
segment->urgent_pointer = 0; // Don't support urgent data
|
|
||||||
|
|
||||||
segment->checksum = calculate_checksum(segment, sizeof(tcp_segment_t));
|
|
||||||
|
|
||||||
int sent_bytes = tcp_send(tcp_state, connection->peer_ip, segment, sizeof(tcp_segment_t) + connection->out_used);
|
|
||||||
|
|
||||||
if (sent_bytes < 0) {
|
|
||||||
// It wasn't possible to send out bytes. We'll try again later!
|
|
||||||
} else {
|
|
||||||
memmove(connection->out_buffer, connection->out_buffer + sent_bytes, connection->out_used - sent_bytes);
|
|
||||||
connection->out_used -= sent_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len)
|
size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len)
|
||||||
{
|
{
|
||||||
size_t num = append_to_output_buffer(connection, src, len);
|
size_t num = append_to_output_buffer(connection, src, len);
|
||||||
try_flushing_output_buffer(connection);
|
emit_segment(connection, false, false, SIZE_MAX);
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <endian.h>
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
|
|
||||||
#define TCP_MAX_LISTENERS 32
|
#define TCP_MAX_LISTENERS 32
|
||||||
@@ -22,11 +21,10 @@ typedef struct {
|
|||||||
uint16_t dst_port;
|
uint16_t dst_port;
|
||||||
uint32_t seq_no;
|
uint32_t seq_no;
|
||||||
uint32_t ack_no;
|
uint32_t ack_no;
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
uint8_t unused: 4;
|
uint8_t unused: 4;
|
||||||
uint8_t offset: 4;
|
uint8_t offset: 4;
|
||||||
#endif
|
#else
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
uint8_t offset: 4;
|
uint8_t offset: 4;
|
||||||
uint8_t unused: 4;
|
uint8_t unused: 4;
|
||||||
#endif
|
#endif
|
||||||
@@ -49,20 +47,52 @@ struct tcp_listener_t {
|
|||||||
tcp_connection_t *connections_waiting_for_ack;
|
tcp_connection_t *connections_waiting_for_ack;
|
||||||
tcp_connection_t *connections_waiting_for_accept_head;
|
tcp_connection_t *connections_waiting_for_accept_head;
|
||||||
tcp_connection_t *connections_waiting_for_accept_tail;
|
tcp_connection_t *connections_waiting_for_accept_tail;
|
||||||
void (*callback)(void*);
|
void (*callback_ready_to_accept)(void*);
|
||||||
void *data;
|
void *callback_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tcp_connection_t {
|
struct tcp_connection_t {
|
||||||
tcp_listener_t *listener; // Listener that accepted this connection
|
tcp_listener_t *listener; // Listener that accepted this connection
|
||||||
tcp_connection_t *next;
|
tcp_connection_t *next;
|
||||||
tcp_connection_t *prev;
|
tcp_connection_t *prev;
|
||||||
ip_address_t peer_ip;
|
|
||||||
uint16_t peer_port;
|
void *callback_data;
|
||||||
uint32_t seq_no;
|
void (*callback_ready_to_recv)(void*);
|
||||||
uint32_t ack_no;
|
void (*callback_ready_to_send)(void*);
|
||||||
size_t in_used;
|
|
||||||
size_t out_used;
|
ip_address_t peer_ip; // Network byte order
|
||||||
|
uint16_t peer_port; // CPU byte order
|
||||||
|
|
||||||
|
uint32_t rcv_unread; // It's the sequence number of the first
|
||||||
|
// byte stored in the input buffer, such
|
||||||
|
// that [rcv_next - rcv_unread] is the
|
||||||
|
// number of bytes that the parent application
|
||||||
|
// can read from the socket.
|
||||||
|
|
||||||
|
uint32_t rcv_nxt; // RCV.NXT from RFC 793
|
||||||
|
// It's the sequence number of the next
|
||||||
|
// byte waiting to be received.
|
||||||
|
|
||||||
|
uint32_t rcv_wnd; // RCV.WND from RFC 793
|
||||||
|
// It's the size of the portion of input
|
||||||
|
// buffer that's currently free.
|
||||||
|
|
||||||
|
uint32_t snd_wnd; // SND.WND from RFC 793
|
||||||
|
// It's the number of bytes stored in
|
||||||
|
// the [out_buffer] output buffer, both
|
||||||
|
// sent but not acknowledged and not sent.
|
||||||
|
|
||||||
|
uint32_t snd_nxt; // SND.NXT from RFC 793
|
||||||
|
// It's the sequence number of the first
|
||||||
|
// not yet sent byte in the output buffer.
|
||||||
|
// By subtracting [snd_una] from this value,
|
||||||
|
// you get the amount of bytes sent out but
|
||||||
|
// not yet acknowledged.
|
||||||
|
|
||||||
|
uint32_t snd_una; // SND.UNA from RFC 793
|
||||||
|
// It's the sequence number of the last
|
||||||
|
// byte sent and acknowledged by the peer.
|
||||||
|
|
||||||
tcp_segment_t out_header; // There must be no padding between
|
tcp_segment_t out_header; // There must be no padding between
|
||||||
char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; // these two
|
char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; // these two
|
||||||
char in_buffer[TCP_INPUT_BUFFER_SIZE];
|
char in_buffer[TCP_INPUT_BUFFER_SIZE];
|
||||||
@@ -75,6 +105,7 @@ typedef struct {
|
|||||||
} tcp_callbacks_t;
|
} tcp_callbacks_t;
|
||||||
|
|
||||||
struct tcp_state_t {
|
struct tcp_state_t {
|
||||||
|
ip_address_t ip;
|
||||||
tcp_callbacks_t callbacks;
|
tcp_callbacks_t callbacks;
|
||||||
tcp_connection_t *used_connection_list;
|
tcp_connection_t *used_connection_list;
|
||||||
tcp_connection_t *free_connection_list;
|
tcp_connection_t *free_connection_list;
|
||||||
@@ -84,13 +115,13 @@ struct tcp_state_t {
|
|||||||
tcp_listener_t listener_pool[TCP_MAX_LISTENERS];
|
tcp_listener_t listener_pool[TCP_MAX_LISTENERS];
|
||||||
};
|
};
|
||||||
|
|
||||||
void tcp_init(tcp_state_t *tcp_state, tcp_callbacks_t callbacks);
|
void tcp_init(tcp_state_t *tcp_state, ip_address_t ip, tcp_callbacks_t callbacks);
|
||||||
void tcp_free(tcp_state_t *tcp_state);
|
void tcp_free(tcp_state_t *tcp_state);
|
||||||
void tcp_seconds_passed(tcp_state_t *state, size_t seconds);
|
void tcp_seconds_passed(tcp_state_t *state, size_t seconds);
|
||||||
void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len);
|
void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len);
|
||||||
tcp_listener_t *tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*));
|
tcp_listener_t *tcp_listener_create(tcp_state_t *state, uint16_t port, void *data, void (*callback)(void*));
|
||||||
void tcp_listener_destroy(tcp_listener_t *listener);
|
void tcp_listener_destroy(tcp_listener_t *listener);
|
||||||
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener);
|
tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, void (*callback_ready_to_recv)(void*), void (*callback_ready_to_send)(void*));
|
||||||
void tcp_connection_destroy(tcp_connection_t *connection);
|
void tcp_connection_destroy(tcp_connection_t *connection);
|
||||||
size_t tcp_connection_recv(tcp_connection_t *connection, void *dst, size_t len);
|
size_t tcp_connection_recv(tcp_connection_t *connection, void *dst, size_t len);
|
||||||
size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len);
|
size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len);
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <microtcp.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
microtcp_errcode_t errcode;
|
||||||
|
|
||||||
|
microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL);
|
||||||
|
if (mtcp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint16_t port = 80;
|
||||||
|
microtcp_socket_t *server = microtcp_open(mtcp, port, &errcode);
|
||||||
|
if (errcode) {
|
||||||
|
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||||
|
microtcp_destroy(mtcp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(server);
|
||||||
|
|
||||||
|
fprintf(stderr, "Listening on port %d\n", port);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
fprintf(stderr, "About to accept\n");
|
||||||
|
microtcp_socket_t *client = microtcp_accept(server, false, &errcode);
|
||||||
|
if (errcode) {
|
||||||
|
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Accepted a connection\n");
|
||||||
|
|
||||||
|
char buffer[1024];
|
||||||
|
size_t num = microtcp_recv(client, buffer, sizeof(buffer), false, &errcode);
|
||||||
|
if (errcode) {
|
||||||
|
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||||
|
goto handled;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "(%ld bytes received)\n", num);
|
||||||
|
|
||||||
|
size_t sent1 = microtcp_send(client, "echo: ", 6, false, &errcode);
|
||||||
|
if (errcode) {
|
||||||
|
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||||
|
goto handled;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "(%ld bytes sent 1)\n", sent1);
|
||||||
|
|
||||||
|
size_t sent2 = microtcp_send(client, buffer, num, false, &errcode);
|
||||||
|
if (errcode) {
|
||||||
|
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||||
|
goto handled;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "(%ld bytes sent 2)\n", sent2);
|
||||||
|
|
||||||
|
handled:
|
||||||
|
microtcp_close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
microtcp_close(server);
|
||||||
|
microtcp_destroy(mtcp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
-161
@@ -1,161 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "test_arp_util.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
WHITE BOX TEST CASES
|
|
||||||
|
|
||||||
A) Peer requests host's MAC given its IP and host
|
|
||||||
replies.
|
|
||||||
|
|
||||||
B) Peer requests host's non MAC level 2 address
|
|
||||||
given its IP and host doesn't reply because it
|
|
||||||
only supports MAC.
|
|
||||||
|
|
||||||
C) Peer requests host's MAC address given its non
|
|
||||||
IP level 3 address and host doesn't reply
|
|
||||||
because it only supports IP.
|
|
||||||
|
|
||||||
D) Peer requests host's MAC given its IP, but the
|
|
||||||
MAC address length field isn't 6, therefore
|
|
||||||
host doesn't reply.
|
|
||||||
|
|
||||||
E) Peer requests host's MAC given its IP, but the
|
|
||||||
IP address length field isn't 4, therefore host
|
|
||||||
doesn't reply.
|
|
||||||
|
|
||||||
F) Peer sends a request/reply that doesn't refer
|
|
||||||
to host and is from a sender with IP never seen
|
|
||||||
by the host (no entry in the translation table).
|
|
||||||
It's expected that no entry is added to the
|
|
||||||
translation table.
|
|
||||||
|
|
||||||
G) Peer sends a request/reply that doesn't refer
|
|
||||||
to host but is from a sender with IP already
|
|
||||||
in the ARP translation table, therefore is
|
|
||||||
expected that host updates the entry.
|
|
||||||
|
|
||||||
H) Program queries the ARP module for a MAC that
|
|
||||||
isn't cached, therefore an ARP request is
|
|
||||||
expected to be generated and, when replied to,
|
|
||||||
the ARP module is expected to resolve the
|
|
||||||
program's query.
|
|
||||||
|
|
||||||
I) Program queries the ARP module for a MAC that's
|
|
||||||
cached, therefore the ARP module is expected to
|
|
||||||
resolve it without sending packets.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static arp_testcase_result_t test_000(char *msg, size_t msgmax)
|
|
||||||
{
|
|
||||||
char host_ip[4] = {0xc0, 0xa8, 0x01, 0x05};
|
|
||||||
char host_mac[6] = {0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93};
|
|
||||||
|
|
||||||
arp_testcase_t testcase;
|
|
||||||
arp_testcase_init(&testcase, host_mac, host_ip);
|
|
||||||
|
|
||||||
arp_testcase_send(&testcase, (char[]) {
|
|
||||||
0x00, 0x01, // hardware_type=ethernet
|
|
||||||
0x08, 0x00, // protocol_type=ip
|
|
||||||
0x06, // hardware_len=6
|
|
||||||
0x04, // protocol_len=4
|
|
||||||
0x00, 0x01, // operation_type=request
|
|
||||||
0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // sender MAC
|
|
||||||
0xc0, 0xa8, 0x01, 0x01, // sender IP
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target MAC (empty)
|
|
||||||
0xc0, 0xa8, 0x01, 0x05, // target IP
|
|
||||||
});
|
|
||||||
|
|
||||||
arp_testcase_recv(&testcase, (char[]) {
|
|
||||||
0x00, 0x01, // hardware_type=ethernet
|
|
||||||
0x08, 0x00, // protocol_type=ip
|
|
||||||
0x06, // hardware_len=6
|
|
||||||
0x04, // protocol_len=4
|
|
||||||
0x00, 0x02, // operation_type=reply
|
|
||||||
0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93, // sender MAC
|
|
||||||
0xc0, 0xa8, 0x01, 0x05, // sender IP
|
|
||||||
0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // target MAC
|
|
||||||
0xc0, 0xa8, 0x01, 0x01, // target IP
|
|
||||||
});
|
|
||||||
|
|
||||||
arp_testcase_result_t result =
|
|
||||||
arp_testcase_run(testcase, msg, msgmax);
|
|
||||||
|
|
||||||
arp_testcase_free(&testcase);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static arp_testcase_result_t test_001(char *msg, size_t msgmax)
|
|
||||||
{
|
|
||||||
/* This testcase simulates the host receiving
|
|
||||||
* an ARP request not associated to it, therefore
|
|
||||||
* the ARP module is expected to not reply.
|
|
||||||
*/
|
|
||||||
|
|
||||||
char host_ip[4] = {0xc0, 0xa8, 0x01, 0x05};
|
|
||||||
char host_mac[6] = {0xcc, 0x6b, 0x1e, 0x13, 0xa8, 0x93};
|
|
||||||
|
|
||||||
arp_testcase_t testcase;
|
|
||||||
arp_testcase_init(&testcase, host_mac, host_ip);
|
|
||||||
|
|
||||||
arp_testcase_send(&testcase, (char[]) {
|
|
||||||
0x00, 0x01, // hardware_type=ethernet
|
|
||||||
0x08, 0x00, // protocol_type=ip
|
|
||||||
0x06, // hardware_len=6
|
|
||||||
0x04, // protocol_len=4
|
|
||||||
0x00, 0x01, // operation_type=request
|
|
||||||
0xbc, 0x15, 0xac, 0x29, 0xe5, 0x61, // sender MAC
|
|
||||||
0xc0, 0xa8, 0x01, 0x01, // sender IP
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target MAC (empty)
|
|
||||||
0xc0, 0xa8, 0x01, 0x10, // target IP (different to the host's)
|
|
||||||
});
|
|
||||||
|
|
||||||
arp_testcase_result_t result =
|
|
||||||
arp_testcase_run(testcase, msg, msgmax);
|
|
||||||
|
|
||||||
arp_testcase_free(&testcase);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef arp_testcase_result_t (*arp_testcase_routine_t)(char*, size_t);
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
static const arp_testcase_routine_t routines[] = {
|
|
||||||
test_000,
|
|
||||||
test_001,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t passed = 0;
|
|
||||||
size_t failed = 0;
|
|
||||||
size_t aborted = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; routines[i]; i++) {
|
|
||||||
|
|
||||||
char message[1024];
|
|
||||||
arp_testcase_result_t result = routines[i](message, sizeof(message));
|
|
||||||
|
|
||||||
switch (result) {
|
|
||||||
case ARP_TESTCASE_PASSED:
|
|
||||||
fprintf(stdout, "Test %ld ... PASSED\n", i);
|
|
||||||
passed++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARP_TESTCASE_FAILED:
|
|
||||||
fprintf(stdout, "Test %ld ... FAILED: %s\n", i, message);
|
|
||||||
failed++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARP_TESTCASE_ABORTED:
|
|
||||||
fprintf(stdout, "Test %ld ... ABORTED: %s\n", i, message);
|
|
||||||
aborted++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stdout, "SUMMARY: %ld passed, %ld failed and %ld aborted\n",
|
|
||||||
passed, failed, aborted);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "test_arp_util.h"
|
|
||||||
|
|
||||||
void arp_testcase_init(arp_testcase_t *tcase,
|
|
||||||
const char mac[static 6],
|
|
||||||
const char ip[static 4])
|
|
||||||
{
|
|
||||||
memcpy(&tcase->self_ip, ip, 4);
|
|
||||||
memcpy(&tcase->self_mac, mac, 6);
|
|
||||||
tcase->failed = false;
|
|
||||||
tcase->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arp_testcase_free(arp_testcase_t *tcase)
|
|
||||||
{
|
|
||||||
(void) tcase;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arp_testcase_send(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)])
|
|
||||||
{
|
|
||||||
if (tcase->failed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tcase->count == ARP_TESTCASE_MAX_PACKETS) {
|
|
||||||
tcase->failed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
arp_testcase_packet_t *packet = tcase->packets + tcase->count;
|
|
||||||
packet->sender = ARP_TESTCASE_SENDER_PEER;
|
|
||||||
packet->data = data;
|
|
||||||
|
|
||||||
tcase->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arp_testcase_recv(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)])
|
|
||||||
{
|
|
||||||
if (tcase->failed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tcase->count == ARP_TESTCASE_MAX_PACKETS) {
|
|
||||||
tcase->failed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
arp_testcase_packet_t *packet = tcase->packets + tcase->count;
|
|
||||||
packet->sender = ARP_TESTCASE_SENDER_HOST;
|
|
||||||
packet->data = data;
|
|
||||||
|
|
||||||
tcase->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *msg;
|
|
||||||
size_t msgmax;
|
|
||||||
const arp_packet_t *output;
|
|
||||||
const arp_testcase_t *tcase;
|
|
||||||
size_t cursor;
|
|
||||||
bool output_as_expected;
|
|
||||||
bool aborted_while_checking_sent_packets;
|
|
||||||
} testcase_contex_t;
|
|
||||||
|
|
||||||
static void send_packet(void *data, mac_address_t dest)
|
|
||||||
{
|
|
||||||
(void) dest; // Is this ok?
|
|
||||||
|
|
||||||
testcase_contex_t *context = data;
|
|
||||||
|
|
||||||
context->aborted_while_checking_sent_packets = false;
|
|
||||||
|
|
||||||
if (context->cursor == context->tcase->count) {
|
|
||||||
snprintf(context->msg, context->msgmax, "ARP module sent an unexpected packet");
|
|
||||||
context->output_as_expected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const arp_testcase_packet_t *packet = context->tcase->packets + context->cursor;
|
|
||||||
|
|
||||||
if (packet->sender != ARP_TESTCASE_SENDER_HOST) {
|
|
||||||
snprintf(context->msg, context->msgmax, "ARP module sent an unexpected packet");
|
|
||||||
context->output_as_expected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context->cursor++;
|
|
||||||
|
|
||||||
if (!memcmp(context->output, packet->data, sizeof(arp_packet_t)))
|
|
||||||
context->output_as_expected = true;
|
|
||||||
else {
|
|
||||||
snprintf(context->msg, context->msgmax, "ARP module sent a different packet than expected");
|
|
||||||
context->output_as_expected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arp_testcase_result_t arp_testcase_run(arp_testcase_t tcase, char *msg, size_t msgmax)
|
|
||||||
{
|
|
||||||
if (tcase.failed)
|
|
||||||
return ARP_TESTCASE_ABORTED;
|
|
||||||
|
|
||||||
arp_packet_t output;
|
|
||||||
|
|
||||||
testcase_contex_t context = {
|
|
||||||
.output = &output,
|
|
||||||
.tcase = &tcase,
|
|
||||||
.cursor = 0,
|
|
||||||
.msg = msg,
|
|
||||||
.msgmax = msgmax,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (msgmax > 0)
|
|
||||||
msg[0] = '\0';
|
|
||||||
|
|
||||||
arp_state_t state;
|
|
||||||
arp_init(&state, tcase.self_ip, tcase.self_mac, &context, send_packet);
|
|
||||||
arp_change_output_buffer(&state, &output, sizeof(output));
|
|
||||||
|
|
||||||
while (context.cursor < tcase.count) {
|
|
||||||
|
|
||||||
arp_testcase_packet_t *packet = tcase.packets + context.cursor++;
|
|
||||||
|
|
||||||
if (packet->sender != ARP_TESTCASE_SENDER_PEER) {
|
|
||||||
snprintf(msg, msgmax, "ARP module didn't send packet");
|
|
||||||
return ARP_TESTCASE_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize these
|
|
||||||
context.output_as_expected = true;
|
|
||||||
context.aborted_while_checking_sent_packets = false;
|
|
||||||
|
|
||||||
arp_process_result_t status = arp_process_packet(&state, packet->data, sizeof(arp_packet_t));
|
|
||||||
|
|
||||||
(void) status; // Not useful yet
|
|
||||||
|
|
||||||
// Before this point the arp_process_packet will have
|
|
||||||
// sent some packets that were validated in the send_packet
|
|
||||||
// callback. If the testcase didn't fail, the next
|
|
||||||
// packet in the list will be sent by the peer.
|
|
||||||
if (context.aborted_while_checking_sent_packets)
|
|
||||||
return ARP_TESTCASE_ABORTED;
|
|
||||||
if (!context.output_as_expected)
|
|
||||||
return ARP_TESTCASE_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
arp_free(&state);
|
|
||||||
return ARP_TESTCASE_PASSED;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include "../src/arp.h"
|
|
||||||
|
|
||||||
#define ARP_TESTCASE_MAX_PACKETS 1024
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ARP_TESTCASE_SENDER_HOST,
|
|
||||||
ARP_TESTCASE_SENDER_PEER,
|
|
||||||
} arp_testcase_packet_sender_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
arp_testcase_packet_sender_t sender;
|
|
||||||
const char *data;
|
|
||||||
} arp_testcase_packet_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool failed;
|
|
||||||
|
|
||||||
ip_address_t self_ip;
|
|
||||||
mac_address_t self_mac;
|
|
||||||
|
|
||||||
size_t count;
|
|
||||||
arp_testcase_packet_t packets[ARP_TESTCASE_MAX_PACKETS];
|
|
||||||
} arp_testcase_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ARP_TESTCASE_PASSED,
|
|
||||||
ARP_TESTCASE_FAILED,
|
|
||||||
ARP_TESTCASE_ABORTED,
|
|
||||||
} arp_testcase_result_t;
|
|
||||||
|
|
||||||
void arp_testcase_init(arp_testcase_t *tcase, const char mac[static 6], const char ip[static 4]);
|
|
||||||
void arp_testcase_free(arp_testcase_t *tcase);
|
|
||||||
void arp_testcase_send(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]);
|
|
||||||
void arp_testcase_recv(arp_testcase_t *tcase, const char data[static sizeof(arp_packet_t)]);
|
|
||||||
arp_testcase_result_t arp_testcase_run(arp_testcase_t tcase, char *msg, size_t msgmax);
|
|
||||||
Reference in New Issue
Block a user