etherconn

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2020 License: BSD-2-Clause Imports: 18 Imported by: 6

README

etherconn

Build Status PkgGoDev

Package etherconn is a golang pkg that allow user to send/receive Ethernet payload (like IP pkt) or UDP packet ,with custom Ethernet encapsulation like MAC address, VLAN tags, without creating corresponding interface in OS;

For example, with etherconn, a program could send/recive a UDP or IP packet with a source MAC address and VLAN tags don't exists/provisioned in any of OS interfaces;

Another benefit is since etherconn bypasses "normal" Linux kernel routing and IP stack, in scale setup like tens of thousands conns no longer subject to linux kernel limitation like # of socket/fd limitations, UDP buffer size...etc;

Lastly etherconn.RUDPConn implements the net.PacketConn interface, so it could be easily integrated into existing code;

Usage:

interface <---> PacketRelay <----> EtherConn <---> RUDPConn
                            <----> EtherConn <---> RUDPConn
                            <----> EtherConn <---> RUDPConn
  1. Create a PacketRelay instance and bound to an interface.PacketRelay is the "forward engine" that does actual packet sending/receiving for all EtherConn instances registered with it; PacketRelay send/receive Ethernet packet

  2. Create one EtherConn for each source MAC+VLAN(s) combination needed, and register with the PacketRelay instance. EtherConn send/receive Ethernet payload like IP packet;

  3. Create one RUDPConn instance for each UDP endpoint (IP+Port) needed, with a EtherConn. RUDPConn send/receive UDP payload.

  4. RUDPConn and EtherConn is 1:1 mapping, while EtherConn and PacketRelay is N:1 mapping; since EtherConn and RUDPConn is 1:1 mapping, which means EtherConn will forward all received UDP pkts to RUDPConn even when its IP/UDP port is different from RUDPConn's endpoint, and RUDPConn could either only accept correct pkt or accept any UDP packet;

Egress direction:

UDP_payload -> RUDPConn(add UDP&IP header) -> EtherConn(add Ethernet header) -> PacketRelay

Ingress direction:

Ethernet_pkt -> PacketRelay (parse pkt) --- EtherPayload(e.g IP_pkt) --> EtherConn
Ethernet_pkt -> PacketRelay (parse pkt) --- UDP_payload --> RUDPConn (option to accept any UDP pkt)

Note: PacketRelay parse pkt for Ethernet payload based on following rules:

  • PacketRelay has list of EtherTypes, by default are 0x0800 (IPv4) and 0x86dd (IPv6)
  • If Ethernet pkt doesn't have VLAN tag, EtherType in Ethernet header is used to see if the pkt contains the interested payload
  • else, EtherType in last VLAN tag is used

Limitations:

  • linux only
  • since etherconn bypassed linux IP routing stack, it is user's job to provide functions like:
    • routing next-hop lookup
    • IP -> MAC address resolution
  • no IP packet fragementation/reassembly support
  • using of etherconn requires to put interface in promiscuous mode, which requires root privileges

Documentation

Overview

Package etherconn is a golang pkg that allow user to send/receive Ethernet payload (like IP pkt) or UDP packet ,with custom Ethernet encapsulation like MAC address, VLAN tags, without creating corresponding interface in OS;

For example, with etherconn, a program could send/recive a UDP or IP packet with a source MAC address and VLAN tags don't exists/provisioned in any of OS interfaces;

Another benefit is since etherconn bypasses "normal" Linux kernel routing and IP stack, in scale setup like tens of thousands conns no longer subject to linux kernel limitation like # of socket/fd limitations, UDP buffer size...etc;

Lastly etherconn.RUDPConn implements the net.PacketConn interface, so it could be easily integrated into existing code;

Usage:

interface <---> PacketRelay <----> EtherConn <---> RUDPConn
                            <----> EtherConn <---> RUDPConn
                            <----> EtherConn <---> RUDPConn

1. Create a PacketRelay instance and bound to an interface.PacketRelay is the "forward engine" that does actual packet sending/receiving for all EtherConn instances registered with it; PacketRelay send/receive Ethernet packet

