Compare commits

..

No commits in common. "master" and "socket-callbacks" have entirely different histories.

35 changed files with 294 additions and 6409 deletions

View file

@ -1,33 +0,0 @@
Name: Linux interop tests
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
jobs:
unit_test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Update repo
run: |
sudo apt-get update
sudo modprobe tun
- name: Build linux tests
run: |
mkdir -p build/port
make
# - name: Run interop tests
# run: |
# sudo build/test
#
#

View file

@ -1,33 +0,0 @@
Name: Unit Tests
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
jobs:
unit_test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Update repo
run: |
sudo apt-get update
- name: Install check
run: |
sudo apt-get -y install check
- name: Build unit tests
run: |
make unit
- name: Run unit tests
run: |
build/test/unit

8
.gitignore vendored
View file

@ -1,14 +1,6 @@
*.o
*.a
*.pcap
*.so
*.dis
*.uf2
*.bin
CMakeCache.txt
CMakeFiles
CMakeScripts
CMakeTmp
build/*
test/unit/unit
tags

125
Makefile
View file

@ -1,131 +1,42 @@
CC?=gcc
CFLAGS:=-Wall -Werror -Wextra -I.
CFLAGS+=-g -ggdb
LDFLAGS+=-pthread
OBJ=build/femtotcp.o build/test-linux.o build/port/linux.o
OBJ=build/femtotcp.o \
build/port/posix/linux_tap.o
all: build/test
EXE=build/tcpecho build/tcp_netcat_poll build/tcp_netcat_select \
build/test-evloop build/test-dns
LIB=libfemtotcp.so
PREFIX=/usr/local
all: $(EXE) $(LIB)
#Static library
static: CFLAGS+=-static
static: libtcpip.a
libtcpip.a: $(OBJ)
@ar rcs $@ $^
libfemtotcp.so:CFLAGS+=-fPIC
libfemtotcp.so: build/pie/port/posix/bsd_socket.o build/pie/femtotcp.o \
build/pie/port/posix/linux_tap.o
@mkdir -p `dirname $@` || true
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ -Wl,--start-group $(^) -Wl,--end-group
clean:
@rm -rf build
asan: $(EXE) $(LIB)
asan:CFLAGS+=-fsanitize=address
asan:LDFLAGS+=-static-libasan
@rm -f build/*.o build/port/*.o
@make -C test/unit clean
# Test
unit:LDFLAGS+=-lcheck -lm -lpthread -lrt -ldl -lsubunit
build/test-evloop: $(OBJ) build/test/test_linux_eventloop.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/test-dns: $(OBJ) build/test/test_linux_dhcp_dns.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/tcpecho: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_echo.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/tcp_netcat_poll: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_netcat_poll.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/tcp_netcat_select: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_netcat_select.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
asan: build/test
asan:CFLAGS+=-fsanitize=address
asan:LDFLAGS+=-static-libasan
build/test:CFLAGS+=-g -ggdb -DTEST_MAIN -DETHERNET
build/test:LDFLAGS+=-pthread
build/test-wolfssl:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_FEMTOTCP
build/test-httpd:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_FEMTOTCP -Isrc/http
build/test: $(OBJ)
@echo "Linking $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ)
build/test-wolfssl: $(OBJ) build/test/test_native_wolfssl.o build/port/wolfssl_io.o build/certs/server_key.o build/certs/ca_cert.o build/certs/server_cert.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -lwolfssl -Wl,--end-group
build/test-httpd: $(OBJ) build/test/test_httpd.o build/port/wolfssl_io.o build/certs/server_key.o build/certs/server_cert.o build/http/httpd.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -lwolfssl -Wl,--end-group
build/test-linux.o: test/test-linux.c
@echo "Compiling $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/%.o: src/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@echo "Compiling $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/pie/%.o: src/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/certs/%.o: build/certs/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/http/%.o: build/http/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/certs/ca_cert.c:
@echo "[MKCERTS] `dirname $@`"
@tools/certs/mkcerts.sh
build/certs/server_key.c:
@echo "[MKCERTS] `dirname $@`"
@tools/certs/mkcerts.sh
build/certs/server_cert.c:
@echo "[MKCERTS] `dirname $@`"
@tools/certs/mkcerts.sh
build/certs/server_key.o: build/certs/server_key.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
unit: build/test/unit
build/test/unit:
@mkdir -p build/test/
@echo "[CC] unit.c"
@$(CC) $(CFLAGS) -c src/test/unit/unit.c -o build/test/unit.o
@echo "[LD] $@"
@$(CC) -o build/test/unit build/test/unit.o $(LDFLAGS)
# Install dynamic library to re-link linux applications
#
install:
install libfemtotcp.so $(PREFIX)/lib
ldconfig
unit:
@make -C test/unit
.PHONY: clean all static

View file

@ -19,14 +19,11 @@ A single network interface can be associated with the device.
- IPv4 (RFC 791)
- ICMP (RFC 792): only ping replies
- DHCP (RFC 2131): client only
- DNS (RFC 1035): client only
- UDP (RFC 768): unicast only
- TCP (RFC 793)
- TCP options supported: Timestamps, Maximum Segment Size
- BSD-like, non blocking socket API, with custom callbacks
- No dynamic memory allocation
- Fixed number of concurrent sockets
- Pre-allocated buffers for packet processing in static memory
## Copyright and License

View file

@ -1,18 +0,0 @@
#ifndef FEMTO_CONFIG_H
#define FEMTO_CONFIG_H
#define ETHERNET
#define LINK_MTU 1536
#define MAX_TCPSOCKETS 20
#define MAX_UDPSOCKETS 2
#define RXBUF_SIZE LINK_MTU * 4
#define TXBUF_SIZE LINK_MTU * 16
#define MAX_NEIGHBORS 16
/* Linux test configuration */
#define FEMTOTCP_IP "10.10.10.2"
#define LINUX_IP "10.10.10.1"
#endif

View file

