You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

127 lines
2.5 KiB

package core
/*
#cgo CFLAGS: -I./c/include
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
err_t
input(struct pbuf *p)
{
return (*netif_list).input(p, netif_list);
}
*/
import "C"
import (
"encoding/binary"
"errors"
"unsafe"
)
type ipver byte
const (
ipv4 = 4
ipv6 = 6
)
type proto byte
const (
proto_icmp = 1
proto_tcp = 6
proto_udp = 17
)
func peekIPVer(p []byte) (ipver, error) {
if len(p) < 1 {
return 0, errors.New("short IP packet")
}
return ipver((p[0] & 0xf0) >> 4), nil
}
func moreFrags(ipv ipver, p []byte) bool {
switch ipv {
case ipv4:
if (p[6] & 0x20) > 0 /* has MF (More Fragments) bit set */ {
return true
}
case ipv6:
// FIXME Just too lazy to implement this for IPv6, for now
// returning true simply indicate do the copy anyway.
return true
}
return false
}
func fragOffset(ipv ipver, p []byte) uint16 {
switch ipv {
case ipv4:
return binary.BigEndian.Uint16(p[6:8]) & 0x1fff
case ipv6:
// FIXME Just too lazy to implement this for IPv6, for now
// returning a value greater than 0 simply indicate do the
// copy anyway.
return 1
}
return 0
}
func peekNextProto(ipv ipver, p []byte) (proto, error) {
switch ipv {
case ipv4:
if len(p) < 9 {
return 0, errors.New("short IPv4 packet")
}
return proto(p[9]), nil
case ipv6:
if len(p) < 6 {
return 0, errors.New("short IPv6 packet")
}
return proto(p[6]), nil
default:
return 0, errors.New("unknown IP version")
}
}
func Input(pkt []byte) (int, error) {
if len(pkt) == 0 {
return 0, nil
}
ipv, err := peekIPVer(pkt)
if err != nil {
return 0, err
}
nextProto, err := peekNextProto(ipv, pkt)
if err != nil {
return 0, err
}
lwipMutex.Lock()
defer lwipMutex.Unlock()
var buf *C.struct_pbuf
if nextProto == proto_udp && (!moreFrags(ipv, pkt) || fragOffset(ipv, pkt) > 0) {
// Copying data is not necessary for unfragmented UDP packets, and we would like to
// have all data in one pbuf.
buf = C.pbuf_alloc_reference(unsafe.Pointer(&pkt[0]), C.u16_t(len(pkt)), C.PBUF_REF)
} else {
// TODO Copy the data only when lwip need to keep it, e.g. in
// case we are returning ERR_CONN in tcpRecvFn.
//
// Allocating from PBUF_POOL results in a pbuf chain that may
// contain multiple pbufs.
buf = C.pbuf_alloc(C.PBUF_RAW, C.u16_t(len(pkt)), C.PBUF_POOL)
C.pbuf_take(buf, unsafe.Pointer(&pkt[0]), C.u16_t(len(pkt)))
}
ierr := C.input(buf)
if ierr != C.ERR_OK {
C.pbuf_free(buf)
return 0, errors.New("packet not handled")
}
return len(pkt), nil
}