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{ ðerconn.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
- Variables
- func ResolveNexhopMACWithBrodcast(ip net.IP) net.HardwareAddr
- type EtherConn
- func (ec *EtherConn) Close() error
- func (ec *EtherConn) LocalAddr() *L2Endpoint
- func (ec *EtherConn) ReadPkt() ([]byte, net.HardwareAddr, error)
- func (ec *EtherConn) ReadPktFrom(p []byte) (int, net.HardwareAddr, error)
- func (ec *EtherConn) SetDeadline(t time.Time) error
- func (ec *EtherConn) SetReadDeadline(t time.Time) error
- func (ec *EtherConn) SetWriteDeadline(t time.Time) error
- func (ec *EtherConn) WriteIPPktTo(p []byte, dstmac net.HardwareAddr) (int, error)
- func (ec *EtherConn) WritePktTo(p []byte, etype uint16, dstmac net.HardwareAddr) (int, error)
- type EtherConnOption
- type L2Endpoint
- type L2EndpointKey
- type PacketRelay
- type RUDPConn
- func (ruc *RUDPConn) Close() error
- func (ruc *RUDPConn) LocalAddr() net.Addr
- func (ruc *RUDPConn) ReadFrom(p []byte) (int, net.Addr, error)
- func (ruc *RUDPConn) SetAddr(src *net.UDPAddr)
- func (ruc *RUDPConn) SetDeadline(t time.Time) error
- func (ruc *RUDPConn) SetReadDeadline(t time.Time) error
- func (ruc *RUDPConn) SetWriteDeadline(t time.Time) error
- func (ruc *RUDPConn) WriteTo(p []byte, addr net.Addr) (int, error)
- type RUDPConnOption
- type RawSocketRelay
- func (rsr *RawSocketRelay) Deregister(k L2EndpointKey)
- func (rsr *RawSocketRelay) GetStats() *RelayPacketStats
- func (rsr *RawSocketRelay) IfName() string
- func (rsr *RawSocketRelay) Register(k L2EndpointKey, recvMulticast bool) (chan *RelayReceival, chan []byte, chan struct{})
- func (rsr *RawSocketRelay) Stop()
- type RelayOption
- func WithBPFFilter(filter string) RelayOption
- func WithDebug(debug bool) RelayOption
- func WithEtherType(etypes []uint16) RelayOption
- func WithMaxEtherFrameSize(size uint) RelayOption
- func WithPerClntChanRecvDepth(depth uint) RelayOption
- func WithRecvTimeout(t time.Duration) RelayOption
- func WithSendChanDepth(depth uint) RelayOption
- type RelayPacketStats
- type RelayReceival
- type VLAN
- type VLANs
Constants ¶
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 )
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 )
const (
// NOVLANTAG is the value to represents NO vlan tag in L2EndpointKey
NOVLANTAG = 0xffff
)
Variables ¶
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 ¶
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 ¶
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 ¶
SetDeadline implements net.PacketConn interface
func (*EtherConn) SetReadDeadline ¶
SetReadDeadline implements net.PacketConn interface
func (*EtherConn) SetWriteDeadline ¶
SetWriteDeadline implements net.PacketConn interface
func (*EtherConn) WriteIPPktTo ¶
WriteIPPktTo sends an IPv4/IPv6 packet, the pkt will be sent to dstmac, along with EtherConn.L2EP.VLANs.
func (*EtherConn) WritePktTo ¶
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) ReadFrom ¶
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) SetDeadline ¶
SetDeadline implements net.PacketConn interface
func (*RUDPConn) SetReadDeadline ¶
SetReadDeadline implements net.PacketConn interface
func (*RUDPConn) SetWriteDeadline ¶
SetWriteDeadline implements net.PacketConn interface
func (*RUDPConn) WriteTo ¶
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 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 VLANs ¶ added in v0.1.1
type VLANs []*VLAN
VLANs is a slice of VLAN