@ -1,5 +1,5 @@
#ifndef FEMTOTCP_H
#define FEMTOTCP_H
#ifndef QUECTONET_H
#define QUECTONET_H
#include <stdint.h>
/* Types */
@ -12,7 +12,6 @@ typedef uint32_t ip4;
#define ee32(x) __builtin_bswap32(x)
#define DEBUG
#ifdef DEBUG
#include <stdio.h>
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
@ -26,9 +25,9 @@ struct ll {
uint8_t mac[6];
char ifname[16];
/* poll function */
int (*poll)(struct ll *ll, void *buf, uint32_t len);
int (*poll)(struct ll *ll, void *buf, int len);
/* send function */
int (*send)(struct ll *ll, void *buf, uint32_t len);
int (*send)(struct ll *ll, void *buf, int len);
};
/* Struct to contain an IP device configuration */
@ -40,18 +39,9 @@ struct ipconf {
};
/* Socket interface */
#define MARK_TCP_SOCKET 0x100 /* Mark a socket as TCP */
#define MARK_UDP_SOCKET 0x200 /* Mark a socket as UDP */
#if (MARK_TCP_SOCKET >= MARK_UDP_SOCKET)
#error "MARK_TCP_SOCKET must be less than MARK_UDP_SOCKET"
#endif
#ifndef FEMTO_POSIX
#ifndef NATIVE_POSIX_SOCKET
#define IPSTACK_SOCK_STREAM 1
#define IPSTACK_SOCK_DGRAM 2
struct ipstack_sockaddr_in {
uint16_t sin_family;
uint16_t sin_port;
@ -62,13 +52,6 @@ typedef uint32_t socklen_t;
#ifndef AF_INET
#define AF_INET 2
#endif
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define ipstack_sockaddr_in sockaddr_in
#define ipstack_sockaddr sockaddr
#endif
int ft_socket(struct ipstack *s, int domain, int type, int protocol);
@ -77,11 +60,7 @@ int ft_listen(struct ipstack *s, int sockfd, int backlog);
int ft_accept(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr, socklen_t *addrlen);
int ft_connect(struct ipstack *s, int sockfd, const struct ipstack_sockaddr *addr, socklen_t addrlen);
int ft_sendto(struct ipstack *s, int sockfd, const void *buf, size_t len, int flags, const struct ipstack_sockaddr *dest_addr, socklen_t addrlen);
int ft_send(struct ipstack *s, int sockfd, const void *buf, size_t len, int flags);
int ft_write(struct ipstack *s, int sockfd, const void *buf, size_t len);
int ft_recvfrom(struct ipstack *s, int sockfd, void *buf, size_t len, int flags, struct ipstack_sockaddr *src_addr, socklen_t *addrlen);
int ft_recv(struct ipstack *s, int sockfd, void *buf, size_t len, int flags);
int ft_read(struct ipstack *s, int sockfd, void *buf, size_t len);
int ft_close(struct ipstack *s, int sockfd);
int ft_getpeername(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr, socklen_t *addrlen);
int ft_getsockname(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr, socklen_t *addrlen);
@ -89,80 +68,24 @@ int ft_getsockname(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr,
int dhcp_client_init(struct ipstack *s);
int dhcp_bound(struct ipstack *s);
/* DNS client */
int nslookup(struct ipstack *s, const char *name, uint16_t *id, void (*lookup_cb)(uint32_t ip));
/* IP stack interface */
void ipstack_init(struct ipstack *s);
void ipstack_init_static(struct ipstack **s);
int ipstack_poll(struct ipstack *s, uint64_t now);
void ipstack_recv(struct ipstack *s, void *buf, uint32_t len);
void ipstack_ipconfig_set(struct ipstack *s, ip4 ip, ip4 mask, ip4 gw);
void ipstack_ipconfig_get(struct ipstack *s, ip4 *ip, ip4 *mask, ip4 *gw);
struct ll *ipstack_getdev(struct ipstack *s);
ip4 atoip4(const char *ip);
/* Callback flags */
#define CB_EVENT_READABLE 0x01 /* Accepted connection or data available */
#define CB_EVENT_TIMEOUT 0x02 /* Timeout */
#define CB_EVENT_WRITABLE 0x04 /* Connected or space available to send */
#define CB_EVENT_CLOSED 0x10 /* Connection closed by peer */
#define CB_EVENT_WRITABLE 0x02 /* Connected or space available to send */
#define CB_EVENT_CLOSED 0x04 /* Connection closed by peer */
#define CB_EVENT_TIMEOUT 0x08 /* Timeout */
void ipstack_register_callback(struct ipstack *s, int sock_fd, void (*cb)(int sock_fd, uint16_t events, void *arg), void *arg);
/* External requirements */
uint32_t ipstack_getrandom(void);
/* Inline utility functions */
static inline uint32_t atou(const char *s)
{
uint32_t ret = 0;
while (*s >= '0' && *s <= '9') {
ret = ret * 10 + (*s - '0');
s++;
}
return ret;
}
static inline ip4 atoip4(const char *ip)
{
ip4 ret = 0;
int i = 0;
int j = 0;
for (i = 0; i < 4; i++) {
ret |= (atou(ip + j) << (24 - i * 8));
while (ip[j] != '.' && ip[j] != '\0') j++;
if (ip[j] == '\0') break;
j++;
}
return ret;
}
static inline void iptoa(ip4 ip, char *buf)
{
int i, j = 0;
buf[0] = 0;
for (i = 0; i < 4; i++) {
uint8_t x = (ip >> (24 - i * 8)) & 0xFF;
if (x > 99) buf[j++] = x / 100 + '0';
if (x > 9) buf[j++] = (x / 10) % 10 + '0';
buf[j++] = x % 10 + '0';
if (i < 3) buf[j++] = '.';
}
buf[j] = 0;
}
#ifdef WOLFSSL_FEMTOTCP
#ifdef WOLFSSL_USER_SETTINGS
#include "user_settings.h"
#else
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
/* Defined in wolfssl_io.c */
int wolfSSL_SetIO_FT(WOLFSSL* ssl, int fd);
int wolfSSL_SetIO_FT_CTX(WOLFSSL_CTX *ctx, struct ipstack *s);
#endif
#endif

View file

@ -8,19 +8,25 @@
#include <stdint.h>
#include <string.h>
#include "femtotcp.h"
#include "config.h"
/* Fixed size binary heap: each element is a timer. */
#define MAX_TIMERS MAX_TCPSOCKETS * 3
/* Config (TODO: move to some config.h later) */
#define LINK_MTU 1536
#define MAX_TCPSOCKETS 2
#define MAX_UDPSOCKETS 2
#define RXBUF_SIZE LINK_MTU * 8
#define TXBUF_SIZE LINK_MTU * 2
#define MAX_NEIGHBORS 16
/* Constants */
#define ICMP_ECHO_REPLY 0
#define ICMP_ECHO_REQUEST 8
#define ICMP_TTL_EXCEEDED 11
#define MARK_TCP_SOCKET 0x1000 /* Mark a socket as TCP */
#define MARK_UDP_SOCKET 0x4000 /* Mark a socket as UDP */
#define FT_IPPROTO_ICMP 0x01
#define FT_IPPROTO_TCP 0x06
#define FT_IPPROTO_UDP 0x11
#define IPPROTO_ICMP 0x01
#define IPPROTO_TCP 0x06
#define IPPROTO_UDP 0x11
#define IPADDR_ANY 0x00000000
#define TCP_OPTION_MSS 0x02
@ -48,8 +54,8 @@
#define NO_TIMER 0
#define FT_IP_MTU 1500
#define TCP_MSS (FT_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN))
#define IP_MTU 1500
#define TCP_MSS (IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN))
/* Macros */
#define IS_IP_BCAST(ip) (ip == 0xFFFFFFFF)
@ -66,7 +72,7 @@
/* Random number generator, provided by the user */
//extern uint32_t ipstack_getrandom(void);
uint32_t ipstack_getrandom(void);
struct PACKED pkt_desc {
uint32_t pos, len;
@ -513,6 +519,8 @@ struct ipstack_timer {
void (*cb)(void *arg);
};
/* Fixed size binary heap: each element is a timer. */
#define MAX_TIMERS MAX_TCPSOCKETS * 3
/* Timer binary heap */
struct timers_binheap {
struct ipstack_timer timers[MAX_TIMERS];
@ -530,10 +538,6 @@ struct ipstack
uint32_t dhcp_timeout_count; /* DHCP timeout counter */
ip4 dhcp_server_ip; /* DHCP server IP */
ip4 dhcp_ip; /* IP address assigned by DHCP */
ip4 dns_server;
uint16_t dns_id;
int dns_udp_sd;
void (*dns_lookup_cb)(ip4 ip);
struct timers_binheap timers;
struct tsocket tcpsockets[MAX_TCPSOCKETS];
struct tsocket udpsockets[MAX_UDPSOCKETS];
@ -563,7 +567,7 @@ void ipstack_register_callback(struct ipstack *s, int sock_fd, void (*cb)(int so
t->callback = cb;
t->callback_arg = arg;
} else if (sock_fd & MARK_UDP_SOCKET) {
if ((sock_fd &(~MARK_UDP_SOCKET)) >= MAX_UDPSOCKETS)
if (sock_fd >= MAX_UDPSOCKETS)
return;
t = &s->udpsockets[sock_fd & ~MARK_UDP_SOCKET];
t->callback = cb;
@ -649,11 +653,11 @@ static struct tsocket *udp_new_socket(struct ipstack *s)
for (int i = 0; i < MAX_UDPSOCKETS; i++) {
t = &s->udpsockets[i];
if (t->proto == 0) {
t->proto = FT_IPPROTO_UDP;
t->proto = IPPROTO_UDP;
t->S = s;
fifo_init(&t->sock.udp.rxbuf, t->rxmem, RXBUF_SIZE);
fifo_init(&t->sock.udp.txbuf, t->txmem, TXBUF_SIZE);
t->events |= CB_EVENT_WRITABLE;
t->events = CB_EVENT_WRITABLE;
return t;
}
}
@ -673,19 +677,19 @@ static void udp_try_recv(struct ipstack *s, struct ipstack_udp_datagram *udp, ui
return;
/* Insert into socket buffer */
fifo_push(&t->sock.udp.rxbuf, udp, frame_len);
t->events |= CB_EVENT_READABLE;
}
}
}
/* TCP */
static struct tsocket *tcp_new_socket(struct ipstack *s)
{
struct tsocket *t;
for (int i = 0; i < MAX_TCPSOCKETS; i++) {
t = &s->tcpsockets[i];
if (t->proto == 0) {
t->proto = FT_IPPROTO_TCP;
t->proto = IPPROTO_TCP;
t->S = s;
t->sock.tcp.state = TCP_CLOSED;
t->sock.tcp.rto = 1000;
@ -826,20 +830,6 @@ static uint16_t transport_checksum(union transport_pseudo_header *ph, void *_dat
return ~sum;
}
static uint16_t icmp_checksum(struct ipstack_icmp_packet *icmp)
{
uint32_t sum = 0;
uint32_t i = 0;
uint16_t *ptr = (uint16_t *)(&icmp->type);
for (i = 0; i < ICMP_HEADER_LEN / 2; i++) {
sum += ee16(ptr[i]);
}
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
return ~sum;
}
static void iphdr_set_checksum(struct ipstack_ip_packet *ip)
{
uint32_t sum = 0;
@ -893,11 +883,11 @@ static int ip_output_add_header(struct tsocket *t, struct ipstack_ip_packet *ip,
ph.ph.zero = 0;
ph.ph.proto = proto;
ph.ph.len = ee16(len - IP_HEADER_LEN);
if (proto == FT_IPPROTO_TCP) {
if (proto == IPPROTO_TCP) {
struct ipstack_tcp_seg *tcp = (struct ipstack_tcp_seg *)ip;
tcp->csum = 0;
tcp->csum = ee16(transport_checksum(&ph, &tcp->src_port));
} else if (proto == FT_IPPROTO_UDP) {
} else if (proto == IPPROTO_UDP) {
struct ipstack_udp_datagram *udp = (struct ipstack_udp_datagram *)ip;
udp->csum = 0;
udp->csum = ee16(transport_checksum(&ph, &udp->src_port));
@ -909,25 +899,25 @@ static int ip_output_add_header(struct tsocket *t, struct ipstack_ip_packet *ip,
}
/* Process timestamp option, calculate RTT */
static int tcp_process_ts(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
static int tcp_process_ts(struct tsocket *t, struct ipstack_tcp_seg *tcp)
{
const struct tcp_opt_ts *ts;
const uint8_t *opt = tcp->data;
struct tcp_opt_ts *ts;
uint8_t *opt = tcp->data;
while (opt < (tcp->data + (tcp->hlen >> 2))) {
if (*opt == TCP_OPTION_NOP)
opt++;
else if (*opt == TCP_OPTION_EOO)
break;
else {
ts = (const struct tcp_opt_ts *)opt;
ts = (struct tcp_opt_ts *)opt;
if (ts->opt == TCP_OPTION_TS) {
t->sock.tcp.last_ts = ts->val;
if (ts->ecr != 0)
return -1;
if (t->sock.tcp.rtt == 0)
t->sock.tcp.rtt = (uint32_t)(t->S->last_tick - ee32(ts->ecr));
t->sock.tcp.rtt = t->S->last_tick - ee32(ts->ecr);
else {
t->sock.tcp.rtt = (uint32_t)(7 * (t->sock.tcp.rtt << 3)) +
t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) +
((t->S->last_tick - ee32(ts->ecr)) << 3);
}
return 0;
@ -942,7 +932,7 @@ static int tcp_process_ts(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
#define SEQ_DIFF(a,b) ((a - b) > 0x7FFFFFFF) ? (b - a) : (a - b)
/* Receive an ack */
static void tcp_ack(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
static void tcp_ack(struct tsocket *t, struct ipstack_tcp_seg *tcp)
{
uint32_t ack = ee32(tcp->ack);
struct pkt_desc *desc;
@ -972,7 +962,7 @@ static void tcp_ack(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
}
}
if (ack_count > 0) {
struct pkt_desc *fresh_desc = NULL;
struct pkt_desc *fresh_desc;
struct ipstack_tcp_seg *tcp;
/* This ACK ackwnowledged some data. */
desc = fifo_peek(&t->sock.tcp.txbuf);
@ -980,31 +970,30 @@ static void tcp_ack(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
fresh_desc = fifo_pop(&t->sock.tcp.txbuf);
desc = fifo_peek(&t->sock.tcp.txbuf);
}
if (fresh_desc) {
tcp = (struct ipstack_tcp_seg *)(t->txmem + fresh_desc->pos + sizeof(*fresh_desc));
/* Update rtt */
if (tcp_process_ts(t, tcp) < 0) {
/* No timestamp option, use coarse RTT estimation */
int rtt = t->S->last_tick - fresh_desc->time_sent;
if (t->sock.tcp.rtt == 0) {
t->sock.tcp.rtt = rtt;
} else {
t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) + (rtt << 3);
}
}
/* Update cwnd */
if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) {
t->sock.tcp.cwnd += TCP_MSS;
tcp = (struct ipstack_tcp_seg *)(t->txmem + fresh_desc->pos + sizeof(*fresh_desc));
/* Update rtt */
if (tcp_process_ts(t, tcp) < 0) {
/* No timestamp option, use coarse RTT estimation */
int rtt = t->S->last_tick - fresh_desc->time_sent;
if (t->sock.tcp.rtt == 0) {
t->sock.tcp.rtt = rtt;
} else {
t->sock.tcp.cwnd_count++;
if (t->sock.tcp.cwnd_count == t->sock.tcp.cwnd) {
t->sock.tcp.cwnd_count = 0;
t->sock.tcp.cwnd += TCP_MSS;
}
t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) + (rtt << 3);
}
if (fifo_space(&t->sock.tcp.txbuf) > 0)
t->events |= CB_EVENT_WRITABLE;
}
/* Update cwnd */
if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) {
t->sock.tcp.cwnd += TCP_MSS;
} else {
t->sock.tcp.cwnd_count++;
if (t->sock.tcp.cwnd_count == t->sock.tcp.cwnd) {
t->sock.tcp.cwnd_count = 0;
t->sock.tcp.cwnd += TCP_MSS;
}
}
if (fifo_space(&t->sock.tcp.txbuf) > 0)
t->events |= CB_EVENT_WRITABLE;
} else {
struct pkt_desc *desc;
/* Duplicate ack */
@ -1024,6 +1013,7 @@ static void tcp_ack(struct tsocket *t, const struct ipstack_tcp_seg *tcp)
continue;
}
if (ee32(seg->seq) == ack) {
LOG("Retransmit %u size %d due to dupack %u\n", ee32(seg->seq), seg_len, ack);
desc->flags &= ~PKT_FLAG_SENT; /* Resend */
break;
}
@ -1044,37 +1034,16 @@ static void tcp_input(struct ipstack *S, struct ipstack_tcp_seg *tcp, uint32_t f
/* TCP segment sanity checks */
iplen = ee16(tcp->ip.len);
if (iplen > frame_len - sizeof(struct ipstack_eth_frame)) {
LOG("Wrong packet size %d, frame is %d\n", iplen, frame_len);
return; /* discard */
}
if (t->sock.tcp.state > TCP_LISTEN) {
if (t->dst_port != ee16(tcp->src_port) || t->remote_ip != ee32(tcp->ip.src)) {
/* Not the right socket */
continue;
}
}
/* Check IP ttl */
if (tcp->ip.ttl == 0) {
/* Send ICMP TTL exceeded */
struct ipstack_icmp_packet icmp;
memset(&icmp, 0, sizeof(icmp));
icmp.type = ICMP_TTL_EXCEEDED;
icmp.csum = ee16(icmp_checksum(&icmp));
icmp.ip.src = tcp->ip.dst;
icmp.ip.dst = tcp->ip.src;
icmp.ip.proto = FT_IPPROTO_ICMP;
icmp.ip.id = ee16(S->ipcounter++);
icmp.ip.csum = 0;
iphdr_set_checksum(&icmp.ip);
eth_output_add_header(S, icmp.ip.eth.src, &icmp.ip.eth, ETH_TYPE_IP);
S->ll_dev.send(&S->ll_dev, &icmp, sizeof(struct ipstack_icmp_packet));
return;
}
tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2));
/* Check if RST, close socket only if in SYN_SENT */
if ((tcp->flags & 0x04) && (t->sock.tcp.state == TCP_SYN_SENT)) {
close_socket(t);
}
/* Check if FIN */
if (tcp->flags & 0x01) {
@ -1082,15 +1051,16 @@ static void tcp_input(struct ipstack *S, struct ipstack_tcp_seg *tcp, uint32_t f
t->sock.tcp.state = TCP_CLOSE_WAIT;
t->sock.tcp.ack = ee32(tcp->seq) + 1;
tcp_send_ack(t);
t->events |= CB_EVENT_CLOSED | CB_EVENT_READABLE;
t->events |= CB_EVENT_CLOSED;
}
else if (t->sock.tcp.state == TCP_FIN_WAIT_1) {
t->sock.tcp.state = TCP_CLOSING;
t->sock.tcp.ack = ee32(tcp->seq) + 1;
tcp_send_ack(t);
t->events |= CB_EVENT_CLOSED | CB_EVENT_READABLE;
t->events |= CB_EVENT_CLOSED;
}
}
/* Check if SYN */
if (tcp->flags & 0x02) {
if (t->sock.tcp.state == TCP_LISTEN) {
@ -1139,7 +1109,7 @@ static void tcp_input(struct ipstack *S, struct ipstack_tcp_seg *tcp, uint32_t f
t->sock.tcp.state = TCP_CLOSING;
}
t->sock.tcp.ack = ee32(tcp->seq) + 1;
t->events |= CB_EVENT_CLOSED | CB_EVENT_READABLE;
t->events |= CB_EVENT_CLOSED;
tcp_send_ack(t);
}
if (tcp->flags & 0x10) {
@ -1163,12 +1133,15 @@ static void tcp_rto_cb(void *arg)
struct ipstack_timer tmr = { };
struct ipstack_timer *ptmr = NULL;
int pending = 0;
if ((ts->proto != FT_IPPROTO_TCP) || (ts->sock.tcp.state != TCP_ESTABLISHED))
if ((ts->proto != IPPROTO_TCP) || (ts->sock.tcp.state != TCP_ESTABLISHED))
return;
desc = fifo_peek(&ts->sock.tcp.txbuf);
while (desc) {
if (desc->flags & PKT_FLAG_SENT) {
desc->flags &= ~PKT_FLAG_SENT;
LOG("Scheduling retransmit %u\n", ee32(((struct ipstack_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)))->seq));
LOG("Fifo len: %d\n", fifo_len(&ts->sock.tcp.txbuf));
LOG("Fifo space: %d\n", fifo_space(&ts->sock.tcp.txbuf));
pending++;
}
desc = fifo_next(&ts->sock.tcp.txbuf, desc);
@ -1179,6 +1152,7 @@ static void tcp_rto_cb(void *arg)
ts->sock.tcp.tmr_rto = NO_TIMER;
}
if (pending) {
LOG("RTO backoff: %d\n", ts->sock.tcp.rto_backoff);
ts->sock.tcp.rto_backoff++;
ts->sock.tcp.cwnd = TCP_MSS;
ts->sock.tcp.ssthresh = ts->sock.tcp.cwnd / 2;
@ -1222,18 +1196,11 @@ int ft_socket(struct ipstack *s, int domain, int type, int protocol)
int ft_connect(struct ipstack *s, int sockfd, const struct ipstack_sockaddr *addr, socklen_t addrlen)
{
struct tsocket *ts;
const struct ipstack_sockaddr_in *sin = (const struct ipstack_sockaddr_in *)addr;
struct ipstack_sockaddr_in *sin = (struct ipstack_sockaddr_in *)addr;
struct ipstack_tcp_seg tcp;
if (!addr)
return -2;
if (sockfd & MARK_UDP_SOCKET) {
ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET];
ts->dst_port = ee16(sin->sin_port);
ts->remote_ip = ee32(sin->sin_addr.s_addr);
return 0;
}
if ((sockfd & MARK_TCP_SOCKET) == 0)
return -1;
ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET];
ts = &s->tcpsockets[sockfd];
if (ts->sock.tcp.state == TCP_ESTABLISHED)
return 0;
if (ts->sock.tcp.state == TCP_SYN_SENT)
@ -1249,7 +1216,16 @@ int ft_connect(struct ipstack *s, int sockfd, const struct ipstack_sockaddr *add
if (ts->src_port < 1024)
ts->src_port += 1024;
ts->dst_port = ee16(sin->sin_port);
tcp_send_syn(ts, 0x02);
tcp.src_port = ts->src_port;
tcp.dst_port = sin->sin_port;
tcp.seq = ts->sock.tcp.seq;
tcp.ack = 0;
tcp.hlen = (TCP_HEADER_LEN + TCP_OPTIONS_LEN) << 2;
tcp.flags = 0x02; /* SYN */
tcp.win = ee16((uint16_t)queue_space(&ts->sock.tcp.rxbuf));
tcp.csum = 0;
tcp.urg = 0;
fifo_push(&ts->sock.tcp.txbuf, &tcp, sizeof(struct ipstack_tcp_seg) + TCP_OPTIONS_LEN);
return -11;
}
return -2;
@ -1354,7 +1330,7 @@ int ft_sendto(struct ipstack *s, int sockfd, const void *buf, size_t len, int fl
else
return sent;
} else if (sockfd & MARK_UDP_SOCKET) {
const struct ipstack_sockaddr_in *sin = (const struct ipstack_sockaddr_in *)dest_addr;
struct ipstack_sockaddr_in *sin = (struct ipstack_sockaddr_in *)dest_addr;
ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET];
if ((ts->dst_port == 0) && (dest_addr == NULL))
return -1;
@ -1367,7 +1343,7 @@ int ft_sendto(struct ipstack *s, int sockfd, const void *buf, size_t len, int fl
}
if ((ts->dst_port==0) || (ts->remote_ip==0))
return -1;
if (len > FT_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN)
if (len > IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN)
return -1; /* Fragmentation not supported */
if (fifo_space(&ts->sock.udp.txbuf) < len)
return -11;
@ -1376,9 +1352,6 @@ int ft_sendto(struct ipstack *s, int sockfd, const void *buf, size_t len, int fl
if (ts->src_port < 1024)
ts->src_port += 1024;
}
if(ts->local_ip == 0)
ts->local_ip = s->ipconf.ip;
udp->src_port = ee16(ts->src_port);
udp->dst_port = ee16(ts->dst_port);
udp->len = ee16(len + UDP_HEADER_LEN);
@ -1389,16 +1362,6 @@ int ft_sendto(struct ipstack *s, int sockfd, const void *buf, size_t len, int fl
} else return -1;
}
int ft_send(struct ipstack *s, int sockfd, const void *buf, size_t len, int flags)
{
return ft_sendto(s, sockfd, buf, len, flags, NULL, 0);
}
int ft_write(struct ipstack *s, int sockfd, const void *buf, size_t len)
{
return ft_sendto(s, sockfd, buf, len, 0, NULL, 0);
}
int ft_recvfrom(struct ipstack *s, int sockfd, void *buf, size_t len, int flags,
struct ipstack_sockaddr *src_addr, socklen_t *addrlen)
{
@ -1417,10 +1380,7 @@ int ft_recvfrom(struct ipstack *s, int sockfd, void *buf, size_t len, int flags,
return 0;
return queue_pop(&ts->sock.tcp.rxbuf, buf, len);
} else if (ts->sock.tcp.state == TCP_ESTABLISHED) {
int ret = queue_pop(&ts->sock.tcp.rxbuf, buf, len);
if ((ret > 0) && (queue_len(&ts->sock.tcp.rxbuf) > 0))
ts->events |= CB_EVENT_READABLE;
return ret;
return queue_pop(&ts->sock.tcp.rxbuf, buf, len);
} else { /* Not established */
return -1;
}
@ -1429,7 +1389,7 @@ int ft_recvfrom(struct ipstack *s, int sockfd, void *buf, size_t len, int flags,
ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET];
if (sin && *addrlen < sizeof(struct ipstack_sockaddr_in))
return -1;
if (addrlen) *addrlen = sizeof(struct ipstack_sockaddr_in);
*addrlen = sizeof(struct ipstack_sockaddr_in);
if (fifo_len(&ts->sock.udp.rxbuf) == 0)
return -11;
desc = fifo_peek(&ts->sock.udp.rxbuf);
@ -1448,22 +1408,13 @@ int ft_recvfrom(struct ipstack *s, int sockfd, void *buf, size_t len, int flags,
} else return -1;
}
int ft_recv(struct ipstack *s, int sockfd, void *buf, size_t len, int flags)
{
return ft_recvfrom(s, sockfd, buf, len, flags, NULL, 0);
}
int ft_read(struct ipstack *s, int sockfd, void *buf, size_t len)
{
return ft_recvfrom(s, sockfd, buf, len, 0, NULL, 0);
}
int ft_close(struct ipstack *s, int sockfd)
{
if (sockfd & MARK_TCP_SOCKET) {
struct tsocket *ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET];
if (ts->sock.tcp.state == TCP_ESTABLISHED) {
ts->sock.tcp.state = TCP_FIN_WAIT_1;
LOG("close(): Sending FIN\n");
tcp_send_finack(ts);
return -11;
} else if (ts->sock.tcp.state == TCP_CLOSE_WAIT) {
@ -1507,7 +1458,7 @@ int ft_getsockname(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr,
int ft_bind(struct ipstack *s, int sockfd, const struct ipstack_sockaddr *addr, socklen_t addrlen)
{
struct tsocket *ts;
const struct ipstack_sockaddr_in *sin = (const struct ipstack_sockaddr_in *)addr;
struct ipstack_sockaddr_in *sin = (struct ipstack_sockaddr_in *)addr;
if (!sin || addrlen < sizeof(struct ipstack_sockaddr_in))
return -1;
@ -1557,8 +1508,11 @@ int ft_getpeername(struct ipstack *s, int sockfd, struct ipstack_sockaddr *addr,
return 0;
}
/* ICMP */
#define ICMP_ECHO_REPLY 0
#define ICMP_ECHO_REQUEST 8
/* Reply to ICecho requests */
/* Reply to ICMP echo requests */
static void icmp_input(struct ipstack *s, struct ipstack_ip_packet *ip, uint32_t len)
{
struct ipstack_icmp_packet *icmp = (struct ipstack_icmp_packet *)ip;
@ -1652,27 +1606,30 @@ static int dhcp_parse_offer(struct ipstack *s, struct dhcp_msg *msg)
return -1;
}
static int dhcp_parse_ack(struct ipstack *s, struct dhcp_msg *msg)
{
struct dhcp_option *opt = (struct dhcp_option *)(msg->options);
while (opt->code != 0xFF) {
if (opt->code == DHCP_OPTION_MSG_TYPE) {
if (opt->data[0] == DHCP_ACK) {
uint32_t data;
opt = (struct dhcp_option *)((uint8_t *)opt + 3);
data = opt->data[0] | (opt->data[1] << 8) | (opt->data[2] << 16) | (opt->data[3] << 24);
while (opt->code != 0xFF) {
if (opt->code == DHCP_OPTION_SERVER_ID)
if (opt->code == DHCP_OPTION_SERVER_ID) {
uint32_t data = opt->data[0] | (opt->data[1] << 8) | (opt->data[2] << 16) | (opt->data[3] << 24);
s->dhcp_server_ip = ee32(data);
if (opt->code == DHCP_OPTION_OFFER_IP)
}
if (opt->code == DHCP_OPTION_OFFER_IP) {
uint32_t data = opt->data[0] | (opt->data[1] << 8) | (opt->data[2] << 16) | (opt->data[3] << 24);
s->ipconf.ip = ee32(data);
if (opt->code == DHCP_OPTION_SUBNET_MASK)
}
if (opt->code == DHCP_OPTION_SUBNET_MASK) {
uint32_t data = opt->data[0] | (opt->data[1] << 8) | (opt->data[2] << 16) | (opt->data[3] << 24);
s->ipconf.mask = ee32(data);
if (opt->code == DHCP_OPTION_ROUTER)
}
if (opt->code == DHCP_OPTION_ROUTER) {
uint32_t data = opt->data[0] | (opt->data[1] << 8) | (opt->data[2] << 16) | (opt->data[3] << 24);
s->ipconf.gw = ee32(data);
if ((opt->code == DHCP_OPTION_DNS) && (s->dns_server == 0))
s->dns_server = ee32(data);
}
opt = (struct dhcp_option *)((uint8_t *)opt + 2 + opt->len);
}
if ((s->ipconf.ip != 0) && (s->ipconf.mask != 0)) {
@ -1698,14 +1655,8 @@ static int dhcp_poll(struct ipstack *s)
return -1;
if ((s->dhcp_state == DHCP_DISCOVER_SENT) && (dhcp_parse_offer(s, &msg) == 0))
dhcp_send_request(s);
else if ((s->dhcp_state == DHCP_REQUEST_SENT) && (dhcp_parse_ack(s, &msg) == 0)) {
else if ((s->dhcp_state == DHCP_REQUEST_SENT) && (dhcp_parse_ack(s, &msg) == 0))
LOG("DHCP configuration received.\n");
LOG("IP Address: %u.%u.%u.%u\n", (s->ipconf.ip >> 24) & 0xFF, (s->ipconf.ip >> 16) & 0xFF, (s->ipconf.ip >> 8) & 0xFF, (s->ipconf.ip >> 0) & 0xFF);
LOG("Subnet Mask: %u.%u.%u.%u\n", (s->ipconf.mask >> 24) & 0xFF, (s->ipconf.mask >> 16) & 0xFF, (s->ipconf.mask >> 8) & 0xFF, (s->ipconf.mask >> 0) & 0xFF);
LOG("Gateway: %u.%u.%u.%u\n", (s->ipconf.gw >> 24) & 0xFF, (s->ipconf.gw >> 16) & 0xFF, (s->ipconf.gw >> 8) & 0xFF, (s->ipconf.gw >> 0) & 0xFF);
if (s->dns_server)
LOG("DNS Server: %u.%u.%u.%u\n", (s->dns_server >> 24) & 0xFF, (s->dns_server >> 16) & 0xFF, (s->dns_server >> 8) & 0xFF, (s->dns_server >> 0) & 0xFF);
}
return 0;
}
@ -1770,16 +1721,6 @@ static int dhcp_send_request(struct ipstack *s)
return 0;
}
static void dhcp_callback(int sockfd, uint16_t ev, void *arg)
{
struct ipstack *s = (struct ipstack *)arg;
(void)sockfd;
(void)ev;
if (!s)
return;
dhcp_poll(s);
}
static int dhcp_send_discover(struct ipstack *s)
{
struct dhcp_msg disc;
@ -1842,7 +1783,7 @@ int dhcp_client_init(struct ipstack *s)
ft_close(s, s->dhcp_udp_sd);
}
s->dhcp_udp_sd = ft_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, FT_IPPROTO_UDP);
s->dhcp_udp_sd = ft_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, IPPROTO_UDP);
if (s->dhcp_udp_sd < 0) {
s->dhcp_state = DHCP_OFF;
return -1;
@ -1854,7 +1795,6 @@ int dhcp_client_init(struct ipstack *s)
s->dhcp_state = DHCP_OFF;
return -1;
}
ipstack_register_callback(s, s->dhcp_udp_sd, dhcp_callback, s);
return dhcp_send_discover(s);
}
@ -1957,167 +1897,6 @@ void ipstack_init_static(struct ipstack **s)
*s = &ipstack_static;
}
static inline void ip_recv(struct ipstack *s, struct ipstack_ip_packet *ip,
uint32_t len)
{
if (ip->ver_ihl == 0x45 && ip->proto == 0x06) {
struct ipstack_tcp_seg *tcp = (struct ipstack_tcp_seg *)ip;
tcp_input(s, tcp, len);
}
else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) {
struct ipstack_udp_datagram *udp = (struct ipstack_udp_datagram *)ip;
udp_try_recv(s, udp, len);
} else if (ip->ver_ihl == 0x45 && ip->proto == 0x01) {
icmp_input(s, ip, len);
}
}
/* Try to receive a packet from the network interface.
*
* This function is called either after polling the device driver
* in the loop, or in the device driver dsr callback.
*/
void ipstack_recv(struct ipstack *s, void *buf, uint32_t len)
{
#ifdef ETHERNET
struct ipstack_eth_frame *eth = (struct ipstack_eth_frame *)buf;
if (eth->type == ee16(0x0800)) {
struct ipstack_ip_packet *ip = (struct ipstack_ip_packet *)eth;
if ((memcmp(eth->dst, s->ll_dev.mac, 6) != 0) && (memcmp(eth->dst, "\xff\xff\xff\xff\xff\xff", 6) != 0)) {
return; /* Not for us */
}
ip_recv(s, ip, len);
} else if (eth->type == ee16(0x0806)) {
arp_recv(s, buf, len);
}
#else
/* No ethernet, assume IP */
struct ipstack_ip_packet *ip = (struct ipstack_ip_packet *)buf;
ip_recv(s, ip, len);
#endif
/* No default action required: the buffer is in stack and will be discarded on return */
}
/* DNS Client */
#define DNS_PORT 53
#define DNS_QUERY 0x00
#define DNS_RESPONSE 0x80
#define DNS_A 0x01 /* A record only */
#define DNS_RD 0x0100 /* Recursion desired */
struct PACKED dns_header {
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
};
struct PACKED dns_question {
uint16_t qtype;
uint16_t qclass;
};
#define MAX_DNS_RESPONSE 512
void dns_callback(int dns_sd, uint16_t ev, void *arg)
{
struct ipstack *s = (struct ipstack *)arg;
char buf[MAX_DNS_RESPONSE];
struct dns_header *hdr = (struct dns_header *)buf;
struct dns_question *q;
int dns_len;
if (!s)
return;
if (ev & CB_EVENT_READABLE) {
dns_len = ft_recvfrom(s, dns_sd, buf, MAX_DNS_RESPONSE, 0, NULL, 0);
if (dns_len < 0) {
ft_close(s, dns_sd);
s->dns_udp_sd = -1;
s->dns_id = 0;
return;
}
/* Parse DNS response */
if ((ee16(hdr->flags) & 0x8100) == 0x8100) {
/* Skip the question */
char *q_name = buf + sizeof(struct dns_header);
uint32_t ip;
while (*q_name) q_name++;
if (q_name - buf > dns_len) {
s->dns_id = 0;
return;
}
q_name++; /* Skip the null terminator */
q = (struct dns_question *)q_name;
if (ee16(q->qtype) == DNS_A) {
uint8_t *ip_ptr = (uint8_t *)(buf + dns_len - 4);
ip = ip_ptr[3] | (ip_ptr[2] << 8) | (ip_ptr[1] << 16) | (ip_ptr[0] << 24);
if(s->dns_lookup_cb)
s->dns_lookup_cb(ee32(ip));
LOG("DNS response: %u.%u.%u.%u\n", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
s->dns_id = 0;
}
}
}
}
int nslookup(struct ipstack *s, const char *dname, uint16_t *id, void (*lookup_cb)(uint32_t ip))
{
uint8_t buf[512];
struct dns_header *hdr;
struct dns_question *q;
char *q_name, *tok_start, *tok_end;
struct ipstack_sockaddr_in dns_srv;
uint32_t tok_len = 0;
if (!dname || !id || !lookup_cb) return -22; /* Invalid arguments */
if (strlen(dname) > 256) return -22; /* Invalid arguments */
if (s->dns_server == 0) return -101; /* Network unreachable: No DNS server configured */
if (s->dns_id != 0) return -16; /* DNS query already in progress */
if (s->dns_udp_sd <= 0) {
s->dns_udp_sd = ft_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, FT_IPPROTO_UDP);
if (s->dns_udp_sd < 0)
return -1;
ipstack_register_callback(s, s->dns_udp_sd, dns_callback, s);
}
s->dns_lookup_cb = lookup_cb;
s->dns_id = ipstack_getrandom();
*id = s->dns_id;
memset(buf, 0, 512);
hdr = (struct dns_header *)buf;
hdr->id = ee16(s->dns_id);
hdr->flags = ee16(DNS_QUERY);
hdr->qdcount = ee16(1);
hdr->flags = ee16(DNS_RD);
/* Prepare the DNS query name */
q_name = (char *)(buf + sizeof(struct dns_header));
tok_start = (char *)dname;
while(*tok_start) {
tok_end = tok_start;
while ((*tok_end != '.') && (*tok_end != 0)) {
tok_end++;
}
*q_name = tok_end - tok_start;
q_name++;
memcpy(q_name, tok_start, tok_end - tok_start);
q_name += tok_end - tok_start;
tok_len += (tok_end - tok_start) + 1;
if (*tok_end == 0)
break;
tok_start = tok_end + 1;
}
*q_name = 0;
tok_len++;
q = (struct dns_question *)(buf + sizeof(struct dns_header) + tok_len);
q->qtype = ee16(DNS_A);
q->qclass = ee16(1);
memset(&dns_srv, 0, sizeof(struct ipstack_sockaddr_in));
dns_srv.sin_family = AF_INET;
dns_srv.sin_port = ee16(DNS_PORT);
dns_srv.sin_addr.s_addr = ee32(s->dns_server);
ft_sendto(s, s->dns_udp_sd, buf, sizeof(struct dns_header) + tok_len + sizeof(struct dns_question), 0, (struct ipstack_sockaddr *)&dns_srv, sizeof(struct ipstack_sockaddr_in));
return 0;
}
/* ipstack_poll: poll the network stack for incoming packets
* This function should be called in a loop to process incoming packets.
* It will call the poll function of the device driver and process the
@ -2139,15 +1918,49 @@ int ipstack_poll(struct ipstack *s, uint64_t now)
s->last_tick = now;
/* Step 1: Poll the device */
if (s->ll_dev.poll) {
do {
len = s->ll_dev.poll(&s->ll_dev, buf, LINK_MTU);
if (len > 0) {
/* Process packet */
ipstack_recv(s, buf, len);
do {
len = s->ll_dev.poll(&s->ll_dev, buf, LINK_MTU);
if (len > 0) {
/* Process packet */
#ifdef ETHERNET
struct ipstack_eth_frame *eth = (struct ipstack_eth_frame *)buf;
if (eth->type == ee16(0x0800)) {
if ((memcmp(eth->dst, s->ll_dev.mac, 6) != 0) && (memcmp(eth->dst, "\xff\xff\xff\xff\xff\xff", 6) != 0)) {
/* Not for us */
continue;
}
struct ipstack_ip_packet *ip = (struct ipstack_ip_packet *)eth;
if (ip->ver_ihl == 0x45 && ip->proto == 0x06) {
struct ipstack_tcp_seg *tcp = (struct ipstack_tcp_seg *)ip;
tcp_input(s, tcp, len);
}
else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) {
struct ipstack_udp_datagram *udp = (struct ipstack_udp_datagram *)ip;
udp_try_recv(s, udp, len);
} else if (ip->ver_ihl == 0x45 && ip->proto == 0x01) {
icmp_input(s, ip, len);
}
} else if (eth->type == ee16(0x0806)) {
arp_recv(s, buf, len);
}
} while (len > 0);
}
#else
/* No ethernet, assume IP */
struct ipstack_ip_packet *ip = (struct ipstack_ip_packet *)buf;
if (ip->ver_ihl == 0x45 && ip->proto == 0x06) {
struct ipstack_tcp_seg *tcp = (struct ipstack_tcp_seg *)ip;
tcp_input(s, tcp, len);
}
else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) {
struct ipstack_udp_datagram *udp = (struct ipstack_udp_datagram *)ip;
udp_try_recv(s, udp, len);
} else if (ip->ver_ihl == 0x45 && ip->proto == 0x01) {
icmp_input(s, ip, len);
}
#endif
/* No default action required: the buffer is in stack and will be discarded on return */
}
} while (len > 0);
/* Step 2: Handle timers */
while(is_timer_expired(&s->timers, now)) {
tmr = timers_binheap_pop(&s->timers);
@ -2160,6 +1973,7 @@ int ipstack_poll(struct ipstack *s, uint64_t now)
ts->callback(i | MARK_TCP_SOCKET, ts->events, ts->callback_arg);
ts->events = 0;
}
}
for (i = 0; i < MAX_UDPSOCKETS; i++) {
struct tsocket *ts = &s->udpsockets[i];
@ -2168,6 +1982,10 @@ int ipstack_poll(struct ipstack *s, uint64_t now)
ts->events = 0;
}
}
/* Poll DHCP */
if (s->dhcp_state == DHCP_DISCOVER_SENT || s->dhcp_state == DHCP_REQUEST_SENT) {
dhcp_poll(s);
}
/* Step 4: attempt to write any pending data */
for (i = 0; i < MAX_TCPSOCKETS; i++) {
@ -2208,7 +2026,7 @@ int ipstack_poll(struct ipstack *s, uint64_t now)
ts->sock.tcp.last_ack = ts->sock.tcp.ack;
tcp->ack = ee32(ts->sock.tcp.ack);
tcp->win = ee16(queue_space(&ts->sock.tcp.rxbuf));
ip_output_add_header(ts, (struct ipstack_ip_packet *)tcp, FT_IPPROTO_TCP, len);
ip_output_add_header(ts, (struct ipstack_ip_packet *)tcp, IPPROTO_TCP, len);
s->ll_dev.send(&s->ll_dev, tcp, desc->len);
desc->flags |= PKT_FLAG_SENT;
desc->time_sent = now;
@ -2248,7 +2066,7 @@ int ipstack_poll(struct ipstack *s, uint64_t now)
if (IS_IP_BCAST(nexthop)) memset(t->nexthop_mac, 0xFF, 6);
#endif
len = desc->len - ETH_HEADER_LEN;
ip_output_add_header(t, (struct ipstack_ip_packet *)udp, FT_IPPROTO_UDP, len);
ip_output_add_header(t, (struct ipstack_ip_packet *)udp, IPPROTO_UDP, len);
s->ll_dev.send(&s->ll_dev, udp, desc->len);
fifo_pop(&t->sock.udp.txbuf);
desc = fifo_peek(&t->sock.udp.txbuf);
@ -2271,3 +2089,26 @@ void ipstack_ipconfig_get(struct ipstack *s, ip4 *ip, ip4 *mask, ip4 *gw)
*gw = s->ipconf.gw;
}
static uint32_t atou(const char *s)
{
uint32_t ret = 0;
while (*s >= '0' && *s <= '9') {
ret = ret * 10 + (*s - '0');
s++;
}
return ret;
}
ip4 atoip4(const char *ip)
{
ip4 ret = 0;
int i = 0;
int j = 0;
for (i = 0; i < 4; i++) {
ret |= (atou(ip + j) << (24 - i * 8));
while (ip[j] != '.' && ip[j] != '\0') j++;
if (ip[j] == '\0') break;
j++;
}
return ret;
}

View file

@ -1,412 +0,0 @@
/* HTTP 1.1 server
* (c) Danielinux 2024 <root@danielinux.net>
* This code is licensed under the GPLv3 license.
*/
#include "femtotcp.h"
#include "httpd.h"
static const char *http_status_text(int status_code) {
switch (status_code) {
case HTTP_STATUS_OK:
return "OK";
case HTTP_STATUS_BAD_REQUEST:
return "Bad Request";
case HTTP_STATUS_NOT_FOUND:
return "Not Found";
case HTTP_STATUS_TEAPOT:
return "I'm a teapot";
case HTTP_STATUS_TOO_MANY_REQUESTS:
return "Too Many Requests";
case HTTP_STATUS_INTERNAL_SERVER_ERROR:
return "Internal Server Error";
case HTTP_STATUS_SERVICE_UNAVAILABLE:
return "Service Unavailable";
default:
return "Unknown";
}
}
/*
static struct http_client *http_client_find(struct httpd *httpd, int sd) {
for (int i = 0; i < HTTPD_MAX_CLIENTS; i++) {
if (httpd->clients[i].client_sd == sd) {
return &httpd->clients[i];
}
}
return NULL;
}
*/
int httpd_register_handler(struct httpd *httpd, const char *path, int (*handler)(struct httpd *httpd, struct http_client *hc, struct http_request *req)) {
for (int i = 0; i < HTTPD_MAX_URLS; i++) {
if (httpd->urls[i].handler == NULL) {
strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN);
httpd->urls[i].handler = handler;
return 0;
}
}
return -1;
}
int httpd_register_static_page(struct httpd *httpd, const char *path, const char *content) {
for (int i = 0; i < HTTPD_MAX_URLS; i++) {
if (httpd->urls[i].handler == NULL) {
strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN);
httpd->urls[i].handler = NULL;
httpd->urls[i].static_content = content;
return 0;
}
}
return -1;
}
static struct http_url *http_find_url(struct httpd *httpd, const char *path) {
for (int i = 0; i < HTTPD_MAX_URLS; i++) {
if (strcmp(httpd->urls[i].path, path) == 0) {
return &httpd->urls[i];
}
}
return NULL;
}
void http_send_response_headers(struct http_client *hc, int status_code, const char *status_text, const char *content_type, size_t content_length)
{
char txt_response[HTTP_TX_BUF_LEN];
memset(txt_response, 0, sizeof(txt_response));
if (!hc) return;
/* If content_lenght is 0, assume chunked encoding */
if (content_length == 0) {
snprintf(txt_response, sizeof(txt_response), "HTTP/1.1 %d %s\r\n"
"Content-Type: %s\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n",
status_code, status_text, content_type);
} else {
snprintf(txt_response, sizeof(txt_response), "HTTP/1.1 %d %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %lu\r\n"
"\r\n",
status_code, status_text, content_type, content_length);
}
if (hc->ssl) {
wolfSSL_write(hc->ssl, txt_response, strlen(txt_response));
} else {
ft_send(hc->httpd->ipstack, hc->client_sd, txt_response, strlen(txt_response), 0);
}
}
void http_send_response_body(struct http_client *hc, const void *body, size_t len) {
if (!hc) return;
if (hc->ssl) {
wolfSSL_write(hc->ssl, body, len);
} else {
ft_send(hc->httpd->ipstack, hc->client_sd, body, len, 0);
}
}
void http_send_response_chunk(struct http_client *hc, const void *chunk, size_t len) {
char txt_chunk[8];
memset(txt_chunk, 0, sizeof(txt_chunk));
if (!hc) return;
snprintf(txt_chunk, sizeof(txt_chunk), "%lx\r\n", len);
if (hc->ssl) {
wolfSSL_write(hc->ssl, txt_chunk, strlen(txt_chunk));
wolfSSL_write(hc->ssl, chunk, len);
wolfSSL_write(hc->ssl, "\r\n", 2);
} else {
struct ipstack *s = hc->httpd->ipstack;
ft_send(s, hc->client_sd, txt_chunk, strlen(txt_chunk), 0);
ft_send(s, hc->client_sd, chunk, len, 0);
ft_send(s, hc->client_sd, "\r\n", 2, 0);
}
}
void http_send_response_chunk_end(struct http_client *hc) {
if (!hc) return;
if (hc->ssl) {
wolfSSL_write(hc->ssl, "0\r\n\r\n", 5);
} else {
ft_send(hc->httpd->ipstack, hc->client_sd, "0\r\n\r\n", 5, 0);
}
}
void http_send_200_OK(struct http_client *hc) {
http_send_response_headers(hc, HTTP_STATUS_OK,
http_status_text(HTTP_STATUS_OK), "text/plain", 0);
}
void http_send_500_server_error(struct http_client *hc) {
http_send_response_headers(hc, HTTP_STATUS_INTERNAL_SERVER_ERROR,
http_status_text(HTTP_STATUS_INTERNAL_SERVER_ERROR), "text/plain", 0);
}
void http_send_503_service_unavailable(struct http_client *hc) {
http_send_response_headers(hc, HTTP_STATUS_SERVICE_UNAVAILABLE,
http_status_text(HTTP_STATUS_SERVICE_UNAVAILABLE), "text/plain", 0);
}
void http_send_418_teapot(struct http_client *hc) {
http_send_response_headers(hc, HTTP_STATUS_TEAPOT,
http_status_text(HTTP_STATUS_TEAPOT), "text/plain", 0);
}
int http_url_decode(char *buf, size_t len) {
char *p = buf;
char *q;
while (p < buf + len) {
q = strchr(p, '%');
if (!q) {
break;
}
if (q + 2 >= buf + len) {
break;
}
*q = (char) strtol(q + 1, NULL, 16);
memmove(q + 1, q + 3, len - (q + 3 - buf));
len -= 2;
}
return len;
}
int http_url_encode(char *buf, size_t len, size_t max_len) {
char *p = buf;
char *q;
while (p < buf + len) {
q = strchr(p, ' ');
if (!q) {
break;
}
if (len + 2 >= max_len) {
return -1; /* Not enough space */
}
memmove(q + 3, q + 1, len - (q + 1 - buf));
*q = '%';
*(q + 1) = '2';
*(q + 2) = '0';
len += 2;
}
q[len] = '\0';
return len;
}
static int parse_http_request(struct http_client *hc, uint8_t *buf, size_t len) {
char *p = (char *) buf;
char *end = p + len;
char *q;
size_t n;
int ret;
struct http_request req;
struct http_url *url = NULL;
memset(&req, 0, sizeof(struct http_request));
http_url_decode(p, len); /* Decode can be done in place */
if (len < 4) goto bad_request;
/* Parse the request line */
q = strchr(p, ' ');
if (!q) goto bad_request;
n = q - p;
if (n >= sizeof(req.method)) goto bad_request;
memcpy(req.method, p, n);
req.method[n] = '\0';
p = q + 1;
q = strchr(p, ' ');
if (!q) goto bad_request;
n = q - p;
if (n >= sizeof(req.path)) goto bad_request;
memcpy(req.path, p, n);
req.path[n] = '\0';
p = q + 1;
q = strchr(p, '\r');
if (!q) goto bad_request;
n = q - p;
if (n >= sizeof(req.query)) goto bad_request;
memcpy(req.query, p, n);
req.query[n] = '\0';
p = q + 2;
/* Parse the headers */
while (p < end) {
q = strstr(p, "\r\n");
if (!q) goto bad_request;
n = q - p;
if (n == 0) {
break;
}
if (n >= sizeof(req.headers)) goto bad_request;
memcpy(req.headers, p, n);
req.headers[n] = '\0';
p = q + 2;
}
/* Parse the body */
if (p < end) {
n = end - p;
if (n >= sizeof(req.body)) {
return -1;
}
memcpy(req.body, p, n);
req.body[n] = '\0';
req.body_len = n;
}
if ((strcmp(req.method, "GET") != 0) && (strcmp(req.method, "POST") != 0))
goto bad_request;
url = http_find_url(hc->httpd, req.path);
if (!url) goto not_found;
if ((url->handler == NULL) && (url->static_content == NULL))
goto service_unavailable;
if (url->handler == NULL) {
http_send_response_headers(hc, HTTP_STATUS_OK, http_status_text(HTTP_STATUS_OK), "text/html", strlen(url->static_content));
http_send_response_body(hc, url->static_content, strlen(url->static_content));
ret = 0;
} else {
ret = url->handler(hc->httpd, hc, &req);
}
return ret;
bad_request:
http_send_response_headers(hc, HTTP_STATUS_BAD_REQUEST, http_status_text(HTTP_STATUS_BAD_REQUEST), "text/plain", 0);
return -1;
not_found:
http_send_response_headers(hc, HTTP_STATUS_NOT_FOUND, http_status_text(HTTP_STATUS_NOT_FOUND), "text/plain", 0);
return -1;
service_unavailable:
http_send_response_headers(hc, HTTP_STATUS_SERVICE_UNAVAILABLE, http_status_text(HTTP_STATUS_SERVICE_UNAVAILABLE), "text/plain", 0);
return -1;
}
static void http_recv_cb(int sd, uint16_t event, void *arg) {
struct http_client *hc = (struct http_client *) arg;
int parse_r;
uint8_t buf[HTTP_RECV_BUF_LEN];
int ret;
if (!hc) return;
(void) event;
if (hc->ssl) {
ret = wolfSSL_read(hc->ssl, buf, sizeof(buf));
if (ret < 0) {
if (wolfSSL_get_error(hc->ssl, ret) == WOLFSSL_ERROR_WANT_READ) {
return;
} else {
goto fail_close;
}
}
} else {
ret = ft_recv(hc->httpd->ipstack, sd, buf, sizeof(buf), 0);
if (ret == -11)
return;
}
if (ret <= 0)
goto fail_close;
parse_r = parse_http_request(hc, buf, ret);
if (parse_r < 0)
goto fail_close;
return;
fail_close:
if (hc->ssl) {
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
}
ft_close(hc->httpd->ipstack, sd);
hc->client_sd = 0;
}
static void http_accept_cb(int sd, uint16_t event, void *arg) {
struct httpd *httpd = (struct httpd *) arg;
struct ipstack_sockaddr_in addr;
socklen_t addr_len = sizeof(struct ipstack_sockaddr_in);
int client_sd = ft_accept(httpd->ipstack, sd, (struct ipstack_sockaddr *) &addr, &addr_len);
if (client_sd < 0) {
return;
}
(void) event;
for (int i = 0; i < HTTPD_MAX_CLIENTS; i++) {
if (httpd->clients[i].client_sd == 0) {
httpd->clients[i].client_sd = client_sd;
httpd->clients[i].httpd = httpd;
memcpy(&httpd->clients[i].addr, &addr, sizeof(addr));
if (httpd->ssl_ctx) {
httpd->clients[i].ssl = wolfSSL_new(httpd->ssl_ctx);
if (httpd->clients[i].ssl) {
wolfSSL_SetIO_FT(httpd->clients[i].ssl, client_sd);
} else {
/* Failed to create SSL object */
ft_close(httpd->ipstack, client_sd);
httpd->clients[i].client_sd = 0;
return;
}
}
ipstack_register_callback(httpd->ipstack, client_sd, http_recv_cb, &httpd->clients[i]);
break;
}
}
}
/* Extra utility to extract requests arguments */
int httpd_get_request_arg(struct http_request *req, const char *name, char *value, size_t value_len) {
char *p;
char *q;
char *sep;
if (strcmp(req->method, "GET") == 0)
p = req->query;
else if (strcmp(req->method, "POST") == 0)
p = req->body;
else
return -1; // Unsupported method
while (*p) {
q = strchr(p, '&');
if (!q) {
q = p + strlen(p); // End of key-value pair
}
sep = strchr(p, '=');
if (sep && sep < q) { // Ensure '=' is within bounds
size_t key_len = sep - p;
if (key_len == strlen(name) && strncmp(p, name, key_len) == 0) {
size_t value_len_actual = q - (sep + 1);
if (value_len_actual >= value_len) {
return -1; // Insufficient buffer size
}
memcpy(value, sep + 1, value_len_actual);
value[value_len_actual] = '\0';
return 0;
}
}
p = q + 1; // Move to next key-value pair
}
return -1; // Key not found
}
int httpd_init(struct httpd *httpd, struct ipstack *s, uint16_t port, void *ssl_ctx) {
struct ipstack_sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (!httpd) {
return -1;
}
memset(httpd, 0, sizeof(struct httpd));
httpd->ipstack = s;
httpd->port = port;
httpd->listen_sd = ft_socket(s, AF_INET, SOCK_STREAM, 0);
if (httpd->listen_sd < 0) {
return -1;
}
if (ft_bind(s, httpd->listen_sd, (struct ipstack_sockaddr *) &addr, sizeof(addr)) < 0) {
return -1;
}
if (ft_listen(s, httpd->listen_sd, 5) < 0) {
return -1;
}
if (ssl_ctx) {
httpd->ssl_ctx = (WOLFSSL_CTX *) ssl_ctx;
wolfSSL_SetIO_FT_CTX(httpd->ssl_ctx, httpd->ipstack);
}
ipstack_register_callback(s, httpd->listen_sd, http_accept_cb, httpd);
return 0;
}

View file

@ -1,85 +0,0 @@
#ifndef FEMTO_HTTPD_H
#define FEMTO_HTTPD_H
#ifdef WOLFSSL_USER_SETTINGS
#include <user_settings.h>
#else
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
#include <stdint.h>
#define HTTP_METHOD_LEN 8
#define HTTP_PATH_LEN 128
#define HTTP_QUERY_LEN 256
#define HTTP_HEADERS_LEN 512
#define HTTP_BODY_LEN 1024
/* Config */
#define HTTP_RECV_BUF_LEN 1460
#define HTTP_TX_BUF_LEN 1460
#define HTTPD_MAX_URLS 16
#define HTTPD_MAX_CLIENTS 4
/* Constants for HTTP status codes */
#define HTTP_STATUS_OK 200
#define HTTP_STATUS_BAD_REQUEST 400
#define HTTP_STATUS_NOT_FOUND 404
#define HTTP_STATUS_TEAPOT 418
#define HTTP_STATUS_TOO_MANY_REQUESTS 429
#define HTTP_STATUS_INTERNAL_SERVER_ERROR 500
#define HTTP_STATUS_SERVICE_UNAVAILABLE 503
struct httpd;
struct http_request {
char method[HTTP_METHOD_LEN]; // "GET", "POST", etc.
char path[HTTP_PATH_LEN]; // URL path
char query[HTTP_QUERY_LEN]; // URL query string (for GET requests)
char headers[HTTP_HEADERS_LEN]; // HTTP headers
char body[HTTP_BODY_LEN]; // HTTP body (for POST requests)
size_t body_len;
};
struct http_client {
struct httpd *httpd;
int client_sd;
struct ipstack_sockaddr_in addr;
WOLFSSL *ssl; /* NULL if not using SSL */
};
struct http_url {
char path[HTTP_PATH_LEN];
int (*handler)(struct httpd *httpd, struct http_client *hc, struct http_request *req);
const char *static_content;
};
struct httpd {
struct http_url urls[HTTPD_MAX_URLS];
struct http_client clients[HTTPD_MAX_CLIENTS];
struct ipstack *ipstack;
int listen_sd;
uint16_t port;
WOLFSSL_CTX *ssl_ctx;
};
int httpd_init(struct httpd *httpd, struct ipstack *s, uint16_t port, void *ssl_ctx);
int httpd_register_handler(struct httpd *httpd, const char *path, int (*handler)(struct httpd *httpd, struct http_client *hc, struct http_request *req));
int httpd_register_static_page(struct httpd *httpd, const char *path, const char *content);
int httpd_get_request_arg(struct http_request *req, const char *name, char *value, size_t value_len);
void http_send_response_headers(struct http_client *hc, int status_code, const char *status_text, const char *content_type, size_t content_length);
void http_send_response_body(struct http_client *hc, const void *body, size_t len);
void http_send_response_chunk(struct http_client *hc, const void *chunk, size_t len);
void http_send_response_chunk_end(struct http_client *hc);
void http_send_200_OK(struct http_client *hc);
void http_send_500_server_error(struct http_client *hc);
void http_send_503_service_unavailable(struct http_client *hc);
void http_send_418_teapot(struct http_client *hc);
int http_url_decode(char *buf, size_t len);
int http_url_encode(char *buf, size_t len, size_t max_len);
#endif

View file

@ -25,7 +25,7 @@ void print_buffer(uint8_t *buf, int len)
printf("\n");
}
static int tap_poll(struct ll *ll, void *buf, uint32_t len)
static int tap_poll(struct ll *ll, void *buf, int len)
{
struct pollfd pfd;
(void)ll;
@ -43,7 +43,7 @@ static int tap_poll(struct ll *ll, void *buf, uint32_t len)
return read(tap_fd, buf, len);
}
static int tap_send(struct ll *ll, void *buf, uint32_t len)
static int tap_send(struct ll *ll, void *buf, int len)
{
(void)ll;
//print_buffer(buf, len);

View file

@ -1,574 +0,0 @@
/* POSIX socket calls wrapper for femtoTCP */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <pthread.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/time.h>
#include <poll.h>
#include <string.h>
#define FEMTO_POSIX
#include "config.h"
#include "femtotcp.h"
static __thread int in_the_stack = 1;
static struct ipstack *IPSTACK = NULL;
pthread_mutex_t ipstack_mutex;
/* host_ functions are the original functions from the libc */
static int (*host_socket ) (int domain, int type, int protocol) = NULL;
static int (*host_bind ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
static int (*host_connect ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
static int (*host_accept ) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
static int (*host_listen ) (int sockfd, int backlog);
static ssize_t (*host_recvfrom) (int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen);
static ssize_t (*host_recv ) (int sockfd, void *buf, size_t len, int flags);
static ssize_t (*host_read ) (int sockfd, void *buf, size_t len);
static ssize_t (*host_sendto ) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen);
static ssize_t (*host_send ) (int sockfd, const void *buf, size_t len, int flags);
static ssize_t (*host_write ) (int sockfd, const void *buf, size_t len);
static int (*host_close ) (int sockfd);
static int (*host_setsockopt) (int sockfd, int level, int optname, const void *optval, socklen_t optlen);
static int (*host_getsockopt) (int sockfd, int level, int optname, void *optval, socklen_t *optlen);
static int (*host_getsockname) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
static int (*host_getpeername) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
static int (*host_poll) (struct pollfd *fds, nfds_t nfds, int timeout);
static int (*host_select) (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
static int (*host_fcntl) (int fd, int cmd, ...);
#define swap_socketcall(call, name) \
{ \
const char *msg; \
if (host_##call == NULL) { \
*(void **)(&host_##call) = dlsym(RTLD_NEXT, name); \
if ((msg = dlerror()) != NULL) \
fprintf (stderr, "%s: dlsym(%s): %s\n", "femtoTCP", name, msg); \
} \
}
#define conditional_steal_call(call, fd, ...) \
if(in_the_stack) { \
return host_##call(fd, ## __VA_ARGS__); \
} else { \
pthread_mutex_lock(&ipstack_mutex); \
if ((fd & (MARK_TCP_SOCKET | MARK_UDP_SOCKET)) != 0) { \
int __femto_retval = ft_##call(IPSTACK, fd, ## __VA_ARGS__); \
if (__femto_retval < 0) { \
errno = __femto_retval; \
pthread_mutex_unlock(&ipstack_mutex); \
return -1; \
} \
pthread_mutex_unlock(&ipstack_mutex); \
return __femto_retval; \
}else { \
pthread_mutex_unlock(&ipstack_mutex); \
return host_##call(fd, ## __VA_ARGS__); \
} \
}
#define conditional_steal_blocking_call(call, fd, ...) \
if(in_the_stack) { \
return host_##call(fd, ## __VA_ARGS__); \
} else { \
pthread_mutex_lock(&ipstack_mutex); \
if ((fd & (MARK_TCP_SOCKET | MARK_UDP_SOCKET)) != 0) { \
int __femto_retval; \
do { \
__femto_retval = ft_##call(IPSTACK, fd, ## __VA_ARGS__); \
if (__femto_retval == -11) { \
usleep(1000); \
} \
} while (__femto_retval == -11); \
if (__femto_retval < 0) { \
errno = __femto_retval; \
pthread_mutex_unlock(&ipstack_mutex); \
return -1; \
} \
pthread_mutex_unlock(&ipstack_mutex); \
return __femto_retval; \
}else { \
pthread_mutex_unlock(&ipstack_mutex); \
return host_##call(fd, ## __VA_ARGS__); \
} \
}
int ft_setsockopt(struct ipstack *ipstack, int fd, int level, int optname, const void *optval, socklen_t optlen) {
printf("Intercepted setsockopt\n");
(void)ipstack;
(void)fd;
(void)level;
(void)optname;
(void)optval;
(void)optlen;
return 0;
}
int ft_getsockopt(struct ipstack *ipstack, int fd, int level, int optname, void *optval, socklen_t *optlen) {
printf("Intercepted getsockopt\n");
(void)ipstack;
(void)fd;
(void)level;
(void)optname;
(void)optval;
(void)optlen;
return 0;
}
int ft_fcntl(struct ipstack *ipstack, int fd, int cmd, int arg) {
printf("Intercepted fcntl\n");
(void)ipstack;
(void)fd;
(void)cmd;
(void)arg;
return 0;
}
int fcntl(int fd, int cmd, ...) {
va_list ap;
int arg;
va_start(ap, cmd);
arg = va_arg(ap, int);
va_end(ap);
int ret;
if (in_the_stack) {
return host_fcntl(fd, cmd, arg);
} else {
pthread_mutex_lock(&ipstack_mutex);
ret = ft_fcntl(IPSTACK, fd, cmd, arg);
pthread_mutex_unlock(&ipstack_mutex);
return ret;
}
}
struct bsd_poll_helper {
int fd; /* Original fd */
int events; /* Original events */
int pipefds[2]; /* Pipe for triggering events */
};
/* Static arrays for poll helpers */
static struct bsd_poll_helper tcp_pollers[MAX_TCPSOCKETS] = {{0}};
static struct bsd_poll_helper udp_pollers[MAX_UDPSOCKETS] = {{0}};
void poller_callback(int fd, uint16_t event, void *arg)
{
struct bsd_poll_helper *poller;
char c;
(void)arg;
if ((fd & MARK_TCP_SOCKET) != 0)
poller = &tcp_pollers[fd & ~MARK_TCP_SOCKET];
else if ((fd & MARK_UDP_SOCKET) != 0)
poller = &udp_pollers[fd & ~MARK_UDP_SOCKET];
else
return;
if (poller->fd != fd)
return;
if (event & CB_EVENT_READABLE)
c = 'r';
else if (event & CB_EVENT_WRITABLE)
c = 'w';
else if (event & CB_EVENT_CLOSED)
c = 'h';
else
return;
write(poller->pipefds[1], &c, 1);
}
int ft_poll(struct ipstack *ipstack, struct pollfd *fds, nfds_t nfds, int timeout) {
nfds_t i;
int fd;
int ret;
int miss = 0;
if (in_the_stack) {
return host_poll(fds, nfds, timeout);
}
memset(tcp_pollers, 0, sizeof(tcp_pollers));
memset(udp_pollers, 0, sizeof(udp_pollers));
for (i = 0; i < nfds; i++) {
struct bsd_poll_helper *poller = NULL;
fd = fds[i].fd;
if ((fd & MARK_TCP_SOCKET) != 0)
poller = &tcp_pollers[fd & ~MARK_TCP_SOCKET];
else if ((fd & MARK_UDP_SOCKET) != 0)
poller = &udp_pollers[fd & ~MARK_UDP_SOCKET];
else
continue;
if (pipe(poller->pipefds) < 0) {
perror("pipe");
return -1;
}
poller->fd = fd;
poller->events = fds[i].events;
/* Replace the original fd with the read end of the pipe */
fds[i].fd = poller->pipefds[0];
fds[i].events = POLLIN;
fds[i].revents = 0;
/* Assign the callback */
ipstack_register_callback(ipstack, fd, poller_callback, ipstack);
}
/* Call the original poll */
repeat:
miss = 0;
pthread_mutex_unlock(&ipstack_mutex);
ret = host_poll(fds, nfds, timeout);
pthread_mutex_lock(&ipstack_mutex);
if (ret <= 0)
return ret;
for (i = 0; i < nfds; i++) {
struct bsd_poll_helper *poller = NULL;
int j;
int fd = fds[i].fd;
char c = 0;
for (j = 0; j < MAX_TCPSOCKETS; j++) {
if (tcp_pollers[j].fd == 0)
continue;
if (tcp_pollers[j].pipefds[0] == fd) {
poller = &tcp_pollers[j];
break;
}
}
if (!poller) {
for (j = 0; j < MAX_UDPSOCKETS; j++) {
if (udp_pollers[j].fd == 0)
continue;
if (udp_pollers[j].pipefds[0] == fd) {
poller = &udp_pollers[j];
break;
}
}
}
if (poller) {
if ((fds[i].revents & POLLIN) != 0) {
fds[i].revents = 0;
host_read(poller->pipefds[0], &c, 1);
switch(c) {
case 'r':
fds[i].revents |= POLLIN;
break;
case 'w':
fds[i].revents |= POLLOUT;
break;
case 'e':
fds[i].revents |= POLLERR;
break;
case 'h':
fds[i].revents |= POLLHUP;
break;
}
if ((fds[i].revents != 0) && (fds[i].revents & (poller->events | POLLHUP | POLLERR)) == 0) {
miss++;
ret--;
continue;
}
fds[i].revents &= (POLLHUP | POLLERR | poller->events);
} else {
fds[i].revents = 0;
}
fds[i].fd = poller->fd;
fds[i].events = poller->events;
host_close(poller->pipefds[0]);
host_close(poller->pipefds[1]);
poller->fd = 0;
ipstack_register_callback(ipstack, poller->fd, NULL, NULL);
}
}
if ((miss != 0) && (ret == 0))
goto repeat;
return ret;
}
int ft_select(struct ipstack *ipstack, int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) {
int i;
int maxfd;
int ret;
fd_set readfds_local;
/* Assume MARK_TCP_SOCKET < MARK_UDP_SOCKET */
if (nfds < MARK_TCP_SOCKET + 1) {
return host_select(nfds, readfds, writefds, exceptfds, timeout);
}
memset(tcp_pollers, 0, sizeof(tcp_pollers));
memset(udp_pollers, 0, sizeof(udp_pollers));
for (i = 0; (i < MARK_TCP_SOCKET) && (i < nfds); i++) {
if ((readfds && FD_ISSET(i, readfds)) ||
(writefds && FD_ISSET(i, writefds)) ||
(exceptfds && FD_ISSET(i, exceptfds))) {
maxfd = i;
}
}
/* At this point, we do need a fd_set to read from pipes */
if (!readfds) {
FD_ZERO(&readfds_local);
readfds = &readfds_local;
}
for (i = MARK_TCP_SOCKET; i < nfds && i < (MARK_TCP_SOCKET | MAX_TCPSOCKETS); i++) {
int tcp_pos = i & (~MARK_TCP_SOCKET);
if ((readfds && (FD_ISSET(i, readfds))) || (writefds && (FD_ISSET(i, writefds))) || (exceptfds && (FD_ISSET(i, exceptfds)))) {
if (pipe(tcp_pollers[tcp_pos].pipefds) < 0)
return -1;
tcp_pollers[tcp_pos].fd = i;
tcp_pollers[tcp_pos].events = 0;
ipstack_register_callback(ipstack, i, poller_callback, ipstack);
if (readfds && (FD_ISSET(i, readfds))) {
tcp_pollers[tcp_pos].events |= POLLIN;
FD_CLR(i, readfds);
FD_SET(tcp_pollers[tcp_pos].pipefds[0], readfds);
}
if (writefds && (FD_ISSET(i, writefds))) {
tcp_pollers[tcp_pos].events |= POLLOUT;
FD_CLR(i, writefds);
FD_SET(tcp_pollers[tcp_pos].pipefds[0], writefds);
}
if (exceptfds && (FD_ISSET(i, exceptfds))) {
tcp_pollers[tcp_pos].events |= POLLERR | POLLHUP;
FD_CLR(i, exceptfds);
FD_SET(tcp_pollers[tcp_pos].pipefds[0], exceptfds);
}
if (maxfd < tcp_pollers[tcp_pos].pipefds[0]) {
maxfd = tcp_pollers[tcp_pos].pipefds[0];
}
} else {
}
}
for (i = MARK_UDP_SOCKET; i < nfds && i < (MARK_UDP_SOCKET | MAX_UDPSOCKETS); i++) {
int udp_pos = i & (~MARK_UDP_SOCKET);
if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) {
pipe(udp_pollers[udp_pos].pipefds);
udp_pollers[udp_pos].fd = i;
udp_pollers[udp_pos].events = 0;
ipstack_register_callback(ipstack, i, poller_callback, ipstack);
if (readfds && FD_ISSET(i, readfds)) {
udp_pollers[udp_pos].events |= POLLIN;
FD_CLR(i, readfds);
FD_SET(udp_pollers[udp_pos].pipefds[0], readfds);
}
if (writefds && FD_ISSET(i, writefds)) {
udp_pollers[udp_pos].events |= POLLOUT;
FD_CLR(i, writefds);
FD_SET(udp_pollers[udp_pos].pipefds[0], writefds);
}
if (exceptfds && FD_ISSET(i, exceptfds)) {
udp_pollers[udp_pos].events |= POLLERR | POLLHUP;
FD_CLR(i, exceptfds);
FD_SET(udp_pollers[udp_pos].pipefds[0], exceptfds);
}
if (maxfd < udp_pollers[udp_pos].pipefds[0]) {
maxfd = udp_pollers[udp_pos].pipefds[0];
}
}
}
/* Call the original select */
pthread_mutex_unlock(&ipstack_mutex);
ret = host_select(maxfd + 1, readfds, writefds, exceptfds, timeout);
pthread_mutex_lock(&ipstack_mutex);
if (ret <= 0) {
return ret;
}
for (i = 0; i < MAX_TCPSOCKETS; i++) {
if (tcp_pollers[i].fd == 0) {
continue;
}
if (FD_ISSET(tcp_pollers[i].pipefds[0], readfds)) {
char c;
host_read(tcp_pollers[i].pipefds[0], &c, 1);
if (readfds && (c == 'r')) {
FD_SET(tcp_pollers[i].fd, readfds);
} else if (writefds && (c == 'w')) {
FD_SET(tcp_pollers[i].fd, writefds);
} else if (exceptfds && (c == 'e')) {
FD_SET(tcp_pollers[i].fd, exceptfds);
}
}
ipstack_register_callback(ipstack, tcp_pollers[i].fd, NULL, NULL);
host_close(tcp_pollers[i].pipefds[0]);
host_close(tcp_pollers[i].pipefds[1]);
tcp_pollers[i].fd = 0;
}
for (i = 0; i < MAX_UDPSOCKETS; i++) {
if (udp_pollers[i].fd == 0) {
continue;
}
if (FD_ISSET(udp_pollers[i].pipefds[0], readfds)) {
char c;
read(udp_pollers[i].pipefds[0], &c, 1);
if (readfds && (c == 'r')) {
FD_SET(udp_pollers[i].fd, readfds);
} else if (writefds && (c == 'w')) {
FD_SET(udp_pollers[i].fd, writefds);
} else if (exceptfds && (c == 'e')) {
FD_SET(udp_pollers[i].fd, exceptfds);
}
}
host_close(udp_pollers[i].pipefds[0]);
host_close(udp_pollers[i].pipefds[1]);
ipstack_register_callback(ipstack, tcp_pollers[i].fd, NULL, NULL);
udp_pollers[i].fd = 0;
}
return ret;
}
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) {
int ret;
if (in_the_stack) {
return host_select(nfds, readfds, writefds, exceptfds, timeout);
} else {
pthread_mutex_lock(&ipstack_mutex);
ret = ft_select(IPSTACK, nfds, readfds, writefds, exceptfds, timeout);
pthread_mutex_unlock(&ipstack_mutex);
return ret;
}
}
int socket(int domain, int type, int protocol) {
if (in_the_stack) {
return host_socket(domain, type, protocol);
} else {
return ft_socket(IPSTACK, domain, type, protocol);
}
}
int listen(int sockfd, int backlog) {
conditional_steal_call(listen, sockfd, backlog);
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_call(bind, sockfd, addr, addrlen);
}
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
conditional_steal_call(getsockopt, sockfd, level, optname, optval, optlen);
}
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_call(getpeername, sockfd, addr, addrlen);
}
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_call(getsockname, sockfd, addr, addrlen);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
conditional_steal_call(setsockopt, sockfd, level, optname, optval, optlen);
}
int close(int sockfd) {
conditional_steal_call(close, sockfd);
}
/* Blocking calls */
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_blocking_call(accept, sockfd, addr, addrlen);
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_blocking_call(connect, sockfd, addr, addrlen);
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_blocking_call(recvfrom, sockfd, buf, len, flags, addr, addrlen);
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
conditional_steal_blocking_call(recv, sockfd, buf, len, flags);
}
ssize_t read(int sockfd, void *buf, size_t len) {
conditional_steal_blocking_call(read, sockfd, buf, len);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_blocking_call(sendto, sockfd, buf, len, flags, addr, addrlen);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
conditional_steal_blocking_call(send, sockfd, buf, len, flags);
}
ssize_t write(int sockfd, const void *buf, size_t len) {
conditional_steal_blocking_call(write, sockfd, buf, len);
}
int poll(struct pollfd *fds, nfds_t nfds, int timeout) {
int ret;
if (in_the_stack) {
return host_poll(fds, nfds, timeout);
} else {
pthread_mutex_lock(&ipstack_mutex);
ret = ft_poll(IPSTACK, fds, nfds, timeout);
pthread_mutex_unlock(&ipstack_mutex);
return ret;
}
}
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
void *ft_posix_ip_loop(void *arg) {
struct ipstack *ipstack = (struct ipstack *)arg;
uint32_t ms_next;
struct timeval tv;
while (1) {
pthread_mutex_lock(&ipstack_mutex);
gettimeofday(&tv, NULL);
ms_next = ipstack_poll(ipstack, tv.tv_sec * 1000 + tv.tv_usec / 1000);
pthread_mutex_unlock(&ipstack_mutex);
usleep(ms_next * 1000);
in_the_stack = 1;
}
return NULL;
}
void __attribute__((constructor)) init_femto_posix() {
struct in_addr linux_ip;
struct ll *tapdev;
pthread_t ipstack_thread;
if (IPSTACK)
return;
inet_aton(LINUX_IP, &linux_ip);
swap_socketcall(socket, "socket");
swap_socketcall(bind, "bind");
swap_socketcall(listen, "listen");
swap_socketcall(accept, "accept");
swap_socketcall(connect, "connect");
swap_socketcall(sendto, "sendto");
swap_socketcall(recvfrom, "recvfrom");
swap_socketcall(recv, "recv");
swap_socketcall(send, "send");
swap_socketcall(close, "close");
swap_socketcall(write, "write");
swap_socketcall(read, "read");
swap_socketcall(getsockname, "getsockname");
swap_socketcall(getpeername, "getpeername");
swap_socketcall(setsockopt, "getaddrinfo");
swap_socketcall(getsockopt, "freeaddrinfo");
swap_socketcall(poll, "poll");
swap_socketcall(select, "select");
swap_socketcall(fcntl, "fcntl");
pthread_mutex_init(&ipstack_mutex, NULL);
ipstack_init_static(&IPSTACK);
tapdev = ipstack_getdev(IPSTACK);
if (tap_init(tapdev, "femt0", linux_ip.s_addr) < 0) {
perror("tap init");
}
ipstack_ipconfig_set(IPSTACK, atoip4(FEMTOTCP_IP), atoip4("255.255.255.0"),
atoip4(LINUX_IP));
printf("IP: manually configured - %s\n", FEMTOTCP_IP);
sleep(1);
pthread_create(&ipstack_thread, NULL, ft_posix_ip_loop, IPSTACK);
in_the_stack = 0;
}

View file

@ -1,53 +0,0 @@
cmake_minimum_required(VERSION 3.17)
#include(${CMAKE_CURRENT_LIST_DIR}/../../../hw/bsp/family_support.cmake)
include(${PICO_SDK_PATH}/lib/tinyusb/hw/bsp/family_support.cmake)
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
project(${PROJECT} C CXX ASM)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT})
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/main.c
${CMAKE_CURRENT_LIST_DIR}/src/rand.c
${CMAKE_CURRENT_LIST_DIR}/src/motd.c
${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/../../femtotcp.c
)
# Example include
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/../../..
)
# lib/networking sources
target_sources(${PROJECT} PUBLIC
)
# due to warnings from other net source, we need to prevent error from some of the warnings options
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(${PROJECT} PUBLIC
-Wno-error=null-dereference
-Wno-error=conversion
-Wno-error=sign-conversion
-Wno-error=sign-compare
-Wno-error=cast-align
)
elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
endif ()
target_link_libraries(${PROJECT} PUBLIC hardware_adc)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT} noos)

File diff suppressed because it is too large Load diff

View file

@ -1,29 +0,0 @@
## FemtoTCP demo on raspberry pi pico
### Instructions
- clone pico-sdk
`git clone https://github.com/raspberrypi/pico-sdk.git`
- run CMake from this directory, specitying the FAMILY and PICO_COMPILER variables. Also specify the path where you cloned the pico-sdk in the previous step.
cmake . -DPICO_SDK_PATH=/path/to/src/pico-sdk -DFAMILY=rp2040 -DPICO_COMPILER=arm-none-eabi-gcc
- run make
`make`
- copy the uf2 file to the pico
`cp raspberrypi-pico-usb-server.uf2 /media/$USER/RPI-RP2`
- Assign a static IP to the usb0 interface on the host machine
`sudo ip addr add usb0 192.168.7.1/24`
- Ping the pico board!
`ping 192.168.7.2`

View file

@ -1 +0,0 @@
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = (2 * 1024 * 1024)

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
#if defined(__ICCARM__)
#pragma pack(1)
#endif

View file

@ -1,75 +0,0 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
#ifndef __CC_H__
#define __CC_H__
//#include "cpu.h"
typedef int sys_prot_t;
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0)
#endif /* __CC_H__ */

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
#if defined(__ICCARM__)
#pragma pack()
#endif

View file

@ -1,267 +0,0 @@
/* FemtoTCP: Raspberry-pi pico port + example
*
* This file is part of femtoTCP
* (c) 2024 Daniele Lacamera <root@danielinux.net>
*
* FemtoTCP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* FemtoTCP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*
* *****
*
* Based on LwIP drivers for TinyUSB,
* Copyright (c) 2020 Peter Lawrence (MIT Licensed),
* which was also influenced by lrndis https://github.com/fetisov/lrndis
This appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS picks its preference
RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts
The MCU appears to the host as IP address 192.168.7.1.
Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers,
and likely their manufacturer has not tested such functionality. Some code workarounds could be tried:
The smartphone may only have an ECM driver, but refuse to automatically pick ECM (unlike the OSes above);
try modifying usb_descriptors.c so that CONFIG_ID_ECM is default.
The smartphone may be artificially picky about which Ethernet MAC address to recognize; if this happens,
try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00 (clearing bit 1).
*/
#include "bsp/board_api.h"
#include "tusb.h"
#include "config.h"
#include "femtotcp.h"
extern char MOTD[];
/* Our globals */
static struct ipstack *IPStack = NULL;
/* Two static buffers for RX frames from USB host */
uint8_t tusb_net_rxbuf[LINK_MTU][2];
uint8_t tusb_net_rxbuf_used[2] = {0, 0};
/* Two static buffers for TX frames to USB host */
uint8_t tusb_net_txbuf[LINK_MTU][2];
uint16_t tusb_net_txbuf_sz[2] = {0, 0};
/* Fixed mac-address for the raspberry side of the link.
* it is suggested that the first byte is 0x02 to indicate a link-local address
*/
uint8_t tud_network_mac_address[6] = {0x02, 0x02, 0x84, 0x6A, 0x96, 0x00};
/* ipstack_getrandom is a frontend to the ADC-based random number generator.
* See rand.c for more details.
*/
extern int custom_random_seed(unsigned char *seed, unsigned int size);
uint32_t ipstack_getrandom(void)
{
uint32_t seed;
custom_random_seed((unsigned char *)&seed, 4);
return seed;
}
/* ll_usb_send is the function that sends a frame to the USB host.
* It is called by the femtoTCP stack when a frame is ready to be sent.
* It will return the number of bytes sent, or 0 if the USB host is not ready.
*/
static int ll_usb_send(struct ll *dev, void *frame, uint32_t sz) {
uint16_t sz16 = (uint16_t)sz;
uint32_t i;
(void) dev;
board_led_on();
for (;;) {
if (!tud_ready()) {
return 0;
}
if (tud_network_can_xmit(sz16)) {
for (i = 0; i < 2; i++) {
if (tusb_net_txbuf_sz[i] == 0) {
memcpy(tusb_net_txbuf[i], frame, sz16);
tusb_net_txbuf_sz[i] = sz16;
tud_network_xmit(tusb_net_txbuf[i], tusb_net_txbuf_sz[i]);
board_led_on();
return (int)sz16;
}
}
return 0;
}
/* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */
tud_task();
}
}
/* This is the callback that TinyUSB calls when it is ready to send a frame.
* This is where the write operation is finalized.
*/
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
uint16_t ret = arg;
(void) ref;
(void) arg;
memcpy(dst, ref, arg);
if (ref == tusb_net_rxbuf[0])
tusb_net_txbuf_sz[0] = 0;
else if (ref == tusb_net_txbuf[1])
tusb_net_txbuf_sz[1] = 0;
board_led_off();
return ret;
}
/* This is the callback that TinyUSB calls when it is ready to receive a frame.
* This is where the read operation is initiated, the frame is copied to the
* static buffer, and the buffer is marked as used.
*/
static void tusb_net_push_rx(const uint8_t *src, uint16_t size) {
uint8_t *dst = NULL;
int i;
for (i = 0; i < 2; i++) {
if (!tusb_net_rxbuf_used[i]) {
dst = tusb_net_rxbuf[i];
break;
}
}
if (dst) {
memcpy(dst, src, size);
tusb_net_rxbuf_used[i] = 1;
board_led_on();
}
}
bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
tusb_net_push_rx(src, size);
return true;
}
/* This is the poll function of the femtoTCP device driver.
* It is called by the femtoTCP stack when it is ready to receive a frame.
* It will return the number of bytes received, or 0 if no frame is available.
*
* Frames copied in tusb_net_push_rx are processed here and sent to the stack.
*/
int ll_usb_poll(struct ll *dev, void *frame, uint32_t sz) {
int i;
(void) dev;
if (sz < 64)
return 0;
for (i = 0; i < 2; i++) {
if (tusb_net_rxbuf_used[i]) {
memcpy(frame, tusb_net_rxbuf[i], sz);
tusb_net_rxbuf_used[i] = 0;
board_led_off();
return (int)sz;
}
}
return 0;
}
void tud_network_init_cb(void)
{
}
/* Telnet server (telnetd) initialization */
static int tel_s = -1;
static int tel_c = -1;
static void telnet_cb(int fd, uint16_t event, void *arg)
{
struct ipstack_sockaddr_in addr;
uint32_t socklen = sizeof(addr);
(void)arg;
if ((fd == tel_s) && (event & CB_EVENT_READABLE) && (tel_c == -1)) {
char ipaddr[16];
char welcome_msg[32];
tel_c = ft_accept(IPStack, tel_s, (struct ipstack_sockaddr*)&addr, &socklen);
if (tel_c > 0) {
iptoa(ee32(addr.sin_addr.s_addr), ipaddr);
snprintf(welcome_msg, sizeof(welcome_msg), "Welcome %s!\n", ipaddr);
ft_write(IPStack, tel_c, MOTD, strlen(MOTD));
ft_write(IPStack, tel_c, welcome_msg, strlen(welcome_msg));
}
}
#if 0
else if ((fd == tel_c) && (event & CB_EVENT_READABLE )) {
int ret;
ret = ft_recv((struct ipstack *)arg, tel_c, buf, sizeof(buf), 0);
if (ret != -11) {
if (ret < 0) {
printf("Recv error: %d\n", ret);
ft_close((struct ipstack *)arg, tel_c);
} else if (ret == 0) {
printf("Client side closed the connection.\n");
ft_close((struct ipstack *)arg, tel_c);
printf("Server: Exiting.\n");
exit_ok = 1;
} else if (ret > 0) {
printf("recv: %d, echoing back\n", ret);
tot_recv += ret;
}
}
}
#endif
}
static void telnetd_init(void)
{
struct ipstack_sockaddr_in addr;
if (tel_s < 0)
tel_s = ft_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0);
ipstack_register_callback(IPStack, tel_s, telnet_cb, NULL);
addr.sin_family = AF_INET;
addr.sin_port = ee16(23);
addr.sin_addr.s_addr = 0;
ft_bind(IPStack, tel_s, (struct ipstack_sockaddr *)&addr, sizeof(addr));
ft_listen(IPStack, tel_s, 1);
}
int main(void)
{
struct ll *tusb_netdev;
/* initialize TinyUSB */
board_init();
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
if (board_init_after_tusb) {
board_init_after_tusb();
}
board_led_on();
ipstack_init_static(&IPStack);
tusb_netdev = ipstack_getdev(IPStack);
memcpy(tusb_netdev->mac, tud_network_mac_address, 6);
strcpy(tusb_netdev->ifname, "tusb");
tusb_netdev->poll = ll_usb_poll;
tusb_netdev->send = ll_usb_send;
/* set the IP address, netmask, and gateway */
/* 192.168.7.2/24, gateway 192.168.7.1 */
ipstack_ipconfig_set(IPStack, atoip4("192.168.7.2"),
atoip4("255.255.255.0"), atoip4("192.168.7.1"));
telnetd_init();
board_led_off();
while (1) {
tud_task();
ipstack_poll(IPStack, board_millis());
}
return 0;
}

