lwIP (Raw API)¶
Platform/LwipRaw/ wraps the Raw API of lwIP
(lwIP documentation). Its .c
files compile against your lwipopts.h, so the adapter inherits your stack's
configuration.
Fills the Resolver, Datagram and Stream roles, plus the address handle they share. A TLS platform layers over the TCP stream; the capability matrix shows which provide it.
What it ships¶
| Header | What it is |
|---|---|
SolidSyslogLwipRawAddress.h |
An lwIP destination-address handle wrapping an ip_addr_t plus port. |
SolidSyslogLwipRawAddressErrors.h |
Source identity for the LwipRawAddress adapter; the detail codes it reports are the portable ones in SolidSyslogAddressErrors.h. |
SolidSyslogLwipRawDatagram.h |
UDP transport over the lwIP Raw API, for a UdpSender. |
SolidSyslogLwipRawDatagramErrors.h |
Source identity for the LwipRawDatagram adapter; the detail codes it reports are the portable ones in SolidSyslogDatagramErrors.h. |
SolidSyslogLwipRawDnsResolver.h |
The lwIP Raw resolver for a collector named by name, resolving names and numeric addresses alike. |
SolidSyslogLwipRawDnsResolverErrors.h |
Source identity for the LwipRawDnsResolver adapter; the detail codes it reports are the portable ones in SolidSyslogResolverErrors.h. |
SolidSyslogLwipRawMarshal.h |
The marshal seam that pins the library's lwIP Raw API calls to the core-owning thread. |
SolidSyslogLwipRawResolver.h |
The lwIP Raw resolver for a collector named by address: it resolves nothing, it parses the numeric literal you configured. |
SolidSyslogLwipRawResolverErrors.h |
Source identity for the LwipRawResolver adapter; the detail codes it reports are the portable ones in SolidSyslogResolverErrors.h. |
SolidSyslogLwipRawTcpStream.h |
A TCP stream over the lwIP Raw API, for a StreamSender or as the byte transport under a TLS stream. |
SolidSyslogLwipRawTcpStreamErrors.h |
Source identity for the LwipRawTcpStream adapter; the detail codes it reports are the portable ones in SolidSyslogTcpStreamErrors.h. |
The marshal¶
Not a role: SolidSyslogLwipRaw_SetMarshal
is a process-global seam, not a component you wire into the config. Every lwIP call
the Datagram and TcpStream make is routed through one marshal hop.
NO_SYS=1- bare metal, one execution context. Do nothing; the default direct-call marshal is correct.NO_SYS=0- an RTOS tcpip thread (lwIP multithreading documentation). CallSolidSyslogLwipRaw_SetMarshal(fn)once at boot, before creating any adapter, passing a function that runs its callback on the core-owning thread.
The marshal must invoke its callback synchronously - the adapter reads results the
moment the hop returns. A LOCK_TCPIP_CORE / UNLOCK_TCPIP_CORE pair satisfies
that directly. Posting to lwIP's mailbox does not, in any of its forms:
tcpip_callback_with_block blocks until the message is accepted, not until the
callback runs, so a mailbox marshal has to wait for completion itself.
Requirements¶
lwIP 2.1.0 or later: the TCP stream includes lwip/tcpbase.h, which that
release introduced. The pack is tested against 2.2.1 in CI.
The source calls lwIP only - no direct OS calls. The TCP stream's synchronous
Open and the DNS resolver's bounded wait both need a sleep, injected as a
SolidSyslogSleepFunction. Create either without one, and it reports a
bad configuration and hands back the shared Null object, so no record is
delivered.
Your lwipopts.h must enable the features the adapter wraps:
| Setting | For |
|---|---|
LWIP_UDP=1 |
the UDP datagram |
LWIP_TCP=1 |
the TCP stream |
LWIP_DNS=1 |
the DNS resolver only |
Also set ARP_QUEUEING=1 and size ARP_QUEUE_LEN to the burst you log at
start-up - see a burst to an unresolved next hop
below - and size PBUF_POOL_SIZE / MEMP_NUM_TCP_PCB / MEMP_NUM_UDP_PCB to
your instance counts.
LWIP_TCP_KEEPALIVE=1 is worth setting but not required -
see dead-peer detection below. IP_FRAG
decides what becomes of a record too large for the path - see
what becomes of an over-large record
below.
Security behaviour and obligations¶
The transport carries syslog in clear¶
Neither the datagram nor the TCP stream provides confidentiality, integrity or peer authentication. TLS is a separate role filled by a different platform - the capability matrix shows which - layered over this stream rather than replacing it.
The marshal is a correctness requirement, not a tuning knob¶
On a build with an lwIP thread (NO_SYS=0), every call this adapter makes must
reach the core-owning context, and it must do so synchronously because results
are read the moment the hop returns. An asynchronous marshal, or none at all,
corrupts lwIP's internal state rather than failing cleanly. Install it once at
boot, before any adapter is created.
A failed connect is reported, with its err_t¶
Each step of a TCP connect that fails is reported, once per run of the same step,
under the stream's own error source, with the detail naming the step, as
SOLIDSYSLOG_CAT_STREAM_CONNECT_FAILED.
Where the stack gave an err_t - the one tcp_connect answered, or the one it
handed the connected or error callback that ended the attempt - the event's
NativeCode carries it. A peer that closed the attempt rather than refusing
it carries ERR_CLSD. A tcp_new that answers no pcb, and a connect budget
that expires with no answer from the peer, leave none.
A burst to an unresolved next hop is held up to a bound¶
While lwIP resolves a next hop's link address it holds the datagrams sent to
it, and udp_sendto answers success for each one it takes into that queue;
it refuses only when it is out of memory. Without ARP_QUEUEING it
holds only the latest, so a burst sent before the reply arrives, such as the
records logged at start-up, loses all but its last record with nothing
reported. With ARP_QUEUEING=1 it holds ARP_QUEUE_LEN of them, three by
default, and drops the oldest for each one beyond that. It re-sends the
request every ARP_TMR_INTERVAL (1 s) and gives up after ARP_MAXPENDING
(5) tries, dropping what it held. The datagram does not check the cache
itself; set ARP_QUEUE_LEN to the number of records you log before the
first reply can arrive, and remember that a collector that never answers
loses them whatever the depth. TCP does not have this loss: a connection
waits for resolution and retransmits its opening, so the first records after
boot are held by the stream rather than dropped by the stack.
What becomes of an over-large record¶
The datagram cannot tell an over-large datagram from any other send failure,
which the Datagram contract permits,
and MaxPayload answers the limit the integrator states through the datagram
config's payload-limit callback, or SOLIDSYSLOG_UDP_IPV4_PAYLOAD_LIMIT where
none is stated, because the stack exposes no path MTU.
A record above that size is offered to lwIP whole, and what happens to that
first attempt is IP_FRAG's decision rather than the adapter's. Only the
outcome where lwIP reports that the send failed is recoverable: the sender then
recognises a record that could not have fitted, trims it on a UTF-8 codepoint
boundary and sends again.
IP_FRAG=1, lwIP's default: lwIP attempts to fragment the datagram and submits the fragments to your interface. Submission is reported as success, so the record is not trimmed - this is the case RFC 5426 section 3.2 warns about, where a lost fragment costs the whole record and some collectors and middleboxes drop fragments outright.IP_FRAG=0: lwIP compiles the length check out of its send path altogether and hands the over-length packet to your driver. A driver that drops it and answersERR_OKloses the record while the store counts it delivered; a driver that answers an error fails the send, and the record is then trimmed and delivered short.
Setting IP_FRAG=0 is therefore the configuration that lets an over-large
record be recovered, and it depends on your driver reporting the rejection
rather than swallowing it.
No record reaches that size at the default SOLIDSYSLOG_MAX_MESSAGE_SIZE. These
fates apply where the tunable has been raised past what the datagram reports -
noting that the tunable is library-wide rather than per-transport, so a value
chosen for this path applies to every transport the instance uses.
Dead-peer detection is yours to size¶
The stream sets the keepalive timings on its own connection, so the
SOLIDSYSLOG_TCP_KEEPALIVE_* tunables govern it and no other connection in your
system is affected.
How much of that the stack honours depends on one lwipopts.h setting.
LWIP_TCP_KEEPALIVE=1 makes the probe interval and count per-connection fields,
and every keepalive tunable applies. Without it those two are compile-time constants the
stack applies to every connection, so the idle period is still yours and the
other two come from TCP_KEEPINTVL_DEFAULT and TCP_KEEPCNT_DEFAULT. Either way
a silent peer is first probed when the idle tunable elapses.
This governs the idle case only. A connection actually carrying records notices a dead peer sooner: lwIP's send buffer fills, the write fails, and the stream closes itself so the sender reconnects.
Resolution is trusted as the stack returns it¶
The DNS resolver forwards what lwIP answers. A deployment that cannot trust its DNS should give the collector a numeric address, so that no resolution step exists to be poisoned.
Give that address in IPv4. The datagram and the stream send only to IPv4, so
on a build with LWIP_IPV6 both resolvers refuse an IPv6 answer, whether a
literal you configured or one the DNS returned, rather than store a destination
every send would fail to. The refusal is reported as ERROR with the detail
ADDRESS_FAMILY_UNSUPPORTED, and asking again will not change it.
Pool sizing is yours, and exhaustion is silent at the stack¶
PBUF_POOL_SIZE, MEMP_NUM_TCP_PCB and MEMP_NUM_UDP_PCB must cover the
instances you create alongside everything else using the stack. lwIP itself
reports nothing when a pool runs out. A MEMP_NUM_TCP_PCB shortage is the one
the adapter names: tcp_new answers no pcb, and the stream reports
ENDPOINT_UNAVAILABLE at ERROR once, on the first attempt that finds the pool
short, and again only once a connection has succeeded or a different connect
failure has been reported in between. A shortage of UDP
pcbs or pbufs fails the datagram's open or a send with no event of the adapter's
own, and reaches you as the sender's delivery failure.