diff --git a/if.go b/if.go new file mode 100644 index 0000000..229d141 --- /dev/null +++ b/if.go @@ -0,0 +1,71 @@ +package water + +import ( + "os" +) + +// Interface is a TUN/TAP interface. +type Interface struct { + isTAP bool + file *os.File + name string +} + +// Create a new TAP interface whose name is ifName. +// If ifName is empty, a default name (tap0, tap1, ... ) will be assigned. +// ifName should not exceed 16 bytes. +func NewTAP(ifName string) (ifce *Interface, err error) { + file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0) + if err != nil { + return nil, err + } + name, err := createInterface(file.Fd(), ifName, cIFF_TAP|cIFF_NO_PI) + if err != nil { + return nil, err + } + ifce = &Interface{isTAP: true, file: file, name: name} + return +} + +// Create a new TUN interface whose name is ifName. +// If ifName is empty, a default name (tap0, tap1, ... ) will be assigned. +// ifName should not exceed 16 bytes. +func NewTUN(ifName string) (ifce *Interface, err error) { + file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0) + if err != nil { + return nil, err + } + name, err := createInterface(file.Fd(), ifName, cIFF_TUN|cIFF_NO_PI) + if err != nil { + return nil, err + } + ifce = &Interface{isTAP: false, file: file, name: name} + return +} + +// Returns true if ifce is a TUN interface, otherwise returns false; +func (ifce *Interface) IsTUN() bool { + return !ifce.isTAP +} + +// Returns true if ifce is a TAP interface, otherwise returns false; +func (ifce *Interface) IsTAP() bool { + return ifce.isTAP +} + +// Returns the interface name of ifce, e.g. tun0, tap1, etc.. +func (ifce *Interface) Name() string { + return ifce.name +} + +// Implement io.Writer interface. +func (ifce *Interface) Write(p []byte) (n int, err error) { + n, err = ifce.file.Write(p) + return +} + +// Implement io.Reader interface. +func (ifce *Interface) Read(p []byte) (n int, err error) { + n, err = ifce.file.Read(p) + return +} diff --git a/syscalls_linux.go b/syscalls_linux.go new file mode 100644 index 0000000..6bf6c57 --- /dev/null +++ b/syscalls_linux.go @@ -0,0 +1,34 @@ +// +build linux + +package water + +import ( + "strings" + "syscall" + "unsafe" +) + +const ( + cIFF_TUN = 0x0001 + cIFF_TAP = 0x0002 + cIFF_NO_PI = 0x1000 +) + +type ifReq struct { + Name [0x10]byte + Flags uint16 + pad [0x28 - 0x10 - 2]byte +} + +func createInterface(fd uintptr, ifName string, flags uint16) (createdIFName string, err error) { + var req ifReq + req.Flags = flags + copy(req.Name[:], ifName) + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req))) + if errno != 0 { + err = errno + return + } + createdIFName = strings.Trim(string(req.Name[:]), "\x00") + return +} diff --git a/waterutil/ethertypes.go b/waterutil/ethertypes.go new file mode 100644 index 0000000..1d2b1aa --- /dev/null +++ b/waterutil/ethertypes.go @@ -0,0 +1,46 @@ +package waterutil + +type Ethertype [2]byte + +// Ethertype values. From: http://en.wikipedia.org/wiki/Ethertype +var ( + IPv4 = Ethertype{0x08, 0x00} + ARP = Ethertype{0x08, 0x06} + WakeOnLAN = Ethertype{0x08, 0x42} + TRILL = Ethertype{0x22, 0xF3} + DECnetPhase4 = Ethertype{0x60, 0x03} + RARP = Ethertype{0x80, 0x35} + AppleTalk = Ethertype{0x80, 0x9B} + AARP = Ethertype{0x80, 0xF3} + IPX1 = Ethertype{0x81, 0x37} + IPX2 = Ethertype{0x81, 0x38} + QNXQnet = Ethertype{0x82, 0x04} + IPv6 = Ethertype{0x86, 0xDD} + EthernetFlowControl = Ethertype{0x88, 0x08} + IEEE802_3 = Ethertype{0x88, 0x09} + CobraNet = Ethertype{0x88, 0x19} + MPLSUnicast = Ethertype{0x88, 0x47} + MPLSMulticast = Ethertype{0x88, 0x48} + PPPoEDiscovery = Ethertype{0x88, 0x63} + PPPoESession = Ethertype{0x88, 0x64} + JumboFrames = Ethertype{0x88, 0x70} + HomePlug1_0MME = Ethertype{0x88, 0x7B} + IEEE802_1X = Ethertype{0x88, 0x8E} + PROFINET = Ethertype{0x88, 0x92} + HyperSCSI = Ethertype{0x88, 0x9A} + AoE = Ethertype{0x88, 0xA2} + EtherCAT = Ethertype{0x88, 0xA4} + EthernetPowerlink = Ethertype{0x88, 0xAB} + LLDP = Ethertype{0x88, 0xCC} + SERCOS3 = Ethertype{0x88, 0xCD} + HomePlugAVMME = Ethertype{0x88, 0xE1} + MRP = Ethertype{0x88, 0xE3} + IEEE802_1AE = Ethertype{0x88, 0xE5} + IEEE1588 = Ethertype{0x88, 0xF7} + IEEE802_1ag = Ethertype{0x89, 0x02} + FCoE = Ethertype{0x89, 0x06} + FCoEInit = Ethertype{0x89, 0x14} + RoCE = Ethertype{0x89, 0x15} + CTP = Ethertype{0x90, 0x00} + VeritasLLT = Ethertype{0xCA, 0xFE} +) diff --git a/waterutil/ip_protocols.go b/waterutil/ip_protocols.go new file mode 100644 index 0000000..2fa1cbe --- /dev/null +++ b/waterutil/ip_protocols.go @@ -0,0 +1,139 @@ +package waterutil + +type IPProtocol byte + +// IP Protocols. From: http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers +const ( + HOPOPT = 0x00 + ICMP = 0x01 + IGMP = 0x02 + GGP = 0x03 + IPv4Encapsulation = 0x04 + ST = 0x05 + TCP = 0x06 + CBT = 0x07 + EGP = 0x08 + IGP = 0x09 + BBN_RCC_MON = 0x0A + NVP_II = 0x0B + PUP = 0x0C + ARGUS = 0x0D + EMCON = 0x0E + XNET = 0x0F + CHAOS = 0x10 + UDP = 0x11 + MUX = 0x12 + DCN_MEAS = 0x13 + HMP = 0x14 + PRM = 0x15 + XNS_IDP = 0x16 + TRUNK_1 = 0x17 + TRUNK_2 = 0x18 + LEAF_1 = 0x19 + LEAF_2 = 0x1A + RDP = 0x1B + IRTP = 0x1C + ISO_TP4 = 0x1D + NETBLT = 0x1E + MFE_NSP = 0x1F + MERIT_INP = 0x20 + DCCP = 0x21 + ThirdPC = 0x22 + IDPR = 0x23 + XTP = 0x24 + DDP = 0x25 + IDPR_CMTP = 0x26 + TPxx = 0x27 + IL = 0x28 + IPv6Encapsulation = 0x29 + SDRP = 0x2A + IPv6_Route = 0x2B + IPv6_Frag = 0x2C + IDRP = 0x2D + RSVP = 0x2E + GRE = 0x2F + MHRP = 0x30 + BNA = 0x31 + ESP = 0x32 + AH = 0x33 + I_NLSP = 0x34 + SWIPE = 0x35 + NARP = 0x36 + MOBILE = 0x37 + TLSP = 0x38 + SKIP = 0x39 + IPv6_ICMP = 0x3A + IPv6_NoNxt = 0x3B + IPv6_Opts = 0x3C + CFTP = 0x3E + SAT_EXPAK = 0x40 + KRYPTOLAN = 0x41 + RVD = 0x42 + IPPC = 0x43 + SAT_MON = 0x45 + VISA = 0x46 + IPCV = 0x47 + CPNX = 0x48 + CPHB = 0x49 + WSN = 0x4A + PVP = 0x4B + BR_SAT_MON = 0x4C + SUN_ND = 0x4D + WB_MON = 0x4E + WB_EXPAK = 0x4F + ISO_IP = 0x50 + VMTP = 0x51 + SECURE_VMTP = 0x52 + VINES = 0x53 + TTP = 0x54 + IPTM = 0x54 + NSFNET_IGP = 0x55 + DGP = 0x56 + TCF = 0x57 + EIGRP = 0x58 + OSPF = 0x59 + Sprite_RPC = 0x5A + LARP = 0x5B + MTP = 0x5C + AX_25 = 0x5D + IPIP = 0x5E + MICP = 0x5F + SCC_SP = 0x60 + ETHERIP = 0x61 + ENCAP = 0x62 + GMTP = 0x64 + IFMP = 0x65 + PNNI = 0x66 + PIM = 0x67 + ARIS = 0x68 + SCPS = 0x69 + QNX = 0x6A + A_N = 0x6B + IPComp = 0x6C + SNP = 0x6D + Compaq_Peer = 0x6E + IPX_in_IP = 0x6F + VRRP = 0x70 + PGM = 0x71 + L2TP = 0x73 + DDX = 0x74 + IATP = 0x75 + STP = 0x76 + SRP = 0x77 + UTI = 0x78 + SMP = 0x79 + SM = 0x7A + PTP = 0x7B + FIRE = 0x7D + CRTP = 0x7E + CRUDP = 0x7F + SSCOPMCE = 0x80 + IPLT = 0x81 + SPS = 0x82 + PIPE = 0x83 + SCTP = 0x84 + FC = 0x85 + manet = 0x8A + HIP = 0x8B + Shim6 = 0x8C +) diff --git a/waterutil/tap.go b/waterutil/tap.go new file mode 100644 index 0000000..d81da37 --- /dev/null +++ b/waterutil/tap.go @@ -0,0 +1,40 @@ +package waterutil + +import ( + "net" +) + +type Tagging int + +// Indicating whether/how a MAC frame is tagged. The value is number of bytes taken by tagging. +const ( + NotTagged Tagging = 0 + Tagged Tagging = 4 + DoubleTagged Tagging = 8 +) + +func MACDestination(macFrame []byte) net.HardwareAddr { + return net.HardwareAddr(macFrame[:6]) +} + +func MACSource(macFrame []byte) net.HardwareAddr { + return net.HardwareAddr(macFrame[6:12]) +} + +func MACTagging(macFrame []byte) Tagging { + if macFrame[12] == 0x81 && macFrame[13] == 0x00 { + return Tagged + } else if macFrame[12] == 0x88 && macFrame[13] == 0xa8 { + return DoubleTagged + } + return NotTagged +} + +func MACEthertype(macFrame []byte) Ethertype { + ethertypePos := 12 + MACTagging(macFrame) + return Ethertype{macFrame[ethertypePos], macFrame[ethertypePos+1]} +} + +func MACPayload(macFrame []byte) []byte { + return macFrame[12+MACTagging(macFrame)+2:] +} diff --git a/waterutil/tun.go b/waterutil/tun.go new file mode 100644 index 0000000..a972e8e --- /dev/null +++ b/waterutil/tun.go @@ -0,0 +1,9 @@ +package waterutil + +func IsIPv4(packet []byte) bool { + return 4 == (packet[0] >> 4) +} + +func IsIPv6(packet []byte) bool { + return 6 == (packet[0] >> 4) +} diff --git a/waterutil/tun_ipv4.go b/waterutil/tun_ipv4.go new file mode 100644 index 0000000..6878174 --- /dev/null +++ b/waterutil/tun_ipv4.go @@ -0,0 +1,38 @@ +package waterutil + +import ( + "net" +) + +func IPv4DSCP(packet []byte) byte { + return packet[1] >> 2 +} + +func IPv4ECN(packet []byte) byte { + return packet[1] & 0x03 +} + +func IPv4Identification(packet []byte) [2]byte { + return [2]byte{packet[4], packet[5]} +} + +func IPv4TTL(packet []byte) byte { + return packet[8] +} + +func IPv4Protocol(packet []byte) IPProtocol { + return IPProtocol(packet[9]) +} + +func IPv4Source(packet []byte) net.IP { + return net.IPv4(packet[12], packet[13], packet[14], packet[15]) +} + +func IPv4Destination(packet []byte) net.IP { + return net.IPv4(packet[16], packet[17], packet[18], packet[19]) +} + +func IPv4Payload(packet []byte) []byte { + ihl := packet[0] & 0x0F + return packet[ihl*4:] +}