2. Create one EtherConn for each source MAC+VLAN(s) combination needed, and register with the PacketRelay instance. EtherConn send/receive Ethernet payload like IP packet;

3. Create one RUDPConn instance for each UDP endpoint (IP+Port) needed, with a EtherConn. RUDPConn send/receive UDP payload.

4. RUDPConn and EtherConn is 1:1 mapping, while EtherConn and PacketRelay is N:1 mapping; since EtherConn and RUDPConn is 1:1 mapping, which means EtherConn will forward all received UDP pkts to RUDPConn even when its IP/UDP port is different from RUDPConn's endpoint, and RUDPConn could either only accept correct pkt or accept any UDP packet;

Egress direction:

UDP_payload -> RUDPConn(add UDP&IP header) -> EtherConn(add Ethernet header) -> PacketRelay

Ingress direction:

Ethernet_pkt -> PacketRelay (parse pkt) --- EtherPayload(e.g IP_pkt) --> EtherConn
Ethernet_pkt -> PacketRelay (parse pkt) --- UDP_payload --> RUDPConn (option to accept any UDP pkt)

Note: PacketRelay parse pkt for Ethernet payload based on following rules: * PacketRelay has list of EtherTypes, by default are 0x0800 (IPv4) and 0x86dd (IPv6) * If Ethernet pkt doesn't have VLAN tag, EtherType in Ethernet header is used to see if the pkt contains the interested payload * else, EtherType in last VLAN tag is used

Limitations:

  • linux only
  • since etherconn bypassed linux IP routing stack, it is user's job to provide functions like:
  • routing next-hop lookup
  • IP -> MAC address resolution
  • no IP packet fragementation/reassembly support
  • using of etherconn requires to put interface in promiscuous mode, which requires root privileges

Example:

// This is an example of using RUDPConn, a DHCPv4 client
// it also uses "github.com/insomniacslk/dhcp/dhcpv4/nclient4" for dhcpv4 client part

// create PacketRelay for interface "enp0s10"
relay, err := etherconn.NewRawSocketRelay(context.Background(), "enp0s10")
if err != nil {
	log.Fatalf("failed to create PacketRelay,%v", err)
}
defer relay.Stop()
mac, _ := net.ParseMAC("aa:bb:cc:11:22:33")
vlanLlist := []*etherconn.VLAN{
	&etherconn.VLAN{
		ID:        100,
		EtherType: 0x8100,
	},
}
// create EtherConn, with src mac "aa:bb:cc:11:22:33" and VLAN 100,
// with DOT1Q EtherType 0x8100, the mac/vlan doesn't need to be provisioned in OS
econn := etherconn.NewEtherConn(mac, relay, etherconn.WithVLANs(vlanLlist))
// create RUDPConn to use 0.0.0.0 and UDP port 68 as source, with option to accept any UDP packet
// since DHCP server will send reply to assigned IP address
rudpconn, err := etherconn.NewRUDPConn("0.0.0.0:68", econn, etherconn.WithAcceptAny(true))
if err != nil {
	log.Fatalf("failed to create RUDPConn,%v", err)
}
// create DHCPv4 client with the RUDPConn
clnt, err := nclient4.NewWithConn(rudpconn, mac, nclient4.WithDebugLogger())
if err != nil {
	log.Fatalf("failed to create dhcpv4 client for %v", err)
}
// do DORA
_, _, err = clnt.Request(context.Background())
if err != nil {
	log.Fatalf("failed to finish DORA,%v", err)
}

Index

Constants