View file

@ -1,13 +0,0 @@
const char MOTD[] =
" \n"
" /\\_/\\ \n"
" ( o.o )\n"
" > ^ < ⭐️ ⭐️ ⭐️ \n"
" \n"
" 🟪🟪🟪🟪 ⭐️ ⭐️ 🟪 🟪🟪🟪🟪🟪 🟪🟪🟪 🟪🟪🟪 \n"
"⭐️🟪 🟪🟪 🟪🟪🟪🟪🟪🟪 🟪🟪🟪🟪 🟪🟪 🟪 🟪 🟪 🟪 \n"
" 🟪🟪🟪 🟪🟪🟪🟪 🟪 🟪 🟪 🟪 🟪 🟪 🟪 ⭐️ 🟪 🟪🟪🟪 \n"
" 🟪 🟪 🟪 🟪 🟪 🟪 🟪 🟪 🟪 🟪 🟪 ⭐️ \n"
" 🟪 🟪🟪🟪 🟪 🟪 🟪 🟪🟪 🟪🟪 🟪 🟪🟪🟪 🟪 \n"
" ⭐️ by Danielinux \n"
"Best viewed in (80 x 30)\n";

View file

@ -1,74 +0,0 @@
/*
* (c) 2023 Daniele Lacamera <root@danielinux.net>
*
*
* FemtoTCP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* FemtoTCP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*
*/
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include <string.h>
#define IN3_PIN 29
#define IN0_PIN 28
#define IN1_PIN 27
#define IN2_PIN 26
const uint32_t IN[4] = {IN0_PIN, IN1_PIN, IN2_PIN, IN3_PIN};
static int adc_initialized = 0;
const int in_a[8] = { 0, 1, 2, 3, 1, 3, 0, 2 };
int custom_random_seed(unsigned char *output, unsigned int sz) {
uint32_t i;
uint32_t result = 0;
uint32_t rd = 0, wsz;
if (!adc_initialized) {
adc_init();
for (i = 0; i < 4; i++) {
adc_gpio_init(IN[i]);
}
adc_initialized = 1;
sleep_ms(10);
}
/* Perform eight 3-bit samples with sources 0-1-2-4 */
for (i = 0; rd < sz; i = (i + 1) % 8) {
adc_select_input(in_a[i]);
/* Read the least significant 3 bits from the ADC */
result = (result << 3) | (adc_read() & 0x00000007);
/* Introduce a delay to capture environmental noise */
sleep_ms(1);
/* If we've completed eight samples, copy the result to the output */
if (i == 7) {
wsz = 3;
if (wsz > (sz - rd)) {
wsz = sz - rd;
}
memcpy(output + rd, &result, wsz);
rd += wsz;
result = 0;
}
}
return 0;
}

