Skip to content
This repository has been archived by the owner on Sep 12, 2024. It is now read-only.

Add vm-switch core logic #4

Merged
merged 2 commits into from
Feb 22, 2023
Merged

Add vm-switch core logic #4

merged 2 commits into from
Feb 22, 2023

Conversation

Nino-K
Copy link
Member

@Nino-K Nino-K commented Feb 22, 2023

This PR adds VM Switch process that runs in an isolated network namespace and starts the tap device to talk to the Host Switch.

Fixes: rancher-sandbox/rancher-desktop#3955

@Nino-K Nino-K requested a review from mook-as February 22, 2023 15:39
Copy link
Contributor

@mook-as mook-as left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please amend commits and remove all traces of cmd/host/host.exe. (Just removing it in a new commit doesn't remove it from git history.)

go.mod Outdated
Comment on lines 18 to 20
require (
github.com/pkg/errors v0.9.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These shouldn't be in a separate section. Please merge all requires into a single section then re-run go mod tidy (so it sorts and splits all dependencies deterministically).

func main() {
flag.BoolVar(&debug, "debug", true, "enable debug flag")
flag.StringVar(&tapIface, "tap-interface", defaultTapDevice, "tap interface name, eg. eth0, eth1")
flag.Parse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have options for (non-default) mtu, mac addr?

Copy link
Member Author

@Nino-K Nino-K Feb 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same mac address is also being used in the host switch so making this configurable is kind of pointless, also, the arp message is isolated to a network namespace so mac address conflict is not an option. As for the mtu, the default standard value for most networks is 1500 bytes (although we are setting it to 4000), we can always add an extra arg to configure this if there is a demand for it or if we ever decide to support jumbo frames but for now, it should be fine.

Comment on lines 45 to 59
// network namespace (rd1), the logic behind this approach was the
// lack of support for network namespaces in the AF_VSOCK libs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last sentence is backwards: this is required because AF_VSOCK is affected by network namespaces, therefore we need to open it before entering a new namespace (via unshare/nsenter).

Also, AF_VSOCK is not a library: it's a kernel feature.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the exact wording from the open issue on vsock kernel module thread:

it could be nice to support network namespace in vsock

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the reason we need all thing fd-passing is because they ended up implementing network namespaces for AF_VSOCK (presumably in a commit later than that quoted bit); at least, that seems to be the observed behaviour (in that we don't get to use the same AF_VSOCK things inside the network namespace). That is, your comment here is exactly backwards — if AF_VSOCK didn't support namespaces, we wouldn't need to do this.

// the FD is passed-in as an extra arg from exec.Command
// of the parent process. This is for the AF_VSOCK connection that
// is handed over from the default namespace to Rancher Desktop's
// network namespace (rd1), the logic behind this approach was the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, we no longer need to name the namespace, right? So rd1 doesn't apply anymore. (Hint: they were never named; the "name" was just a name of an arbitrary mount point; both the netns package, and unshare was just bind-mounting /proc/<pid>/ns/net somewhere and calling that a name.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We create the rd1 namespace manually during the namespace setup, it is being used by the nsenter something like:

exec.Command(nsenter, fmt.Sprintf("-n/run/netns/%s", defaultNetworkNamespace), "-F", unshare, "--pid", "--mount-proc", "--fork", "--propagation", "slave", unshareArg)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we run nsenter, we'd have the pid of a process that's already in the namespace (and therefore don't need to do it by path), right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be able to do the pid.

// is handed over from the default namespace to Rancher Desktop's
// network namespace (rd1), the logic behind this approach was the
// lack of support for network namespaces in the AF_VSOCK libs.
connFile := os.NewFile(uintptr(3), "RancherDesktop-AFVsock-Connection")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no reason not to use spaces in this name?

return
}

if debug {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would logrus.IsLevelEnabled() be a better check?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't see a huge benefit but more verbose code stuff...

errCh <- fmt.Errorf("unexpected size %d", n)
return
}
size := int(binary.LittleEndian.Uint16(sizeBuf[0:2]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — little endian?

return
}

if _, err := tap.Write(buf[:size]); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we check the returned size here?

return
}

if debug {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, logrus.

}
}

func checkForExsitingIf(ifName string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If sounds like the English conjunction. Can we be slightly more verbose here and use iface or interface?

Signed-off-by: Nino Kodabande <[email protected]>
@Nino-K Nino-K requested a review from mook-as February 22, 2023 21:33
for {
select {
case <-ctx.Done():
logrus.Info("exiting tx goroutine")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be rx

.gitignore Outdated
@@ -0,0 +1,4 @@
./bin/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bin/ would be better (to make it clearer that it's relative to the root of the repo), but this works too.

logrus.Debugf("using a AF_VSOCK connection file from default namespace: %v", connFile)

// this should never happen
if err := checkForExsitingIface(tapIface); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did we miss this…

Suggested change
if err := checkForExsitingIface(tapIface); err != nil {
if err := checkForExistingIface(tapIface); err != nil {

go func() {
if err := dhcp(ctx, tapIface); err != nil {
errCh <- fmt.Errorf("dhcp error: %w", err)
ctx.Done()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this line. ctx.Done() returns a channel that will emit when the context is done, but you're dropping the return value on the floor. (It doesn't cause the context to be cancelled; for that we need a different context.WithCancel().)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created the context.WithCancel() I called the wrong function mistakenly. I will pass in the cancel func.

@Nino-K Nino-K requested a review from mook-as February 22, 2023 22:29
Copy link
Contributor

@mook-as mook-as left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just some unimportant comments we can deal with later.

case s := <-sigChan:
logrus.Errorf("signal caught: %v", s)
cancel()
os.Exit(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, if you want to let deferred things run (given that this is main()), one way would be:

func main() {
  exitCode := 0
  defer os.Exit(exitCode)
…
  // in this code here
  exitCode = 1
  return

}
}

func run(ctx context.Context, cancel context.CancelFunc, connFile io.ReadWriteCloser) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connFile is probably better named conn (because the fact that it's a file is opaque).

@Nino-K Nino-K merged commit 37f79f2 into rancher-sandbox:main Feb 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a VM daemon for Rancher Desktop Networking on WSL
2 participants