View Source
const (
	// DefaultSendChanDepth is the default value for PacketRelay send channel depth, e.g. send buffer
	DefaultSendChanDepth = 1024
	// DefaultPerClntRecvChanDepth is the defaul value for per registered client(EtherConn)'s receive channel depth. e.g. recv buffer
	DefaultPerClntRecvChanDepth = 1024
	// DefaultMaxEtherFrameSize is the deafult max size of Ethernet frame that PacketRelay could receive from the interface
	DefaultMaxEtherFrameSize = 2000
	// DefaultRelayRecvTimeout is the default value for PacketReceive receiving timeout
	DefaultRelayRecvTimeout = time.Second
)
View Source
const (
	// MaxNumVLAN specifies max number vlan this pkg supports
	MaxNumVLAN = 2
	// L2EndpointKeySize is the size of L2EndpointKey in bytes
	L2EndpointKeySize = 6 + 2*MaxNumVLAN //must be 6+2*n
)
View Source
const (
	// NOVLANTAG is the value to represents NO vlan tag in L2EndpointKey
	NOVLANTAG = 0xffff
)

Variables

View Source
var (
	// ErrTimeOut is the error returned when opeartion timeout
	ErrTimeOut = fmt.Errorf("timeout")
	// ErrRelayStopped is the error returned when relay already stopped
	ErrRelayStopped = fmt.Errorf("relay stopped")
	// BroadCastMAC is the broadcast MAC address
	BroadCastMAC = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
)

Functions

func ResolveNexhopMACWithBrodcast

func ResolveNexhopMACWithBrodcast(ip net.IP) net.HardwareAddr

ResolveNexhopMACWithBrodcast is the default resolve function that always return broadcast mac

Types

type EtherConn

type EtherConn struct {
	// contains filtered or unexported fields
}

EtherConn send/recv Ethernet payload like IP packet with customizable Ethernet encapsualtion like MAC and VLANs without provisioning them in OS. it needs to be registed with a PacketRelay instance to work.

func NewEtherConn

func NewEtherConn(mac net.HardwareAddr, relay PacketRelay, options ...EtherConnOption) *EtherConn

NewEtherConn creates a new EtherConn instance, mac is used as part of EtherConn's L2Endpoint; relay is the PacketRelay that EtherConn instance register with; options specifies EtherConnOption(s) to use;

func (*EtherConn) Close

func (ec *EtherConn) Close() error

Close implements net.PacketConn interface, deregister itself from PacketRelay

func (*EtherConn) LocalAddr

func (ec *EtherConn) LocalAddr() *L2Endpoint

LocalAddr return EtherConn's L2Endpoint

func (*EtherConn) ReadPkt

func (ec *EtherConn) ReadPkt() ([]byte, net.HardwareAddr, error)

ReadPkt return received Ethernet payload bytes with an already allocated byte slice; ReadPkt only return payload that matches one of underlying PacketRelay's configured EtherTypes

func (*EtherConn) ReadPktFrom

func (ec *EtherConn) ReadPktFrom(p []byte) (int, net.HardwareAddr, error)

ReadPktFrom copies the received Ethernet payload to p; it calls ReadPkt to get the payload, it return number bytes of IP packet, remote MAC address

func (*EtherConn) SetDeadline

func (ec *EtherConn) SetDeadline(t time.Time) error

SetDeadline implements net.PacketConn interface

func (*EtherConn) SetReadDeadline

func (ec *EtherConn) SetReadDeadline(t time.Time) error

SetReadDeadline implements net.PacketConn interface

func (*EtherConn) SetWriteDeadline

func (ec *EtherConn) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements net.PacketConn interface

func (*EtherConn) WriteIPPktTo

func (ec *EtherConn) WriteIPPktTo(p []byte, dstmac net.HardwareAddr) (int, error)

WriteIPPktTo sends an IPv4/IPv6 packet, the pkt will be sent to dstmac, along with EtherConn.L2EP.VLANs.

func (*EtherConn) WritePktTo

func (ec *EtherConn) WritePktTo(p []byte, etype uint16, dstmac net.HardwareAddr) (int, error)

WritePktTo sends an Ethernet payload, along with specified EtherType, the pkt will be sent to dstmac, along with EtherConn.L2EP.VLANs.

type EtherConnOption

type EtherConnOption func(ec *EtherConn)

EtherConnOption is a function use to provide customized option when creating EtherConn