View file

@ -1,137 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* 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 _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#include "config.h"
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
#endif
// Use different configurations to test all net devices (also due to resource limitations)
#if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML21, OPT_MCU_SAML22)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
#define USE_ECM 1
#else
#define USE_ECM 0
#define INCLUDE_IPERF
#endif
//--------------------------------------------------------------------
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
//--------------------------------------------------------------------
// Must be >> MTU
// Can be set to 2048 without impact
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * LINK_MTU)
// Must be >> MTU
// Can be set to smaller values if wNtbOutMaxDatagrams==1
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * LINK_MTU)
// Number of NCM transfer blocks for reception side
#ifndef CFG_TUD_NCM_OUT_NTB_N
#define CFG_TUD_NCM_OUT_NTB_N 1
#endif
// Number of NCM transfer blocks for transmission side
#ifndef CFG_TUD_NCM_IN_NTB_N
#define CFG_TUD_NCM_IN_NTB_N 1
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
// Network class has 2 drivers: ECM/RNDIS and NCM.
// Only one of the drivers can be enabled
#define CFG_TUD_ECM_RNDIS USE_ECM
#define CFG_TUD_NCM (1 - CFG_TUD_ECM_RNDIS)
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View file

@ -1,250 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* 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 "bsp/board_api.h"
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(ECM_RNDIS, 5) | _PID_MAP(NCM, 5) )
// String Descriptor Index
enum
{
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_INTERFACE,
STRID_MAC
};
enum
{
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
ITF_NUM_TOTAL
};
enum
{
#if CFG_TUD_ECM_RNDIS
CONFIG_ID_RNDIS = 0,
CONFIG_ID_ECM = 1,
#else
CONFIG_ID_NCM = 0,
#endif
CONFIG_ID_COUNT
};
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD) device class
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0101,
.iManufacturer = STRID_MANUFACTURER,
.iProduct = STRID_PRODUCT,
.iSerialNumber = STRID_SERIAL,
.bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
// SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x83
#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#endif
#if CFG_TUD_ECM_RNDIS
static uint8_t const rndis_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
};
static uint8_t const ecm_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
};
#else
static uint8_t const ncm_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM+1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
};
#endif
// Configuration array: RNDIS and CDC-ECM
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
static uint8_t const * const configuration_arr[2] =
{
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_configuration,
[CONFIG_ID_ECM ] = ecm_configuration
#else
[CONFIG_ID_NCM ] = ncm_configuration
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
static char const* string_desc_arr [] =
{
[STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409)
[STRID_MANUFACTURER] = "TinyUSB", // Manufacturer
[STRID_PRODUCT] = "TinyUSB Device", // Product
[STRID_SERIAL] = NULL, // Serials will use unique ID if possible
[STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description
// STRID_MAC index is handled separately
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
unsigned int chr_count = 0;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
case STRID_MAC:
// Convert MAC address into UTF-16
for (unsigned i=0; i<sizeof(tud_network_mac_address); i++) {
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
if ( chr_count > max_count ) chr_count = max_count;
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2*chr_count + 2));
return _desc_str;
}

View file

@ -1,51 +0,0 @@
#include "femtotcp.h"
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
static struct ipstack *ref_ipstack = NULL;
static int ipstack_io_recv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
int ret = 0;
int fd = (intptr_t)ctx;
(void)ssl;
if (!ref_ipstack)
return -1;
ret = ft_recv(ref_ipstack, fd, buf, sz, 0);
if (ret == -11)
return WOLFSSL_CBIO_ERR_WANT_READ;
else if (ret <= 0)
return WOLFSSL_CBIO_ERR_CONN_CLOSE;
return ret;
}
static int ipstack_io_send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
int ret = 0;
int fd = (intptr_t)ctx;
(void)ssl;
if (!ref_ipstack)
return -1;
ret = ft_send(ref_ipstack, fd, buf, sz, 0);
if (ret == -11)
return WOLFSSL_CBIO_ERR_WANT_WRITE;
else if (ret <= 0)
return WOLFSSL_CBIO_ERR_CONN_CLOSE;
return ret;
}
int wolfSSL_SetIO_FT_CTX(WOLFSSL_CTX* ctx, struct ipstack *s)
{
wolfSSL_SetIORecv(ctx, ipstack_io_recv);
wolfSSL_SetIOSend(ctx, ipstack_io_send);
ref_ipstack = s;
return 0;
}
int wolfSSL_SetIO_FT(WOLFSSL* ssl, int fd)
{
wolfSSL_SetIOReadCtx(ssl, (void*)(intptr_t)fd);
wolfSSL_SetIOWriteCtx(ssl, (void*)(intptr_t)fd);
return 0;
}

