mirror of https://github.com/jetkvm/kvm.git
Compare commits
3 Commits
3004dcd808
...
6e25d44597
Author | SHA1 | Date |
---|---|---|
|
6e25d44597 | |
|
cb7da61ab4 | |
|
748bfe5477 |
11
go.mod
11
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/hanwen/go-fuse/v2 v2.5.1
|
||||
github.com/hashicorp/go-getter/v2 v2.2.3
|
||||
github.com/jellydator/ttlcache/v3 v3.3.0
|
||||
github.com/pion/logging v0.2.2
|
||||
github.com/pion/logging v0.2.3
|
||||
github.com/pion/mdns/v2 v2.0.7
|
||||
github.com/pion/webrtc/v4 v4.0.16
|
||||
github.com/pojntfx/go-nbd v0.3.2
|
||||
|
@ -33,8 +33,8 @@ require (
|
|||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
go.bug.st/serial v1.6.2
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sys v0.32.0
|
||||
)
|
||||
|
||||
|
@ -62,6 +62,7 @@ require (
|
|||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
|
@ -96,8 +97,8 @@ require (
|
|||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
golang.org/x/arch v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
29
go.sum
29
go.sum
|
@ -34,12 +34,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
|||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/logger v1.2.6 h1:EPolruKUTzNXMVBD9LuAFQmRjTs7AH7yKGuXgYqrKWc=
|
||||
github.com/gin-contrib/logger v1.2.6/go.mod h1:7niPrd7F0Nscw/zvgz8RiGJxSdbKM2yfQNy8xCHcm64=
|
||||
github.com/gin-contrib/logger v1.2.5 h1:qVQI4omayQecuN4zX9ZZnsOq7w9J/ZLds3J/FMn8ypM=
|
||||
github.com/gin-contrib/logger v1.2.5/go.mod h1:/bj+vNMuA2xOEQ1aRHoJ1m9+uyaaXIAxQTvM2llsc6I=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
|
@ -95,6 +95,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
|
@ -201,24 +202,24 @@ go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
|
|||
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -230,8 +231,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
@ -39,7 +39,7 @@ type testNetworkConfig struct {
|
|||
IPv6Mode null.String `json:"ipv6_mode" one_of:"slaac,dhcpv6,slaac_and_dhcpv6,static,link_local,disabled" default:"slaac"`
|
||||
IPv6Static *testIPv6StaticConfig `json:"ipv6_static,omitempty" required_if:"IPv6Mode=static"`
|
||||
|
||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,basic,all" default:"basic"`
|
||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,rx_only,tx_only,enabled" default:"enabled"`
|
||||
LLDPTxTLVs []string `json:"lldp_tx_tlvs,omitempty" one_of:"chassis,port,system,vlan" default:"chassis,port,system,vlan"`
|
||||
MDNSMode null.String `json:"mdns_mode,omitempty" one_of:"disabled,auto,ipv4_only,ipv6_only" default:"auto"`
|
||||
TimeSyncMode null.String `json:"time_sync_mode,omitempty" one_of:"ntp_only,ntp_and_http,http_only,custom" default:"ntp_and_http"`
|
||||
|
|
|
@ -18,7 +18,6 @@ const (
|
|||
|
||||
func afPacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
|
||||
frameSize int, blockSize int, numBlocks int, err error) {
|
||||
|
||||
if snaplen < pageSize {
|
||||
frameSize = pageSize / (pageSize / snaplen)
|
||||
} else {
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
// Copyright 2018 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
// afpacket provides a simple example of using afpacket with zero-copy to read
|
||||
// packet data.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/afpacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
_ "github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
const ETH_P_LLDP = 0x88cc
|
||||
|
||||
var bpfFilter = []bpf.RawInstruction{
|
||||
{0x28, 0, 0, 0x0000000c},
|
||||
{0x15, 0, 5, 0x000088cc},
|
||||
{0x20, 0, 0, 0x00000002},
|
||||
{0x15, 0, 3, 0xc200000e},
|
||||
{0x28, 0, 0, 0x00000000},
|
||||
{0x15, 0, 1, 0x00000180},
|
||||
{0x6, 0, 0, 0x00040000},
|
||||
{0x6, 0, 0, 0x00000000},
|
||||
}
|
||||
|
||||
func rawSocketaddrFromMAC(mac net.HardwareAddr) (sockaddr syscall.RawSockaddr) {
|
||||
for i, n := range mac {
|
||||
sockaddr.Data[i] = uint8(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
iface = flag.String("i", "any", "Interface to read from")
|
||||
cpuprofile = flag.String("cpuprofile", "", "If non-empty, write CPU profile here")
|
||||
snaplen = flag.Int("s", 0, "Snaplen, if <= 0, use 65535")
|
||||
bufferSize = flag.Int("b", 8, "Interface buffersize (MB)")
|
||||
filter = flag.String("f", "port not 22", "BPF filter")
|
||||
count = flag.Int64("c", -1, "If >= 0, # of packets to capture before returning")
|
||||
verbose = flag.Int64("log_every", 1, "Write a log every X packets")
|
||||
addVLAN = flag.Bool("add_vlan", false, "If true, add VLAN header")
|
||||
)
|
||||
|
||||
type afpacketHandle struct {
|
||||
TPacket *afpacket.TPacket
|
||||
}
|
||||
|
||||
const IFNAMSIZ = 16
|
||||
|
||||
type ifreq struct {
|
||||
ifrName [IFNAMSIZ]byte
|
||||
ifrHwaddr syscall.RawSockaddr
|
||||
}
|
||||
|
||||
// addMulticastAddr adds a multicast address to an interface using an ioctl call
|
||||
func addMulticastAddr(intf string, addr string) error {
|
||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
var name [IFNAMSIZ]byte
|
||||
copy(name[:], []byte(intf))
|
||||
|
||||
mac, _ := net.ParseMAC(addr)
|
||||
ifr := &ifreq{
|
||||
ifrName: name,
|
||||
ifrHwaddr: rawSocketaddrFromMAC(mac),
|
||||
}
|
||||
|
||||
_, _, ep := unix.Syscall(unix.SYS_IOCTL, uintptr(fd),
|
||||
unix.SIOCADDMULTI, uintptr(unsafe.Pointer(ifr)))
|
||||
|
||||
if ep != 0 {
|
||||
return syscall.Errno(ep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newAfpacketHandle(device string, snaplen int, block_size int, num_blocks int,
|
||||
useVLAN bool, timeout time.Duration) (*afpacketHandle, error) {
|
||||
|
||||
h := &afpacketHandle{}
|
||||
var err error
|
||||
|
||||
if device == "any" {
|
||||
h.TPacket, err = afpacket.NewTPacket(
|
||||
afpacket.OptFrameSize(snaplen),
|
||||
afpacket.OptBlockSize(block_size),
|
||||
afpacket.OptNumBlocks(num_blocks),
|
||||
afpacket.OptAddVLANHeader(useVLAN),
|
||||
afpacket.OptPollTimeout(timeout),
|
||||
afpacket.SocketRaw,
|
||||
afpacket.TPacketVersion3)
|
||||
} else {
|
||||
h.TPacket, err = afpacket.NewTPacket(
|
||||
afpacket.OptInterface(device),
|
||||
afpacket.OptFrameSize(snaplen),
|
||||
afpacket.OptBlockSize(block_size),
|
||||
afpacket.OptNumBlocks(num_blocks),
|
||||
afpacket.OptAddVLANHeader(useVLAN),
|
||||
afpacket.OptPollTimeout(timeout),
|
||||
afpacket.SocketRaw,
|
||||
afpacket.TPacketVersion3)
|
||||
}
|
||||
return h, err
|
||||
}
|
||||
|
||||
// ZeroCopyReadPacketData satisfies ZeroCopyPacketDataSource interface
|
||||
func (h *afpacketHandle) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
return h.TPacket.ZeroCopyReadPacketData()
|
||||
}
|
||||
|
||||
// LinkType returns ethernet link type.
|
||||
func (h *afpacketHandle) LinkType() layers.LinkType {
|
||||
return layers.LinkTypeEthernet
|
||||
}
|
||||
|
||||
// Close will close afpacket source.
|
||||
func (h *afpacketHandle) Close() {
|
||||
h.TPacket.Close()
|
||||
}
|
||||
|
||||
// SocketStats prints received, dropped, queue-freeze packet stats.
|
||||
func (h *afpacketHandle) SocketStats() (as afpacket.SocketStats, asv afpacket.SocketStatsV3, err error) {
|
||||
return h.TPacket.SocketStats()
|
||||
}
|
||||
|
||||
// afpacketComputeSize computes the block_size and the num_blocks in such a way that the
|
||||
// allocated mmap buffer is close to but smaller than target_size_mb.
|
||||
// The restriction is that the block_size must be divisible by both the
|
||||
// frame size and page size.
|
||||
func afpacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
|
||||
frameSize int, blockSize int, numBlocks int, err error) {
|
||||
|
||||
if snaplen < pageSize {
|
||||
frameSize = pageSize / (pageSize / snaplen)
|
||||
} else {
|
||||
frameSize = (snaplen/pageSize + 1) * pageSize
|
||||
}
|
||||
|
||||
// 128 is the default from the gopacket library so just use that
|
||||
blockSize = frameSize * 128
|
||||
numBlocks = (targetSizeMb * 1024 * 1024) / blockSize
|
||||
|
||||
if numBlocks == 0 {
|
||||
return 0, 0, 0, fmt.Errorf("Interface buffersize is too small")
|
||||
}
|
||||
|
||||
return frameSize, blockSize, numBlocks, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *cpuprofile != "" {
|
||||
log.Printf("Writing CPU profile to %q", *cpuprofile)
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
log.Printf("Starting on interface %q", *iface)
|
||||
if *snaplen <= 0 {
|
||||
*snaplen = 65535
|
||||
}
|
||||
szFrame, szBlock, numBlocks, err := afpacketComputeSize(*bufferSize, *snaplen, os.Getpagesize())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *addVLAN {
|
||||
log.Printf("Adding VLAN header")
|
||||
}
|
||||
|
||||
lldpPrefix := "01:80:c2:00:00"
|
||||
for _, lastByte := range []byte{0x00, 0x03, 0x0e} {
|
||||
// Add multicast address so that the kernel does not discard it
|
||||
if err := addMulticastAddr(*iface, fmt.Sprintf("%s:%02X", lldpPrefix, lastByte)); err != nil {
|
||||
log.Fatalf("Failed to add multicast address: %s", err)
|
||||
}
|
||||
}
|
||||
afpacketHandle, err := newAfpacketHandle(*iface, szFrame, szBlock, numBlocks, *addVLAN, -time.Millisecond*10)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = afpacketHandle.TPacket.SetBPF(bpfFilter)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
source := gopacket.NewPacketSource(afpacketHandle.TPacket, afpacketHandle.LinkType())
|
||||
defer afpacketHandle.Close()
|
||||
|
||||
for packet := range source.Packets() {
|
||||
fmt.Println("packet", packet)
|
||||
lldpLayer := packet.Layer(layers.LayerTypeLinkLayerDiscovery)
|
||||
if lldpLayer != nil {
|
||||
fmt.Println("lldpLayer", lldpLayer)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package lldp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
|
@ -16,6 +18,9 @@ type LLDP struct {
|
|||
l *zerolog.Logger
|
||||
tPacket *afpacket.TPacket
|
||||
pktSource *gopacket.PacketSource
|
||||
rxCtx context.Context
|
||||
rxCancel context.CancelFunc
|
||||
rxLock sync.Mutex
|
||||
|
||||
enableRx bool
|
||||
enableTx bool
|
||||
|
@ -53,16 +58,49 @@ func NewLLDP(opts *LLDPOptions) *LLDP {
|
|||
}
|
||||
|
||||
func (l *LLDP) Start() error {
|
||||
l.rxLock.Lock()
|
||||
defer l.rxLock.Unlock()
|
||||
|
||||
if l.rxCtx != nil {
|
||||
l.l.Info().Msg("LLDP already started")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.rxCtx, l.rxCancel = context.WithCancel(context.Background())
|
||||
|
||||
if l.enableRx {
|
||||
l.l.Info().Msg("setting up AF_PACKET")
|
||||
if err := l.setUpCapture(); err != nil {
|
||||
l.l.Error().Err(err).Msg("unable to set up AF_PACKET")
|
||||
return err
|
||||
}
|
||||
l.startCapture()
|
||||
if err := l.startCapture(); err != nil {
|
||||
l.l.Error().Err(err).Msg("unable to start capture")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
go l.neighbors.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LLDP) Stop() error {
|
||||
l.rxLock.Lock()
|
||||
defer l.rxLock.Unlock()
|
||||
|
||||
if l.rxCancel != nil {
|
||||
l.rxCancel()
|
||||
l.rxCancel = nil
|
||||
l.rxCtx = nil
|
||||
}
|
||||
|
||||
if l.enableRx {
|
||||
_ = l.shutdownCapture()
|
||||
}
|
||||
|
||||
l.neighbors.Stop()
|
||||
l.neighbors.DeleteAll()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -51,5 +51,7 @@ func (l *LLDP) GetNeighbors() []Neighbor {
|
|||
neighbors = append(neighbors, item.Value())
|
||||
}
|
||||
|
||||
l.l.Info().Interface("neighbors", neighbors).Msg("neighbors")
|
||||
|
||||
return neighbors
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ var (
|
|||
|
||||
// from lldpd
|
||||
// https://github.com/lldpd/lldpd/blob/9034c9332cca0c8b1a20e1287f0e5fed81f7eb2a/src/daemon/lldpd.h#L246
|
||||
//
|
||||
//nolint:govet
|
||||
var bpfFilter = []bpf.RawInstruction{
|
||||
{0x30, 0, 0, 0x00000000}, {0x54, 0, 0, 0x00000001}, {0x15, 0, 16, 0x00000001},
|
||||
{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000088cc},
|
||||
|
@ -99,9 +101,15 @@ func (l *LLDP) startCapture() error {
|
|||
go func() {
|
||||
logger.Info().Msg("starting capture LLDP ethernet frames")
|
||||
|
||||
for packet := range l.pktSource.Packets() {
|
||||
if err := l.handlePacket(packet, &logger); err != nil {
|
||||
logger.Error().Msgf("error handling packet: %s", err)
|
||||
for {
|
||||
select {
|
||||
case <-l.rxCtx.Done():
|
||||
logger.Info().Msg("shutting down LLDP capture")
|
||||
return
|
||||
case packet := <-l.pktSource.Packets():
|
||||
if err := l.handlePacket(packet, &logger); err != nil {
|
||||
logger.Error().Msgf("error handling packet: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -232,7 +240,7 @@ func (l *LLDP) handlePacketCDP(mac string, raw *layers.CiscoDiscovery, info *lay
|
|||
}
|
||||
|
||||
if len(info.MgmtAddresses) > 0 {
|
||||
n.ManagementAddress = fmt.Sprintf("%s", info.MgmtAddresses[0])
|
||||
n.ManagementAddress = string(info.MgmtAddresses[0])
|
||||
}
|
||||
|
||||
l.addNeighbor(mac, *n, ttl)
|
||||
|
@ -242,11 +250,13 @@ func (l *LLDP) handlePacketCDP(mac string, raw *layers.CiscoDiscovery, info *lay
|
|||
|
||||
func (l *LLDP) shutdownCapture() error {
|
||||
if l.tPacket != nil {
|
||||
l.l.Info().Msg("closing TPacket")
|
||||
l.tPacket.Close()
|
||||
l.tPacket = nil
|
||||
}
|
||||
|
||||
if l.pktSource != nil {
|
||||
l.l.Info().Msg("closing packet source")
|
||||
l.pktSource = nil
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ type NetworkConfig struct {
|
|||
IPv6Mode null.String `json:"ipv6_mode,omitempty" one_of:"slaac,dhcpv6,slaac_and_dhcpv6,static,link_local,disabled" default:"slaac"`
|
||||
IPv6Static *IPv6StaticConfig `json:"ipv6_static,omitempty" required_if:"IPv6Mode=static"`
|
||||
|
||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,basic,all" default:"basic"`
|
||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,rx_only,tx_only,basic,all,enabled" default:"enabled"`
|
||||
LLDPTxTLVs []string `json:"lldp_tx_tlvs,omitempty" one_of:"chassis,port,system,vlan" default:"chassis,port,system,vlan"`
|
||||
MDNSMode null.String `json:"mdns_mode,omitempty" one_of:"disabled,auto,ipv4_only,ipv6_only" default:"auto"`
|
||||
TimeSyncMode null.String `json:"time_sync_mode,omitempty" one_of:"ntp_only,ntp_and_http,http_only,custom" default:"ntp_and_http"`
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/lldp"
|
||||
)
|
||||
|
||||
func (s *NetworkInterfaceState) shouldStartLLDP() bool {
|
||||
if s.lldp == nil {
|
||||
s.l.Trace().Msg("LLDP not initialized")
|
||||
return false
|
||||
}
|
||||
|
||||
s.l.Trace().Msgf("LLDP mode: %s", s.config.LLDPMode.String)
|
||||
|
||||
return s.config.LLDPMode.String != "disabled"
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) startLLDP() {
|
||||
if !s.shouldStartLLDP() || s.lldp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Trace().Msg("starting LLDP")
|
||||
if err := s.lldp.Start(); err != nil {
|
||||
s.l.Error().Err(err).Msg("unable to start LLDP")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) stopLLDP() {
|
||||
if s.lldp == nil {
|
||||
return
|
||||
}
|
||||
s.l.Trace().Msg("stopping LLDP")
|
||||
if err := s.lldp.Stop(); err != nil {
|
||||
s.l.Error().Err(err).Msg("unable to stop LLDP")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) GetLLDPNeighbors() ([]lldp.Neighbor, error) {
|
||||
if s.lldp == nil {
|
||||
return nil, errors.New("lldp not initialized")
|
||||
}
|
||||
return s.lldp.GetNeighbors(), nil
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/confparser"
|
||||
"github.com/jetkvm/kvm/internal/lldp"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/jetkvm/kvm/internal/udhcpc"
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -29,6 +30,8 @@ type NetworkInterfaceState struct {
|
|||
config *NetworkConfig
|
||||
dhcpClient *udhcpc.DHCPClient
|
||||
|
||||
lldp *lldp.LLDP
|
||||
|
||||
defaultHostname string
|
||||
currentHostname string
|
||||
currentFqdn string
|
||||
|
@ -96,8 +99,16 @@ func NewNetworkInterfaceState(opts *NetworkInterfaceOptions) (*NetworkInterfaceS
|
|||
},
|
||||
})
|
||||
|
||||
s.dhcpClient = dhcpClient
|
||||
// create the lldp service
|
||||
lldpClient := lldp.NewLLDP(&lldp.LLDPOptions{
|
||||
InterfaceName: opts.InterfaceName,
|
||||
EnableRx: true,
|
||||
EnableTx: true,
|
||||
Logger: l,
|
||||
})
|
||||
|
||||
s.dhcpClient = dhcpClient
|
||||
s.lldp = lldpClient
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -310,14 +321,30 @@ func (s *NetworkInterfaceState) update() (DhcpTargetState, error) {
|
|||
}
|
||||
|
||||
if initialCheck {
|
||||
s.onInitialCheck(s)
|
||||
s.handleInitialCheck()
|
||||
} else if changed {
|
||||
s.onStateChange(s)
|
||||
s.handleStateChange()
|
||||
}
|
||||
|
||||
return dhcpTargetState, nil
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) handleInitialCheck() {
|
||||
if s.IsUp() {
|
||||
s.startLLDP()
|
||||
}
|
||||
s.onInitialCheck(s)
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) handleStateChange() {
|
||||
if s.IsUp() {
|
||||
s.startLLDP()
|
||||
} else {
|
||||
s.stopLLDP()
|
||||
}
|
||||
s.onStateChange(s)
|
||||
}
|
||||
|
||||
func (s *NetworkInterfaceState) CheckAndUpdateDhcp() error {
|
||||
dhcpTargetState, err := s.update()
|
||||
if err != nil {
|
||||
|
|
|
@ -1104,4 +1104,5 @@ var rpcHandlers = map[string]RPCHandler{
|
|||
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
||||
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
|
||||
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
|
||||
"getLLDPNeighbors": {Func: rpcGetLLDPNeighbors},
|
||||
}
|
||||
|
|
14
network.go
14
network.go
|
@ -32,16 +32,6 @@ func networkStateChanged() {
|
|||
func initNetwork() error {
|
||||
ensureConfigLoaded()
|
||||
|
||||
lldp := lldp.NewLLDP(&lldp.LLDPOptions{
|
||||
InterfaceName: NetIfName,
|
||||
EnableRx: true,
|
||||
EnableTx: true,
|
||||
Logger: networkLogger,
|
||||
})
|
||||
if err := lldp.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state, err := network.NewNetworkInterfaceState(&network.NetworkInterfaceOptions{
|
||||
DefaultHostname: GetDefaultHostname(),
|
||||
InterfaceName: NetIfName,
|
||||
|
@ -116,3 +106,7 @@ func rpcSetNetworkSettings(settings network.RpcNetworkSettings) (*network.RpcNet
|
|||
func rpcRenewDHCPLease() error {
|
||||
return networkState.RpcRenewDHCPLease()
|
||||
}
|
||||
|
||||
func rpcGetLLDPNeighbors() ([]lldp.Neighbor, error) {
|
||||
return networkState.GetLLDPNeighbors()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { LLDPNeighbor } from "../hooks/stores";
|
||||
import { LifeTimeLabel } from "../routes/devices.$id.settings.network";
|
||||
|
||||
import { GridCard } from "./Card";
|
||||
|
||||
export default function LLDPNeighCard({
|
||||
neighbors,
|
||||
}: {
|
||||
neighbors: LLDPNeighbor[];
|
||||
}) {
|
||||
return (
|
||||
<GridCard>
|
||||
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
LLDP Neighbors
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
{neighbors.map(neighbor => (
|
||||
<div className="space-y-3" key={neighbor.mac}>
|
||||
<h4 className="text-sm font-semibold font-mono">{neighbor.mac}</h4>
|
||||
<div
|
||||
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Interface
|
||||
</span>
|
||||
<span className="text-sm font-medium">{neighbor.port_description}</span>
|
||||
</div>
|
||||
|
||||
{neighbor.system_name && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
System Name
|
||||
</span>
|
||||
<span className="text-sm font-medium">{neighbor.system_name}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{neighbor.system_description && (
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
System Description
|
||||
</span>
|
||||
<span className="text-sm font-medium">{neighbor.system_description}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{neighbor.port_id && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Port ID
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{neighbor.port_id}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{neighbor.port_description && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Port Description
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{neighbor.port_description}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
);
|
||||
}
|
|
@ -741,7 +741,7 @@ export type IPv6Mode =
|
|||
| "link_local"
|
||||
| "unknown";
|
||||
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
||||
export type LLDPMode = "disabled" | "basic" | "all" | "unknown";
|
||||
export type LLDPMode = "disabled" | "basic" | "all" | "tx_only" | "rx_only" | "unknown";
|
||||
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
||||
export type TimeSyncMode =
|
||||
| "ntp_only"
|
||||
|
@ -761,6 +761,19 @@ export interface NetworkSettings {
|
|||
time_sync_mode: TimeSyncMode;
|
||||
}
|
||||
|
||||
export interface LLDPNeighbor {
|
||||
mac: string;
|
||||
source: string;
|
||||
chassis_id: string;
|
||||
port_id: string;
|
||||
port_description: string;
|
||||
system_name: string;
|
||||
system_description: string;
|
||||
ttl: number | null;
|
||||
management_address: string | null;
|
||||
values: Record<string, string>;
|
||||
}
|
||||
|
||||
export const useNetworkStateStore = create<NetworkState>((set, get) => ({
|
||||
setNetworkState: (state: NetworkState) => set(state),
|
||||
setDhcpLease: (lease: NetworkState["dhcp_lease"]) => set({ dhcp_lease: lease }),
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
IPv4Mode,
|
||||
IPv6Mode,
|
||||
LLDPMode,
|
||||
LLDPNeighbor,
|
||||
mDNSMode,
|
||||
NetworkSettings,
|
||||
NetworkState,
|
||||
|
@ -29,6 +30,7 @@ import AutoHeight from "../components/AutoHeight";
|
|||
import DhcpLeaseCard from "../components/DhcpLeaseCard";
|
||||
|
||||
import { SettingsItem } from "./devices.$id.settings";
|
||||
import LLDPNeighCard from "../components/LLDPNeighCard";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
|
@ -88,6 +90,14 @@ export default function SettingsNetworkRoute() {
|
|||
const [customDomain, setCustomDomain] = useState<string>("");
|
||||
const [selectedDomainOption, setSelectedDomainOption] = useState<string>("dhcp");
|
||||
|
||||
const [lldpNeighbors, setLldpNeighbors] = useState<LLDPNeighbor[] | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
send("getLLDPNeighbors", {}, resp => {
|
||||
if ("error" in resp) return;
|
||||
setLldpNeighbors(resp.result as LLDPNeighbor[]);
|
||||
});
|
||||
}, [send]);
|
||||
|
||||
useEffect(() => {
|
||||
if (networkSettings.domain && networkSettingsLoaded) {
|
||||
// Check if the domain is one of the predefined options
|
||||
|
@ -130,7 +140,7 @@ export default function SettingsNetworkRoute() {
|
|||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
"Failed to save network settings: " +
|
||||
(resp.error.data ? resp.error.data : resp.error.message),
|
||||
(resp.error.data ? resp.error.data : resp.error.message),
|
||||
);
|
||||
setNetworkSettingsLoaded(true);
|
||||
return;
|
||||
|
@ -402,7 +412,7 @@ export default function SettingsNetworkRoute() {
|
|||
</SettingsItem>
|
||||
<AutoHeight>
|
||||
{!networkSettingsLoaded &&
|
||||
!(networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0) ? (
|
||||
!(networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0) ? (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
|
@ -428,22 +438,49 @@ export default function SettingsNetworkRoute() {
|
|||
)}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
<div className="hidden space-y-4">
|
||||
<SettingsItem
|
||||
title="LLDP"
|
||||
description="Control which TLVs will be sent over Link Layer Discovery Protocol"
|
||||
>
|
||||
|
||||
<div className="space-y-4">
|
||||
<SettingsItem title="LLDP" description="Configure the LLDP mode">
|
||||
<SelectMenuBasic
|
||||
size="SM"
|
||||
value={networkSettings.lldp_mode}
|
||||
onChange={e => handleLldpModeChange(e.target.value)}
|
||||
options={filterUnknown([
|
||||
{ value: "disabled", label: "Disabled" },
|
||||
{ value: "basic", label: "Basic" },
|
||||
{ value: "all", label: "All" },
|
||||
{ value: "tx_only", label: "Tx only" },
|
||||
{ value: "rx_only", label: "Rx only" },
|
||||
{ value: "basic", label: "Tx Minimal + Rx" },
|
||||
{ value: "all", label: "Tx Detailed + Rx" },
|
||||
{ value: "enabled", label: "Enabled" },
|
||||
])}
|
||||
/>
|
||||
</SettingsItem>
|
||||
<AutoHeight>
|
||||
{lldpNeighbors === undefined ? (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
LLDP Neighbors
|
||||
</h3>
|
||||
<div className="animate-pulse space-y-3">
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/2 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
) : lldpNeighbors.length > 0 ? (
|
||||
<LLDPNeighCard neighbors={lldpNeighbors} />
|
||||
) : (
|
||||
<EmptyCard
|
||||
IconElm={LuEthernetPort}
|
||||
headline="LLDP Neighbors"
|
||||
description="No LLDP neighbors found"
|
||||
/>
|
||||
)}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
</Fieldset>
|
||||
<ConfirmDialog
|
||||
|
|
Loading…
Reference in New Issue