├── .gitignore ├── LICENSE ├── README.md ├── config.go ├── encryption.go ├── encryption_cbc.go ├── encryption_hmac.go ├── encryption_none.go ├── go.mod ├── go.sum ├── iface_darwin.go ├── iface_linux.go ├── lcvpn.service ├── main.go ├── netlink ├── LICENSE ├── MAINTAINERS ├── netlink.go ├── netlink_linux.go ├── netlink_linux_armppc64.go ├── netlink_linux_notarm.go ├── netlink_unsupported.go └── note.txt ├── packet.go ├── packet_test.go └── topology.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2017 Anton Skorokhod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LCVPN - Light decentralized VPN in golang 2 | 3 | Originally this repo was just an answer on a question "how much time it'll take to write my own simple VPN in golang" (answer is about 3 hours for first prototype), but now it used in production in different environments. 4 | 5 | So, LCVPN is 6 | - Very light and easy (one similar config on all hosts) 7 | - Use same config for all hosts (autedetect local params) - useful with puppet etc 8 | - Uses AES-128, AES-192 or AES-256 encryption (note that AES-256 is **much slower** than AES-128 on most computers) + optional HMAC-SHA256 or (super secure! 😅 ) NONE encryption (just copy without modification) 9 | - Communicates via UDP directly to selected host (no central server) 10 | - Works only on Linux (uses TUN device) 11 | - Support of basic routing - can be used to connect several networks 12 | - Multithread send and receive - scaleable for big traffc 13 | - Due to use so_reuseport better result in case of bigger number of hosts 14 | - It's still in beta stage, use it on your own risk (and please use only versions marked as "release") 15 | 16 | ![alt tag](https://raw.githubusercontent.com/kanocz/lcvpn/master/topology.png) 17 | 18 | ### Install and run 19 | 20 | You need golang (at least 1.5) installed and configured: 21 | 22 | ```sh 23 | $ go get -u github.com/kanocz/lcvpn 24 | ``` 25 | 26 | if you have config in /etc/lcvpn.conf 27 | 28 | ```sh 29 | $ sudo $GOPATH/bin/lcvpn 30 | ``` 31 | 32 | if you want to specify different location of config (or if you need to run several instances) 33 | 34 | ```sh 35 | $ sudo $GOPATH/bin/lcvpn -config lcvpn.conf 36 | ``` 37 | if you host is hidden behind firewall (with udp port forward) lcvpn is unable to detect 38 | which "remote" is localhost. In this case use next syntax: 39 | 40 | ```sh 41 | $ sudo $GOPATH/bin/lcvpn -local berlin -config lcvpn.conf 42 | ``` 43 | 44 | 45 | ### Config example 46 | 47 | ``` 48 | [main] 49 | port = 23456 50 | encryption = aescbc 51 | mainkey = 4A34E352D7C32FC42F1CEB0CAA54D40E9D1EEDAF14EBCBCECA429E1B2EF72D21 52 | altkey = 1111111117C32FC42F1CEB0CAA54D40E9D1EEDAF14EBCBCECA429E1B2EF72D21 53 | broadcast = 192.168.3.255 54 | netcidr = 24 55 | recvThreads = 4 56 | sendThreads = 4 57 | 58 | [remote "prague"] 59 | ExtIP = 46.234.105.229 60 | LocIP = 192.168.3.15 61 | route = 192.168.10.0/24 62 | route = 192.168.15.0/24 63 | route = 192.168.20.0/24 64 | 65 | [remote "berlin"] 66 | ExtIP = 103.224.182.245 67 | LocIP = 192.168.3.8 68 | route = 192.168.11.0/24 69 | 70 | [remote "kiev"] 71 | ExtIP = 95.168.211.37 72 | LocIP = 192.168.3.3 73 | ``` 74 | 75 | where port is UDP port for communication 76 | encryption is *aescbc* for AES-CBC, *aescbchmac* for AES-CBC+HMAC-SHA245 or *none* for no encryption 77 | for *aescbc* mainkey/altkey is hex form of 16, 24 or 32 bytes key (for AES-128, AES-192 or AES-256) 78 | for *aescbchmac* mainkey/altkey is 32 bytes longer 79 | for *none* mainkey/altkey mainkey/altkey is just ignored 80 | number of remotes is virtualy unlimited, each takes about 256 bytes in memory 81 | 82 | ### Config reload 83 | 84 | Config is reloaded on HUP signal. In case of invalid config just log message will appeared, previous one is used. 85 | P.S.: listening udp socket is not reopened for now, so on port change restart is needed 86 | 87 | ### Online key change 88 | 89 | **altkey** configuration option allows specify alternative encryption key that will be used in case if decription with primary 90 | one failed. This allow to use next algoritm to change keys without link going offline: 91 | - In normal state only **mainkey** is set (setting altkey is more cpu-consuming) 92 | - Set altkey to new key on all hosts and send HUP signal 93 | - Exchange altkey and aeskey on all hosts and send HUP signal 94 | - Remove altkey (with old key) from configs on all hosts and send HUP signal again 95 | - We are running with new key :) 96 | 97 | ### Roadmap 98 | 99 | * 100% unit test coverage 100 | * please let me know if you need anything more 101 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | "os" 10 | "os/signal" 11 | "strings" 12 | "sync/atomic" 13 | "syscall" 14 | 15 | "gopkg.in/gcfg.v1" 16 | ) 17 | 18 | // VPNState represents config mixed with pre-parsed values 19 | type VPNState struct { 20 | Main struct { 21 | Port int 22 | MainKey string 23 | AltKey string 24 | Encryption string 25 | Broadcast string 26 | NetCIDR int 27 | RecvThreads int 28 | SendThreads int 29 | 30 | // filled by readConfig 31 | bcastIP [4]byte 32 | main PacketEncrypter 33 | alt PacketEncrypter 34 | local string 35 | } 36 | Remote map[string]*struct { 37 | ExtIP string 38 | LocIP string 39 | Route []string 40 | } 41 | // filled by readConfig 42 | remotes map[[4]byte]*net.UDPAddr 43 | routes map[*net.IPNet]*net.UDPAddr 44 | } 45 | 46 | var ( 47 | configfile = flag.String("config", "/etc/lcvpn.conf", "Config file") 48 | local = flag.String("local", "", 49 | "ID from \"remotes\" which idtenify this host [default: autodetect]") 50 | config atomic.Value 51 | ) 52 | 53 | func getLocalIPsMap() map[string]bool { 54 | result := map[string]bool{} 55 | 56 | ipnetlist, err := net.InterfaceAddrs() 57 | if nil != err { 58 | return result 59 | } 60 | 61 | for _, _ipnet := range ipnetlist { 62 | if ipnet, ok := _ipnet.(*net.IPNet); ok { 63 | result[ipnet.IP.String()] = true 64 | } 65 | } 66 | 67 | return result 68 | } 69 | 70 | func readConfig() error { 71 | var newConfig VPNState 72 | 73 | err := gcfg.ReadFileInto(&newConfig, *configfile) 74 | if nil != err { 75 | return fmt.Errorf("Error reading config \"%s\" %s", *configfile, err) 76 | } 77 | if newConfig.Main.Port < 1 || newConfig.Main.Port > 65535 { 78 | return errors.New("main.port is invalid in config") 79 | } 80 | if newConfig.Main.NetCIDR < 8 || newConfig.Main.NetCIDR > 30 { 81 | return errors.New("netCIDR can't be less than 8 or greater than 30") 82 | } 83 | 84 | if "" == newConfig.Main.Encryption { 85 | return errors.New("main.encryption is empty") 86 | } 87 | newEFunc, ok := registeredEncrypters[strings.ToLower(newConfig.Main.Encryption)] 88 | if !ok { 89 | return fmt.Errorf( 90 | "main.encryption type \"%s\" is unknown", 91 | newConfig.Main.Encryption) 92 | } 93 | 94 | newConfig.Main.main, err = newEFunc(newConfig.Main.MainKey) 95 | if nil != err { 96 | return fmt.Errorf("main.mainkey error: %s", err.Error()) 97 | } 98 | 99 | if "" != newConfig.Main.AltKey { 100 | newConfig.Main.alt, err = newEFunc(newConfig.Main.AltKey) 101 | if nil != err { 102 | return fmt.Errorf("main.altkey error: %s", err.Error()) 103 | } 104 | } 105 | 106 | // local ip detect or select 107 | if "" != *local { 108 | host, ok := newConfig.Remote[*local] 109 | if !ok { 110 | return fmt.Errorf( 111 | "Remote with id \"%s\" not found in %s", 112 | *local, *configfile) 113 | } 114 | newConfig.Main.local = fmt.Sprintf("%s/%d", 115 | host.LocIP, newConfig.Main.NetCIDR) 116 | 117 | // we don't need it in routes and so on 118 | delete(newConfig.Remote, *local) 119 | } else { 120 | ips := getLocalIPsMap() 121 | for name, r := range newConfig.Remote { 122 | if _, ok := ips[r.ExtIP]; ok { 123 | newConfig.Main.local = fmt.Sprintf("%s/%d", r.LocIP, newConfig.Main.NetCIDR) 124 | log.Printf("%s (%s) is detected as local ip\n", newConfig.Main.local, name) 125 | // we don't need it in routes and so on 126 | delete(newConfig.Remote, name) 127 | break 128 | } 129 | } 130 | if "" == newConfig.Main.local { 131 | return errors.New("Local ip can't be detected") 132 | } 133 | } 134 | 135 | newConfig.remotes = make(map[[4]byte]*net.UDPAddr, len(newConfig.Remote)) 136 | newConfig.routes = map[*net.IPNet]*net.UDPAddr{} 137 | 138 | for name, r := range newConfig.Remote { 139 | 140 | rmtAddr, err := net.ResolveUDPAddr("udp", 141 | fmt.Sprintf("%s:%d", r.ExtIP, newConfig.Main.Port)) 142 | if nil != err { 143 | return err 144 | } 145 | 146 | tIP := net.ParseIP(r.LocIP) 147 | if nil == tIP { 148 | log.Fatalln("Invalid local ip", r.LocIP, "for server", name) 149 | } 150 | 151 | newConfig.remotes[[4]byte{tIP[12], tIP[13], tIP[14], tIP[15]}] = rmtAddr 152 | 153 | for _, routestr := range r.Route { 154 | _, route, err := net.ParseCIDR(routestr) 155 | if nil != err { 156 | return fmt.Errorf("Invalid route %s for %s", routestr, name) 157 | } 158 | newConfig.routes[route] = rmtAddr 159 | } 160 | } 161 | 162 | bIP := net.ParseIP(newConfig.Main.Broadcast) 163 | if nil != bIP { 164 | newConfig.Main.bcastIP = [4]byte{bIP[12], bIP[13], bIP[14], bIP[15]} 165 | } 166 | 167 | if newConfig.Main.RecvThreads < 1 { 168 | newConfig.Main.RecvThreads = 1 169 | } 170 | 171 | if newConfig.Main.SendThreads < 1 { 172 | newConfig.Main.SendThreads = 1 173 | } 174 | 175 | config.Store(newConfig) 176 | 177 | return nil 178 | } 179 | 180 | func initConfig(routeReload chan bool) { 181 | err := readConfig() 182 | if nil != err { 183 | log.Fatalln("Error loading config:", err) 184 | } 185 | routeReload <- true 186 | 187 | // setup reloading on HUP signal 188 | c := make(chan os.Signal, 1) 189 | signal.Notify(c, syscall.SIGHUP) 190 | go func() { 191 | for range c { 192 | err := readConfig() 193 | if nil != err { 194 | log.Println("Config reload failed:", err) 195 | } else { 196 | log.Println("Config reloaded") 197 | routeReload <- true 198 | } 199 | } 200 | }() 201 | } 202 | -------------------------------------------------------------------------------- /encryption.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // PacketEncrypter represents wrapper for encryption alg 8 | type PacketEncrypter interface { 9 | Encrypt(input []byte, output []byte, iv []byte) int 10 | Decrypt(input []byte, output []byte) (int, error) 11 | 12 | CheckSize(size int) bool 13 | AdjustInputSize(size int) int 14 | 15 | // OutputAdd returns number of bytes will be added after encryption 16 | OutputAdd() int 17 | 18 | // IVLen returns bytes needed to store IV or other state 19 | IVLen() int 20 | } 21 | 22 | type newEncrypterFunc func(string) (PacketEncrypter, error) 23 | 24 | var ( 25 | registeredEncrypters = make(map[string]newEncrypterFunc) 26 | 27 | // predefined errors 28 | ePacketSmall = errors.New("Packet too small") 29 | ePacketNonIPv4 = errors.New("Non IPv4 packet") 30 | ePacketInvalidSize = errors.New("Stored packet size bigger then packet itself") 31 | ) 32 | 33 | func DecryptV4Chk(e PacketEncrypter, src []byte, dst []byte) (int, error) { 34 | num, err := e.Decrypt(src, dst) 35 | if nil != err { 36 | return 0, err 37 | } 38 | 39 | // 2 bytes size + 20 bytes ip header 40 | if num < 22 { 41 | return 0, ePacketSmall 42 | } 43 | 44 | if (*IPPacket)(&dst).IPver() != 4 { 45 | return 0, ePacketNonIPv4 46 | } 47 | 48 | size := (*IPPacket)(&dst).GetSize() 49 | if size > num { 50 | return 0, ePacketInvalidSize 51 | } 52 | 53 | return size, nil 54 | } 55 | -------------------------------------------------------------------------------- /encryption_cbc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "encoding/hex" 7 | "errors" 8 | ) 9 | 10 | // aescbc implements plain AES-CBC encryption-decryption 11 | type aescbc struct { 12 | c cipher.Block 13 | } 14 | 15 | func newAesCbc(key string) (PacketEncrypter, error) { 16 | 17 | if "" == key { 18 | return nil, errors.New("key is empty") 19 | } 20 | 21 | bkey, err := hex.DecodeString(key) 22 | if nil != err { 23 | return nil, errors.New("not valid hex string") 24 | } 25 | 26 | if (len(bkey) != 16) && (len(bkey) != 24) && (len(bkey) != 32) { 27 | return nil, errors.New(`Length of key must be 16, 24 or 32 bytes 28 | (32, 48 or 64 hex symbols) 29 | to select AES-128, AES-192 or AES-256`) 30 | } 31 | 32 | a := aescbc{} 33 | a.c, err = aes.NewCipher(bkey) 34 | if nil != err { 35 | return nil, err 36 | } 37 | 38 | return &a, nil 39 | } 40 | 41 | func (a *aescbc) CheckSize(size int) bool { 42 | return size > aes.BlockSize && size%aes.BlockSize == 0 43 | } 44 | 45 | func (a *aescbc) AdjustInputSize(size int) int { 46 | if size%aes.BlockSize != 0 { 47 | return size + (aes.BlockSize - (size % aes.BlockSize)) 48 | } 49 | return size 50 | } 51 | 52 | func (a *aescbc) Encrypt(input []byte, output []byte, iv []byte) int { 53 | copy(output[:aes.BlockSize], iv) 54 | cipher.NewCBCEncrypter(a.c, iv).CryptBlocks(output[aes.BlockSize:], input) 55 | 56 | inputLen := len(input) 57 | // whole len of output is len(input) + aes.BlockSize, 58 | // so copy of last aes.BlockSize 59 | copy(iv, output[inputLen:]) 60 | return inputLen + aes.BlockSize 61 | } 62 | 63 | func (a *aescbc) Decrypt(input []byte, output []byte) (int, error) { 64 | resultLen := len(input) - aes.BlockSize 65 | cipher.NewCBCDecrypter(a.c, input[:aes.BlockSize]). 66 | CryptBlocks(output, input[aes.BlockSize:]) 67 | return resultLen, nil 68 | } 69 | 70 | func (a *aescbc) OutputAdd() int { 71 | // adding IV to each message 72 | return aes.BlockSize 73 | } 74 | 75 | func (a *aescbc) IVLen() int { 76 | return aes.BlockSize 77 | } 78 | 79 | func init() { 80 | registeredEncrypters["aescbc"] = newAesCbc 81 | } 82 | -------------------------------------------------------------------------------- /encryption_hmac.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/hmac" 7 | "crypto/sha256" 8 | "encoding/hex" 9 | "errors" 10 | "hash" 11 | ) 12 | 13 | // aescbchmac implements plain AES-CBC+HMAC encryption-decryption 14 | type aescbchmac struct { 15 | c cipher.Block 16 | h hash.Hash 17 | hashsize int 18 | } 19 | 20 | var ( 21 | HMACError = errors.New("HMAC validation failed") 22 | ) 23 | 24 | func newAesCbcHmac(key string) (PacketEncrypter, error) { 25 | 26 | if "" == key { 27 | return nil, errors.New("key is empty") 28 | } 29 | 30 | bkey, err := hex.DecodeString(key) 31 | if nil != err { 32 | return nil, errors.New("not valid hex string") 33 | } 34 | 35 | lbkey := len(bkey) 36 | if (lbkey != 48) && (lbkey != 56) && (lbkey != 64) { 37 | return nil, errors.New(`Length of key must be 48, 56 or 64 bytes 38 | (96, 112 or 128 hex symbols) 39 | to select AES-128, AES-192 or AES-256 (+ 32 for HMAC sha256 key)`) 40 | } 41 | 42 | a := aescbchmac{} 43 | a.c, err = aes.NewCipher(bkey[0 : lbkey-32]) 44 | if nil != err { 45 | return nil, err 46 | } 47 | a.h = hmac.New(sha256.New, bkey[lbkey-32:]) 48 | 49 | a.hashsize = a.h.Size() 50 | 51 | return &a, nil 52 | } 53 | 54 | func (a *aescbchmac) CheckSize(size int) bool { 55 | return size > (aes.BlockSize+a.hashsize) && size%aes.BlockSize == 0 56 | } 57 | 58 | func (a *aescbchmac) AdjustInputSize(size int) int { 59 | if size%aes.BlockSize != 0 { 60 | return size + (aes.BlockSize - (size % aes.BlockSize)) 61 | } 62 | return size 63 | } 64 | 65 | func (a *aescbchmac) Encrypt(input []byte, output []byte, iv []byte) int { 66 | copy(output[:aes.BlockSize], iv) 67 | cipher.NewCBCEncrypter(a.c, iv).CryptBlocks(output[aes.BlockSize:], input) 68 | 69 | inputLen := len(input) 70 | // whole len of output is len(input) + aes.BlockSize, 71 | // so copy of last aes.BlockSize 72 | copy(iv, output[inputLen:]) 73 | sum := a.h.Sum(output[:inputLen+aes.BlockSize]) 74 | copy(output[inputLen+aes.BlockSize:], sum[:a.hashsize]) 75 | 76 | return inputLen + aes.BlockSize + a.hashsize 77 | } 78 | 79 | func (a *aescbchmac) Decrypt(input []byte, output []byte) (int, error) { 80 | msgLen := len(input) - a.hashsize 81 | test := a.h.Sum(input[:msgLen]) 82 | if !hmac.Equal(input[msgLen:], test[len(test)-a.hashsize:]) { 83 | return 0, HMACError 84 | } 85 | 86 | resultLen := msgLen - aes.BlockSize 87 | cipher.NewCBCDecrypter(a.c, input[:aes.BlockSize]). 88 | CryptBlocks(output, input[aes.BlockSize:aes.BlockSize+resultLen]) 89 | return resultLen, nil 90 | } 91 | 92 | func (a *aescbchmac) OutputAdd() int { 93 | // adding IV and HMAC-SHA256 to each message 94 | return aes.BlockSize + a.hashsize 95 | } 96 | 97 | func (a *aescbchmac) IVLen() int { 98 | return aes.BlockSize 99 | } 100 | 101 | func init() { 102 | registeredEncrypters["aescbchmac"] = newAesCbcHmac 103 | } 104 | -------------------------------------------------------------------------------- /encryption_none.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // encnone implements just copy "encryption"-"decryption" 4 | // so no encryption at all!!! 5 | type encnone struct { 6 | } 7 | 8 | func newEncNone(key string) (PacketEncrypter, error) { 9 | 10 | a := encnone{} 11 | return &a, nil 12 | 13 | } 14 | 15 | func (a *encnone) CheckSize(size int) bool { 16 | return true 17 | } 18 | 19 | func (a *encnone) AdjustInputSize(size int) int { 20 | return size 21 | } 22 | 23 | func (a *encnone) Encrypt(input []byte, output []byte, iv []byte) int { 24 | copy(output, input) 25 | return len(input) 26 | } 27 | 28 | func (a *encnone) Decrypt(input []byte, output []byte) (int, error) { 29 | copy(output, input) 30 | return len(input), nil 31 | } 32 | 33 | func (a *encnone) OutputAdd() int { 34 | return 0 35 | } 36 | 37 | func (a *encnone) IVLen() int { 38 | return 0 39 | } 40 | 41 | func init() { 42 | registeredEncrypters["none"] = newEncNone 43 | } 44 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kanocz/lcvpn 2 | 3 | go 1.22.2 4 | 5 | require ( 6 | github.com/matishsiao/go_reuseport v0.0.0-20140609025215-7f88524278ad 7 | github.com/milosgajdos/tenus v0.0.3 8 | github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 9 | golang.org/x/net v0.35.0 10 | gopkg.in/gcfg.v1 v1.2.3 11 | ) 12 | 13 | require ( 14 | github.com/docker/libcontainer v2.2.1+incompatible // indirect 15 | golang.org/x/sys v0.30.0 // indirect 16 | gopkg.in/warnings.v0 v0.1.2 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0= 2 | github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw= 3 | github.com/matishsiao/go_reuseport v0.0.0-20140609025215-7f88524278ad h1:3DDkUys/HMqZaGBzpioV75Z9vp9KqDDTqXn3aEclYmY= 4 | github.com/matishsiao/go_reuseport v0.0.0-20140609025215-7f88524278ad/go.mod h1:N+pvXboGXV169bhdsx+tIT+pIDfB+WCqNR6lozDBA14= 5 | github.com/milosgajdos/tenus v0.0.3 h1:jmaJzwaY1DUyYVD0lM4U+uvP2kkEg1VahDqRFxIkVBE= 6 | github.com/milosgajdos/tenus v0.0.3/go.mod h1:eIjx29vNeDOYWJuCnaHY2r4fq5egetV26ry3on7p8qY= 7 | github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= 8 | github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= 9 | golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= 10 | golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= 11 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 12 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 13 | gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= 14 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 15 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 16 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 17 | -------------------------------------------------------------------------------- /iface_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | "os/exec" 8 | "strconv" 9 | 10 | "github.com/songgao/water" 11 | ) 12 | 13 | const ( 14 | // MTU used for tunneled packets 15 | MTU = 1300 16 | ) 17 | 18 | // ifaceSetup returns new interface OR PANIC! 19 | func ifaceSetup(localCIDR string) *water.Interface { 20 | 21 | iface, err := water.New(water.Config{DeviceType: water.TUN}) 22 | 23 | if nil != err { 24 | log.Println("Unable to allocate TUN interface:", err) 25 | panic(err) 26 | } 27 | 28 | log.Println("Interface allocated:", iface.Name()) 29 | 30 | if err := exec.Command("ifconfig", iface.Name(), "inet", localCIDR, "mtu", strconv.FormatInt(MTU, 10), "up").Run(); err != nil { 31 | log.Fatalln("Unable to setup interface:", err) 32 | } 33 | 34 | return iface 35 | } 36 | 37 | func routesThread(ifaceName string, refresh chan bool) { 38 | currentRoutes := map[string]bool{} 39 | for { 40 | <-refresh 41 | log.Println("Reloading routes...") 42 | conf := config.Load().(VPNState) 43 | 44 | routes2Del := map[string]bool{} 45 | 46 | for r := range currentRoutes { 47 | routes2Del[r] = true 48 | } 49 | 50 | for r := range conf.routes { 51 | rs := r.String() 52 | if _, exist := routes2Del[rs]; exist { 53 | delete(routes2Del, rs) 54 | } else { 55 | // real add route 56 | currentRoutes[rs] = true 57 | log.Println("Adding route:", rs) 58 | 59 | if err := exec.Command("route", "add", "-net", rs, "-interface", ifaceName).Run(); err != nil { 60 | log.Println("Adding route", rs, "failed:", err) 61 | } 62 | } 63 | } 64 | 65 | for r := range routes2Del { 66 | delete(currentRoutes, r) 67 | log.Println("Removing route:", r) 68 | if err := exec.Command("route", "delete", "-net", r, "-interface", ifaceName).Run(); err != nil { 69 | log.Printf("Error removeing route \"%s\": %s", r, err.Error()) 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /iface_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | "net" 8 | 9 | "github.com/kanocz/lcvpn/netlink" 10 | "github.com/milosgajdos/tenus" 11 | "github.com/songgao/water" 12 | ) 13 | 14 | const ( 15 | // MTU used for tunneled packets 16 | MTU = 1300 17 | ) 18 | 19 | // ifaceSetup returns new interface OR PANIC! 20 | func ifaceSetup(localCIDR string) *water.Interface { 21 | 22 | lIP, lNet, err := net.ParseCIDR(localCIDR) 23 | if nil != err { 24 | log.Fatalln("\nlocal ip is not in ip/cidr format") 25 | panic("invalid local ip") 26 | } 27 | 28 | iface, err := water.New(water.Config{DeviceType: water.TUN}) 29 | 30 | if nil != err { 31 | log.Println("Unable to allocate TUN interface:", err) 32 | panic(err) 33 | } 34 | 35 | log.Println("Interface allocated:", iface.Name()) 36 | 37 | link, err := tenus.NewLinkFrom(iface.Name()) 38 | if nil != err { 39 | log.Fatalln("Unable to get interface info", err) 40 | } 41 | 42 | err = link.SetLinkMTU(MTU) 43 | if nil != err { 44 | log.Fatalln("Unable to set MTU to 1300 on interface") 45 | } 46 | 47 | err = link.SetLinkIp(lIP, lNet) 48 | if nil != err { 49 | log.Fatalln("Unable to set IP to ", lIP, "/", lNet, " on interface") 50 | } 51 | 52 | err = link.SetLinkUp() 53 | if nil != err { 54 | log.Fatalln("Unable to UP interface") 55 | } 56 | 57 | return iface 58 | } 59 | 60 | func routesThread(ifaceName string, refresh chan bool) { 61 | currentRoutes := map[string]bool{} 62 | for { 63 | <-refresh 64 | log.Println("Reloading routes...") 65 | conf := config.Load().(VPNState) 66 | 67 | routes2Del := map[string]bool{} 68 | 69 | for r := range currentRoutes { 70 | routes2Del[r] = true 71 | } 72 | 73 | for r := range conf.routes { 74 | rs := r.String() 75 | if _, exist := routes2Del[rs]; exist { 76 | delete(routes2Del, rs) 77 | } else { 78 | // real add route 79 | currentRoutes[rs] = true 80 | log.Println("Adding route:", rs) 81 | err := netlink.AddRoute(rs, "", "", ifaceName) 82 | if nil != err { 83 | log.Println("Adding route", rs, "failed:", err) 84 | } 85 | } 86 | } 87 | 88 | for r := range routes2Del { 89 | delete(currentRoutes, r) 90 | log.Println("Removing route:", r) 91 | err := netlink.DelRoute(r, "", "", ifaceName) 92 | if nil != err { 93 | log.Printf("Error removeing route \"%s\": %s", r, err.Error()) 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lcvpn.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Light decentralized VPN in golang 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | ExecStart=/opt/lcvpn 7 | Restart=always 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | 14 | "github.com/matishsiao/go_reuseport" 15 | "github.com/songgao/water" 16 | "golang.org/x/net/ipv4" 17 | ) 18 | 19 | const ( 20 | // AppVersion contains current application version for -version command flag 21 | AppVersion = "0.2.0b" 22 | ) 23 | 24 | const ( 25 | // I use TUN interface, so only plain IP packet, 26 | // no ethernet header + mtu is set to 1300 27 | 28 | // BUFFERSIZE is size of buffer to receive packets 29 | // (little bit bigger than maximum) 30 | BUFFERSIZE = 1518 31 | ) 32 | 33 | func rcvrThread(proto string, port int, iface *water.Interface) { 34 | conn, err := reuseport.NewReusableUDPPortConn(proto, fmt.Sprintf(":%v", port)) 35 | if nil != err { 36 | log.Fatalln("Unable to get UDP socket:", err) 37 | } 38 | 39 | encrypted := make([]byte, BUFFERSIZE) 40 | var decrypted IPPacket = make([]byte, BUFFERSIZE) 41 | 42 | for { 43 | n, _, err := conn.ReadFrom(encrypted) 44 | 45 | if err != nil { 46 | log.Println("Error: ", err) 47 | continue 48 | } 49 | 50 | // ReadFromUDP can return 0 bytes on timeout 51 | if 0 == n { 52 | continue 53 | } 54 | 55 | conf := config.Load().(VPNState) 56 | 57 | if !conf.Main.main.CheckSize(n) { 58 | log.Println("invalid packet size ", n) 59 | continue 60 | } 61 | 62 | size, mainErr := DecryptV4Chk(conf.Main.main, encrypted[:n], decrypted) 63 | if nil != mainErr { 64 | if nil != conf.Main.alt { 65 | size, err = DecryptV4Chk(conf.Main.alt, encrypted[:n], decrypted) 66 | if nil != err { 67 | log.Println("Corrupted package: ", mainErr, " / ", err) 68 | continue 69 | } 70 | } else { 71 | log.Println("Corrupted package: ", mainErr) 72 | continue 73 | } 74 | } 75 | 76 | n, err = iface.Write(decrypted[:size]) 77 | if nil != err { 78 | log.Println("Error writing to local interface: ", err) 79 | } else if n != size { 80 | log.Println("Partial package written to local interface") 81 | } 82 | } 83 | } 84 | 85 | func sndrThread(conn *net.UDPConn, iface *water.Interface) { 86 | // first time fill with random numbers 87 | ivbuf := make([]byte, config.Load().(VPNState).Main.main.IVLen()) 88 | if _, err := io.ReadFull(rand.Reader, ivbuf); err != nil { 89 | log.Fatalln("Unable to get rand data:", err) 90 | } 91 | 92 | var packet IPPacket = make([]byte, BUFFERSIZE) 93 | var encrypted = make([]byte, BUFFERSIZE) 94 | 95 | for { 96 | plen, err := iface.Read(packet[:MTU]) 97 | if err != nil { 98 | break 99 | } 100 | 101 | if 4 != packet.IPver() { 102 | header, _ := ipv4.ParseHeader(packet) 103 | log.Printf("Non IPv4 packet [%+v]\n", header) 104 | continue 105 | } 106 | 107 | // each time get pointer to (probably) new config 108 | c := config.Load().(VPNState) 109 | 110 | dst := packet.Dst() 111 | 112 | wanted := false 113 | 114 | addr, ok := c.remotes[dst] 115 | 116 | if ok { 117 | wanted = true 118 | } 119 | 120 | if dst == c.Main.bcastIP || packet.IsMulticast() { 121 | wanted = true 122 | } 123 | 124 | // very ugly and useful only for a limited numbers of routes! 125 | if !wanted { 126 | ip := packet.DstV4() 127 | for n, s := range c.routes { 128 | if n.Contains(ip) { 129 | addr = s 130 | ok = true 131 | wanted = true 132 | break 133 | } 134 | } 135 | } 136 | 137 | if wanted { 138 | // new len contatins also 2byte original size 139 | clen := c.Main.main.AdjustInputSize(plen) 140 | 141 | if clen+c.Main.main.OutputAdd() > len(packet) { 142 | log.Println("clen + data > len(package)", clen, len(packet)) 143 | continue 144 | } 145 | 146 | tsize := c.Main.main.Encrypt(packet[:clen], encrypted, ivbuf) 147 | 148 | if ok { 149 | n, err := conn.WriteToUDP(encrypted[:tsize], addr) 150 | if nil != err { 151 | log.Println("Error sending package:", err) 152 | } 153 | if n != tsize { 154 | log.Println("Only ", n, " bytes of ", tsize, " sent") 155 | } 156 | } else { 157 | // multicast or broadcast 158 | for _, addr := range c.remotes { 159 | n, err := conn.WriteToUDP(encrypted[:tsize], addr) 160 | if nil != err { 161 | log.Println("Error sending package:", err) 162 | } 163 | if n != tsize { 164 | log.Println("Only ", n, " bytes of ", tsize, " sent") 165 | } 166 | } 167 | } 168 | } else { 169 | log.Println("Unknown dst: ", dst) 170 | } 171 | } 172 | 173 | } 174 | 175 | func main() { 176 | 177 | version := flag.Bool("version", false, "print lcvpn version") 178 | flag.Parse() 179 | 180 | if *version { 181 | fmt.Println(AppVersion) 182 | os.Exit(0) 183 | } 184 | 185 | routeReload := make(chan bool, 1) 186 | 187 | initConfig(routeReload) 188 | 189 | conf := config.Load().(VPNState) 190 | 191 | iface := ifaceSetup(conf.Main.local) 192 | 193 | // start routes changes in config monitoring 194 | go routesThread(iface.Name(), routeReload) 195 | 196 | log.Println("Interface parameters configured") 197 | 198 | // Start listen threads 199 | for i := 0; i < conf.Main.RecvThreads; i++ { 200 | go rcvrThread("udp4", conf.Main.Port, iface) 201 | } 202 | 203 | // init udp socket for write 204 | 205 | writeAddr, err := net.ResolveUDPAddr("udp", ":") 206 | if nil != err { 207 | log.Fatalln("Unable to get UDP socket:", err) 208 | } 209 | 210 | writeConn, err := net.ListenUDP("udp", writeAddr) 211 | if nil != err { 212 | log.Fatalln("Unable to create UDP socket:", err) 213 | } 214 | 215 | // Start sender threads 216 | 217 | for i := 0; i < conf.Main.SendThreads; i++ { 218 | go sndrThread(writeConn, iface) 219 | } 220 | 221 | exitChan := make(chan os.Signal, 1) 222 | signal.Notify(exitChan, syscall.SIGTERM) 223 | 224 | <-exitChan 225 | 226 | err = writeConn.Close() 227 | if nil != err { 228 | log.Println("Error closing UDP connection: ", err) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /netlink/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2014 Docker, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /netlink/MAINTAINERS: -------------------------------------------------------------------------------- 1 | Michael Crosby (@crosbymichael) 2 | Guillaume J. Charmes (@creack) 3 | -------------------------------------------------------------------------------- /netlink/netlink.go: -------------------------------------------------------------------------------- 1 | // Packet netlink provide access to low level Netlink sockets and messages. 2 | // 3 | // Actual implementations are in: 4 | // netlink_linux.go 5 | // netlink_darwin.go 6 | package netlink 7 | 8 | import ( 9 | "errors" 10 | "net" 11 | ) 12 | 13 | var ( 14 | ErrWrongSockType = errors.New("Wrong socket type") 15 | ErrShortResponse = errors.New("Got short response from netlink") 16 | ErrInterfaceExists = errors.New("Network interface already exists") 17 | ) 18 | 19 | // A Route is a subnet associated with the interface to reach it. 20 | type Route struct { 21 | *net.IPNet 22 | Iface *net.Interface 23 | Default bool 24 | } 25 | 26 | // An IfAddr defines IP network settings for a given network interface 27 | type IfAddr struct { 28 | Iface *net.Interface 29 | IP net.IP 30 | IPNet *net.IPNet 31 | } 32 | -------------------------------------------------------------------------------- /netlink/netlink_linux.go: -------------------------------------------------------------------------------- 1 | package netlink 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "math/rand" 8 | "net" 9 | "os" 10 | "sync/atomic" 11 | "syscall" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | const ( 17 | IFNAMSIZ = 16 18 | DEFAULT_CHANGE = 0xFFFFFFFF 19 | IFLA_INFO_KIND = 1 20 | IFLA_INFO_DATA = 2 21 | VETH_INFO_PEER = 1 22 | IFLA_MACVLAN_MODE = 1 23 | IFLA_VLAN_ID = 1 24 | IFLA_NET_NS_FD = 28 25 | IFLA_ADDRESS = 1 26 | IFLA_BRPORT_MODE = 4 27 | SIOC_BRADDBR = 0x89a0 28 | SIOC_BRDELBR = 0x89a1 29 | SIOC_BRADDIF = 0x89a2 30 | SIOC_BRDELIF = 0x89a3 31 | ) 32 | 33 | const ( 34 | MACVLAN_MODE_PRIVATE = 1 << iota 35 | MACVLAN_MODE_VEPA 36 | MACVLAN_MODE_BRIDGE 37 | MACVLAN_MODE_PASSTHRU 38 | ) 39 | 40 | var nextSeqNr uint32 41 | 42 | type ifreqHwaddr struct { 43 | IfrnName [IFNAMSIZ]byte 44 | IfruHwaddr syscall.RawSockaddr 45 | } 46 | 47 | type ifreqIndex struct { 48 | IfrnName [IFNAMSIZ]byte 49 | IfruIndex int32 50 | } 51 | 52 | type ifreqFlags struct { 53 | IfrnName [IFNAMSIZ]byte 54 | Ifruflags uint16 55 | } 56 | 57 | var native binary.ByteOrder 58 | 59 | var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) 60 | 61 | func init() { 62 | var x uint32 = 0x01020304 63 | if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 64 | native = binary.BigEndian 65 | } else { 66 | native = binary.LittleEndian 67 | } 68 | } 69 | 70 | func getIpFamily(ip net.IP) int { 71 | if len(ip) <= net.IPv4len { 72 | return syscall.AF_INET 73 | } 74 | if ip.To4() != nil { 75 | return syscall.AF_INET 76 | } 77 | return syscall.AF_INET6 78 | } 79 | 80 | type NetlinkRequestData interface { 81 | Len() int 82 | ToWireFormat() []byte 83 | } 84 | 85 | type IfInfomsg struct { 86 | syscall.IfInfomsg 87 | } 88 | 89 | func newIfInfomsg(family int) *IfInfomsg { 90 | return &IfInfomsg{ 91 | IfInfomsg: syscall.IfInfomsg{ 92 | Family: uint8(family), 93 | }, 94 | } 95 | } 96 | 97 | func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg { 98 | msg := newIfInfomsg(family) 99 | parent.children = append(parent.children, msg) 100 | return msg 101 | } 102 | 103 | func (msg *IfInfomsg) ToWireFormat() []byte { 104 | length := syscall.SizeofIfInfomsg 105 | b := make([]byte, length) 106 | b[0] = msg.Family 107 | b[1] = 0 108 | native.PutUint16(b[2:4], msg.Type) 109 | native.PutUint32(b[4:8], uint32(msg.Index)) 110 | native.PutUint32(b[8:12], msg.Flags) 111 | native.PutUint32(b[12:16], msg.Change) 112 | return b 113 | } 114 | 115 | func (msg *IfInfomsg) Len() int { 116 | return syscall.SizeofIfInfomsg 117 | } 118 | 119 | type IfAddrmsg struct { 120 | syscall.IfAddrmsg 121 | } 122 | 123 | func newIfAddrmsg(family int) *IfAddrmsg { 124 | return &IfAddrmsg{ 125 | IfAddrmsg: syscall.IfAddrmsg{ 126 | Family: uint8(family), 127 | }, 128 | } 129 | } 130 | 131 | func (msg *IfAddrmsg) ToWireFormat() []byte { 132 | length := syscall.SizeofIfAddrmsg 133 | b := make([]byte, length) 134 | b[0] = msg.Family 135 | b[1] = msg.Prefixlen 136 | b[2] = msg.Flags 137 | b[3] = msg.Scope 138 | native.PutUint32(b[4:8], msg.Index) 139 | return b 140 | } 141 | 142 | func (msg *IfAddrmsg) Len() int { 143 | return syscall.SizeofIfAddrmsg 144 | } 145 | 146 | type RtMsg struct { 147 | syscall.RtMsg 148 | } 149 | 150 | func newRtMsg() *RtMsg { 151 | return &RtMsg{ 152 | RtMsg: syscall.RtMsg{ 153 | Table: syscall.RT_TABLE_MAIN, 154 | Scope: syscall.RT_SCOPE_UNIVERSE, 155 | Protocol: syscall.RTPROT_BOOT, 156 | Type: syscall.RTN_UNICAST, 157 | }, 158 | } 159 | } 160 | 161 | func (msg *RtMsg) ToWireFormat() []byte { 162 | length := syscall.SizeofRtMsg 163 | b := make([]byte, length) 164 | b[0] = msg.Family 165 | b[1] = msg.Dst_len 166 | b[2] = msg.Src_len 167 | b[3] = msg.Tos 168 | b[4] = msg.Table 169 | b[5] = msg.Protocol 170 | b[6] = msg.Scope 171 | b[7] = msg.Type 172 | native.PutUint32(b[8:12], msg.Flags) 173 | return b 174 | } 175 | 176 | func (msg *RtMsg) Len() int { 177 | return syscall.SizeofRtMsg 178 | } 179 | 180 | func rtaAlignOf(attrlen int) int { 181 | return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) 182 | } 183 | 184 | type RtAttr struct { 185 | syscall.RtAttr 186 | Data []byte 187 | children []NetlinkRequestData 188 | } 189 | 190 | func newRtAttr(attrType int, data []byte) *RtAttr { 191 | return &RtAttr{ 192 | RtAttr: syscall.RtAttr{ 193 | Type: uint16(attrType), 194 | }, 195 | children: []NetlinkRequestData{}, 196 | Data: data, 197 | } 198 | } 199 | 200 | func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { 201 | attr := newRtAttr(attrType, data) 202 | parent.children = append(parent.children, attr) 203 | return attr 204 | } 205 | 206 | func (a *RtAttr) Len() int { 207 | if len(a.children) == 0 { 208 | return (syscall.SizeofRtAttr + len(a.Data)) 209 | } 210 | 211 | l := 0 212 | for _, child := range a.children { 213 | l += child.Len() 214 | } 215 | l += syscall.SizeofRtAttr 216 | return rtaAlignOf(l + len(a.Data)) 217 | } 218 | 219 | func (a *RtAttr) ToWireFormat() []byte { 220 | length := a.Len() 221 | buf := make([]byte, rtaAlignOf(length)) 222 | 223 | if a.Data != nil { 224 | copy(buf[4:], a.Data) 225 | } else { 226 | next := 4 227 | for _, child := range a.children { 228 | childBuf := child.ToWireFormat() 229 | copy(buf[next:], childBuf) 230 | next += rtaAlignOf(len(childBuf)) 231 | } 232 | } 233 | 234 | if l := uint16(length); l != 0 { 235 | native.PutUint16(buf[0:2], l) 236 | } 237 | native.PutUint16(buf[2:4], a.Type) 238 | return buf 239 | } 240 | 241 | func uint32Attr(t int, n uint32) *RtAttr { 242 | buf := make([]byte, 4) 243 | native.PutUint32(buf, n) 244 | return newRtAttr(t, buf) 245 | } 246 | 247 | type NetlinkRequest struct { 248 | syscall.NlMsghdr 249 | Data []NetlinkRequestData 250 | } 251 | 252 | func (rr *NetlinkRequest) ToWireFormat() []byte { 253 | length := rr.Len 254 | dataBytes := make([][]byte, len(rr.Data)) 255 | for i, data := range rr.Data { 256 | dataBytes[i] = data.ToWireFormat() 257 | length += uint32(len(dataBytes[i])) 258 | } 259 | b := make([]byte, length) 260 | native.PutUint32(b[0:4], length) 261 | native.PutUint16(b[4:6], rr.Type) 262 | native.PutUint16(b[6:8], rr.Flags) 263 | native.PutUint32(b[8:12], rr.Seq) 264 | native.PutUint32(b[12:16], rr.Pid) 265 | 266 | next := 16 267 | for _, data := range dataBytes { 268 | copy(b[next:], data) 269 | next += len(data) 270 | } 271 | return b 272 | } 273 | 274 | func (rr *NetlinkRequest) AddData(data NetlinkRequestData) { 275 | if data != nil { 276 | rr.Data = append(rr.Data, data) 277 | } 278 | } 279 | 280 | func newNetlinkRequest(proto, flags int) *NetlinkRequest { 281 | return &NetlinkRequest{ 282 | NlMsghdr: syscall.NlMsghdr{ 283 | Len: uint32(syscall.NLMSG_HDRLEN), 284 | Type: uint16(proto), 285 | Flags: syscall.NLM_F_REQUEST | uint16(flags), 286 | Seq: atomic.AddUint32(&nextSeqNr, 1), 287 | }, 288 | } 289 | } 290 | 291 | type NetlinkSocket struct { 292 | fd int 293 | lsa syscall.SockaddrNetlink 294 | } 295 | 296 | func getNetlinkSocket() (*NetlinkSocket, error) { 297 | fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE) 298 | if err != nil { 299 | return nil, err 300 | } 301 | s := &NetlinkSocket{ 302 | fd: fd, 303 | } 304 | s.lsa.Family = syscall.AF_NETLINK 305 | if err := syscall.Bind(fd, &s.lsa); err != nil { 306 | syscall.Close(fd) 307 | return nil, err 308 | } 309 | 310 | return s, nil 311 | } 312 | 313 | func (s *NetlinkSocket) Close() { 314 | syscall.Close(s.fd) 315 | } 316 | 317 | func (s *NetlinkSocket) Send(request *NetlinkRequest) error { 318 | if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil { 319 | return err 320 | } 321 | return nil 322 | } 323 | 324 | func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) { 325 | rb := make([]byte, syscall.Getpagesize()) 326 | nr, _, err := syscall.Recvfrom(s.fd, rb, 0) 327 | if err != nil { 328 | return nil, err 329 | } 330 | if nr < syscall.NLMSG_HDRLEN { 331 | return nil, ErrShortResponse 332 | } 333 | rb = rb[:nr] 334 | return syscall.ParseNetlinkMessage(rb) 335 | } 336 | 337 | func (s *NetlinkSocket) GetPid() (uint32, error) { 338 | lsa, err := syscall.Getsockname(s.fd) 339 | if err != nil { 340 | return 0, err 341 | } 342 | switch v := lsa.(type) { 343 | case *syscall.SockaddrNetlink: 344 | return v.Pid, nil 345 | } 346 | return 0, ErrWrongSockType 347 | } 348 | 349 | func (s *NetlinkSocket) CheckMessage(m syscall.NetlinkMessage, seq, pid uint32) error { 350 | if m.Header.Seq != seq { 351 | return fmt.Errorf("netlink: invalid seq %d, expected %d", m.Header.Seq, seq) 352 | } 353 | if m.Header.Pid != pid { 354 | return fmt.Errorf("netlink: wrong pid %d, expected %d", m.Header.Pid, pid) 355 | } 356 | if m.Header.Type == syscall.NLMSG_DONE { 357 | return io.EOF 358 | } 359 | if m.Header.Type == syscall.NLMSG_ERROR { 360 | e := int32(native.Uint32(m.Data[0:4])) 361 | if e == 0 { 362 | return io.EOF 363 | } 364 | return syscall.Errno(-e) 365 | } 366 | return nil 367 | } 368 | 369 | func (s *NetlinkSocket) HandleAck(seq uint32) error { 370 | pid, err := s.GetPid() 371 | if err != nil { 372 | return err 373 | } 374 | 375 | outer: 376 | for { 377 | msgs, err := s.Receive() 378 | if err != nil { 379 | return err 380 | } 381 | for _, m := range msgs { 382 | if err := s.CheckMessage(m, seq, pid); err != nil { 383 | if err == io.EOF { 384 | break outer 385 | } 386 | return err 387 | } 388 | } 389 | } 390 | 391 | return nil 392 | } 393 | 394 | func zeroTerminated(s string) []byte { 395 | return []byte(s + "\000") 396 | } 397 | 398 | func nonZeroTerminated(s string) []byte { 399 | return []byte(s) 400 | } 401 | 402 | // Add a new network link of a specified type. 403 | // This is identical to running: ip link add $name type $linkType 404 | func NetworkLinkAdd(name string, linkType string) error { 405 | if name == "" || linkType == "" { 406 | return fmt.Errorf("Neither link name nor link type can be empty!") 407 | } 408 | 409 | s, err := getNetlinkSocket() 410 | if err != nil { 411 | return err 412 | } 413 | defer s.Close() 414 | 415 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 416 | 417 | msg := newIfInfomsg(syscall.AF_UNSPEC) 418 | wb.AddData(msg) 419 | 420 | linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil) 421 | newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType)) 422 | wb.AddData(linkInfo) 423 | 424 | nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) 425 | wb.AddData(nameData) 426 | 427 | if err := s.Send(wb); err != nil { 428 | return err 429 | } 430 | 431 | return s.HandleAck(wb.Seq) 432 | } 433 | 434 | // Delete a network link. 435 | // This is identical to running: ip link del $name 436 | func NetworkLinkDel(name string) error { 437 | if name == "" { 438 | return fmt.Errorf("Network link name can not be empty!") 439 | } 440 | 441 | s, err := getNetlinkSocket() 442 | if err != nil { 443 | return err 444 | } 445 | defer s.Close() 446 | 447 | iface, err := net.InterfaceByName(name) 448 | if err != nil { 449 | return err 450 | } 451 | 452 | wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) 453 | 454 | msg := newIfInfomsg(syscall.AF_UNSPEC) 455 | msg.Index = int32(iface.Index) 456 | wb.AddData(msg) 457 | 458 | if err := s.Send(wb); err != nil { 459 | return err 460 | } 461 | 462 | return s.HandleAck(wb.Seq) 463 | } 464 | 465 | // Bring up a particular network interface. 466 | // This is identical to running: ip link set dev $name up 467 | func NetworkLinkUp(iface *net.Interface) error { 468 | s, err := getNetlinkSocket() 469 | if err != nil { 470 | return err 471 | } 472 | defer s.Close() 473 | 474 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) 475 | 476 | msg := newIfInfomsg(syscall.AF_UNSPEC) 477 | msg.Index = int32(iface.Index) 478 | msg.Flags = syscall.IFF_UP 479 | msg.Change = syscall.IFF_UP 480 | wb.AddData(msg) 481 | 482 | if err := s.Send(wb); err != nil { 483 | return err 484 | } 485 | 486 | return s.HandleAck(wb.Seq) 487 | } 488 | 489 | // Bring down a particular network interface. 490 | // This is identical to running: ip link set $name down 491 | func NetworkLinkDown(iface *net.Interface) error { 492 | s, err := getNetlinkSocket() 493 | if err != nil { 494 | return err 495 | } 496 | defer s.Close() 497 | 498 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) 499 | 500 | msg := newIfInfomsg(syscall.AF_UNSPEC) 501 | msg.Index = int32(iface.Index) 502 | msg.Flags = 0 & ^syscall.IFF_UP 503 | msg.Change = DEFAULT_CHANGE 504 | wb.AddData(msg) 505 | 506 | if err := s.Send(wb); err != nil { 507 | return err 508 | } 509 | 510 | return s.HandleAck(wb.Seq) 511 | } 512 | 513 | // Set link layer address ie. MAC Address. 514 | // This is identical to running: ip link set dev $name address $macaddress 515 | func NetworkSetMacAddress(iface *net.Interface, macaddr string) error { 516 | s, err := getNetlinkSocket() 517 | if err != nil { 518 | return err 519 | } 520 | defer s.Close() 521 | 522 | hwaddr, err := net.ParseMAC(macaddr) 523 | if err != nil { 524 | return err 525 | } 526 | 527 | var ( 528 | MULTICAST byte = 0x1 529 | ) 530 | 531 | if hwaddr[0]&0x1 == MULTICAST { 532 | return fmt.Errorf("Multicast MAC Address is not supported: %s", macaddr) 533 | } 534 | 535 | wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 536 | 537 | msg := newIfInfomsg(syscall.AF_UNSPEC) 538 | msg.Index = int32(iface.Index) 539 | msg.Change = DEFAULT_CHANGE 540 | wb.AddData(msg) 541 | 542 | macdata := make([]byte, 6) 543 | copy(macdata, hwaddr) 544 | data := newRtAttr(IFLA_ADDRESS, macdata) 545 | wb.AddData(data) 546 | 547 | if err := s.Send(wb); err != nil { 548 | return err 549 | } 550 | return s.HandleAck(wb.Seq) 551 | } 552 | 553 | // Set link Maximum Transmission Unit 554 | // This is identical to running: ip link set dev $name mtu $MTU 555 | // bridge is a bitch here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=292088 556 | // https://bugzilla.redhat.com/show_bug.cgi?id=697021 557 | // There is a discussion about how to deal with ifcs joining bridge with MTU > 1500 558 | // Regular network nterfaces do seem to work though! 559 | func NetworkSetMTU(iface *net.Interface, mtu int) error { 560 | s, err := getNetlinkSocket() 561 | if err != nil { 562 | return err 563 | } 564 | defer s.Close() 565 | 566 | wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 567 | 568 | msg := newIfInfomsg(syscall.AF_UNSPEC) 569 | msg.Type = syscall.RTM_SETLINK 570 | msg.Flags = syscall.NLM_F_REQUEST 571 | msg.Index = int32(iface.Index) 572 | msg.Change = DEFAULT_CHANGE 573 | wb.AddData(msg) 574 | wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu))) 575 | 576 | if err := s.Send(wb); err != nil { 577 | return err 578 | } 579 | return s.HandleAck(wb.Seq) 580 | } 581 | 582 | // Set link queue length 583 | // This is identical to running: ip link set dev $name txqueuelen $QLEN 584 | func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error { 585 | s, err := getNetlinkSocket() 586 | if err != nil { 587 | return err 588 | } 589 | defer s.Close() 590 | 591 | wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 592 | 593 | msg := newIfInfomsg(syscall.AF_UNSPEC) 594 | msg.Type = syscall.RTM_SETLINK 595 | msg.Flags = syscall.NLM_F_REQUEST 596 | msg.Index = int32(iface.Index) 597 | msg.Change = DEFAULT_CHANGE 598 | wb.AddData(msg) 599 | wb.AddData(uint32Attr(syscall.IFLA_TXQLEN, uint32(txQueueLen))) 600 | 601 | if err := s.Send(wb); err != nil { 602 | return err 603 | } 604 | return s.HandleAck(wb.Seq) 605 | } 606 | 607 | func networkMasterAction(iface *net.Interface, rtattr *RtAttr) error { 608 | s, err := getNetlinkSocket() 609 | if err != nil { 610 | return err 611 | } 612 | defer s.Close() 613 | 614 | wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 615 | 616 | msg := newIfInfomsg(syscall.AF_UNSPEC) 617 | msg.Type = syscall.RTM_SETLINK 618 | msg.Flags = syscall.NLM_F_REQUEST 619 | msg.Index = int32(iface.Index) 620 | msg.Change = DEFAULT_CHANGE 621 | wb.AddData(msg) 622 | wb.AddData(rtattr) 623 | 624 | if err := s.Send(wb); err != nil { 625 | return err 626 | } 627 | 628 | return s.HandleAck(wb.Seq) 629 | } 630 | 631 | // Add an interface to bridge. 632 | // This is identical to running: ip link set $name master $master 633 | func NetworkSetMaster(iface, master *net.Interface) error { 634 | data := uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)) 635 | return networkMasterAction(iface, data) 636 | } 637 | 638 | // Remove an interface from the bridge 639 | // This is is identical to to running: ip link $name set nomaster 640 | func NetworkSetNoMaster(iface *net.Interface) error { 641 | data := uint32Attr(syscall.IFLA_MASTER, 0) 642 | return networkMasterAction(iface, data) 643 | } 644 | 645 | func networkSetNsAction(iface *net.Interface, rtattr *RtAttr) error { 646 | s, err := getNetlinkSocket() 647 | if err != nil { 648 | return err 649 | } 650 | defer s.Close() 651 | 652 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) 653 | msg := newIfInfomsg(syscall.AF_UNSPEC) 654 | msg.Index = int32(iface.Index) 655 | wb.AddData(msg) 656 | wb.AddData(rtattr) 657 | 658 | if err := s.Send(wb); err != nil { 659 | return err 660 | } 661 | 662 | return s.HandleAck(wb.Seq) 663 | } 664 | 665 | // Move a particular network interface to a particular network namespace 666 | // specified by PID. This is identical to running: ip link set dev $name netns $pid 667 | func NetworkSetNsPid(iface *net.Interface, nspid int) error { 668 | data := uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)) 669 | return networkSetNsAction(iface, data) 670 | } 671 | 672 | // Move a particular network interface to a particular mounted 673 | // network namespace specified by file descriptor. 674 | // This is idential to running: ip link set dev $name netns $fd 675 | func NetworkSetNsFd(iface *net.Interface, fd int) error { 676 | data := uint32Attr(IFLA_NET_NS_FD, uint32(fd)) 677 | return networkSetNsAction(iface, data) 678 | } 679 | 680 | // Rename a particular interface to a different name 681 | // !!! Note that you can't rename an active interface. You need to bring it down before renaming it. 682 | // This is identical to running: ip link set dev ${oldName} name ${newName} 683 | func NetworkChangeName(iface *net.Interface, newName string) error { 684 | if len(newName) >= IFNAMSIZ { 685 | return fmt.Errorf("Interface name %s too long", newName) 686 | } 687 | 688 | s, err := getNetlinkSocket() 689 | if err != nil { 690 | return err 691 | } 692 | defer s.Close() 693 | 694 | wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 695 | 696 | msg := newIfInfomsg(syscall.AF_UNSPEC) 697 | msg.Index = int32(iface.Index) 698 | msg.Change = DEFAULT_CHANGE 699 | wb.AddData(msg) 700 | 701 | nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(newName)) 702 | wb.AddData(nameData) 703 | 704 | if err := s.Send(wb); err != nil { 705 | return err 706 | } 707 | 708 | return s.HandleAck(wb.Seq) 709 | } 710 | 711 | // Add a new VETH pair link on the host 712 | // This is identical to running: ip link add name $name type veth peer name $peername 713 | func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error { 714 | s, err := getNetlinkSocket() 715 | if err != nil { 716 | return err 717 | } 718 | defer s.Close() 719 | 720 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 721 | 722 | msg := newIfInfomsg(syscall.AF_UNSPEC) 723 | wb.AddData(msg) 724 | 725 | nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1)) 726 | wb.AddData(nameData) 727 | 728 | txqLen := make([]byte, 4) 729 | native.PutUint32(txqLen, uint32(txQueueLen)) 730 | txqData := newRtAttr(syscall.IFLA_TXQLEN, txqLen) 731 | wb.AddData(txqData) 732 | 733 | nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) 734 | newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth")) 735 | nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) 736 | nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil) 737 | 738 | newIfInfomsgChild(nest3, syscall.AF_UNSPEC) 739 | newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2)) 740 | 741 | txqLen2 := make([]byte, 4) 742 | native.PutUint32(txqLen2, uint32(txQueueLen)) 743 | newRtAttrChild(nest3, syscall.IFLA_TXQLEN, txqLen2) 744 | 745 | wb.AddData(nest1) 746 | 747 | if err := s.Send(wb); err != nil { 748 | return err 749 | } 750 | 751 | if err := s.HandleAck(wb.Seq); err != nil { 752 | if os.IsExist(err) { 753 | return ErrInterfaceExists 754 | } 755 | 756 | return err 757 | } 758 | 759 | return nil 760 | } 761 | 762 | // Add a new VLAN interface with masterDev as its upper device 763 | // This is identical to running: 764 | // ip link add name $name link $masterdev type vlan id $id 765 | func NetworkLinkAddVlan(masterDev, vlanDev string, vlanId uint16) error { 766 | s, err := getNetlinkSocket() 767 | if err != nil { 768 | return err 769 | } 770 | defer s.Close() 771 | 772 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 773 | 774 | masterDevIfc, err := net.InterfaceByName(masterDev) 775 | if err != nil { 776 | return err 777 | } 778 | 779 | msg := newIfInfomsg(syscall.AF_UNSPEC) 780 | wb.AddData(msg) 781 | 782 | nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) 783 | newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("vlan")) 784 | 785 | nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) 786 | vlanData := make([]byte, 2) 787 | native.PutUint16(vlanData, vlanId) 788 | newRtAttrChild(nest2, IFLA_VLAN_ID, vlanData) 789 | wb.AddData(nest1) 790 | 791 | wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) 792 | wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(vlanDev))) 793 | 794 | if err := s.Send(wb); err != nil { 795 | return err 796 | } 797 | return s.HandleAck(wb.Seq) 798 | } 799 | 800 | // MacVlan link has LowerDev, UpperDev and operates in Mode mode 801 | // This simplifies the code when creating MacVlan or MacVtap interface 802 | type MacVlanLink struct { 803 | MasterDev string 804 | SlaveDev string 805 | mode string 806 | } 807 | 808 | func (m MacVlanLink) Mode() uint32 { 809 | modeMap := map[string]uint32{ 810 | "private": MACVLAN_MODE_PRIVATE, 811 | "vepa": MACVLAN_MODE_VEPA, 812 | "bridge": MACVLAN_MODE_BRIDGE, 813 | "passthru": MACVLAN_MODE_PASSTHRU, 814 | } 815 | 816 | return modeMap[m.mode] 817 | } 818 | 819 | // Add MAC VLAN network interface with masterDev as its upper device 820 | // This is identical to running: 821 | // ip link add name $name link $masterdev type macvlan mode $mode 822 | func networkLinkMacVlan(dev_type string, mcvln *MacVlanLink) error { 823 | s, err := getNetlinkSocket() 824 | if err != nil { 825 | return err 826 | } 827 | defer s.Close() 828 | 829 | wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 830 | 831 | masterDevIfc, err := net.InterfaceByName(mcvln.MasterDev) 832 | if err != nil { 833 | return err 834 | } 835 | 836 | msg := newIfInfomsg(syscall.AF_UNSPEC) 837 | wb.AddData(msg) 838 | 839 | nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) 840 | newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated(dev_type)) 841 | 842 | nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) 843 | macVlanData := make([]byte, 4) 844 | native.PutUint32(macVlanData, mcvln.Mode()) 845 | newRtAttrChild(nest2, IFLA_MACVLAN_MODE, macVlanData) 846 | wb.AddData(nest1) 847 | 848 | wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) 849 | wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(mcvln.SlaveDev))) 850 | 851 | if err := s.Send(wb); err != nil { 852 | return err 853 | } 854 | return s.HandleAck(wb.Seq) 855 | } 856 | 857 | func NetworkLinkAddMacVlan(masterDev, macVlanDev string, mode string) error { 858 | return networkLinkMacVlan("macvlan", &MacVlanLink{ 859 | MasterDev: masterDev, 860 | SlaveDev: macVlanDev, 861 | mode: mode, 862 | }) 863 | } 864 | 865 | func NetworkLinkAddMacVtap(masterDev, macVlanDev string, mode string) error { 866 | return networkLinkMacVlan("macvtap", &MacVlanLink{ 867 | MasterDev: masterDev, 868 | SlaveDev: macVlanDev, 869 | mode: mode, 870 | }) 871 | } 872 | 873 | func networkLinkIpAction(action, flags int, ifa IfAddr) error { 874 | s, err := getNetlinkSocket() 875 | if err != nil { 876 | return err 877 | } 878 | defer s.Close() 879 | 880 | family := getIpFamily(ifa.IP) 881 | 882 | wb := newNetlinkRequest(action, flags) 883 | 884 | msg := newIfAddrmsg(family) 885 | msg.Index = uint32(ifa.Iface.Index) 886 | prefixLen, _ := ifa.IPNet.Mask.Size() 887 | msg.Prefixlen = uint8(prefixLen) 888 | wb.AddData(msg) 889 | 890 | var ipData []byte 891 | if family == syscall.AF_INET { 892 | ipData = ifa.IP.To4() 893 | } else { 894 | ipData = ifa.IP.To16() 895 | } 896 | 897 | localData := newRtAttr(syscall.IFA_LOCAL, ipData) 898 | wb.AddData(localData) 899 | 900 | addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) 901 | wb.AddData(addrData) 902 | 903 | if err := s.Send(wb); err != nil { 904 | return err 905 | } 906 | 907 | return s.HandleAck(wb.Seq) 908 | } 909 | 910 | // Delete an IP address from an interface. This is identical to: 911 | // ip addr del $ip/$ipNet dev $iface 912 | func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { 913 | return networkLinkIpAction( 914 | syscall.RTM_DELADDR, 915 | syscall.NLM_F_ACK, 916 | IfAddr{iface, ip, ipNet}, 917 | ) 918 | } 919 | 920 | // Add an Ip address to an interface. This is identical to: 921 | // ip addr add $ip/$ipNet dev $iface 922 | func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { 923 | return networkLinkIpAction( 924 | syscall.RTM_NEWADDR, 925 | syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK, 926 | IfAddr{iface, ip, ipNet}, 927 | ) 928 | } 929 | 930 | // Returns an array of IPNet for all the currently routed subnets on ipv4 931 | // This is similar to the first column of "ip route" output 932 | func NetworkGetRoutes() ([]Route, error) { 933 | s, err := getNetlinkSocket() 934 | if err != nil { 935 | return nil, err 936 | } 937 | defer s.Close() 938 | 939 | wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) 940 | 941 | msg := newIfInfomsg(syscall.AF_UNSPEC) 942 | wb.AddData(msg) 943 | 944 | if err := s.Send(wb); err != nil { 945 | return nil, err 946 | } 947 | 948 | pid, err := s.GetPid() 949 | if err != nil { 950 | return nil, err 951 | } 952 | 953 | res := make([]Route, 0) 954 | 955 | outer: 956 | for { 957 | msgs, err := s.Receive() 958 | if err != nil { 959 | return nil, err 960 | } 961 | for _, m := range msgs { 962 | if err := s.CheckMessage(m, wb.Seq, pid); err != nil { 963 | if err == io.EOF { 964 | break outer 965 | } 966 | return nil, err 967 | } 968 | if m.Header.Type != syscall.RTM_NEWROUTE { 969 | continue 970 | } 971 | 972 | var r Route 973 | 974 | msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) 975 | 976 | if msg.Flags&syscall.RTM_F_CLONED != 0 { 977 | // Ignore cloned routes 978 | continue 979 | } 980 | 981 | if msg.Table != syscall.RT_TABLE_MAIN { 982 | // Ignore non-main tables 983 | continue 984 | } 985 | 986 | if msg.Family != syscall.AF_INET { 987 | // Ignore non-ipv4 routes 988 | continue 989 | } 990 | 991 | if msg.Dst_len == 0 { 992 | // Default routes 993 | r.Default = true 994 | } 995 | 996 | attrs, err := syscall.ParseNetlinkRouteAttr(&m) 997 | if err != nil { 998 | return nil, err 999 | } 1000 | for _, attr := range attrs { 1001 | switch attr.Attr.Type { 1002 | case syscall.RTA_DST: 1003 | ip := attr.Value 1004 | r.IPNet = &net.IPNet{ 1005 | IP: ip, 1006 | Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), 1007 | } 1008 | case syscall.RTA_OIF: 1009 | index := int(native.Uint32(attr.Value[0:4])) 1010 | r.Iface, _ = net.InterfaceByIndex(index) 1011 | } 1012 | } 1013 | if r.Default || r.IPNet != nil { 1014 | res = append(res, r) 1015 | } 1016 | } 1017 | } 1018 | 1019 | return res, nil 1020 | } 1021 | 1022 | // Add a new route table entry. 1023 | func AddRoute(destination, source, gateway, device string) error { 1024 | if destination == "" && source == "" && gateway == "" { 1025 | return fmt.Errorf("one of destination, source or gateway must not be blank") 1026 | } 1027 | 1028 | s, err := getNetlinkSocket() 1029 | if err != nil { 1030 | return err 1031 | } 1032 | defer s.Close() 1033 | 1034 | wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 1035 | msg := newRtMsg() 1036 | currentFamily := -1 1037 | var rtAttrs []*RtAttr 1038 | 1039 | if destination != "" { 1040 | destIP, destNet, err := net.ParseCIDR(destination) 1041 | if err != nil { 1042 | return fmt.Errorf("destination CIDR %s couldn't be parsed", destination) 1043 | } 1044 | destFamily := getIpFamily(destIP) 1045 | currentFamily = destFamily 1046 | destLen, bits := destNet.Mask.Size() 1047 | if destLen == 0 && bits == 0 { 1048 | return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination) 1049 | } 1050 | msg.Family = uint8(destFamily) 1051 | msg.Dst_len = uint8(destLen) 1052 | var destData []byte 1053 | if destFamily == syscall.AF_INET { 1054 | destData = destIP.To4() 1055 | } else { 1056 | destData = destIP.To16() 1057 | } 1058 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData)) 1059 | } 1060 | 1061 | if source != "" { 1062 | srcIP := net.ParseIP(source) 1063 | if srcIP == nil { 1064 | return fmt.Errorf("source IP %s couldn't be parsed", source) 1065 | } 1066 | srcFamily := getIpFamily(srcIP) 1067 | if currentFamily != -1 && currentFamily != srcFamily { 1068 | return fmt.Errorf("source and destination ip were not the same IP family") 1069 | } 1070 | currentFamily = srcFamily 1071 | msg.Family = uint8(srcFamily) 1072 | var srcData []byte 1073 | if srcFamily == syscall.AF_INET { 1074 | srcData = srcIP.To4() 1075 | } else { 1076 | srcData = srcIP.To16() 1077 | } 1078 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_PREFSRC, srcData)) 1079 | } 1080 | 1081 | if gateway != "" { 1082 | gwIP := net.ParseIP(gateway) 1083 | if gwIP == nil { 1084 | return fmt.Errorf("gateway IP %s couldn't be parsed", gateway) 1085 | } 1086 | gwFamily := getIpFamily(gwIP) 1087 | if currentFamily != -1 && currentFamily != gwFamily { 1088 | return fmt.Errorf("gateway, source, and destination ip were not the same IP family") 1089 | } 1090 | msg.Family = uint8(gwFamily) 1091 | var gwData []byte 1092 | if gwFamily == syscall.AF_INET { 1093 | gwData = gwIP.To4() 1094 | } else { 1095 | gwData = gwIP.To16() 1096 | } 1097 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData)) 1098 | } 1099 | 1100 | wb.AddData(msg) 1101 | for _, attr := range rtAttrs { 1102 | wb.AddData(attr) 1103 | } 1104 | 1105 | iface, err := net.InterfaceByName(device) 1106 | if err != nil { 1107 | return err 1108 | } 1109 | wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index))) 1110 | 1111 | if err := s.Send(wb); err != nil { 1112 | return err 1113 | } 1114 | return s.HandleAck(wb.Seq) 1115 | } 1116 | 1117 | // Delete route table entry. 1118 | func DelRoute(destination, source, gateway, device string) error { 1119 | if destination == "" && source == "" && gateway == "" { 1120 | return fmt.Errorf("one of destination, source or gateway must not be blank") 1121 | } 1122 | 1123 | s, err := getNetlinkSocket() 1124 | if err != nil { 1125 | return err 1126 | } 1127 | defer s.Close() 1128 | 1129 | wb := newNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 1130 | msg := newRtMsg() 1131 | currentFamily := -1 1132 | var rtAttrs []*RtAttr 1133 | 1134 | if destination != "" { 1135 | destIP, destNet, err := net.ParseCIDR(destination) 1136 | if err != nil { 1137 | return fmt.Errorf("destination CIDR %s couldn't be parsed", destination) 1138 | } 1139 | destFamily := getIpFamily(destIP) 1140 | currentFamily = destFamily 1141 | destLen, bits := destNet.Mask.Size() 1142 | if destLen == 0 && bits == 0 { 1143 | return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination) 1144 | } 1145 | msg.Family = uint8(destFamily) 1146 | msg.Dst_len = uint8(destLen) 1147 | var destData []byte 1148 | if destFamily == syscall.AF_INET { 1149 | destData = destIP.To4() 1150 | } else { 1151 | destData = destIP.To16() 1152 | } 1153 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData)) 1154 | } 1155 | 1156 | if source != "" { 1157 | srcIP := net.ParseIP(source) 1158 | if srcIP == nil { 1159 | return fmt.Errorf("source IP %s couldn't be parsed", source) 1160 | } 1161 | srcFamily := getIpFamily(srcIP) 1162 | if currentFamily != -1 && currentFamily != srcFamily { 1163 | return fmt.Errorf("source and destination ip were not the same IP family") 1164 | } 1165 | currentFamily = srcFamily 1166 | msg.Family = uint8(srcFamily) 1167 | var srcData []byte 1168 | if srcFamily == syscall.AF_INET { 1169 | srcData = srcIP.To4() 1170 | } else { 1171 | srcData = srcIP.To16() 1172 | } 1173 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_PREFSRC, srcData)) 1174 | } 1175 | 1176 | if gateway != "" { 1177 | gwIP := net.ParseIP(gateway) 1178 | if gwIP == nil { 1179 | return fmt.Errorf("gateway IP %s couldn't be parsed", gateway) 1180 | } 1181 | gwFamily := getIpFamily(gwIP) 1182 | if currentFamily != -1 && currentFamily != gwFamily { 1183 | return fmt.Errorf("gateway, source, and destination ip were not the same IP family") 1184 | } 1185 | msg.Family = uint8(gwFamily) 1186 | var gwData []byte 1187 | if gwFamily == syscall.AF_INET { 1188 | gwData = gwIP.To4() 1189 | } else { 1190 | gwData = gwIP.To16() 1191 | } 1192 | rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData)) 1193 | } 1194 | 1195 | wb.AddData(msg) 1196 | for _, attr := range rtAttrs { 1197 | wb.AddData(attr) 1198 | } 1199 | 1200 | iface, err := net.InterfaceByName(device) 1201 | if err != nil { 1202 | return err 1203 | } 1204 | wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index))) 1205 | 1206 | if err := s.Send(wb); err != nil { 1207 | return err 1208 | } 1209 | return s.HandleAck(wb.Seq) 1210 | } 1211 | 1212 | // Add a new default gateway. Identical to: 1213 | // ip route add default via $ip 1214 | func AddDefaultGw(ip, device string) error { 1215 | return AddRoute("", "", ip, device) 1216 | } 1217 | 1218 | // THIS CODE DOES NOT COMMUNICATE WITH KERNEL VIA RTNETLINK INTERFACE 1219 | // IT IS HERE FOR BACKWARDS COMPATIBILITY WITH OLDER LINUX KERNELS 1220 | // WHICH SHIP WITH OLDER NOT ENTIRELY FUNCTIONAL VERSION OF NETLINK 1221 | func getIfSocket() (fd int, err error) { 1222 | for _, socket := range []int{ 1223 | syscall.AF_INET, 1224 | syscall.AF_PACKET, 1225 | syscall.AF_INET6, 1226 | } { 1227 | if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil { 1228 | break 1229 | } 1230 | } 1231 | if err == nil { 1232 | return fd, nil 1233 | } 1234 | return -1, err 1235 | } 1236 | 1237 | // Create the actual bridge device. This is more backward-compatible than 1238 | // netlink.NetworkLinkAdd and works on RHEL 6. 1239 | func CreateBridge(name string, setMacAddr bool) error { 1240 | if len(name) >= IFNAMSIZ { 1241 | return fmt.Errorf("Interface name %s too long", name) 1242 | } 1243 | 1244 | s, err := getIfSocket() 1245 | if err != nil { 1246 | return err 1247 | } 1248 | defer syscall.Close(s) 1249 | 1250 | nameBytePtr, err := syscall.BytePtrFromString(name) 1251 | if err != nil { 1252 | return err 1253 | } 1254 | if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 { 1255 | return err 1256 | } 1257 | if setMacAddr { 1258 | return SetMacAddress(name, randMacAddr()) 1259 | } 1260 | return nil 1261 | } 1262 | 1263 | // Delete the actual bridge device. 1264 | func DeleteBridge(name string) error { 1265 | s, err := getIfSocket() 1266 | if err != nil { 1267 | return err 1268 | } 1269 | defer syscall.Close(s) 1270 | 1271 | nameBytePtr, err := syscall.BytePtrFromString(name) 1272 | if err != nil { 1273 | return err 1274 | } 1275 | 1276 | var ifr ifreqFlags 1277 | copy(ifr.IfrnName[:len(ifr.IfrnName)-1], []byte(name)) 1278 | if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), 1279 | syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifr))); err != 0 { 1280 | return err 1281 | } 1282 | 1283 | if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), 1284 | SIOC_BRDELBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 { 1285 | return err 1286 | } 1287 | return nil 1288 | } 1289 | 1290 | func ifIoctBridge(iface, master *net.Interface, op uintptr) error { 1291 | if len(master.Name) >= IFNAMSIZ { 1292 | return fmt.Errorf("Interface name %s too long", master.Name) 1293 | } 1294 | 1295 | s, err := getIfSocket() 1296 | if err != nil { 1297 | return err 1298 | } 1299 | defer syscall.Close(s) 1300 | 1301 | ifr := ifreqIndex{} 1302 | copy(ifr.IfrnName[:len(ifr.IfrnName)-1], master.Name) 1303 | ifr.IfruIndex = int32(iface.Index) 1304 | 1305 | if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), op, uintptr(unsafe.Pointer(&ifr))); err != 0 { 1306 | return err 1307 | } 1308 | 1309 | return nil 1310 | } 1311 | 1312 | // Add a slave to a bridge device. This is more backward-compatible than 1313 | // netlink.NetworkSetMaster and works on RHEL 6. 1314 | func AddToBridge(iface, master *net.Interface) error { 1315 | return ifIoctBridge(iface, master, SIOC_BRADDIF) 1316 | } 1317 | 1318 | // Detach a slave from a bridge device. This is more backward-compatible than 1319 | // netlink.NetworkSetMaster and works on RHEL 6. 1320 | func DelFromBridge(iface, master *net.Interface) error { 1321 | return ifIoctBridge(iface, master, SIOC_BRDELIF) 1322 | } 1323 | 1324 | func randMacAddr() string { 1325 | hw := make(net.HardwareAddr, 6) 1326 | for i := 0; i < 6; i++ { 1327 | hw[i] = byte(rnd.Intn(255)) 1328 | } 1329 | hw[0] &^= 0x1 // clear multicast bit 1330 | hw[0] |= 0x2 // set local assignment bit (IEEE802) 1331 | return hw.String() 1332 | } 1333 | 1334 | func SetMacAddress(name, addr string) error { 1335 | if len(name) >= IFNAMSIZ { 1336 | return fmt.Errorf("Interface name %s too long", name) 1337 | } 1338 | 1339 | hw, err := net.ParseMAC(addr) 1340 | if err != nil { 1341 | return err 1342 | } 1343 | 1344 | s, err := getIfSocket() 1345 | if err != nil { 1346 | return err 1347 | } 1348 | defer syscall.Close(s) 1349 | 1350 | ifr := ifreqHwaddr{} 1351 | ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER 1352 | copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name) 1353 | 1354 | for i := 0; i < 6; i++ { 1355 | ifr.IfruHwaddr.Data[i] = ifrDataByte(hw[i]) 1356 | } 1357 | 1358 | if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 { 1359 | return err 1360 | } 1361 | return nil 1362 | } 1363 | 1364 | func SetHairpinMode(iface *net.Interface, enabled bool) error { 1365 | s, err := getNetlinkSocket() 1366 | if err != nil { 1367 | return err 1368 | } 1369 | defer s.Close() 1370 | req := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) 1371 | 1372 | msg := newIfInfomsg(syscall.AF_BRIDGE) 1373 | msg.Type = syscall.RTM_SETLINK 1374 | msg.Flags = syscall.NLM_F_REQUEST 1375 | msg.Index = int32(iface.Index) 1376 | msg.Change = DEFAULT_CHANGE 1377 | req.AddData(msg) 1378 | 1379 | mode := []byte{0} 1380 | if enabled { 1381 | mode[0] = byte(1) 1382 | } 1383 | 1384 | br := newRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil) 1385 | newRtAttrChild(br, IFLA_BRPORT_MODE, mode) 1386 | req.AddData(br) 1387 | if err := s.Send(req); err != nil { 1388 | return err 1389 | } 1390 | 1391 | return s.HandleAck(req.Seq) 1392 | } 1393 | 1394 | func ChangeName(iface *net.Interface, newName string) error { 1395 | if len(newName) >= IFNAMSIZ { 1396 | return fmt.Errorf("Interface name %s too long", newName) 1397 | } 1398 | 1399 | fd, err := getIfSocket() 1400 | if err != nil { 1401 | return err 1402 | } 1403 | defer syscall.Close(fd) 1404 | 1405 | data := [IFNAMSIZ * 2]byte{} 1406 | // the "-1"s here are very important for ensuring we get proper null 1407 | // termination of our new C strings 1408 | copy(data[:IFNAMSIZ-1], iface.Name) 1409 | copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName) 1410 | 1411 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 { 1412 | return errno 1413 | } 1414 | 1415 | return nil 1416 | } 1417 | -------------------------------------------------------------------------------- /netlink/netlink_linux_armppc64.go: -------------------------------------------------------------------------------- 1 | // +build arm ppc64 ppc64le 2 | 3 | package netlink 4 | 5 | func ifrDataByte(b byte) uint8 { 6 | return uint8(b) 7 | } 8 | -------------------------------------------------------------------------------- /netlink/netlink_linux_notarm.go: -------------------------------------------------------------------------------- 1 | // +build !arm,!ppc64,!ppc64le 2 | 3 | package netlink 4 | 5 | func ifrDataByte(b byte) int8 { 6 | return int8(b) 7 | } 8 | -------------------------------------------------------------------------------- /netlink/netlink_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package netlink 4 | 5 | import ( 6 | "errors" 7 | "net" 8 | ) 9 | 10 | var ( 11 | ErrNotImplemented = errors.New("not implemented") 12 | ) 13 | 14 | func NetworkGetRoutes() ([]Route, error) { 15 | return nil, ErrNotImplemented 16 | } 17 | 18 | func NetworkLinkAdd(name string, linkType string) error { 19 | return ErrNotImplemented 20 | } 21 | 22 | func NetworkLinkDel(name string) error { 23 | return ErrNotImplemented 24 | } 25 | 26 | func NetworkLinkUp(iface *net.Interface) error { 27 | return ErrNotImplemented 28 | } 29 | 30 | func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { 31 | return ErrNotImplemented 32 | } 33 | 34 | func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { 35 | return ErrNotImplemented 36 | } 37 | 38 | func AddRoute(destination, source, gateway, device string) error { 39 | return ErrNotImplemented 40 | } 41 | 42 | func AddDefaultGw(ip, device string) error { 43 | return ErrNotImplemented 44 | } 45 | 46 | func NetworkSetMTU(iface *net.Interface, mtu int) error { 47 | return ErrNotImplemented 48 | } 49 | 50 | func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error { 51 | return ErrNotImplemented 52 | } 53 | 54 | func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error { 55 | return ErrNotImplemented 56 | } 57 | 58 | func NetworkChangeName(iface *net.Interface, newName string) error { 59 | return ErrNotImplemented 60 | } 61 | 62 | func NetworkSetNsFd(iface *net.Interface, fd int) error { 63 | return ErrNotImplemented 64 | } 65 | 66 | func NetworkSetNsPid(iface *net.Interface, nspid int) error { 67 | return ErrNotImplemented 68 | } 69 | 70 | func NetworkSetMaster(iface, master *net.Interface) error { 71 | return ErrNotImplemented 72 | } 73 | 74 | func NetworkLinkDown(iface *net.Interface) error { 75 | return ErrNotImplemented 76 | } 77 | 78 | func CreateBridge(name string, setMacAddr bool) error { 79 | return ErrNotImplemented 80 | } 81 | 82 | func DeleteBridge(name string) error { 83 | return ErrNotImplemented 84 | } 85 | 86 | func AddToBridge(iface, master *net.Interface) error { 87 | return ErrNotImplemented 88 | } 89 | -------------------------------------------------------------------------------- /netlink/note.txt: -------------------------------------------------------------------------------- 1 | Clone from github.com/docker/libcontainer/netlink 2 | -------------------------------------------------------------------------------- /packet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // IPPacket offers some functions working with IPv4 (!) IP packets 8 | // packed for transmission wrapped into UDP 9 | type IPPacket []byte 10 | 11 | func (p *IPPacket) GetSize() int { 12 | return int((*p)[3]) | (int((*p)[2]) << 8) 13 | } 14 | 15 | // IPver returns 4 or 6 for IPv4 or IPv6 16 | func (p *IPPacket) IPver() int { 17 | if 4 == ((*p)[0] >> 4) { 18 | return 4 19 | } 20 | if 6 == ((*p)[0] >> 4) { 21 | return 6 22 | } 23 | return 0 24 | 25 | } 26 | 27 | // Dst returns [4]byte for destination of package 28 | func (p *IPPacket) Dst() [4]byte { 29 | return [4]byte{(*p)[16], (*p)[17], (*p)[18], (*p)[19]} 30 | } 31 | 32 | // DstV4 returns net.IP for destination of package 33 | func (p *IPPacket) DstV4() net.IP { 34 | return net.IPv4((*p)[16], (*p)[17], (*p)[18], (*p)[19]) 35 | } 36 | 37 | // Src returns [4]byte for source address of package 38 | func (p *IPPacket) Src() [4]byte { 39 | return [4]byte{(*p)[12], (*p)[13], (*p)[14], (*p)[15]} 40 | } 41 | 42 | // IsMulticast returns if IP destination looks like multicast 43 | func (p *IPPacket) IsMulticast() bool { 44 | return ((*p)[16] > 223) && ((*p)[16] < 240) 45 | } 46 | -------------------------------------------------------------------------------- /packet_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | testICMPPing = IPPacket([]byte{0x45, 0x00, 0x00, 0x54, 0x0e, 0xe1, 0x40, 0x00, 0x40, 0x01, 0xa4, 0x65, 0xc0, 0xa8, 0x03, 0x0f, 0xc0, 0xa8, 0x03, 0x03, 0x08, 0x00, 0xad, 0xf2, 0x6a, 0x8c, 0x00, 0x08, 0x4c, 0xee, 0xfc, 0x58, 0xab, 0x2e, 0x00, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}) 11 | testICMPPong = IPPacket([]byte{0x45, 0x00, 0x00, 0x54, 0xed, 0x4e, 0x00, 0x00, 0x40, 0x01, 0x05, 0xf8, 0xc0, 0xa8, 0x03, 0x03, 0xc0, 0xa8, 0x03, 0x0f, 0x00, 0x00, 0xb5, 0xf2, 0x6a, 0x8c, 0x00, 0x08, 0x4c, 0xee, 0xfc, 0x58, 0xab, 0x2e, 0x00, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}) 12 | ) 13 | 14 | func TestIPPacket_IPver(t *testing.T) { 15 | tests := []struct { 16 | name string 17 | p IPPacket 18 | want int 19 | }{ 20 | { 21 | name: "ping", 22 | p: testICMPPing, 23 | want: 4, 24 | }, 25 | { 26 | name: "pong", 27 | p: testICMPPong, 28 | want: 4, 29 | }, 30 | { 31 | name: "ipv6", 32 | p: IPPacket([]byte{6 << 4, 0}), 33 | want: 6, 34 | }, 35 | { 36 | name: "invalid", 37 | p: IPPacket([]byte{3 << 4, 0}), 38 | want: 0, 39 | }, 40 | } 41 | for _, tt := range tests { 42 | t.Run(tt.name, func(t *testing.T) { 43 | if got := tt.p.IPver(); got != tt.want { 44 | t.Errorf("IPPacket.IPver() = %v, want %v", got, tt.want) 45 | } 46 | }) 47 | } 48 | } 49 | 50 | func TestIPPacket_Dst(t *testing.T) { 51 | tests := []struct { 52 | name string 53 | p IPPacket 54 | want [4]byte 55 | }{ 56 | { 57 | name: "ping", 58 | p: testICMPPing, 59 | want: [4]byte{192, 168, 3, 3}, 60 | }, 61 | { 62 | name: "pong", 63 | p: testICMPPong, 64 | want: [4]byte{192, 168, 3, 15}, 65 | }, 66 | } 67 | for _, tt := range tests { 68 | t.Run(tt.name, func(t *testing.T) { 69 | if got := tt.p.Dst(); !reflect.DeepEqual(got, tt.want) { 70 | t.Errorf("IPPacket.Dst() = %v, want %v", got, tt.want) 71 | } 72 | }) 73 | } 74 | } 75 | 76 | func TestIPPacket_DstV4(t *testing.T) { 77 | tests := []struct { 78 | name string 79 | p IPPacket 80 | want net.IP 81 | }{ 82 | { 83 | name: "ping", 84 | p: testICMPPing, 85 | want: net.ParseIP("192.168.3.3"), 86 | }, 87 | { 88 | name: "pong", 89 | p: testICMPPong, 90 | want: net.ParseIP("192.168.3.15"), 91 | }, 92 | } 93 | for _, tt := range tests { 94 | t.Run(tt.name, func(t *testing.T) { 95 | if got := tt.p.DstV4(); !reflect.DeepEqual(got, tt.want) { 96 | t.Errorf("IPPacket.DstV4() = %v, want %v", got, tt.want) 97 | } 98 | }) 99 | } 100 | } 101 | 102 | func TestIPPacket_Src(t *testing.T) { 103 | tests := []struct { 104 | name string 105 | p IPPacket 106 | want [4]byte 107 | }{ 108 | { 109 | name: "ping", 110 | p: testICMPPing, 111 | want: [4]byte{192, 168, 3, 15}, 112 | }, 113 | { 114 | name: "pong", 115 | p: testICMPPong, 116 | want: [4]byte{192, 168, 3, 3}, 117 | }, 118 | } 119 | for _, tt := range tests { 120 | t.Run(tt.name, func(t *testing.T) { 121 | if got := tt.p.Src(); !reflect.DeepEqual(got, tt.want) { 122 | t.Errorf("IPPacket.Src() = %v, want %v", got, tt.want) 123 | } 124 | }) 125 | } 126 | } 127 | 128 | func TestIPPacket_IsMulticast(t *testing.T) { 129 | tests := []struct { 130 | name string 131 | p IPPacket 132 | want bool 133 | }{ 134 | { 135 | name: "230.0.0.1", 136 | p: IPPacket([]byte{4 << 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0, 1, 0}), 137 | want: true, 138 | }, 139 | { 140 | name: "ping", 141 | p: testICMPPing, 142 | want: false, 143 | }, 144 | { 145 | name: "pong", 146 | p: testICMPPong, 147 | want: false, 148 | }, 149 | } 150 | for _, tt := range tests { 151 | t.Run(tt.name, func(t *testing.T) { 152 | if got := tt.p.IsMulticast(); got != tt.want { 153 | t.Errorf("IPPacket.IsMulticast() = %v, want %v", got, tt.want) 154 | } 155 | }) 156 | } 157 | } 158 | 159 | func TestIPPacket_GetSize(t *testing.T) { 160 | tests := []struct { 161 | name string 162 | p IPPacket 163 | want int 164 | }{ 165 | { 166 | name: "ping", 167 | p: testICMPPing, 168 | want: 84, 169 | }, 170 | { 171 | name: "pong", 172 | p: testICMPPong, 173 | want: 84, 174 | }, 175 | } 176 | for _, tt := range tests { 177 | t.Run(tt.name, func(t *testing.T) { 178 | if got := tt.p.GetSize(); got != tt.want { 179 | t.Errorf("IPPacket.GetSize() = %v, want %v", got, tt.want) 180 | } 181 | }) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanocz/lcvpn/a869d49bd41d7c7f08e9671976e59f823461ec90/topology.png --------------------------------------------------------------------------------