View file

@ -1,65 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
// Create a socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
printf("Socket created: %d\n", server_fd);
// Bind to the specified port
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Bind successful\n");
// Start listening for incoming connections
if (listen(server_fd, 3) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Echo server listening on port %d\n", PORT);
while (1) {
// Accept a client connection
if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Accept failed");
continue;
}
printf("Client connected, fd: %d\n", client_fd);
ssize_t bytes_read;
while ((bytes_read = read(client_fd, buffer, BUFFER_SIZE)) > 0) {
write(client_fd, buffer, bytes_read); // Echo data back to the client
}
printf("Client disconnected\n");
close(client_fd);
}
close(server_fd);
return 0;
}

View file

@ -1,101 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
#define PORT 12345
int main() {
int server_fd, new_socket = -1, nfds;
struct sockaddr_in server_addr;
struct pollfd fds[2];
// Create a TCP socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// Bind the socket to the address
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_fd, 3) == -1) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// Set up poll to monitor stdin and the socket
fds[0].fd = STDIN_FILENO; // Monitor stdin
fds[1].fd = server_fd; // Monitor the server socket
while (1) {
fds[0].events = POLLIN;
fds[1].events = POLLIN;
// Poll for events
nfds = poll(fds, 2, -1); // -1 means wait indefinitely
if (nfds == -1) {
perror("Poll error");
close(server_fd);
exit(EXIT_FAILURE);
}
if (fds[0].revents & POLLIN) {
// Data available on stdin
char buffer[1024];
ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer));
if (bytes_read > 0) {
// Write stdin data to the socket
if (new_socket != -1) {
send(new_socket, buffer, bytes_read, 0);
}
}
}
if (fds[1].revents & POLLIN) {
// New connection on the socket
if (new_socket == -1) {
printf("Calling accept()\n");
new_socket = accept(server_fd, NULL, NULL);
if (new_socket == -1) {
perror("Accept failed");
continue;
}
fds[1].fd = new_socket;
printf("New connection established\n");
continue;
} else {
// Data available on the socket
char buffer[1024];
ssize_t bytes_received = recv(new_socket, buffer, sizeof(buffer), 0);
if (bytes_received > 0) {
// Write socket data to stdout
write(STDOUT_FILENO, buffer, bytes_received);
} else if (bytes_received == 0) {
// Connection closed by the client
close(new_socket);
new_socket = -1;
fds[1].fd = server_fd;
printf("Connection closed\n");
}
}
}
}
close(server_fd);
return 0;
}

