diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml index c4cf27a6..a254cc38 100644 --- a/.github/workflows/cppcheck.yml +++ b/.github/workflows/cppcheck.yml @@ -1,5 +1,4 @@ -Name: CppCheck code linter - +name: CppCheck code linter on: push: @@ -9,16 +8,17 @@ on: jobs: linter: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - name: Update repo run: | sudo apt-get update + sudo apt install -y cppcheck - name: Run cppcheck run: | diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9ec07eb0..7acef615 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,5 +1,4 @@ -Name: Linux interop tests - +name: Linux interop tests on: push: @@ -8,17 +7,18 @@ on: branches: [ '*' ] jobs: - unit_test: - runs-on: ubuntu-22.04 + linux_test: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - name: Update repo run: | sudo apt-get update + sudo apt-get install -y libwolfssl-dev sudo modprobe tun - name: Build linux tests @@ -26,8 +26,21 @@ jobs: mkdir -p build/port make -# - name: Run interop tests -# run: | -# sudo build/test -# -# + - name: Run standalone "event loop" test + run: | + sudo ./build/test-evloop + sudo killall tcpdump || true + + - name: Run standalone wolfssl test + run: | + sudo ./build/test-wolfssl + sudo killall tcpdump || true + + - name: Run standalone forwarding test + run: | + sudo ./build/test-wolfssl-forwarding + + - name: Run standalone TTL expired test + run: | + ./build/test-ttl-expired + diff --git a/.github/workflows/units.yml b/.github/workflows/units.yml index a92a93ee..bebafe9a 100644 --- a/.github/workflows/units.yml +++ b/.github/workflows/units.yml @@ -1,5 +1,4 @@ -Name: Unit Tests - +name: Unit Tests on: push: @@ -9,10 +8,10 @@ on: jobs: unit_test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true diff --git a/Makefile b/Makefile index 5077b101..218b2a2b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ CC?=gcc CFLAGS:=-Wall -Werror -Wextra -I. -D_GNU_SOURCE -CFLAGS+=-g -ggdb +CFLAGS+=-g -ggdb -Wdeclaration-after-statement LDFLAGS+=-pthread CPPCHECK=cppcheck -CPPCHECK_FLAGS=--enable=all --suppress=missingIncludeSystem \ +CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \ + --suppress=missingIncludeSystem \ + -i src/test \ --suppress=unusedFunction --suppress=unusedVariable \ --suppress=missingInclude --suppress=variableScope \ --suppress=constVariable --suppress=constVariablePointer \ @@ -12,15 +14,18 @@ CPPCHECK_FLAGS=--enable=all --suppress=missingIncludeSystem \ --suppress=constParameterCallback \ --suppress=toomanyconfigs \ --suppress=unmatchedSuppression --inconclusive \ + --disable=style \ --std=c99 --language=c \ --platform=unix64 \ + --check-level=exhaustive \ --error-exitcode=1 --xml --xml-version=2 OBJ=build/wolfip.o \ build/port/posix/linux_tap.o EXE=build/tcpecho build/tcp_netcat_poll build/tcp_netcat_select \ - build/test-evloop build/test-dns + build/test-evloop build/test-dns build/test-wolfssl-forwarding \ + build/test-ttl-expired build/test-wolfssl LIB=libwolfip.so PREFIX=/usr/local @@ -80,12 +85,29 @@ build/tcp_netcat_select: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_net build/test-wolfssl:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP build/test-httpd:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP -Isrc/http +build/test-wolfssl-forwarding:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP -DWOLFIP_MAX_INTERFACES=2 -DWOLFIP_ENABLE_FORWARDING=1 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-wolfssl-forwarding: build/test/test_wolfssl_forwarding.o build/test/wolfip_forwarding.o build/port/posix/linux_tap.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/test_wolfssl_forwarding.o: CFLAGS+=-DWOLFIP_MAX_INTERFACES=2 -DWOLFIP_ENABLE_FORWARDING=1 + +build/test/wolfip_forwarding.o: src/wolfip.c + @mkdir -p `dirname $@` || true + @echo "[CC] $< (forwarding)" + @$(CC) $(CFLAGS) -DWOLFIP_MAX_INTERFACES=2 -DWOLFIP_ENABLE_FORWARDING=1 -c $< -o $@ + +build/test/test_ttl_expired.o: CFLAGS+=-DWOLFIP_MAX_INTERFACES=2 -DWOLFIP_ENABLE_FORWARDING=1 +build/test-ttl-expired: build/test/test_ttl_expired.o build/test/wolfip_forwarding.o + @echo "[LD] $@" + @$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -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 diff --git a/config.h b/config.h index 6455142b..2915717a 100644 --- a/config.h +++ b/config.h @@ -6,11 +6,27 @@ #define MAX_TCPSOCKETS 4 #define MAX_UDPSOCKETS 2 -#define RXBUF_SIZE LINK_MTU * 4 -#define TXBUF_SIZE LINK_MTU * 4 +#define RXBUF_SIZE LINK_MTU * 16 +#define TXBUF_SIZE LINK_MTU * 16 #define MAX_NEIGHBORS 16 +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 2 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 0 +#endif + +#ifndef WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_ENABLE_LOOPBACK 0 +#endif + +#if WOLFIP_ENABLE_LOOPBACK && WOLFIP_MAX_INTERFACES < 2 +#error "WOLFIP_ENABLE_LOOPBACK requires WOLFIP_MAX_INTERFACES > 1" +#endif + /* Linux test configuration */ #define WOLFIP_IP "10.10.10.2" #define LINUX_IP "10.10.10.1" diff --git a/docs/API.md b/docs/API.md index cf1d7b5f..70a45871 100644 --- a/docs/API.md +++ b/docs/API.md @@ -23,23 +23,32 @@ wolfIP is a minimal TCP/IP stack designed for resource-constrained embedded syst ### Device Driver Interface ```c -struct ll { +struct wolfIP_ll_dev { uint8_t mac[6]; // Device MAC address char ifname[16]; // Interface name - int (*poll)(struct ll *ll, void *buf, uint32_t len); // Receive function - int (*send)(struct ll *ll, void *buf, uint32_t len); // Transmit function + int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Receive function + int (*send)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Transmit function }; ``` +wolfIP maintains an array of these descriptors sized by `WOLFIP_MAX_INTERFACES` (default `1`). Call `wolfIP_getdev_ex()` to access a specific slot; the legacy `wolfIP_getdev()` helper targets the first hardware slot (index `0` normally, or `1` when the optional loopback interface is enabled). ### IP Configuration ```c struct ipconf { - struct ll *ll; // Link layer device + struct wolfIP_ll_dev *ll; // Link layer device ip4 ip; // IPv4 address ip4 mask; // Subnet mask ip4 gw; // Default gateway }; ``` +Each `struct wolfIP` instance owns `WOLFIP_MAX_INTERFACES` `ipconf` entries—one per link-layer slot. Use the `_ex` helpers to read or update a specific interface; the legacy accessors operate on the first hardware interface (index `0` unless loopback support is compiled in). + +If `WOLFIP_ENABLE_FORWARDING` is set to `1` at compile time, the stack performs simple IPv4 forwarding between interfaces. Packets received on one interface whose destinations match another configured interface are re-sent with the IP TTL decreased by one (or an ICMP TTL-exceeded response if the TTL would drop to zero). + +Enabling `WOLFIP_ENABLE_LOOPBACK` (requires `WOLFIP_MAX_INTERFACES > 1`) creates an internal loopback +device at index `0` with the fixed address `127.0.0.1/8`. Traffic sent to that address is reflected back +through the stack so local sockets, pings, and other services behave as they would on a standard +loopback interface; the first hardware interface then shifts to index `1` for legacy helpers. ### Socket Address Structures ```c @@ -151,6 +160,11 @@ Initializes a static wolfIP instance. - Parameters: - s: Pointer to wolfIP instance pointer +```c +size_t wolfIP_instance_size(void); +``` +Returns the size (in bytes) required to store a `struct wolfIP`. Use this when allocating stacks from custom memory managers. + ```c int wolfIP_poll(struct wolfIP *s, uint64_t now); ``` @@ -160,6 +174,12 @@ Processes pending network events. - now: Current timestamp - Returns: Number of events processed +```c +void wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len); +void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len); +``` +Pass inbound frames to the stack. `_ex` allows the caller to specify which interface slot produced the frame. + ```c void wolfIP_ipconfig_set(struct wolfIP *s, ip4 ip, ip4 mask, ip4 gw); void wolfIP_ipconfig_get(struct wolfIP *s, ip4 *ip, ip4 *mask, ip4 *gw); @@ -171,6 +191,18 @@ Set/get IP configuration. - mask: Subnet mask - gw: Default gateway +```c +void wolfIP_ipconfig_set_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, ip4 mask, ip4 gw); +void wolfIP_ipconfig_get_ex(struct wolfIP *s, unsigned int if_idx, ip4 *ip, ip4 *mask, ip4 *gw); +``` +Per-interface versions of the IP configuration helpers. The legacy functions target interface `0`. + +```c +struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s); +struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx); +``` +Access the link-layer descriptor(s) that should be wired to hardware drivers. `_ex` returns `NULL` if `if_idx` exceeds `WOLFIP_MAX_INTERFACES`. + ## DHCP Client Functions ```c diff --git a/src/http/httpd.c b/src/http/httpd.c index 8ff8d2f1..2ca7ca15 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -17,9 +17,34 @@ * 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 + * + * + * This is a simple HTTP server module for wolfIP. + * + * This file contains a basic implementation of a HTTP server + * that can be used with wolfIP. + * + * The HTTP server supports: + * - GET requests + * - POST requests + * - Basic file serving + * - Basic error handling + * + * Usage: + * - Initialize via httpd_init() + * - Add static pages via httpd_register_static_page() + * - Add request handlers via httpd_register_handler() + * + * Note: + * - Responses are sent immediately after generation. No buffering is done. + * If the output socket is flooded, extra responses will be discarded. + * + * */ #include "wolfip.h" #include "httpd.h" +#include +#include static const char *http_status_text(int status_code) { switch (status_code) { @@ -41,21 +66,13 @@ static const char *http_status_text(int status_code) { 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) { + /* Copy path and guarantee null termination */ strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN); + httpd->urls[i].path[HTTP_PATH_LEN-1] = '\0'; httpd->urls[i].handler = handler; return 0; } @@ -66,7 +83,9 @@ int httpd_register_handler(struct httpd *httpd, const char *path, int (*handler) 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) { + /* Copy path and guarantee null termination */ strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN); + httpd->urls[i].path[HTTP_PATH_LEN-1] = '\0'; httpd->urls[i].handler = NULL; httpd->urls[i].static_content = content; return 0; @@ -87,6 +106,7 @@ static struct http_url *http_find_url(struct httpd *httpd, const char *path) { 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]; + int rc; memset(txt_response, 0, sizeof(txt_response)); if (!hc) return; /* If content_lenght is 0, assume chunked encoding */ @@ -104,44 +124,77 @@ void http_send_response_headers(struct http_client *hc, int status_code, const c status_code, status_text, content_type, content_length); } if (hc->ssl) { - wolfSSL_write(hc->ssl, txt_response, strlen(txt_response)); + rc = wolfSSL_write(hc->ssl, txt_response, strlen(txt_response)); } else { - wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, txt_response, strlen(txt_response), 0); + rc = wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, txt_response, strlen(txt_response), 0); + } + if (rc <= 0) { + /* Error – close connection */ + wolfSSL_free(hc->ssl); + hc->ssl = NULL; + wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd); + hc->client_sd = 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 { - wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, body, len, 0); + int rc; + if (!hc) + return; + if (hc->ssl) + rc = wolfSSL_write(hc->ssl, body, len); + else + rc = wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, body, len, 0); + + if (rc <= 0) { + wolfSSL_free(hc->ssl); + hc->ssl = NULL; + wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd); + hc->client_sd = 0; } } +static int http_write_response(struct http_client *hc, const void *buf, size_t len) +{ + struct wolfIP *s; + if (!hc) + return -1; + s = hc->httpd->ipstack; + if (hc->ssl) + return wolfSSL_write(hc->ssl, buf, len); + else + return wolfIP_sock_send(s, hc->client_sd, buf, 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; + if (!hc) + return; snprintf(txt_chunk, sizeof(txt_chunk), "%zx\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 wolfIP *s = hc->httpd->ipstack; - wolfIP_sock_send(s, hc->client_sd, txt_chunk, strlen(txt_chunk), 0); - wolfIP_sock_send(s, hc->client_sd, chunk, len, 0); - wolfIP_sock_send(s, hc->client_sd, "\r\n", 2, 0); + if ((http_write_response(hc, txt_chunk, strlen(txt_chunk)) <= 0) || + (http_write_response(hc, chunk, len) <= 0) || + (http_write_response(hc, "\r\n", 2) <= 0)) { + wolfSSL_free(hc->ssl); + hc->ssl = NULL; + wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd); + hc->client_sd = 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 { - wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, "0\r\n\r\n", 5, 0); + int rc; + if (!hc) + return; + if (hc->ssl) + rc = wolfSSL_write(hc->ssl, "0\r\n\r\n", 5); + else + rc = wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, "0\r\n\r\n", 5, 0); + if (rc <= 0) { + wolfSSL_free(hc->ssl); + hc->ssl = NULL; + wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd); + hc->client_sd = 0; } } @@ -165,22 +218,47 @@ void http_send_418_teapot(struct http_client *hc) { http_status_text(HTTP_STATUS_TEAPOT), "text/plain", 0); } -int http_url_decode(char *buf, size_t len) { +static int http_hex_value(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + c = (char)tolower((unsigned char)c); + if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + return -1; +} + +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) { + char *end = buf + len; + int hi; + int lo; + size_t tail; + + while (p < end) { + char *percent = memchr(p, '%', (size_t)(end - p)); + if (!percent) break; - } - *q = (char) strtol(q + 1, NULL, 16); - memmove(q + 1, q + 3, len - (q + 3 - buf)); + + if (percent + 2 >= end) + return HTTP_URL_DECODE_ERR_TRUNCATED; + + hi = http_hex_value(percent[1]); + lo = http_hex_value(percent[2]); + if (hi < 0 || lo < 0) + return HTTP_URL_DECODE_ERR_BAD_ESCAPE; + + *percent = (char)((hi << 4) | lo); + + tail = (size_t)(end - (percent + 3)); + memmove(percent + 1, percent + 3, tail); + end -= 2; len -= 2; + p = percent + 1; } - return len; + + return (int)len; } int http_url_encode(char *buf, size_t len, size_t max_len) { @@ -194,14 +272,18 @@ int http_url_encode(char *buf, size_t len, size_t max_len) { if (len + 2 >= max_len) { return -1; /* Not enough space */ } + /* Shift memory to create space for %20 */ memmove(q + 3, q + 1, len - (q + 1 - buf)); *q = '%'; *(q + 1) = '2'; *(q + 2) = '0'; len += 2; } - if (q) + if (q) { + if (len >= max_len) + return -1; /* No space for the null terminator */ q[len] = '\0'; + } return len; } @@ -211,43 +293,63 @@ static int parse_http_request(struct http_client *hc, uint8_t *buf, size_t len) char *q; size_t n; int ret; + int decoded_len; 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; + decoded_len = http_url_decode(p, len); /* Decode can be done in place */ + if (decoded_len < 0) { + http_send_response_headers(hc, HTTP_STATUS_BAD_REQUEST, + http_status_text(HTTP_STATUS_BAD_REQUEST), "text/plain", 0); + return decoded_len; + } + len = (size_t)decoded_len; + end = p + len; + if (len < 4) + goto bad_request; /* Parse the request line */ q = strchr(p, ' '); - if (!q) goto bad_request; + if (!q) + goto bad_request; n = q - p; - if (n >= sizeof(req.method)) goto bad_request; + 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; + if (!q) + goto bad_request; n = q - p; - if (n >= sizeof(req.path)) goto bad_request; + 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; + if (!q) + goto bad_request; n = q - p; - if (n >= sizeof(req.query)) goto bad_request; + if (n >= sizeof(req.query)) + goto bad_request; memcpy(req.query, p, n); req.query[n] = '\0'; p = q + 2; /* Parse the headers */ + /* Parse headers – keep them in a single buffer to avoid allocations */ while (p < end) { q = strstr(p, "\r\n"); - if (!q) goto bad_request; + if (!q) + goto bad_request; n = q - p; if (n == 0) { - break; + break; /* End of headers */ } - if (n >= sizeof(req.headers)) goto bad_request; + /* Enforce header maximum length */ + if (n >= sizeof(req.headers)) + goto bad_request; + /* Copy header and terminate */ memcpy(req.headers, p, n); req.headers[n] = '\0'; p = q + 2; @@ -266,8 +368,9 @@ static int parse_http_request(struct http_client *hc, uint8_t *buf, size_t len) 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) + goto not_found; + if ((url->handler == NULL) && (url->static_content == NULL)) goto service_unavailable; if (url->handler == NULL) { @@ -307,7 +410,7 @@ static void http_recv_cb(int sd, uint16_t event, void *arg) { } } else { ret = wolfIP_sock_recv(hc->httpd->ipstack, sd, buf, sizeof(buf), 0); - if (ret == -11) + if (ret == -WOLFIP_EAGAIN) return; } if (ret <= 0) @@ -344,7 +447,7 @@ static void http_accept_cb(int sd, uint16_t event, void *arg) { 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); + wolfSSL_SetIO_wolfIP(httpd->clients[i].ssl, client_sd); } else { /* Failed to create SSL object */ wolfIP_sock_close(httpd->ipstack, client_sd); @@ -417,13 +520,8 @@ int httpd_init(struct httpd *httpd, struct wolfIP *s, uint16_t port, void *ssl_c } if (ssl_ctx) { httpd->ssl_ctx = (WOLFSSL_CTX *) ssl_ctx; - wolfSSL_SetIO_FT_CTX(httpd->ssl_ctx, httpd->ipstack); + wolfSSL_SetIO_wolfIP_CTX(httpd->ssl_ctx, httpd->ipstack); } wolfIP_register_callback(s, httpd->listen_sd, http_accept_cb, httpd); return 0; } - - - - - diff --git a/src/http/httpd.h b/src/http/httpd.h index 0265e39e..a94a2452 100644 --- a/src/http/httpd.h +++ b/src/http/httpd.h @@ -78,6 +78,11 @@ 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); +/* URL decoding return codes */ +#define HTTP_URL_DECODE_ERR_TRUNCATED (-1) +#define HTTP_URL_DECODE_ERR_BAD_ESCAPE (-2) + +/* Returns the decoded length on success, or negative error code on failure. */ int http_url_decode(char *buf, size_t len); int http_url_encode(char *buf, size_t len, size_t max_len); diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index b2c8f949..ec825178 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -101,10 +101,10 @@ static int (*host_fcntl) (int fd, int cmd, ...); int __wolfip_retval; \ do { \ __wolfip_retval = wolfIP_sock_##call(IPSTACK, fd, ## __VA_ARGS__); \ - if (__wolfip_retval == -11) { \ + if (__wolfip_retval == -EAGAIN) { \ usleep(1000); \ } \ - } while (__wolfip_retval == -11); \ + } while (__wolfip_retval == -EAGAIN); \ if (__wolfip_retval < 0) { \ errno = __wolfip_retval; \ pthread_mutex_unlock(&wolfIP_mutex); \ @@ -153,10 +153,10 @@ int wolfIP_sock_fcntl(struct wolfIP *ipstack, int fd, int cmd, int arg) { int fcntl(int fd, int cmd, ...) { va_list ap; int arg; + int ret; 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 { @@ -534,7 +534,7 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) { /* 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); +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); void *wolfIP_sock_posix_ip_loop(void *arg) { struct wolfIP *ipstack = (struct wolfIP *)arg; @@ -553,7 +553,7 @@ void *wolfIP_sock_posix_ip_loop(void *arg) { void __attribute__((constructor)) init_wolfip_posix() { struct in_addr linux_ip; - struct ll *tapdev; + struct wolfIP_ll_dev *tapdev; pthread_t wolfIP_thread; if (IPSTACK) return; diff --git a/src/port/posix/linux_tap.c b/src/port/posix/linux_tap.c index 65cc824c..a7764fbf 100644 --- a/src/port/posix/linux_tap.c +++ b/src/port/posix/linux_tap.c @@ -45,11 +45,11 @@ 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 wolfIP_ll_dev *ll, void *buf, uint32_t len) { struct pollfd pfd; - (void)ll; int ret; + (void)ll; pfd.fd = tap_fd; pfd.events = POLLIN; ret = poll(&pfd, 1, 2); @@ -63,14 +63,14 @@ 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 wolfIP_ll_dev *ll, void *buf, uint32_t len) { (void)ll; //print_buffer(buf, len); return write(tap_fd, buf, len); } -int tap_init(struct ll *ll, const char *ifname, uint32_t host_ip) +int tap_init(struct wolfIP_ll_dev *ll, const char *ifname, uint32_t host_ip) { struct ifreq ifr; struct sockaddr_in *addr; @@ -83,6 +83,7 @@ int tap_init(struct ll *ll, const char *ifname, uint32_t host_ip) memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; if (ioctl(tap_fd, TUNSETIFF, (void *)&ifr) < 0) { perror("ioctl TUNSETIFF"); close(tap_fd); diff --git a/src/port/raspberry-pico-usb-server/src/main.c b/src/port/raspberry-pico-usb-server/src/main.c index c4c6b9af..d4ee612b 100644 --- a/src/port/raspberry-pico-usb-server/src/main.c +++ b/src/port/raspberry-pico-usb-server/src/main.c @@ -77,7 +77,7 @@ uint32_t wolfIP_getrandom(void) * It is called by the wolfIP 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) { +static int ll_usb_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t sz) { uint16_t sz16 = (uint16_t)sz; uint32_t i; (void) dev; @@ -152,7 +152,7 @@ bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { * * 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 ll_usb_poll(struct wolfIP_ll_dev *dev, void *frame, uint32_t sz) { int i; (void) dev; if (sz < 64) @@ -213,7 +213,7 @@ static void telnetd_init(void) int main(void) { - struct ll *tusb_netdev; + struct wolfIP_ll_dev *tusb_netdev; /* initialize TinyUSB */ board_init(); diff --git a/src/port/wolfssl_io.c b/src/port/wolfssl_io.c index 83c50c55..dbd1f4be 100644 --- a/src/port/wolfssl_io.c +++ b/src/port/wolfssl_io.c @@ -1,6 +1,5 @@ /* wolfssl_io.c - * - * Copyright (C) 2024 wolfSSL Inc. + * Copyright (C) 2025 wolfSSL Inc. * * This file is part of wolfIP TCP/IP stack. * @@ -17,55 +16,122 @@ * 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 + * + * wolfIP <-> wolfSSL glue for custom IO callbacks. */ #include "wolfip.h" #include #include +#include + + +#ifndef MAX_WOLFIP_CTX + #define MAX_WOLFIP_CTX 8 /* Default value */ +#endif + +struct ctx_entry { + WOLFSSL_CTX *ctx; + struct wolfIP *stack; +}; + +struct wolfip_io_desc { + int fd; + struct wolfIP *stack; +}; + +static struct ctx_entry ctx_map[MAX_WOLFIP_CTX]; +static struct wolfip_io_desc io_descs[MAX_WOLFIP_CTX]; -static struct wolfIP *ref_ipstack = NULL; +static struct wolfIP *wolfIP_lookup_stack(WOLFSSL_CTX *ctx) +{ + for (int i = 0; i < MAX_WOLFIP_CTX; i++) { + if (ctx_map[i].ctx == ctx) + return ctx_map[i].stack; + } + return NULL; +} + +static void wolfIP_register_stack(WOLFSSL_CTX *ctx, struct wolfIP *stack) +{ + for (int i = 0; i < MAX_WOLFIP_CTX; i++) { + if (ctx_map[i].ctx == ctx || ctx_map[i].ctx == NULL) { + ctx_map[i].ctx = ctx; + ctx_map[i].stack = stack; + return; + } + } +} static int wolfIP_io_recv(WOLFSSL* ssl, char* buf, int sz, void* ctx) { - int ret = 0; - int fd = (intptr_t)ctx; + struct wolfip_io_desc *desc = (struct wolfip_io_desc *)ctx; + int ret; (void)ssl; - if (!ref_ipstack) - return -1; - ret = wolfIP_sock_recv(ref_ipstack, fd, buf, sz, 0); - if (ret == -11) + + if (!desc || !desc->stack) + return WOLFSSL_CBIO_ERR_GENERAL; + + ret = wolfIP_sock_recv(desc->stack, desc->fd, buf, sz, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) return WOLFSSL_CBIO_ERR_WANT_READ; - else if (ret <= 0) + if (ret <= 0) return WOLFSSL_CBIO_ERR_CONN_CLOSE; return ret; } static int wolfIP_io_send(WOLFSSL* ssl, char* buf, int sz, void* ctx) { - int ret = 0; - int fd = (intptr_t)ctx; + struct wolfip_io_desc *desc = (struct wolfip_io_desc *)ctx; + int ret; (void)ssl; - if (!ref_ipstack) - return -1; - ret = wolfIP_sock_send(ref_ipstack, fd, buf, sz, 0); - if (ret == -11) + + if (!desc || !desc->stack) + return WOLFSSL_CBIO_ERR_GENERAL; + + ret = wolfIP_sock_send(desc->stack, desc->fd, buf, sz, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) return WOLFSSL_CBIO_ERR_WANT_WRITE; - else if (ret <= 0) + if (ret <= 0) return WOLFSSL_CBIO_ERR_CONN_CLOSE; return ret; } -int wolfSSL_SetIO_FT_CTX(WOLFSSL_CTX* ctx, struct wolfIP *s) +int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX* ctx, struct wolfIP *s) { wolfSSL_SetIORecv(ctx, wolfIP_io_recv); wolfSSL_SetIOSend(ctx, wolfIP_io_send); - ref_ipstack = s; + wolfIP_register_stack(ctx, s); return 0; } -int wolfSSL_SetIO_FT(WOLFSSL* ssl, int fd) +int wolfSSL_SetIO_wolfIP(WOLFSSL* ssl, int fd) { - wolfSSL_SetIOReadCtx(ssl, (void*)(intptr_t)fd); - wolfSSL_SetIOWriteCtx(ssl, (void*)(intptr_t)fd); - return 0; -} + WOLFSSL_CTX *ctx; + struct wolfIP *stack; + + if (!ssl) + return -1; + + ctx = wolfSSL_get_SSL_CTX(ssl); + + if (!ctx) + return -1; + stack = wolfIP_lookup_stack(ctx); + if (fd < 0) + return -1; + + if (!stack) + return WOLFSSL_CBIO_ERR_GENERAL; + + for (int i = 0; i < MAX_WOLFIP_CTX; i++) { + if (io_descs[i].stack == NULL) { + io_descs[i].fd = fd; + io_descs[i].stack = stack; + wolfSSL_SetIOReadCtx(ssl, &io_descs[i]); + wolfSSL_SetIOWriteCtx(ssl, &io_descs[i]); + return 0; + } + } + return -1; +} diff --git a/src/test/tcp_echo.c b/src/test/tcp_echo.c index fd63a3e9..ae5f0d1c 100644 --- a/src/test/tcp_echo.c +++ b/src/test/tcp_echo.c @@ -63,6 +63,7 @@ int main() { printf("Echo server listening on port %d\n", PORT); while (1) { + ssize_t bytes_read; // Accept a client connection if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("Accept failed"); @@ -71,7 +72,6 @@ int main() { 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 } diff --git a/src/test/test_httpd.c b/src/test/test_httpd.c index 109ff86e..ca5332f3 100644 --- a/src/test/test_httpd.c +++ b/src/test/test_httpd.c @@ -71,7 +71,7 @@ static int test_loop(struct wolfIP *s, int active_close) /* 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); +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); /* Test cases */ @@ -126,7 +126,7 @@ static void test_httpd(struct wolfIP *s) int main(int argc, char **argv) { struct wolfIP *s; - struct ll *tapdev; + struct wolfIP_ll_dev *tapdev; struct timeval tv = {0, 0}; struct in_addr linux_ip; uint32_t srv_ip; diff --git a/src/test/test_linux_dhcp_dns.c b/src/test/test_linux_dhcp_dns.c index 2d859ad8..3133f367 100644 --- a/src/test/test_linux_dhcp_dns.c +++ b/src/test/test_linux_dhcp_dns.c @@ -75,7 +75,7 @@ static void client_cb(int fd, uint16_t event, void *arg) while ((total_r < total_w) && (event & CB_EVENT_READABLE)) { ret = wolfIP_sock_recvfrom(s, fd, buf + total_r, sizeof(buf) - total_r, 0, NULL, NULL); if (ret < 0){ - if (ret != -11) { + if (ret != -EAGAIN) { printf("Client read: %d\n", ret); } return; @@ -192,7 +192,7 @@ static void *pt_echoserver(void *arg) /* 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); +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); void test_wolfip_echoclient(struct wolfIP *s) { @@ -237,7 +237,7 @@ void ns_cb(uint32_t ip) int main(int argc, char **argv) { struct wolfIP *s; - struct ll *tapdev; + struct wolfIP_ll_dev *tapdev; struct timeval tv; struct in_addr linux_ip; uint32_t srv_ip; diff --git a/src/test/test_linux_eventloop.c b/src/test/test_linux_eventloop.c index 9e8840b9..1587b59b 100644 --- a/src/test/test_linux_eventloop.c +++ b/src/test/test_linux_eventloop.c @@ -58,7 +58,7 @@ static void server_cb(int fd, uint16_t event, void *arg) } } else if ((fd == client_fd) && (event & CB_EVENT_READABLE )) { ret = wolfIP_sock_recvfrom((struct wolfIP *)arg, client_fd, buf, sizeof(buf), 0, NULL, NULL); - if (ret != -11) { + if (ret != -EAGAIN) { if (ret < 0) { printf("Recv error: %d\n", ret); wolfIP_sock_close((struct wolfIP *)arg, client_fd); @@ -83,7 +83,7 @@ static void server_cb(int fd, uint16_t event, void *arg) } if ((!closed) && (tot_sent < tot_recv)) { snd_ret = wolfIP_sock_sendto((struct wolfIP *)arg, client_fd, buf + tot_sent, tot_recv - tot_sent, 0, NULL, 0); - if (snd_ret != -11) { + if (snd_ret != -EAGAIN) { if (snd_ret < 0) { printf("Send error: %d\n", snd_ret); wolfIP_sock_close((struct wolfIP *)arg, client_fd); @@ -141,7 +141,7 @@ static void client_cb(int fd, uint16_t event, void *arg) while ((total_r < total_w) && (event & CB_EVENT_READABLE)) { ret = wolfIP_sock_recvfrom(s, fd, buf + total_r, sizeof(buf) - total_r, 0, NULL, NULL); if (ret < 0){ - if (ret != -11) { + if (ret != -EAGAIN) { printf("Client read: %d\n", ret); } return; @@ -324,7 +324,7 @@ static void *pt_echoserver(void *arg) /* 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); +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); /* Test cases */ @@ -420,7 +420,7 @@ void test_wolfip_echoclient(struct wolfIP *s) int main(int argc, char **argv) { struct wolfIP *s; - struct ll *tapdev; + struct wolfIP_ll_dev *tapdev; struct timeval tv = {0, 0}; struct in_addr linux_ip; uint32_t srv_ip; diff --git a/src/test/test_native_wolfssl.c b/src/test/test_native_wolfssl.c index f2b3ad5b..03bc1648 100644 --- a/src/test/test_native_wolfssl.c +++ b/src/test/test_native_wolfssl.c @@ -53,8 +53,8 @@ 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 wolfIP *s); +int wolfSSL_SetIO_wolfIP(WOLFSSL* ssl, int fd); +int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s); /* wolfIP: server side callback. */ static void server_cb(int fd, uint16_t event, void *arg) @@ -70,7 +70,7 @@ static void server_cb(int fd, uint16_t event, void *arg) printf("Failed to create server SSL object\n"); return; } - wolfSSL_SetIO_FT(server_ssl, client_fd); + wolfSSL_SetIO_wolfIP(server_ssl, client_fd); /* Accepting the TLS session is not necessary here, as the * first read will trigger the handshake. */ @@ -257,7 +257,7 @@ void *pt_echoclient(void *arg) /* 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); +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); /* Test cases */ @@ -285,7 +285,7 @@ void test_wolfip_echoserver(struct wolfIP *s, uint32_t srv_ip) return; } printf("Associating server context with wolfIP\n"); - wolfSSL_SetIO_FT_CTX(server_ctx, s); + wolfSSL_SetIO_wolfIP_CTX(server_ctx, s); printf("Importing server certificate\n"); ret = wolfSSL_CTX_use_certificate_buffer(server_ctx, server_der, @@ -333,7 +333,7 @@ void test_wolfip_echoserver(struct wolfIP *s, uint32_t srv_ip) int main(int argc, char **argv) { struct wolfIP *s; - struct ll *tapdev; + struct wolfIP_ll_dev *tapdev; struct timeval tv = {0, 0}; struct in_addr linux_ip; uint32_t srv_ip; diff --git a/src/test/test_ttl_expired.c b/src/test/test_ttl_expired.c new file mode 100644 index 00000000..a29ce07e --- /dev/null +++ b/src/test/test_ttl_expired.c @@ -0,0 +1,416 @@ +/* test_ttl_expired.c + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + * + * Validate that wolfIP emits an ICMP TTL Expired message when forwarding + * a packet whose TTL reaches zero. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "wolfip.h" + +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 2 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 1 +#endif + +#ifndef IP4 +#define IP4(a,b,c,d) (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | \ + ((uint32_t)(c) << 8) | (uint32_t)(d)) +#endif + +#define HOST_IP IP4(10,0,1,10) +#define ROUTER_IF0 IP4(10,0,1,1) +#define ROUTER_GW IP4(10,0,1,254) +#define ROUTER_IF1 IP4(10,0,2,1) +#define DEST_IP IP4(10,0,2,200) +#define TTL_EXCEEDED_DATA_LEN 28 + +#define PACKED __attribute__((packed)) + +struct eth_hdr { + uint8_t dst[6]; + uint8_t src[6]; + uint16_t type; +} PACKED; + +struct ipv4_hdr { + uint8_t ver_ihl; + uint8_t tos; + uint16_t len; + uint16_t id; + uint16_t flags_frag; + uint8_t ttl; + uint8_t proto; + uint16_t csum; + uint32_t src; + uint32_t dst; +} PACKED; + +struct icmp_echo { + uint8_t type; + uint8_t code; + uint16_t csum; + uint16_t id; + uint16_t seq; +} PACKED; + +struct icmp_ttl_exceeded { + uint8_t type; + uint8_t code; + uint16_t csum; + uint8_t unused[4]; + uint8_t data[TTL_EXCEEDED_DATA_LEN]; +} PACKED; + +static uint16_t ones_csum(const void *buf, size_t len) +{ + const uint16_t *p = buf; + uint32_t acc = 0; + while (len > 1) { + acc += *p++; + len -= 2; + } + if (len) + acc += *((const uint8_t *)p); + while (acc >> 16) + acc = (acc & 0xFFFF) + (acc >> 16); + return (uint16_t)~acc; +} + +uint32_t wolfIP_getrandom(void) +{ + return (uint32_t)rand(); +} + +struct mem_link { + pthread_mutex_t lock; + pthread_cond_t cond[2]; + int ready[2]; + size_t len[2]; + uint8_t buf[2][LINK_MTU]; +}; + +struct mem_ep { + struct wolfIP_ll_dev *ll; + struct mem_link *link; + int idx; +}; + +static struct mem_ep mem_eps[2]; + +static void mem_link_init(struct mem_link *link) +{ + pthread_mutex_init(&link->lock, NULL); + pthread_cond_init(&link->cond[0], NULL); + pthread_cond_init(&link->cond[1], NULL); + link->ready[0] = link->ready[1] = 0; + link->len[0] = link->len[1] = 0; +} + +static struct mem_ep *mem_ep_lookup(struct wolfIP_ll_dev *ll) +{ + for (size_t i = 0; i < 2; i++) + if (mem_eps[i].ll == ll) + return &mem_eps[i]; + return NULL; +} + +static int mem_ll_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct mem_ep *ep = mem_ep_lookup(ll); + struct mem_link *link; + int idx; + int ret = 0; + + if (!ep) + return -1; + link = ep->link; + idx = ep->idx; + + pthread_mutex_lock(&link->lock); + if (link->ready[idx]) { + size_t copy = link->len[idx]; + if (copy > len) + copy = len; + memcpy(buf, link->buf[idx], copy); + link->ready[idx] = 0; + pthread_cond_signal(&link->cond[idx]); + ret = (int)copy; + } + pthread_mutex_unlock(&link->lock); + return ret; +} + +static int mem_ll_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct mem_ep *ep = mem_ep_lookup(ll); + struct mem_link *link; + int dst; + + if (!ep) + return -1; + link = ep->link; + dst = 1 - ep->idx; + + pthread_mutex_lock(&link->lock); + while (link->ready[dst]) + pthread_cond_wait(&link->cond[dst], &link->lock); + if (len > LINK_MTU) + len = LINK_MTU; + memcpy(link->buf[dst], buf, len); + link->len[dst] = len; + link->ready[dst] = 1; + pthread_cond_signal(&link->cond[dst]); + pthread_mutex_unlock(&link->lock); + return (int)len; +} + +static void mem_attach(struct wolfIP_ll_dev *ll, struct mem_link *link, int idx, const uint8_t *mac) +{ + ll->poll = mem_ll_poll; + ll->send = mem_ll_send; + memcpy(ll->mac, mac, 6); + snprintf(ll->ifname, sizeof(ll->ifname), "mem%d", idx); + mem_eps[idx].ll = ll; + mem_eps[idx].link = link; + mem_eps[idx].idx = idx; +} + +static void mem_host_send(struct mem_link *link, const uint8_t *frame, size_t len) +{ + pthread_mutex_lock(&link->lock); + while (link->ready[1]) + pthread_cond_wait(&link->cond[1], &link->lock); + if (len > LINK_MTU) + len = LINK_MTU; + memcpy(link->buf[1], frame, len); + link->len[1] = len; + link->ready[1] = 1; + pthread_cond_signal(&link->cond[1]); + pthread_mutex_unlock(&link->lock); +} + +static int mem_host_recv(struct mem_link *link, uint8_t *frame, size_t cap, int timeout_ms) +{ + int ret = -1; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeout_ms / 1000; + ts.tv_nsec += (timeout_ms % 1000) * 1000000L; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + pthread_mutex_lock(&link->lock); + while (!link->ready[0]) { + int rc = pthread_cond_timedwait(&link->cond[0], &link->lock, &ts); + if (rc == ETIMEDOUT) + goto out; + } + if (link->len[0] > cap) + cap = link->len[0]; + memcpy(frame, link->buf[0], link->len[0]); + ret = (int)link->len[0]; + link->ready[0] = 0; + pthread_cond_signal(&link->cond[0]); +out: + pthread_mutex_unlock(&link->lock); + return ret; +} + +static void build_ttl_frame(uint8_t *frame, const uint8_t *src_mac, const uint8_t *dst_mac, + uint32_t src_ip, uint32_t dst_ip) +{ + struct eth_hdr *eth = (struct eth_hdr *)frame; + struct ipv4_hdr *ip = (struct ipv4_hdr *)(frame + sizeof(*eth)); + struct icmp_echo *icmp = (struct icmp_echo *)(frame + sizeof(*eth) + sizeof(*ip)); + + memcpy(eth->dst, dst_mac, 6); + memcpy(eth->src, src_mac, 6); + eth->type = htons(ETH_P_IP); + + memset(ip, 0, sizeof(*ip)); + ip->ver_ihl = 0x45; + ip->ttl = 1; + ip->proto = 1; + ip->len = htons(sizeof(*ip) + sizeof(*icmp)); + ip->id = htons(0x1234); + ip->src = htonl(src_ip); + ip->dst = htonl(dst_ip); + ip->csum = ones_csum(ip, sizeof(*ip)); + + memset(icmp, 0, sizeof(*icmp)); + icmp->type = 8; + icmp->id = htons(0x0101); + icmp->seq = htons(1); + icmp->csum = ones_csum(icmp, sizeof(*icmp)); +} + +static int dummy_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + (void)ll; + (void)buf; + return (int)len; +} + +static int dummy_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + (void)ll; + (void)buf; + (void)len; + return 0; +} + +static volatile int running = 1; + +static void *poll_thread(void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + while (running) { + struct timespec ts; + uint64_t now; + + clock_gettime(CLOCK_MONOTONIC, &ts); + now = (uint64_t)ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000ULL; + wolfIP_poll(s, now); + usleep(1000); + } + return NULL; +} + +int main(void) +{ + struct wolfIP *router; + struct wolfIP_ll_dev *iface0; + struct wolfIP_ll_dev *iface1; + struct mem_link link; + pthread_t th; + uint8_t host_mac[6] = {0x02,0x00,0x00,0x00,0xAA,0x01}; + uint8_t router0_mac[6] = {0x02,0x00,0x00,0x00,0xBB,0x01}; + uint8_t router1_mac[6] = {0x02,0x00,0x00,0x00,0xCC,0x01}; + uint8_t frame[LINK_MTU]; + int rc = EXIT_FAILURE; + int n; + + setvbuf(stdout, NULL, _IONBF, 0); + + mem_link_init(&link); + wolfIP_init_static(&router); + + iface0 = wolfIP_getdev(router); + iface1 = wolfIP_getdev_ex(router, 1); + if (!iface0 || !iface1) { + fprintf(stderr, "missing interfaces\n"); + return EXIT_FAILURE; + } + + mem_attach(iface0, &link, 1, router0_mac); + + iface1->send = dummy_send; + iface1->poll = dummy_poll; + memcpy(iface1->mac, router1_mac, 6); + + wolfIP_ipconfig_set(router, ROUTER_IF0, IP4(255,255,255,0), ROUTER_GW); + wolfIP_ipconfig_set_ex(router, 1, ROUTER_IF1, IP4(255,255,255,0), IP4(0,0,0,0)); + + running = 1; + if (pthread_create(&th, NULL, poll_thread, router) != 0) { + perror("pthread_create"); + return EXIT_FAILURE; + } + + build_ttl_frame(frame, host_mac, router0_mac, HOST_IP, DEST_IP); + mem_host_send(&link, frame, sizeof(struct eth_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct icmp_echo)); + + n = mem_host_recv(&link, frame, sizeof(frame), 1000); + if (n <= 0) { + fprintf(stderr, "No TTL expired response\n"); + goto cleanup; + } + + { + struct eth_hdr *eth = (struct eth_hdr *)frame; + struct ipv4_hdr *ip = (struct ipv4_hdr *)(frame + sizeof(*eth)); + struct icmp_ttl_exceeded *icmp = (struct icmp_ttl_exceeded *)(frame + sizeof(*eth) + sizeof(*ip)); + struct ipv4_hdr *orig_ip = (struct ipv4_hdr *)icmp->data; + struct icmp_echo *orig_icmp = (struct icmp_echo *)(icmp->data + sizeof(*orig_ip)); + size_t expected_len = sizeof(*eth) + sizeof(*ip) + sizeof(*icmp); + uint16_t ip_len = ntohs(ip->len); + uint16_t expected_ip_len = (uint16_t)(sizeof(*ip) + sizeof(*icmp)); + + if ((size_t)n != expected_len) { + fprintf(stderr, "Unexpected frame length: got %d expected %zu\n", n, expected_len); + goto mismatch; + } + if (ntohs(eth->type) != ETH_P_IP || + memcmp(eth->dst, host_mac, sizeof(host_mac)) != 0 || + memcmp(eth->src, router0_mac, sizeof(router0_mac)) != 0) { + fprintf(stderr, "Ethernet header mismatch\n"); + goto mismatch; + } + if (ip->ver_ihl != 0x45 || ip->proto != 1 || ip->ttl != 64 || + ip_len != expected_ip_len || + ntohl(ip->src) != ROUTER_IF0 || ntohl(ip->dst) != HOST_IP) { + fprintf(stderr, "IPv4 header mismatch\n"); + goto mismatch; + } + if (icmp->type != 11 || icmp->code != 0 || + memcmp(icmp->unused, "\x00\x00\x00\x00", sizeof(icmp->unused)) != 0) { + fprintf(stderr, "ICMP header mismatch\n"); + goto mismatch; + } + if (orig_ip->ver_ihl != 0x45 || orig_ip->proto != 1 || + ntohl(orig_ip->src) != HOST_IP || ntohl(orig_ip->dst) != DEST_IP || + ntohs(orig_ip->len) != sizeof(*orig_ip) + sizeof(struct icmp_echo) || + ntohs(orig_ip->id) != 0x1234 || orig_ip->ttl != 1) { + fprintf(stderr, "Embedded IPv4 header mismatch\n"); + goto mismatch; + } + if (orig_icmp->type != 8 || orig_icmp->code != 0 || + ntohs(orig_icmp->id) != 0x0101 || ntohs(orig_icmp->seq) != 1) { + fprintf(stderr, "Embedded ICMP header mismatch\n"); + goto mismatch; + } + printf("TTL expired response received\n"); + rc = EXIT_SUCCESS; + goto cleanup; + } + +mismatch: + fprintf(stderr, "TTL expired response mismatch\n"); + +cleanup: + running = 0; + pthread_join(th, NULL); + return rc; +} diff --git a/src/test/test_wolfssl_forwarding.c b/src/test/test_wolfssl_forwarding.c new file mode 100644 index 00000000..e1ef5fe1 --- /dev/null +++ b/src/test/test_wolfssl_forwarding.c @@ -0,0 +1,663 @@ +/* test_wolfssl_forwarding.c + * + * Simplified forwarding test that exercises a wolfIP TLS echo server + * reachable through a wolfIP router while the client runs on the host + * using the Linux TCP/IP stack. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 2 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 1 +#endif + +#include "wolfip.h" +#include +#include +#include +#include + +extern const unsigned char ca_der[]; +extern const unsigned long ca_der_len; +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; + +extern int tap_init(struct wolfIP_ll_dev *dev, const char *name, uint32_t host_ip); + +#define IP4(a,b,c,d) (((ip4)(a) << 24) | ((ip4)(b) << 16) | ((ip4)(c) << 8) | (ip4)(d)) + +#define TEST_PAYLOAD 1024 +#define TAP_IFNAME "wtls0" +#define HOST_ROUTE "10.20.2.0/24" + +static const ip4 host_ip4 = IP4(10,20,1,2); +static const ip4 router_lan_ip4 = IP4(10,20,1,254); +static const ip4 router_wan_ip4 = IP4(10,20,2,1); +static const ip4 server_ip4 = IP4(10,20,2,2); + +static pthread_t th_server; +static pthread_t th_router; + +static struct wolfIP *router_stack; +static struct wolfIP *server_stack; + +static int server_listen_fd = -1; +static int server_client_fd = -1; +static WOLFSSL_CTX *server_ctx = NULL; +static WOLFSSL *server_ssl = NULL; +static uint8_t server_buf[TEST_PAYLOAD]; +static int server_bytes_recv = 0; +static int server_bytes_sent = 0; +static volatile int server_done = 0; +static int server_handshake_done = 0; + +static volatile int router_running = 1; + +static void ip4_to_str(ip4 addr, char *buf, size_t len) +{ + snprintf(buf, len, "%u.%u.%u.%u", + (unsigned)((addr >> 24) & 0xFF), + (unsigned)((addr >> 16) & 0xFF), + (unsigned)((addr >> 8) & 0xFF), + (unsigned)(addr & 0xFF)); +} + +static uint64_t monotonic_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000ULL; +} + +static void wolfip_reset_io(WOLFSSL *ssl) +{ + void *ctx; + if (!ssl) + return; + ctx = wolfSSL_GetIOReadCtx(ssl); + if (ctx) { + wolfSSL_SetIOReadCtx(ssl, NULL); + wolfSSL_SetIOWriteCtx(ssl, NULL); + } +} + +/* ------------------------------------------------------------------------- */ +/* In-memory link layer */ +/* ------------------------------------------------------------------------- */ + +struct mem_link { + pthread_mutex_t lock; + pthread_cond_t cond[2]; + uint8_t buf[2][LINK_MTU]; + uint32_t len[2]; + int ready[2]; + const char *name[2]; +}; + +struct mem_ep { + struct wolfIP_ll_dev *ll; + struct mem_link *link; + int idx; +}; + +static struct mem_ep mem_eps[8]; +static size_t mem_ep_count; + +static void mem_link_init(struct mem_link *link) +{ + pthread_mutex_init(&link->lock, NULL); + pthread_cond_init(&link->cond[0], NULL); + pthread_cond_init(&link->cond[1], NULL); + link->ready[0] = link->ready[1] = 0; + link->len[0] = link->len[1] = 0; + link->name[0] = link->name[1] = ""; +} + +static struct mem_ep *mem_ep_lookup(struct wolfIP_ll_dev *ll) +{ + for (size_t i = 0; i < mem_ep_count; i++) { + if (mem_eps[i].ll == ll) + return &mem_eps[i]; + } + return NULL; +} + +static int mem_ll_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct mem_ep *ep = mem_ep_lookup(ll); + struct mem_link *link; + int idx; + int ret = 0; + + if (!ep) + return -1; + link = ep->link; + idx = ep->idx; + + pthread_mutex_lock(&link->lock); + if (link->ready[idx]) { + uint32_t copy = link->len[idx]; + if (copy > len) + copy = len; + memcpy(buf, link->buf[idx], copy); + link->ready[idx] = 0; + pthread_cond_signal(&link->cond[idx]); + ret = (int)copy; + } + pthread_mutex_unlock(&link->lock); + return ret; +} + +static int mem_ll_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct mem_ep *ep = mem_ep_lookup(ll); + struct mem_link *link; + int dst; + + if (!ep) + return -1; + link = ep->link; + dst = 1 - ep->idx; + + pthread_mutex_lock(&link->lock); + while (link->ready[dst]) + pthread_cond_wait(&link->cond[dst], &link->lock); + if (len > LINK_MTU) + len = LINK_MTU; + memcpy(link->buf[dst], buf, len); + link->len[dst] = len; + link->ready[dst] = 1; + pthread_cond_signal(&link->cond[dst]); + pthread_mutex_unlock(&link->lock); + return (int)len; +} + +static void mem_link_attach(struct wolfIP_ll_dev *ll, struct mem_link *link, int idx, + const char *ifname, const uint8_t mac[6]) +{ + ll->poll = mem_ll_poll; + ll->send = mem_ll_send; + snprintf(ll->ifname, sizeof(ll->ifname), "%s", ifname); + memcpy(ll->mac, mac, 6); + link->name[idx] = ifname; + mem_eps[mem_ep_count++] = (struct mem_ep){ .ll = ll, .link = link, .idx = idx }; +} + +/* ------------------------------------------------------------------------- */ +/* TLS echo server (wolfIP stack) */ +/* ------------------------------------------------------------------------- */ + +static void server_cb(int fd, uint16_t events, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + (void)events; + if (fd == server_listen_fd && (events & CB_EVENT_READABLE) && server_client_fd == -1) { + server_client_fd = wolfIP_sock_accept(s, server_listen_fd, NULL, NULL); + if (server_client_fd > 0) { + wolfIP_register_callback(s, server_client_fd, server_cb, s); + server_ssl = wolfSSL_new(server_ctx); + wolfSSL_SetIO_wolfIP(server_ssl, server_client_fd); + server_handshake_done = 0; + server_bytes_recv = 0; + server_bytes_sent = 0; + printf("TLS server: accepted client (fd 0x%04x)\n", server_client_fd); + } + return; + } + + if (fd != server_client_fd || server_ssl == NULL) + return; + + if (!server_handshake_done) { + int ret = wolfSSL_accept(server_ssl); + if (ret == SSL_SUCCESS) { + server_handshake_done = 1; + printf("TLS server: handshake complete\n"); + } else { + int err = wolfSSL_get_error(server_ssl, ret); + if (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE) + return; + fprintf(stderr, "TLS server: handshake failed (%d)\n", err); + wolfIP_sock_close(s, server_client_fd); + server_client_fd = -1; + wolfip_reset_io(server_ssl); + wolfSSL_free(server_ssl); + server_ssl = NULL; + server_done = 1; + return; + } + } + + if (events & (CB_EVENT_READABLE | CB_EVENT_WRITABLE)) { + if (server_bytes_recv < TEST_PAYLOAD) { + int ret = wolfSSL_read(server_ssl, server_buf + server_bytes_recv, + TEST_PAYLOAD - server_bytes_recv); + if (ret > 0) { + server_bytes_recv += ret; + } + } + if (server_bytes_recv == TEST_PAYLOAD && server_bytes_sent < TEST_PAYLOAD) { + int ret = wolfSSL_write(server_ssl, server_buf + server_bytes_sent, + TEST_PAYLOAD - server_bytes_sent); + if (ret > 0) { + server_bytes_sent += ret; + } + } + if (server_bytes_sent == TEST_PAYLOAD) { + wolfIP_sock_close(s, server_client_fd); + server_client_fd = -1; + wolfip_reset_io(server_ssl); + wolfSSL_free(server_ssl); + server_ssl = NULL; + server_handshake_done = 0; + server_done = 1; + printf("TLS server: echoed %d bytes\n", TEST_PAYLOAD); + } + } + + if (events & CB_EVENT_CLOSED) { + if (server_ssl) { + wolfip_reset_io(server_ssl); + wolfSSL_free(server_ssl); + server_ssl = NULL; + } + server_client_fd = -1; + server_handshake_done = 0; + server_done = 1; + } +} + +static int server_setup(struct wolfIP *s) +{ + struct wolfIP_sockaddr_in local = { + .sin_family = AF_INET, + .sin_port = ee16(4433), + .sin_addr.s_addr = 0, + }; + + server_ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (!server_ctx) + return -1; + wolfSSL_SetIO_wolfIP_CTX(server_ctx, s); + + if (wolfSSL_CTX_use_certificate_buffer(server_ctx, server_der, + server_der_len, SSL_FILETYPE_ASN1) != SSL_SUCCESS) + return -1; + if (wolfSSL_CTX_use_PrivateKey_buffer(server_ctx, server_key_der, + server_key_der_len, SSL_FILETYPE_ASN1) != SSL_SUCCESS) + return -1; + + server_listen_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (server_listen_fd < 0) + return -1; + wolfIP_register_callback(s, server_listen_fd, server_cb, s); + if (wolfIP_sock_bind(s, server_listen_fd, (struct wolfIP_sockaddr *)&local, + sizeof(local)) < 0) + return -1; + if (wolfIP_sock_listen(s, server_listen_fd, 1) < 0) + return -1; + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* Stack polling */ +/* ------------------------------------------------------------------------- */ + +static void *poll_thread(void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + while (1) { + uint64_t now = monotonic_ms(); + wolfIP_poll(s, now); + usleep(1000); + if (s == router_stack) { + if (!router_running) + break; + } else if (s == server_stack) { + if (server_done) + break; + } + } + return NULL; +} + +/* ------------------------------------------------------------------------- */ +/* Linux TLS client */ +/* ------------------------------------------------------------------------- */ + +static int run_linux_tls_client(ip4 server_ip) +{ + struct sockaddr_in remote = { + .sin_family = AF_INET, + .sin_port = htons(4433), + .sin_addr.s_addr = htonl(server_ip), + }; + uint8_t tx[TEST_PAYLOAD]; + uint8_t rx[TEST_PAYLOAD]; + WOLFSSL_CTX *ctx = NULL; + WOLFSSL *ssl = NULL; + int fd = -1; + int ret = -1; + int err = 0; + size_t sent = 0; + size_t received = 0; + int connected = 0; + char remote_str[16]; + + sleep(1); + ip4_to_str(server_ip, remote_str, sizeof(remote_str)); + printf("TLS client: connecting to %s:%u\n", remote_str, 4433); + + ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if (!ctx) { + fprintf(stderr, "linux client: failed to init context\n"); + goto out; + } + wolfSSL_CTX_load_verify_buffer(ctx, ca_der, ca_der_len, SSL_FILETYPE_ASN1); + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "linux client: failed to allocate ssl object\n"); + goto out; + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + goto out; + } + wolfSSL_set_fd(ssl, fd); + + for (int attempt = 0; attempt < 50; attempt++) { + int cret; + cret = connect(fd, (struct sockaddr *)&remote, sizeof(remote)); + if (cret == 0) { + connected = 1; + printf("TLS client: TCP connected\n"); + break; + } + if (errno == EINPROGRESS) { + if (wolfSSL_get_using_nonblock(ssl) == 0) + wolfSSL_set_using_nonblock(ssl, 1); + if (poll(&(struct pollfd){ .fd = fd, .events = POLLOUT }, 1, 100) > 0) { + socklen_t errlen = sizeof(err); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0 && err == 0) { + connected = 1; + printf("TLS client: TCP connect completed after wait\n"); + break; + } + } + continue; + } + if (errno == ECONNREFUSED || errno == ENETUNREACH || errno == ETIMEDOUT) { + usleep(100000); + continue; + } + perror("connect"); + goto out; + } + + if (!connected) { + fprintf(stderr, "linux client: unable to connect after retries\n"); + goto out; + } + + while (1) { + int hret = wolfSSL_connect(ssl); + if (hret == SSL_SUCCESS) { + printf("TLS client: TLS handshake complete\n"); + break; + } + err = wolfSSL_get_error(ssl, hret); + if (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE) { + usleep(1000); + continue; + } + fprintf(stderr, "linux client: handshake failed (%d)\n", err); + goto out; + } + + for (size_t i = 0; i < sizeof(tx); i += 16) + memcpy(tx + i, "Test pattern - -", 16); + + while (sent < sizeof(tx)) { + int wrote = wolfSSL_write(ssl, tx + sent, (int)(sizeof(tx) - sent)); + if (wrote > 0) { + sent += (size_t)wrote; + continue; + } + err = wolfSSL_get_error(ssl, wrote); + if (err == WOLFSSL_ERROR_WANT_WRITE || err == WOLFSSL_ERROR_WANT_READ) { + usleep(1000); + continue; + } + fprintf(stderr, "linux client: write failed (%d)\n", err); + goto out; + } + printf("TLS client: wrote %d bytes\n", TEST_PAYLOAD); + + while (received < sizeof(rx)) { + int got = wolfSSL_read(ssl, rx + received, (int)(sizeof(rx) - received)); + if (got > 0) { + received += (size_t)got; + continue; + } + if (got == 0) { + fprintf(stderr, "linux client: unexpected eof\n"); + goto out; + } + err = wolfSSL_get_error(ssl, got); + if (err == WOLFSSL_ERROR_WANT_READ) { + usleep(1000); + continue; + } + fprintf(stderr, "linux client: read failed (%d)\n", err); + goto out; + } + printf("TLS client: read %d bytes\n", TEST_PAYLOAD); + + if (memcmp(tx, rx, sizeof(tx)) != 0) { + fprintf(stderr, "linux client: payload mismatch\n"); + goto out; + } + + printf("TLS client: verified %d-byte echo\n", TEST_PAYLOAD); + ret = 0; + +out: + if (ssl) { + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + } + if (ctx) + wolfSSL_CTX_free(ctx); + if (fd >= 0) + close(fd); + return ret; +} + +/* ------------------------------------------------------------------------- */ +/* ARP helper */ +/* ------------------------------------------------------------------------- */ + +#define TEST_PACKED __attribute__((packed)) + +struct TEST_PACKED test_arp_packet { + uint8_t dst[6]; + uint8_t src[6]; + uint16_t type; + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t opcode; + uint8_t sma[6]; + uint32_t sip; + uint8_t tma[6]; + uint32_t tip; +}; + +/* ------------------------------------------------------------------------- */ +/* Main */ +/* ------------------------------------------------------------------------- */ + +int main(void) +{ + static const uint8_t mac_router1[6] = {0x02, 0x00, 0x00, 0x00, 0x02, 0xFE}; + static const uint8_t mac_server[6] = {0x02, 0x00, 0x00, 0x00, 0x02, 0x10}; + struct mem_link link_router_server; + struct wolfIP_ll_dev *tap_dev = NULL; + size_t stack_sz; + int ret = 0; + int router_started = 0; + int server_started = 0; + char route_cmd[128] = {0}; + char route_del_cmd[128] = {0}; + struct in_addr host_addr = { .s_addr = htonl(host_ip4) }; + uint8_t host_mac[6]; + char host_str[16]; + char lan_str[16]; + char wan_str[16]; + char srv_str[16]; + + setvbuf(stdout, NULL, _IONBF, 0); + + mem_link_init(&link_router_server); + + wolfSSL_Init(); + wolfSSL_Debugging_OFF(); + + ip4_to_str(host_ip4, host_str, sizeof(host_str)); + ip4_to_str(router_lan_ip4, lan_str, sizeof(lan_str)); + ip4_to_str(router_wan_ip4, wan_str, sizeof(wan_str)); + ip4_to_str(server_ip4, srv_str, sizeof(srv_str)); + printf("Configuration: host=%s router_lan=%s router_wan=%s server=%s\n", + host_str, lan_str, wan_str, srv_str); + + stack_sz = wolfIP_instance_size(); + router_stack = (struct wolfIP *)XMALLOC(stack_sz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + server_stack = (struct wolfIP *)XMALLOC(stack_sz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!router_stack || !server_stack) { + fprintf(stderr, "failed to allocate stacks\n"); + ret = 1; + goto cleanup; + } + XMEMSET(router_stack, 0, stack_sz); + XMEMSET(server_stack, 0, stack_sz); + wolfIP_init(router_stack); + wolfIP_init(server_stack); + + tap_dev = wolfIP_getdev(router_stack); + if (!tap_dev) { + fprintf(stderr, "failed to obtain router interface 0\n"); + ret = 1; + goto cleanup; + } + if (tap_init(tap_dev, TAP_IFNAME, host_addr.s_addr) < 0) { + perror("tap_init"); + ret = 1; + goto cleanup; + } + memcpy(host_mac, tap_dev->mac, sizeof(host_mac)); + host_mac[5] ^= 1; + + mem_link_attach(wolfIP_getdev_ex(router_stack, 1), &link_router_server, 0, + "rt1", mac_router1); + mem_link_attach(wolfIP_getdev(server_stack), &link_router_server, 1, + "srv0", mac_server); + + wolfIP_ipconfig_set_ex(router_stack, 0, router_lan_ip4, IP4(255,255,255,0), IP4(0,0,0,0)); + wolfIP_ipconfig_set_ex(router_stack, 1, router_wan_ip4, IP4(255,255,255,0), IP4(0,0,0,0)); + wolfIP_ipconfig_set(server_stack, server_ip4, IP4(255,255,255,0), router_wan_ip4); + + if (server_setup(server_stack) < 0) { + fprintf(stderr, "failed to set up server\n"); + ret = 1; + goto cleanup; + } + + router_running = 1; + server_done = 0; + + if (pthread_create(&th_router, NULL, poll_thread, router_stack) != 0) { + fprintf(stderr, "failed to start router thread\n"); + ret = 1; + goto cleanup; + } + router_started = 1; + + if (pthread_create(&th_server, NULL, poll_thread, server_stack) != 0) { + fprintf(stderr, "failed to start server thread\n"); + ret = 1; + goto cleanup; + } + server_started = 1; + + snprintf(route_cmd, sizeof(route_cmd), + "ip route add %s dev %s via %u.%u.%u.%u 2>/dev/null", + HOST_ROUTE, TAP_IFNAME, + (router_lan_ip4 >> 24) & 0xFF, + (router_lan_ip4 >> 16) & 0xFF, + (router_lan_ip4 >> 8) & 0xFF, + router_lan_ip4 & 0xFF); + snprintf(route_del_cmd, sizeof(route_del_cmd), + "ip route del %s dev %s 2>/dev/null", + HOST_ROUTE, TAP_IFNAME); + if (route_cmd[0]) + system(route_cmd); + + if (run_linux_tls_client(server_ip4) < 0) { + fprintf(stderr, "linux client: test failed\n"); + ret = 1; + } + +cleanup: + router_running = 0; + server_done = 1; + + if (server_started) + pthread_join(th_server, NULL); + if (router_started) + pthread_join(th_router, NULL); + + if (route_del_cmd[0]) + system(route_del_cmd); + + if (server_ssl) { + wolfip_reset_io(server_ssl); + wolfSSL_free(server_ssl); + server_ssl = NULL; + } + if (server_ctx) { + wolfSSL_CTX_free(server_ctx); + server_ctx = NULL; + } + if (server_listen_fd >= 0 && server_stack) + wolfIP_sock_close(server_stack, server_listen_fd); + if (router_stack) + XFREE(router_stack, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (server_stack) + XFREE(server_stack, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + printf("Test result: %s\n", ret == 0 ? "SUCCESS" : "FAIL"); + return ret; +} diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 0aeca382..5efbb7da 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -19,6 +19,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include "check.h" +#include "../../../config.h" +#undef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 3 +#undef WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_ENABLE_LOOPBACK 1 +#undef WOLFIP_ENABLE_FORWARDING +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 1 +#endif +#if WOLFIP_ENABLE_LOOPBACK +#define TEST_LOOPBACK_IF 0U +#define TEST_PRIMARY_IF 1U +#define TEST_SECOND_IF 2U +#else +#define TEST_LOOPBACK_IF 0U +#define TEST_PRIMARY_IF 0U +#define TEST_SECOND_IF 1U +#endif +#include #include "../../wolfip.c" #include /* for random() */ @@ -33,12 +52,11 @@ uint32_t wolfIP_getrandom(void) static uint8_t mem[8 * 1024]; static uint32_t memsz = 8 * 1024; -static const char ifname[] = "mock0"; 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 wolfIP_ll_dev *dev, void *frame, uint32_t len) { (void)dev; memcpy(last_frame_sent, frame, len); @@ -46,7 +64,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 wolfIP_ll_dev *dev, void *frame, uint32_t len) { (void)dev; (void)frame; @@ -54,17 +72,31 @@ static int mock_poll(struct ll *dev, void *frame, uint32_t len) return 0; } - -void mock_link_init(struct wolfIP *s) +static void mock_link_init_idx(struct wolfIP *s, unsigned int idx, const uint8_t *mac_override) { - struct ll *ll = &s->ll_dev; - strncpy((char *)ll->ifname, ifname, sizeof(ll->ifname) - 1); - memcpy(ll->mac, ifmac, 6); - ll->mac[5] ^= 1; + struct wolfIP_ll_dev *ll = wolfIP_getdev_ex(s, idx); + ck_assert_ptr_nonnull(ll); + memset(ll, 0, sizeof(*ll)); + snprintf((char *)ll->ifname, sizeof(ll->ifname), "mock%u", idx); + if (mac_override) { + memcpy(ll->mac, mac_override, 6); + } else { + memcpy(ll->mac, ifmac, 6); + ll->mac[5] ^= (uint8_t)(idx + 1); + } ll->poll = mock_poll; ll->send = mock_send; } +void mock_link_init(struct wolfIP *s) +{ + unsigned int idx = 0; +#if WOLFIP_ENABLE_LOOPBACK + idx = 1; +#endif + mock_link_init_idx(s, idx, NULL); +} + static struct timers_binheap heap; static void reset_heap(void) { heap.size = 0; @@ -117,6 +149,7 @@ START_TEST(test_fifo_push_and_pop_multiple) { struct fifo f; uint8_t data[] = {1, 2, 3, 4, 5}; uint8_t data2[] = {6, 7, 8, 9, 10}; + struct pkt_desc *desc; fifo_init(&f, mem, memsz); ck_assert_int_eq(fifo_space(&f), memsz); @@ -128,7 +161,7 @@ START_TEST(test_fifo_push_and_pop_multiple) { ck_assert_int_eq(fifo_push(&f, data2, sizeof(data2)), 0); // Test pop - struct pkt_desc *desc = fifo_pop(&f); + desc = fifo_pop(&f); ck_assert_ptr_nonnull(desc); ck_assert_int_eq(desc->len, sizeof(data)); ck_assert_mem_eq((const uint8_t *)f.data + desc->pos + sizeof(struct pkt_desc), data, sizeof(data)); @@ -143,10 +176,11 @@ END_TEST START_TEST(test_fifo_pop_success) { struct fifo f; uint8_t data[] = {1, 2, 3, 4}; + struct pkt_desc *desc; fifo_init(&f, mem, memsz); fifo_push(&f, data, sizeof(data)); // Add data to FIFO - struct pkt_desc *desc = fifo_pop(&f); + desc = fifo_pop(&f); ck_assert_ptr_nonnull(desc); // Ensure we got a valid descriptor ck_assert_int_eq(desc->len, sizeof(data)); // Check length ck_assert_mem_eq((const uint8_t *)f.data + desc->pos + sizeof(struct pkt_desc), data, sizeof(data)); // Check data @@ -154,9 +188,10 @@ START_TEST(test_fifo_pop_success) { START_TEST(test_fifo_pop_empty) { struct fifo f; + struct pkt_desc *desc; fifo_init(&f, mem, memsz); - struct pkt_desc *desc = fifo_pop(&f); + desc = fifo_pop(&f); ck_assert_ptr_eq(desc, NULL); // Ensure pop returns NULL on empty FIFO } @@ -177,11 +212,12 @@ START_TEST(test_fifo_push_wrap) { uint8_t buffer[100]; uint8_t data[] = {1, 2, 3, 4}; int ret; + struct pkt_desc *desc; fifo_init(&f, buffer, sizeof(buffer)); fifo_push(&f, data, sizeof(data)); // Add data to FIFO // Pop the data to make space - struct pkt_desc *desc = fifo_pop(&f); + desc = fifo_pop(&f); ck_assert_ptr_nonnull(desc); // Push data to wrap around the buffer @@ -197,11 +233,12 @@ START_TEST(test_fifo_push_wrap_multiple) { uint8_t data[] = {1, 2, 3, 4}; uint8_t data2[] = {5, 6, 7, 8, 9}; int ret; + struct pkt_desc *desc; fifo_init(&f, mem, memsz); fifo_push(&f, data, sizeof(data)); // Add data to FIFO // Pop the data to make space - struct pkt_desc *desc = fifo_pop(&f); + desc = fifo_pop(&f); ck_assert_ptr_nonnull(desc); // Push data to wrap around the buffer @@ -221,6 +258,8 @@ START_TEST(test_fifo_next_success) { struct fifo f; uint8_t data1[] = {1, 2, 3, 4}; uint8_t data2[] = {5, 6, 7, 8, 9}; + struct pkt_desc *desc; + struct pkt_desc *next_desc; fifo_init(&f, mem, memsz); @@ -230,35 +269,39 @@ START_TEST(test_fifo_next_success) { ck_assert_int_eq(fifo_len(&f), sizeof(data1) + sizeof(data2) + 2 * sizeof(struct pkt_desc)); // Get the first packet descriptor - struct pkt_desc *desc = fifo_peek(&f); + desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); // Get the next packet descriptor using fifo_next - struct pkt_desc *next_desc = fifo_next(&f, desc); + next_desc = fifo_next(&f, desc); ck_assert_ptr_nonnull(next_desc); // Ensure next descriptor is valid ck_assert_int_eq(next_desc->len, sizeof(data2)); // Check length of next packet } START_TEST(test_fifo_next_empty_fifo) { struct fifo f; + struct pkt_desc *desc; + struct pkt_desc *next_desc; fifo_init(&f, mem, memsz); // Start with an empty FIFO - struct pkt_desc *desc = NULL; - struct pkt_desc *next_desc = fifo_next(&f, desc); + desc = NULL; + next_desc = fifo_next(&f, desc); ck_assert_ptr_eq(next_desc, NULL); // Ensure next returns NULL on empty FIFO } START_TEST(test_fifo_next_end_of_fifo) { struct fifo f; uint8_t data[] = {1, 2, 3, 4}; + struct pkt_desc *desc; + struct pkt_desc *next_desc; fifo_init(&f, mem, memsz); fifo_push(&f, data, sizeof(data)); - struct pkt_desc *desc = fifo_peek(&f); // Get first packet + desc = fifo_peek(&f); // Get first packet fifo_pop(&f); // Simulate removing the packet - struct pkt_desc *next_desc = fifo_next(&f, desc); + next_desc = fifo_next(&f, desc); ck_assert_ptr_eq(next_desc, NULL); // Should return NULL as there are no more packets } END_TEST @@ -314,10 +357,11 @@ END_TEST START_TEST(test_queue_insert_empty) { struct queue q; - queue_init(&q, mem, memsz, 0x12345678); uint8_t data[] = {1, 2, 3, 4}; + int res; + queue_init(&q, mem, memsz, 0x12345678); - int res = queue_insert(&q, data, 0, sizeof(data)); + res = queue_insert(&q, data, 0, sizeof(data)); ck_assert_int_eq(res, 0); ck_assert_int_eq(queue_len(&q), sizeof(data)); ck_assert_int_eq(q.head, sizeof(data)); @@ -327,12 +371,14 @@ END_TEST START_TEST(test_queue_insert_sequential) { struct queue q; - queue_init(&q, mem, memsz, 0x12345678); uint8_t data1[] = {1, 2}; uint8_t data2[] = {3, 4}; + int res1; + int res2; + queue_init(&q, mem, memsz, 0x12345678); - int res1 = queue_insert(&q, data1, 0, sizeof(data1)); - int res2 = queue_insert(&q, data2, 2, sizeof(data2)); + res1 = queue_insert(&q, data1, 0, sizeof(data1)); + res2 = queue_insert(&q, data2, 2, sizeof(data2)); ck_assert_int_eq(res1, 0); ck_assert_int_eq(res2, 0); ck_assert_int_eq(queue_len(&q), sizeof(data1) + sizeof(data2)); @@ -343,12 +389,13 @@ END_TEST START_TEST(test_queue_pop) { struct queue q; - queue_init(&q, mem, memsz, 0x12345678); uint8_t data[] = {5, 6, 7, 8}; uint8_t out[4]; + int len; + queue_init(&q, mem, memsz, 0x12345678); queue_insert(&q, data, 0, sizeof(data)); - int len = queue_pop(&q, out, sizeof(out)); + len = queue_pop(&q, out, sizeof(out)); ck_assert_int_eq(len, sizeof(out)); ck_assert_mem_eq(out, data, sizeof(data)); ck_assert_int_eq(queue_len(&q), 0); @@ -358,14 +405,15 @@ END_TEST START_TEST(test_queue_pop_wraparound) { struct queue q; - queue_init(&q, mem, memsz, 0x12345678); uint8_t data[] = {9, 10, 11, 12}; uint8_t out[4]; + int len; + queue_init(&q, mem, memsz, 0x12345678); q.head = memsz - 1; q.tail = memsz - 1; queue_insert(&q, data, 0, sizeof(data)); - int len = queue_pop(&q, out, sizeof(out)); + len = queue_pop(&q, out, sizeof(out)); ck_assert_int_eq(len, sizeof(out)); ck_assert_mem_eq(out, data, sizeof(data)); ck_assert_int_eq(queue_len(&q), 0); @@ -375,15 +423,18 @@ END_TEST /* Utils */ START_TEST(test_insert_timer) { - reset_heap(); - struct wolfIP_timer tmr1 = { .expires = 100 }; struct wolfIP_timer tmr2 = { .expires = 50 }; struct wolfIP_timer tmr3 = { .expires = 200 }; + int id1; + int id2; + int id3; + + reset_heap(); - int id1 = timers_binheap_insert(&heap, tmr1); - int id2 = timers_binheap_insert(&heap, tmr2); - int id3 = timers_binheap_insert(&heap, tmr3); + id1 = timers_binheap_insert(&heap, tmr1); + id2 = timers_binheap_insert(&heap, tmr2); + id3 = timers_binheap_insert(&heap, tmr3); ck_assert_int_eq(heap.size, 3); ck_assert_int_lt(heap.timers[0].expires, heap.timers[1].expires); @@ -394,17 +445,18 @@ START_TEST(test_insert_timer) { END_TEST START_TEST(test_pop_timer) { - reset_heap(); - struct wolfIP_timer tmr1 = { .expires = 300 }; struct wolfIP_timer tmr2 = { .expires = 100 }; struct wolfIP_timer tmr3 = { .expires = 200 }; + struct wolfIP_timer popped; + + reset_heap(); timers_binheap_insert(&heap, tmr1); timers_binheap_insert(&heap, tmr2); timers_binheap_insert(&heap, tmr3); - struct wolfIP_timer popped = timers_binheap_pop(&heap); + popped = timers_binheap_pop(&heap); ck_assert_int_eq(popped.expires, 100); ck_assert_int_eq(heap.size, 2); ck_assert_int_lt(heap.timers[0].expires, heap.timers[1].expires); @@ -412,9 +464,9 @@ START_TEST(test_pop_timer) { END_TEST START_TEST(test_is_timer_expired) { - reset_heap(); - struct wolfIP_timer tmr = { .expires = 150 }; + + reset_heap(); timers_binheap_insert(&heap, tmr); ck_assert_int_eq(is_timer_expired(&heap, 100), 0); @@ -424,19 +476,22 @@ START_TEST(test_is_timer_expired) { END_TEST START_TEST(test_cancel_timer) { - reset_heap(); - struct wolfIP_timer tmr1 = { .expires = 100 }; struct wolfIP_timer tmr2 = { .expires = 200 }; + int id1; + int id2; + struct wolfIP_timer popped; + + reset_heap(); - int id1 = timers_binheap_insert(&heap, tmr1); - int id2 = timers_binheap_insert(&heap, tmr2); + id1 = timers_binheap_insert(&heap, tmr1); + id2 = timers_binheap_insert(&heap, tmr2); (void)id2; timer_binheap_cancel(&heap, id1); ck_assert_int_eq(heap.timers[0].expires, 0); // tmr1 canceled - struct wolfIP_timer popped = timers_binheap_pop(&heap); + popped = timers_binheap_pop(&heap); ck_assert_int_eq(popped.expires, 200); // Only tmr2 should remain ck_assert_int_eq(heap.size, 0); } @@ -448,23 +503,24 @@ START_TEST(test_arp_request_basic) { struct wolfIP s; struct arp_packet *arp; - wolfIP_init(&s); uint32_t target_ip = 0xC0A80002; /* 192.168.0.2 */ + + wolfIP_init(&s); mock_link_init(&s); s.last_tick = 1000; - arp_request(&s, target_ip); + arp_request(&s, TEST_PRIMARY_IF, target_ip); ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); arp = (struct arp_packet *)last_frame_sent; ck_assert_mem_eq(arp->eth.dst, "\xff\xff\xff\xff\xff\xff", 6); - ck_assert_mem_eq(arp->eth.src, s.ll_dev.mac, 6); + ck_assert_mem_eq(arp->eth.src, s.ll_dev[TEST_PRIMARY_IF].mac, 6); ck_assert_int_eq(arp->eth.type, ee16(0x0806)); ck_assert_int_eq(arp->htype, ee16(1)); ck_assert_int_eq(arp->ptype, ee16(0x0800)); ck_assert_int_eq(arp->hlen, 6); ck_assert_int_eq(arp->plen, 4); ck_assert_int_eq(arp->opcode, ee16(ARP_REQUEST)); - ck_assert_mem_eq(arp->sma, s.ll_dev.mac, 6); - ck_assert_int_eq(arp->sip, ee32(s.ipconf.ip)); + ck_assert_mem_eq(arp->sma, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + ck_assert_int_eq(arp->sip, ee32(s.ipconf[TEST_PRIMARY_IF].ip)); ck_assert_mem_eq(arp->tma, "\x00\x00\x00\x00\x00\x00", 6); ck_assert_int_eq(arp->tip, ee32(target_ip)); } @@ -473,24 +529,26 @@ END_TEST START_TEST(test_arp_request_throttle) { struct wolfIP s; - wolfIP_init(&s); uint32_t target_ip = 0xC0A80002; /*192.168.0.2*/ + + wolfIP_init(&s); mock_link_init(&s); s.last_tick = 1000; - s.arp.last_arp = 880; + s.arp.last_arp[TEST_PRIMARY_IF] = 880; last_frame_sent_size = 0; - arp_request(&s, target_ip); + arp_request(&s, TEST_PRIMARY_IF, target_ip); ck_assert_int_eq(last_frame_sent_size, 0); } END_TEST START_TEST(test_arp_request_target_ip) { - uint32_t target_ip = 0xC0A80002; struct wolfIP s; + uint32_t target_ip = 0xC0A80002; + wolfIP_init(&s); mock_link_init(&s); s.last_tick = 1000; - arp_request(&s, target_ip); + arp_request(&s, TEST_PRIMARY_IF, target_ip); ck_assert_int_eq(((struct arp_packet *)(last_frame_sent))->tip, ee32(target_ip)); } END_TEST @@ -498,15 +556,16 @@ END_TEST START_TEST(test_arp_request_handling) { struct arp_packet arp_req; struct arp_packet *arp_reply; - memset(&arp_req, 0, sizeof(arp_req)); uint32_t req_ip = 0xC0A80002; // 192.168.0.2 uint32_t device_ip = 0xC0A80001; // 192.168.0.1 uint8_t req_mac[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; //uint8_t mac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; struct wolfIP s; + + memset(&arp_req, 0, sizeof(arp_req)); wolfIP_init(&s); mock_link_init(&s); - s.ipconf.ip = device_ip; + s.ipconf[TEST_PRIMARY_IF].ip = device_ip; /* Prepare ARP request */ arp_req.opcode = ee16(ARP_REQUEST); @@ -515,7 +574,7 @@ START_TEST(test_arp_request_handling) { arp_req.tip = ee32(device_ip); /* Call arp_recv with the ARP request */ - arp_recv(&s, &arp_req, sizeof(arp_req)); + arp_recv(&s, TEST_PRIMARY_IF, &arp_req, sizeof(arp_req)); wolfIP_poll(&s, 1000); wolfIP_poll(&s, 1001); wolfIP_poll(&s, 1002); @@ -529,7 +588,7 @@ START_TEST(test_arp_request_handling) { arp_reply = (struct arp_packet *)last_frame_sent; ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); ck_assert_int_eq(arp_reply->opcode, ee16(ARP_REPLY)); - ck_assert_mem_eq(arp_reply->sma, s.ll_dev.mac, 6); // source MAC + ck_assert_mem_eq(arp_reply->sma, s.ll_dev[TEST_PRIMARY_IF].mac, 6); // source MAC ck_assert_int_eq(arp_reply->sip, ee32(device_ip)); // source IP ck_assert_mem_eq(arp_reply->tma, req_mac, 6); // target MAC ck_assert_int_eq(arp_reply->tip, ee32(req_ip)); // target IP @@ -538,10 +597,12 @@ END_TEST START_TEST(test_arp_reply_handling) { struct arp_packet arp_reply; - memset(&arp_reply, 0, sizeof(arp_reply)); uint32_t reply_ip = 0xC0A80003; // 192.168.0.3 uint8_t reply_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; + uint8_t new_mac[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; struct wolfIP s; + + memset(&arp_reply, 0, sizeof(arp_reply)); wolfIP_init(&s); mock_link_init(&s); @@ -551,16 +612,15 @@ START_TEST(test_arp_reply_handling) { memcpy(arp_reply.sma, reply_mac, 6); /* Call arp_recv with the ARP reply */ - arp_recv(&s, &arp_reply, sizeof(arp_reply)); + arp_recv(&s, TEST_PRIMARY_IF, &arp_reply, sizeof(arp_reply)); /* Check if ARP table updated with reply IP and MAC */ ck_assert_int_eq(s.arp.neighbors[0].ip, reply_ip); ck_assert_mem_eq(s.arp.neighbors[0].mac, reply_mac, 6); /* Update same IP with a different MAC address */ - uint8_t new_mac[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; memcpy(arp_reply.sma, new_mac, 6); - arp_recv(&s, &arp_reply, sizeof(arp_reply)); + arp_recv(&s, TEST_PRIMARY_IF, &arp_reply, sizeof(arp_reply)); /* Check if ARP table updates with new MAC */ ck_assert_mem_eq(s.arp.neighbors[0].mac, new_mac, 6); @@ -572,15 +632,18 @@ START_TEST(test_arp_lookup_success) { uint32_t ip = 0xC0A80002; const uint8_t mock_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; struct wolfIP s; + int result; + wolfIP_init(&s); mock_link_init(&s); /* Add a known IP-MAC pair */ s.arp.neighbors[0].ip = ip; + s.arp.neighbors[0].if_idx = TEST_PRIMARY_IF; memcpy(s.arp.neighbors[0].mac, mock_mac, 6); /* Test arp_lookup */ - int result = arp_lookup(&s, ip, found_mac); + result = arp_lookup(&s, TEST_PRIMARY_IF, ip, found_mac); ck_assert_int_eq(result, 0); ck_assert_mem_eq(found_mac, mock_mac, 6); } @@ -590,29 +653,300 @@ START_TEST(test_arp_lookup_failure) { uint8_t found_mac[6]; uint32_t ip = 0xC0A80004; struct wolfIP s; + int result; + uint8_t zero_mac[6] = {0, 0, 0, 0, 0, 0}; + wolfIP_init(&s); mock_link_init(&s); /* Ensure arp_lookup fails for unknown IP */ - int result = arp_lookup(&s, ip, found_mac); + result = arp_lookup(&s, TEST_PRIMARY_IF, ip, found_mac); ck_assert_int_eq(result, -1); - uint8_t zero_mac[6] = {0, 0, 0, 0, 0, 0}; ck_assert_mem_eq(found_mac, zero_mac, 6); } END_TEST +START_TEST(test_wolfip_getdev_ex_api) +{ + struct wolfIP s; + struct wolfIP_ll_dev *ll_def; + wolfIP_init(&s); + ll_def = wolfIP_getdev(&s); + ck_assert_ptr_nonnull(ll_def); + ck_assert_ptr_eq(ll_def, wolfIP_getdev_ex(&s, TEST_PRIMARY_IF)); +#if WOLFIP_ENABLE_LOOPBACK + ck_assert_ptr_ne(ll_def, wolfIP_getdev_ex(&s, TEST_LOOPBACK_IF)); +#endif + ck_assert_ptr_null(wolfIP_getdev_ex(&s, WOLFIP_MAX_INTERFACES)); +} +END_TEST + +#if WOLFIP_ENABLE_LOOPBACK +START_TEST(test_wolfip_loopback_defaults) +{ + struct wolfIP s; + struct wolfIP_ll_dev *loop; + struct wolfIP_ll_dev *hw; + ip4 ip = 0, mask = 0, gw = 0; + + wolfIP_init(&s); + + loop = wolfIP_getdev_ex(&s, TEST_LOOPBACK_IF); + ck_assert_ptr_nonnull(loop); + ck_assert_ptr_nonnull(loop->send); + ck_assert_uint_eq(loop->mac[0], 0x02); + + wolfIP_ipconfig_get_ex(&s, TEST_LOOPBACK_IF, &ip, &mask, &gw); + ck_assert_uint_eq(ip, 0x7F000001U); + ck_assert_uint_eq(mask, 0xFF000000U); + ck_assert_uint_eq(gw, 0U); + + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0x0A0000FEU); + wolfIP_ipconfig_get_ex(&s, TEST_PRIMARY_IF, &ip, &mask, &gw); + ck_assert_uint_eq(ip, 0x0A000001U); + ck_assert_uint_eq(mask, 0xFFFFFF00U); + ck_assert_uint_eq(gw, 0x0A0000FEU); + + hw = wolfIP_getdev(&s); + ck_assert_ptr_eq(hw, wolfIP_getdev_ex(&s, TEST_PRIMARY_IF)); +} +END_TEST +#endif + +START_TEST(test_wolfip_ipconfig_ex_per_interface) +{ + struct wolfIP s; + ip4 base_ip = 0x0A000001; + ip4 base_mask = 0xFFFFFF00; + ip4 base_gw = 0x0A0000FE; + ip4 iface_ip = 0x0A000201; + ip4 iface_mask = 0xFFFF0000; + ip4 iface_gw = 0x0A0002FE; + ip4 out_ip = 0, out_mask = 0, out_gw = 0; + ip4 def_ip = 0, def_mask = 0, def_gw = 0; + + wolfIP_init(&s); + wolfIP_ipconfig_set(&s, base_ip, base_mask, base_gw); + + wolfIP_ipconfig_set_ex(&s, TEST_SECOND_IF, iface_ip, iface_mask, iface_gw); + wolfIP_ipconfig_get_ex(&s, TEST_SECOND_IF, &out_ip, &out_mask, &out_gw); + + ck_assert_uint_eq(out_ip, iface_ip); + ck_assert_uint_eq(out_mask, iface_mask); + ck_assert_uint_eq(out_gw, iface_gw); + + wolfIP_ipconfig_get(&s, &def_ip, &def_mask, &def_gw); + ck_assert_uint_eq(def_ip, base_ip); + ck_assert_uint_eq(def_mask, base_mask); + ck_assert_uint_eq(def_gw, base_gw); + + wolfIP_ipconfig_set_ex(&s, WOLFIP_MAX_INTERFACES, 0xDEADBEEF, 0xFFFFFFFF, 0x01010101); + ck_assert_uint_eq(s.ipconf[TEST_SECOND_IF].ip, iface_ip); + ck_assert_uint_eq(s.ipconf[TEST_SECOND_IF].mask, iface_mask); + ck_assert_uint_eq(s.ipconf[TEST_SECOND_IF].gw, iface_gw); + + wolfIP_ipconfig_get_ex(&s, TEST_SECOND_IF, NULL, NULL, NULL); +} +END_TEST + +START_TEST(test_wolfip_recv_ex_multi_interface_arp_reply) +{ + struct wolfIP s; + struct arp_packet arp_req; + struct arp_packet *arp_reply; + uint8_t requester_mac[6] = {0x10, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t iface1_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, iface1_mac); + wolfIP_ipconfig_set(&s, 0xC0A80001, 0xFFFFFF00, 0); + wolfIP_ipconfig_set_ex(&s, TEST_SECOND_IF, 0xC0A80101, 0xFFFFFF00, 0); + + memset(&arp_req, 0, sizeof(arp_req)); + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + memset(arp_req.eth.dst, 0xFF, sizeof(arp_req.eth.dst)); + memcpy(arp_req.eth.src, requester_mac, 6); + arp_req.eth.type = ee16(ETH_TYPE_ARP); + arp_req.htype = ee16(1); + arp_req.ptype = ee16(ETH_TYPE_IP); + arp_req.hlen = 6; + arp_req.plen = 4; + arp_req.opcode = ee16(ARP_REQUEST); + memcpy(arp_req.sma, requester_mac, 6); + arp_req.sip = ee32(0xC0A80164); + memset(arp_req.tma, 0, sizeof(arp_req.tma)); + arp_req.tip = ee32(0xC0A80101); + + wolfIP_recv_ex(&s, TEST_SECOND_IF, &arp_req, sizeof(arp_req)); + + ck_assert_uint_eq(last_frame_sent_size, sizeof(struct arp_packet)); + arp_reply = (struct arp_packet *)last_frame_sent; + ck_assert_uint_eq(arp_reply->opcode, ee16(ARP_REPLY)); + ck_assert_mem_eq(arp_reply->eth.src, iface1_mac, 6); + ck_assert_mem_eq(arp_reply->sma, iface1_mac, 6); + ck_assert_uint_eq(arp_reply->sip, ee32(s.ipconf[TEST_SECOND_IF].ip)); +} +END_TEST + +START_TEST(test_wolfip_forwarding_basic) +{ + struct wolfIP s; + struct wolfIP_ip_packet frame; + struct wolfIP_ip_packet *fwd; + uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + uint8_t iface1_mac[6] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + uint8_t next_hop_mac[6] = {0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; + uint32_t dest_ip = 0xC0A80164; /* 192.168.1.100 */ + uint8_t initial_ttl = 2; + uint16_t orig_csum; + uint16_t expected_csum; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, iface1_mac); + wolfIP_ipconfig_set(&s, 0xC0A80001, 0xFFFFFF00, 0); + wolfIP_ipconfig_set_ex(&s, TEST_SECOND_IF, 0xC0A80101, 0xFFFFFF00, 0); + s.arp.neighbors[0].ip = dest_ip; + s.arp.neighbors[0].if_idx = TEST_SECOND_IF; + memcpy(s.arp.neighbors[0].mac, next_hop_mac, 6); + + memset(&frame, 0, sizeof(frame)); + memcpy(frame.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame.eth.src, src_mac, 6); + frame.eth.type = ee16(ETH_TYPE_IP); + frame.ver_ihl = 0x45; + frame.ttl = initial_ttl; + frame.proto = WI_IPPROTO_UDP; + frame.len = ee16(IP_HEADER_LEN); + frame.src = ee32(0xC0A800AA); + frame.dst = ee32(dest_ip); + frame.csum = 0; + iphdr_set_checksum(&frame); + orig_csum = ee16(frame.csum); + + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, &frame, sizeof(frame)); + + ck_assert_uint_eq(last_frame_sent_size, sizeof(struct wolfIP_ip_packet)); + fwd = (struct wolfIP_ip_packet *)last_frame_sent; + ck_assert_mem_eq(fwd->eth.dst, next_hop_mac, 6); + ck_assert_mem_eq(fwd->eth.src, s.ll_dev[TEST_SECOND_IF].mac, 6); + ck_assert_uint_eq(fwd->ttl, (uint8_t)(initial_ttl - 1)); + { + uint32_t sum = orig_csum + 0x0100; + sum = (sum & 0xFFFF) + (sum >> 16); + expected_csum = (uint16_t)sum; + if (expected_csum == 0) + expected_csum = 0xFFFF; + } + ck_assert_uint_eq(ee16(fwd->csum), expected_csum); +} +END_TEST + +START_TEST(test_wolfip_forwarding_ttl_expired) +{ + struct wolfIP s; + struct wolfIP_ip_packet frame; + struct wolfIP_icmp_ttl_exceeded_packet *icmp; + uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0xAA, 0xBB, 0xCC}; + uint8_t iface1_mac[6] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x03}; + uint32_t dest_ip = 0xC0A80110; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, iface1_mac); + wolfIP_ipconfig_set(&s, 0xC0A80001, 0xFFFFFF00, 0); + wolfIP_ipconfig_set_ex(&s, TEST_SECOND_IF, 0xC0A80101, 0xFFFFFF00, 0); + + memset(&frame, 0, sizeof(frame)); + memcpy(frame.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame.eth.src, src_mac, 6); + frame.eth.type = ee16(ETH_TYPE_IP); + frame.ver_ihl = 0x45; + frame.ttl = 1; + frame.proto = WI_IPPROTO_UDP; + frame.len = ee16(IP_HEADER_LEN); + frame.src = ee32(0xC0A800AA); + frame.dst = ee32(dest_ip); + frame.csum = 0; + iphdr_set_checksum(&frame); + + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, &frame, sizeof(frame)); + + ck_assert_uint_eq(last_frame_sent_size, + sizeof(struct wolfIP_icmp_ttl_exceeded_packet)); + icmp = (struct wolfIP_icmp_ttl_exceeded_packet *)last_frame_sent; + ck_assert_uint_eq(icmp->type, ICMP_TTL_EXCEEDED); + ck_assert_uint_eq(icmp->code, 0); + ck_assert_mem_eq(icmp->unused, "\x00\x00\x00\x00", sizeof(icmp->unused)); + ck_assert_mem_eq(icmp->ip.eth.dst, src_mac, 6); + ck_assert_mem_eq(icmp->ip.eth.src, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + ck_assert_uint_eq(icmp->ip.ttl, 64); + ck_assert_uint_eq(ee16(icmp->ip.len), + (uint16_t)(IP_HEADER_LEN + ICMP_TTL_EXCEEDED_SIZE)); + ck_assert_uint_eq(ee32(icmp->ip.src), s.ipconf[TEST_PRIMARY_IF].ip); + ck_assert_uint_eq(ee32(icmp->ip.dst), ee32(frame.src)); + ck_assert_mem_eq(icmp->orig_packet, + ((uint8_t *)&frame) + ETH_HEADER_LEN, + TTL_EXCEEDED_ORIG_PACKET_SIZE); + ck_assert_uint_eq(frame.ttl, 1); /* original packet should remain unchanged */ +} +END_TEST + +START_TEST(test_loopback_dest_not_forwarded) +{ + struct wolfIP s; + struct wolfIP_ip_packet frame; + uint8_t src_mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, NULL); + wolfIP_ipconfig_set(&s, 0xC0A80001, 0xFFFFFF00, 0); + + memset(&frame, 0, sizeof(frame)); + memcpy(frame.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame.eth.src, src_mac, 6); + frame.eth.type = ee16(ETH_TYPE_IP); + frame.ver_ihl = 0x45; + frame.ttl = 64; + frame.proto = WI_IPPROTO_UDP; + frame.len = ee16(IP_HEADER_LEN); + frame.src = ee32(0x0A000002U); + frame.dst = ee32(0x7F000001U); + frame.csum = 0; + iphdr_set_checksum(&frame); + + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, &frame, sizeof(frame)); + + ck_assert_uint_eq(last_frame_sent_size, 0); +} +END_TEST + // Test for `transport_checksum` calculation START_TEST(test_transport_checksum) { union transport_pseudo_header ph; struct wolfIP_tcp_seg tcp_data; + uint16_t checksum; memset(&ph, 0, sizeof(ph)); memset(&tcp_data, 0, sizeof(tcp_data)); // 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 = WI_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 @@ -624,7 +958,7 @@ START_TEST(test_transport_checksum) { tcp_data.flags = 0x02; // SYN tcp_data.win = ee16(65535); - uint16_t checksum = transport_checksum(&ph, &tcp_data.src_port); + checksum = transport_checksum(&ph, &tcp_data.src_port); ck_assert_msg(checksum != 0, "Transport checksum should not be zero"); } END_TEST @@ -640,7 +974,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 = WI_IPPROTO_TCP; ip.src = ee32(0xc0a80101); // 192.168.1.1 ip.dst = ee32(0xc0a80102); // 192.168.1.2 @@ -653,13 +987,16 @@ END_TEST START_TEST(test_eth_output_add_header) { struct wolfIP_eth_frame eth_frame; struct wolfIP S; - memset(&S, 0, sizeof(S)); + uint8_t test_mac[6] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + struct wolfIP_ll_dev *ll; + + wolfIP_init(&S); memset(ð_frame, 0, sizeof(eth_frame)); - uint8_t test_mac[6] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; - memcpy(S.ll_dev.mac, test_mac, 6); + ll = wolfIP_getdev_ex(&S, TEST_PRIMARY_IF); + memcpy(ll->mac, test_mac, 6); - eth_output_add_header(&S, NULL, ð_frame, ETH_TYPE_IP); + eth_output_add_header(&S, TEST_PRIMARY_IF, NULL, ð_frame, ETH_TYPE_IP); ck_assert_mem_eq(eth_frame.dst, "\xff\xff\xff\xff\xff\xff", 6); // Broadcast ck_assert_mem_eq(eth_frame.src, test_mac, 6); @@ -672,6 +1009,9 @@ START_TEST(test_ip_output_add_header) { struct tsocket t; struct wolfIP_ip_packet ip; struct wolfIP S; + int result; + struct wolfIP_tcp_seg *tcp; + memset(&t, 0, sizeof(t)); memset(&ip, 0, sizeof(ip)); memset(&S, 0, sizeof(S)); @@ -682,19 +1022,19 @@ 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); + result = ip_output_add_header(&t, &ip, WI_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, WI_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"); // Check the pseudo-header checksum calculation for TCP segment - struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)&ip; + tcp = (struct wolfIP_tcp_seg *)&ip; ck_assert_msg(tcp->csum != 0, "TCP checksum should not be zero"); } END_TEST @@ -765,6 +1105,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_cancel_timer); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_wolfip_getdev_ex_api); + suite_add_tcase(s, tc_utils); +#if WOLFIP_ENABLE_LOOPBACK + tcase_add_test(tc_utils, test_wolfip_loopback_defaults); + suite_add_tcase(s, tc_utils); +#endif + tcase_add_test(tc_utils, test_wolfip_ipconfig_ex_per_interface); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_proto, test_arp_request_basic); suite_add_tcase(s, tc_proto); @@ -780,6 +1128,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_arp_lookup_failure); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_wolfip_recv_ex_multi_interface_arp_reply); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_wolfip_forwarding_basic); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_wolfip_forwarding_ttl_expired); + suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_loopback_dest_not_forwarded); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_utils, test_transport_checksum); suite_add_tcase(s, tc_proto); diff --git a/src/wolfip.c b/src/wolfip.c index fd97fab6..3a1277c9 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -21,9 +21,37 @@ #include #include +#include #include "wolfip.h" #include "config.h" +#if WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_LOOPBACK_IF_IDX 0U +#define WOLFIP_PRIMARY_IF_IDX 1U +#define WOLFIP_LOOPBACK_IP 0x7F000001U +#define WOLFIP_LOOPBACK_MASK 0xFF000000U +static inline int wolfIP_is_loopback_if(unsigned int if_idx) +{ + return if_idx == WOLFIP_LOOPBACK_IF_IDX; +} +#else +#define WOLFIP_LOOPBACK_IF_IDX 0U +#define WOLFIP_PRIMARY_IF_IDX 0U +static inline int wolfIP_is_loopback_if(unsigned int if_idx) +{ + (void)if_idx; + return 0; +} +#endif + +#define WOLFIP_CONTAINER_OF(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +#if WOLFIP_ENABLE_LOOPBACK +static int wolfIP_loopback_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); +#endif +static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len); + /* Fixed size binary heap: each element is a timer. */ #define MAX_TIMERS MAX_TCPSOCKETS * 3 @@ -32,9 +60,9 @@ #define ICMP_ECHO_REQUEST 8 #define ICMP_TTL_EXCEEDED 11 -#define FT_IPPROTO_ICMP 0x01 -#define FT_IPPROTO_TCP 0x06 -#define FT_IPPROTO_UDP 0x11 +#define WI_IPPROTO_ICMP 0x01 +#define WI_IPPROTO_TCP 0x06 +#define WI_IPPROTO_UDP 0x11 #define IPADDR_ANY 0x00000000 #define TCP_OPTION_MSS 0x02 @@ -62,17 +90,11 @@ #define NO_TIMER 0 -#define FT_IP_MTU 1500 -#define TCP_MSS (FT_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) +#define WI_IP_MTU 1500 +#define TCP_MSS (WI_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) /* Macros */ #define IS_IP_BCAST(ip) (ip == 0xFFFFFFFF) -#define IS_IP_LOCAL(s,ipa) \ - ((ipa & s->ipconf.mask) == (s->ipconf.ip & s->ipconf.mask)) - -#define NEXTHOP(s,ip) \ - (IS_IP_BCAST(ip)?ip: \ - ((IS_IP_LOCAL(s,ip) ? ip : s->ipconf.gw))) #define PKT_FLAG_SENT 0x01 #define PKT_FLAG_ACKED 0x02 @@ -313,7 +335,7 @@ static int queue_pop(struct queue *q, void *data, uint32_t len) { uint32_t q_len = queue_len(q); if (q_len == 0) - return -11; + return -WOLFIP_EAGAIN; if (len > q_len) len = q_len; memcpy(data, (const uint8_t *)q->data + q->tail, len); @@ -392,11 +414,23 @@ union transport_pseudo_header { }; /* ICMP */ + +#define TTL_EXCEEDED_ORIG_PACKET_SIZE (28) +#define ICMP_TTL_EXCEEDED_SIZE (36) + struct PACKED wolfIP_icmp_packet { struct wolfIP_ip_packet ip; uint8_t type, code; uint16_t csum; - uint8_t data[0]; + uint8_t unused[4]; +}; + +struct PACKED wolfIP_icmp_ttl_exceeded_packet { + struct wolfIP_ip_packet ip; + uint8_t type, code; + uint16_t csum; + uint8_t unused[4]; + uint8_t orig_packet[TTL_EXCEEDED_ORIG_PACKET_SIZE]; }; /* DHCP */ @@ -493,6 +527,7 @@ struct tsocket { #ifdef ETHERNET uint8_t nexthop_mac[6]; #endif + uint8_t if_idx; uint8_t rxmem[RXBUF_SIZE]; uint8_t txmem[TXBUF_SIZE]; void (*callback)(int sock_fd, uint16_t events, void *arg); @@ -514,8 +549,26 @@ struct PACKED arp_packet { struct arp_neighbor { ip4 ip; uint8_t mac[6]; + uint8_t if_idx; }; +#ifndef WOLFIP_ARP_PENDING_MAX +#define WOLFIP_ARP_PENDING_MAX 4 +#endif + +struct arp_pending_entry { + ip4 dest; + uint32_t len; + uint8_t if_idx; + uint8_t frame[LINK_MTU]; +}; + +static int arp_lookup(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac); +#if WOLFIP_ENABLE_FORWARDING +static void wolfIP_forward_packet(struct wolfIP *s, unsigned int out_if, struct wolfIP_ip_packet *ip, + uint32_t len, const uint8_t *mac, int broadcast); +#endif + #endif struct wolfIP; @@ -535,8 +588,9 @@ struct timers_binheap { struct wolfIP { - struct ll ll_dev; - struct ipconf ipconf; + struct wolfIP_ll_dev ll_dev[WOLFIP_MAX_INTERFACES]; + struct ipconf ipconf[WOLFIP_MAX_INTERFACES]; + unsigned int if_count; enum dhcp_state dhcp_state; /* State machine for DHCP */ uint32_t dhcp_xid; /* DHCP transaction ID while DORA */ int dhcp_udp_sd; /* DHCP socket descriptor. DHCP uses an UDP socket */ @@ -555,15 +609,217 @@ struct wolfIP uint64_t last_tick; #ifdef ETHERNET struct wolfIP_arp { - uint64_t last_arp; + uint64_t last_arp[WOLFIP_MAX_INTERFACES]; struct arp_neighbor neighbors[MAX_NEIGHBORS]; } arp; + struct arp_pending_entry arp_pending[WOLFIP_ARP_PENDING_MAX]; #endif }; +#if WOLFIP_ENABLE_LOOPBACK + +static int wolfIP_loopback_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct wolfIP *s; + uint32_t copy = len; + uint8_t frame[LINK_MTU]; + if (!ll || !buf) + return -1; + s = WOLFIP_CONTAINER_OF(ll, struct wolfIP, ll_dev); + if (!s) + return -1; + if (copy > LINK_MTU) + copy = LINK_MTU; + memcpy(frame, buf, copy); + wolfIP_recv_on(s, WOLFIP_LOOPBACK_IF_IDX, frame, copy); + return (int)copy; +} +#endif + /* ***************************** */ /* Implementation */ +static inline struct wolfIP_ll_dev *wolfIP_ll_at(struct wolfIP *s, unsigned int if_idx) +{ + if (!s || if_idx >= s->if_count) + return NULL; + return &s->ll_dev[if_idx]; +} + +static inline struct ipconf *wolfIP_ipconf_at(struct wolfIP *s, unsigned int if_idx) +{ + if (!s || if_idx >= s->if_count) + return NULL; + return &s->ipconf[if_idx]; +} + +static inline struct ipconf *wolfIP_primary_ipconf(struct wolfIP *s) +{ + return wolfIP_ipconf_at(s, WOLFIP_PRIMARY_IF_IDX); +} + +static inline int ip_is_local_conf(const struct ipconf *conf, ip4 addr) +{ + if (!conf) + return 0; + if (conf->mask == 0) + return conf->ip == addr; + return ((addr & conf->mask) == (conf->ip & conf->mask)); +} + +#if WOLFIP_ENABLE_FORWARDING +static int wolfIP_forward_interface(struct wolfIP *s, unsigned int in_if, ip4 dest) +{ + int i; + if (!s || s->if_count < 2) + return s ? s->if_count : 0; + for (i = 0; i < (int)s->if_count; i++) { + struct ipconf *conf = &s->ipconf[i]; + if (i == (int)in_if) + continue; + if (!conf || conf->ip == IPADDR_ANY) + continue; + if (dest == conf->ip) + return -1; + if (ip_is_local_conf(conf, dest)) { + return i; + } + } + return -1; +} +#endif + +static inline ip4 wolfIP_select_nexthop(const struct ipconf *conf, ip4 dest) +{ + if (IS_IP_BCAST(dest)) + return dest; + if (!conf) + return dest; + if (ip_is_local_conf(conf, dest)) + return dest; + if (conf->gw != IPADDR_ANY) + return conf->gw; + return dest; +} + +static unsigned int wolfIP_route_for_ip(struct wolfIP *s, ip4 dest) +{ + unsigned int default_if = 0; + unsigned int gw_fallback = 0; + unsigned int first_non_loop = 0; + int has_gw_fallback = 0; + int has_non_loop = 0; + unsigned int i; + + if (!s || s->if_count == 0) + return 0; + + if (WOLFIP_PRIMARY_IF_IDX < s->if_count) + default_if = WOLFIP_PRIMARY_IF_IDX; + + if (dest == IPADDR_ANY || IS_IP_BCAST(dest)) + return default_if; + + for (i = 0; i < s->if_count; i++) { + struct ipconf *conf = &s->ipconf[i]; + if (conf->ip == IPADDR_ANY && conf->gw == IPADDR_ANY) + continue; + if (ip_is_local_conf(conf, dest) || conf->ip == dest) { + return i; + } + if (!wolfIP_is_loopback_if(i) && !has_non_loop) { + first_non_loop = i; + has_non_loop = 1; + } + if (!wolfIP_is_loopback_if(i) && !has_gw_fallback && conf->gw != IPADDR_ANY) { + gw_fallback = i; + has_gw_fallback = 1; + } + } + if (has_gw_fallback) { + return gw_fallback; + } + if (has_non_loop) { + return first_non_loop; + } + return default_if; +} + +static inline unsigned int wolfIP_socket_if_idx(const struct tsocket *t) +{ + if (!t || !t->S || t->if_idx >= t->S->if_count) + return 0; + return t->if_idx; +} + +static unsigned int wolfIP_if_for_local_ip(struct wolfIP *s, ip4 local_ip, int *found) +{ + unsigned int primary = 0; + unsigned int i; + if (found) + *found = 0; + if (!s || s->if_count == 0) + return 0; + if (WOLFIP_PRIMARY_IF_IDX < s->if_count) + primary = WOLFIP_PRIMARY_IF_IDX; + if (local_ip == IPADDR_ANY) + return primary; + for (i = 0; i < s->if_count; i++) { + struct ipconf *conf = &s->ipconf[i]; + if (conf->ip == local_ip) { + if (found) + *found = 1; + return i; + } + } + return primary; +} + +#ifdef ETHERNET +static uint16_t icmp_checksum(struct wolfIP_icmp_packet *icmp, uint16_t len); +static void iphdr_set_checksum(struct wolfIP_ip_packet *ip); +static int eth_output_add_header(struct wolfIP *S, unsigned int if_idx, const uint8_t *dst, struct wolfIP_eth_frame *eth, + uint16_t type); +#endif +#if WOLFIP_ENABLE_FORWARDING && defined(ETHERNET) +static void arp_request(struct wolfIP *s, unsigned int if_idx, ip4 tip); +static int arp_lookup(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac); +#endif + +#ifdef ETHERNET +static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *orig) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + struct wolfIP_icmp_ttl_exceeded_packet icmp; + if (!ll || !ll->send) + return; + memset(&icmp, 0, sizeof(icmp)); + icmp.type = ICMP_TTL_EXCEEDED; + memcpy(icmp.orig_packet, ((uint8_t *)orig) + ETH_HEADER_LEN, + TTL_EXCEEDED_ORIG_PACKET_SIZE); + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_TTL_EXCEEDED_SIZE)); + icmp.ip.ver_ihl = 0x45; + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.id = ee16(s->ipcounter++); + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_TTL_EXCEEDED_SIZE); + icmp.ip.src = ee32(wolfIP_ipconf_at(s, if_idx)->ip); + icmp.ip.dst = orig->src; + icmp.ip.csum = 0; + iphdr_set_checksum(&icmp.ip); + eth_output_add_header(s, if_idx, orig->eth.src, &icmp.ip.eth, ETH_TYPE_IP); + ll->send(ll, &icmp, sizeof(icmp)); +} +#else +static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *orig) +{ + (void)s; + (void)if_idx; + (void)orig; +} +#endif + /* User Callbacks */ void wolfIP_register_callback(struct wolfIP *s, int sock_fd, void (*cb)(int sock_fd, uint16_t events, void *arg), void *arg) { @@ -615,6 +871,7 @@ static struct wolfIP_timer timers_binheap_pop(struct timers_binheap *heap) static int timers_binheap_insert(struct timers_binheap *heap, struct wolfIP_timer tmr) { static uint32_t timer_id = 1; + int i; if (timer_id == 0) timer_id = 1; while (heap->size > 0 && heap->timers[0].expires == 0) @@ -623,7 +880,7 @@ static int timers_binheap_insert(struct timers_binheap *heap, struct wolfIP_time /* Insert at the end */ heap->timers[heap->size] = tmr; heap->size++; - int i = heap->size - 1; + i = heap->size - 1; while (i > 0 && heap->timers[i].expires < heap->timers[(i-1)/2].expires) { struct wolfIP_timer tmp = heap->timers[i]; heap->timers[i] = heap->timers[(i-1)/2]; @@ -659,12 +916,14 @@ static void timer_binheap_cancel(struct timers_binheap *heap, uint32_t id) static struct tsocket *udp_new_socket(struct wolfIP *s) { struct tsocket *t; + int i; - for (int i = 0; i < MAX_UDPSOCKETS; i++) { + for (i = 0; i < MAX_UDPSOCKETS; i++) { t = &s->udpsockets[i]; if (t->proto == 0) { - t->proto = FT_IPPROTO_UDP; + t->proto = WI_IPPROTO_UDP; t->S = s; + t->if_idx = 0; 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; @@ -674,13 +933,20 @@ static struct tsocket *udp_new_socket(struct wolfIP *s) return NULL; } -static void udp_try_recv(struct wolfIP *s, struct wolfIP_udp_datagram *udp, uint32_t frame_len) +static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_udp_datagram *udp, uint32_t frame_len) { - for (int i = 0; i < MAX_UDPSOCKETS; i++) { + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + int i; + ip4 local_ip = conf ? conf->ip : IPADDR_ANY; + ip4 dst_ip = ee32(udp->ip.dst); + for (i = 0; i < MAX_UDPSOCKETS; i++) { struct tsocket *t = &s->udpsockets[i]; if (t->src_port == ee16(udp->dst_port) && t->dst_port == ee16(udp->src_port) && (((t->local_ip == 0) && DHCP_IS_RUNNING(s)) || - (t->local_ip == ee32(udp->ip.dst) && t->remote_ip != s->ipconf.ip)) ) { + (t->local_ip == dst_ip && t->remote_ip != local_ip)) ) { + + if (t->local_ip == 0) + t->if_idx = (uint8_t)if_idx; /* UDP datagram sanity checks */ if ((int)frame_len != ee16(udp->len) + IP_HEADER_LEN + ETH_HEADER_LEN) @@ -696,11 +962,13 @@ static void udp_try_recv(struct wolfIP *s, struct wolfIP_udp_datagram *udp, uint static struct tsocket *tcp_new_socket(struct wolfIP *s) { struct tsocket *t; - for (int i = 0; i < MAX_TCPSOCKETS; i++) { + int i; + for (i = 0; i < MAX_TCPSOCKETS; i++) { t = &s->tcpsockets[i]; if (t->proto == 0) { - t->proto = FT_IPPROTO_TCP; + t->proto = WI_IPPROTO_TCP; t->S = s; + t->if_idx = 0; t->sock.tcp.state = TCP_CLOSED; t->sock.tcp.rto = 1000; t->sock.tcp.cwnd = 2 * TCP_MSS; @@ -840,12 +1108,12 @@ static uint16_t transport_checksum(union transport_pseudo_header *ph, void *_dat return ~sum; } -static uint16_t icmp_checksum(struct wolfIP_icmp_packet *icmp) +static uint16_t icmp_checksum(struct wolfIP_icmp_packet *icmp, uint16_t len) { uint32_t sum = 0; uint32_t i = 0; uint16_t *ptr = (uint16_t *)(&icmp->type); - for (i = 0; i < ICMP_HEADER_LEN / 2; i++) { + for (i = 0; i < len / 2; i++) { sum += ee16(ptr[i]); } while (sum >> 16) { @@ -869,9 +1137,12 @@ static void iphdr_set_checksum(struct wolfIP_ip_packet *ip) } #ifdef ETHERNET -static int eth_output_add_header(struct wolfIP *S, const uint8_t *dst, struct wolfIP_eth_frame *eth, +static int eth_output_add_header(struct wolfIP *S, unsigned int if_idx, const uint8_t *dst, struct wolfIP_eth_frame *eth, uint16_t type) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(S, if_idx); + if (!ll) + return -1; if (!dst) { /* Arp request, broadcast */ memset(eth->dst, 0xff, 6); @@ -879,15 +1150,70 @@ static int eth_output_add_header(struct wolfIP *S, const uint8_t *dst, struct wo /* Send to nexthop */ memcpy(eth->dst, dst, 6); } - memcpy(eth->src, S->ll_dev.mac, 6); + memcpy(eth->src, ll->mac, 6); eth->type = ee16(type); return 0; } #endif +#if WOLFIP_ENABLE_FORWARDING +static int wolfIP_forward_prepare(struct wolfIP *s, unsigned int out_if, ip4 dest, uint8_t *mac, int *broadcast) +{ +#ifdef ETHERNET + if (!broadcast || !mac) + return 0; + if (wolfIP_is_loopback_if(out_if)) { + struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, out_if); + if (loop) + memcpy(mac, loop->mac, 6); + *broadcast = 0; + return 1; + } + if (IS_IP_BCAST(dest)) { + *broadcast = 1; + return 1; + } + *broadcast = 0; + if (arp_lookup(s, out_if, dest, mac) == 0) + return 1; + arp_request(s, out_if, dest); + return 0; +#else + (void)s; + (void)out_if; + (void)dest; + (void)mac; + (void)broadcast; + return 0; +#endif +} + +static void wolfIP_forward_packet(struct wolfIP *s, unsigned int out_if, struct wolfIP_ip_packet *ip, uint32_t len, const uint8_t *mac, int broadcast) +{ +#ifdef ETHERNET + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, out_if); + if (!ll || !ll->send) + return; + if (broadcast) + eth_output_add_header(s, out_if, NULL, &ip->eth, ETH_TYPE_IP); + else + eth_output_add_header(s, out_if, mac, &ip->eth, ETH_TYPE_IP); + ll->send(ll, ip, len); +#else + (void)s; + (void)out_if; + (void)ip; + (void)len; + (void)mac; + (void)broadcast; +#endif +} +#endif + static int ip_output_add_header(struct tsocket *t, struct wolfIP_ip_packet *ip, uint8_t proto, uint16_t len) { union transport_pseudo_header ph; + unsigned int if_idx; memset(&ph, 0, sizeof(ph)); memset(ip, 0, sizeof(struct wolfIP_ip_packet)); ip->src = ee32(t->local_ip); @@ -907,17 +1233,20 @@ static int ip_output_add_header(struct tsocket *t, struct wolfIP_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 == WI_IPPROTO_TCP) { struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)ip; tcp->csum = 0; tcp->csum = ee16(transport_checksum(&ph, &tcp->src_port)); - } else if (proto == FT_IPPROTO_UDP) { + } else if (proto == WI_IPPROTO_UDP) { struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip; udp->csum = 0; udp->csum = ee16(transport_checksum(&ph, &udp->src_port)); } #ifdef ETHERNET - eth_output_add_header(t->S, t->nexthop_mac, (struct wolfIP_eth_frame *)ip, ETH_TYPE_IP); + if_idx = wolfIP_socket_if_idx(t); + eth_output_add_header(t->S, if_idx, t->nexthop_mac, (struct wolfIP_eth_frame *)ip, ETH_TYPE_IP); +#else + (void)if_idx; #endif return 0; } @@ -1050,14 +1379,18 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } /* Preselect socket, parse options, manage handshakes, pass to application */ -static void tcp_input(struct wolfIP *S, struct wolfIP_tcp_seg *tcp, uint32_t frame_len) +static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_seg *tcp, uint32_t frame_len) { - for (int i = 0; i < MAX_TCPSOCKETS; i++) { + struct ipconf *conf = wolfIP_ipconf_at(S, if_idx); + ip4 local_ip = conf ? conf->ip : IPADDR_ANY; + int i; + for (i = 0; i < MAX_TCPSOCKETS; i++) { uint32_t tcplen; uint32_t iplen; struct tsocket *t = &S->tcpsockets[i]; if (t->src_port == ee16(tcp->dst_port) && - t->local_ip == ee32(tcp->ip.dst) && t->remote_ip != S->ipconf.ip) { + t->local_ip == ee32(tcp->ip.dst) && t->remote_ip != local_ip) { + t->if_idx = (uint8_t)if_idx; /* TCP segment sanity checks */ iplen = ee16(tcp->ip.len); if (iplen > frame_len - sizeof(struct wolfIP_eth_frame)) { @@ -1072,19 +1405,7 @@ static void tcp_input(struct wolfIP *S, struct wolfIP_tcp_seg *tcp, uint32_t fra } /* Check IP ttl */ if (tcp->ip.ttl == 0) { - /* Send ICMP TTL exceeded */ - struct wolfIP_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 wolfIP_icmp_packet)); + wolfIP_send_ttl_exceeded(S, if_idx, &tcp->ip); return; } tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2)); @@ -1179,7 +1500,7 @@ static void tcp_rto_cb(void *arg) struct wolfIP_timer tmr = { }; struct wolfIP_timer *ptmr = NULL; int pending = 0; - if ((ts->proto != FT_IPPROTO_TCP) || (ts->sock.tcp.state != TCP_ESTABLISHED)) + if ((ts->proto != WI_IPPROTO_TCP) || (ts->sock.tcp.state != TCP_ESTABLISHED)) return; desc = fifo_peek(&ts->sock.tcp.txbuf); while (desc) { @@ -1238,37 +1559,69 @@ int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) { struct tsocket *ts; - const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)addr; - if (!addr) - return -2; + const struct wolfIP_sockaddr_in *sin; + unsigned int if_idx; + if ((!addr)|| (sockfd < 0)) + return -WOLFIP_EINVAL; + sin = (const struct wolfIP_sockaddr_in *)addr; if (sockfd & MARK_UDP_SOCKET) { + struct ipconf *conf; + if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; ts->dst_port = ee16(sin->sin_port); ts->remote_ip = ee32(sin->sin_addr.s_addr); + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (ts->local_ip == 0 && conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else if (ts->local_ip == 0) { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } return 0; } + if ((sockfd & MARK_TCP_SOCKET) == 0) - return -1; + return -WOLFIP_EINVAL; + if ((sockfd & ~MARK_TCP_SOCKET)>= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (ts->sock.tcp.state == TCP_ESTABLISHED) return 0; if (ts->sock.tcp.state == TCP_SYN_SENT) - return -11; /* Call again */ + return -WOLFIP_EAGAIN; /* Call again */ if ((sin->sin_family != AF_INET) || (addrlen < sizeof(struct wolfIP_sockaddr_in))) - return -2; + return -WOLFIP_EINVAL; if (ts->sock.tcp.state == TCP_CLOSED) { + struct ipconf *conf; ts->sock.tcp.state = TCP_SYN_SENT; - ts->local_ip = s->ipconf.ip; ts->remote_ip = ee32(sin->sin_addr.s_addr); + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + else + ts->local_ip = 0; + } if (!ts->src_port) ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port < 1024) ts->src_port += 1024; ts->dst_port = ee16(sin->sin_port); tcp_send_syn(ts, 0x02); - return -11; + return -WOLFIP_EAGAIN; } - return -2; + return -WOLFIP_EINVAL; } int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) @@ -1278,12 +1631,17 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add struct tsocket *newts; if ((addr) && (!(addrlen) || (*addrlen < sizeof(struct wolfIP_sockaddr_in)))) - return -1; + return -WOLFIP_EINVAL; + + if (sockfd < 0) + return -WOLFIP_EINVAL; if (addr && addrlen) *addrlen = sizeof(struct wolfIP_sockaddr_in); if (sockfd & MARK_TCP_SOCKET) { + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if ((ts->sock.tcp.state != TCP_SYN_RCVD) && (ts->sock.tcp.state != TCP_LISTEN)) return -1; @@ -1298,6 +1656,7 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add newts->callback = ts->callback; newts->callback_arg = ts->callback_arg; newts->local_ip = ts->local_ip; + newts->if_idx = ts->if_idx; newts->remote_ip = ts->remote_ip; newts->src_port = ts->src_port; newts->dst_port = ts->dst_port; @@ -1313,10 +1672,10 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add ts->sock.tcp.seq = wolfIP_getrandom(); return (newts - s->tcpsockets) | MARK_TCP_SOCKET; } else if (ts->sock.tcp.state == TCP_LISTEN) { - return -11; + return -WOLFIP_EAGAIN; } } - return -1;; + return -WOLFIP_EINVAL; } int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len, int flags, @@ -1324,19 +1683,28 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len { uint8_t frame[LINK_MTU]; struct tsocket *ts; - struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)frame; - struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)frame; - + struct wolfIP_tcp_seg *tcp; + struct wolfIP_udp_datagram *udp; + tcp = (struct wolfIP_tcp_seg *)frame; + udp = (struct wolfIP_udp_datagram *)frame; (void)flags; if (sockfd < 0) + return -WOLFIP_EINVAL; + + if ((!buf) || (len == 0)) return -1; + if (sockfd & MARK_TCP_SOCKET) { size_t sent = 0; struct tcp_opt_ts *tsopt = (struct tcp_opt_ts *)tcp->data; + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (ts->sock.tcp.state != TCP_ESTABLISHED) return -1; + while (sent < len) { uint32_t payload_len = len - sent; if (payload_len > (TCP_MSS - TCP_OPTIONS_LEN)) @@ -1366,11 +1734,16 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len ts->sock.tcp.seq += payload_len; } if (sent == 0) - return -11; + return -WOLFIP_EAGAIN; else return sent; } else if (sockfd & MARK_UDP_SOCKET) { const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; + unsigned int if_idx; + struct ipconf *conf; + if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; if ((ts->dst_port == 0) && (dest_addr == NULL)) return -1; @@ -1383,17 +1756,27 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len } if ((ts->dst_port==0) || (ts->remote_ip==0)) return -1; - if (len > FT_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) + if (len > WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) return -1; /* Fragmentation not supported */ if (fifo_space(&ts->sock.udp.txbuf) < len) - return -11; + return -WOLFIP_EAGAIN; if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port < 1024) ts->src_port += 1024; } - if(ts->local_ip == 0) - ts->local_ip = s->ipconf.ip; + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (ts->local_ip == 0) { + if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } + } udp->src_port = ee16(ts->src_port); udp->dst_port = ee16(ts->dst_port); @@ -1408,7 +1791,7 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len int wolfIP_sock_send(struct wolfIP *s, int sockfd, const void *buf, size_t len, int flags) { return wolfIP_sock_sendto(s, sockfd, buf, len, flags, NULL, 0); -} +} int wolfIP_sock_write(struct wolfIP *s, int sockfd, const void *buf, size_t len) { @@ -1424,7 +1807,12 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in struct tsocket *ts; (void)flags; + if (sockfd < 0) + return -WOLFIP_EINVAL; + if (sockfd & MARK_TCP_SOCKET) { + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (ts->sock.tcp.state == TCP_CLOSE_WAIT) { @@ -1442,12 +1830,14 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in } } else if (sockfd & MARK_UDP_SOCKET) { struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)src_addr; + if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; if (sin && *addrlen < sizeof(struct wolfIP_sockaddr_in)) return -1; if (addrlen) *addrlen = sizeof(struct wolfIP_sockaddr_in); if (fifo_len(&ts->sock.udp.rxbuf) == 0) - return -11; + return -WOLFIP_EAGAIN; desc = fifo_peek(&ts->sock.udp.rxbuf); udp = (struct wolfIP_udp_datagram *)(ts->rxmem + desc->pos + sizeof(*desc)); if (sin) { @@ -1461,7 +1851,8 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in memcpy(buf, udp->data, seg_len); fifo_pop(&ts->sock.udp.rxbuf); return seg_len; - } else return -1; + } else + return -WOLFIP_EINVAL; } int wolfIP_sock_recv(struct wolfIP *s, int sockfd, void *buf, size_t len, int flags) @@ -1476,32 +1867,40 @@ int wolfIP_sock_read(struct wolfIP *s, int sockfd, void *buf, size_t len) int wolfIP_sock_close(struct wolfIP *s, int sockfd) { + if (sockfd < 0) + return -WOLFIP_EINVAL; if (sockfd & MARK_TCP_SOCKET) { - struct tsocket *ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; + struct tsocket *ts; + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (ts->sock.tcp.state == TCP_ESTABLISHED) { ts->sock.tcp.state = TCP_FIN_WAIT_1; tcp_send_finack(ts); - return -11; + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_CLOSE_WAIT) { ts->sock.tcp.state = TCP_LAST_ACK; tcp_send_finack(ts); - return -11; + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_CLOSING) { ts->sock.tcp.state = TCP_TIME_WAIT; - return -11; + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_FIN_WAIT_1) { ts->sock.tcp.state = TCP_CLOSING; - return -11; + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_FIN_WAIT_2) { ts->sock.tcp.state = TCP_TIME_WAIT; - return -11; + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state != TCP_CLOSED) { ts->sock.tcp.state = TCP_CLOSED; close_socket(ts); return 0; } else return -1; } else if (sockfd & MARK_UDP_SOCKET) { - struct tsocket *ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; + struct tsocket *ts; + if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->tcpsockets[sockfd & ~MARK_UDP_SOCKET]; close_socket(ts); return 0; } else return -1; @@ -1510,8 +1909,19 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) int wolfIP_sock_getsockname(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *addr, const socklen_t *addrlen) { - struct tsocket *ts = &s->tcpsockets[sockfd]; - struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)addr; + struct tsocket *ts; + struct wolfIP_sockaddr_in *sin; + + if ((!addr) || (sockfd < 0)) + return -WOLFIP_EINVAL; + + if ((sockfd & MARK_TCP_SOCKET) == 0) + return -1; + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + + ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; + sin = (struct wolfIP_sockaddr_in *)addr; if (!sin || *addrlen < sizeof(struct wolfIP_sockaddr_in)) return -1; sin->sin_family = AF_INET; @@ -1523,25 +1933,62 @@ int wolfIP_sock_getsockname(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) { struct tsocket *ts; + ip4 bind_ip; + struct ipconf *conf; const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)addr; + int match = 0; + unsigned int if_idx; + if (!sin || addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -WOLFIP_EINVAL; + + if (sockfd < 0) + return -WOLFIP_EINVAL; + + bind_ip = ee32(sin->sin_addr.s_addr); + if_idx = wolfIP_if_for_local_ip(s, bind_ip, &match); + conf = wolfIP_ipconf_at(s, if_idx); + if ((bind_ip != IPADDR_ANY) && !match) return -1; if (sockfd & MARK_TCP_SOCKET) { + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (ts->sock.tcp.state != TCP_CLOSED) return -1; if ((sin->sin_family != AF_INET) || (addrlen < sizeof(struct wolfIP_sockaddr_in))) return -1; - ts->local_ip = s->ipconf.ip; + ts->if_idx = (uint8_t)if_idx; + if (bind_ip != IPADDR_ANY) + ts->local_ip = bind_ip; + else if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } ts->src_port = ee16(sin->sin_port); return 0; } else if (sockfd & MARK_UDP_SOCKET) { + if ((sockfd & ~MARK_UDP_SOCKET) >= MAX_UDPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->udpsockets[sockfd & ~MARK_UDP_SOCKET]; if (ts->src_port != 0) return -1; if ((sin->sin_family != AF_INET) || (addrlen < sizeof(struct wolfIP_sockaddr_in))) return -1; + ts->if_idx = (uint8_t)if_idx; + if (bind_ip != IPADDR_ANY) + ts->local_ip = bind_ip; + else if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } ts->src_port = ee16(sin->sin_port); return 0; } else return -1; @@ -1552,9 +1999,15 @@ int wolfIP_sock_listen(struct wolfIP *s, int sockfd, int backlog) { struct tsocket *ts; (void)backlog; + if (sockfd < 0) + return -WOLFIP_EINVAL; if (sockfd & MARK_TCP_SOCKET) { + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; - } else return -1; + } else + return -1; + if (ts->sock.tcp.state != TCP_CLOSED) return -1; ts->sock.tcp.state = TCP_LISTEN; @@ -1563,8 +2016,17 @@ int wolfIP_sock_listen(struct wolfIP *s, int sockfd, int backlog) int wolfIP_sock_getpeername(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *addr, const socklen_t *addrlen) { - struct tsocket *ts = &s->tcpsockets[sockfd]; + struct tsocket *ts; struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)addr; + if (sockfd < 0) + return -WOLFIP_EINVAL; + if ((sockfd & MARK_TCP_SOCKET) == 0) { + return -1; + } + if ((sockfd & ~MARK_TCP_SOCKET) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + + ts = &s->tcpsockets[sockfd & ~MARK_TCP_SOCKET]; if (!sin || *addrlen < sizeof(struct wolfIP_sockaddr_in)) return -1; sin->sin_family = AF_INET; @@ -1575,10 +2037,11 @@ int wolfIP_sock_getpeername(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr /* Reply to ICecho requests */ -static void icmp_input(struct wolfIP *s, struct wolfIP_ip_packet *ip, uint32_t len) +static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *ip, uint32_t len) { struct wolfIP_icmp_packet *icmp = (struct wolfIP_icmp_packet *)ip; uint32_t tmp; + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); if (!DHCP_IS_RUNNING(s) && (icmp->type == ICMP_ECHO_REQUEST)) { icmp->type = ICMP_ECHO_REPLY; icmp->csum += 8; @@ -1588,8 +2051,9 @@ static void icmp_input(struct wolfIP *s, struct wolfIP_ip_packet *ip, uint32_t l ip->id = ee16(s->ipcounter++); ip->csum = 0; iphdr_set_checksum(ip); - eth_output_add_header(s, ip->eth.src, &ip->eth, ETH_TYPE_IP); - s->ll_dev.send(&s->ll_dev, ip, len); + eth_output_add_header(s, if_idx, ip->eth.src, &ip->eth, ETH_TYPE_IP); + if (ll && ll->send) + ll->send(ll, ip, len); } } @@ -1635,6 +2099,7 @@ static int dhcp_parse_offer(struct wolfIP *s, struct dhcp_msg *msg) struct dhcp_option *opt = (struct dhcp_option *)(msg->options); uint32_t ip; uint32_t netmask = 0xFFFFFF00; + struct ipconf *primary = wolfIP_primary_ipconf(s); while (opt->code != 0xFF) { if (opt->code == DHCP_OPTION_MSG_TYPE) { if (opt->data[0] == DHCP_OFFER) { @@ -1651,8 +2116,10 @@ static int dhcp_parse_offer(struct wolfIP *s, struct dhcp_msg *msg) opt = (struct dhcp_option *)((uint8_t *)opt + 2 + opt->len); } ip = ee32(msg->yiaddr); - s->ipconf.ip = ip; - s->ipconf.mask = ee32(netmask); + if (primary) { + primary->ip = ip; + primary->mask = ee32(netmask); + } s->dhcp_ip = ip; dhcp_cancel_timer(s); s->dhcp_state = DHCP_REQUEST_SENT; @@ -1672,6 +2139,7 @@ static int dhcp_parse_offer(struct wolfIP *s, struct dhcp_msg *msg) static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg) { struct dhcp_option *opt = (struct dhcp_option *)(msg->options); + struct ipconf *primary = wolfIP_primary_ipconf(s); while (opt->code != 0xFF) { if (opt->code == DHCP_OPTION_MSG_TYPE) { if (opt->data[0] == DHCP_ACK) { @@ -1681,17 +2149,19 @@ static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg) while (opt->code != 0xFF) { if (opt->code == DHCP_OPTION_SERVER_ID) s->dhcp_server_ip = ee32(data); - if (opt->code == DHCP_OPTION_OFFER_IP) - s->ipconf.ip = ee32(data); - if (opt->code == DHCP_OPTION_SUBNET_MASK) - s->ipconf.mask = ee32(data); - if (opt->code == DHCP_OPTION_ROUTER) - s->ipconf.gw = ee32(data); + if (primary) { + if (opt->code == DHCP_OPTION_OFFER_IP) + primary->ip = ee32(data); + if (opt->code == DHCP_OPTION_SUBNET_MASK) + primary->mask = ee32(data); + if (opt->code == DHCP_OPTION_ROUTER) + primary->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)) { + if (primary && (primary->ip != 0) && (primary->mask != 0)) { dhcp_cancel_timer(s); s->dhcp_state = DHCP_BOUND; return 0; @@ -1715,10 +2185,13 @@ static int dhcp_poll(struct wolfIP *s) 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)) { + struct ipconf *primary = wolfIP_primary_ipconf(s); 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 (primary) { + LOG("IP Address: %u.%u.%u.%u\n", (primary->ip >> 24) & 0xFF, (primary->ip >> 16) & 0xFF, (primary->ip >> 8) & 0xFF, (primary->ip >> 0) & 0xFF); + LOG("Subnet Mask: %u.%u.%u.%u\n", (primary->mask >> 24) & 0xFF, (primary->mask >> 16) & 0xFF, (primary->mask >> 8) & 0xFF, (primary->mask >> 0) & 0xFF); + LOG("Gateway: %u.%u.%u.%u\n", (primary->gw >> 24) & 0xFF, (primary->gw >> 16) & 0xFF, (primary->gw >> 8) & 0xFF, (primary->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); } @@ -1740,7 +2213,13 @@ static int dhcp_send_request(struct wolfIP *s) req.hlen = 6; /* MAC */ req.xid = ee32(s->dhcp_xid); req.magic = ee32(DHCP_MAGIC); - memcpy(req.chaddr, s->ll_dev.mac, 6); + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, WOLFIP_PRIMARY_IF_IDX); + if (ll) + memcpy(req.chaddr, ll->mac, 6); + else + memset(req.chaddr, 0, 6); + } /* Set options */ memset(req.options, 0xFF, sizeof(req.options)); @@ -1810,7 +2289,13 @@ static int dhcp_send_discover(struct wolfIP *s) disc.hlen = 6; /* MAC */ disc.xid = ee32(s->dhcp_xid); disc.magic = ee32(DHCP_MAGIC); - memcpy(disc.chaddr, s->ll_dev.mac, 6); + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, WOLFIP_PRIMARY_IF_IDX); + if (ll) + memcpy(disc.chaddr, ll->mac, 6); + else + memset(disc.chaddr, 0, 6); + } /* Set options */ memset(disc.options, 0xFF, sizeof(disc.options)); @@ -1858,7 +2343,7 @@ int dhcp_client_init(struct wolfIP *s) wolfIP_sock_close(s, s->dhcp_udp_sd); } - s->dhcp_udp_sd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, FT_IPPROTO_UDP); + s->dhcp_udp_sd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); if (s->dhcp_udp_sd < 0) { s->dhcp_state = DHCP_OFF; return -1; @@ -1877,73 +2362,165 @@ int dhcp_client_init(struct wolfIP *s) /* ARP */ #ifdef ETHERNET -static void arp_request(struct wolfIP *s, ip4 tip) +#if WOLFIP_ENABLE_FORWARDING +static void arp_queue_packet(struct wolfIP *s, unsigned int if_idx, ip4 dest, + const struct wolfIP_ip_packet *ip, uint32_t len) +{ + int slot = -1; + int i; + + if (!s || len == 0) + return; + if (len > LINK_MTU) + len = LINK_MTU; + + for (i = 0; i < WOLFIP_ARP_PENDING_MAX; i++) { + if (s->arp_pending[i].dest == dest && s->arp_pending[i].if_idx == if_idx) { + slot = i; + break; + } + if (slot < 0 && s->arp_pending[i].dest == IPADDR_ANY) + slot = i; + } + if (slot < 0) + slot = 0; + + memcpy(s->arp_pending[slot].frame, ip, len); + s->arp_pending[slot].len = len; + s->arp_pending[slot].dest = dest; + s->arp_pending[slot].if_idx = (uint8_t)if_idx; +} + +static void arp_flush_pending(struct wolfIP *s, unsigned int if_idx, ip4 ip) +{ + uint8_t mac[6]; + int i; + + if (!s) + return; + if (arp_lookup(s, if_idx, ip, mac) != 0) + return; + + for (i = 0; i < WOLFIP_ARP_PENDING_MAX; i++) { + struct arp_pending_entry *pending = &s->arp_pending[i]; + if (pending->dest != ip || pending->if_idx != if_idx) + continue; + if (pending->len == 0) { + pending->dest = IPADDR_ANY; + continue; + } + if (pending->len > LINK_MTU) + pending->len = LINK_MTU; + { + struct wolfIP_ip_packet *pkt = + (struct wolfIP_ip_packet *)pending->frame; + + if (pkt->ttl <= 1) { + pending->dest = IPADDR_ANY; + pending->len = 0; + continue; + } + pkt->ttl--; + pkt->csum = 0; + iphdr_set_checksum(pkt); + wolfIP_forward_packet(s, if_idx, pkt, pending->len, mac, 0); + } + pending->dest = IPADDR_ANY; + pending->len = 0; + } +} +#endif /* WOLFIP_ENABLE_FORWARDING */ + +static void arp_store_neighbor(struct wolfIP *s, unsigned int if_idx, ip4 ip, const uint8_t *mac) +{ + int i; + int stored = 0; + if (!s) + return; + for (i = 0; i < MAX_NEIGHBORS; i++) { + if (s->arp.neighbors[i].ip == ip && s->arp.neighbors[i].if_idx == if_idx) { + memcpy(s->arp.neighbors[i].mac, mac, 6); + stored = 1; + break; + } + } + if (!stored) { + for (i = 0; i < MAX_NEIGHBORS; i++) { + if (s->arp.neighbors[i].ip == IPADDR_ANY) { + s->arp.neighbors[i].ip = ip; + s->arp.neighbors[i].if_idx = (uint8_t)if_idx; + memcpy(s->arp.neighbors[i].mac, mac, 6); + stored = 1; + break; + } + } + } + if (stored) { +#if WOLFIP_ENABLE_FORWARDING + arp_flush_pending(s, if_idx, ip); +#endif + } +} + +static void arp_request(struct wolfIP *s, unsigned int if_idx, ip4 tip) { struct arp_packet arp; - if (s->arp.last_arp + 1000 > s->last_tick) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + + if (!ll || !conf) + return; + + if (s->arp.last_arp[if_idx] + 1000 > s->last_tick) { return; } - s->arp.last_arp = s->last_tick; + s->arp.last_arp[if_idx] = s->last_tick; memset(&arp, 0, sizeof(struct arp_packet)); - memcpy(arp.eth.dst, "\xff\xff\xff\xff\xff\xff", 6); - memcpy(arp.eth.src, s->ll_dev.mac, 6); - arp.eth.type = ee16(0x0806); + eth_output_add_header(s, if_idx, NULL, &arp.eth, ETH_TYPE_ARP); arp.htype = ee16(1); /* Ethernet */ arp.ptype = ee16(0x0800); arp.hlen = 6; arp.plen = 4; arp.opcode = ee16(ARP_REQUEST); - memcpy(arp.sma, s->ll_dev.mac, 6); - arp.sip = ee32(s->ipconf.ip); + memcpy(arp.sma, ll->mac, 6); + arp.sip = ee32(conf->ip); memset(arp.tma, 0, 6); arp.tip = ee32(tip); - eth_output_add_header(s, NULL, &arp.eth, ETH_TYPE_ARP); - s->ll_dev.send(&s->ll_dev, &arp, sizeof(struct arp_packet)); + if (ll->send) + ll->send(ll, &arp, sizeof(struct arp_packet)); } -static void arp_recv(struct wolfIP *s, void *buf, int len) +static void arp_recv(struct wolfIP *s, unsigned int if_idx, void *buf, int len) { struct arp_packet *arp = (struct arp_packet *)buf; - if (arp->opcode == ee16(ARP_REQUEST) && arp->tip == ee32(s->ipconf.ip)) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + + if (!ll || !conf) + return; + + if (arp->opcode == ee16(ARP_REQUEST) && arp->tip == ee32(conf->ip)) { arp->opcode = ee16(ARP_REPLY); memcpy(arp->tma, arp->sma, 6); - memcpy(arp->sma, s->ll_dev.mac, 6); + memcpy(arp->sma, ll->mac, 6); arp->tip = arp->sip; - arp->sip = ee32(s->ipconf.ip); - /* Add neighbor to ARP table */ - for (int i = 0; i < MAX_NEIGHBORS; i++) { - if ((s->arp.neighbors[i].ip == IPADDR_ANY) || (s->arp.neighbors[i].ip == ee32(arp->sip))) { - memcpy(s->arp.neighbors[i].mac, arp->sma, 6); - s->arp.neighbors[i].ip = ee32(arp->sip); - break; - } - } - eth_output_add_header(s, arp->tma, &arp->eth, ETH_TYPE_ARP); - s->ll_dev.send(&s->ll_dev, buf, len); + arp->sip = ee32(conf->ip); + arp_store_neighbor(s, if_idx, ee32(arp->sip), arp->sma); + eth_output_add_header(s, if_idx, arp->tma, &arp->eth, ETH_TYPE_ARP); + if (ll->send) + ll->send(ll, buf, len); } if (arp->opcode == ee16(ARP_REPLY)) { - int i; - for (i = 0; i < MAX_NEIGHBORS; i++) { - if (s->arp.neighbors[i].ip == ee32(arp->sip)) { - memcpy(s->arp.neighbors[i].mac, arp->sma, 6); - break; - } - } - for (i = 0; i < MAX_NEIGHBORS; i++) { - if ((s->arp.neighbors[i].ip == IPADDR_ANY) || (s->arp.neighbors[i].ip == ee32(arp->sip))) { - memcpy(s->arp.neighbors[i].mac, arp->sma, 6); - s->arp.neighbors[i].ip = ee32(arp->sip); - break; - } - } + arp_store_neighbor(s, if_idx, ee32(arp->sip), arp->sma); } } -static int arp_lookup(struct wolfIP *s, ip4 ip, uint8_t *mac) +static int arp_lookup(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac) { + int i; memset(mac, 0, 6); - for (int i = 0; i < MAX_NEIGHBORS; i++) { - if (s->arp.neighbors[i].ip == ip) { + for (i = 0; i < MAX_NEIGHBORS; i++) { + if (s->arp.neighbors[i].ip == ip && s->arp.neighbors[i].if_idx == if_idx) { memcpy(mac, s->arp.neighbors[i].mac, 6); return 0; } @@ -1956,12 +2533,44 @@ static int arp_lookup(struct wolfIP *s, ip4 ip, uint8_t *mac) /* Initialize the IP stack */ void wolfIP_init(struct wolfIP *s) { + unsigned int i; + if (!s) + return; memset(s, 0, sizeof(struct wolfIP)); + s->if_count = WOLFIP_MAX_INTERFACES; + for (i = 0; i < s->if_count; i++) { + s->ipconf[i].ll = wolfIP_ll_at(s, i); + } +#if WOLFIP_ENABLE_LOOPBACK + if (s->if_count > WOLFIP_LOOPBACK_IF_IDX) { + struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, WOLFIP_LOOPBACK_IF_IDX); + struct ipconf *loop_conf = wolfIP_ipconf_at(s, WOLFIP_LOOPBACK_IF_IDX); + static const uint8_t loop_mac[6] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 }; + if (loop) { + memcpy(loop->mac, loop_mac, sizeof(loop_mac)); + strncpy(loop->ifname, "lo", sizeof(loop->ifname) - 1); + loop->ifname[sizeof(loop->ifname) - 1] = '\0'; + loop->poll = NULL; + loop->send = wolfIP_loopback_send; + } + if (loop_conf) { + loop_conf->ll = loop; + loop_conf->ip = WOLFIP_LOOPBACK_IP; + loop_conf->mask = WOLFIP_LOOPBACK_MASK; + loop_conf->gw = IPADDR_ANY; + } + } +#endif } -struct ll *wolfIP_getdev(struct wolfIP *s) +struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s) { - return &s->ll_dev; + return wolfIP_getdev_ex(s, WOLFIP_PRIMARY_IF_IDX); +} + +struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx) +{ + return wolfIP_ll_at(s, if_idx); } static struct wolfIP wolfIP_static; @@ -1973,45 +2582,122 @@ void wolfIP_init_static(struct wolfIP **s) *s = &wolfIP_static; } -static inline void ip_recv(struct wolfIP *s, struct wolfIP_ip_packet *ip, +size_t wolfIP_instance_size(void) +{ + return sizeof(struct wolfIP); +} + +static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *ip, uint32_t len) { +#if WOLFIP_ENABLE_FORWARDING + unsigned int i; +#endif +#if WOLFIP_ENABLE_LOOPBACK + if (!wolfIP_is_loopback_if(if_idx)) { + ip4 dest = ee32(ip->dst); + if ((dest & WOLFIP_LOOPBACK_MASK) == (WOLFIP_LOOPBACK_IP & WOLFIP_LOOPBACK_MASK)) { + return; + } + } +#endif +#if WOLFIP_ENABLE_FORWARDING + if (ip->ver_ihl == 0x45) { + ip4 dest = ee32(ip->dst); + int is_local = 0; + if (dest == IPADDR_ANY || IS_IP_BCAST(dest)) { + is_local = 1; + } else { + for (i = 0; i < s->if_count; i++) { + struct ipconf *conf = &s->ipconf[i]; + if (!conf || conf->ip == IPADDR_ANY) + continue; + if (conf->ip == dest) { + is_local = 1; + break; + } + } + } + if (!is_local) { + int out_if = wolfIP_forward_interface(s, if_idx, dest); + if (out_if >= 0) { + uint8_t mac[6]; + int broadcast = 0; + + if (ip->ttl <= 1) { + wolfIP_send_ttl_exceeded(s, if_idx, ip); + return; + } + if (!wolfIP_forward_prepare(s, out_if, dest, mac, &broadcast)) { + arp_queue_packet(s, out_if, dest, ip, len); + return; + } + ip->ttl--; + ip->csum = 0; + iphdr_set_checksum(ip); + wolfIP_forward_packet(s, out_if, ip, len, broadcast ? NULL : mac, broadcast); + return; + } + } + } +#endif if (ip->ver_ihl == 0x45 && ip->proto == 0x06) { struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)ip; - tcp_input(s, tcp, len); + tcp_input(s, if_idx, tcp, len); } else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) { struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip; - udp_try_recv(s, udp, len); + udp_try_recv(s, if_idx, udp, len); } else if (ip->ver_ihl == 0x45 && ip->proto == 0x01) { - icmp_input(s, ip, len); + icmp_input(s, if_idx, 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 wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len) +static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len) { #ifdef ETHERNET - struct wolfIP_eth_frame *eth = (struct wolfIP_eth_frame *)buf; - if (eth->type == ee16(0x0800)) { + struct wolfIP_ll_dev *ll; + struct wolfIP_eth_frame *eth; +#else + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; +#endif + if (!s) + return; + +#ifdef ETHERNET + ll = wolfIP_ll_at(s, if_idx); + if (!ll) + return; + eth = (struct wolfIP_eth_frame *)buf; + if (eth->type == ee16(ETH_TYPE_IP)) { struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)eth; - if ((memcmp(eth->dst, s->ll_dev.mac, 6) != 0) && (memcmp(eth->dst, "\xff\xff\xff\xff\xff\xff", 6) != 0)) { + if ((memcmp(eth->dst, ll->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); + ip_recv(s, if_idx, ip, len); + } else if (eth->type == ee16(ETH_TYPE_ARP)) { + arp_recv(s, if_idx, buf, len); } #else /* No ethernet, assume IP */ - struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; - ip_recv(s, ip, len); + ip = (struct wolfIP_ip_packet *)buf; + ip_recv(s, if_idx, ip, len); #endif - /* No default action required: the buffer is in stack and will be discarded on return */ +} + +/* 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 wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len) +{ + wolfIP_recv_on(s, WOLFIP_PRIMARY_IF_IDX, buf, len); +} + +void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len) +{ + wolfIP_recv_on(s, if_idx, buf, len); } /* DNS Client */ @@ -2090,7 +2776,7 @@ int nslookup(struct wolfIP *s, const char *dname, uint16_t *id, void (*lookup_cb 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 = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, FT_IPPROTO_UDP); + s->dns_udp_sd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); if (s->dns_udp_sd < 0) return -1; wolfIP_register_callback(s, s->dns_udp_sd, dns_callback, s); @@ -2149,18 +2835,22 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) int len = 0; int i = 0; uint8_t buf[LINK_MTU]; + unsigned int if_idx; struct wolfIP_timer tmr; memset(buf, 0, LINK_MTU); s->last_tick = now; /* Step 1: Poll the device */ - if (s->ll_dev.poll) { + for (if_idx = 0; if_idx < s->if_count; if_idx++) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + if (!ll || !ll->poll) + continue; do { - len = s->ll_dev.poll(&s->ll_dev, buf, LINK_MTU); + len = ll->poll(ll, buf, LINK_MTU); if (len > 0) { /* Process packet */ - wolfIP_recv(s, buf, len); + wolfIP_recv_on(s, if_idx, buf, len); } } while (len > 0); } @@ -2201,12 +2891,18 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) continue; } else { #ifdef ETHERNET - ip4 nexthop = NEXTHOP(s, ts->remote_ip); - if (arp_lookup(s, nexthop, ts->nexthop_mac) < 0) { + unsigned int if_idx = wolfIP_socket_if_idx(ts); + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + ip4 nexthop = wolfIP_select_nexthop(conf, ts->remote_ip); + if (wolfIP_is_loopback_if(if_idx)) { + struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, if_idx); + if (loop) + memcpy(ts->nexthop_mac, loop->mac, 6); + } else if (arp_lookup(s, if_idx, nexthop, ts->nexthop_mac) < 0) { /* Send ARP request */ - arp_request(s, nexthop); + arp_request(s, if_idx, nexthop); break; - }else + } #endif if (in_flight <= ts->sock.tcp.cwnd) { struct wolfIP_timer new_tmr = {}; @@ -2224,8 +2920,12 @@ int wolfIP_poll(struct wolfIP *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 wolfIP_ip_packet *)tcp, FT_IPPROTO_TCP, size); - s->ll_dev.send(&s->ll_dev, tcp, desc->len); + ip_output_add_header(ts, (struct wolfIP_ip_packet *)tcp, WI_IPPROTO_TCP, size); + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, wolfIP_socket_if_idx(ts)); + if (ll && ll->send) + ll->send(ll, tcp, desc->len); + } desc->flags |= PKT_FLAG_SENT; desc->time_sent = now; if (size == IP_HEADER_LEN + (uint32_t)(tcp->hlen >> 2)) { @@ -2255,17 +2955,29 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) while (desc) { struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)(t->txmem + desc->pos + sizeof(*desc)); #ifdef ETHERNET - ip4 nexthop = NEXTHOP(s, t->remote_ip); - if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, nexthop, t->nexthop_mac) < 0))) { - /* Send ARP request */ - arp_request(s, nexthop); - break; + unsigned int if_idx = wolfIP_socket_if_idx(t); + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + ip4 nexthop = wolfIP_select_nexthop(conf, t->remote_ip); + if (wolfIP_is_loopback_if(if_idx)) { + struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, if_idx); + if (loop) + memcpy(t->nexthop_mac, loop->mac, 6); + } else { + if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, if_idx, nexthop, t->nexthop_mac) < 0))) { + /* Send ARP request */ + arp_request(s, if_idx, nexthop); + break; + } + if (IS_IP_BCAST(nexthop)) memset(t->nexthop_mac, 0xFF, 6); } - if (IS_IP_BCAST(nexthop)) memset(t->nexthop_mac, 0xFF, 6); #endif len = desc->len - ETH_HEADER_LEN; - ip_output_add_header(t, (struct wolfIP_ip_packet *)udp, FT_IPPROTO_UDP, len); - s->ll_dev.send(&s->ll_dev, udp, desc->len); + ip_output_add_header(t, (struct wolfIP_ip_packet *)udp, WI_IPPROTO_UDP, len); + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, wolfIP_socket_if_idx(t)); + if (ll && ll->send) + ll->send(ll, udp, desc->len); + } fifo_pop(&t->sock.udp.txbuf); desc = fifo_peek(&t->sock.udp.txbuf); } @@ -2275,15 +2987,33 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) void wolfIP_ipconfig_set(struct wolfIP *s, ip4 ip, ip4 mask, ip4 gw) { - s->ipconf.ip = ip; - s->ipconf.mask = mask; - s->ipconf.gw = gw; + wolfIP_ipconfig_set_ex(s, WOLFIP_PRIMARY_IF_IDX, ip, mask, gw); } void wolfIP_ipconfig_get(struct wolfIP *s, ip4 *ip, ip4 *mask, ip4 *gw) { - *ip = s->ipconf.ip; - *mask = s->ipconf.mask; - *gw = s->ipconf.gw; + wolfIP_ipconfig_get_ex(s, WOLFIP_PRIMARY_IF_IDX, ip, mask, gw); +} + +void wolfIP_ipconfig_set_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, ip4 mask, ip4 gw) +{ + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + if (!conf) + return; + conf->ip = ip; + conf->mask = mask; + conf->gw = gw; } +void wolfIP_ipconfig_get_ex(struct wolfIP *s, unsigned int if_idx, ip4 *ip, ip4 *mask, ip4 *gw) +{ + struct ipconf *conf = wolfIP_ipconf_at(s, if_idx); + if (!conf) + return; + if (ip) + *ip = conf->ip; + if (mask) + *mask = conf->mask; + if (gw) + *gw = conf->gw; +} diff --git a/wolfip.h b/wolfip.h index 8a3b0c89..3109da9c 100644 --- a/wolfip.h +++ b/wolfip.h @@ -12,6 +12,13 @@ typedef uint32_t ip4; #define ee32(x) __builtin_bswap32(x) #define DEBUG +#ifndef WOLFIP_EINVAL +#define WOLFIP_EINVAL (22) +#endif +#ifndef WOLFIP_EAGAIN +#define WOLFIP_EAGAIN (11) +#endif + #ifdef DEBUG #include @@ -21,19 +28,20 @@ typedef uint32_t ip4; #endif /* Device driver interface */ -/* Struct to contain a hw device description */ -struct ll { +/* Struct to contain link-layer (ll) device description + */ +struct wolfIP_ll_dev { uint8_t mac[6]; char ifname[16]; /* poll function */ - int (*poll)(struct ll *ll, void *buf, uint32_t len); + int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); /* send function */ - int (*send)(struct ll *ll, void *buf, uint32_t len); + int (*send)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); }; /* Struct to contain an IP device configuration */ struct ipconf { - struct ll *ll; + struct wolfIP_ll_dev *ll; ip4 ip; ip4 mask; ip4 gw; @@ -42,10 +50,23 @@ struct ipconf { /* Socket interface */ #define MARK_TCP_SOCKET 0x100 /* Mark a socket as TCP */ #define MARK_UDP_SOCKET 0x200 /* Mark a socket as UDP */ + + +/* Compile-time sanity check for socket marks & number of sockets */ #if (MARK_TCP_SOCKET >= MARK_UDP_SOCKET) #error "MARK_TCP_SOCKET must be less than MARK_UDP_SOCKET" #endif +#if MAX_TCPSOCKETS > 255 +#error "MAX_TCPSOCKETS must be less than 256" +#endif + +#if MAX_UDPSOCKETS > 255 +#error "MAX_UDPSOCKETS must be less than 256" +#endif + + + #ifndef WOLF_POSIX #define IPSTACK_SOCK_STREAM 1 @@ -96,12 +117,17 @@ int nslookup(struct wolfIP *s, const char *name, uint16_t *id, void (*lookup_cb) /* IP stack interface */ void wolfIP_init(struct wolfIP *s); void wolfIP_init_static(struct wolfIP **s); +size_t wolfIP_instance_size(void); int wolfIP_poll(struct wolfIP *s, uint64_t now); void wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len); +void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len); void wolfIP_ipconfig_set(struct wolfIP *s, ip4 ip, ip4 mask, ip4 gw); void wolfIP_ipconfig_get(struct wolfIP *s, ip4 *ip, ip4 *mask, ip4 *gw); -struct ll *wolfIP_getdev(struct wolfIP *s); +struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s); +struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx); +void wolfIP_ipconfig_set_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, ip4 mask, ip4 gw); +void wolfIP_ipconfig_get_ex(struct wolfIP *s, unsigned int if_idx, ip4 *ip, ip4 *mask, ip4 *gw); /* Callback flags */ #define CB_EVENT_READABLE 0x01 /* Accepted connection or data available */ @@ -160,9 +186,8 @@ static inline void iptoa(ip4 ip, char *buf) #endif #include #include -/* Defined in wolfssl_io.c */ -int wolfSSL_SetIO_FT(WOLFSSL* ssl, int fd); -int wolfSSL_SetIO_FT_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s); +int wolfSSL_SetIO_wolfIP(WOLFSSL* ssl, int fd); +int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s); #endif #endif