diff --git a/Makefile b/Makefile
index 9fe4b5923c..a163de85e3 100644
--- a/Makefile
+++ b/Makefile
@@ -199,7 +199,7 @@ endif
 # include this lightweight helper binary.
 #
 GV_GITURL=https://github.com/containers/gvisor-tap-vsock.git
-GV_SHA=aab0ac9367fc5142f5857c36ac2352bcb3c60ab7
+GV_SHA=407efb5dcdb0f4445935f7360535800b60447544
 
 ###
 ### Primary entry-point targets
diff --git a/cmd/podman-wslkerninst/main.go b/cmd/podman-wslkerninst/main.go
index 0485db13c7..8316f7201d 100644
--- a/cmd/podman-wslkerninst/main.go
+++ b/cmd/podman-wslkerninst/main.go
@@ -11,7 +11,7 @@ import (
 	"time"
 	"unsafe"
 
-	"github.com/containers/podman/v4/pkg/machine/wsl"
+	"github.com/containers/podman/v4/pkg/machine/wsl/wutil"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/windows/svc/eventlog"
 )
@@ -49,7 +49,7 @@ func installWslKernel() error {
 	)
 	backoff := 500 * time.Millisecond
 	for i := 1; i < 6; i++ {
-		err = wsl.SilentExec("wsl", "--update")
+		err = wutil.SilentExec("wsl", "--update")
 		if err == nil {
 			break
 		}
@@ -87,7 +87,7 @@ func warn(title string, caption string) int {
 func main() {
 	args := os.Args
 	setupLogging(path.Base(args[0]))
-	if wsl.IsWSLInstalled() {
+	if wutil.IsWSLInstalled() {
 		// nothing to do
 		logrus.Info("WSL Kernel already installed")
 		return
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index 57cb909437..959cef2bd9 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -27,10 +27,17 @@ var (
 	}
 
 	initOpts           = machine.InitOptions{}
+	initOptionalFlags  = InitOptionalFlags{}
 	defaultMachineName = machine.DefaultMachineName
 	now                bool
+	defaultProvider    = GetSystemDefaultProvider()
 )
 
+// Flags which have a meaning when unspecified that differs from the flag default
+type InitOptionalFlags struct {
+	UserModeNetworking bool
+}
+
 // maxMachineNameSize is set to thirty to limit huge machine names primarily
 // because macOS has a much smaller file size limit.
 const maxMachineNameSize = 30
@@ -110,6 +117,10 @@ func init() {
 
 	rootfulFlagName := "rootful"
 	flags.BoolVar(&initOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution")
+
+	userModeNetFlagName := "user-mode-networking"
+	flags.BoolVar(&initOptionalFlags.UserModeNetworking, userModeNetFlagName, false,
+		"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
 }
 
 func initMachine(cmd *cobra.Command, args []string) error {
@@ -118,7 +129,7 @@ func initMachine(cmd *cobra.Command, args []string) error {
 		vm  machine.VM
 	)
 
-	provider := GetSystemDefaultProvider()
+	provider := defaultProvider
 	initOpts.Name = defaultMachineName
 	if len(args) > 0 {
 		if len(args[0]) > maxMachineNameSize {
@@ -132,6 +143,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
 	for idx, vol := range initOpts.Volumes {
 		initOpts.Volumes[idx] = os.ExpandEnv(vol)
 	}
+
+	// Process optional flags (flags where unspecified / nil has meaning )
+	if cmd.Flags().Changed("user-mode-networking") {
+		initOpts.UserModeNetworking = &initOptionalFlags.UserModeNetworking
+	}
+
 	vm, err = provider.NewMachine(initOpts)
 	if err != nil {
 		return err
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 435771da59..ac87bfa647 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -178,6 +178,7 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, err
 		response.RemoteUsername = vm.RemoteUsername
 		response.IdentityPath = vm.IdentityPath
 		response.Starting = vm.Starting
+		response.UserModeNetworking = vm.UserModeNetworking
 
 		machineResponses = append(machineResponses, response)
 	}
diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go
index 1b9e1b2bdb..5c933ea491 100644
--- a/cmd/podman/machine/set.go
+++ b/cmd/podman/machine/set.go
@@ -32,10 +32,11 @@ var (
 )
 
 type SetFlags struct {
-	CPUs     uint64
-	DiskSize uint64
-	Memory   uint64
-	Rootful  bool
+	CPUs               uint64
+	DiskSize           uint64
+	Memory             uint64
+	Rootful            bool
+	UserModeNetworking bool
 }
 
 func init() {
@@ -72,6 +73,10 @@ func init() {
 		"Memory in MB",
 	)
 	_ = setCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
+
+	userModeNetFlagName := "user-mode-networking"
+	flags.BoolVar(&setFlags.UserModeNetworking, userModeNetFlagName, false, // defaults not-relevant due to use of Changed()
+		"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
 }
 
 func setMachine(cmd *cobra.Command, args []string) error {
@@ -102,6 +107,9 @@ func setMachine(cmd *cobra.Command, args []string) error {
 	if cmd.Flags().Changed("disk-size") {
 		setOpts.DiskSize = &setFlags.DiskSize
 	}
+	if cmd.Flags().Changed("user-mode-networking") {
+		setOpts.UserModeNetworking = &setFlags.UserModeNetworking
+	}
 
 	setErrs, lasterr := vm.Set(vmName, setOpts)
 	for _, err := range setErrs {
diff --git a/docs/source/markdown/.gitignore b/docs/source/markdown/.gitignore
index 47161e9831..8807c337f6 100644
--- a/docs/source/markdown/.gitignore
+++ b/docs/source/markdown/.gitignore
@@ -19,7 +19,9 @@ podman-kube-play.1.md
 podman-login.1.md
 podman-logout.1.md
 podman-logs.1.md
+podman-machine-init.1.md
 podman-machine-list.1.md
+podman-machine-set.1.md
 podman-manifest-add.1.md
 podman-manifest-annotate.1.md
 podman-manifest-create.1.md
diff --git a/docs/source/markdown/options/user-mode-networking.md b/docs/source/markdown/options/user-mode-networking.md
new file mode 100644
index 0000000000..dc6129f834
--- /dev/null
+++ b/docs/source/markdown/options/user-mode-networking.md
@@ -0,0 +1,21 @@
+####> This option file is used in:
+####>   podman machine init, machine set
+####> If file is edited, make sure the changes
+####> are applicable to all of those.
+#### **--user-mode-networking**
+
+Whether this machine should relay traffic from the guest through a user-space
+process running on the host. In some VPN configurations the VPN may drop
+traffic from alternate network interfaces, including VM network devices. By
+enabling user-mode networking (a setting of `true`), VPNs will observe all
+podman machine traffic as coming from the host, bypassing the problem.
+
+When the qemu backend is used (Linux, Mac), user-mode networking is
+mandatory and the only allowed value is `true`. In contrast, The Windows/WSL
+backend defaults to `false`, and follows the standard WSL network setup.
+Changing this setting to `true` on Windows/WSL will inform Podman to replace
+the WSL networking setup on start of this machine instance with a user-mode
+networking distribution. Since WSL shares the same kernel across
+distributions, all other running distributions will reuse this network.
+Likewise, when the last machine instance with a `true` setting stops, the
+original networking setup will be restored.
diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md.in
similarity index 99%
rename from docs/source/markdown/podman-machine-init.1.md
rename to docs/source/markdown/podman-machine-init.1.md.in
index df4ba2f501..e5242f8f4c 100644
--- a/docs/source/markdown/podman-machine-init.1.md
+++ b/docs/source/markdown/podman-machine-init.1.md.in
@@ -76,6 +76,8 @@ Set the timezone for the machine and containers.  Valid values are `local` or
 a `timezone` such as `America/Chicago`.  A value of `local`, which is the default,
 means to use the timezone of the machine host.
 
+@@option user-mode-networking
+
 #### **--username**
 
 Username to use for executing commands in remote VM. Default value is `core`
diff --git a/docs/source/markdown/podman-machine-inspect.1.md b/docs/source/markdown/podman-machine-inspect.1.md
index fefe7533b7..6693ec8151 100644
--- a/docs/source/markdown/podman-machine-inspect.1.md
+++ b/docs/source/markdown/podman-machine-inspect.1.md
@@ -31,6 +31,7 @@ Print results with a Go template.
 | .Resources ...      | Resources used by the machine                         |
 | .SSHConfig ...      | SSH configuration info for communitating with machine |
 | .State ...          | Machine state                                         |
+| .UserModeNetworking | Whether this machine uses user-mode networking        |
 
 #### **--help**
 
diff --git a/docs/source/markdown/podman-machine-list.1.md.in b/docs/source/markdown/podman-machine-list.1.md.in
index 80373906fb..2564c983ac 100644
--- a/docs/source/markdown/podman-machine-list.1.md.in
+++ b/docs/source/markdown/podman-machine-list.1.md.in
@@ -32,22 +32,23 @@ Change the default output format.  This can be of a supported type like 'json'
 or a Go template.
 Valid placeholders for the Go template are listed below:
 
-| **Placeholder** | **Description**                 |
-| --------------- | ------------------------------- |
-| .CPUs           | Number of CPUs                  |
-| .Created        | Time since VM creation          |
-| .Default        | Is default machine              |
-| .DiskSize       | Disk size of machine            |
-| .IdentityPath   | Path to ssh identity file       |
-| .LastUp         | Time machine was last up        |
-| .LastUp         | Time since the VM was last run  |
-| .Memory         | Allocated memory for machine   |
-| .Name           | VM name                         |
-| .Port           | SSH Port to use to connect to VM|
-| .RemoteUsername | VM Username for rootless Podman |
-| .Running        | Is machine running              |
-| .Stream         | Stream name                     |
-| .VMType         | VM type                         |
+| **Placeholder**     | **Description**                           |
+| ------------------- | ----------------------------------------- |
+| .CPUs               | Number of CPUs                            |
+| .Created            | Time since VM creation                    |
+| .Default            | Is default machine                        |
+| .DiskSize           | Disk size of machine                      |
+| .IdentityPath       | Path to ssh identity file                 |
+| .LastUp             | Time machine was last up                  |
+| .LastUp             | Time since the VM was last run            |
+| .Memory             | Allocated memory for machine              |
+| .Name               | VM name                                   |
+| .Port               | SSH Port to use to connect to VM          |
+| .RemoteUsername     | VM Username for rootless Podman           |
+| .Running            | Is machine running                        |
+| .Stream             | Stream name                               |
+| .UserModeNetworking | Whether machine uses user-mode networking |
+| .VMType             | VM type                                   |
 
 #### **--help**
 
diff --git a/docs/source/markdown/podman-machine-set.1.md b/docs/source/markdown/podman-machine-set.1.md.in
similarity index 98%
rename from docs/source/markdown/podman-machine-set.1.md
rename to docs/source/markdown/podman-machine-set.1.md.in
index a5ca5033ee..0cc6852c8e 100644
--- a/docs/source/markdown/podman-machine-set.1.md
+++ b/docs/source/markdown/podman-machine-set.1.md.in
@@ -40,6 +40,8 @@ container execution. This option will also update the current podman
 remote connection default if it is currently pointing at the specified
 machine name (or `podman-machine-default` if no name is specified).
 
+@@option user-mode-networking
+
 Unlike [**podman system connection default**](podman-system-connection-default.1.md)
 this option will also make the API socket, if available, forward to the rootful/rootless
 socket in the VM.
diff --git a/pkg/domain/entities/machine.go b/pkg/domain/entities/machine.go
index 4fd0413c9b..bd8be9779c 100644
--- a/pkg/domain/entities/machine.go
+++ b/pkg/domain/entities/machine.go
@@ -3,20 +3,21 @@ package entities
 import "github.com/containers/podman/v4/libpod/define"
 
 type ListReporter struct {
-	Name           string
-	Default        bool
-	Created        string
-	Running        bool
-	Starting       bool
-	LastUp         string
-	Stream         string
-	VMType         string
-	CPUs           uint64
-	Memory         string
-	DiskSize       string
-	Port           int
-	RemoteUsername string
-	IdentityPath   string
+	Name               string
+	Default            bool
+	Created            string
+	Running            bool
+	Starting           bool
+	LastUp             string
+	Stream             string
+	VMType             string
+	CPUs               uint64
+	Memory             string
+	DiskSize           string
+	Port               int
+	RemoteUsername     string
+	IdentityPath       string
+	UserModeNetworking bool
 }
 
 // MachineInfo contains info on the machine host and version info
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index 1e1021d9a7..5cd44bb712 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -17,22 +17,22 @@ import (
 )
 
 type InitOptions struct {
-	CPUS         uint64
-	DiskSize     uint64
-	IgnitionPath string
-	ImagePath    string
-	Volumes      []string
-	VolumeDriver string
-	IsDefault    bool
-	Memory       uint64
-	Name         string
-	TimeZone     string
-	URI          url.URL
-	Username     string
-	ReExec       bool
-	Rootful      bool
-	// The numerical userid of the user that called machine
-	UID string
+	CPUS               uint64
+	DiskSize           uint64
+	IgnitionPath       string
+	ImagePath          string
+	Volumes            []string
+	VolumeDriver       string
+	IsDefault          bool
+	Memory             uint64
+	Name               string
+	TimeZone           string
+	URI                url.URL
+	Username           string
+	ReExec             bool
+	Rootful            bool
+	UID                string // uid of the user that called machine
+	UserModeNetworking *bool  // nil = use backend/system default, false = disable, true = enable
 }
 
 type Status = string
@@ -91,26 +91,28 @@ type Download struct {
 type ListOptions struct{}
 
 type ListResponse struct {
-	Name           string
-	CreatedAt      time.Time
-	LastUp         time.Time
-	Running        bool
-	Starting       bool
-	Stream         string
-	VMType         string
-	CPUs           uint64
-	Memory         uint64
-	DiskSize       uint64
-	Port           int
-	RemoteUsername string
-	IdentityPath   string
+	Name               string
+	CreatedAt          time.Time
+	LastUp             time.Time
+	Running            bool
+	Starting           bool
+	Stream             string
+	VMType             string
+	CPUs               uint64
+	Memory             uint64
+	DiskSize           uint64
+	Port               int
+	RemoteUsername     string
+	IdentityPath       string
+	UserModeNetworking bool
 }
 
 type SetOptions struct {
-	CPUs     *uint64
-	DiskSize *uint64
-	Memory   *uint64
-	Rootful  *bool
+	CPUs               *uint64
+	DiskSize           *uint64
+	Memory             *uint64
+	Rootful            *bool
+	UserModeNetworking *bool
 }
 
 type SSHOptions struct {
@@ -151,15 +153,16 @@ type DistributionDownload interface {
 	CleanCache() error
 }
 type InspectInfo struct {
-	ConfigPath     VMFile
-	ConnectionInfo ConnectionConfig
-	Created        time.Time
-	Image          ImageConfig
-	LastUp         time.Time
-	Name           string
-	Resources      ResourceConfig
-	SSHConfig      SSHConfig
-	State          Status
+	ConfigPath         VMFile
+	ConnectionInfo     ConnectionConfig
+	Created            time.Time
+	Image              ImageConfig
+	LastUp             time.Time
+	Name               string
+	Resources          ResourceConfig
+	SSHConfig          SSHConfig
+	State              Status
+	UserModeNetworking bool
 }
 
 func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL {
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index a046a8977b..ea73a54568 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -364,6 +364,11 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
 	if err := v.resizeDisk(opts.DiskSize, originalDiskSize>>(10*3)); err != nil {
 		return false, err
 	}
+
+	if opts.UserModeNetworking != nil && !*opts.UserModeNetworking {
+		logrus.Warn("ignoring init option to disable user-mode networking: this mode is not supported by the QEMU backend")
+	}
+
 	// If the user provides an ignition file, we need to
 	// copy it into the conf dir
 	if len(opts.IgnitionPath) > 0 {
@@ -1152,6 +1157,7 @@ func getVMInfos() ([]*machine.ListResponse, error) {
 			listEntry.IdentityPath = vm.IdentityPath
 			listEntry.CreatedAt = vm.Created
 			listEntry.Starting = vm.Starting
+			listEntry.UserModeNetworking = true // always true
 
 			if listEntry.CreatedAt.IsZero() {
 				listEntry.CreatedAt = time.Now()
@@ -1609,15 +1615,16 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
 	}
 	connInfo.PodmanSocket = podmanSocket
 	return &machine.InspectInfo{
-		ConfigPath:     v.ConfigPath,
-		ConnectionInfo: *connInfo,
-		Created:        v.Created,
-		Image:          v.ImageConfig,
-		LastUp:         v.LastUp,
-		Name:           v.Name,
-		Resources:      v.ResourceConfig,
-		SSHConfig:      v.SSHConfig,
-		State:          state,
+		ConfigPath:         v.ConfigPath,
+		ConnectionInfo:     *connInfo,
+		Created:            v.Created,
+		Image:              v.ImageConfig,
+		LastUp:             v.LastUp,
+		Name:               v.Name,
+		Resources:          v.ResourceConfig,
+		SSHConfig:          v.SSHConfig,
+		State:              state,
+		UserModeNetworking: true, // always true
 	}, nil
 }
 
diff --git a/pkg/machine/wsl/filelock.go b/pkg/machine/wsl/filelock.go
new file mode 100644
index 0000000000..dadc806629
--- /dev/null
+++ b/pkg/machine/wsl/filelock.go
@@ -0,0 +1,73 @@
+//go:build windows
+// +build windows
+
+package wsl
+
+import (
+	"io/fs"
+	"math"
+	"os"
+
+	"golang.org/x/sys/windows"
+)
+
+type fileLock struct {
+	file *os.File
+}
+
+// Locks a file path, creating or overwriting a file if necessary. This API only
+// supports dedicated empty lock files. Locking is not advisory, once a file is
+// locked, additional opens will block on read/write.
+func lockFile(path string) (*fileLock, error) {
+	// In the future we may want to switch this to an async open vs the win32 API
+	// to bring support for timeouts, so we don't export the current underlying
+	// File object.
+	file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
+	if err != nil {
+		return nil, &fs.PathError{
+			Op:   "lock",
+			Path: path,
+			Err:  err,
+		}
+	}
+
+	const max = uint32(math.MaxUint32)
+	overlapped := new(windows.Overlapped)
+	lockType := windows.LOCKFILE_EXCLUSIVE_LOCK
+	// Lock largest possible length (all 64 bits (lo + hi) set)
+	err = windows.LockFileEx(windows.Handle(file.Fd()), uint32(lockType), 0, max, max, overlapped)
+	if err != nil {
+		file.Close()
+		return nil, &fs.PathError{
+			Op:   "lock",
+			Path: file.Name(),
+			Err:  err,
+		}
+	}
+
+	return &fileLock{file: file}, nil
+}
+
+func (flock *fileLock) unlock() error {
+	if flock == nil || flock.file == nil {
+		return nil
+	}
+
+	defer func() {
+		flock.file.Close()
+		flock.file = nil
+	}()
+
+	const max = uint32(math.MaxUint32)
+	overlapped := new(windows.Overlapped)
+	err := windows.UnlockFileEx(windows.Handle(flock.file.Fd()), 0, max, max, overlapped)
+	if err != nil {
+		return &fs.PathError{
+			Op:   "unlock",
+			Path: flock.file.Name(),
+			Err:  err,
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index db14af074a..9d790c9783 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/containers/common/pkg/config"
 	"github.com/containers/podman/v4/pkg/machine"
+	"github.com/containers/podman/v4/pkg/machine/wsl/wutil"
 	"github.com/containers/podman/v4/utils"
 	"github.com/containers/storage/pkg/homedir"
 	"github.com/containers/storage/pkg/ioutils"
@@ -115,6 +116,15 @@ const wslConf = `[user]
 default=[USER]
 `
 
+const wslConfUserNet = `
+[network]
+generateResolvConf = false
+`
+
+const resolvConfUserNet = `
+nameserver 192.168.127.1
+`
+
 // WSL kernel does not have sg and crypto_user modules
 const overrideSysusers = `[Service]
 LoadCredential=
@@ -198,10 +208,12 @@ http://docs.microsoft.com/en-us/windows/wsl/install\
 `
 
 const (
+	gvProxy        = "gvproxy.exe"
 	winSShProxy    = "win-sshproxy.exe"
 	winSshProxyTid = "win-sshproxy.tid"
 	pipePrefix     = "npipe:////./pipe/"
 	globalPipe     = "docker_engine"
+	userModeDist   = "podman-net-usermode"
 )
 
 type Virtualization struct {
@@ -241,6 +253,8 @@ type MachineVM struct {
 	machine.SSHConfig
 	// machine version
 	Version int
+	// Whether to use user-mode networking
+	UserModeNetworking bool
 }
 
 type ExitCodeError struct {
@@ -276,6 +290,11 @@ func (p *Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error
 	vm.Created = time.Now()
 	vm.LastUp = vm.Created
 
+	// Default is false
+	if opts.UserModeNetworking != nil {
+		vm.UserModeNetworking = *opts.UserModeNetworking
+	}
+
 	// Add a random port for ssh
 	port, err := utils.GetRandomPort()
 	if err != nil {
@@ -395,11 +414,19 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
 		return false, err
 	}
 
-	dist, err := provisionWSLDist(v)
+	const prompt = "Importing operating system into WSL (this may take a few minutes on a new WSL install)..."
+	dist, err := provisionWSLDist(v.Name, v.ImagePath, prompt)
 	if err != nil {
 		return false, err
 	}
 
+	if v.UserModeNetworking {
+		if err = installUserModeDist(dist, v.ImagePath); err != nil {
+			_ = unregisterDist(dist)
+			return false, err
+		}
+	}
+
 	fmt.Println("Configuring system...")
 	if err = configureSystem(v, dist); err != nil {
 		return false, err
@@ -496,21 +523,21 @@ func setupConnections(v *MachineVM, opts machine.InitOptions, sshDir string) err
 	return nil
 }
 
-func provisionWSLDist(v *MachineVM) (string, error) {
+func provisionWSLDist(name string, imagePath string, prompt string) (string, error) {
 	vmDataDir, err := machine.GetDataDir(vmtype)
 	if err != nil {
 		return "", err
 	}
 
 	distDir := filepath.Join(vmDataDir, "wsldist")
-	distTarget := filepath.Join(distDir, v.Name)
+	distTarget := filepath.Join(distDir, name)
 	if err := os.MkdirAll(distDir, 0755); err != nil {
 		return "", fmt.Errorf("could not create wsldist directory: %w", err)
 	}
 
-	dist := toDist(v.Name)
-	fmt.Println("Importing operating system into WSL (this may take a few minutes on a new WSL install)...")
-	if err = runCmdPassThrough("wsl", "--import", dist, distTarget, v.ImagePath, "--version", "2"); err != nil {
+	dist := toDist(name)
+	fmt.Println(prompt)
+	if err = runCmdPassThrough("wsl", "--import", dist, distTarget, imagePath, "--version", "2"); err != nil {
 		return "", fmt.Errorf("the WSL import of guest OS failed: %w", err)
 	}
 
@@ -519,15 +546,6 @@ func provisionWSLDist(v *MachineVM) (string, error) {
 		return "", fmt.Errorf("package permissions restore of shadow-utils on guest OS failed: %w", err)
 	}
 
-	// Windows 11 (NT Version = 10, Build 22000) generates harmless but scary messages on every
-	// operation when mount was not present on the initial start. Force a cycle so that it won't
-	// repeatedly complain.
-	if winVersionAtLeast(10, 0, 22000) {
-		if err := terminateDist(dist); err != nil {
-			logrus.Warnf("could not cycle WSL dist: %s", err.Error())
-		}
-	}
-
 	return dist, nil
 }
 
@@ -606,11 +624,7 @@ func configureSystem(v *MachineVM, dist string) error {
 		return fmt.Errorf("could not create podman-machine file for guest OS: %w", err)
 	}
 
-	if err := wslPipe(withUser(wslConf, user), dist, "sh", "-c", "cat > /etc/wsl.conf"); err != nil {
-		return fmt.Errorf("could not configure wsl config for guest OS: %w", err)
-	}
-
-	return nil
+	return changeDistUserModeNetworking(dist, user, "", v.UserModeNetworking)
 }
 
 func configureProxy(dist string, useProxy bool, quiet bool) error {
@@ -694,8 +708,16 @@ func installScripts(dist string) error {
 	return nil
 }
 
+func writeWslConf(dist string, user string) error {
+	if err := wslPipe(withUser(wslConf, user), dist, "sh", "-c", "cat > /etc/wsl.conf"); err != nil {
+		return fmt.Errorf("could not configure wsl config for guest OS: %w", err)
+	}
+
+	return nil
+}
+
 func checkAndInstallWSL(opts machine.InitOptions) (bool, error) {
-	if IsWSLInstalled() {
+	if wutil.IsWSLInstalled() {
 		return true, nil
 	}
 
@@ -1015,6 +1037,27 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
 		setErrors = append(setErrors, errors.New("changing Disk Size not supported for WSL machines"))
 	}
 
+	if opts.UserModeNetworking != nil && *opts.UserModeNetworking != v.UserModeNetworking {
+		update := true
+		if v.isRunning() {
+			update = false
+			setErrors = append(setErrors, fmt.Errorf("user-mode networking can only be changed when the machine is not running"))
+		}
+
+		if update && *opts.UserModeNetworking {
+			dist := toDist(v.Name)
+
+			if err := changeDistUserModeNetworking(dist, v.RemoteUsername, v.ImagePath, *opts.UserModeNetworking); err != nil {
+				update = false
+				setErrors = append(setErrors, err)
+			}
+		}
+
+		if update {
+			v.UserModeNetworking = *opts.UserModeNetworking
+		}
+	}
+
 	return setErrors, v.writeConfig()
 }
 
@@ -1029,6 +1072,11 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
 		return err
 	}
 
+	// Startup user-mode networking if enabled
+	if err := v.startUserModeNetworking(); err != nil {
+		return err
+	}
+
 	err := wslInvoke(dist, "/root/bootstrap")
 	if err != nil {
 		return fmt.Errorf("the WSL bootstrap script failed: %w", err)
@@ -1072,6 +1120,20 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
 	return err
 }
 
+func findExecutablePeer(name string) (string, error) {
+	exe, err := os.Executable()
+	if err != nil {
+		return "", err
+	}
+
+	exe, err = filepath.EvalSymlinks(exe)
+	if err != nil {
+		return "", err
+	}
+
+	return filepath.Join(filepath.Dir(exe), name), nil
+}
+
 func launchWinProxy(v *MachineVM) (bool, string, error) {
 	machinePipe := toDist(v.Name)
 	if !machine.PipeNameAvailable(machinePipe) {
@@ -1083,17 +1145,11 @@ func launchWinProxy(v *MachineVM) (bool, string, error) {
 		globalName = true
 	}
 
-	exe, err := os.Executable()
+	command, err := findExecutablePeer(winSShProxy)
 	if err != nil {
 		return globalName, "", err
 	}
 
-	exe, err = filepath.EvalSymlinks(exe)
-	if err != nil {
-		return globalName, "", err
-	}
-
-	command := filepath.Join(filepath.Dir(exe), winSShProxy)
 	stateDir, err := getWinProxyStateDir(v)
 	if err != nil {
 		return globalName, "", err
@@ -1143,59 +1199,54 @@ func getWinProxyStateDir(v *MachineVM) (string, error) {
 	return stateDir, nil
 }
 
-func IsWSLInstalled() bool {
-	cmd := SilentExecCmd("wsl", "--status")
-	out, err := cmd.StdoutPipe()
-	cmd.Stderr = nil
-	if err != nil {
-		return false
-	}
-	if err = cmd.Start(); err != nil {
-		return false
-	}
-	scanner := bufio.NewScanner(transform.NewReader(out, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
-	result := true
-	for scanner.Scan() {
-		line := scanner.Text()
-		// Windows 11 does not set an error exit code when a kernel is not avail
-		if strings.Contains(line, "kernel file is not found") {
-			result = false
-			break
-		}
-	}
-	if err := cmd.Wait(); !result || err != nil {
-		return false
-	}
-
-	return true
-}
-
 func IsWSLFeatureEnabled() bool {
-	return SilentExec("wsl", "--set-default-version", "2") == nil
+	return wutil.SilentExec("wsl", "--set-default-version", "2") == nil
 }
 
 func isWSLRunning(dist string) (bool, error) {
-	cmd := exec.Command("wsl", "-l", "--running", "--quiet")
-	out, err := cmd.StdoutPipe()
+	return wslCheckExists(dist, true)
+}
+
+func isWSLExist(dist string) (bool, error) {
+	return wslCheckExists(dist, false)
+}
+
+func wslCheckExists(dist string, running bool) (bool, error) {
+	all, err := getAllWSLDistros(running)
 	if err != nil {
 		return false, err
 	}
-	if err = cmd.Start(); err != nil {
-		return false, err
+
+	_, exists := all[dist]
+	return exists, nil
+}
+
+func getAllWSLDistros(running bool) (map[string]struct{}, error) {
+	args := []string{"-l", "--quiet"}
+	if running {
+		args = append(args, "--running")
 	}
+	cmd := exec.Command("wsl", args...)
+	out, err := cmd.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+	if err = cmd.Start(); err != nil {
+		return nil, err
+	}
+
+	all := make(map[string]struct{})
 	scanner := bufio.NewScanner(transform.NewReader(out, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
-	result := false
 	for scanner.Scan() {
 		fields := strings.Fields(scanner.Text())
-		if len(fields) > 0 && dist == fields[0] {
-			result = true
-			break
+		if len(fields) > 0 {
+			all[fields[0]] = struct{}{}
 		}
 	}
 
 	_ = cmd.Wait()
 
-	return result, nil
+	return all, nil
 }
 
 func isSystemdRunning(dist string) (bool, error) {
@@ -1243,6 +1294,11 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
 		return fmt.Errorf("%q is not running", v.Name)
 	}
 
+	// Stop user-mode networking if enabled
+	if err := v.stopUserModeNetworking(dist); err != nil {
+		fmt.Fprintf(os.Stderr, "Could not cleanly stop user-mode networking: %s\n", err.Error())
+	}
+
 	_, _, _ = v.updateTimeStamps(true)
 
 	if err := stopWinProxy(v); err != nil {
@@ -1272,6 +1328,11 @@ func terminateDist(dist string) error {
 	return cmd.Run()
 }
 
+func unregisterDist(dist string) error {
+	cmd := exec.Command("wsl", "--unregister", dist)
+	return cmd.Run()
+}
+
 func (v *MachineVM) State(bypass bool) (machine.Status, error) {
 	if v.isRunning() {
 		return machine.Running, nil
@@ -1470,6 +1531,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
 			listEntry.Port = vm.Port
 			listEntry.IdentityPath = vm.IdentityPath
 			listEntry.Starting = false
+			listEntry.UserModeNetworking = vm.UserModeNetworking
 
 			running := vm.isRunning()
 			listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running)
@@ -1624,11 +1686,12 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
 			ImagePath:   machine.VMFile{Path: v.ImagePath},
 			ImageStream: v.ImageStream,
 		},
-		LastUp:    lastUp,
-		Name:      v.Name,
-		Resources: v.getResources(),
-		SSHConfig: v.SSHConfig,
-		State:     state,
+		LastUp:             lastUp,
+		Name:               v.Name,
+		Resources:          v.getResources(),
+		SSHConfig:          v.SSHConfig,
+		State:              state,
+		UserModeNetworking: v.UserModeNetworking,
 	}, nil
 }
 
diff --git a/pkg/machine/wsl/usermodenet.go b/pkg/machine/wsl/usermodenet.go
new file mode 100644
index 0000000000..798c248a16
--- /dev/null
+++ b/pkg/machine/wsl/usermodenet.go
@@ -0,0 +1,326 @@
+//go:build windows
+// +build windows
+
+package wsl
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+
+	"github.com/containers/podman/v4/pkg/machine"
+	"github.com/containers/podman/v4/pkg/specgen"
+	"github.com/sirupsen/logrus"
+)
+
+const startUserModeNet = `
+set -e
+STATE=/mnt/wsl/podman-usermodenet
+mkdir -p $STATE
+cp -f /mnt/wsl/resolv.conf $STATE/resolv.orig
+ip route show default > $STATE/route.dat
+ROUTE=$(<$STATE/route.dat)
+if [[ $ROUTE =~ .*192\.168\.127\.1.* ]]; then
+	exit 2
+fi
+if [[ ! $ROUTE =~ default\ via ]]; then
+	exit 3
+fi
+nohup /usr/local/bin/vm -iface podman-usermode -stop-if-exist ignore -url "stdio:$GVPROXY?listen-stdio=accept" > /var/log/vm.log 2> /var/log/vm.err  < /dev/null &
+echo $! > $STATE/vm.pid
+sleep 1
+ps -eo args | grep -q -m1 ^/usr/local/bin/vm || exit 42
+`
+
+const stopUserModeNet = `
+STATE=/mnt/wsl/podman-usermodenet
+if [[ ! -f "$STATE/vm.pid" || ! -f "$STATE/route.dat" ]]; then
+	exit 2
+fi
+cp -f $STATE/resolv.orig /mnt/wsl/resolv.conf
+GPID=$(<$STATE/vm.pid)
+kill $GPID > /dev/null
+while kill -0 $GPID > /dev/null 2>&1; do
+	sleep 1
+done
+ip route del default > /dev/null 2>&1
+ROUTE=$(<$STATE/route.dat)
+if [[ ! $ROUTE =~ default\ via ]]; then
+	exit 3
+fi
+ip route add $ROUTE
+rm -rf /mnt/wsl/podman-usermodenet
+`
+
+func (v *MachineVM) startUserModeNetworking() error {
+	if !v.UserModeNetworking {
+		return nil
+	}
+
+	exe, err := findExecutablePeer(gvProxy)
+	if err != nil {
+		return fmt.Errorf("could not locate %s, which is necessary for user-mode networking, please reinstall", gvProxy)
+	}
+
+	flock, err := v.obtainUserModeNetLock()
+	if err != nil {
+		return err
+	}
+	defer flock.unlock()
+
+	running, err := isWSLRunning(userModeDist)
+	if err != nil {
+		return err
+	}
+	running = running && isGvProxyVMRunning()
+
+	// Start or reuse
+	if !running {
+		if err := v.launchUserModeNetDist(exe); err != nil {
+			return err
+		}
+	}
+
+	if err := createUserModeResolvConf(toDist(v.Name)); err != nil {
+		return err
+	}
+
+	// Register in-use
+	err = v.addUserModeNetEntry()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (v *MachineVM) stopUserModeNetworking(dist string) error {
+	if !v.UserModeNetworking {
+		return nil
+	}
+
+	flock, err := v.obtainUserModeNetLock()
+	if err != nil {
+		return err
+	}
+	defer flock.unlock()
+
+	err = v.removeUserModeNetEntry()
+	if err != nil {
+		return err
+	}
+
+	count, err := v.cleanupAndCountNetEntries()
+	if err != nil {
+		return err
+	}
+
+	// Leave running if still in-use
+	if count > 0 {
+		return nil
+	}
+
+	fmt.Println("Stopping user-mode networking...")
+
+	err = wslPipe(stopUserModeNet, userModeDist, "bash")
+	if err != nil {
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			switch exitErr.ExitCode() {
+			case 2:
+				err = fmt.Errorf("startup state was missing")
+			case 3:
+				err = fmt.Errorf("route state is missing a default route")
+			}
+		}
+		logrus.Warnf("problem tearing down user-mode networking cleanly, forcing: %s", err.Error())
+	}
+
+	return terminateDist(userModeDist)
+}
+
+func isGvProxyVMRunning() bool {
+	return wslInvoke(userModeDist, "bash", "-c", "ps -eo args | grep -q -m1 ^/usr/local/bin/vm || exit 42") == nil
+}
+
+func (v *MachineVM) launchUserModeNetDist(exeFile string) error {
+	fmt.Println("Starting user-mode networking...")
+
+	exe, err := specgen.ConvertWinMountPath(exeFile)
+	if err != nil {
+		return err
+	}
+
+	cmdStr := fmt.Sprintf("GVPROXY=%q\n%s", exe, startUserModeNet)
+	if err := wslPipe(cmdStr, userModeDist, "bash"); err != nil {
+		_ = terminateDist(userModeDist)
+
+		if exitErr, ok := err.(*exec.ExitError); ok {
+			switch exitErr.ExitCode() {
+			case 2:
+				return fmt.Errorf("another user-mode network is running, only one can be used at a time: shut down all machines and run wsl --shutdown if this is unexpected")
+			case 3:
+				err = fmt.Errorf("route state is missing a default route: shutdown all machines and run wsl --shutdown to recover")
+			}
+		}
+
+		return fmt.Errorf("error setting up user-mode networking: %w", err)
+	}
+
+	return nil
+}
+
+func installUserModeDist(dist string, imagePath string) error {
+	exists, err := isWSLExist(userModeDist)
+	if err != nil {
+		return err
+	}
+
+	if !exists {
+		if err := wslInvoke(dist, "test", "-f", "/usr/local/bin/vm"); err != nil {
+			return fmt.Errorf("existing machine is too old, can't install user-mode networking dist until machine is reinstalled (using podman machine rm, then podman machine init)")
+		}
+
+		const prompt = "Installing user-mode networking distribution..."
+		if _, err := provisionWSLDist(userModeDist, imagePath, prompt); err != nil {
+			return err
+		}
+
+		_ = terminateDist(userModeDist)
+	}
+
+	return nil
+}
+
+func createUserModeResolvConf(dist string) error {
+	err := wslPipe(resolvConfUserNet, dist, "bash", "-c", "(rm -f /etc/resolv.conf; cat > /etc/resolv.conf)")
+	if err != nil {
+		return fmt.Errorf("could not create resolv.conf: %w", err)
+	}
+	return err
+}
+
+func (v *MachineVM) getUserModeNetDir() (string, error) {
+	vmDataDir, err := machine.GetDataDir(vmtype)
+	if err != nil {
+		return "", err
+	}
+
+	dir := filepath.Join(vmDataDir, userModeDist)
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		return "", fmt.Errorf("could not create %s directory: %w", userModeDist, err)
+	}
+
+	return dir, nil
+}
+
+func (v *MachineVM) getUserModeNetEntriesDir() (string, error) {
+	netDir, err := v.getUserModeNetDir()
+	if err != nil {
+		return "", err
+	}
+
+	dir := filepath.Join(netDir, "entries")
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		return "", fmt.Errorf("could not create %s/entries directory: %w", userModeDist, err)
+	}
+
+	return dir, nil
+}
+
+func (v *MachineVM) addUserModeNetEntry() error {
+	entriesDir, err := v.getUserModeNetEntriesDir()
+	if err != nil {
+		return err
+	}
+
+	path := filepath.Join(entriesDir, toDist(v.Name))
+	file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+	if err != nil {
+		return fmt.Errorf("could not add user-mode networking registration: %w", err)
+	}
+	file.Close()
+	return nil
+}
+
+func (v *MachineVM) removeUserModeNetEntry() error {
+	entriesDir, err := v.getUserModeNetEntriesDir()
+	if err != nil {
+		return err
+	}
+
+	path := filepath.Join(entriesDir, toDist(v.Name))
+	return os.Remove(path)
+}
+
+func (v *MachineVM) cleanupAndCountNetEntries() (uint, error) {
+	entriesDir, err := v.getUserModeNetEntriesDir()
+	if err != nil {
+		return 0, err
+	}
+
+	allDists, err := getAllWSLDistros(true)
+	if err != nil {
+		return 0, err
+	}
+
+	var count uint = 0
+	files, err := os.ReadDir(entriesDir)
+	if err != nil {
+		return 0, err
+	}
+
+	for _, file := range files {
+		_, running := allDists[file.Name()]
+		if !running {
+			_ = os.Remove(filepath.Join(entriesDir, file.Name()))
+			continue
+		}
+		count++
+	}
+
+	return count, nil
+}
+
+func (v *MachineVM) obtainUserModeNetLock() (*fileLock, error) {
+	dir, err := v.getUserModeNetDir()
+
+	if err != nil {
+		return nil, err
+	}
+
+	var flock *fileLock
+	lockPath := filepath.Join(dir, "podman-usermodenet.lck")
+	if flock, err = lockFile(lockPath); err != nil {
+		return nil, fmt.Errorf("could not lock user-mode networking lock file: %w", err)
+	}
+
+	return flock, nil
+}
+
+func changeDistUserModeNetworking(dist string, user string, image string, enable bool) error {
+	// Only install if user-mode is being enabled and there was an image path passed
+	if enable && len(image) > 0 {
+		if err := installUserModeDist(dist, image); err != nil {
+			return err
+		}
+	}
+
+	if err := writeWslConf(dist, user); err != nil {
+		return err
+	}
+
+	if enable {
+		return appendDisableAutoResolve(dist)
+	}
+
+	return nil
+}
+
+func appendDisableAutoResolve(dist string) error {
+	if err := wslPipe(wslConfUserNet, dist, "sh", "-c", "cat >> /etc/wsl.conf"); err != nil {
+		return fmt.Errorf("could not append resolv config to wsl.conf: %w", err)
+	}
+
+	return nil
+}
diff --git a/pkg/machine/wsl/util_windows.go b/pkg/machine/wsl/util_windows.go
index 8672f2593f..fac85f9b90 100644
--- a/pkg/machine/wsl/util_windows.go
+++ b/pkg/machine/wsl/util_windows.go
@@ -8,7 +8,6 @@ import (
 	"errors"
 	"fmt"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"strings"
 	"syscall"
@@ -345,17 +344,3 @@ func sendQuit(tid uint32) {
 	postMessage := user32.NewProc("PostThreadMessageW")
 	postMessage.Call(uintptr(tid), WM_QUIT, 0, 0)
 }
-
-func SilentExec(command string, args ...string) error {
-	cmd := exec.Command(command, args...)
-	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
-	cmd.Stdout = nil
-	cmd.Stderr = nil
-	return cmd.Run()
-}
-
-func SilentExecCmd(command string, args ...string) *exec.Cmd {
-	cmd := exec.Command(command, args...)
-	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
-	return cmd
-}
diff --git a/pkg/machine/wsl/wutil/wutil.go b/pkg/machine/wsl/wutil/wutil.go
new file mode 100644
index 0000000000..b1fb02afd5
--- /dev/null
+++ b/pkg/machine/wsl/wutil/wutil.go
@@ -0,0 +1,55 @@
+//go:build windows
+// +build windows
+
+package wutil
+
+import (
+	"bufio"
+	"os/exec"
+	"strings"
+	"syscall"
+
+	"golang.org/x/text/encoding/unicode"
+	"golang.org/x/text/transform"
+)
+
+func SilentExec(command string, args ...string) error {
+	cmd := exec.Command(command, args...)
+	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
+	cmd.Stdout = nil
+	cmd.Stderr = nil
+	return cmd.Run()
+}
+
+func SilentExecCmd(command string, args ...string) *exec.Cmd {
+	cmd := exec.Command(command, args...)
+	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
+	return cmd
+}
+
+func IsWSLInstalled() bool {
+	cmd := SilentExecCmd("wsl", "--status")
+	out, err := cmd.StdoutPipe()
+	cmd.Stderr = nil
+	if err != nil {
+		return false
+	}
+	if err = cmd.Start(); err != nil {
+		return false
+	}
+	scanner := bufio.NewScanner(transform.NewReader(out, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
+	result := true
+	for scanner.Scan() {
+		line := scanner.Text()
+		// Windows 11 does not set an error exit code when a kernel is not avail
+		if strings.Contains(line, "kernel file is not found") {
+			result = false
+			break
+		}
+	}
+	if err := cmd.Wait(); !result || err != nil {
+		return false
+	}
+
+	return true
+}