View file

@ -1,102 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#define PORT 12346
int main() {
int server_fd, new_socket = -1;
struct sockaddr_in server_addr;
fd_set readfds;
int max_fd;
// Create a TCP socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
printf("server socket: %d\n", server_fd);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// Bind the socket to the address
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_fd, 3) == -1) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// Initialize file descriptor sets
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds); // Monitor stdin
FD_SET(server_fd, &readfds); // Monitor the server socket
max_fd = server_fd;
while (1) {
fd_set tempfds = readfds;
int activity = select(max_fd + 1, &tempfds, NULL, NULL, NULL);
if (activity == -1) {
perror("Select error");
close(server_fd);
exit(EXIT_FAILURE);
}
if (FD_ISSET(STDIN_FILENO, &tempfds)) {
// Data available on stdin
char buffer[1024];
ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer));
if (bytes_read > 0 && new_socket != -1) {
// Write stdin data to the socket
send(new_socket, buffer, bytes_read, 0);
}
}
if ((new_socket == -1) && FD_ISSET(server_fd, &tempfds)) {
printf("Server socket activity\n");
// New connection on the socket
if (new_socket == -1) {
new_socket = accept(server_fd, NULL, NULL);
if (new_socket == -1) {
perror("Accept failed");
continue;
}
printf("New connection established\n");
FD_SET(new_socket, &readfds); // Monitor the new socket
max_fd = (new_socket > max_fd) ? new_socket : max_fd;
continue;
}
}
if ((new_socket != -1) && FD_ISSET(new_socket, &tempfds)) {
// Data available on the socket
char buffer[1024];
ssize_t bytes_received = recv(new_socket, buffer, sizeof(buffer), 0);
if (bytes_received > 0) {
write(STDOUT_FILENO, buffer, bytes_received);
} else if (bytes_received == 0) {
// Connection closed by the client
close(new_socket);
FD_CLR(new_socket, &readfds); // Stop monitoring the socket
new_socket = -1;
printf("Connection closed\n");
}
}
}
close(server_fd);
return 0;
}

View file

@ -1,162 +0,0 @@
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "femtotcp.h"
#include "httpd.h"
#define TEST_SIZE (8 * 1024)
#define BUFFER_SIZE TEST_SIZE
static int exit_ok = 0, exit_count = 0;
static int tot_sent = 0;
static int femtotcp_closing = 0;
static int closed = 0;
/* FemtoTCP side: main loop of the stack under test. */
static int test_loop(struct ipstack *s, int active_close)
{
exit_ok = 0;
exit_count = 0;
tot_sent = 0;
femtotcp_closing = active_close;
closed = 0;
while(1) {
uint32_t ms_next;
struct timeval tv;
gettimeofday(&tv, NULL);
ms_next = ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(ms_next * 1000);
if (exit_ok > 0) {
if (exit_count++ < 10)
continue;
else break;
}
}
return 0;
}
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
/* Test cases */
extern const unsigned char server_der[];
extern const unsigned long server_der_len;
extern const unsigned char server_key_der[];
extern const unsigned long server_key_der_len;
static void test_httpd(struct ipstack *s)
{
int ret;
struct httpd httpd;
WOLFSSL_CTX *server_ctx;
const char homepage[] = "<html><body><h1>Hello, world!</h1></body></html>";
printf("HTTP server test\n");
printf("Creating TLS server context\n");
server_ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method());
if (!server_ctx) {
printf("Failed to create server context\n");
return;
}
printf("Importing server certificate\n");
ret = wolfSSL_CTX_use_certificate_buffer(server_ctx, server_der,
server_der_len, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
printf("Failed to import server certificate\n");
return;
}
printf("Importing server private key\n");
ret = wolfSSL_CTX_use_PrivateKey_buffer(server_ctx, server_key_der,
server_key_der_len, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
printf("Failed to import server private key\n");
return;
}
/* Initializing HTTPD server */
printf("Initializing HTTPD server\n");
ret = httpd_init(&httpd, s, 443, server_ctx);
if (ret < 0) {
printf("Failed to initialize HTTPD server\n");
return;
}
httpd_register_static_page(&httpd, "/", homepage);
test_loop(s, 0);
}
/* Main test function. */
int main(int argc, char **argv)
{
struct ipstack *s;
struct ll *tapdev;
struct timeval tv;
struct in_addr linux_ip;
uint32_t srv_ip;
ip4 ip = 0, nm = 0, gw = 0;
wolfSSL_Init();
wolfSSL_Debugging_OFF();
(void)argc;
(void)argv;
(void)ip;
(void)nm;
(void)gw;
(void)tv;
ipstack_init_static(&s);
tapdev = ipstack_getdev(s);
if (!tapdev)
return 1;
inet_aton(LINUX_IP, &linux_ip);
if (tap_init(tapdev, "femt0", linux_ip.s_addr) < 0) {
perror("tap init");
return 2;
}
system("tcpdump -i femt0 -w test.pcap &");
#ifdef DHCP
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
dhcp_client_init(s);
do {
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(1000);
ipstack_ipconfig_get(s, &ip, &nm, &gw);
} while (!dhcp_bound(s));
printf("DHCP: obtained IP address.\n");
ipstack_ipconfig_get(s, &ip, &nm, &gw);
srv_ip = htonl(ip);
#else
ipstack_ipconfig_set(s, atoip4(FEMTOTCP_IP), atoip4("255.255.255.0"),
atoip4(LINUX_IP));
printf("IP: manually configured\n");
inet_pton(AF_INET, FEMTOTCP_IP, &srv_ip);
#endif
/* Server side test */
test_httpd(s);
sleep(2);
sync();
system("killall tcpdump");
return 0;
}

View file