func WithRecvMulticast added in v0.1.2

func WithRecvMulticast(recv bool) EtherConnOption

WithRecvMulticast allow/disallow EtherConn to receive multicast/broadcast Ethernet traffic

func WithVLANs

func WithVLANs(vlans VLANs) EtherConnOption

WithVLANs specifies VLAN(s) as part of EtherConn's L2Endpoint. by default, there is no VLAN.

type L2Endpoint

type L2Endpoint struct {
	HwAddr net.HardwareAddr
	VLANs  []uint16
}

L2Endpoint represents a layer2 endpoint that send/receives Ethernet frame

func NewL2EndpointFromMACVLAN

func NewL2EndpointFromMACVLAN(mac net.HardwareAddr, vlans VLANs) *L2Endpoint

NewL2EndpointFromMACVLAN creates a new L2Endpoint from mac and vlans

func (*L2Endpoint) GetKey

func (l2e *L2Endpoint) GetKey() (key L2EndpointKey)

GetKey returns the key of the L2Endpoint

func (*L2Endpoint) Network

func (l2e *L2Endpoint) Network() string

Network implements net.Addr interface, return "l2ep"

func (*L2Endpoint) String

func (l2e *L2Endpoint) String() (s string)

String implements net.Addr interface, return a string with format: l2ep&<l2EndpointKey_str>, see L2EndpointKey.String for format of <l2EndpointKey_str>

type L2EndpointKey

type L2EndpointKey [L2EndpointKeySize]byte

L2EndpointKey is key identify a L2EndPoint,first 6 bytes are MAC address, rest are VLAN Ids in order (from outside to inner), each VLAN id are two bytes in network endian, if VLAN id is NOVLANTAG, then it means no such tag

func (L2EndpointKey) String

func (l2epkey L2EndpointKey) String() string

type PacketRelay

type PacketRelay interface {
	// Register register a L2EndpointKey of a EtherConn, PacketRely send/recv pkt on its behalf,
	// it returns two channels:
	// recv is the channel used to recive;
	// send is the channel used to send;
	// stop is a channel that will be closed when PacketRelay stops sending;
	// if recvMulticast is true, then multicast ethernet traffic will be recvied as well
	Register(k L2EndpointKey, recvMulticast bool) (recv chan *RelayReceival, send chan []byte, stop chan struct{})
	// Deregister removes L2EndpointKey from registration
	Deregister(k L2EndpointKey)
	// Stop stops the forwarding of PacketRelay
	Stop()
	//IfName return binding interface name
	IfName() string
}

PacketRelay is a interface for the packet forwarding engine, RawSocketRelay implements this interface;

type RUDPConn

type RUDPConn struct {
	// contains filtered or unexported fields
}

RUDPConn implement net.PacketConn interface; it used to send/recv UDP payload, using a underlying EtherConn for pkt forwarding.

func NewRUDPConn

func NewRUDPConn(src string, c *EtherConn, options ...RUDPConnOption) (*RUDPConn, error)

NewRUDPConn creates a new RUDPConn, with specified EtherConn, and, optionally RUDPConnOption(s). src is the string represents its UDP Address as format supported by net.ResolveUDPAddr(). note the src UDP address could be any IP address, even address not provisioned in OS, like 0.0.0.0

func (*RUDPConn) Close

func (ruc *RUDPConn) Close() error

Close implements net.PacketConn interface, it closes underlying EtherConn

func (*RUDPConn) LocalAddr

func (ruc *RUDPConn) LocalAddr() net.Addr

LocalAddr implements net.PacketConn interface, it returns its UDPAddr

func (*RUDPConn) ReadFrom

func (ruc *RUDPConn) ReadFrom(p []byte) (int, net.Addr, error)

ReadFrom implements net.PacketConn interface, it copy UDP payload to p; note: the underlying EtherConn will send all received pkts as *RelayReceival to RUDPConn, RUDPConn will ignore pkts that is not destined to its UDPAddr, unless WithAcceptAny(true) is specified when creating the RUDPConn, in that case, RUDPConn will accept any UDP packet;

