aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-09-09 03:14:50 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-09-18 01:08:14 +0200
commitc11cb04929f28853142b14339b66f561ca028f36 (patch)
treea77a3ddcc1d01028e4077cb295b9b41f594c5d52
First commitHEADmaster
-rw-r--r--CMakeLists.txt61
-rw-r--r--LICENSE373
-rw-r--r--README.md18
-rw-r--r--include/wip/private.h96
-rw-r--r--include/wip/types.h44
-rw-r--r--include/wip/wip.h28
-rw-r--r--private_include/wip/prv/icmp.h21
-rw-r--r--private_include/wip/prv/icmp/routines.h20
-rw-r--r--private_include/wip/prv/icmp/types.h34
-rw-r--r--private_include/wip/prv/io.h22
-rw-r--r--private_include/wip/prv/log.h15
-rw-r--r--private_include/wip/prv/routines.h17
-rw-r--r--private_include/wip/prv/tcp.h21
-rw-r--r--private_include/wip/prv/tcp/types.h20
-rw-r--r--private_include/wip/prv/udp.h21
-rw-r--r--private_include/wip/prv/udp/types.h20
-rw-r--r--src/CMakeLists.txt16
-rw-r--r--src/init.c20
-rw-r--r--src/io/CMakeLists.txt15
-rw-r--r--src/io/be16.c16
-rw-r--r--src/io/be32.c17
-rw-r--r--src/io/read.c32
-rw-r--r--src/io/read_c.c28
-rw-r--r--src/io/tobe16.c17
-rw-r--r--src/io/tobe32.c19
-rw-r--r--src/log/CMakeLists.txt10
-rw-r--r--src/log/log.c23
-rw-r--r--src/protocol/CMakeLists.txt10
-rw-r--r--src/protocol/icmp/CMakeLists.txt14
-rw-r--r--src/protocol/icmp/alloc.c29
-rw-r--r--src/protocol/icmp/free.c25
-rw-r--r--src/protocol/icmp/rx.c200
-rw-r--r--src/protocol/icmp/start.c205
-rw-r--r--src/protocol/icmp/tx.c21
-rw-r--r--src/protocol/tcp/CMakeLists.txt13
-rw-r--r--src/protocol/tcp/alloc.c20
-rw-r--r--src/protocol/tcp/free.c19
-rw-r--r--src/protocol/tcp/rx.c21
-rw-r--r--src/protocol/tcp/tx.c21
-rw-r--r--src/protocol/udp/CMakeLists.txt13
-rw-r--r--src/protocol/udp/alloc.c20
-rw-r--r--src/protocol/udp/free.c19
-rw-r--r--src/protocol/udp/rx.c21
-rw-r--r--src/protocol/udp/tx.c21
-rw-r--r--src/routines/CMakeLists.txt10
-rw-r--r--src/routines/rx.c447
-rw-r--r--src/run.c49
47 files changed, 2242 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..648f8b2
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,61 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+cmake_minimum_required(VERSION 3.19)
+project(wip LANGUAGES C VERSION 0.0.0)
+option(WIP_LOG "Enables logging to stderr")
+option(WIP_LOG_CUSTOM "Allows user code to define wip_log. Enables WIP_LOG")
+add_library(${PROJECT_NAME})
+target_include_directories(${PROJECT_NAME} PUBLIC include
+ PRIVATE private_include)
+
+set(compilers
+ "GNU"
+ "Clang"
+ "TinyCC"
+)
+
+if(WIP_LOG_CUSTOM)
+ set(WIP_LOG ON)
+endif()
+
+foreach(c ${compilers})
+ if(CMAKE_C_COMPILER_ID STREQUAL ${c})
+ set(cflags_np
+ -pedantic
+ -Wall
+ )
+
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(cflags_np ${cflags_np} -Og -g)
+ else()
+ set(cflags_np ${cflags_np} -Os)
+ endif()
+
+ break()
+ endif()
+endforeach()
+
+include(CheckCompilerFlag)
+
+foreach(f ${cflags_np})
+ string(REPLACE "-" "_" var supported_${f})
+ check_compiler_flag(C ${f} ${var})
+
+ if(${var})
+ set(sup_cflags ${sup_cflags} ${f})
+ endif()
+endforeach()
+
+if(WIP_LOG)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE WIP_LOG)
+endif()
+
+target_compile_options(${PROJECT_NAME} PRIVATE ${sup_cflags})
+set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 90 C_EXTENSIONS OFF)
+add_subdirectory(src)
+install(TARGETS ${PROJECT_NAME})
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d0a1fa1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e177223
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# wip
+
+**This project is still unfinished and is not meant for production use.**
+
+`wip` (prounced _weep_ or _double u-ip_) is a small TCP/IP stack.
+
+## License
+
+```
+wip, a small TCP/IP stack.
+Copyright (C) 2025 Xavier Del Campo Romero
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at https://mozilla.org/MPL/2.0/.
+```
+
+Also, see [`LICENSE`](LICENSE).
diff --git a/include/wip/private.h b/include/wip/private.h
new file mode 100644
index 0000000..6de3733
--- /dev/null
+++ b/include/wip/private.h
@@ -0,0 +1,96 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRIVATE_H
+#define WIP_PRIVATE_H
+
+#include <stddef.h>
+
+#if !defined(WIP_H) && !defined(WIP_TYPES_H)
+#error Do not #include <wip/private.h> directly. \
+ Please either #include <wip/wip.h> or <wip/types.h>
+#endif
+
+struct wip;
+
+typedef void *(*wip_prot_alloc)(struct wip *);
+typedef enum wip_state (*wip_prot_rx)(struct wip *, const void *, size_t, void *);
+typedef int (*wip_prot_tx)(struct wip *, void *, size_t, void *);
+typedef void (*wip_prot_free)(struct wip *, void *);
+
+enum wip_protocol
+{
+ WIP_P_ICMP = 1,
+ WIP_P_IGMP,
+ WIP_P_TCP = 6,
+ WIP_P_UDP = 17
+};
+
+struct wip_prot_ops
+{
+ unsigned char id;
+ wip_prot_alloc alloc;
+ wip_prot_free free;
+ wip_prot_rx rx;
+ wip_prot_tx tx;
+};
+
+struct wip_sm_io
+{
+ void *buf;
+ size_t n, read;
+};
+
+struct wip_be16
+{
+ unsigned char v[2];
+};
+
+struct wip_be32
+{
+ unsigned char v[4];
+};
+
+union wip_rx_sm
+{
+ struct wip_rx_sm_h
+ {
+ unsigned short chksum;
+ unsigned char ttl, protocol;
+ size_t header_sz, i;
+ struct wip_sm_io io;
+ struct wip_be16 len, id, fl_off, rchksum;
+ struct wip_be32 src, dst;
+ const struct wip_prot_ops *prot;
+ void *args;
+ } h;
+};
+
+struct wip_rx
+{
+ char buf[192];
+ union wip_rx_sm sm;
+ enum wip_state (*next)(struct wip *);
+};
+
+struct wip_tx
+{
+ char buf[192];
+ enum wip_state (*next)(struct wip *);
+};
+
+struct wip
+{
+ const char *exception;
+ struct wip_rx rx;
+ struct wip_tx tx;
+ struct wip_cfg cfg;
+};
+
+#endif
diff --git a/include/wip/types.h b/include/wip/types.h
new file mode 100644
index 0000000..b97f7b9
--- /dev/null
+++ b/include/wip/types.h
@@ -0,0 +1,44 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_TYPES_H
+#define WIP_TYPES_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum wip_state
+{
+ WIP_OK,
+ WIP_AGAIN,
+ WIP_INVALID,
+ WIP_FATAL
+};
+
+struct wip_cfg
+{
+ void *(*alloc)(size_t n, void *user);
+ void *(*realloc)(void *p, size_t n, void *user);
+ void (*free)(void *p, void *user);
+ int (*read)(void *buf, size_t n, void *user);
+ int (*write)(const void *buf, size_t n, void *user);
+ void *user;
+};
+
+#include <wip/private.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/wip/wip.h b/include/wip/wip.h
new file mode 100644
index 0000000..22c9f8c
--- /dev/null
+++ b/include/wip/wip.h
@@ -0,0 +1,28 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_H
+#define WIP_H
+
+#include <wip/types.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void wip_init(struct wip *w, const struct wip_cfg *cfg);
+int wip_run(struct wip *w);
+const char *wip_exc(const struct wip *w);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private_include/wip/prv/icmp.h b/private_include/wip/prv/icmp.h
new file mode 100644
index 0000000..bc7dbe1
--- /dev/null
+++ b/private_include/wip/prv/icmp.h
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_ICMP_H
+#define WIP_PRV_ICMP_H
+
+#include <wip/wip.h>
+#include <stddef.h>
+
+void *wip_icmp_alloc(struct wip *const w);
+enum wip_state wip_icmp_rx(struct wip *w, const void *buf, size_t n, void *args);
+int wip_icmp_tx(struct wip *w, void *buf, size_t n, void *args);
+void wip_icmp_free(struct wip *w, void *args);
+
+#endif
diff --git a/private_include/wip/prv/icmp/routines.h b/private_include/wip/prv/icmp/routines.h
new file mode 100644
index 0000000..508efce
--- /dev/null
+++ b/private_include/wip/prv/icmp/routines.h
@@ -0,0 +1,20 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_ICMP_ROUTINES_H
+#define WIP_PRV_ICMP_ROUTINES_H
+
+#include <wip/wip.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+
+enum wip_state wip_icmp_rx_start(struct wip *w, const void *buf, size_t n,
+ struct wip_icmp *icmp, size_t *r);
+
+#endif
diff --git a/private_include/wip/prv/icmp/types.h b/private_include/wip/prv/icmp/types.h
new file mode 100644
index 0000000..c9355dd
--- /dev/null
+++ b/private_include/wip/prv/icmp/types.h
@@ -0,0 +1,34 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_ICMP_TYPES_H
+#define WIP_PRV_ICMP_TYPES_H
+
+#include <wip/wip.h>
+#include <stddef.h>
+
+enum icmp_type
+{
+ ICMP_ECHO_REPLY,
+ ICMP_ECHO_REQUEST = 8
+};
+
+struct wip_icmp
+{
+ enum icmp_type type;
+ struct wip_sm_io io;
+ struct wip_be16 exp_checksum, id, seqnum;
+ unsigned short checksum;
+ void *buf;
+ size_t read;
+ enum wip_state (*next)(struct wip *, const void *, size_t,
+ struct wip_icmp *, size_t *);
+};
+
+#endif
diff --git a/private_include/wip/prv/io.h b/private_include/wip/prv/io.h
new file mode 100644
index 0000000..cece6b3
--- /dev/null
+++ b/private_include/wip/prv/io.h
@@ -0,0 +1,22 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_IO_H
+#define WIP_IO_H
+
+#include <wip/wip.h>
+
+enum wip_state wip_io_read(struct wip *w, struct wip_sm_io *io);
+enum wip_state wip_io_read_c(struct wip_cfg *cfg, struct wip_sm_io *io);
+unsigned short wip_be16(const struct wip_be16 *v);
+unsigned long wip_be32(const struct wip_be32 *v);
+void wip_tobe16(unsigned long v, struct wip_be16 *out);
+void wip_tobe32(unsigned long v, struct wip_be32 *out);
+
+#endif
diff --git a/private_include/wip/prv/log.h b/private_include/wip/prv/log.h
new file mode 100644
index 0000000..438b569
--- /dev/null
+++ b/private_include/wip/prv/log.h
@@ -0,0 +1,15 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_LOG_H
+#define WIP_LOG_H
+
+int wip_log(const char *fmt, ...);
+
+#endif
diff --git a/private_include/wip/prv/routines.h b/private_include/wip/prv/routines.h
new file mode 100644
index 0000000..e8e919b
--- /dev/null
+++ b/private_include/wip/prv/routines.h
@@ -0,0 +1,17 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_ROUTINES_H
+#define WIP_PRV_ROUTINES_H
+
+#include <wip/wip.h>
+
+void wip_rx(struct wip *w);
+
+#endif
diff --git a/private_include/wip/prv/tcp.h b/private_include/wip/prv/tcp.h
new file mode 100644
index 0000000..8919ba2
--- /dev/null
+++ b/private_include/wip/prv/tcp.h
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_TCP_H
+#define WIP_PRV_TCP_H
+
+#include <wip/wip.h>
+#include <stddef.h>
+
+void *wip_tcp_alloc(struct wip *const w);
+enum wip_state wip_tcp_rx(struct wip *w, const void *buf, size_t n, void *args);
+int wip_tcp_tx(struct wip *w, void *buf, size_t n, void *args);
+void wip_tcp_free(struct wip *w, void *args);
+
+#endif
diff --git a/private_include/wip/prv/tcp/types.h b/private_include/wip/prv/tcp/types.h
new file mode 100644
index 0000000..66dfdb4
--- /dev/null
+++ b/private_include/wip/prv/tcp/types.h
@@ -0,0 +1,20 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_TCP_TYPES_H
+#define WIP_PRV_TCP_TYPES_H
+
+#include <stddef.h>
+
+struct wip_tcp
+{
+ int dummy;
+};
+
+#endif
diff --git a/private_include/wip/prv/udp.h b/private_include/wip/prv/udp.h
new file mode 100644
index 0000000..48422fe
--- /dev/null
+++ b/private_include/wip/prv/udp.h
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_UDP_H
+#define WIP_PRV_UDP_H
+
+#include <wip/wip.h>
+#include <stddef.h>
+
+void *wip_udp_alloc(struct wip *const w);
+enum wip_state wip_udp_rx(struct wip *w, const void *buf, size_t n, void *args);
+int wip_udp_tx(struct wip *w, void *buf, size_t n, void *args);
+void wip_udp_free(struct wip *w, void *args);
+
+#endif
diff --git a/private_include/wip/prv/udp/types.h b/private_include/wip/prv/udp/types.h
new file mode 100644
index 0000000..c807430
--- /dev/null
+++ b/private_include/wip/prv/udp/types.h
@@ -0,0 +1,20 @@
+/*
+ * wip, a small UDP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef WIP_PRV_UDP_TYPES_H
+#define WIP_PRV_UDP_TYPES_H
+
+#include <stddef.h>
+
+struct wip_udp
+{
+ int dummy;
+};
+
+#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..8ca9376
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,16 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ init.c
+ run.c
+)
+
+add_subdirectory(io)
+add_subdirectory(log)
+add_subdirectory(protocol)
+add_subdirectory(routines)
diff --git a/src/init.c b/src/init.c
new file mode 100644
index 0000000..570f78b
--- /dev/null
+++ b/src/init.c
@@ -0,0 +1,20 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/routines.h>
+
+void wip_init(struct wip *const w, const struct wip_cfg *const cfg)
+{
+ const struct wip ww = {0};
+
+ *w = ww;
+ w->cfg = *cfg;
+ wip_rx(w);
+}
diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt
new file mode 100644
index 0000000..b989afd
--- /dev/null
+++ b/src/io/CMakeLists.txt
@@ -0,0 +1,15 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ be16.c
+ be32.c
+ read.c
+ read_c.c
+ tobe16.c
+ tobe32.c
+)
diff --git a/src/io/be16.c b/src/io/be16.c
new file mode 100644
index 0000000..d8b995a
--- /dev/null
+++ b/src/io/be16.c
@@ -0,0 +1,16 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+unsigned short wip_be16(const struct wip_be16 *const v)
+{
+ return ((unsigned long)v->v[0] << 8) | v->v[1];
+}
diff --git a/src/io/be32.c b/src/io/be32.c
new file mode 100644
index 0000000..9defd06
--- /dev/null
+++ b/src/io/be32.c
@@ -0,0 +1,17 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+unsigned long wip_be32(const struct wip_be32 *const v)
+{
+ return ((unsigned long)v->v[0] << 24) | ((unsigned long)v->v[1] << 16)
+ | (v->v[2] << 8) | v->v[3];
+}
diff --git a/src/io/read.c b/src/io/read.c
new file mode 100644
index 0000000..f8ee1b2
--- /dev/null
+++ b/src/io/read.c
@@ -0,0 +1,32 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+enum wip_state wip_io_read(struct wip *const w, struct wip_sm_io *const io)
+{
+ void *const buf = (unsigned char *)io->buf + io->read;
+ const struct wip_cfg *const cfg = &w->cfg;
+ const size_t rem = io->n - io->read;
+ const int n = cfg->read(buf, rem, cfg->user);
+
+ if (n < 0)
+ {
+ w->exception = "read error";
+ return WIP_FATAL;
+ }
+ else if ((io->read += n) >= io->n)
+ {
+ io->read = 0;
+ return WIP_OK;
+ }
+
+ return WIP_AGAIN;
+}
diff --git a/src/io/read_c.c b/src/io/read_c.c
new file mode 100644
index 0000000..4433a9f
--- /dev/null
+++ b/src/io/read_c.c
@@ -0,0 +1,28 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+enum wip_state wip_io_read_c(struct wip_cfg *const cfg, struct wip_sm_io *const io)
+{
+ void *const buf = (unsigned char *)io->buf + io->read;
+ const size_t rem = io->n - io->read;
+ const int n = cfg->read(buf, rem, cfg->user);
+
+ if (n < 0)
+ return WIP_FATAL;
+ else if ((io->read += n) >= io->n)
+ {
+ io->read = 0;
+ return WIP_OK;
+ }
+
+ return WIP_AGAIN;
+}
diff --git a/src/io/tobe16.c b/src/io/tobe16.c
new file mode 100644
index 0000000..3ac7992
--- /dev/null
+++ b/src/io/tobe16.c
@@ -0,0 +1,17 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+void wip_tobe16(const unsigned long v, struct wip_be16 *const r)
+{
+ r->v[0] = v >> 8;
+ r->v[1] = v;
+}
diff --git a/src/io/tobe32.c b/src/io/tobe32.c
new file mode 100644
index 0000000..251a0f9
--- /dev/null
+++ b/src/io/tobe32.c
@@ -0,0 +1,19 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+
+void wip_tobe32(const unsigned long v, struct wip_be32 *const r)
+{
+ r->v[0] = v >> 24;
+ r->v[1] = v >> 16;
+ r->v[2] = v >> 8;
+ r->v[3] = v;
+}
diff --git a/src/log/CMakeLists.txt b/src/log/CMakeLists.txt
new file mode 100644
index 0000000..e4a0289
--- /dev/null
+++ b/src/log/CMakeLists.txt
@@ -0,0 +1,10 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+if(WIP_LOG AND NOT WIP_LOG_CUSTOM)
+ target_sources(${PROJECT_NAME} PRIVATE log.c)
+endif()
diff --git a/src/log/log.c b/src/log/log.c
new file mode 100644
index 0000000..b9e20af
--- /dev/null
+++ b/src/log/log.c
@@ -0,0 +1,23 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/prv/log.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+int nwp_log(const char *const fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt
new file mode 100644
index 0000000..194274d
--- /dev/null
+++ b/src/protocol/CMakeLists.txt
@@ -0,0 +1,10 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+add_subdirectory(icmp)
+add_subdirectory(tcp)
+add_subdirectory(udp)
diff --git a/src/protocol/icmp/CMakeLists.txt b/src/protocol/icmp/CMakeLists.txt
new file mode 100644
index 0000000..4fd374d
--- /dev/null
+++ b/src/protocol/icmp/CMakeLists.txt
@@ -0,0 +1,14 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ alloc.c
+ free.c
+ rx.c
+ start.c
+ tx.c
+)
diff --git a/src/protocol/icmp/alloc.c b/src/protocol/icmp/alloc.c
new file mode 100644
index 0000000..23d7329
--- /dev/null
+++ b/src/protocol/icmp/alloc.c
@@ -0,0 +1,29 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/icmp/routines.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+
+void *wip_icmp_alloc(struct wip *const w)
+{
+ const struct wip_cfg *const cfg = &w->cfg;
+ const struct wip_icmp z = {0};
+ struct wip_icmp *const ret = cfg->alloc(sizeof *ret, cfg->user);
+
+ if (!ret)
+ return ret;
+
+ *ret = z;
+ ret->next = wip_icmp_rx_start;
+ return ret;
+}
diff --git a/src/protocol/icmp/free.c b/src/protocol/icmp/free.c
new file mode 100644
index 0000000..452e1d4
--- /dev/null
+++ b/src/protocol/icmp/free.c
@@ -0,0 +1,25 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+
+void wip_icmp_free(struct wip *const w, void *const args)
+{
+ const struct wip_cfg *const cfg = &w->cfg;
+ struct wip_icmp *const icmp = args;
+
+ if (icmp)
+ cfg->free(icmp->buf, cfg->user);
+
+ cfg->free(icmp, cfg->user);
+}
diff --git a/src/protocol/icmp/rx.c b/src/protocol/icmp/rx.c
new file mode 100644
index 0000000..8e543f9
--- /dev/null
+++ b/src/protocol/icmp/rx.c
@@ -0,0 +1,200 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+#include <string.h>
+
+enum
+{
+ TYPE,
+ CHECKSUM = 2,
+ ID = 4,
+ SEQ_NUM = 6,
+ DATA = 8
+};
+
+#if 0
+
+static int get_type(struct wip_icmp *const icmp,
+ const unsigned char *const p, const size_t n)
+{
+ unsigned char type;
+
+ if (icmp->read)
+ return 0;
+
+ type = p[0];
+
+ if (type != ECHO_REQUEST)
+ return -1;
+
+ wip_log("%s: got echo request\n");
+ icmp->type = type;
+ return 0;
+}
+
+static void get_checksum(struct wip_icmp *const icmp,
+ const unsigned char *const p, const size_t n)
+{
+ size_t off;
+ struct wip_be16 v;
+ enum {END = CHECKSUM + sizeof v};
+
+ if (icmp->read >= END || n < END - icmp->read)
+ return;
+
+ off = CHECKSUM - icmp->read;
+ v.v[0] = p[off];
+ v.v[1] = p[off + 1];
+ icmp->exp_checksum = wip_be16(&v);
+}
+
+static void get_id(struct wip_icmp *const icmp, const unsigned char *const p,
+ const size_t n)
+{
+ size_t off;
+ struct wip_be16 v;
+ enum {END = ID + sizeof v};
+
+ if (icmp->read >= END || n < END - icmp->read)
+ return;
+
+ off = ID - icmp->read;
+ v.v[0] = p[off];
+ v.v[1] = p[off + 1];
+ icmp->id = wip_be16(&v);
+}
+
+static void get_seqnum(struct wip_icmp *const icmp, const unsigned char *const p,
+ const size_t n)
+{
+ size_t off;
+ struct wip_be16 v;
+ enum {END = SEQ_NUM + sizeof v};
+
+ if (icmp->read >= END || n < END - icmp->read)
+ return;
+
+ off = SEQ_NUM - icmp->read;
+ v.v[0] = p[off];
+ v.v[1] = p[off + 1];
+ icmp->seqnum = wip_be16(&v);
+ wip_log("chksum=%#x, id=%#x, seq_num=%#x\n", icmp->exp_checksum,
+ icmp->id, icmp->seqnum);
+}
+
+static enum wip_state get_data(struct wip *const w,
+ struct wip_icmp *const icmp, const unsigned char *const p, const size_t n)
+{
+ const struct wip_cfg *const cfg = &w->cfg;
+ const size_t offset = icmp->read + n;
+ const void *src = p;
+ size_t sz = n;
+
+ if (offset < DATA)
+ return WIP_OK;
+ else if (!icmp->buf)
+ {
+ sz = n - DATA - icmp->read;
+ src = p + DATA - icmp->read;
+
+ if (!(icmp->buf = cfg->alloc(sz, cfg->user)))
+ return WIP_FATAL;
+ }
+ else
+ {
+ const size_t nsz = icmp->n + n;
+ enum {MAXSZ = 40};
+ void *buf;
+
+ if (nsz > MAXSZ)
+ return WIP_INVALID;
+ else if (!(buf = cfg->realloc(icmp->buf, nsz, cfg->user)))
+ return WIP_FATAL;
+
+ icmp->buf = buf;
+ }
+
+ memcpy((char *)icmp->buf + icmp->read, src, sz);
+ icmp->n += sz;
+
+ wip_log("data (%lu bytes): {", (unsigned long)icmp->n);
+
+ {
+ size_t i;
+
+ for (i = 0; i < icmp->n; i++)
+ {
+ wip_log("%#hhx", *((const char *)icmp->buf + i));
+
+ if (i + 1 < icmp->n)
+ wip_log(", ");
+ }
+ }
+
+ wip_log("}\n");
+ return WIP_OK;
+}
+#endif
+
+enum wip_state wip_icmp_rx(struct wip *const w, const void *buf,
+ size_t n, void *const args)
+{
+ struct wip_icmp *const icmp = args;
+ enum wip_state ret;
+ size_t r;
+
+ while ((ret = icmp->next(w, buf, n, icmp, &r)) == WIP_AGAIN)
+ {
+ buf = (const char *)buf + r;
+ n -= r;
+ }
+
+ return ret;
+
+#if 0
+ enum wip_state (*next)(struct wip *, const void *, size_t,
+ struct wip_icmp *)
+
+ const struct wip_rx_sm_h *const h = &w->rx.sm.h;
+ const unsigned char *const p = buf;
+ const unsigned short dlen = wip_be16(&h->len) - h->header_sz;
+
+ enum wip_state ret;
+ size_t i;
+
+ if (get_type(icmp, buf, n))
+ return 0;
+
+ get_checksum(icmp, buf, n);
+ get_id(icmp, buf, n);
+ get_seqnum(icmp, buf, n);
+
+ if ((ret = get_data(w, icmp, buf, n)))
+ return ret;
+
+ for (i = 0; i < n; i++)
+ /* Avoid suming checksum */
+ if (icmp->read >= CHECKSUM || ((i != 2) && (i != 3)))
+ icmp->checksum += p[i];
+
+ if (icmp->read += n >= dlen)
+ {
+ wip_log("expected checksum=%#x, got=%#x\n",
+ icmp->exp_checksum, icmp->checksum);
+ }
+
+ return WIP_OK;
+#endif
+}
diff --git a/src/protocol/icmp/start.c b/src/protocol/icmp/start.c
new file mode 100644
index 0000000..f50052e
--- /dev/null
+++ b/src/protocol/icmp/start.c
@@ -0,0 +1,205 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/icmp/routines.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+#include <string.h>
+
+struct read
+{
+ const void *buf;
+ size_t n;
+ struct wip_icmp *icmp;
+};
+
+static int io_read(void *const buf, const size_t n, void *const user)
+{
+ const struct read *const r = user;
+ const size_t ret = n > r->n ? r->n : n;
+
+ memcpy(buf, r->buf, ret);
+ return ret;
+}
+
+static enum wip_state readbuf(struct wip_sm_io *const io, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp)
+{
+ struct wip_cfg cfg = {0};
+ struct read r;
+
+ r.buf = buf;
+ r.n = n;
+ r.icmp = icmp;
+
+ cfg.read = io_read;
+ cfg.user = &r;
+ return wip_io_read_c(&cfg, io);
+}
+
+enum wip_state get_payload(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ const struct wip_cfg *const cfg = &w->cfg;
+ const size_t nsz = icmp->read + n;
+ char *const nbuf = cfg->realloc(icmp->buf, nsz, cfg->user);
+ size_t i;
+
+ if (!nbuf)
+ return WIP_FATAL;
+
+ memcpy(nbuf + icmp->read, buf, n);
+ icmp->buf = nbuf;
+ icmp->read += n;
+ *r = n;
+
+ for (i = icmp->read; i < nsz; i++)
+ icmp->checksum += *(const char *)icmp->buf;
+
+ wip_log("ICMP data={");
+
+ for (i = 0; i < icmp->read; i++)
+ {
+ wip_log("%#hhx", *((const char *)icmp->buf + i));
+
+ if (i + 1 < icmp->read)
+ wip_log(", ");
+ }
+
+ wip_log("}\n");
+
+ wip_log("calculated checksum: %#hx, expected checksum: %#hx\n",
+ icmp->checksum, wip_be16(&icmp->exp_checksum));
+ return WIP_OK;
+}
+
+enum wip_state get_seqnum(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ const enum wip_state state = readbuf(&icmp->io, buf, n, icmp);
+
+ if (state)
+ return state;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &icmp->seqnum;
+ io.n = sizeof icmp->seqnum;
+ icmp->io = io;
+ }
+
+ wip_log("got seqnum: %#x\n", wip_be16(&icmp->seqnum));
+ icmp->checksum += wip_be16(&icmp->seqnum);
+ icmp->next = get_payload;
+ *r = sizeof icmp->seqnum;
+ return WIP_AGAIN;
+}
+
+enum wip_state get_id(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ const enum wip_state state = readbuf(&icmp->io, buf, n, icmp);
+
+ if (state)
+ return state;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &icmp->seqnum;
+ io.n = sizeof icmp->seqnum;
+ icmp->io = io;
+ }
+
+ wip_log("got id: %#x\n", wip_be16(&icmp->id));
+ icmp->checksum += wip_be16(&icmp->id);
+ icmp->next = get_seqnum;
+ *r = sizeof icmp->id;
+ return WIP_AGAIN;
+}
+
+enum wip_state get_checksum(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ const enum wip_state state = readbuf(&icmp->io, buf, n, icmp);
+
+ if (state)
+ return state;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &icmp->id;
+ io.n = sizeof icmp->id;
+ icmp->io = io;
+ }
+
+ wip_log("got expected checksum: %#x\n", wip_be16(&icmp->exp_checksum));
+ icmp->next = get_id;
+ *r = sizeof icmp->exp_checksum;
+ return WIP_AGAIN;
+}
+
+enum wip_state get_code(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ unsigned char code;
+ struct wip_sm_io io = {0};
+ enum wip_state state;
+
+ io.buf = &code;
+ io.n = sizeof code;
+
+ if ((state = readbuf(&io, buf, n, icmp)))
+ return state;
+ else if (code)
+ return WIP_INVALID;
+ else
+ {
+ struct wip_sm_io nio = {0};
+
+ nio.buf = &icmp->exp_checksum;
+ nio.n = sizeof icmp->exp_checksum;
+ icmp->io = nio;
+ }
+
+ wip_log("got code\n");
+ icmp->checksum += code;
+ icmp->next = get_checksum;
+ *r = sizeof code;
+ return WIP_AGAIN;
+}
+
+enum wip_state wip_icmp_rx_start(struct wip *const w, const void *const buf,
+ const size_t n, struct wip_icmp *const icmp, size_t *const r)
+{
+ unsigned char type;
+ struct wip_sm_io io = {0};
+ enum wip_state state;
+
+ io.buf = &type;
+ io.n = sizeof type;
+
+ if ((state = readbuf(&io, buf, n, icmp)))
+ return state;
+ else if (type != ICMP_ECHO_REQUEST)
+ return WIP_INVALID;
+
+ wip_log("got echo request\n");
+ icmp->checksum += type;
+ icmp->type = type;
+ icmp->next = get_code;
+ *r = sizeof type;
+ return WIP_AGAIN;
+}
diff --git a/src/protocol/icmp/tx.c b/src/protocol/icmp/tx.c
new file mode 100644
index 0000000..27fe876
--- /dev/null
+++ b/src/protocol/icmp/tx.c
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/icmp/types.h>
+#include <stddef.h>
+
+int wip_icmp_tx(struct wip *const wip, void *const buf, const size_t n,
+ void *const args)
+{
+ wip_log("%s: TODO\n");
+ return 0;
+}
diff --git a/src/protocol/tcp/CMakeLists.txt b/src/protocol/tcp/CMakeLists.txt
new file mode 100644
index 0000000..0caec18
--- /dev/null
+++ b/src/protocol/tcp/CMakeLists.txt
@@ -0,0 +1,13 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ alloc.c
+ free.c
+ rx.c
+ tx.c
+)
diff --git a/src/protocol/tcp/alloc.c b/src/protocol/tcp/alloc.c
new file mode 100644
index 0000000..c90f1c9
--- /dev/null
+++ b/src/protocol/tcp/alloc.c
@@ -0,0 +1,20 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/tcp.h>
+#include <wip/prv/tcp/types.h>
+#include <stddef.h>
+
+void *wip_tcp_alloc(struct wip *const w)
+{
+ wip_log("%s: TODO\n", __func__);
+ return NULL;
+}
diff --git a/src/protocol/tcp/free.c b/src/protocol/tcp/free.c
new file mode 100644
index 0000000..ddde038
--- /dev/null
+++ b/src/protocol/tcp/free.c
@@ -0,0 +1,19 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/tcp.h>
+#include <wip/prv/tcp/types.h>
+#include <stddef.h>
+
+void wip_tcp_free(struct wip *const w, void *const args)
+{
+ wip_log("%s: TODO\n", __func__);
+}
diff --git a/src/protocol/tcp/rx.c b/src/protocol/tcp/rx.c
new file mode 100644
index 0000000..6b23546
--- /dev/null
+++ b/src/protocol/tcp/rx.c
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/tcp.h>
+#include <wip/prv/tcp/types.h>
+#include <stddef.h>
+
+enum wip_state wip_tcp_rx(struct wip *const wip, const void *const buf,
+ const size_t n, void *const args)
+{
+ wip_log("%s: TODO\n");
+ return WIP_OK;
+}
diff --git a/src/protocol/tcp/tx.c b/src/protocol/tcp/tx.c
new file mode 100644
index 0000000..55524e3
--- /dev/null
+++ b/src/protocol/tcp/tx.c
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/tcp.h>
+#include <wip/prv/tcp/types.h>
+#include <stddef.h>
+
+int wip_tcp_tx(struct wip *const wip, void *const buf, const size_t n,
+ void *const args)
+{
+ wip_log("%s: TODO\n");
+ return 0;
+}
diff --git a/src/protocol/udp/CMakeLists.txt b/src/protocol/udp/CMakeLists.txt
new file mode 100644
index 0000000..0caec18
--- /dev/null
+++ b/src/protocol/udp/CMakeLists.txt
@@ -0,0 +1,13 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ alloc.c
+ free.c
+ rx.c
+ tx.c
+)
diff --git a/src/protocol/udp/alloc.c b/src/protocol/udp/alloc.c
new file mode 100644
index 0000000..d812ea5
--- /dev/null
+++ b/src/protocol/udp/alloc.c
@@ -0,0 +1,20 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/udp.h>
+#include <wip/prv/udp/types.h>
+#include <stddef.h>
+
+void *wip_udp_alloc(struct wip *const w)
+{
+ wip_log("%s: TODO\n", __func__);
+ return NULL;
+}
diff --git a/src/protocol/udp/free.c b/src/protocol/udp/free.c
new file mode 100644
index 0000000..c6354fb
--- /dev/null
+++ b/src/protocol/udp/free.c
@@ -0,0 +1,19 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/udp.h>
+#include <wip/prv/udp/types.h>
+#include <stddef.h>
+
+void wip_udp_free(struct wip *const w, void *const args)
+{
+ wip_log("%s: TODO\n", __func__);
+}
diff --git a/src/protocol/udp/rx.c b/src/protocol/udp/rx.c
new file mode 100644
index 0000000..29aa4cc
--- /dev/null
+++ b/src/protocol/udp/rx.c
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/udp.h>
+#include <wip/prv/udp/types.h>
+#include <stddef.h>
+
+enum wip_state wip_udp_rx(struct wip *const wip, const void *const buf,
+ const size_t n, void *const args)
+{
+ wip_log("%s: TODO\n");
+ return WIP_OK;
+}
diff --git a/src/protocol/udp/tx.c b/src/protocol/udp/tx.c
new file mode 100644
index 0000000..a29ec0f
--- /dev/null
+++ b/src/protocol/udp/tx.c
@@ -0,0 +1,21 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/log.h>
+#include <wip/prv/udp.h>
+#include <wip/prv/udp/types.h>
+#include <stddef.h>
+
+int wip_udp_tx(struct wip *const wip, void *const buf, const size_t n,
+ void *const args)
+{
+ wip_log("%s: TODO\n");
+ return 0;
+}
diff --git a/src/routines/CMakeLists.txt b/src/routines/CMakeLists.txt
new file mode 100644
index 0000000..aa69a13
--- /dev/null
+++ b/src/routines/CMakeLists.txt
@@ -0,0 +1,10 @@
+# wip, a small TCP/IP stack.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ rx.c
+)
diff --git a/src/routines/rx.c b/src/routines/rx.c
new file mode 100644
index 0000000..b031d14
--- /dev/null
+++ b/src/routines/rx.c
@@ -0,0 +1,447 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/io.h>
+#include <wip/prv/log.h>
+#include <wip/prv/icmp.h>
+#include <wip/prv/tcp.h>
+#include <wip/prv/udp.h>
+#include <wip/prv/routines.h>
+#include <stddef.h>
+
+#include <time.h>
+
+unsigned valid_packets;
+
+enum {MIN_HEADER_SZ = 20};
+
+static const struct wip_prot_ops protocols[] =
+{
+ {
+ WIP_P_ICMP,
+ wip_icmp_alloc,
+ wip_icmp_free,
+ wip_icmp_rx,
+ wip_icmp_tx
+ },
+
+ {
+ WIP_P_TCP,
+ wip_tcp_alloc,
+ wip_tcp_free,
+ wip_tcp_rx,
+ wip_tcp_tx
+ },
+
+ {
+ WIP_P_UDP,
+ wip_udp_alloc,
+ wip_udp_free,
+ wip_udp_rx,
+ wip_udp_tx
+ }
+};
+
+static enum wip_state get_data(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io *const io = &h->io;
+ const unsigned short dlen = wip_be16(&h->len) - h->header_sz;
+ const struct wip_prot_ops *const p = h->prot;
+ enum wip_state n;
+
+ if ((n = wip_io_read(w, io)))
+ return n;
+ else if (p && (n = p->rx(w, rx->buf, io->n, h->args)))
+ return n;
+
+#if 0
+ wip_log("got new data, h->i=%lu, io->n=%lu, dlen=%u\n",
+ (unsigned long)h->i, (unsigned long)io->n, dlen);
+#endif
+
+ if ((h->i += io->n) >= dlen)
+ {
+#if 1
+ wip_log("finished getting data (%u bytes)\n", (unsigned)dlen);
+#endif
+ if (h->prot)
+ {
+ h->prot->free(w, h->args);
+ h->prot = h->args = NULL;
+ }
+
+ wip_rx(w);
+ valid_packets++;
+ }
+ else
+ {
+ struct wip_sm_io nio = {0};
+ const size_t n = dlen - h->i;
+
+#if 0
+ wip_log("still need more data! (%u bytes)\n", n);
+#endif
+
+ nio.buf = rx->buf;
+ nio.n = n > sizeof rx->buf ? sizeof rx->buf : n;
+ *io = nio;
+ }
+
+ return WIP_OK;
+}
+
+static enum wip_state get_options(struct wip *const w)
+{
+ unsigned char b;
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+ enum wip_state n;
+
+ io.buf = &b;
+ io.n = sizeof b;
+
+ if ((n = wip_io_read(w, &io)))
+ return n;
+ else if (++h->i >= h->header_sz - MIN_HEADER_SZ)
+ {
+ struct wip_sm_io nio = {0};
+ const unsigned short dlen = wip_be16(&h->len);
+ const size_t n = dlen - h->i;
+
+ nio.buf = rx->buf;
+ nio.n = n > sizeof rx->buf ? sizeof rx->buf : n;
+ h->io = nio;
+ h->i = 0;
+#if 0
+ wip_log("%s: will read %lu bytes of data\n", __func__, (unsigned long)h->io.n);
+#endif
+ rx->next = get_data;
+ }
+
+ h->chksum += b;
+ return WIP_OK;
+}
+
+static enum wip_state get_dst(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ if (n)
+ return n;
+
+#if 0
+ wip_log("got dst address=%hhu.%hhu.%hhu.%hhu\n",
+ h->src.v[0], h->src.v[1], h->src.v[2], h->src.v[3]);
+#endif
+ h->chksum += wip_be32(&h->dst);
+
+ if (h->header_sz > MIN_HEADER_SZ)
+ {
+#if 0
+ wip_log("will parse options\n");
+#endif
+ rx->next = get_options;
+ }
+ else
+ {
+ struct wip_sm_io io = {0};
+ const size_t n = wip_be16(&h->len) - h->header_sz;
+
+ io.buf = rx->buf;
+ io.n = n >= sizeof rx->buf ? sizeof rx->buf : n;
+#if 0
+ wip_log("%s: expecting %lu bytes of data\n", __func__, (unsigned long)io.n);
+#endif
+ h->i = 0;
+ h->io = io;
+ rx->next = get_data;
+ }
+
+ return WIP_OK;
+}
+
+static enum wip_state get_src(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ if (n)
+ return n;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->dst;
+ io.n = sizeof h->dst;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got source address=%hhu.%hhu.%hhu.%hhu\n",
+ h->src.v[0], h->src.v[1], h->src.v[2], h->src.v[3]);
+#endif
+ h->chksum += wip_be32(&h->src);
+ rx->next = get_dst;
+ return WIP_OK;
+}
+
+static enum wip_state get_chksum(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ if (n)
+ return n;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->src;
+ io.n = sizeof h->src;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got expected checksum=%hu\n", wip_be16(&h->rchksum));
+#endif
+ rx->next = get_src;
+ return WIP_OK;
+}
+
+static const struct wip_prot_ops *get_ops(const unsigned char protocol)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof protocols / sizeof *protocols; i++)
+ {
+ const struct wip_prot_ops *const p = &protocols[i];
+
+ if (protocol == p->id)
+ return p;
+ }
+
+ return NULL;
+}
+
+static enum wip_state get_protocol(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+ enum wip_state n;
+
+ io.buf = &h->protocol;
+ io.n = sizeof h->protocol;
+
+ if ((n = wip_io_read(w, &io)))
+ return n;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->rchksum;
+ io.n = sizeof h->rchksum;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got protocol: %#hhx\n", h->protocol);
+#endif
+ if ((h->prot = get_ops(h->protocol))
+ && !(h->args = h->prot->alloc(w)))
+ return WIP_FATAL;
+
+ h->chksum += h->protocol;
+ rx->next = get_chksum;
+ return WIP_OK;
+}
+
+static enum wip_state get_ttl(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+ enum wip_state n;
+
+ io.buf = &h->ttl;
+ io.n = sizeof h->ttl;
+
+ if ((n = wip_io_read(w, &io)))
+ return n;
+
+#if 0
+ wip_log("got TTL\n");
+#endif
+ h->chksum += h->ttl;
+ rx->next = get_protocol;
+ return WIP_OK;
+}
+
+static enum wip_state get_flags_offset(struct wip *const w)
+{
+ unsigned char flags;
+ unsigned short fl_off;
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ if (n)
+ return n;
+
+ fl_off = wip_be16(&h->fl_off);
+
+ if ((flags = fl_off >> 13) & 1)
+ return WIP_INVALID;
+
+#if 0
+ wip_log("got flags offset: %hu\n", fl_off);
+#endif
+ h->chksum += fl_off;
+ rx->next = get_ttl;
+ return WIP_OK;
+}
+
+static enum wip_state get_id(struct wip *const w)
+{
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ /* TODO: use ID? */
+ if (n)
+ return n;
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->fl_off;
+ io.n = sizeof h->fl_off;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got id: %hu\n", wip_be16(&h->id));
+#endif
+ h->chksum += wip_be16(&h->id);
+ rx->next = get_flags_offset;
+ return WIP_OK;
+}
+
+static enum wip_state get_len(struct wip *const w)
+{
+ unsigned short len;
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ const enum wip_state n = wip_io_read(w, &h->io);
+
+ if (n)
+ return n;
+ else if ((len = wip_be16(&h->len)) < MIN_HEADER_SZ)
+ return WIP_INVALID;
+ else if (len > sizeof rx->buf)
+ {
+#if 0
+ wip_log("data too big: %hu (max %lu)\n", len,
+ (unsigned long)sizeof rx->buf);
+#endif
+ return WIP_INVALID;
+ }
+ else
+ {
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->id;
+ io.n = sizeof h->id;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got length: %hu\n", len);
+#endif
+ h->chksum = len;
+ rx->next = get_id;
+ return WIP_OK;
+}
+
+static enum wip_state get_dscp_ecn(struct wip *const w)
+{
+ unsigned char b;
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+ enum wip_state n;
+
+ io.buf = &b;
+ io.n = sizeof b;
+
+ /* TODO: support DSCP and ECN? */
+ if ((n = wip_io_read(w, &io)))
+ return n;
+ else
+ {
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+
+ io.buf = &h->len;
+ io.n = sizeof h->len;
+ h->io = io;
+ }
+
+#if 0
+ wip_log("got DSCP and ECN\n");
+#endif
+ h->chksum += b;
+ rx->next = get_len;
+ return WIP_OK;
+}
+
+static enum wip_state get_version_ihl(struct wip *const w)
+{
+ enum {VERSION = 4};
+ unsigned char b, version, ihl;
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const h = &rx->sm.h;
+ struct wip_sm_io io = {0};
+ enum wip_state n;
+
+ io.buf = &b;
+ io.n = sizeof b;
+
+ if ((n = wip_io_read(w, &io)))
+ return n;
+ else if ((version = b >> 4) != 4
+ || (ihl = b & 0xf) < MIN_HEADER_SZ / 4)
+ return WIP_INVALID;
+
+#if 0
+ wip_log("got IPv4 header, length=%u\n", ihl * 4);
+#endif
+ h->chksum += b;
+ h->header_sz = ihl * 4;
+ rx->next = get_dscp_ecn;
+ return WIP_OK;
+}
+
+void wip_rx(struct wip *const w)
+{
+ const struct wip_rx_sm_h h = {0};
+ struct wip_rx *const rx = &w->rx;
+ struct wip_rx_sm_h *const ph = &rx->sm.h;
+
+ *ph = h;
+ rx->next = get_version_ihl;
+}
diff --git a/src/run.c b/src/run.c
new file mode 100644
index 0000000..8ef952f
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,49 @@
+/*
+ * wip, a small TCP/IP stack.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <wip/wip.h>
+#include <wip/prv/routines.h>
+
+int wip_run(struct wip *const w)
+{
+ enum wip_state n;
+
+again:
+
+ switch ((n = w->rx.next(w)))
+ {
+ case WIP_OK:
+ goto again;
+
+ case WIP_AGAIN:
+ break;
+
+ case WIP_FATAL:
+ return -1;
+
+ case WIP_INVALID:
+ wip_rx(w);
+ break;
+ }
+
+#if 0
+ switch (w->tx.next(w))
+ {
+ case WIP_OK:
+ case WIP_AGAIN:
+ break;
+
+ case WIP_INVALID:
+ case WIP_FATAL:
+ return -1;
+ }
+#endif
+
+ return 0;
+}