Skip to content

Commit 20b34b5

Browse files
committed
Support for lima usernet network
Signed-off-by: Balaji Vijayakumar <[email protected]>
1 parent f0282b2 commit 20b34b5

27 files changed

+1017
-203
lines changed

.cirrus.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ task:
2323
EXAMPLE: fedora.yaml
2424
EXAMPLE: archlinux.yaml
2525
EXAMPLE: opensuse.yaml
26+
EXAMPLE: experimental/net-user-v2.yaml
2627
info_script:
2728
- uname -a
2829
- df -T

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func newApp() *cobra.Command {
100100
newEditCommand(),
101101
newFactoryResetCommand(),
102102
newDiskCommand(),
103+
newUsernetCommand(),
103104
)
104105
return rootCmd
105106
}

cmd/limactl/usernet.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"strconv"
8+
9+
"github.com/lima-vm/lima/pkg/networks/usernet"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
func newUsernetCommand() *cobra.Command {
14+
var hostagentCommand = &cobra.Command{
15+
Use: "usernet",
16+
Short: "run usernet",
17+
Args: cobra.ExactArgs(0),
18+
RunE: usernetAction,
19+
Hidden: true,
20+
}
21+
hostagentCommand.Flags().StringP("pidfile", "p", "", "write pid to file")
22+
hostagentCommand.Flags().StringP("endpoint", "e", "", "exposes usernet api(s) on this endpoint")
23+
hostagentCommand.Flags().String("listen-qemu", "", "listen for qemu connections")
24+
hostagentCommand.Flags().String("listen", "", "listen on a Unix socket and receive Bess-compatible FDs as SCM_RIGHTS messages")
25+
hostagentCommand.Flags().Int("mtu", 1500, "mtu")
26+
return hostagentCommand
27+
}
28+
29+
func usernetAction(cmd *cobra.Command, args []string) error {
30+
pidfile, err := cmd.Flags().GetString("pidfile")
31+
if err != nil {
32+
return err
33+
}
34+
if pidfile != "" {
35+
if _, err := os.Stat(pidfile); !errors.Is(err, os.ErrNotExist) {
36+
return fmt.Errorf("pidfile %q already exists", pidfile)
37+
}
38+
if err := os.WriteFile(pidfile, []byte(strconv.Itoa(os.Getpid())+"\n"), 0644); err != nil {
39+
return err
40+
}
41+
defer os.RemoveAll(pidfile)
42+
}
43+
endpoint, err := cmd.Flags().GetString("endpoint")
44+
if err != nil {
45+
return err
46+
}
47+
qemuSocket, err := cmd.Flags().GetString("listen-qemu")
48+
if err != nil {
49+
return err
50+
}
51+
fdSocket, err := cmd.Flags().GetString("listen")
52+
if err != nil {
53+
return err
54+
}
55+
56+
mtu, err := cmd.Flags().GetInt("mtu")
57+
if err != nil {
58+
return err
59+
}
60+
61+
os.RemoveAll(endpoint)
62+
os.RemoveAll(qemuSocket)
63+
os.RemoveAll(fdSocket)
64+
65+
return usernet.StartGVisorNetstack(cmd.Context(), &usernet.GVisorNetstackOpts{
66+
MTU: mtu,
67+
Endpoint: endpoint,
68+
QemuSocket: qemuSocket,
69+
FdSocket: fdSocket,
70+
})
71+
}

docs/experimental.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The following features are experimental and subject to change:
66
- `vmType: vz` and relevant configurations (`mountType: virtiofs`, `rosetta`, `[]networks.vzNAT`)
77
- `arch: riscv64`
88
- `video.display: vnc` and relevant configuration (`video.vnc.display`)
9+
- `mode: user-v2` in `networks.yml` and relevant configuration in `lima.yaml`
910

1011
The following flags are experimental and subject to change:
1112

docs/network.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,33 @@ networks:
193193
The range of the IP address is not specifiable.
194194

