mirror of
				https://github.com/xjasonlyu/tun2socks.git
				synced 2025-10-31 20:12:41 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			128 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 | 