func (*RUDPConn) SetAddr

func (ruc *RUDPConn) SetAddr(src *net.UDPAddr)

SetAddr set RUDPConn's UDP address to src

func (*RUDPConn) SetDeadline

func (ruc *RUDPConn) SetDeadline(t time.Time) error

SetDeadline implements net.PacketConn interface

func (*RUDPConn) SetReadDeadline

func (ruc *RUDPConn) SetReadDeadline(t time.Time) error

SetReadDeadline implements net.PacketConn interface

func (*RUDPConn) SetWriteDeadline

func (ruc *RUDPConn) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements net.PacketConn interface

func (*RUDPConn) WriteTo

func (ruc *RUDPConn) WriteTo(p []byte, addr net.Addr) (int, error)

WriteTo implements net.PacketConn interface, it sends UDP payload; This function adds UDP and IP header, and uses RUDPConn's resolve function to get nexthop's MAC address, and use underlying EtherConn to send IP packet, with EtherConn's Ethernet encapsulation, to nexthop MAC address; by default ResolveNexhopMACWithBrodcast is used for nexthop mac resolvement

type RUDPConnOption

type RUDPConnOption func(rudpc *RUDPConn)

RUDPConnOption is a function use to provide customized option when creating RUDPConn

func WithAcceptAny

func WithAcceptAny(accept bool) RUDPConnOption

WithAcceptAny allows RUDPConn to accept any UDP pkts, even it is not destinated to its address

func WithResolveNextHopMacFunc

func WithResolveNextHopMacFunc(f func(net.IP) net.HardwareAddr) RUDPConnOption

WithResolveNextHopMacFunc specifies a function to resolve a destination IP address to next-hop MAC address; by default, ResolveNexhopMACWithBrodcast is used.

type RawSocketRelay

type RawSocketRelay struct {
	// contains filtered or unexported fields
}

RawSocketRelay implements PacketRelay interface

func NewRawSocketRelay

func NewRawSocketRelay(parentctx context.Context, ifname string, options ...RelayOption) (*RawSocketRelay, error)

NewRawSocketRelay creates a new RawSocketRelay instance, bound to the interface ifname, optionally along with RelayOption functions. This function will put the interface in promisc mode, which means it requires root privilage

func (*RawSocketRelay) Deregister

func (rsr *RawSocketRelay) Deregister(k L2EndpointKey)

Deregister implements PacketRelay interface;

func (*RawSocketRelay) GetStats

func (rsr *RawSocketRelay) GetStats() *RelayPacketStats

GetStats return pkt forwarding stats as *RelayPacketStats

func (*RawSocketRelay) IfName added in v0.1.3

func (rsr *RawSocketRelay) IfName() string

func (*RawSocketRelay) Register

func (rsr *RawSocketRelay) Register(k L2EndpointKey, recvMulticast bool) (chan *RelayReceival, chan []byte, chan struct{})

Register implements PacketRelay interface; if mac is already registered, then returns its corresponding channel.

func (*RawSocketRelay) Stop

func (rsr *RawSocketRelay) Stop()

Stop implements PacketRelay interface

type RelayOption

type RelayOption func(*RawSocketRelay)

RelayOption is a function use to provide customized option when creating RawSocketRelay

func WithBPFFilter added in v0.1.2

func WithBPFFilter(filter string) RelayOption

WithBPFFilter set BPF filter, which is a pcap filter string; if filter is an empty string, then it means no filter; by default, Relay will have a filter only allow traffic with specified EtherType.

func WithDebug

func WithDebug(debug bool) RelayOption

WithDebug enable/disable debug log output

func WithEtherType

func WithEtherType(etypes []uint16) RelayOption

WithEtherType specifies a list of EtherTypes need to be parsed; by default, only 0x0800 (IPv4) and 0x86dd (IPv6) are parsed;

func WithMaxEtherFrameSize

func WithMaxEtherFrameSize(size uint) RelayOption

