Skip to content

Commit 62ddb12

Browse files
anmaxvlkevpar
andauthored
HvSocket support for containers (#2353)
* HvSocket support for containers Applications connecting from the host into the container should use container-specific VMID. This ID will need to be the same as the container's VMID inside the guest, which is calculated by HCS/GCS like it's done in this PR by `HCSIDToGUID`. To allow the container ID to work with HvSocket on the host, we need to set up an AddressInfo mapping to tell HvSocket to redirect the call into the UVM, which is done in this PR by default for all WCOW containers. Add internal `hvsocketaddr.exe` tool that clients can use to generate VM ID for container. Add a generic function for creating HvSocket address info mapping. export a function that creates a mapping for containers only. --------- Signed-off-by: Maksim An <[email protected]> Co-authored-by: Kevin Parsons <[email protected]>
1 parent fa9d402 commit 62ddb12

File tree

7 files changed

+188
-2
lines changed

7 files changed

+188
-2
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ jobs:
657657
name: Build uvmboot.exe
658658
- run: ${{ env.GO_BUILD_CMD }} ./internal/tools/zapdir
659659
name: Build zapdir.exe
660+
- run: ${{ env.GO_BUILD_CMD }} ./internal/tools/hvsocketaddr
661+
name: Build hvsocketaddr.exe
660662

661663
- uses: actions/upload-artifact@v4
662664
if: ${{ github.event_name == 'pull_request' }}
@@ -669,6 +671,7 @@ jobs:
669671
wclayer.exe
670672
device-util.exe
671673
ncproxy.exe
674+
hvsocketaddr.exe
672675
grantvmgroupaccess.exe
673676
networkagent.exe
674677
securitypolicy.exe

internal/hcs/schema2/properties.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type Properties struct {
2626

2727
RuntimeId string `json:"RuntimeId,omitempty"`
2828

29+
SystemGUID string `json:"SystemGUID,omitempty"`
30+
2931
RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"`
3032

3133
State string `json:"State,omitempty"`

internal/hcs/schema2/property_type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ const (
2323
PTICHeartbeatStatus PropertyType = "ICHeartbeatStatus"
2424
PTProcessorTopology PropertyType = "ProcessorTopology"
2525
PTCPUGroup PropertyType = "CpuGroup"
26+
PTSystemGUID PropertyType = "SystemGUID"
2627
)

internal/hcsoci/create.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ import (
1212
"strconv"
1313

1414
"github.com/Microsoft/go-winio/pkg/guid"
15+
specs "github.com/opencontainers/runtime-spec/specs-go"
16+
"github.com/sirupsen/logrus"
17+
1518
"github.com/Microsoft/hcsshim/internal/cow"
1619
"github.com/Microsoft/hcsshim/internal/guestpath"
1720
"github.com/Microsoft/hcsshim/internal/hcs"
1821
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
22+
"github.com/Microsoft/hcsshim/internal/hvsocket"
1923
"github.com/Microsoft/hcsshim/internal/layers"
2024
"github.com/Microsoft/hcsshim/internal/log"
2125
"github.com/Microsoft/hcsshim/internal/oci"
2226
"github.com/Microsoft/hcsshim/internal/resources"
2327
"github.com/Microsoft/hcsshim/internal/schemaversion"
2428
"github.com/Microsoft/hcsshim/internal/uvm"
25-
specs "github.com/opencontainers/runtime-spec/specs-go"
26-
"github.com/sirupsen/logrus"
2729
)
2830

2931
var (
@@ -278,6 +280,24 @@ func CreateContainer(ctx context.Context, createOptions *CreateOptions) (_ cow.C
278280
if err != nil {
279281
return nil, r, err
280282
}
283+
284+
if coi.HostingSystem.OS() == "windows" {
285+
log.G(ctx).Debug("redirecting container HvSocket for WCOW")
286+
props, err := c.PropertiesV2(ctx, hcsschema.PTSystemGUID)
287+
if err != nil {
288+
return nil, r, fmt.Errorf("query created container properties failed: %w", err)
289+
}
290+
containerSystemGUID, err := guid.FromString(props.SystemGUID)
291+
if err != nil {
292+
return nil, r, fmt.Errorf("convert to system GUID failed: %w", err)
293+
}
294+
addressInfoCloser, err := hvsocket.CreateContainerAddressInfo(containerSystemGUID, coi.HostingSystem.RuntimeID())
295+
if err != nil {
296+
return nil, r, fmt.Errorf("redirect container HvSocket failed: %w", err)
297+
}
298+
r.Add(addressInfoCloser)
299+
}
300+
281301
return c, r, nil
282302
}
283303

internal/hvsocket/hvsocket.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//go:build windows
2+
// +build windows
3+
4+
package hvsocket
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"unsafe"
10+
11+
"github.com/Microsoft/go-winio/pkg/guid"
12+
"golang.org/x/sys/windows"
13+
14+
"github.com/Microsoft/hcsshim/internal/resources"
15+
)
16+
17+
const (
18+
addressFlagPassthru = 0x00000001
19+
ioCtlHVSocketUpdateAddressInfo = 0x21c004
20+
)
21+
22+
type addressInfo struct {
23+
systemID guid.GUID
24+
virtualMachineID guid.GUID
25+
siloID guid.GUID
26+
flags uint32
27+
}
28+
29+
type addressInfoCloser struct {
30+
handle windows.Handle
31+
}
32+
33+
var _ resources.ResourceCloser = addressInfoCloser{}
34+
35+
func (aic addressInfoCloser) Release(_ context.Context) error {
36+
return windows.CloseHandle(aic.handle)
37+
}
38+
39+
// CreateContainerAddressInfo creates an address info entry in HvSocket to redirect
40+
// the calls to the container silo inside UVM.
41+
func CreateContainerAddressInfo(containerID, uvmID guid.GUID) (resources.ResourceCloser, error) {
42+
return CreateAddressInfo(containerID, uvmID, guid.GUID{}, true)
43+
}
44+
45+
// CreateAddressInfo creates an address info entry in the HvSocket provider to map a
46+
// compute system GUID to a virtual machine ID or compartment ID.
47+
//
48+
// `systemID` is the compute system GUID to map.
49+
// `vmID` is the virtual machine ID to which the system GUID maps to. Must be guid.GUID{} to specify
50+
// that the system GUID maps to a network compartment ID on the hosting system.
51+
// `siloID` is the silo object ID to which the system GUID maps to.
52+
// `passthru` when vmID is not guid.GUID{}, specifies whether the systemID maps to the primary
53+
// compartment of the virtual machine (set to `false`) or to another compartment within the
54+
// virtual machine (set to `true`)
55+
func CreateAddressInfo(systemID, vmID, siloID guid.GUID, passthru bool) (resources.ResourceCloser, error) {
56+
path := fmt.Sprintf(`\\.\HvSocketSystem\AddressInfo\{%s}`, systemID)
57+
u16, err := windows.UTF16PtrFromString(path)
58+
if err != nil {
59+
return nil, err
60+
}
61+
h, err := windows.CreateFile(
62+
u16,
63+
windows.GENERIC_READ|windows.GENERIC_WRITE,
64+
0,
65+
nil,
66+
windows.CREATE_NEW,
67+
0,
68+
0,
69+
)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
addrInfo := addressInfo{
75+
systemID: systemID,
76+
virtualMachineID: vmID,
77+
siloID: siloID,
78+
}
79+
80+
if passthru {
81+
addrInfo.flags |= addressFlagPassthru
82+
}
83+
84+
var ret uint32
85+
if err := windows.DeviceIoControl(
86+
h,
87+
ioCtlHVSocketUpdateAddressInfo,
88+
(*byte)(unsafe.Pointer(&addrInfo)),
89+
uint32(unsafe.Sizeof(addrInfo)),
90+
nil,
91+
0,
92+
&ret,
93+
nil,
94+
); err != nil {
95+
return nil, err
96+
}
97+
98+
return &addressInfoCloser{h}, nil
99+
}

internal/tools/hvsocketaddr/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## Overview
2+
Applications connecting from the host into the container should use container-specific VMID.
3+
This VMID will need to be the same as the container's VMID inside the guest. One way to get
4+
the VMID is to query HCS for it or use this binary, which outputs the same VMID, when
5+
querying HCS isn't an option.
6+
7+
## Build
8+
Build the binary as following
9+
```powershell
10+
> go build ./internal/tools/hvsocketaddr
11+
```
12+
13+
## Run
14+
Find container ID using (e.g.) `crictl.exe`:
15+
```powershell
16+
> crictl ps --no-trunc
17+
```
18+
Note that we need full container ID, rather than a truncated one.
19+
20+
Get VMID:
21+
```powershell
22+
> .\hvsocketaddr.exe <container-id>
23+
```
24+
The output VMID can be used by the services on the host.

internal/tools/hvsocketaddr/main.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"fmt"
7+
"os"
8+
"strings"
9+
"unicode/utf16"
10+
11+
"github.com/Microsoft/go-winio/pkg/guid"
12+
)
13+
14+
func HCSIDToGUID(id string) (guid.GUID, error) {
15+
var buf bytes.Buffer
16+
if err := binary.Write(&buf, binary.LittleEndian, utf16.Encode([]rune(strings.ToUpper(id)))); err != nil {
17+
return guid.GUID{}, err
18+
}
19+
// Namespace GUID: cab70344-facb-41e4-b5e5-ab6592283e6e
20+
g, err := guid.NewV5(guid.GUID{Data1: 0xcab70344, Data2: 0xfacb, Data3: 0x41e4, Data4: [8]byte{0xb5, 0xe5, 0xab, 0x65, 0x92, 0x28, 0x3e, 0x6e}}, buf.Bytes())
21+
if err != nil {
22+
return guid.GUID{}, err
23+
}
24+
return g, nil
25+
}
26+
27+
func main() {
28+
if len(os.Args) != 2 || os.Args[1] == "--help" || os.Args[1] == "-h" {
29+
fmt.Printf("usage: %s <CONTAINER ID>\n", os.Args[0])
30+
os.Exit(1)
31+
}
32+
g, err := HCSIDToGUID(os.Args[1])
33+
if err != nil {
34+
fmt.Printf("error: %s\n", err)
35+
}
36+
fmt.Printf("%s\n", g)
37+
}

0 commit comments

Comments
 (0)