@ -1,274 +0,0 @@
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "femtotcp.h"
#define DHCP
#define TEST_SIZE (8 * 1024)
#define BUFFER_SIZE TEST_SIZE
static int conn_fd = -1;
static int exit_ok = 0, exit_count = 0;
static uint8_t buf[TEST_SIZE];
static int tot_sent = 0;
static int femtotcp_closing = 0;
static int closed = 0;
static int client_connected = 0;
static const uint8_t test_pattern[16] = "Test pattern - -";
/* Client-side callback. */
static void client_cb(int fd, uint16_t event, void *arg)
{
struct ipstack *s = (struct ipstack *)arg;
uint32_t i;
int ret;
static unsigned int total_r = 0, total_w = 0;
if (fd == conn_fd) {
if ((event & CB_EVENT_WRITABLE) && (client_connected == 0)) {
printf("Client: connected\n");
client_connected = 1;
}
}
if (total_w == 0) {
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
memcpy(buf + i, test_pattern, sizeof(test_pattern));
}
}
if (client_connected && (event & CB_EVENT_WRITABLE) && (total_w < sizeof(buf))) {
ret = ft_sendto(s, fd, buf + total_w, sizeof(buf) - total_w, 0, NULL, 0);
if (ret <= 0) {
printf("Test client write: %d\n", ret);
return;
}
total_w += ret;
}
while ((total_r < total_w) && (event & CB_EVENT_READABLE)) {
ret = ft_recvfrom(s, fd, buf + total_r, sizeof(buf) - total_r, 0, NULL, NULL);
if (ret < 0){
if (ret != -11) {
printf("Client read: %d\n", ret);
}
return;
}
if (ret == 0) {
printf("Client read: server has closed the connection.\n");
return;
}
total_r += ret;
printf("Client RX total: %u\n", total_r);
}
if (total_r == sizeof(buf)) {
exit_ok = 1;
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
if (memcmp(buf + i, test_pattern, sizeof(test_pattern))) {
printf("test client: pattern mismatch\n");
printf("at position %d\n", i);
buf[i + 16] = 0;
printf("%s\n", &buf[i]);
return;
}
}
if (femtotcp_closing) {
ft_close(s, fd);
conn_fd = -1;
}
printf("Test client: success\n");
}
}
/* FemtoTCP side: main loop of the stack under test. */
static int test_loop(struct ipstack *s, int active_close)
{
exit_ok = 0;
exit_count = 0;
tot_sent = 0;
femtotcp_closing = active_close;
closed = 0;
while(1) {
uint32_t ms_next;
struct timeval tv;
gettimeofday(&tv, NULL);
ms_next = ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(ms_next * 1000);
if (exit_ok > 0) {
if (exit_count++ < 10)
continue;
else break;
}
}
return 0;
}
/* Test code (Linux side).
* Thread with echo server to test the client.
*/
static void *pt_echoserver(void *arg)
{
int fd, ret;
unsigned total_r = 0;
uint8_t buf[BUFFER_SIZE];
struct sockaddr_in local_sock = {
.sin_family = AF_INET,
.sin_port = ntohs(8), /* Echo */
.sin_addr.s_addr = 0
};
femtotcp_closing = (uintptr_t)arg;
fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0);
if (fd < 0) {
printf("test server socket: %d\n", fd);
return (void *)-1;
}
local_sock.sin_addr.s_addr = inet_addr(LINUX_IP);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
ret = bind(fd, (struct sockaddr *)&local_sock, sizeof(local_sock));
if (ret < 0) {
printf("test server bind: %d (%s)\n", ret, strerror(errno));
return (void *)-1;
}
ret = listen(fd, 1);
if (ret < 0) {
printf("test server listen: %d\n", ret);
return (void *)-1;
}
printf("Waiting for client\n");
ret = accept(fd, NULL, NULL);
if (ret < 0) {
printf("test server accept: %d\n", ret);
return (void *)-1;
}
printf("test server: client %d connected\n", ret);
fd = ret;
while (1) {
ret = read(fd, buf + total_r, sizeof(buf) - total_r);
if (ret < 0) {
printf("failed test server read: %d (%s) \n", ret, strerror(errno));
return (void *)-1;
}
if (ret == 0) {
printf("test server read: client has closed the connection.\n");
if (femtotcp_closing)
return (void *)0;
else
return (void *)-1;
}
total_r += ret;
write(fd, buf + total_r - ret, ret);
}
}
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
void test_femtotcp_echoclient(struct ipstack *s)
{
int ret, test_ret = 0;
pthread_t pt;
struct ipstack_sockaddr_in remote_sock;
/* Client side test: client is closing the connection */
remote_sock.sin_family = AF_INET;
remote_sock.sin_port = ee16(8);
remote_sock.sin_addr.s_addr = inet_addr(LINUX_IP);
printf("TCP client tests\n");
conn_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
printf("client socket: %04x\n", conn_fd);
ipstack_register_callback(s, conn_fd, client_cb, s);
printf("Connecting to %s:8\n", LINUX_IP);
ft_connect(s, conn_fd, (struct ipstack_sockaddr *)&remote_sock, sizeof(remote_sock));
pthread_create(&pt, NULL, pt_echoserver, (void*)1);
printf("Starting test: echo client active close\n");
ret = test_loop(s, 1);
printf("Test echo client active close: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux server: %d\n", test_ret);
if (conn_fd >= 0) {
ft_close(s, conn_fd);
conn_fd = -1;
}
}
static int example_com_resolved = 0;
void ns_cb(uint32_t ip)
{
printf("Obtained ip address for example.com: %s\n", inet_ntoa(*(struct in_addr *)&ip));
example_com_resolved = 1;
}
/* Main test function. */
int main(int argc, char **argv)
{
struct ipstack *s;
struct ll *tapdev;
struct timeval tv;
struct in_addr linux_ip;
uint32_t srv_ip;
uint16_t dns_id = 0;
ip4 ip = 0, nm = 0, gw = 0;
(void)argc;
(void)argv;
(void)ip;
(void)nm;
(void)gw;
(void)tv;
ipstack_init_static(&s);
tapdev = ipstack_getdev(s);
if (!tapdev)
return 1;
inet_aton(LINUX_IP, &linux_ip);
if (tap_init(tapdev, "femt0", linux_ip.s_addr) < 0) {
perror("tap init");
return 2;
}
system("tcpdump -i femt0 -w test.pcap &");
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
dhcp_client_init(s);
do {
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(1000);
ipstack_ipconfig_get(s, &ip, &nm, &gw);
} while (!dhcp_bound(s));
printf("DHCP: obtained IP address.\n");
ipstack_ipconfig_get(s, &ip, &nm, &gw);
srv_ip = htonl(ip);
(void)srv_ip;
nslookup(s, "example.com", &dns_id, ns_cb);
while(!example_com_resolved) {
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(1000);
}
/* Client side test */
test_femtotcp_echoclient(s);
sleep(2);
sync();
system("killall tcpdump");
return 0;
}

View file

