feat: Initial comimt
This commit is contained in:
commit
3d97c863d1
10 changed files with 841 additions and 0 deletions
25
README.md
Normal file
25
README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# govmtools
|
||||||
|
|
||||||
|
> Reimplementation of [open-vm-tools](https://github.com/vmware/open-vm-tools) in pure Go
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
## DataMap
|
||||||
|
|
||||||
|
### Field types
|
||||||
|
- empty = 0
|
||||||
|
- int64 = 1
|
||||||
|
- string = 2
|
||||||
|
- int64list = 3
|
||||||
|
- stringlist = 4
|
||||||
|
- max = 5
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
- type = 1
|
||||||
|
- payload = 2
|
||||||
|
- fast_close = 3
|
||||||
|
|
||||||
|
### Types
|
||||||
|
- data = 1
|
||||||
|
- ping = 2
|
38
cmd/govmtoolsd/main.go
Normal file
38
cmd/govmtoolsd/main.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ptrcnull/govmtools"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
sock, err := govmtools.NewSocket()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("connected! %#v\n", sock)
|
||||||
|
err = sock.ReportVersionData()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sock.MustSendCommand("vmx.capability.unified_loop toolbox\x00")
|
||||||
|
sock.MustSendCommand("log toolbox: Version: 11.2.5.26209 (build-17337674)\x00")
|
||||||
|
sock.MustSendCommand("tools.capability.statechange ")
|
||||||
|
sock.MustSendCommand("tools.capability.softpowerop_retry ")
|
||||||
|
sock.MustSendCommand("tools.capability.guest_conf_directory /etc/vmware-tools\x00")
|
||||||
|
sock.MustSendCommand("tools.set.versiontype 11333 4\x00")
|
||||||
|
sock.MustSendCommand("info-set guestinfo.ip 10.99.0.6\x00")
|
||||||
|
guestInfoNetwork, err := govmtools.GetGuestInfoNetwork()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sock.SetGuestInfo(govmtools.GuestInfoDnsName, "openldap")
|
||||||
|
sock.SetGuestInfo(govmtools.GuestInfoUptime, "26642699")
|
||||||
|
sock.SetGuestInfo(govmtools.GuestInfoIpAddressV3, guestInfoNetwork)
|
||||||
|
sock.SetGuestInfo(govmtools.GuestInfoBuildNumber, "build-" + govmtools.BuildNumber)
|
||||||
|
sock.MustSendCommand("info-set guestinfo.appInfo \x00")
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
12
go.mod
Normal file
12
go.mod
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module github.com/ptrcnull/govmtools
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/ptrcnull/vsock v0.0.0-20211110040213-bace62f83228
|
||||||
|
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee
|
||||||
|
github.com/vishvananda/netlink v1.1.0
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3
|
||||||
|
)
|
||||||
|
|
||||||
|
require github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
20
go.sum
Normal file
20
go.sum
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/ptrcnull/vsock v0.0.0-20211110040213-bace62f83228 h1:ApLO3fuH29Nslm8s0G9tVe8UVG61Yg9ob4J80RxNX5U=
|
||||||
|
github.com/ptrcnull/vsock v0.0.0-20211110040213-bace62f83228/go.mod h1:1bEx6UYqCMOhYlho1+7LgWEJroZcFMSpAt7iesq/uiQ=
|
||||||
|
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8=
|
||||||
|
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps=
|
||||||
|
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
288
network.go
Normal file
288
network.go
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
xdr "github.com/stellar/go-xdr/xdr3"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetGuestNicProto() (*GuestNicProto, error) {
|
||||||
|
res := NicInfoV3{
|
||||||
|
Nics: []GuestNicV3{},
|
||||||
|
Routes: []InetCidrRouteEntry{},
|
||||||
|
DnsConfigInfo: []DnsConfigInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
ifaces, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get ifaces: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ignoredPrefixes := []string{"veth", "docker", "virbr", "lo", "br-"}
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
attrs := iface.Attrs()
|
||||||
|
|
||||||
|
skip := false
|
||||||
|
for _, prefix := range ignoredPrefixes {
|
||||||
|
if strings.HasPrefix(attrs.Name, prefix) {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skip || attrs.HardwareAddr.String() == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nic := GuestNicV3{
|
||||||
|
MacAddress: attrs.HardwareAddr.String(),
|
||||||
|
Ips: []IpAddressEntry{},
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error getting addresses:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(addrs) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
// ugly
|
||||||
|
ip := addr.IP
|
||||||
|
var address InetAddress
|
||||||
|
var addrType InetAddressType
|
||||||
|
if ip.To4() != nil {
|
||||||
|
address = InetAddress(ip.To4())
|
||||||
|
addrType = IatIpv4
|
||||||
|
} else {
|
||||||
|
address = InetAddress(ip.To16())
|
||||||
|
addrType = IatIpv6
|
||||||
|
}
|
||||||
|
prefixLength, _ := addr.Mask.Size()
|
||||||
|
|
||||||
|
entry := IpAddressEntry{
|
||||||
|
IpAddressAddr: TypedIpAddress{
|
||||||
|
IpAddressAddrType: addrType,
|
||||||
|
IpAddressAddr: address,
|
||||||
|
},
|
||||||
|
IpAddressPrefixLength: InetAddressPrefixLength(prefixLength),
|
||||||
|
IpAddressOrigin: nil,
|
||||||
|
IpAddressStatus: nil,
|
||||||
|
}
|
||||||
|
nic.Ips = append(nic.Ips, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
routes, err := netlink.RouteList(iface, netlink.FAMILY_ALL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error getting routes:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
var addr TypedIpAddress
|
||||||
|
if route.Dst.IP.To4() != nil {
|
||||||
|
addr = TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: InetAddress(route.Dst.IP.To4()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addr = TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv6,
|
||||||
|
IpAddressAddr: InetAddress(route.Dst.IP.To16()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routePrefix, _ := route.Dst.Mask.Size()
|
||||||
|
r := InetCidrRouteEntry{
|
||||||
|
InetCidrRouteDest: addr,
|
||||||
|
InetCidrRoutePfxLen: InetAddressPrefixLength(routePrefix),
|
||||||
|
InetCidrRouteNextHop: route.Gw.,
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Nics = append(res.Nics, nic)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GuestNicProto{
|
||||||
|
Type: ProtoTypeV3,
|
||||||
|
NicInfoV3: &NicInfoV3Wrapper{
|
||||||
|
NicInfoV3: []NicInfoV3{},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGuestInfoNetwork() (string, error) {
|
||||||
|
data, err := GetGuestNicProto()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
_, err = xdr.Marshal(buf, data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoStuff() {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
xdr.Marshal(buf, &GuestNicProto{
|
||||||
|
Type: ProtoTypeV3,
|
||||||
|
NicInfoV3: &NicInfoV3Wrapper{
|
||||||
|
NicInfoV3: []NicInfoV3{
|
||||||
|
{
|
||||||
|
Nics: []GuestNicV3{
|
||||||
|
{
|
||||||
|
MacAddress: "00:50:56:9f:41:dd",
|
||||||
|
Ips: []IpAddressEntry{
|
||||||
|
{
|
||||||
|
IpAddressAddr: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 0, 4},
|
||||||
|
},
|
||||||
|
IpAddressPrefixLength: 16,
|
||||||
|
IpAddressOrigin: nil,
|
||||||
|
IpAddressStatus: []IpAddressStatus{
|
||||||
|
IasPreferred,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IpAddressAddr: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 1, 51},
|
||||||
|
},
|
||||||
|
IpAddressPrefixLength: 16,
|
||||||
|
IpAddressOrigin: nil,
|
||||||
|
IpAddressStatus: []IpAddressStatus{
|
||||||
|
IasPreferred,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IpAddressAddr: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv6,
|
||||||
|
IpAddressAddr: []byte{
|
||||||
|
0xfe, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x50, 0x56, 0xff,
|
||||||
|
0xfe, 0x9f, 0x41, 0xdd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IpAddressPrefixLength: 64,
|
||||||
|
IpAddressOrigin: nil,
|
||||||
|
IpAddressStatus: []IpAddressStatus{
|
||||||
|
IasUnknown,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Routes: []InetCidrRouteEntry{
|
||||||
|
{
|
||||||
|
InetCidrRouteDest: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
InetCidrRoutePfxLen: 0,
|
||||||
|
InetCidrRouteNextHop: []TypedIpAddress{
|
||||||
|
{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 0, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InetCidrRouteDest: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 0, 0},
|
||||||
|
},
|
||||||
|
InetCidrRoutePfxLen: 16,
|
||||||
|
InetCidrRouteNextHop: nil,
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InetCidrRouteDest: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv6,
|
||||||
|
IpAddressAddr: []byte{
|
||||||
|
0xfe, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InetCidrRoutePfxLen: 64,
|
||||||
|
InetCidrRouteNextHop: nil,
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 0x100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InetCidrRouteDest: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv6,
|
||||||
|
IpAddressAddr: []byte{
|
||||||
|
0xfe, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x50, 0x56, 0xff,
|
||||||
|
0xfe, 0x9f, 0x41, 0xdd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InetCidrRoutePfxLen: 128,
|
||||||
|
InetCidrRouteNextHop: nil,
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InetCidrRouteDest: TypedIpAddress{
|
||||||
|
IpAddressAddrType: IatIpv6,
|
||||||
|
IpAddressAddr: []byte{
|
||||||
|
0xff, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InetCidrRoutePfxLen: 8,
|
||||||
|
InetCidrRouteNextHop: nil,
|
||||||
|
InetCidrRouteIfIndex: 0,
|
||||||
|
InetCidrRouteType: 0,
|
||||||
|
InetCidrRouteMetric: 256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DnsConfigInfo: []DnsConfigInfo{
|
||||||
|
{
|
||||||
|
HostName: []string{"imagepacker"},
|
||||||
|
DomainName: []string{"t2hack.internal"},
|
||||||
|
ServerList: []TypedIpAddress{
|
||||||
|
{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 0, 8},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IpAddressAddrType: IatIpv4,
|
||||||
|
IpAddressAddr: []byte{10, 99, 0, 8},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SearchSuffixes: []string{"t2hack.internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fmt.Println(hex.Dump(buf.Bytes()))
|
||||||
|
ioutil.WriteFile("/home/patrycja/newdata", buf.Bytes(), 0755)
|
||||||
|
|
||||||
|
}
|
140
network_xdr.go
Normal file
140
network_xdr.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
xdr "github.com/stellar/go-xdr/xdr3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InetAddressType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IatUnknown InetAddressType = iota
|
||||||
|
IatIpv4
|
||||||
|
IatIpv6
|
||||||
|
IatIpv4Z
|
||||||
|
IatIpv6Z
|
||||||
|
IatDns
|
||||||
|
)
|
||||||
|
|
||||||
|
type IpAddressOrigin int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IaoOther IpAddressOrigin = 1
|
||||||
|
IaoManual IpAddressOrigin = 2
|
||||||
|
IaoDhcp IpAddressOrigin = 4
|
||||||
|
IaoLinkLayer IpAddressOrigin = 5
|
||||||
|
IaoRandom IpAddressOrigin = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
type IpAddressStatus int
|
||||||
|
const (
|
||||||
|
IasPreferred = iota + 1
|
||||||
|
IasDeprecated
|
||||||
|
IasInvalid
|
||||||
|
IasInaccessible
|
||||||
|
IasUnknown
|
||||||
|
IasTentative
|
||||||
|
IasDuplicate
|
||||||
|
IasOptimistic
|
||||||
|
)
|
||||||
|
|
||||||
|
type InetAddress []byte
|
||||||
|
|
||||||
|
type TypedIpAddress struct {
|
||||||
|
IpAddressAddrType InetAddressType
|
||||||
|
IpAddressAddr InetAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
type InetAddressPrefixLength uint
|
||||||
|
|
||||||
|
type IpAddressEntry struct {
|
||||||
|
IpAddressAddr TypedIpAddress
|
||||||
|
IpAddressPrefixLength InetAddressPrefixLength
|
||||||
|
IpAddressOrigin []IpAddressOrigin
|
||||||
|
IpAddressStatus []IpAddressStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
type DnsConfigInfo struct {
|
||||||
|
HostName []string
|
||||||
|
DomainName []string
|
||||||
|
ServerList []TypedIpAddress
|
||||||
|
SearchSuffixes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WinsConfigInfo struct {
|
||||||
|
Primary TypedIpAddress
|
||||||
|
Secondary TypedIpAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
type DhcpConfigInfo struct {
|
||||||
|
Enabled bool
|
||||||
|
DhcpSettings []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GuestNicV3 struct {
|
||||||
|
MacAddress string
|
||||||
|
Ips []IpAddressEntry
|
||||||
|
DnsConfigInfo []DnsConfigInfo
|
||||||
|
WinsConfigInfo []WinsConfigInfo
|
||||||
|
DhcpConfigInfoV4 []DhcpConfigInfo
|
||||||
|
DhcpConfigInfoV6 []DhcpConfigInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type InetCidrRouteType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IcrtOther InetCidrRouteType = iota + 1
|
||||||
|
IcrtReject
|
||||||
|
IcrtLocal
|
||||||
|
IcrtRemote
|
||||||
|
)
|
||||||
|
|
||||||
|
type InetCidrRouteEntry struct {
|
||||||
|
InetCidrRouteDest TypedIpAddress
|
||||||
|
InetCidrRoutePfxLen InetAddressPrefixLength
|
||||||
|
InetCidrRouteNextHop []TypedIpAddress
|
||||||
|
InetCidrRouteIfIndex uint32
|
||||||
|
InetCidrRouteType InetCidrRouteType
|
||||||
|
InetCidrRouteMetric uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type NicInfoV3Wrapper struct {
|
||||||
|
NicInfoV3 []NicInfoV3
|
||||||
|
}
|
||||||
|
|
||||||
|
type NicInfoV3 struct {
|
||||||
|
Nics []GuestNicV3
|
||||||
|
Routes []InetCidrRouteEntry
|
||||||
|
DnsConfigInfo []DnsConfigInfo
|
||||||
|
WinsConfigInfo []WinsConfigInfo
|
||||||
|
DhcpConfigInfoV4 []DhcpConfigInfo
|
||||||
|
DhcpConfigInfoV6 []DhcpConfigInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type GuestNicProtoType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtoTypeV3 = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func (GuestNicProtoType) ValidEnum(i int32) bool {
|
||||||
|
return i == ProtoTypeV3
|
||||||
|
}
|
||||||
|
|
||||||
|
type GuestNicProto struct {
|
||||||
|
Type GuestNicProtoType
|
||||||
|
NicInfoV3 *NicInfoV3Wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ xdr.Union = (*GuestNicProto)(nil)
|
||||||
|
|
||||||
|
func (g GuestNicProto) ArmForSwitch(i int32) (string, bool) {
|
||||||
|
if i != 3 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return "NicInfoV3", true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GuestNicProto) SwitchFieldName() string {
|
||||||
|
return "Type"
|
||||||
|
}
|
126
proto.go
Normal file
126
proto.go
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
Type FieldType
|
||||||
|
ID FieldID
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) WritePacket(pkt []Field) error {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
for _, field := range pkt {
|
||||||
|
err := binary.Write(buf, order, field.Type)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write field type: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Type == FieldEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = binary.Write(buf, order, field.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write field ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Type {
|
||||||
|
case FieldInt64:
|
||||||
|
err = binary.Write(buf, order, field.Value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write int64: %w", err)
|
||||||
|
}
|
||||||
|
case FieldString:
|
||||||
|
val := field.Value.(string)
|
||||||
|
err = binary.Write(buf, order, int32(len(val)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write string len: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.WriteString(val)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write string: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]byte, buf.Len()+4)
|
||||||
|
order.PutUint32(res, uint32(buf.Len()))
|
||||||
|
copy(res[4:], buf.Bytes())
|
||||||
|
|
||||||
|
_, err := s.conn.Write(res)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) ReadPacket() ([]Field, error) {
|
||||||
|
length := make([]byte, 4)
|
||||||
|
_, err := s.conn.Read(length)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read len: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
response := make([]byte, order.Uint32(length))
|
||||||
|
_, err = s.conn.Read(response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(response)
|
||||||
|
var fields []Field
|
||||||
|
|
||||||
|
for buf.Len() > 0 {
|
||||||
|
field := Field{}
|
||||||
|
|
||||||
|
err = binary.Read(buf, order, &field.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse field type: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Type == FieldEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = binary.Read(buf, order, &field.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse field ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Type {
|
||||||
|
case FieldInt64:
|
||||||
|
var val int64
|
||||||
|
err = binary.Read(buf, order, val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse int64: %w", err)
|
||||||
|
}
|
||||||
|
field.Value = val
|
||||||
|
case FieldString:
|
||||||
|
var length uint32
|
||||||
|
err = binary.Read(buf, order, &length)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse string length: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
byt := make([]byte, length)
|
||||||
|
_, err := buf.Read(byt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse string: %w", err)
|
||||||
|
}
|
||||||
|
field.Value = string(byt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields, nil
|
||||||
|
}
|
49
rpc.go
Normal file
49
rpc.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var order = binary.BigEndian
|
||||||
|
|
||||||
|
func (s *Socket) RpcSend(data []byte) ([]byte, error) {
|
||||||
|
// TODO error handling
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
binary.Write(buf, order, FieldInt64)
|
||||||
|
binary.Write(buf, order, FieldIDType)
|
||||||
|
binary.Write(buf, order, PacketTypeData)
|
||||||
|
|
||||||
|
binary.Write(buf, order, FieldEmpty)
|
||||||
|
|
||||||
|
binary.Write(buf, order, FieldString)
|
||||||
|
binary.Write(buf, order, FieldIDPayload)
|
||||||
|
binary.Write(buf, order, uint32(len(data)))
|
||||||
|
|
||||||
|
buf.Write(data)
|
||||||
|
|
||||||
|
res := make([]byte, buf.Len()+4)
|
||||||
|
order.PutUint32(res, uint32(buf.Len()))
|
||||||
|
copy(res[4:], buf.Bytes())
|
||||||
|
|
||||||
|
_, err := s.conn.Write(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("write: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responseLength := make([]byte, 4)
|
||||||
|
_, err = s.conn.Read(responseLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read len: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]byte, order.Uint32(responseLength))
|
||||||
|
_, err = s.conn.Read(response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
137
socket.go
Normal file
137
socket.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ptrcnull/vsock"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PacketType uint32
|
||||||
|
const (
|
||||||
|
PacketTypeData PacketType = iota + 1
|
||||||
|
PacketTypePing
|
||||||
|
)
|
||||||
|
|
||||||
|
type FieldID uint32
|
||||||
|
const (
|
||||||
|
FieldIDType FieldID = iota + 1
|
||||||
|
FieldIDPayload
|
||||||
|
FieldIDFastClose
|
||||||
|
)
|
||||||
|
|
||||||
|
type FieldType uint32
|
||||||
|
const (
|
||||||
|
FieldEmpty FieldType = iota
|
||||||
|
FieldInt64
|
||||||
|
FieldString
|
||||||
|
FieldInt64List
|
||||||
|
FieldStringList
|
||||||
|
FieldMax
|
||||||
|
)
|
||||||
|
|
||||||
|
type GuestInfoType uint32
|
||||||
|
const (
|
||||||
|
GuestInfoError GuestInfoType = iota
|
||||||
|
GuestInfoDnsName
|
||||||
|
GuestInfoIpAddress
|
||||||
|
GuestInfoDiskFreeSpace
|
||||||
|
GuestInfoBuildNumber
|
||||||
|
GuestInfoOsNameFull
|
||||||
|
GuestInfoOsName
|
||||||
|
GuestInfoUptime
|
||||||
|
GuestInfoMemory
|
||||||
|
GuestInfoIpAddressV2
|
||||||
|
GuestInfoIpAddressV3
|
||||||
|
GuestInfoOsDetailed
|
||||||
|
GuestInfoMax
|
||||||
|
)
|
||||||
|
|
||||||
|
const GuestConnectPort = 976
|
||||||
|
const ContextID = unix.VMADDR_CID_HYPERVISOR
|
||||||
|
|
||||||
|
type Socket struct {
|
||||||
|
conn *vsock.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSocket() (*Socket, error) {
|
||||||
|
conn, err := vsock.Dial(ContextID, GuestConnectPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("connect: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Socket{
|
||||||
|
conn: conn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) SendCommand(cmd string) error {
|
||||||
|
err := s.WritePacket([]Field{
|
||||||
|
{
|
||||||
|
Type: FieldInt64,
|
||||||
|
ID: FieldIDType,
|
||||||
|
Value: PacketTypeData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: FieldEmpty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: FieldString,
|
||||||
|
ID: FieldIDPayload,
|
||||||
|
Value: cmd,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write packet: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := s.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read packet: %w", err)
|
||||||
|
}
|
||||||
|
if len(res) != 1 {
|
||||||
|
return fmt.Errorf("unexpected response length: %d", len(res))
|
||||||
|
}
|
||||||
|
field := res[0]
|
||||||
|
if field.Type != FieldString {
|
||||||
|
return fmt.Errorf("unexpected response type: %d", field.Type)
|
||||||
|
}
|
||||||
|
if field.ID != FieldIDPayload {
|
||||||
|
return fmt.Errorf("unexpected response id: %d", field.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := field.Value.(string)
|
||||||
|
if response != "1 " {
|
||||||
|
return fmt.Errorf("unexpected response: %s", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) SetGuestInfo(messageType GuestInfoType, message string) {
|
||||||
|
s.MustSendCommand(fmt.Sprintf("SetGuestInfo %d %s", messageType, message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) MustSendCommand(cmd string) {
|
||||||
|
err := s.SendCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Socket) ReportVersionData() error {
|
||||||
|
data := []string{
|
||||||
|
"description " + Name + " " + Version + " build " + BuildNumber,
|
||||||
|
"versionString " + Version,
|
||||||
|
"versionNumber " + VersionNumber,
|
||||||
|
"buildNumber " + BuildNumber,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range data {
|
||||||
|
err := s.SendCommand("info-set guestinfo.vmtools." + value + "\x00")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("send: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
6
version.go
Normal file
6
version.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package govmtools
|
||||||
|
|
||||||
|
const Name = "open-vm-tools"
|
||||||
|
const Version = "11.2.5"
|
||||||
|
const VersionNumber = "11333"
|
||||||
|
const BuildNumber = "17337674"
|
Loading…
Reference in a new issue