195195
The "vzNAT" network does not need the `socket_vmnet` binary and the `sudoers` file.
196+
197+
## Lima user-v2 network
198+
199+
user-v2 network provides a user-mode networking similar to the [default user-mode network](#user-mode-network--1921685024-) and also provides support for `vm -> vm` communication.
200+
201+
> **Warning**
202+
> This network mode is experimental
203+
204+
To enable this network mode, define a network with `mode: user-v2` in networks.yaml
205+
206+
```yaml
207+
...
208+
networks:
209+
example-user-v2:
210+
mode: user-v2
211+
...
212+
```
213+
214+
Instances can then reference these networks from their `lima.yaml` file:
215+
216+
```yaml
217+
networks:
218+
- lima: example-user-v2
219+
```
220+
221+
_Note_
222+
223+
- Enabling this network will disable the [default user-mode network](#user-mode-network--1921685024-)
224+
- Subnet used for this network is 192.168.5.0/24 with 192.168.5.2 used for host connection and 192.168.5.3 used for DNS resolution
225+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Example to run lima instance with experimental user-v2 network enabled
2+
images:
3+
- location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
4+
arch: "x86_64"
5+
- location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-arm64.img"
6+
arch: "aarch64"
7+
8+
mounts:
9+
- location: "~"
10+
- location: "/tmp/lima"
11+
writable: true
12+
networks:
13+
- lima: user-v2

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ require (
5050
github.com/a8m/envsubst v1.4.2 // indirect
5151
github.com/alecthomas/participle/v2 v2.0.0 // indirect
5252
github.com/apparentlymart/go-cidr v1.1.0 // indirect
53+
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e // indirect
5354
github.com/davecgh/go-spew v1.1.1 // indirect
5455
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 // indirect
5556
github.com/dimchansky/utfbom v1.1.1 // indirect
@@ -78,10 +79,13 @@ require (
7879
github.com/json-iterator/go v1.1.12 // indirect
7980
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
8081
github.com/kr/fs v0.1.0 // indirect
82+
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2 // indirect
8183
github.com/magiconair/properties v1.8.7 // indirect
8284
github.com/mailru/easyjson v0.7.6 // indirect
8385
github.com/mattn/go-colorable v0.1.13 // indirect
8486
github.com/mattn/go-runewidth v0.0.12 // indirect
87+
github.com/mdlayher/socket v0.4.0 // indirect
88+
github.com/mdlayher/vsock v1.2.0 // indirect
8589
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
8690
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
8791
github.com/modern-go/reflect2 v1.0.2 // indirect

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4t
7171
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
7272
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
7373
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
74+
github.com/balajiv113/fd v0.0.0-20150925145434-c6d800382fff h1:igcsiQjkP5E7AfbHNG73RNYQq+MZt0MezdYyW/zqOcg=
75+
github.com/balajiv113/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ=
76+
github.com/balajiv113/fd v0.0.0-20230330061141-18b1d0daf6e7 h1:6Y2nNPVsEoUA3fmWCouMVLlezu++LP6y9CSX1J8VtVg=
77+
github.com/balajiv113/fd v0.0.0-20230330061141-18b1d0daf6e7/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ=
78+
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e h1:IdMhFPEfTZQU971tIHx3UhY4l+yCeynprnINrDTSrOc=
79+
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ=
7480
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
7581
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
7682
github.com/cheggaaa/pb/v3 v3.1.2 h1:FIxT3ZjOj9XJl0U4o2XbEhjFfZl7jCVCDOGq1ZAB7wQ=
@@ -360,6 +366,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
360366
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
361367
github.com/lima-vm/sshocker v0.3.2 h1:o0WqVzcpt6mzVCuqtS3N3O8kwTx6X4SLr4h7YaRISuE=
362368
github.com/lima-vm/sshocker v0.3.2/go.mod h1:9SWN6wob210VM6oJkkzvWQOlHSp/rQLB+0fSEc92zig=
369+
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2 h1:DZMFueDbfz6PNc1GwDRA8+6lBx1TB9UnxDQliCqR73Y=
370+
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2/go.mod h1:SWzULI85WerrFt3u+nIm5F9l7EvxZTKQvd0InF3nmgM=
363371
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
364372
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
365373
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -392,6 +400,10 @@ github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZ
392400
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
393401
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
394402
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
403+
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
404+
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
405+
github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A=
406+
github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE=
395407
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
396408
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
397409
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=

hack/test-example.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ declare -A CHECKS=(
2525
["port-forwards"]="1"
2626
["vmnet"]=""
2727
["disk"]=""
28+
["user-v2"]=""
2829
)
2930

3031
case "$NAME" in
@@ -49,6 +50,10 @@ case "$NAME" in
4950
"test-misc")
5051
CHECKS["disk"]=1
5152
;;
53+
"net-user-v2")
54+
CHECKS["port-forwards"]=""
55+
CHECKS["user-v2"]=1
56+
;;
5257
esac
5358

5459
if limactl ls -q | grep -q "$NAME"; then
@@ -303,6 +308,28 @@ if [[ -n ${CHECKS["restart"]} ]]; then
303308
fi
304309
fi
305310

311+
if [[ -n ${CHECKS["user-v2"]} ]]; then
312+
INFO "Testing user-v2 network"
313+
secondvm="$NAME-1"
314+
limactl start "$FILE" --name "$secondvm" --tty=false
315+
guestNewip="$(limactl shell "$secondvm" ip -4 -j addr show dev eth0 | jq -r '.[0].addr_info[0].local')"
316+
INFO "IP of $secondvm is $guestNewip"
317+
set -x
318+
if ! limactl shell "$NAME" ping -c 1 "$guestNewip"; then
319+
ERROR "Failed to do vm->vm communication via user-v2"
320+
INFO "Stopping \"$secondvm\""
321+
limactl stop "$secondvm"
322+
INFO "Deleting \"$secondvm\""
323+
limactl delete "$secondvm"
324+
exit 1
325+
fi
326+
INFO "Stopping \"$secondvm\""
327+
limactl stop "$secondvm"
328+
INFO "Deleting \"$secondvm\""
329+
limactl delete "$secondvm"
330+
set +x
331+
fi
332+
306333
INFO "Stopping \"$NAME\""
307334
limactl stop "$NAME"
308335
sleep 3

pkg/cidata/cidata.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,11 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort
211211

212212
slirpMACAddress := limayaml.MACAddress(instDir)
213213
args.Networks = append(args.Networks, Network{MACAddress: slirpMACAddress, Interface: networks.SlirpNICName})
214-
for _, nw := range y.Networks {
214+
firstUsernetIndex := limayaml.FirstUsernetIndex(y)
215+
for i, nw := range y.Networks {
216+
if i == firstUsernetIndex {
217+
continue
218+
}
215219
args.Networks = append(args.Networks, Network{MACAddress: nw.MACAddress, Interface: nw.Interface})
216220
}
217221

pkg/limayaml/defaults.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"strconv"
1212
"text/template"
1313

14+
"github.com/lima-vm/lima/pkg/networks"
15+
1416
"github.com/lima-vm/lima/pkg/guestagent/api"
1517
"github.com/lima-vm/lima/pkg/osutil"
1618
"github.com/lima-vm/lima/pkg/store/dirnames"
@@ -49,6 +51,18 @@ func defaultContainerdArchives() []File {
4951
}
5052
}
5153

54+
// FirstUsernetIndex gets the index of first usernet network under l.Network[]. Returns -1 if no usernet network found
55+
func FirstUsernetIndex(l *LimaYAML) int {
56+
for i := range l.Networks {
57+
nwName := l.Networks[i].Lima
58+
isUsernet, _ := networks.Usernet(nwName)
59+
if isUsernet {
60+
return i
61+
}
62+
}
63+
return -1
64+
}
65+
5266
func MACAddress(uniqueID string) string {
5367
sha := sha256.Sum256([]byte(osutil.MachineID() + uniqueID))
5468
// "5" is the magic number in the Lima ecosystem.

pkg/limayaml/validate.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,18 @@ func validateNetwork(y LimaYAML, warn bool) error {
281281
for i, nw := range y.Networks {
282282
field := fmt.Sprintf("networks[%d]", i)
283283
if nw.Lima != "" {
284-
if runtime.GOOS != "darwin" {
284+
config, err := networks.Config()
285+
if err != nil {
286+
return err
287+
}
288+
if config.Check(nw.Lima) != nil {
289+
return fmt.Errorf("field `%s.lima` references network %q which is not defined in networks.yaml", field, nw.Lima)
290+
}
291+
usernet, err := config.Usernet(nw.Lima)
292+
if err != nil {
293+
return err
294+
}
295+
if !usernet && runtime.GOOS != "darwin" {
285296
return fmt.Errorf("field `%s.lima` is only supported on macOS right now", field)
286297
}
287298
if nw.Socket != "" {
@@ -296,13 +307,6 @@ func validateNetwork(y LimaYAML, warn bool) error {
296307
if nw.SwitchPortDeprecated != 0 {
297308
return fmt.Errorf("field `%s.switchPort` cannot be used with field `%s.lima`", field, field)
298309
}
299-
config, err := networks.Config()
300-
if err != nil {
301-
return err
302-
}
303-
if config.Check(nw.Lima) != nil {
304-
return fmt.Errorf("field `%s.lima` references network %q which is not defined in networks.yaml", field, nw.Lima)
305-
}
306310
} else if nw.Socket != "" {
307311
if nw.VZNAT != nil && *nw.VZNAT {
308312
return fmt.Errorf("field `%s.socket` and field `%s.vzNAT` are mutually exclusive", field, field)

pkg/networks/commands.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ func (config *YAML) Check(name string) error {
2828
return fmt.Errorf("network %q is not defined", name)
2929
}
3030

31+
// Usernet Returns true if the mode of given network is ModeUserV2
32+
func (config *YAML) Usernet(name string) (bool, error) {
33+
if nw, ok := config.Networks[name]; ok {
34+
return nw.Mode == ModeUserV2, nil
35+
}
36+
return false, fmt.Errorf("network %q is not defined", name)
37+
}
38+
3139
// DaemonPath returns the daemon path.
3240
func (config *YAML) DaemonPath(daemon string) (string, error) {
3341
switch daemon {

pkg/networks/config.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"os"
88
"os/exec"
99
"path/filepath"
10-
"runtime"
1110
"sync"
1211

1312
"github.com/goccy/go-yaml"
@@ -122,9 +121,6 @@ func loadCache() {
122121

123122
// Config returns the network config from the _config/networks.yaml file.
124123
func Config() (YAML, error) {
125-
if runtime.GOOS != "darwin" {
126-
return YAML{}, errors.New("networks.yaml configuration is only supported on macOS right now")
127-
}
128124
loadCache()
129125
return cache.config, cache.err
130126
}
@@ -144,6 +140,15 @@ func Sock(name string) (string, error) {
144140
return cache.config.Sock(name), nil
145141
}
146142

143+
// Usernet Returns true if the given network name is usernet network
144+
func Usernet(name string) (bool, error) {
145+
loadCache()
146+
if cache.err != nil {
147+
return false, cache.err
148+
}
149+
return cache.config.Usernet(name)
150+
}
151+
147152
// VDESock returns a vde socket.
148153
//
149154
// Deprecated. Use Sock.

0 commit comments

Comments
 (0)