@ -1,455 +0,0 @@
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "femtotcp.h"
#define TEST_SIZE (8 * 1024)
#define BUFFER_SIZE TEST_SIZE
static int listen_fd = -1, client_fd = -1;
static int exit_ok = 0, exit_count = 0;
static uint8_t buf[TEST_SIZE];
static int tot_sent = 0;
static int tot_recv = 0;
static int femtotcp_closing = 0;
static int closed = 0;
static int conn_fd = -1;
static int client_connected = 0;
static const uint8_t test_pattern[16] = "Test pattern - -";
/* FemtoTCP: server side callback. */
static void server_cb(int fd, uint16_t event, void *arg)
{
int ret = 0;
if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) {
client_fd = ft_accept((struct ipstack *)arg, listen_fd, NULL, NULL);
if (client_fd > 0) {
printf("accept: %04x\n", client_fd);
}
} else if ((fd == client_fd) && (event & CB_EVENT_READABLE )) {
ret = ft_recvfrom((struct ipstack *)arg, client_fd, buf, sizeof(buf), 0, NULL, NULL);
if (ret != -11) {
if (ret < 0) {
printf("Recv error: %d\n", ret);
ft_close((struct ipstack *)arg, client_fd);
} else if (ret == 0) {
printf("Client side closed the connection.\n");
ft_close((struct ipstack *)arg, client_fd);
printf("Server: Exiting.\n");
exit_ok = 1;
} else if (ret > 0) {
printf("recv: %d, echoing back\n", ret);
tot_recv += ret;
}
}
}
if ((event & CB_EVENT_WRITABLE) || ((ret > 0) && !closed)) {
int snd_ret;
if ((tot_sent >= 4096) && femtotcp_closing) {
ft_close((struct ipstack *)arg, client_fd);
printf("Server: I closed the connection.\n");
closed = 1;
exit_ok = 1;
}
if ((!closed) && (tot_sent < tot_recv)) {
snd_ret = ft_sendto((struct ipstack *)arg, client_fd, buf + tot_sent, tot_recv - tot_sent, 0, NULL, 0);
if (snd_ret != -11) {
if (snd_ret < 0) {
printf("Send error: %d\n", snd_ret);
ft_close((struct ipstack *)arg, client_fd);
} else {
tot_sent += snd_ret;
printf("sent %d bytes\n", snd_ret);
if (tot_recv == tot_sent) {
tot_sent = 0;
tot_recv = 0;
}
}
}
}
}
if (event & CB_EVENT_CLOSED) {
printf("Closing %d, client fd: %d\n", fd, client_fd);
}
if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) {
printf("Client side closed the connection (EVENT_CLOSED)\n");
ft_close((struct ipstack *)arg, client_fd);
client_fd = -1;
printf("Server: Exiting.\n");
exit_ok = 1;
}
(void)arg;
}
/* Client-side callback. */
static void client_cb(int fd, uint16_t event, void *arg)
{
struct ipstack *s = (struct ipstack *)arg;
uint32_t i;
int ret;
static unsigned int total_r = 0, total_w = 0;
if (fd == conn_fd) {
if ((event & CB_EVENT_WRITABLE) && (client_connected == 0)) {
printf("Client: connected\n");
client_connected = 1;
}
}
if (total_w == 0) {
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
memcpy(buf + i, test_pattern, sizeof(test_pattern));
}
}
if (client_connected && (event & CB_EVENT_WRITABLE) && (total_w < sizeof(buf))) {
ret = ft_sendto(s, fd, buf + total_w, sizeof(buf) - total_w, 0, NULL, 0);
if (ret <= 0) {
printf("Test client write: %d\n", ret);
return;
}
total_w += ret;
}
while ((total_r < total_w) && (event & CB_EVENT_READABLE)) {
ret = ft_recvfrom(s, fd, buf + total_r, sizeof(buf) - total_r, 0, NULL, NULL);
if (ret < 0){
if (ret != -11) {
printf("Client read: %d\n", ret);
}
return;
}
if (ret == 0) {
printf("Client read: server has closed the connection.\n");
return;
}
total_r += ret;
printf("Client RX total: %u\n", total_r);
}
if (total_r == sizeof(buf)) {
exit_ok = 1;
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
if (memcmp(buf + i, test_pattern, sizeof(test_pattern))) {
printf("test client: pattern mismatch\n");
printf("at position %d\n", i);
buf[i + 16] = 0;
printf("%s\n", &buf[i]);
return;
}
}
if (femtotcp_closing) {
ft_close(s, fd);
conn_fd = -1;
}
printf("Test client: success\n");
}
}
/* FemtoTCP side: main loop of the stack under test. */
static int test_loop(struct ipstack *s, int active_close)
{
exit_ok = 0;
exit_count = 0;
tot_sent = 0;
femtotcp_closing = active_close;
closed = 0;
while(1) {
uint32_t ms_next;
struct timeval tv;
gettimeofday(&tv, NULL);
ms_next = ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(ms_next * 1000);
if (exit_ok > 0) {
if (exit_count++ < 10)
continue;
else break;
}
}
return 0;
}
/* Test code (Linux side).
* Thread with client to test the echoserver.
*/
void *pt_echoclient(void *arg)
{
int fd, ret;
unsigned total_r = 0;
unsigned i;
uint8_t buf[BUFFER_SIZE];
uint32_t *srv_addr = (uint32_t *)arg;
struct sockaddr_in remote_sock = {
.sin_family = AF_INET,
.sin_port = ntohs(8), /* Echo */
};
remote_sock.sin_addr.s_addr = *srv_addr;
fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0);
if (fd < 0) {
printf("test client socket: %d\n", fd);
return (void *)-1;
}
sleep(1);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
printf("Connecting to echo server\n");
ret = connect(fd, (struct sockaddr *)&remote_sock, sizeof(remote_sock));
if (ret < 0) {
printf("test client connect: %d\n", ret);
perror("connect");
return (void *)-1;
}
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
memcpy(buf + i, test_pattern, sizeof(test_pattern));
}
ret = write(fd, buf, sizeof(buf));
if (ret < 0) {
printf("test client write: %d\n", ret);
return (void *)-1;
}
while (total_r < sizeof(buf)) {
ret = read(fd, buf + total_r, sizeof(buf) - total_r);
if (ret < 0) {
printf("failed test client read: %d\n", ret);
return (void *)-1;
}
if (ret == 0) {
printf("test client read: server has closed the connection.\n");
if (femtotcp_closing)
return (void *)0;
else
return (void *)-1;
}
total_r += ret;
}
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
if (memcmp(buf + i, test_pattern, sizeof(test_pattern))) {
printf("test client: pattern mismatch\n");
printf("at position %d\n", i);
buf[i + 16] = 0;
printf("%s\n", &buf[i]);
return (void *)-1;
}
}
close(fd);
printf("Test client: success\n");
return (void *)0;
}
/* Test code (Linux side).
* Thread with echo server to test the client.
*/
static void *pt_echoserver(void *arg)
{
int fd, ret;
unsigned total_r = 0;
uint8_t buf[BUFFER_SIZE];
struct sockaddr_in local_sock = {
.sin_family = AF_INET,
.sin_port = ntohs(8), /* Echo */
.sin_addr.s_addr = 0
};
femtotcp_closing = (uintptr_t)arg;
fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0);
if (fd < 0) {
printf("test server socket: %d\n", fd);
return (void *)-1;
}
local_sock.sin_addr.s_addr = inet_addr(LINUX_IP);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
ret = bind(fd, (struct sockaddr *)&local_sock, sizeof(local_sock));
if (ret < 0) {
printf("test server bind: %d (%s)\n", ret, strerror(errno));
return (void *)-1;
}
ret = listen(fd, 1);
if (ret < 0) {
printf("test server listen: %d\n", ret);
return (void *)-1;
}
printf("Waiting for client\n");
ret = accept(fd, NULL, NULL);
if (ret < 0) {
printf("test server accept: %d\n", ret);
return (void *)-1;
}
printf("test server: client %d connected\n", ret);
fd = ret;
while (1) {
ret = read(fd, buf + total_r, sizeof(buf) - total_r);
if (ret < 0) {
printf("failed test server read: %d (%s) \n", ret, strerror(errno));
return (void *)-1;
}
if (ret == 0) {
printf("test server read: client has closed the connection.\n");
if (femtotcp_closing)
return (void *)0;
else
return (void *)-1;
}
total_r += ret;
write(fd, buf + total_r - ret, ret);
}
}
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
/* Test cases */
void test_femtotcp_echoserver(struct ipstack *s, uint32_t srv_ip)
{
int ret, test_ret = 0;
pthread_t pt;
struct ipstack_sockaddr_in local_sock = {
.sin_family = AF_INET,
.sin_port = ee16(8), /* Echo */
.sin_addr.s_addr = 0
};
printf("TCP server tests\n");
listen_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
printf("socket: %04x\n", listen_fd);
ipstack_register_callback(s, listen_fd, server_cb, s);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server close-wait\n");
ret = ft_bind(s, listen_fd, (struct ipstack_sockaddr *)&local_sock, sizeof(local_sock));
printf("bind: %d\n", ret);
ret = ft_listen(s, listen_fd, 1);
printf("listen: %d\n", ret);
ret = test_loop(s, 0);
pthread_join(pt, (void **)&test_ret);
printf("Test echo server close-wait: %d\n", ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server active close\n");
ret = test_loop(s, 1);
printf("Test echo server close-wait: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
ft_close(s, listen_fd);
}
void test_femtotcp_echoclient(struct ipstack *s)
{
int ret, test_ret = 0;
pthread_t pt;
struct ipstack_sockaddr_in remote_sock;
/* Client side test: client is closing the connection */
remote_sock.sin_family = AF_INET;
remote_sock.sin_port = ee16(8);
remote_sock.sin_addr.s_addr = inet_addr(LINUX_IP);
printf("TCP client tests\n");
conn_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
printf("client socket: %04x\n", conn_fd);
ipstack_register_callback(s, conn_fd, client_cb, s);
printf("Connecting to %s:8\n", LINUX_IP);
ft_connect(s, conn_fd, (struct ipstack_sockaddr *)&remote_sock, sizeof(remote_sock));
pthread_create(&pt, NULL, pt_echoserver, (void*)1);
printf("Starting test: echo client active close\n");
ret = test_loop(s, 1);
printf("Test echo client active close: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux server: %d\n", test_ret);
if (conn_fd >= 0) {
ft_close(s, conn_fd);
conn_fd = -1;
}
/* Client side test: server is closing the connection */
/* Excluded for now as linux cannot bind twice on port 8 */
#if 0
conn_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
if (conn_fd < 0) {
printf("cannot create socket: %d\n", conn_fd);
}
printf("client socket: %04x\n", conn_fd);
ipstack_register_callback(s, conn_fd, client_cb, s);
printf("Connecting to %s:8\n", LINUX_IP);
ft_connect(s, conn_fd, (struct ipstack_sockaddr *)&remote_sock, sizeof(remote_sock));
pthread_create(&pt, NULL, pt_echoserver, (void*)0);
printf("Starting test: echo client passive close\n");
ret = test_loop(s, 0);
printf("Test echo client, server closing: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux server: %d\n", test_ret);
#endif
}
/* Main test function. */
int main(int argc, char **argv)
{
struct ipstack *s;
struct ll *tapdev;
struct timeval tv;
struct in_addr linux_ip;
uint32_t srv_ip;
ip4 ip = 0, nm = 0, gw = 0;
(void)argc;
(void)argv;
(void)ip;
(void)nm;
(void)gw;
(void)tv;
ipstack_init_static(&s);
tapdev = ipstack_getdev(s);
if (!tapdev)
return 1;
inet_aton(LINUX_IP, &linux_ip);
if (tap_init(tapdev, "femt0", linux_ip.s_addr) < 0) {
perror("tap init");
return 2;
}
system("tcpdump -i femt0 -w test.pcap &");
#ifdef DHCP
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
dhcp_client_init(s);
do {
gettimeofday(&tv, NULL);
ipstack_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(1000);
ipstack_ipconfig_get(s, &ip, &nm, &gw);
} while (!dhcp_bound(s));
printf("DHCP: obtained IP address.\n");
ipstack_ipconfig_get(s, &ip, &nm, &gw);
srv_ip = htonl(ip);
#else
ipstack_ipconfig_set(s, atoip4(FEMTOTCP_IP), atoip4("255.255.255.0"),
atoip4(LINUX_IP));
printf("IP: manually configured\n");
inet_pton(AF_INET, FEMTOTCP_IP, &srv_ip);
#endif
/* Server side test */
test_femtotcp_echoserver(s, srv_ip);
/* Client side test */
test_femtotcp_echoclient(s);
system("killall tcpdump");
return 0;
}

View file

@ -3,16 +3,14 @@
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include "femtotcp.h"
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "femtotcp.h"
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
//#define DHCP
#define FEMTOTCP_IP "10.10.10.2"
#define LINUX_IP "10.10.10.1"
#define TEST_SIZE (8 * 1024)
#define BUFFER_SIZE TEST_SIZE
@ -22,72 +20,49 @@ static int exit_ok = 0, exit_count = 0;
static uint8_t buf[TEST_SIZE];
static int tot_sent = 0;
static int tot_recv = 0;
static int femtotcp_closing = 0;
static int server_closing = 0;
static int closed = 0;
static const uint8_t test_pattern[16] = "Test pattern - -";
static WOLFSSL_CTX *server_ctx = NULL; /* Used by femtoTCP */
static WOLFSSL_CTX *client_ctx = NULL; /* Used by Linux */
static WOLFSSL *client_ssl = NULL;
static WOLFSSL *server_ssl = NULL;
/* Defined in wolfssl_io.c */
int wolfSSL_SetIO_FT(WOLFSSL* ssl, int fd);
int wolfSSL_SetIO_FT_CTX(WOLFSSL_CTX *ctx, struct ipstack *s);
/* FemtoTCP: server side callback. */
static void server_cb(int fd, uint16_t event, void *arg)
static void socket_cb(int fd, uint16_t event, void *arg)
{
int ret = 0;
printf("Called socket_cb, events: %04x fd %d\n", event, fd & (~0x1000));
if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) {
client_fd = ft_accept((struct ipstack *)arg, listen_fd, NULL, NULL);
if (client_fd > 0) {
printf("accept: Client FD is 0x%04x\n", client_fd);
/* Create the wolfSSL object */
server_ssl = wolfSSL_new(server_ctx);
if (!server_ssl) {
printf("Failed to create server SSL object\n");
return;
}
wolfSSL_SetIO_FT(server_ssl, client_fd);
/* Accepting the TLS session is not necessary here, as the
* first read will trigger the handshake.
*/
printf("Server: TCP connection established\n");
printf("accept: %04x\n", client_fd);
}
} else if ((fd == client_fd) && (event & CB_EVENT_READABLE )) {
ret = wolfSSL_read(server_ssl, buf, sizeof(buf));
if (ret < 0) {
ret = wolfSSL_get_error(server_ssl, 0);
if (ret != WOLFSSL_ERROR_WANT_READ) {
ret = ft_recvfrom((struct ipstack *)arg, client_fd, buf, sizeof(buf), 0, NULL, NULL);
if (ret != -11) {
if (ret < 0) {
printf("Recv error: %d\n", ret);
ft_close((struct ipstack *)arg, client_fd);
} else if (ret == 0) {
printf("Client side closed the connection.\n");
ft_close((struct ipstack *)arg, client_fd);
printf("Server: Exiting.\n");
exit_ok = 1;
} else if (ret > 0) {
printf("recv: %d, echoing back\n", ret);
tot_recv += ret;
}
} else if (ret == 0) {
printf("Client side closed the connection.\n");
ft_close((struct ipstack *)arg, client_fd);
printf("Server: Exiting.\n");
exit_ok = 1;
} else if (ret > 0) {
printf("recv: %d, echoing back\n", ret);
tot_recv += ret;
}
}
if ((event & CB_EVENT_WRITABLE) || ((ret > 0) && !closed)) {
int snd_ret;
if ((tot_sent >= 4096) && femtotcp_closing) {
if ((tot_sent >= 4096) && server_closing) {
ft_close((struct ipstack *)arg, client_fd);
printf("Server: I closed the connection.\n");
closed = 1;
exit_ok = 1;
}
if ((!closed) && (tot_sent < tot_recv)) {
snd_ret = wolfSSL_write(server_ssl, buf + tot_sent, tot_recv - tot_sent);
if (snd_ret != WANT_WRITE) {
snd_ret = ft_sendto((struct ipstack *)arg, client_fd, buf + tot_sent, tot_recv - tot_sent, 0, NULL, 0);
if (snd_ret != -11) {
if (snd_ret < 0) {
printf("Send error: %d\n", snd_ret);
wolfSSL_free(server_ssl);
ft_close((struct ipstack *)arg, client_fd);
} else {
tot_sent += snd_ret;
@ -102,27 +77,24 @@ static void server_cb(int fd, uint16_t event, void *arg)
}
if (event & CB_EVENT_CLOSED) {
printf("Closing %d, client fd: %d\n", fd, client_fd);
wolfSSL_free(server_ssl);
server_ssl = NULL;
}
if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) {
printf("Client side closed the connection (EVENT_CLOSED)\n");
wolfSSL_free(server_ssl);
ft_close((struct ipstack *)arg, client_fd);
client_fd = -1;
printf("Server: Exiting.\n");
exit_ok = 1;
}
(void)arg;
}
/* FemtoTCP side: main loop of the stack under test. */
static int test_loop(struct ipstack *s, int active_close)
static int test_echoserver(struct ipstack *s, int active_close)
{
exit_ok = 0;
exit_count = 0;
tot_sent = 0;
femtotcp_closing = active_close;
server_closing = active_close;
closed = 0;
while(1) {
@ -140,47 +112,25 @@ static int test_loop(struct ipstack *s, int active_close)
return 0;
}
/* Test code (Linux side).
* Thread with client to test the echoserver.
*/
extern const unsigned char ca_der[];
extern const unsigned long ca_der_len;
void *pt_echoclient(void *arg)
{
int fd, ret;
unsigned total_r = 0;
unsigned i;
uint8_t buf[BUFFER_SIZE];
uint8_t test_pattern[16] = "Test pattern - -";
uint32_t *srv_addr = (uint32_t *)arg;
struct sockaddr_in remote_sock = {
.sin_family = AF_INET,
.sin_port = ntohs(8), /* Echo */
};
client_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
if (!client_ctx) {
printf("Failed to create client context\n");
return (void *)-1;
}
client_ssl = wolfSSL_new(client_ctx);
if (!client_ssl) {
printf("Failed to create client SSL object\n");
return (void *)-1;
}
wolfSSL_CTX_load_verify_buffer(client_ctx, ca_der, ca_der_len, SSL_FILETYPE_ASN1);
remote_sock.sin_addr.s_addr = *srv_addr;
fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0);
if (fd < 0) {
printf("test client socket: %d\n", fd);
return (void *)-1;
}
wolfSSL_set_fd(client_ssl, fd);
sleep(1);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
printf("Connecting to echo server\n");
ret = connect(fd, (struct sockaddr *)&remote_sock, sizeof(remote_sock));
if (ret < 0) {
@ -188,29 +138,23 @@ void *pt_echoclient(void *arg)
perror("connect");
return (void *)-1;
}
printf("Linux client: TCP connection established\n");
ret = wolfSSL_connect(client_ssl);
if (ret != SSL_SUCCESS) {
printf("Linux client: Failed to connect to TLS server, err: %d\n", ret);
return (void *)-1;
}
for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) {
memcpy(buf + i, test_pattern, sizeof(test_pattern));
}
ret = wolfSSL_write(client_ssl, buf, sizeof(buf));
ret = write(fd, buf, sizeof(buf));
if (ret < 0) {
printf("test client write: %d\n", ret);
return (void *)-1;
}
while (total_r < sizeof(buf)) {
ret = wolfSSL_read(client_ssl, buf + total_r, sizeof(buf) - total_r);
ret = read(fd, buf + total_r, sizeof(buf) - total_r);
if (ret < 0) {
printf("failed test client read: %d\n", ret);
return (void *)-1;
}
if (ret == 0) {
printf("test client read: server has closed the connection.\n");
if (femtotcp_closing)
if (server_closing)
return (void *)0;
else
return (void *)-1;
@ -226,108 +170,37 @@ void *pt_echoclient(void *arg)
return (void *)-1;
}
}
client_ssl = NULL;
close(fd);
printf("Test client: success\n");
wolfSSL_free(client_ssl);
return (void *)0;
}
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
/* Test cases */
extern const unsigned char server_der[];
extern const unsigned long server_der_len;
extern const unsigned char server_key_der[];
extern const unsigned long server_key_der_len;
void test_femtotcp_echoserver(struct ipstack *s, uint32_t srv_ip)
int main(int argc, char **argv)
{
int ret, test_ret = 0;
struct ipstack *s;
struct ll *tapdev;
pthread_t pt;
struct timeval tv;
struct in_addr linux_ip;
ip4 ip = 0, nm = 0, gw = 0;
uint32_t srv_ip;
struct ipstack_sockaddr_in local_sock = {
.sin_family = AF_INET,
.sin_port = ee16(8), /* Echo */
.sin_addr.s_addr = 0
};
printf("TCP server tests\n");
printf("Creating TLS server context\n");
server_ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (!server_ctx) {
printf("Failed to create server context\n");
return;
}
printf("Associating server context with femtoTCP\n");
wolfSSL_SetIO_FT_CTX(server_ctx, s);
printf("Importing server certificate\n");
ret = wolfSSL_CTX_use_certificate_buffer(server_ctx, server_der,
server_der_len, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
printf("Failed to import server certificate\n");
return;
}
printf("Importing server private key\n");
ret = wolfSSL_CTX_use_PrivateKey_buffer(server_ctx, server_key_der,
server_key_der_len, SSL_FILETYPE_ASN1);
if (ret != SSL_SUCCESS) {
printf("Failed to import server private key\n");
return;
}
listen_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
printf("socket: %04x\n", listen_fd);
ipstack_register_callback(s, listen_fd, server_cb, s);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server close-wait\n");
ret = ft_bind(s, listen_fd, (struct ipstack_sockaddr *)&local_sock, sizeof(local_sock));
printf("bind: %d\n", ret);
ret = ft_listen(s, listen_fd, 1);
printf("listen: %d\n", ret);
ret = test_loop(s, 0);
pthread_join(pt, (void **)&test_ret);
printf("Test echo server close-wait: %d\n", ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server active close\n");
ret = test_loop(s, 1);
printf("Test echo server close-wait: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
ft_close(s, listen_fd);
}
/* Main test function. */
int main(int argc, char **argv)
{
struct ipstack *s;
struct ll *tapdev;
struct timeval tv;
struct in_addr linux_ip;
uint32_t srv_ip;
ip4 ip = 0, nm = 0, gw = 0;
wolfSSL_Init();
wolfSSL_Debugging_OFF();
int ret, test_ret = 0;
(void)argc;
(void)argv;
(void)ip;
(void)nm;
(void)gw;
(void)tv;
(void)pt;
(void)srv_ip;
ipstack_init_static(&s);
tapdev = ipstack_getdev(s);
if (!tapdev)
@ -337,6 +210,7 @@ int main(int argc, char **argv)
perror("tap init");
return 2;
}
system("tcpdump -i femt0 -w test.pcap &");
#ifdef DHCP
@ -359,10 +233,32 @@ int main(int argc, char **argv)
inet_pton(AF_INET, FEMTOTCP_IP, &srv_ip);
#endif
/* Server side test */
test_femtotcp_echoserver(s, srv_ip);
sleep(2);
sync();
listen_fd = ft_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
printf("socket: %04x\n", listen_fd);
ipstack_register_callback(s, listen_fd, socket_cb, s);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server close-wait\n");
ret = ft_bind(s, listen_fd, (struct ipstack_sockaddr *)&local_sock, sizeof(local_sock));
printf("bind: %d\n", ret);
ret = ft_listen(s, listen_fd, 1);
printf("listen: %d\n", ret);
ret = test_echoserver(s, 0);
pthread_join(pt, (void **)&test_ret);
printf("Test echo server close-wait: %d\n", ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
pthread_create(&pt, NULL, pt_echoclient, &srv_ip);
printf("Starting test: echo server active close\n");
ret = test_echoserver(s, 1);
printf("Test echo server close-wait: %d\n", ret);
pthread_join(pt, (void **)&test_ret);
printf("Test linux client: %d\n", test_ret);
sleep(1);
system("killall tcpdump");
return 0;
}

15
test/unit/Makefile Normal file
View file

@ -0,0 +1,15 @@
LDFLAGS=-lcheck -lm -lpthread -lrt -ldl -lsubunit
CFLAGS=-g -Wall -Wextra -Werror -I../../ -DETHERNET
CC=gcc
all: unit
unit: unit.o
$(CC) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f *.o unit

View file

@ -1,5 +1,5 @@
#include "check.h"
#include "../../femtotcp.c"
#include "../../src/femtotcp.c"
#include <stdlib.h> /* for random() */
/* MOCKS */
@ -18,7 +18,7 @@ static const uint8_t ifmac[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
static uint8_t last_frame_sent[LINK_MTU];
static uint32_t last_frame_sent_size = 0;
static int mock_send(struct ll *dev, void *frame, uint32_t len)
static int mock_send(struct ll *dev, void *frame, int len)
{
(void)dev;
memcpy(last_frame_sent, frame, len);
@ -26,7 +26,7 @@ static int mock_send(struct ll *dev, void *frame, uint32_t len)
return 0;
}
static int mock_poll(struct ll *dev, void *frame, uint32_t len)
static int mock_poll(struct ll *dev, void *frame, int len)
{
(void)dev;
(void)frame;
@ -592,7 +592,7 @@ START_TEST(test_transport_checksum) {
// Set up pseudo-header values for test
ph.ph.src = 0xc0a80101; // 192.168.1.1
ph.ph.dst = 0xc0a80102; // 192.168.1.2
ph.ph.proto = FT_IPPROTO_TCP;
ph.ph.proto = IPPROTO_TCP;
ph.ph.len = ee16(20); // TCP header length (without options)
// Test with a simple TCP header with src/dst ports and no data
@ -620,7 +620,7 @@ START_TEST(test_iphdr_set_checksum) {
ip.id = ee16(1);
ip.flags_fo = 0;
ip.ttl = 64;
ip.proto = FT_IPPROTO_TCP;
ip.proto = IPPROTO_TCP;
ip.src = ee32(0xc0a80101); // 192.168.1.1
ip.dst = ee32(0xc0a80102); // 192.168.1.2
@ -662,13 +662,13 @@ START_TEST(test_ip_output_add_header) {
t.S = &S;
// Run the function for a TCP packet
int result = ip_output_add_header(&t, &ip, FT_IPPROTO_TCP, 40);
int result = ip_output_add_header(&t, &ip, IPPROTO_TCP, 40);
ck_assert_int_eq(result, 0);
// Validate IP header fields
ck_assert_uint_eq(ip.ver_ihl, 0x45);
ck_assert_uint_eq(ip.ttl, 64);
ck_assert_uint_eq(ip.proto, FT_IPPROTO_TCP);
ck_assert_uint_eq(ip.proto, IPPROTO_TCP);
ck_assert_uint_eq(ip.src, ee32(t.local_ip));
ck_assert_uint_eq(ip.dst, ee32(t.remote_ip));
ck_assert_msg(ip.csum != 0, "IP header checksum should not be zero");

View file

@ -1,61 +0,0 @@
#!/bin/bash
#
OUT_DIR=build/certs
: "${COUNTRY:=US}"
: "${STATE:=State}"
: "${CITY:=City}"
: "${ORG:=ExampleOrg}"
: "${ORG_UNIT:=IT}"
: "${CA_COMMON_NAME:=Example CA}"
: "${SERVER_COMMON_NAME:=example.com}"
: "${DAYS_CA:=3650}" # CA certificate validity in days (10 years)
: "${DAYS_SERVER:=825}" # Server certificate validity in days (2 years)
: "${ECC_CURVE:=secp384r1}" # ECC curve to use
# Create the output directory if it doesn't exist
mkdir -p "$OUT_DIR"
# 1. Generate CA private key
openssl ecparam -name "$ECC_CURVE" -genkey -noout -out "$OUT_DIR/ca.key"
# 2. Generate the CA self-signed certificate (PEM format)
openssl req -x509 -new -key "$OUT_DIR/ca.key" -sha256 -days "$DAYS_CA" -out "$OUT_DIR/ca.crt" \
-subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORG/OU=$ORG_UNIT/CN=$CA_COMMON_NAME"
# 3. Convert CA certificate to DER format
openssl x509 -in "$OUT_DIR/ca.crt" -outform DER -out "$OUT_DIR/ca.der"
xxd -i "$OUT_DIR/ca.der" |sed -e "s/unsigned/const unsigned/g" | sed -e "s/build_certs_//g" > "$OUT_DIR/ca_cert.c"
echo "==== Generating server private key ===="
# 4. Generate server private key
openssl ecparam -name "$ECC_CURVE" -genkey -noout -out "$OUT_DIR/server.key"
# 5. Convert server private key to DER format
openssl pkcs8 -topk8 -nocrypt -in "$OUT_DIR/server.key" -outform DER -out "$OUT_DIR/server.key.der"
xxd -i "$OUT_DIR/server.key.der" |sed -e "s/unsigned/const unsigned/g" | sed -e "s/build_certs_//g" > "$OUT_DIR/server_key.c"
echo "==== Generating server Certificate Signing Request (CSR) ===="
# 6. Generate server Certificate Signing Request (CSR)
openssl req -new -key "$OUT_DIR/server.key" -out "$OUT_DIR/server.csr" \
-subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORG/OU=$ORG_UNIT/CN=$SERVER_COMMON_NAME"
echo "==== Signing server certificate with the CA ===="
# 7. Sign the server CSR with the CA to create a server certificate (PEM format)
openssl x509 -req -in "$OUT_DIR/server.csr" -CA "$OUT_DIR/ca.crt" -CAkey "$OUT_DIR/ca.key" \
-CAcreateserial -out "$OUT_DIR/server.crt" -days "$DAYS_SERVER" -sha256
# 8. Convert server certificate to DER format
openssl x509 -in "$OUT_DIR/server.crt" -outform DER -out "$OUT_DIR/server.der"
xxd -i "$OUT_DIR/server.der" |sed -e "s/unsigned/const unsigned/g" | sed -e "s/build_certs_//g" > "$OUT_DIR/server_cert.c"
echo "==== Done ===="