### RMII Practical Tx Example in C Source: https://github.com/soypat/lneto/blob/main/phy/rmii.md Illustrates programmatic transmission over RMII, requiring a byte for every clock cycle. This example prepares preamble, SFD, frame data, and inter-packet gap according to RMII specifications. ```c uint8_t tx0 = 1<<0; uint8_t tx1 = 1<<1; uint8_t txen = 1<<3; uint8_t txdmsk = tx0|tx1; // data mask. // wait for previous transmission to end to comply with RMII. transmit_wait() // 28 dibits of preamble. "TXEN is asserted synchronously with first dibit of the preamble" -KSZ8081RNA/RND datasheet. 7 bytes of preamble. int index = 0; for (int i = 0; i < 28; i++) { tx_frame_bits[index++] = txen | tx0; // 0b101 == 0x5 } // Start Frame Delimiter marks end of preamble. // First 3 bytes written identical to loop above, // we just make the strict distinction of preamble vs. SFD tx_frame_bits[index++] = tx0|txen; // 0b101 == 0x5 tx_frame_bits[index++] = tx0|txen; // 0b101 == 0x5 tx_frame_bits[index++] = tx0|txen; // 0b101 == 0x5 tx_frame_bits[index++] = tx0|tx1|txen; // 0b111 == 0x7 // Now store the actual ethernet frame bits. We assume the CRC is included in length. for (int i = 0; i < frame_len; i++) { uint8_t b = tx_frame[i]; // The actual frame data. tx_frame_bits[index++] = txen | ((b >> 0) & txdmsk); tx_frame_bits[index++] = txen | ((b >> 2) & txdmsk); tx_frame_bits[index++] = txen | ((b >> 4) & txdmsk); tx_frame_bits[index++] = txen | ((b >> 6) & txdmsk); } // TxEnable driven low. This ensures sending hardware is acquired // during the TxEnable low period which has a minimum time must remain low between transmissions. // "TXD[1:0] is 00 to indicate idle when TXEN is deasserted. The PHY ignores values other than 00 on TXD[1:0] while TXEN is deasserted" -KSZ8081RNA/RND for (int i = 0; i < (12 * 4); i++) { tx_frame_bits[index++] = 0x00; } // transmit enacts tx0,tx1,txen bits over wire. Need not be blocking since we call transmit_wait. transmit(tx_frame_bits, index) ``` -------------------------------- ### Install Lneto Package with Go Modules Source: https://github.com/soypat/lneto/blob/main/README.md Use this command to download the latest version of the lneto package using Go modules. Ensure you are using Go version 1.16 or newer. ```sh go mod download github.com/soypat/lneto@latest ``` -------------------------------- ### Run xcurl with pcap logging Source: https://github.com/soypat/lneto/blob/main/README.md Execute the xcurl example with HTTP and no-TLS proxy options to generate packet capture logs. This demonstrates network traffic logging capabilities. ```log go run ./examples/xcurl -host google.com -ihttp -ntp softrand 1767229198 NIC hardware address: d8:5e:d3:43:03:eb bridgeHW: d8:5e:d3:43:03:eb mtu: 1500 addr: 192.168.1.53/24 OUT 328 [Ethernet len=14; destination=ff:ff:ff:ff:ff:ff; source=us | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=us; destination=255.255.255.255 | UDP [RFC768] len=8; (Source port)=68; (Destination port)=67 | DHCPv4 len=285; op=1; Flags=0x0000; (Client Address)=us; (Offered Address)=us; (Server Next Address)=255.255.255.255; (Relay Agent Address)=us; (Client Hardware Address)=d85e:d343:3eb::] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 98 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=192.168.1.1; destination=192.168.1.53 | ICMP [RFC792] len=64] IN 60 [Ethernet len=14; destination=ff:ff:ff:ff:ff:ff; source=e8:4d:74:9f:61:4a | ARP len=28; op=1; (Sender hardware address)=e8:4d:74:9f:61:4a; (Sender protocol address)=192.168.1.1; (Target hardware address)=00:00:00:00:00:00; (Target protocol address)=192.168.1.53] IN 590 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x0000; source=192.168.1.1; destination=192.168.1.53 | UDP [RFC768] len=8; (Source port)=67; (Destination port)=68 | DHCPv4 len=273; op=2; Flags=0x0000; (Client Address)=us; (Offered Address)=192.168.1.53; (Server Next Address)=us; (Relay Agent Address)=us; (Client Hardware Address)=d85e:d343:3eb::] OUT 326 [Ethernet len=14; destination=ff:ff:ff:ff:ff:ff; source=us | IPv4 len=20; (Type of Service)=0x00; flags=0x4000; source=us; destination=255.255.255.255 | UDP [RFC768] len=8; (Source port)=68; (Destination port)=67 | DHCPv4 len=283; op=1; Flags=0x0000; (Client Address)=us; (Offered Address)=192.168.1.53; (Server Next Address)=us; (Relay Agent Address)=us; (Client Hardware Address)=d85e:d343:3eb::] IN 590 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | IPv4 len=20; (Type of Service)=0x00; flags=0x0000; source=192.168.1.1; destination=192.168.1.53 | UDP [RFC768] len=8; (Source port)=67; (Destination port)=68 | DHCPv4 len=273; op=2; Flags=0x0000; (Client Address)=us; (Offered Address)=192.168.1.53; (Server Next Address)=us; (Relay Agent Address)=us; (Client Hardware Address)=d85e:d343:3eb::] [119ms] DHCP request completed 2025/12/31 21:59:58 INFO dhcp-complete assignedIP=192.168.1.53 routerIP=192.168.1.1 DNS=[192.168.1.1] subnet=192.168.1.0/24 OUT 42 [Ethernet len=14; destination=ff:ff:ff:ff:ff:ff; source=us | ARP len=28; op=1; (Sender hardware address)=us; (Sender protocol address)=us; (Target hardware address)=00:00:00:00:00:00; (Target protocol address)=192.168.1.1] IN 60 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | ARP len=28; op=2; (Sender hardware address)=e8:4d:74:9f:61:4a; (Sender protocol address)=192.168.1.1; (Target hardware address)=us; (Target protocol address)=us] [1.1s] Router ARP resolution IN 60 [Ethernet len=14; destination=us; source=e8:4d:74:9f:61:4a | ARP len=28; op=1; (Sender hardware address)=e8:4d:74:9f:61:4a; (Sender protocol address)=192.168.1.1; (Target hardware address)=00:00:00:00:00:00; (Target protocol address)=us] OUT 42 [Ethernet len=14; destination=e8:4d:74:9f:61:4a; source=us | ARP len=28; op=2; (Sender hardware address)=us; (Sender protocol address)=us; (Target hardware address)=e8:4d:74:9f:61:4a; (Target protocol address)=192.168.1.1] ``` -------------------------------- ### RMII Transmit Preamble and Packet Transmission Source: https://github.com/soypat/lneto/blob/main/phy/rmii.md This code implements the transmit preamble and packet transmission logic for RMII. It sets up pins, emits preamble and SFD, then transmits packet data. Ensure correct pin configuration and data buffer setup before execution. ```c // Preamble+SFD is composed of 32 RMII cycles. Note: Each byte is 4 RMII cycles. // Strictly speaking the Preamble is 28 cycles of TX0=1, TX1=0 // followed by the SFD which is 4 cycles, 3 of them TX0=1, TX1=0 // and one cycle with TX0=1, TX1=1. // These dibits, when interpreted as bytes as per RMII are 7 bytes of 0x55 (Preamble) followed by a single 0x57 byte (SFD). // Out pins are set as [TX0,TX1,TXEN]. // Assert TXEN=1 (a.k.a DV) and set TX0=1, TX1=0. We now need to emit 31 RMII cycles of this. // Following 6 instructions do 3 RMII cycles. preamb_t: set pins, 0b101 side 0 // Set pins TX0=1, TX1=0, TXEN=1 // Shift 16 bits from OSR into x out x, 8 side 1 in x, 8 side 0 out x, 8 side 1 in x, 24 side 0 set x, 27 side 1 // Set X=27, to loop 28 times in ploop_t ploop_t: set y, 22 side 0 // Setup Y reg for IPG inner loop. jmp x--,ploop_t side 1 // By now 3+28=31 RMII cycles complete with TX0=1,TX1=0. set pins, 0b111 side 0 // We need last cycle SFD with TX0=1,TX1=1,TXEN=1 mov x, isr side 1 // Read package length from input buffer. // Transmit until packet count exhausted xmit_t: out pins, 2 side 0 // Send two bits of tx data. jmp x--,xmit_t side 1 // loop until x==0 (data exhausted). // Do Inter Packet Gap(IPG) - 960ns, 48 RMII dibit clks. // Delay 47 clocks here, then 1 clock for Tx queue status. ipg_t: set pins, 0b000 side 0 // TX0/1/EN deasserted during IPG. RMII bus is said to be idle. nop side 1 nop side 0 jmp y--, ipg_t side 1 // Do 23 times, we set Y to 22 above. nop side 0 nop side 1 public tx_start_t: .wraptarget_t mov x, status side 0 // Get Tx not empty status. jmp !x, preamb_t side 1 .wrap_t ``` -------------------------------- ### Configure and Open TCP Connection Source: https://context7.com/soypat/lneto/llms.txt Demonstrates configuring a TCP connection with buffers and opening it in either active (client) or passive (server) mode. The connection must be registered with the IP stack and wait for the handshake to complete before reading or writing. ```go import ( "net/netip" "github.com/soypat/lneto/tcp" ) var conn tcp.Conn rxBuf := make([]byte, 4096) txBuf := make([]byte, 4096) err := conn.Configure(tcp.ConnConfig{ RxBuf: rxBuf, TxBuf: txBuf, TxPacketQueueSize: 4, }) if err != nil { panic(err) } // Active (client) mode: open a connection to a remote server. remote := netip.MustParseAddrPort("142.251.129.142:80") err = conn.OpenActive(51982 /* local port */, remote, 0xdeadbeef /* ISS */) // Passive (server) mode: wait for incoming SYN on port 80. err = conn.OpenListen(80, 0xdeadbeef) // Register with IP stack — routes proto=TCP packets to conn. ip.Register(&conn) // Read / Write after handshake completes (StateEstablished). for conn.State() != tcp.StateEstablished { time.Sleep(time.Millisecond) // wait for handshake driven by stack loop } conn.Write([]byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")) buf := make([]byte, 1024) n, err := conn.Read(buf) fmt.Printf("received: %s\n", buf[:n]) // Set deadlines, just like net.Conn. conn.SetReadDeadline(time.Now().Add(5 * time.Second)) conn.Close() ``` -------------------------------- ### Configure and Run Integrated Network Stack with xnet.StackAsync Source: https://context7.com/soypat/lneto/llms.txt Initialize xnet.StackAsync with StackConfig, including MTU, MAC address, and port limits. The NIC loop must be run in a separate goroutine to handle packet ingress and egress. Bootstrap common network services like DHCP and ARP using StackRetrying. ```go import ( "context" "time" "github.com/soypat/lneto/x/xnet" "github.com/soypat/lneto/ethernet" ) var stack xnet.StackAsync err := stack.Reset(xnet.StackConfig{ Hostname: "my-device", RandSeed: time.Now().UnixNano(), MaxActiveTCPPorts: 4, MaxActiveUDPPorts: 2, MTU: uint16(nic.MaxFrameLen() - ethernet.MaxOverheadSize), HardwareAddress: nicMAC, ICMPQueueLimit: 4, PassivePeers: 8, }) // NIC loop — runs in a goroutine. ctx, cancel := context.WithCancel(context.Background()) go func() { buf := make([]byte, 1514) for ctx.Err() == nil { n, _ := stack.EgressEthernet(buf) // send if n > 0 { nic.SendEth(buf[:n]) } n, _ = nic.RecvEth(buf) // receive if n > 0 { stack.IngressEthernet(buf[:n]) } if n == 0 { time.Sleep(time.Millisecond) } } }() // Bootstrap: DHCP → ARP → TCP listener. rstack := stack.StackRetrying(func(n uint) time.Duration { if n < 10 { return time.Millisecond } return 10 * time.Millisecond }) results, err := rstack.DoDHCPv4([4]byte{}, 5*time.Second, 3) stack.AssimilateDHCPResults(results) gw, _ := rstack.DoResolveHardwareAddress6(results.Router, 5*time.Second, 3) stack.SetGateway6(gw) // TCP server using Berkeley socket API. berkstack := stack.StackBlocking(nil).StackGo(xnet.StackGoConfig{ ListenerPoolConfig: xnet.TCPPoolConfig{ PoolSize: 10, QueueSize: 4, TxBufSize: 2048, RxBufSize: 2048, NanoTime: func() int64 { return time.Now().UnixNano() }, }, }) laddr := net.TCPAddrFromAddrPort(netip.AddrPortFrom(results.AssignedAddr, 8080)) c, _ := berkstack.Socket(ctx, "tcp", syscall.AF_INET, syscall.SOCK_STREAM, laddr, nil) ln := c.(net.Listener) for { conn, err := ln.Accept() if err != nil { continue } go func(c net.Conn) { defer c.Close(); io.Copy(c, c) }(conn) } ``` -------------------------------- ### Implement TCP Listener with Connection Pool Source: https://context7.com/soypat/lneto/llms.txt Shows how to set up a TCP listener that accepts multiple connections using a custom connection pool. The listener is registered on the IP stack, and incoming connections are handled in separate goroutines. ```go import "github.com/soypat/lneto/tcp" // Implement the pool interface. type myPool struct { conns [4]tcp.Conn; n int } func (p *myPool) GetTCP() (*tcp.Conn, any, tcp.Value) { if p.n >= len(p.conns) { return nil, nil, 0 } c := &p.conns[p.n]; p.n++ c.Configure(tcp.ConnConfig{RxBuf: make([]byte,2048), TxBuf: make([]byte,2048), TxPacketQueueSize:4}) return c, nil, tcp.Value(rand.Uint32()) } func (p *myPool) PutTCP(c *tcp.Conn) { /* return conn to pool */ } var pool myPool var listener tcp.Listener err := listener.Reset(80, &pool) // Register listener on IP stack. ip.Register(&listener) // Poll for accepted connections in a goroutine. for { conn, _, err := listener.TryAccept() if err == lneto.ErrExhausted { time.Sleep(time.Millisecond) continue } if err != nil { break } go func(c *tcp.Conn) { defer c.Close() buf := make([]byte, 512) n, _ := c.Read(buf) fmt.Printf("server received: %s\n", buf[:n]) c.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")) }(conn) } ``` -------------------------------- ### Run httptap Server Source: https://github.com/soypat/lneto/blob/main/README.md Build and run the HTTP Tap server. This program exposes the 'tap0' TAP interface over an HTTP interface, with '/recv' and '/send' endpoints. Requires root privileges. ```sh go build ./examples/httptap && sudo ./httpap ``` -------------------------------- ### internet.StackEthernet Configuration and Usage Source: https://context7.com/soypat/lneto/llms.txt Configures and uses the Ethernet link-layer node. Shows how to register callbacks, child nodes like ARP, and integrate with NIC send/receive loops. ```go import ( "fmt" "github.com/soypat/lneto/internet" "github.com/soypat/lneto/arp" ) var link internet.StackEthernet var myMAC = [6]byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb} var gwMAC = [6]byte{0xe8, 0x4d, 0x74, 0x9f, 0x61, 0x4a} // err := link.Configure(internet.StackEthernetConfig{ // MTU: 1500, // MaxNodes: 4, // MAC: myMAC, // Gateway: gwMAC, // }) // if err != nil { // panic(err) // } // Register a callback to observe every outgoing frame. // link.OnEncapsulate(func(frame []byte) { // fmt.Printf("TX %d bytes\n", len(frame)) // }) // Register ARP child node. // var arpHandler arp.Handler // link.Register(&arpHandler) // Receive loop: feed raw Ethernet frames into the stack. // var buf [1514]byte // n, _ := nic.RecvEth(buf[:]) // if n > 0 { // err = link.Demux(buf[:n], 0) // routes to arpHandler or IP // } // Send loop: ask stack to produce next outgoing frame. // n, err = link.Encapsulate(buf[:], 0, 0) // if n > 0 { // nic.SendEth(buf[:n]) // } ``` -------------------------------- ### internet.StackIP Configuration and Registration Source: https://context7.com/soypat/lneto/llms.txt Configures the IPv4 network layer node and registers it with the Ethernet stack. Shows how to register transport handlers and query the assigned IP address. ```go import ( "fmt" "net/netip" "github.com/soypat/lneto/internet" "github.com/soypat/lneto" ) var ip internet.StackIP // err := ip.Reset(netip.MustParseAddr("192.168.1.53"), 8 /* maxNodes */) // if err != nil { // panic(err) // } // Register IP node on the Ethernet stack (EtherType 0x0800). // link.Register(&ip) // Register TCP and UDP child handlers on the IP stack. // ip.Register(&tcpConn) // lneto.IPProtoTCP (6) // ip.Register(&udpPort) // lneto.IPProtoUDP (17) // ip.IsRegistered checks if a protocol is already registered. // if ip.IsRegistered(lneto.IPProtoTCP) { // fmt.Println("TCP is registered") // } // Query assigned address. // fmt.Println("Our IP:", ip.Addr()) // prints 192.168.1.53 ``` -------------------------------- ### Run Go Benchmarks for xnet Package Source: https://github.com/soypat/lneto/blob/main/README.md Execute benchmarks for the x/xnet package using Go's built-in testing tools. This command measures performance and memory allocations for ARP and TCP operations. ```bash go test -bench=. -benchmem ./x/xnet ``` -------------------------------- ### Run TinyGo Benchmarks for xnet Package Source: https://github.com/soypat/lneto/blob/main/README.md Execute benchmarks for the x/xnet package using TinyGo with optimization level 2. This command measures performance and memory allocations for ARP and TCP operations, similar to Go benchmarks but for TinyGo. ```bash tinygo test -opt=2 -bench=. -benchmem ./x/xnet ``` -------------------------------- ### Fuzzing Ethernet/IP/TCP/HTTP Stack Source: https://github.com/soypat/lneto/blob/main/README.md This command demonstrates how to fuzz test the Ethernet, IP, TCP, and HTTP stack using Go's built-in fuzzing capabilities. It achieves a high rate of HTTP exchanges per second. ```go go test ./x/xnet/ -run FuzzStackAsyncHTTP -fuzz=. fuzz: elapsed: 4m9s, execs: 42649941 (169428/sec), new interesting: 5 (total: 52) ``` -------------------------------- ### tcp.Conn - TCP Connection Source: https://context7.com/soypat/lneto/llms.txt Demonstrates how to configure, open, and use a TCP connection for both client (active) and server (passive) modes. It covers registration with the IP stack, reading and writing data, setting deadlines, and closing the connection. ```APIDOC ## tcp.Conn — TCP Connection `tcp.Conn` is a thread-safe TCP socket with `Read`/`Write` semantics similar to `net.TCPConn`. It can be opened in active (dial) or passive (listen) mode and integrates into the `StackIP` node tree as a `StackNode`. ```go import ( "net/netip" "github.com/soypat/lneto/tcp" ) var conn tcp.Conn rxBuf := make([]byte, 4096) txBuf := make([]byte, 4096) err := conn.Configure(tcp.ConnConfig{ RxBuf: rxBuf, TxBuf: txBuf, TxPacketQueueSize: 4, }) if err != nil { panic(err) } // Active (client) mode: open a connection to a remote server. remote := netip.MustParseAddrPort("142.251.129.142:80") err = conn.OpenActive(51982 /* local port */, remote, 0xdeadbeef /* ISS */) // Passive (server) mode: wait for incoming SYN on port 80. err = conn.OpenListen(80, 0xdeadbeef) // Register with IP stack — routes proto=TCP packets to conn. ip.Register(&conn) // Read / Write after handshake completes (StateEstablished). for conn.State() != tcp.StateEstablished { time.Sleep(time.Millisecond) // wait for handshake driven by stack loop } conn.Write([]byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")) buf := make([]byte, 1024) n, err := conn.Read(buf) fmt.Printf("received: %s\n", buf[:n]) // Set deadlines, just like net.Conn. conn.SetReadDeadline(time.Now().Add(5 * time.Second)) conn.Close() ``` ``` -------------------------------- ### DHCPv4 Client Request and State Management Source: https://context7.com/soypat/lneto/llms.txt Illustrates initiating a DHCPv4 request, including client hardware address and hostname. The client polls until it reaches the 'Bound' state to retrieve assigned IP address, router, subnet, and DNS server information. ```go import "github.com/soypat/lneto/dhcpv4" var dhcp dhcpv4.Client mac := [6]byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb} err := dhcp.BeginRequest(0xdeadbeef /* XID */, dhcpv4.RequestConfig{ ClientHardwareAddr: mac, Hostname: "my-device", }) if err != nil { panic(err) } // Register on UDP stack (port 68). // The stack loop calls dhcp.Encapsulate / dhcp.Demux automatically. // Poll until bound. for dhcp.State() != dhcpv4.StateBound { time.Sleep(time.Millisecond) } // Read results. assignedAddr, ok := dhcp.AssignedAddr() router, _ := dhcp.RouterAddr() subnet := dhcp.SubnetPrefix() dns := dhcp.DNSServerFirst() fmt.Printf("IP: %v subnet: %v router: %v dns: %v\n", netip.AddrFrom4(assignedAddr), subnet, netip.AddrFrom4(router), dns) fmt.Printf("lease: %ds renew: %ds\n", dhcp.IPLeaseSeconds(), dhcp.RenewalSeconds()) ``` -------------------------------- ### tcp.Listener - TCP Listener / Server Source: https://context7.com/soypat/lneto/llms.txt Details on setting up a TCP listener to accept multiple simultaneous inbound connections. It explains how to provide a connection pool and register the listener with the IP stack to handle incoming requests. ```APIDOC ## tcp.Listener — TCP Listener / Server `tcp.Listener` accepts multiple simultaneous inbound TCP connections on a single port, sourcing new `tcp.Conn` objects from a user-supplied pool. It implements `StackNode` and is registered directly on a `StackIP`. ```go import "github.com/soypat/lneto/tcp" // Implement the pool interface. type myPool struct { conns [4]tcp.Conn; n int } func (p *myPool) GetTCP() (*tcp.Conn, any, tcp.Value) { if p.n >= len(p.conns) { return nil, nil, 0 } c := &p.conns[p.n]; p.n++ c.Configure(tcp.ConnConfig{RxBuf: make([]byte,2048), TxBuf: make([]byte,2048), TxPacketQueueSize:4}) return c, nil, tcp.Value(rand.Uint32()) } func (p *myPool) PutTCP(c *tcp.Conn) { /* return conn to pool */ } var pool myPool var listener tcp.Listener er r := listener.Reset(80, &pool) // Register listener on IP stack. ip.Register(&listener) // Poll for accepted connections in a goroutine. for { conn, _, err := listener.TryAccept() if err == lneto.ErrExhausted { time.Sleep(time.Millisecond) continue } if err != nil { break } go func(c *tcp.Conn) { defer c.Close() buf := make([]byte, 512) n, _ := c.Read(buf) fmt.Printf("server received: %s\n", buf[:n]) c.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")) }(conn) } ``` ``` -------------------------------- ### dhcpv4.Client - DHCP v4 Client Source: https://context7.com/soypat/lneto/llms.txt Explains how to use the DHCPv4 client to obtain network configuration. It covers initiating a request, registering with the UDP stack, polling for a bound state, and retrieving assigned IP address, router, subnet, and DNS information. ```APIDOC ## dhcpv4.Client — DHCP v4 Client `dhcpv4.Client` implements the DHCPv4 client state machine (RFC 2131): Discover → Offer → Request → Ack. It is a `StackNode` registered on a UDP port and drives itself through `Encapsulate`/`Demux` calls from the stack loop. ```go import "github.com/soypat/lneto/dhcpv4" var dhcp dhcpv4.Client mac := [6]byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb} err := dhcp.BeginRequest(0xdeadbeef /* XID */, dhcpv4.RequestConfig{ ClientHardwareAddr: mac, Hostname: "my-device", }) if err != nil { panic(err) } // Register on UDP stack (port 68). // The stack loop calls dhcp.Encapsulate / dhcp.Demux automatically. // Poll until bound. for dhcp.State() != dhcpv4.StateBound { time.Sleep(time.Millisecond) } // Read results. assignedAddr, ok := dhcp.AssignedAddr() router, _ := dhcp.RouterAddr() subnet := dhcp.SubnetPrefix() dns := dhcp.DNSServerFirst() fmt.Printf("IP: %v subnet: %v router: %v dns: %v\n", netip.AddrFrom4(assignedAddr), subnet, netip.AddrFrom4(router), dns) fmt.Printf("lease: %ds renew: %ds\n", dhcp.IPLeaseSeconds(), dhcp.RenewalSeconds()) ``` ``` -------------------------------- ### NTP Time Synchronization with ntp.Client Source: https://context7.com/soypat/lneto/llms.txt Implements a two-exchange NTP client for clock offset and round-trip delay calculation. The client requires registration on a UDP stack and runs two exchanges for accuracy. Use IsDone() to check for completion. ```go import ( "time" "github.com/soypat/lneto/ntp" ) var ntpClient ntp.Client ntpClient.Reset(-6 /* system precision: 2^-6 ≈ 15ms */, time.Now) // Register on UDP stack (port 123). // Stack loop drives ntpClient.Encapsulate / ntpClient.Demux. // Wait for two NTP exchanges to complete. for !ntpClient.IsDone() { time.Sleep(time.Millisecond) } offset := ntpClient.Offset() // time.Duration: positive = we are ahead rtt := ntpClient.RoundTripDelay() // round-trip delay now := ntpClient.Now() // corrected current time fmt.Printf("offset: %v rtt: %v server-time: %s\n", offset, rtt, now.Format(time.RFC3339)) fmt.Printf("stratum: %s\n", ntpClient.ServerStratum()) ``` -------------------------------- ### Encode and Decode DNS Messages Source: https://context7.com/soypat/lneto/llms.txt Demonstrates low-level encoding of DNS queries and decoding of DNS responses using dns.Message. Ensure proper initialization and error handling for incomplete responses. ```go import "github.com/soypat/lneto/dns" // --- Low-level: encode a DNS query --- name, _ := dns.NewName("google.com") var msg dns.Message msg.LimitResourceDecoding(1, 4, 0, 1) // max 1 question, 4 answers msg.AddQuestions([]dns.Question{ {Name: name, Type: dns.TypeA, Class: dns.ClassINET}, }) buf, err := msg.AppendTo(nil, 0x1234 /* txid */, dns.HeaderFlags{}) ``` ```go import "github.com/soypat/lneto/dns" import "netip" // --- Low-level: decode a DNS response --- var resp dns.Message resp.LimitResourceDecoding(1, 4, 0, 2) _, incomplete, err := resp.Decode(responseBytes) if err != nil && !incomplete { panic(err) } for _, ans := range resp.Answers { hdr := ans.Header() fmt.Printf("%s %s ttl=%d\n", hdr.Name.String(), hdr.Type, hdr.TTL) if hdr.Type == dns.TypeA { fmt.Printf(" IPv4: %v\n", netip.AddrFrom4([4]byte(ans.RawData()))) } } ``` ```go import "github.com/soypat/lneto/dns" // --- Build a resource record --- var rec dns.Resource rec.SetA(name, dns.ClassINET, 300 /* TTL */, []byte{93, 184, 216, 34}) rec.SetSRV( dns.MustNewName("_http._tcp.local"), dns.ClassINET, 120, 0 /* priority */, 0 /* weight */, 80 /* port */, dns.MustNewName("myhost.local"), ) ``` -------------------------------- ### Configure and Use ARP Handler Source: https://context7.com/soypat/lneto/llms.txt Configure an ARP handler with hardware and protocol addresses, set a callback for MAC resolution, and optionally pre-seed the cache. This is useful for managing ARP requests and replies in a network stack. ```go import ( "github.com/soypat/lneto/arp" "github.com/soypat/lneto/ethernet" ) var h arp.Handler ourIP := []byte{192, 168, 1, 53} ourMAC := []byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb} err := h.Reset(arp.HandlerConfig{ HardwareAddr: ourMAC, ProtocolAddr: ourIP, HardwareType: 1, // Ethernet ProtocolType: ethernet.TypeIPv4, MaxQueries: 4, MaxPending: 4, }) // Kick off an ARP query for the router's MAC. routerIP := []byte{192, 168, 1, 1} h.StartQuery(routerIP, true /* trigger callback */) // Set a callback invoked when the MAC resolves. h.SetOnResolveCallback(func(hwAddr, protoAddr []byte) { fmt.Printf("Resolved %v -> %v\n", protoAddr, hwAddr) }) // Register on link stack (EtherType ARP = 0x0806). link.Register(&h) // After a few send/recv cycles the callback fires. // Alternatively, poll directly: mac, err := h.CacheLookup(routerIP) if err == nil { fmt.Printf("Router MAC: %x\n", mac) } // Pre-seed a known entry (skip ARP round-trip): h.CacheSeed(routerIP, []byte{0xe8, 0x4d, 0x74, 0x9f, 0x61, 0x4a}) ``` -------------------------------- ### lneto Common Errors and Handling Source: https://context7.com/soypat/lneto/llms.txt Lists common lneto errors and demonstrates a typical error handling pattern in a stack receive loop. Note that net.ErrClosed signals a StackNode should be deregistered. ```go import "github.com/soypat/lneto" // Common errors and their meanings: _ = lneto.ErrPacketDrop // packet intentionally discarded (wrong dst, unknown proto) _ = lneto.ErrBadCRC // checksum mismatch _ = lneto.ErrShortBuffer // buffer too small to hold frame data _ = lneto.ErrBufferFull // ring buffer or TX queue is full _ = lneto.ErrTruncatedFrame // buffer smaller than minimum protocol header _ = lneto.ErrMismatch // field does not match expected value (port, addr, XID) _ = lneto.ErrMismatchLen // length mismatch between protocol fields _ = lneto.ErrInvalidConfig // bad configuration passed to Reset/Configure _ = lneto.ErrAlreadyRegistered // protocol number already has a handler _ = lneto.ErrExhausted // resource pool empty (no free connections) _ = lneto.ErrUnsupported // feature/protocol not yet implemented _ = lneto.ErrInvalidAddr // nil/invalid IP address // Typical error handling pattern in a stack receive loop: err := stack.IngressEthernet(frame) if err != nil && err != lneto.ErrPacketDrop { log.Printf("unexpected stack error: %v", err) } // net.ErrClosed signals a StackNode should be deregistered. if err == net.ErrClosed { ip.Register(newConn) // replace the closed connection } ``` -------------------------------- ### StackNode Interface Definition and Usage Source: https://context7.com/soypat/lneto/llms.txt Defines the core StackNode interface implemented by all protocol handlers. Shows basic send/receive path usage with a custom node. ```go package main // StackNode is implemented by every protocol handler in lneto. type StackNode interface { // Encapsulate writes this node's frame into carrierData[offsetToFrame:] and returns bytes written. Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) // Demux dispatches an incoming frame to child nodes starting at carrierData[frameOffset:]. Demux(carrierData []byte, frameOffset int) error LocalPort() uint16 Protocol() uint64 ConnectionID() *uint64 } // Example: manually drive a custom StackNode through a send/receive loop. var buf [1514]byte // Receive path: parse raw bytes from NIC. // err := myEthernetStack.Demux(buf[:n], 0) // Send path: ask stack to fill next outgoing frame. // n, err := myEthernetStack.Encapsulate(buf[:], 0, 0) // if n > 0 { // nic.SendEth(buf[:n]) // } ``` -------------------------------- ### SYN Cookie Generation and Validation Benchmark Source: https://github.com/soypat/lneto/blob/main/README.md Benchmarks for SYN cookie generation and validation. These tests are part of the TCP implementation and focus on the efficiency of SYN cookie mechanisms. ```go BenchmarkSYNCookie_Generate / BenchmarkSYNCookie_Validate - SYN cookie generation and validation: 0 B/op, 0 allocs/op each ``` -------------------------------- ### Run xcurl with HTTP Tap Source: https://github.com/soypat/lneto/blob/main/README.md Execute the xcurl application to connect to a host using the HTTP Tap interface. This allows non-root users to debug network traffic comfortably. ```sh go run ./examples/xcurl -host google.com -ihttp ``` -------------------------------- ### Heapless HTTP/1.1 Header Processing Source: https://context7.com/soypat/lneto/llms.txt Parses and formats HTTP/1.1 headers with minimal allocation using httpraw.Header. Supports incremental parsing via TryParse and provides key-value access methods. Use Reset() to clear and reuse the header object. ```go import "github.com/soypat/lneto/http/httpraw" var h httpraw.Header // --- Parse a request header from bytes --- reqBytes := []byte("GET /index.html HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n") err := h.ParseBytes(false /* request */, reqBytes) if err != nil { panic(err) } fmt.Println("method:", string(h.Method())) // "GET" fmt.Println("URI:", string(h.RequestURI())) // "/index.html" fmt.Println("host:", string(h.Get("Host"))) // "example.com" h.ForEach(func(key, value []byte) error { fmt.Printf(" %s: %s\n", key, value) return nil }) ``` ```go import "github.com/soypat/lneto/http/httpraw" var h httpraw.Header // --- Build a response header --- h.Reset(nil) h.SetProtocol("HTTP/1.1") h.SetStatus("200", "OK") h.Set("Content-Type", "text/plain") h.Set("Content-Length", "5") out, err := h.AppendResponse(nil) fmt.Println(string(out)) ``` ```go import "github.com/soypat/lneto/http/httpraw" var h httpraw.Header // --- Streaming parse (incremental) --- h.Reset(nil) h.ReadFromBytes(partialHeaderBytes) needMore, err := h.TryParse(false) if needMore { h.ReadFromBytes(moreBytes) needMore, err = h.TryParse(false) } ``` ```go import "github.com/soypat/lneto/http/httpraw" // Normalize a header key in-place. key := []byte("content-length") httpraw.NormalizeHeaderKey(key) // -> "Content-Length" ``` -------------------------------- ### HTTP/1.1 Header Parsing Benchmark Source: https://github.com/soypat/lneto/blob/main/README.md Benchmark for parsing HTTP/1.1 request headers and body. This specific benchmark measures the allocation per operation for header processing. ```go BenchmarkParseBytes - HTTP/1.1 request header + body parsing: 240 B/op, 2 allocs/op ``` -------------------------------- ### Parse and Build IPv4 Packets Source: https://context7.com/soypat/lneto/llms.txt Offers zero-copy access to IPv4 packet headers for direct field manipulation. Use `ipv4.NewFrame` to parse received packets and modify fields directly on the byte slice for building outgoing packets. Includes validation and checksum calculation. ```go import "github.com/soypat/lneto/ipv4" raw := /* received Ethernet payload */ []byte{ /* ... */ } ifrm, err := ipv4.NewFrame(raw) if err != nil { // buffer shorter than 20 bytes panic(err) } ver, ihl := ifrm.VersionAndIHL() proto := ifrm.Protocol() // lneto.IPProto src := ifrm.SourceAddr() // *[4]byte dst := ifrm.DestinationAddr() // *[4]byte ttl := ifrm.TTL() fmt.Printf("IPv%d proto=%s src=%v dst=%v ttl=%d\n", ver, proto, netip.AddrFrom4(*src), netip.AddrFrom4(*dst), ttl) // Validate header (all fields except CRC). var vld lneto.Validator ifrm.ValidateExceptCRC(&vld) if vld.HasError() { return vld.ErrPop() } // Verify header checksum (should equal zero after calculation). if ifrm.CalculateHeaderCRC() != 0 { return lneto.ErrBadCRC } // Build an outgoing packet. ifrm.SetVersionAndIHL(4, 5) ifrm.SetTTL(64) ifrm.SetProtocol(lneto.IPProtoTCP) *ifrm.SourceAddr() = [4]byte{192, 168, 1, 53} *ifrm.DestinationAddr() = [4]byte{142, 251, 129, 142} ifrm.SetTotalLength(uint16(20 + payloadLen)) ifrm.SetCRC(0) ifrm.SetCRC(ifrm.CalculateHeaderCRC()) ```