WithMaxEtherFrameSize specifies the max Ethernet frame size the RawSocketRelay could receive

func WithPerClntChanRecvDepth

func WithPerClntChanRecvDepth(depth uint) RelayOption

WithPerClntChanRecvDepth specifies the per Client(EtherConn) receive channel depth, By default, DefaultPerClntRecvChanDepth is used

func WithRecvTimeout

func WithRecvTimeout(t time.Duration) RelayOption

WithRecvTimeout specifies the receive timeout for RawSocketRelay

func WithSendChanDepth

func WithSendChanDepth(depth uint) RelayOption

WithSendChanDepth specifies the send channel depth, by default, DefaultSendChanDepth is used

type RelayPacketStats

type RelayPacketStats struct {
	// Tx is number of pkts sent successfully
	Tx *uint64
	// RxOffered is number of pkts relay get from interface
	RxOffered *uint64
	// RxInvalid is nunber of pkts relay get but ignored due to failed valid check
	RxInvalid *uint64
	// RxBufferFull is the number of pkts can't send to receiver's channel right away due to it is full
	RxBufferFull *uint64
	// RxMiss is the number of pkts relay can't find receiver
	RxMiss *uint64
	// Rx is the number of pkts relay successfully deliver to receiver
	Rx *uint64
	// RxNonHitMulticast is the number of multicast pkts that doesn't have direct receiver, but deliver to a multicast recevier
	RxNonHitMulticast *uint64
	// RxMulticastIgnored is the number of multicast pkts ignored
	RxMulticastIgnored *uint64
}

RelayPacketStats is the PacketRelay's forwding stats; use atomic.LoadUint64 to read the values

func (RelayPacketStats) String

func (rps RelayPacketStats) String() string

type RelayReceival

type RelayReceival struct {
	//RemoteMAC is the remote MAC address
	RemoteMAC net.HardwareAddr
	// EtherBytes is the Ethernet frame bytes
	EtherBytes []byte
	// EtherPayloadBytes is the Ethernet payload bytes within the EtherBytes,
	// where payload belongs to the specified EtherTypes,
	// default are 0x0800 (IPv4) and 0x86dd (IPv6),
	// nil if there is no payload with specified EtherTypes;
	EtherPayloadBytes []byte
	// TransportPayloadBytes is the transport layer(UDP/TCP/SCTP) payload bytes within the IPBytes,nil for unsupport transport layer
	TransportPayloadBytes []byte
	// RemoteIP is the remote IP address
	RemoteIP net.IP
	// RemotePort is the remote transport layer port, 0 for unsupport transport layer
	RemotePort uint16
	// Protocol is the IP protocol number
	Protocol uint8
	// LocalIP is the local IP address
	LocalIP net.IP
	// LocalPort is the local transport layer port, 0 for unsupport transport layer
	LocalPort uint16
}

RelayReceival is the what PacketRelay received and parsed

type VLAN

type VLAN struct {
	ID        uint16
	EtherType uint16
}

VLAN reprents a VLAN tag

type VLANs added in v0.1.1

type VLANs []*VLAN

VLANs is a slice of VLAN

func (VLANs) Clone added in v0.1.1

func (vlans VLANs) Clone() VLANs

Clone return a deep copy

func (*VLANs) Copy added in v0.1.1

func (vlans *VLANs) Copy(vlans2 VLANs)

Copy copy value of lvans2 to vlans

func (VLANs) Equal added in v0.1.1

func (vlans VLANs) Equal(vlans2 VLANs) bool

Equal returns true if vlans == vlans2

func (VLANs) IDs added in v0.1.1

func (vlans VLANs) IDs() []uint16

IDs return a VLAN IDs as a slice of uint16

func (VLANs) SetIDs added in v0.1.1

func (vlans VLANs) SetIDs(ids []uint16) error

SetIDs set VLAN ID with the specified uint16 slice

func (VLANs) String added in v0.1.1

func (vlans VLANs) String() string

String return a string representation

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL