VPPTap, not test yet

This commit is contained in:
KusakabeSi 2021-08-23 19:11:01 +00:00
parent 2beb19c224
commit 26ba4dbe94
28 changed files with 521 additions and 187 deletions

View File

@ -1,77 +1,9 @@
# Go Implementation of [WireGuard](https://www.wireguard.com/)
This is an implementation of WireGuard in Go.
## Usage
Most Linux kernel WireGuard users are used to adding an interface with `ip link add wg0 type wireguard`. With wireguard-go, instead simply run:
### Build
```
$ wireguard-go wg0
echo "deb [trusted=yes] https://packagecloud.io/fdio/release/ubuntu focal main" > /etc/apt/sources.list.d/99fd.io.list
curl -L https://packagecloud.io/fdio/release/gpgkey | sudo apt-key add -
apt-get update
apt-get install vpp vpp-plugin-core python3-vpp-api vpp-dbg vpp-dev libmemif libmemif-dev wireguard-tools
export CGO_CFLAGS="-I/usr/include/memif"
make
```
This will create an interface and fork into the background. To remove the interface, use the usual `ip link del wg0`, or if your system does not support removing interfaces directly, you may instead remove the control socket via `rm -f /var/run/wireguard/wg0.sock`, which will result in wireguard-go shutting down.
To run wireguard-go without forking to the background, pass `-f` or `--foreground`:
```
$ wireguard-go -f wg0
```
When an interface is running, you may use [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
## Platforms
### Linux
This will run on Linux; however you should instead use the kernel module, which is faster and better integrated into the OS. See the [installation page](https://www.wireguard.com/install/) for instructions.
### macOS
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
### Windows
This runs on Windows, but you should instead use it from the more [fully featured Windows app](https://git.zx2c4.com/wireguard-windows/about/), which uses this as a module.
### FreeBSD
This will run on FreeBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_USER_COOKIE`.
### OpenBSD
This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_RTABLE`. Since the tun driver cannot have arbitrary interface names, you must either use `tun[0-9]+` for an explicit interface name or `tun` to have the program select one for you. If you choose `tun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
## Building
This requires an installation of [go](https://golang.org) ≥ 1.16.
```
$ git clone https://git.zx2c4.com/wireguard-go
$ cd wireguard-go
$ make
```
## License
Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,5 +1,9 @@
package config
import (
"crypto/rand"
)
type EdgeConfig struct {
Interface InterfaceConf
NodeID Vertex
@ -25,13 +29,14 @@ type SuperConfig struct {
type InterfaceConf struct {
Itype string
IfaceID int
Name string
VPPIfaceID uint32
VPPBridgeID uint32
MacAddr string
MTU int
RecvAddr string
SendAddr string
HumanFriendly bool
DevFriendly bool
}
type PeerInfo struct {
@ -103,3 +108,19 @@ type HTTP_Peerinfo struct {
type HTTP_Peers struct {
Peers map[string]HTTP_Peerinfo
}
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandomStr(length int, defaults string) string {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return defaults
}
for i, b := range bytes {
bytes[i] = chars[b%byte(len(chars))]
}
return string(bytes)
}

View File

@ -6,10 +6,12 @@
package device
import (
"bytes"
"container/list"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"strconv"
"sync"
"sync/atomic"
@ -18,6 +20,7 @@ import (
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/path"
"gopkg.in/yaml.v2"
)
type Peer struct {
@ -304,7 +307,7 @@ func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
return
}
peer.Lock()
peer.device.SaveToConfig(peer, endpoint.DstToString())
peer.device.SaveToConfig(peer, endpoint)
peer.endpoint = endpoint
peer.Unlock()
}
@ -322,3 +325,52 @@ func (peer *Peer) GetEndpointDstStr() string {
}
return peer.endpoint.DstToString()
}
func (device *Device) SaveToConfig(peer *Peer, endpoint conn.Endpoint) {
if device.IsSuperNode { //Can't in super mode
return
}
if !device.DRoute.P2P.UseP2P { //Must in p2p mode
return
}
if peer.endpoint != nil && peer.endpoint.DstIP().Equal(endpoint.DstIP()) { //endpoint changed
return
}
if peer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).After(time.Now()) { //Peer alives
return
}
url := endpoint.DstToString()
foundInFile := false
pubkeystr := PubKey2Str(peer.handshake.remoteStatic)
pskstr := PSKeyStr(peer.handshake.presharedKey)
if bytes.Equal(peer.handshake.presharedKey[:], make([]byte, 32)) {
pskstr = ""
}
for _, peerfile := range device.EdgeConfig.Peers {
if peerfile.NodeID == peer.ID && peerfile.PubKey == pubkeystr {
foundInFile = true
if peerfile.Static == false {
peerfile.EndPoint = url
}
} else if peerfile.NodeID == peer.ID || peerfile.PubKey == pubkeystr {
panic("Found NodeID match " + strconv.Itoa(int(peer.ID)) + ", but PubKey Not match %s enrties in config file" + pubkeystr)
}
}
if !foundInFile {
device.EdgeConfig.Peers = append(device.EdgeConfig.Peers, config.PeerInfo{
NodeID: peer.ID,
PubKey: pubkeystr,
PSKey: pskstr,
EndPoint: url,
Static: false,
})
}
go device.SaveConfig()
}
func (device *Device) SaveConfig() {
if device.DRoute.SaveNewPeers {
configbytes, _ := yaml.Marshal(device.EdgeConfig)
ioutil.WriteFile(device.EdgeConfigPath, configbytes, 0666)
}
}

View File

@ -14,7 +14,6 @@ import (
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/path"
"gopkg.in/yaml.v2"
)
func (device *Device) SendPacket(peer *Peer, packet []byte, offset int) {
@ -375,46 +374,6 @@ func (device *Device) RoutineSetEndpoint() {
}
}
func (device *Device) SaveToConfig(thepeer *Peer, url string) {
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).After(time.Now()) {
//Peer alives
return
}
foundInFile := false
pubkeystr := PubKey2Str(thepeer.handshake.remoteStatic)
pskstr := PSKeyStr(thepeer.handshake.presharedKey)
if bytes.Equal(thepeer.handshake.presharedKey[:], make([]byte, 32)) {
pskstr = ""
}
for _, peerfile := range device.EdgeConfig.Peers {
if peerfile.NodeID == thepeer.ID && peerfile.PubKey == pubkeystr {
foundInFile = true
if peerfile.Static == false {
peerfile.EndPoint = url
}
} else if peerfile.NodeID == thepeer.ID || peerfile.PubKey == pubkeystr {
panic("Found NodeID match " + strconv.Itoa(int(thepeer.ID)) + ", but PubKey Not match %s enrties in config file" + pubkeystr)
}
}
if !foundInFile {
device.EdgeConfig.Peers = append(device.EdgeConfig.Peers, config.PeerInfo{
NodeID: thepeer.ID,
PubKey: pubkeystr,
PSKey: pskstr,
EndPoint: url,
Static: false,
})
}
go device.SaveConfig()
}
func (device *Device) SaveConfig() {
if device.DRoute.SaveNewPeers {
configbytes, _ := yaml.Marshal(device.EdgeConfig)
ioutil.WriteFile(device.EdgeConfigPath, configbytes, 0666)
}
}
func (device *Device) RoutineSendPing() {
if !(device.DRoute.P2P.UseP2P || device.DRoute.SuperNode.UseSuperNode) {
return
@ -430,11 +389,11 @@ func (device *Device) RoutineRegister() {
if !(device.DRoute.SuperNode.UseSuperNode) {
return
}
first := true
for {
body, _ := path.GetByte(path.RegisterMsg{
Node_id: device.ID,
Init: first,
PeerStateHash: device.peers.Peer_state,
NhStateHash: device.graph.NhTableHash,
})
buf := make([]byte, path.EgHeaderLen+len(body))
header, _ := path.NewEgHeader(buf[0:path.EgHeaderLen])
@ -446,7 +405,6 @@ func (device *Device) RoutineRegister() {
copy(buf[path.EgHeaderLen:], body)
device.Send2Super(buf, MessageTransportOffsetContent)
time.Sleep(path.S2TD(device.DRoute.SendPingInterval))
first = false
}
}

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
humanfriendly: true
devfriendly: true
nodeid: 1
nodename: Node01
privkey: aABzjKhWdkFfQ29ZuijtMp1h1TNJe66SDCwvfmvQznw=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
humanfriendly: true
devfriendly: true
nodeid: 2
nodename: Node02
privkey: UNZMzPX5fG/8yGC8edVj/ksF9N6ARRqdq7fqE/PD7ls=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4003
sendaddr: 127.0.0.1:5003
humanfriendly: true
devfriendly: true
nodeid: 3
nodename: Node03
privkey: gJy35nbsd8FuuxyWHjsefN+U+oM7RkuIB1EanNLSVHg=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4004
sendaddr: 127.0.0.1:5004
humanfriendly: true
devfriendly: true
nodeid: 4
nodename: Node04
privkey: wAdLgCk0SHiO11/aUf9944focD1BUCH5b6Pe+cRHHXQ=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4005
sendaddr: 127.0.0.1:5005
humanfriendly: true
devfriendly: true
nodeid: 5
nodename: Node05
privkey: gLmzeCbmN/hjiE+ehNXL9IxuG9hhWIYv2s16/DOW6FE=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4006
sendaddr: 127.0.0.1:5006
humanfriendly: true
devfriendly: true
nodeid: 6
nodename: Node06
privkey: IIX5F6oWZUS2dlhxWFJ7TxdJtDCr5jzeuhxUB6YM7Us=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
humanfriendly: true
devfriendly: true
nodeid: 1
nodename: Node01
privkey: aABzjKhWdkFfQ29ZuijtMp1h1TNJe66SDCwvfmvQznw=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
humanfriendly: true
devfriendly: true
nodeid: 2
nodename: Node02
privkey: UNZMzPX5fG/8yGC8edVj/ksF9N6ARRqdq7fqE/PD7ls=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4003
sendaddr: 127.0.0.1:5003
humanfriendly: true
devfriendly: true
nodeid: 3
nodename: Node03
privkey: gJy35nbsd8FuuxyWHjsefN+U+oM7RkuIB1EanNLSVHg=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4004
sendaddr: 127.0.0.1:5004
humanfriendly: true
devfriendly: true
nodeid: 4
nodename: Node04
privkey: wAdLgCk0SHiO11/aUf9944focD1BUCH5b6Pe+cRHHXQ=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4005
sendaddr: 127.0.0.1:5005
humanfriendly: true
devfriendly: true
nodeid: 5
nodename: Node05
privkey: gLmzeCbmN/hjiE+ehNXL9IxuG9hhWIYv2s16/DOW6FE=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4006
sendaddr: 127.0.0.1:5006
humanfriendly: true
devfriendly: true
nodeid: 6
nodename: Node06
privkey: IIX5F6oWZUS2dlhxWFJ7TxdJtDCr5jzeuhxUB6YM7Us=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
humanfriendly: true
devfriendly: true
nodeid: 1
nodename: Node01
privkey: 6GyDagZKhbm5WNqMiRHhkf43RlbMJ34IieTlIuvfJ1M=

View File

@ -6,7 +6,7 @@ interface:
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
humanfriendly: true
devfriendly: true
nodeid: 2
nodename: Node02
privkey: OH8BsVUU2Rqzeu9B2J5GPG8PUmxWfX8uVvNFZKhVF3o=

3
go.mod
View File

@ -3,7 +3,10 @@ module github.com/KusakabeSi/EtherGuardVPN
go 1.16
require (
git.fd.io/govpp.git v0.3.6-0.20210810100027-c0da1f2999a6
git.fd.io/govpp.git/extras v0.0.0-20210810100027-c0da1f2999a6
github.com/KusakabeSi/go-cache v0.0.0-20210823132304-22b5b1d22b41
github.com/sirupsen/logrus v1.8.1
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
gopkg.in/yaml.v2 v2.4.0

View File

@ -29,13 +29,13 @@ func printExampleEdgeConf() {
tconfig := config.EdgeConfig{
Interface: config.InterfaceConf{
Itype: "stdio",
IfaceID: 5,
VPPIfaceID: 5,
Name: "tap1",
MacAddr: "AA:BB:CC:DD:EE:FF",
MTU: 1400,
RecvAddr: "127.0.0.1:4001",
SendAddr: "127.0.0.1:5001",
HumanFriendly: true,
DevFriendly: true,
},
NodeID: 1,
NodeName: "Node01",
@ -165,12 +165,12 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
case "dummy":
thetap, err = tap.CreateDummyTAP()
case "stdio":
thetap, err = tap.CreateStdIOTAP(tconfig.Interface.Name, tconfig.Interface.HumanFriendly)
thetap, err = tap.CreateStdIOTAP(tconfig.Interface.Name, tconfig.Interface.DevFriendly)
case "udpsock":
{
lis, _ := net.ResolveUDPAddr("udp", tconfig.Interface.RecvAddr)
sen, _ := net.ResolveUDPAddr("udp", tconfig.Interface.SendAddr)
thetap, err = tap.CreateUDPSockTAP(tconfig.Interface.Name, lis, sen, tconfig.Interface.HumanFriendly)
thetap, err = tap.CreateUDPSockTAP(tconfig.Interface.Name, lis, sen, tconfig.Interface.DevFriendly)
}
}
if err != nil {

View File

@ -1,6 +1,7 @@
package main
import (
"bytes"
"net"
"strconv"
@ -15,6 +16,7 @@ var (
http_graph *path.IG
http_device4 *device.Device
http_device6 *device.Device
http_HashSalt []byte
http_NhTable_Hash [32]byte
http_PeerInfo_hash [32]byte
http_NhTableStr []byte
@ -40,24 +42,58 @@ type client struct {
func get_peerinfo(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
PubKey, _ := params["PubKey"]
State, _ := params["State"]
if state := http_PeerState[PubKey[0]]; state != nil {
copy(http_PeerState[PubKey[0]].PeerInfoState[:], State[0])
PubKeyA, has := params["PubKey"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
StateA, has := params["State"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
PubKey := PubKeyA[0]
State := StateA[0]
if bytes.Equal(http_PeerInfo_hash[:], []byte(State)) {
if state := http_PeerState[PubKey]; state != nil {
copy(http_PeerState[PubKey].PeerInfoState[:], State)
w.WriteHeader(http.StatusOK)
w.Write([]byte(http_PeerInfoStr))
return
}
}
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
}
func get_nhtable(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
PubKey, _ := params["PubKey"]
State, _ := params["State"]
if state := http_PeerState[PubKey[0]]; state != nil {
copy(http_PeerState[PubKey[0]].NhTableState[:], State[0])
PubKeyA, has := params["PubKey"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
StateA, has := params["State"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
PubKey := PubKeyA[0]
State := StateA[0]
if bytes.Equal(http_NhTable_Hash[:], []byte(State)) {
if state := http_PeerState[PubKey]; state != nil {
copy(http_PeerState[PubKey].NhTableState[:], State)
w.WriteHeader(http.StatusOK)
w.Write([]byte(http_NhTableStr))
return
}
}
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
}
func HttpServer(http_port int, apiprefix string) {

View File

@ -97,6 +97,7 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
http_PeerState = make(map[string]*PeerState)
http_PeerID2Map = make(map[config.Vertex]string)
http_PeerInfos.Peers = make(map[string]config.HTTP_Peerinfo)
http_HashSalt = []byte(config.RandomStr(32, "Salt generate failed"))
super_chains := path.SUPER_Events{
Event_server_pong: make(chan path.PongMsg, 1<<5),
@ -193,10 +194,8 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
for {
select {
case reg_msg := <-events.Event_server_register:
if reg_msg.Init == true {
copy(http_PeerState[http_PeerID2Map[reg_msg.Node_id]].NhTableState[:], make([]byte, 32))
copy(http_PeerState[http_PeerID2Map[reg_msg.Node_id]].PeerInfoState[:], make([]byte, 32))
}
copy(http_PeerState[http_PeerID2Map[reg_msg.Node_id]].NhTableState[:], reg_msg.NhStateHash[:])
copy(http_PeerState[http_PeerID2Map[reg_msg.Node_id]].PeerInfoState[:], reg_msg.PeerStateHash[:])
PubKey := http_PeerID2Map[reg_msg.Node_id]
if peer := http_device4.LookupPeerByStr(PubKey); peer != nil {
if connstr := peer.GetEndpointDstStr(); connstr != "" {
@ -209,7 +208,7 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
}
}
http_PeerInfoStr, _ = json.Marshal(&http_PeerInfos)
PeerInfo_hash_raw := md5.Sum(http_PeerInfoStr)
PeerInfo_hash_raw := md5.Sum(append(http_PeerInfoStr, http_HashSalt...))
PeerInfo_hash_str := hex.EncodeToString(PeerInfo_hash_raw[:])
PeerInfo_hash_str_byte := []byte(PeerInfo_hash_str)
if bytes.Equal(http_PeerInfo_hash[:], PeerInfo_hash_str_byte) == false {
@ -230,7 +229,7 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
if changed {
NhTable := graph.GetNHTable(false)
NhTablestr, _ := json.Marshal(NhTable)
md5_hash_raw := md5.Sum(http_NhTableStr)
md5_hash_raw := md5.Sum(append(http_NhTableStr, http_HashSalt...))
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
new_hash_str_byte := []byte(new_hash_str)
copy(http_NhTable_Hash[:], new_hash_str_byte)

View File

@ -21,7 +21,8 @@ func GetByte(structIn interface{}) (bb []byte, err error) {
type RegisterMsg struct {
Node_id config.Vertex `struc:"uint32"`
Init bool
PeerStateHash [32]byte
NhStateHash [32]byte
}
func (c *RegisterMsg) ToString() string {

View File

@ -7,7 +7,6 @@ package tap
import (
"bytes"
"os"
)
type Event int
@ -39,7 +38,6 @@ const (
)
type Device interface {
File() *os.File // returns the file descriptor of the device
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
Flush() error // flush all previous writes to the device

View File

@ -1,9 +1,5 @@
package tap
import (
"os"
)
type DummyTap struct {
stopRead chan struct{}
events chan Event
@ -23,10 +19,6 @@ func CreateDummyTAP() (tapdev Device, err error) {
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *DummyTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *DummyTap) Read([]byte, int) (int, error) {
_ = <-tap.stopRead
return 0, nil

View File

@ -47,10 +47,6 @@ func CreateStdIOTAP(interfaceName string, HumanFriendly bool) (tapdev Device, er
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *StdIOTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *StdIOTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
size, err := os.Stdin.Read(buf[offset+10:])

View File

@ -3,7 +3,6 @@ package tap
import (
"fmt"
"net"
"os"
)
type UdpSockTap struct {
@ -38,11 +37,6 @@ func CreateUDPSockTAP(interfaceName string, listenAddr *net.UDPAddr, sendAddr *n
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *UdpSockTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *UdpSockTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
size, _, err := tap.recv.ReadFromUDP(buf[offset+10:])

352
tap/tap_vpp.go Normal file
View File

@ -0,0 +1,352 @@
package tap
import (
"context"
"errors"
"fmt"
"log"
"os"
"sync"
"git.fd.io/govpp.git"
"git.fd.io/govpp.git/adapter/socketclient"
"git.fd.io/govpp.git/binapi/ethernet_types"
interfaces "git.fd.io/govpp.git/binapi/interface"
"git.fd.io/govpp.git/binapi/interface_types"
"git.fd.io/govpp.git/binapi/l2"
"git.fd.io/govpp.git/binapi/memif"
"git.fd.io/govpp.git/extras/libmemif"
"github.com/KusakabeSi/EtherGuardVPN/config"
logger "github.com/sirupsen/logrus"
)
const (
ENV_VPP_MEMIF_SOCKET_DIR = "VPP_MEMIF_SOCKET_DIR"
ENV_VPP_SOCKET_PATH = "VPP_API_SOCKET_PATH"
)
var (
//read from env
vppMemifSocketDir = "/var/run/eggo-vpp"
vppApiSocketPath = socketclient.DefaultSocketName // Path to VPP binary API socket file, default is /run/vpp/api.
//internal
NumQueues = uint8(1)
onConnectWg sync.WaitGroup
tunErrorChannel chan error
)
type VppTap struct {
name string
mtu int
ifuid uint32
memifSockPath string
SwIfIndex interface_types.InterfaceIndex
secret string
memif *libmemif.Memif
RxQueues int
RxintCh <-chan uint8
RxintChNext chan uint8
RxintErrCh <-chan error
TxQueues int
TxCount uint
logger *logger.Logger
errors chan error // async error handling
events chan Event
}
// New creates and returns a new TUN interface for the application.
func CreateVppTAP(interfaceName string, iconfig config.InterfaceConf, loglevel string) (tapdev Device, err error) {
// Setup TUN Config
// Set logger
log := logger.New()
log.Out = os.Stdout
log.Level = func() logger.Level {
switch loglevel {
case "verbose", "debug":
return logger.DebugLevel
case "error":
return logger.ErrorLevel
case "silent":
return logger.PanicLevel
}
return logger.ErrorLevel
}()
libmemif.SetLogger(log)
// connect to VPP
conn, err := govpp.Connect(vppApiSocketPath)
if err != nil {
log.Fatalln("ERROR: connecting to VPP failed:", err)
return nil, err
}
defer conn.Disconnect()
// create a channel
ch, err := conn.NewAPIChannel()
if err != nil {
log.Fatalln("ERROR: creating channel failed:", err)
return nil, err
}
defer ch.Close()
if err := ch.CheckCompatiblity(&memif.MemifSocketFilenameAddDel{}, &memif.MemifCreate{}, &memif.MemifDelete{}); err != nil {
return nil, err
}
if err := ch.CheckCompatiblity(&interfaces.SwInterfaceSetFlags{}, &interfaces.SwInterfaceSetMtu{}); err != nil {
return nil, err
}
if err := ch.CheckCompatiblity(&l2.L2fibAddDel{}, &l2.SwInterfaceSetL2Bridge{}); err != nil {
return nil, err
}
memifservice := memif.NewServiceClient(conn)
l2service := l2.NewServiceClient(conn)
interfacservice := interfaces.NewServiceClient(conn)
vppIfMacAddr, err := ethernet_types.ParseMacAddress(iconfig.MacAddr)
if err != nil {
log.Fatalln("ERROR: Failed parse mac address:", iconfig.MacAddr)
return nil, err
}
tap := &VppTap{
name: iconfig.Name,
mtu: iconfig.MTU,
ifuid: iconfig.VPPIfaceID,
SwIfIndex: 0,
memifSockPath: vppMemifSocketDir + "/" + iconfig.Name,
secret: config.RandomStr(16, iconfig.Name),
logger: log,
errors: make(chan error, 1<<5),
events: make(chan Event, 1<<4),
}
// create memif socket id 1 filename /tmp/icmp-responder-example
_, err = memifservice.MemifSocketFilenameAddDel(context.Background(), &memif.MemifSocketFilenameAddDel{
IsAdd: true,
SocketID: iconfig.VPPIfaceID,
SocketFilename: tap.memifSockPath,
})
if err != nil {
return nil, err
}
// create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
memifCreateReply, err := memifservice.MemifCreate(context.Background(), &memif.MemifCreate{
Role: memif.MEMIF_ROLE_API_SLAVE,
Mode: memif.MEMIF_MODE_API_ETHERNET,
RxQueues: NumQueues, // MEMIF_DEFAULT_RX_QUEUES
TxQueues: NumQueues, // MEMIF_DEFAULT_TX_QUEUES
ID: tap.ifuid,
SocketID: tap.ifuid,
RingSize: 1024, // MEMIF_DEFAULT_RING_SIZE
BufferSize: 2048, // MEMIF_DEFAULT_BUFFER_SIZE 2048
NoZeroCopy: true,
HwAddr: vppIfMacAddr,
Secret: tap.secret,
})
if err != nil {
return nil, err
}
tap.SwIfIndex = memifCreateReply.SwIfIndex
// set int state memif1/1 up
_, err = interfacservice.SwInterfaceSetFlags(context.Background(), &interfaces.SwInterfaceSetFlags{
SwIfIndex: tap.SwIfIndex,
Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
})
if err != nil {
return nil, err
}
//set interface l2 bridge memif1/1 4242
_, err = l2service.SwInterfaceSetL2Bridge(context.Background(), &l2.SwInterfaceSetL2Bridge{
RxSwIfIndex: tap.SwIfIndex,
BdID: iconfig.VPPBridgeID,
PortType: l2.L2_API_PORT_TYPE_NORMAL,
Shg: 0,
Enable: true,
})
if err != nil {
return nil, err
}
//init libmemif
libmemif.Init(tap.name)
onConnectWg.Add(1)
memifCallbacks := &libmemif.MemifCallbacks{
OnConnect: OnConnect,
OnDisconnect: OnDisconnect,
}
// Prepare memif1 configuration.
memifConfig := &libmemif.MemifConfig{
MemifMeta: libmemif.MemifMeta{
IfName: tap.name,
ConnID: tap.ifuid,
SocketFilename: tap.memifSockPath,
Secret: tap.secret,
IsMaster: true,
Mode: libmemif.IfModeEthernet,
},
MemifShmSpecs: libmemif.MemifShmSpecs{
NumRxQueues: NumQueues,
NumTxQueues: NumQueues,
BufferSize: 2048,
Log2RingSize: 10,
},
}
// Create memif1 interface.
memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
if err != nil {
tap.logger.Errorf("libmemif.CreateInterface() error: %v\n", err)
return nil, err
}
onConnectWg.Wait()
_, err = interfacservice.SwInterfaceSetMtu(context.Background(), &interfaces.SwInterfaceSetMtu{
SwIfIndex: tap.SwIfIndex,
Mtu: []uint32{uint32(tap.mtu)},
})
if err != nil {
return nil, err
}
tap.memif = memif
details, err := memif.GetDetails()
tap.RxQueues = len(details.RxQueues)
tap.RxintCh = memif.GetInterruptChan()
tap.RxintErrCh = memif.GetInterruptErrorChan()
tap.TxQueues = len(details.TxQueues)
tapdev.Events() <- EventUp
return
}
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *VppTap) Read(buf []byte, offset int) (n int, err error) {
select {
case err = <-tap.RxintErrCh:
tap.logger.Errorf("libmemif.Memif.RxintErr() error: %v\n", err)
return 0, err
case err = <-tap.errors:
if err == nil {
err = errors.New("Device closed")
}
tap.logger.Errorf("tun error: %v\n", err)
return 0, err
case queueID := <-tap.RxintCh:
select {
case tap.RxintChNext <- queueID:
{
// Use non-blocking write to prevent program stuck
}
default:
tap.logger.Debugln("Buffer full")
}
case queueID := <-tap.RxintChNext:
packets, err := tap.memif.RxBurst(queueID, 1)
if err != nil {
tap.logger.Errorf("libmemif.Memif.RxBurst() error: %v\n", err)
return 0, err
}
if len(packets) == 0 {
// No more packets to read until the next interrupt.
return 0, nil
}
for _, packetData := range packets {
select {
case tap.RxintChNext <- queueID:
{
// Use non-blocking write to prevent program stuck
// repeatedly call RxBurst() until returns an empty slice of packets
}
default:
tap.logger.Debugln("Buffer full")
}
n = copy(buf[offset:], packetData)
}
}
return
} // read a packet from the device (without any additional headers)
func (tap *VppTap) Write(buf []byte, offset int) (size int, err error) {
queueID := tap.getTxQueueID()
buf = buf[offset:]
n, err := tap.memif.TxBurst(queueID, []libmemif.RawPacketData{buf})
return len(buf) * int(n), err
} // writes a packet to the device (without any additional headers)
func (tap *VppTap) Flush() error {
return nil
} // flush all previous writes to the device
func (tap *VppTap) MTU() (int, error) {
return tap.mtu, nil
} // returns the MTU of the device
func (tap *VppTap) Name() (string, error) {
return tap.name, nil
} // fetches and returns the current name
func (tap *VppTap) Events() chan Event {
return tap.events
} // returns a constant channel of events related to the device
func (tap *VppTap) Close() error {
// connect to VPP
conn, err := govpp.Connect(vppApiSocketPath)
if err != nil {
log.Fatalln("ERROR: connecting to VPP failed:", err)
}
defer conn.Disconnect()
// create a channel
ch, err := conn.NewAPIChannel()
if err != nil {
log.Fatalln("ERROR: creating channel failed:", err)
}
defer ch.Close()
memifservice := memif.NewServiceClient(conn)
tap.memif.Close()
libmemif.Cleanup()
// delete interface memif memif1/1
_, err = memifservice.MemifDelete(context.Background(), &memif.MemifDelete{
SwIfIndex: tap.SwIfIndex,
})
// delete memif socket id 1
_, err = memifservice.MemifSocketFilenameAddDel(context.Background(), &memif.MemifSocketFilenameAddDel{
IsAdd: false,
SocketID: tap.ifuid,
SocketFilename: tap.memifSockPath,
})
tap.events <- EventDown
close(tap.events)
return nil
} // stops the device and closes the event channel
// OnConnect is called when a memif connection gets established.
func OnConnect(memif *libmemif.Memif) (err error) {
details, err := memif.GetDetails()
if err != nil {
fmt.Printf("libmemif.GetDetails() error: %v\n", err)
}
fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
onConnectWg.Done()
return nil
}
// OnDisconnect is called when a memif connection is lost.
func OnDisconnect(memif *libmemif.Memif) (err error) {
tunErrorChannel <- errors.New(fmt.Sprintf("memif %s has been disconnected", memif.IfName))
return nil
}
func (tun *VppTap) getTxQueueID() uint8 {
if tun.TxQueues == 1 {
return 0
}
tun.TxCount++
return uint8(tun.TxCount % uint(tun.TxQueues))
}