mirror of
https://github.com/KusakabeShi/EtherGuard-VPN.git
synced 2024-11-07 16:04:00 +01:00
VPPTap, not test yet
This commit is contained in:
parent
2beb19c224
commit
26ba4dbe94
84
README.md
84
README.md
@ -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
|
||||
```
|
||||
|
||||
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.
|
||||
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
|
||||
```
|
@ -1,5 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
type EdgeConfig struct {
|
||||
Interface InterfaceConf
|
||||
NodeID Vertex
|
||||
@ -24,14 +28,15 @@ type SuperConfig struct {
|
||||
}
|
||||
|
||||
type InterfaceConf struct {
|
||||
Itype string
|
||||
IfaceID int
|
||||
Name string
|
||||
MacAddr string
|
||||
MTU int
|
||||
RecvAddr string
|
||||
SendAddr string
|
||||
HumanFriendly bool
|
||||
Itype string
|
||||
Name string
|
||||
VPPIfaceID uint32
|
||||
VPPBridgeID uint32
|
||||
MacAddr string
|
||||
MTU int
|
||||
RecvAddr string
|
||||
SendAddr string
|
||||
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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
Node_id: device.ID,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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
3
go.mod
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(http_PeerInfoStr))
|
||||
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
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(http_NhTableStr))
|
||||
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) {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:])
|
||||
|
@ -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
352
tap/tap_vpp.go
Normal 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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user