package core /* #cgo CFLAGS: -I./c/include #include "lwip/tcp.h" #include "lwip/udp.h" #include "lwip/timeouts.h" */ import "C" import ( "sync" "time" "unsafe" ) const CHECK_TIMEOUTS_INTERVAL = 250 // in millisecond const TCP_POLL_INTERVAL = 8 // poll every 4 seconds type LWIPStack interface { Write([]byte) (int, error) Close() error RestartTimeouts() } // lwIP runs in a single thread, locking is needed in Go runtime. var lwipMutex = &sync.Mutex{} type lwipStack struct { tpcb *C.struct_tcp_pcb upcb *C.struct_udp_pcb } // NewLWIPStack listens for any incoming connections/packets and registers // corresponding accept/recv callback functions. func NewLWIPStack() LWIPStack { tcpPCB := C.tcp_new() if tcpPCB == nil { panic("tcp_new return nil") } err := C.tcp_bind(tcpPCB, C.IP_ADDR_ANY, 0) switch err { case C.ERR_OK: break case C.ERR_VAL: panic("invalid PCB state") case C.ERR_USE: panic("port in use") default: C.memp_free(C.MEMP_TCP_PCB, unsafe.Pointer(tcpPCB)) panic("unknown tcp_bind return value") } tcpPCB = C.tcp_listen_with_backlog(tcpPCB, C.TCP_DEFAULT_LISTEN_BACKLOG) if tcpPCB == nil { panic("can not allocate tcp pcb") } setTCPAcceptCallback(tcpPCB) udpPCB := C.udp_new() if udpPCB == nil { panic("could not allocate udp pcb") } err = C.udp_bind(udpPCB, C.IP_ADDR_ANY, 0) if err != C.ERR_OK { panic("address already in use") } setUDPRecvCallback(udpPCB, nil) go func() { for { select { case <-time.After(CHECK_TIMEOUTS_INTERVAL * time.Millisecond): lwipMutex.Lock() C.sys_check_timeouts() lwipMutex.Unlock() } } }() return &lwipStack{ tpcb: tcpPCB, upcb: udpPCB, } } func (s *lwipStack) Write(data []byte) (int, error) { return Input(data) } func (s *lwipStack) RestartTimeouts() { C.sys_restart_timeouts() } func (s *lwipStack) Close() error { tcpConns.Range(func(_, c interface{}) bool { c.(*tcpConn).Abort() return true }) udpConns.Range(func(_, c interface{}) bool { c.(*udpConn).Close() return true }) return nil } func init() { // Initialize lwIP. // // There is a little trick here, a loop interface (127.0.0.1) // is created in the initialization stage due to the option // `#define LWIP_HAVE_LOOPIF 1` in `lwipopts.h`, so we need // not create our own interface. // // Now the loop interface is just the first element in // `C.netif_list`, i.e. `*C.netif_list`. lwipInit() }