mirror of https://github.com/jetkvm/kvm.git
Compare commits
1 Commits
6e25d44597
...
3004dcd808
Author | SHA1 | Date |
---|---|---|
|
3004dcd808 |
11
go.mod
11
go.mod
|
@ -20,7 +20,7 @@ require (
|
||||||
github.com/hanwen/go-fuse/v2 v2.5.1
|
github.com/hanwen/go-fuse/v2 v2.5.1
|
||||||
github.com/hashicorp/go-getter/v2 v2.2.3
|
github.com/hashicorp/go-getter/v2 v2.2.3
|
||||||
github.com/jellydator/ttlcache/v3 v3.3.0
|
github.com/jellydator/ttlcache/v3 v3.3.0
|
||||||
github.com/pion/logging v0.2.3
|
github.com/pion/logging v0.2.2
|
||||||
github.com/pion/mdns/v2 v2.0.7
|
github.com/pion/mdns/v2 v2.0.7
|
||||||
github.com/pion/webrtc/v4 v4.0.16
|
github.com/pion/webrtc/v4 v4.0.16
|
||||||
github.com/pojntfx/go-nbd v0.3.2
|
github.com/pojntfx/go-nbd v0.3.2
|
||||||
|
@ -33,8 +33,8 @@ require (
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/vishvananda/netlink v1.3.0
|
github.com/vishvananda/netlink v1.3.0
|
||||||
go.bug.st/serial v1.6.2
|
go.bug.st/serial v1.6.2
|
||||||
golang.org/x/crypto v0.37.0
|
golang.org/x/crypto v0.36.0
|
||||||
golang.org/x/net v0.39.0
|
golang.org/x/net v0.38.0
|
||||||
golang.org/x/sys v0.32.0
|
golang.org/x/sys v0.32.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +62,6 @@ require (
|
||||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-version v1.1.0 // indirect
|
github.com/hashicorp/go-version v1.1.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // 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/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
@ -97,8 +96,8 @@ require (
|
||||||
github.com/wlynxg/anet v0.0.5 // indirect
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
golang.org/x/arch v0.17.0 // indirect
|
golang.org/x/arch v0.17.0 // indirect
|
||||||
golang.org/x/oauth2 v0.24.0 // indirect
|
golang.org/x/oauth2 v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // 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/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 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
github.com/gin-contrib/logger v1.2.5 h1:qVQI4omayQecuN4zX9ZZnsOq7w9J/ZLds3J/FMn8ypM=
|
github.com/gin-contrib/logger v1.2.6 h1:EPolruKUTzNXMVBD9LuAFQmRjTs7AH7yKGuXgYqrKWc=
|
||||||
github.com/gin-contrib/logger v1.2.5/go.mod h1:/bj+vNMuA2xOEQ1aRHoJ1m9+uyaaXIAxQTvM2llsc6I=
|
github.com/gin-contrib/logger v1.2.6/go.mod h1:7niPrd7F0Nscw/zvgz8RiGJxSdbKM2yfQNy8xCHcm64=
|
||||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
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-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/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 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
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=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
@ -95,7 +95,6 @@ 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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
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 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
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=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
@ -202,24 +201,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.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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
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/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-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.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
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-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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.12.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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -231,8 +230,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 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
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=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
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"`
|
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"`
|
IPv6Static *testIPv6StaticConfig `json:"ipv6_static,omitempty" required_if:"IPv6Mode=static"`
|
||||||
|
|
||||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,rx_only,tx_only,enabled" default:"enabled"`
|
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,basic,all" default:"basic"`
|
||||||
LLDPTxTLVs []string `json:"lldp_tx_tlvs,omitempty" one_of:"chassis,port,system,vlan" default:"chassis,port,system,vlan"`
|
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"`
|
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"`
|
TimeSyncMode null.String `json:"time_sync_mode,omitempty" one_of:"ntp_only,ntp_and_http,http_only,custom" default:"ntp_and_http"`
|
||||||
|
|
|
@ -18,6 +18,7 @@ const (
|
||||||
|
|
||||||
func afPacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
|
func afPacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
|
||||||
frameSize int, blockSize int, numBlocks int, err error) {
|
frameSize int, blockSize int, numBlocks int, err error) {
|
||||||
|
|
||||||
if snaplen < pageSize {
|
if snaplen < pageSize {
|
||||||
frameSize = pageSize / (pageSize / snaplen)
|
frameSize = pageSize / (pageSize / snaplen)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
// 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,8 +1,6 @@
|
||||||
package lldp
|
package lldp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
|
@ -18,9 +16,6 @@ type LLDP struct {
|
||||||
l *zerolog.Logger
|
l *zerolog.Logger
|
||||||
tPacket *afpacket.TPacket
|
tPacket *afpacket.TPacket
|
||||||
pktSource *gopacket.PacketSource
|
pktSource *gopacket.PacketSource
|
||||||
rxCtx context.Context
|
|
||||||
rxCancel context.CancelFunc
|
|
||||||
rxLock sync.Mutex
|
|
||||||
|
|
||||||
enableRx bool
|
enableRx bool
|
||||||
enableTx bool
|
enableTx bool
|
||||||
|
@ -58,49 +53,16 @@ func NewLLDP(opts *LLDPOptions) *LLDP {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LLDP) Start() error {
|
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 {
|
if l.enableRx {
|
||||||
l.l.Info().Msg("setting up AF_PACKET")
|
l.l.Info().Msg("setting up AF_PACKET")
|
||||||
if err := l.setUpCapture(); err != nil {
|
if err := l.setUpCapture(); err != nil {
|
||||||
l.l.Error().Err(err).Msg("unable to set up AF_PACKET")
|
l.l.Error().Err(err).Msg("unable to set up AF_PACKET")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := l.startCapture(); err != nil {
|
l.startCapture()
|
||||||
l.l.Error().Err(err).Msg("unable to start capture")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go l.neighbors.Start()
|
go l.neighbors.Start()
|
||||||
|
|
||||||
return nil
|
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,7 +51,5 @@ func (l *LLDP) GetNeighbors() []Neighbor {
|
||||||
neighbors = append(neighbors, item.Value())
|
neighbors = append(neighbors, item.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
l.l.Info().Interface("neighbors", neighbors).Msg("neighbors")
|
|
||||||
|
|
||||||
return neighbors
|
return neighbors
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,6 @@ var (
|
||||||
|
|
||||||
// from lldpd
|
// from lldpd
|
||||||
// https://github.com/lldpd/lldpd/blob/9034c9332cca0c8b1a20e1287f0e5fed81f7eb2a/src/daemon/lldpd.h#L246
|
// https://github.com/lldpd/lldpd/blob/9034c9332cca0c8b1a20e1287f0e5fed81f7eb2a/src/daemon/lldpd.h#L246
|
||||||
//
|
|
||||||
//nolint:govet
|
|
||||||
var bpfFilter = []bpf.RawInstruction{
|
var bpfFilter = []bpf.RawInstruction{
|
||||||
{0x30, 0, 0, 0x00000000}, {0x54, 0, 0, 0x00000001}, {0x15, 0, 16, 0x00000001},
|
{0x30, 0, 0, 0x00000000}, {0x54, 0, 0, 0x00000001}, {0x15, 0, 16, 0x00000001},
|
||||||
{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000088cc},
|
{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000088cc},
|
||||||
|
@ -101,15 +99,9 @@ func (l *LLDP) startCapture() error {
|
||||||
go func() {
|
go func() {
|
||||||
logger.Info().Msg("starting capture LLDP ethernet frames")
|
logger.Info().Msg("starting capture LLDP ethernet frames")
|
||||||
|
|
||||||
for {
|
for packet := range l.pktSource.Packets() {
|
||||||
select {
|
if err := l.handlePacket(packet, &logger); err != nil {
|
||||||
case <-l.rxCtx.Done():
|
logger.Error().Msgf("error handling packet: %s", err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -240,7 +232,7 @@ func (l *LLDP) handlePacketCDP(mac string, raw *layers.CiscoDiscovery, info *lay
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(info.MgmtAddresses) > 0 {
|
if len(info.MgmtAddresses) > 0 {
|
||||||
n.ManagementAddress = string(info.MgmtAddresses[0])
|
n.ManagementAddress = fmt.Sprintf("%s", info.MgmtAddresses[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
l.addNeighbor(mac, *n, ttl)
|
l.addNeighbor(mac, *n, ttl)
|
||||||
|
@ -250,13 +242,11 @@ func (l *LLDP) handlePacketCDP(mac string, raw *layers.CiscoDiscovery, info *lay
|
||||||
|
|
||||||
func (l *LLDP) shutdownCapture() error {
|
func (l *LLDP) shutdownCapture() error {
|
||||||
if l.tPacket != nil {
|
if l.tPacket != nil {
|
||||||
l.l.Info().Msg("closing TPacket")
|
|
||||||
l.tPacket.Close()
|
l.tPacket.Close()
|
||||||
l.tPacket = nil
|
l.tPacket = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.pktSource != nil {
|
if l.pktSource != nil {
|
||||||
l.l.Info().Msg("closing packet source")
|
|
||||||
l.pktSource = nil
|
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"`
|
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"`
|
IPv6Static *IPv6StaticConfig `json:"ipv6_static,omitempty" required_if:"IPv6Mode=static"`
|
||||||
|
|
||||||
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,rx_only,tx_only,basic,all,enabled" default:"enabled"`
|
LLDPMode null.String `json:"lldp_mode,omitempty" one_of:"disabled,basic,all" default:"basic"`
|
||||||
LLDPTxTLVs []string `json:"lldp_tx_tlvs,omitempty" one_of:"chassis,port,system,vlan" default:"chassis,port,system,vlan"`
|
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"`
|
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"`
|
TimeSyncMode null.String `json:"time_sync_mode,omitempty" one_of:"ntp_only,ntp_and_http,http_only,custom" default:"ntp_and_http"`
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
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,7 +6,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jetkvm/kvm/internal/confparser"
|
"github.com/jetkvm/kvm/internal/confparser"
|
||||||
"github.com/jetkvm/kvm/internal/lldp"
|
|
||||||
"github.com/jetkvm/kvm/internal/logging"
|
"github.com/jetkvm/kvm/internal/logging"
|
||||||
"github.com/jetkvm/kvm/internal/udhcpc"
|
"github.com/jetkvm/kvm/internal/udhcpc"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -30,8 +29,6 @@ type NetworkInterfaceState struct {
|
||||||
config *NetworkConfig
|
config *NetworkConfig
|
||||||
dhcpClient *udhcpc.DHCPClient
|
dhcpClient *udhcpc.DHCPClient
|
||||||
|
|
||||||
lldp *lldp.LLDP
|
|
||||||
|
|
||||||
defaultHostname string
|
defaultHostname string
|
||||||
currentHostname string
|
currentHostname string
|
||||||
currentFqdn string
|
currentFqdn string
|
||||||
|
@ -99,16 +96,8 @@ func NewNetworkInterfaceState(opts *NetworkInterfaceOptions) (*NetworkInterfaceS
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// create the lldp service
|
|
||||||
lldpClient := lldp.NewLLDP(&lldp.LLDPOptions{
|
|
||||||
InterfaceName: opts.InterfaceName,
|
|
||||||
EnableRx: true,
|
|
||||||
EnableTx: true,
|
|
||||||
Logger: l,
|
|
||||||
})
|
|
||||||
|
|
||||||
s.dhcpClient = dhcpClient
|
s.dhcpClient = dhcpClient
|
||||||
s.lldp = lldpClient
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,30 +310,14 @@ func (s *NetworkInterfaceState) update() (DhcpTargetState, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if initialCheck {
|
if initialCheck {
|
||||||
s.handleInitialCheck()
|
s.onInitialCheck(s)
|
||||||
} else if changed {
|
} else if changed {
|
||||||
s.handleStateChange()
|
s.onStateChange(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dhcpTargetState, nil
|
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 {
|
func (s *NetworkInterfaceState) CheckAndUpdateDhcp() error {
|
||||||
dhcpTargetState, err := s.update()
|
dhcpTargetState, err := s.update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1104,5 +1104,4 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
||||||
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
|
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
|
||||||
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
|
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
|
||||||
"getLLDPNeighbors": {Func: rpcGetLLDPNeighbors},
|
|
||||||
}
|
}
|
||||||
|
|
14
network.go
14
network.go
|
@ -32,6 +32,16 @@ func networkStateChanged() {
|
||||||
func initNetwork() error {
|
func initNetwork() error {
|
||||||
ensureConfigLoaded()
|
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{
|
state, err := network.NewNetworkInterfaceState(&network.NetworkInterfaceOptions{
|
||||||
DefaultHostname: GetDefaultHostname(),
|
DefaultHostname: GetDefaultHostname(),
|
||||||
InterfaceName: NetIfName,
|
InterfaceName: NetIfName,
|
||||||
|
@ -106,7 +116,3 @@ func rpcSetNetworkSettings(settings network.RpcNetworkSettings) (*network.RpcNet
|
||||||
func rpcRenewDHCPLease() error {
|
func rpcRenewDHCPLease() error {
|
||||||
return networkState.RpcRenewDHCPLease()
|
return networkState.RpcRenewDHCPLease()
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetLLDPNeighbors() ([]lldp.Neighbor, error) {
|
|
||||||
return networkState.GetLLDPNeighbors()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
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"
|
| "link_local"
|
||||||
| "unknown";
|
| "unknown";
|
||||||
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
||||||
export type LLDPMode = "disabled" | "basic" | "all" | "tx_only" | "rx_only" | "unknown";
|
export type LLDPMode = "disabled" | "basic" | "all" | "unknown";
|
||||||
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
||||||
export type TimeSyncMode =
|
export type TimeSyncMode =
|
||||||
| "ntp_only"
|
| "ntp_only"
|
||||||
|
@ -761,19 +761,6 @@ export interface NetworkSettings {
|
||||||
time_sync_mode: TimeSyncMode;
|
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) => ({
|
export const useNetworkStateStore = create<NetworkState>((set, get) => ({
|
||||||
setNetworkState: (state: NetworkState) => set(state),
|
setNetworkState: (state: NetworkState) => set(state),
|
||||||
setDhcpLease: (lease: NetworkState["dhcp_lease"]) => set({ dhcp_lease: lease }),
|
setDhcpLease: (lease: NetworkState["dhcp_lease"]) => set({ dhcp_lease: lease }),
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
IPv4Mode,
|
IPv4Mode,
|
||||||
IPv6Mode,
|
IPv6Mode,
|
||||||
LLDPMode,
|
LLDPMode,
|
||||||
LLDPNeighbor,
|
|
||||||
mDNSMode,
|
mDNSMode,
|
||||||
NetworkSettings,
|
NetworkSettings,
|
||||||
NetworkState,
|
NetworkState,
|
||||||
|
@ -30,7 +29,6 @@ import AutoHeight from "../components/AutoHeight";
|
||||||
import DhcpLeaseCard from "../components/DhcpLeaseCard";
|
import DhcpLeaseCard from "../components/DhcpLeaseCard";
|
||||||
|
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
import LLDPNeighCard from "../components/LLDPNeighCard";
|
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
@ -90,14 +88,6 @@ export default function SettingsNetworkRoute() {
|
||||||
const [customDomain, setCustomDomain] = useState<string>("");
|
const [customDomain, setCustomDomain] = useState<string>("");
|
||||||
const [selectedDomainOption, setSelectedDomainOption] = useState<string>("dhcp");
|
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(() => {
|
useEffect(() => {
|
||||||
if (networkSettings.domain && networkSettingsLoaded) {
|
if (networkSettings.domain && networkSettingsLoaded) {
|
||||||
// Check if the domain is one of the predefined options
|
// Check if the domain is one of the predefined options
|
||||||
|
@ -140,7 +130,7 @@ export default function SettingsNetworkRoute() {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
"Failed to save network settings: " +
|
"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);
|
setNetworkSettingsLoaded(true);
|
||||||
return;
|
return;
|
||||||
|
@ -412,7 +402,7 @@ export default function SettingsNetworkRoute() {
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
<AutoHeight>
|
<AutoHeight>
|
||||||
{!networkSettingsLoaded &&
|
{!networkSettingsLoaded &&
|
||||||
!(networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0) ? (
|
!(networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0) ? (
|
||||||
<GridCard>
|
<GridCard>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
@ -438,49 +428,22 @@ export default function SettingsNetworkRoute() {
|
||||||
)}
|
)}
|
||||||
</AutoHeight>
|
</AutoHeight>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="hidden space-y-4">
|
||||||
<div className="space-y-4">
|
<SettingsItem
|
||||||
<SettingsItem title="LLDP" description="Configure the LLDP mode">
|
title="LLDP"
|
||||||
|
description="Control which TLVs will be sent over Link Layer Discovery Protocol"
|
||||||
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
value={networkSettings.lldp_mode}
|
value={networkSettings.lldp_mode}
|
||||||
onChange={e => handleLldpModeChange(e.target.value)}
|
onChange={e => handleLldpModeChange(e.target.value)}
|
||||||
options={filterUnknown([
|
options={filterUnknown([
|
||||||
{ value: "disabled", label: "Disabled" },
|
{ value: "disabled", label: "Disabled" },
|
||||||
{ value: "tx_only", label: "Tx only" },
|
{ value: "basic", label: "Basic" },
|
||||||
{ value: "rx_only", label: "Rx only" },
|
{ value: "all", label: "All" },
|
||||||
{ value: "basic", label: "Tx Minimal + Rx" },
|
|
||||||
{ value: "all", label: "Tx Detailed + Rx" },
|
|
||||||
{ value: "enabled", label: "Enabled" },
|
|
||||||
])}
|
])}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</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>
|
</div>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|
Loading…
Reference in New Issue