mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 17:26:28 +00:00
qemu: Add -networking option, with various new alternatives
This follows the model in the hyperkit runner, although the options are different. The options are: - `user`: the existing user mode networking (the default). - `tap,«device»`: replaces the previous `-tap-device «device»` option. - `bridge,«name»`: tap device on (preexisting) named bridge. - `none`: No networking at all. If not running as root then `bridge` mode requires host configuration http://wiki.qemu.org/Features/HelperNetworking. TL;DR: you need to `chmod u+s` the `qemu-bridge-helper` and to whitelist specific bridges in `/etc/qemu/bridge.conf`. Pass an explicit virtio nic and configure a random MAC since QEMU seems to use the same one by default. In the hyperkit runner the various `networking*` constants become `hyperkitNetworking*` to avoid namespace clashes (e.g. for `None`). The QEMU equivalents are `qemuNetworking*`. Both hyperkit and qemu now support an explicit `-networking default` or `-networking ''` to make scripting easier. Signed-off-by: Ian Campbell <ijc@docker.com>
This commit is contained in:
parent
cb86cdb027
commit
096aec0a19
@ -17,10 +17,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
networkingNone string = "none"
|
||||
networkingDockerForMac = "docker-for-mac"
|
||||
networkingVPNKit = "vpnkit"
|
||||
networkingVMNet = "vmnet"
|
||||
hyperkitNetworkingNone string = "none"
|
||||
hyperkitNetworkingDockerForMac = "docker-for-mac"
|
||||
hyperkitNetworkingVPNKit = "vpnkit"
|
||||
hyperkitNetworkingVMNet = "vmnet"
|
||||
hyperkitNetworkingDefault = hyperkitNetworkingDockerForMac
|
||||
)
|
||||
|
||||
// Process the run arguments and execute run
|
||||
@ -43,7 +44,7 @@ func runHyperKit(args []string) {
|
||||
ipStr := flags.String("ip", "", "IP address for the VM")
|
||||
state := flags.String("state", "", "Path to directory to keep VM state in")
|
||||
vsockports := flags.String("vsock-ports", "", "List of vsock ports to forward from the guest on startup (comma separated). A unix domain socket for each port will be created in the state directory")
|
||||
networking := flags.String("networking", networkingDockerForMac, "Networking mode. Valid options are 'docker-for-mac', 'vpnkit[,socket-path]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket specified. If socket-path is omitted a new VPNKit instance will be started and 'vpnkit_eth.sock' will be created in the state directory. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`")
|
||||
networking := flags.String("networking", hyperkitNetworkingDefault, "Networking mode. Valid options are 'default', 'docker-for-mac', 'vpnkit[,socket-path]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket specified. If socket-path is omitted a new VPNKit instance will be started and 'vpnkit_eth.sock' will be created in the state directory. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
log.Fatal("Unable to parse args")
|
||||
@ -130,12 +131,16 @@ func runHyperKit(args []string) {
|
||||
|
||||
// Select network mode
|
||||
var vpnKitProcess *os.Process
|
||||
if *networking == "" || *networking == "default" {
|
||||
dflt := hyperkitNetworkingDefault
|
||||
networking = &dflt
|
||||
}
|
||||
netMode := strings.SplitN(*networking, ",", 2)
|
||||
|
||||
switch netMode[0] {
|
||||
case networkingDockerForMac:
|
||||
case hyperkitNetworkingDockerForMac:
|
||||
h.VPNKitSock = filepath.Join(os.Getenv("HOME"), "Library/Containers/com.docker.docker/Data/s50")
|
||||
case networkingVPNKit:
|
||||
case hyperkitNetworkingVPNKit:
|
||||
if len(netMode) > 1 {
|
||||
// Socket path specified, try to use existing VPNKit instance
|
||||
h.VPNKitSock = netMode[1]
|
||||
@ -161,10 +166,10 @@ func runHyperKit(args []string) {
|
||||
// VSOCK port 62373 is used to pass traffic from host->guest
|
||||
h.VSockPorts = append(h.VSockPorts, 62373)
|
||||
}
|
||||
case networkingVMNet:
|
||||
case hyperkitNetworkingVMNet:
|
||||
h.VPNKitSock = ""
|
||||
h.VMNet = true
|
||||
case networkingNone:
|
||||
case hyperkitNetworkingNone:
|
||||
h.VPNKitSock = ""
|
||||
default:
|
||||
log.Fatalf("Invalid networking mode: %s", netMode[0])
|
||||
|
@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -36,10 +38,18 @@ type QemuConfig struct {
|
||||
QemuBinPath string
|
||||
QemuImgPath string
|
||||
PublishedPorts []string
|
||||
TapDevice string
|
||||
NetdevConfig string
|
||||
UUID uuid.UUID
|
||||
}
|
||||
|
||||
const (
|
||||
qemuNetworkingNone string = "none"
|
||||
qemuNetworkingUser = "user"
|
||||
qemuNetworkingTap = "tap"
|
||||
qemuNetworkingBridge = "bridge"
|
||||
qemuNetworkingDefault = qemuNetworkingUser
|
||||
)
|
||||
|
||||
func haveKVM() bool {
|
||||
_, err := os.Stat("/dev/kvm")
|
||||
return !os.IsNotExist(err)
|
||||
@ -58,6 +68,20 @@ func envOverrideBool(env string, b *bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateMAC() net.HardwareAddr {
|
||||
mac := make([]byte, 6)
|
||||
n, err := rand.Read(mac)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("failed to generate random mac address")
|
||||
}
|
||||
if n != 6 {
|
||||
log.WithError(err).Fatal("generated %d bytes for random mac address", n)
|
||||
}
|
||||
mac[0] &^= 0x01 // Clear multicast bit
|
||||
mac[0] |= 0x2 // Set locally administered bit
|
||||
return net.HardwareAddr(mac)
|
||||
}
|
||||
|
||||
func runQemu(args []string) {
|
||||
invoked := filepath.Base(os.Args[0])
|
||||
flags := flag.NewFlagSet("qemu", flag.ExitOnError)
|
||||
@ -67,6 +91,10 @@ func runQemu(args []string) {
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("Options:\n")
|
||||
flags.PrintDefaults()
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("If not running as root note that '-networking bridge,br0' requires a\n")
|
||||
fmt.Printf("setuid network helper and appropriate host configuration, see\n")
|
||||
fmt.Printf("http://wiki.qemu.org/Features/HelperNetworking.\n")
|
||||
}
|
||||
|
||||
// Display flags
|
||||
@ -100,9 +128,11 @@ func runQemu(args []string) {
|
||||
// Generate UUID, so that /sys/class/dmi/id/product_uuid is populated
|
||||
vmUUID := uuid.NewV4()
|
||||
|
||||
// Networking
|
||||
networking := flags.String("networking", qemuNetworkingDefault, "Networking mode. Valid options are 'default', 'user', 'bridge[,name]', tap[,name] and 'none'. 'user' uses QEMUs userspace networking. 'bridge' connects to a preexisting bridge. 'tap' uses a prexisting tap device. 'none' disables networking.`")
|
||||
|
||||
publishFlags := multipleFlag{}
|
||||
flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])")
|
||||
tapDevice := flags.String("tap-device", "", "Tap device to use as eth0 (optional)")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
log.Fatal("Unable to parse args")
|
||||
@ -206,6 +236,40 @@ func runQemu(args []string) {
|
||||
if *isoBoot && isoPath != "" {
|
||||
log.Fatalf("metadata and ISO boot currently cannot coexist")
|
||||
}
|
||||
if *networking == "" || *networking == "default" {
|
||||
dflt := qemuNetworkingDefault
|
||||
networking = &dflt
|
||||
}
|
||||
netMode := strings.SplitN(*networking, ",", 2)
|
||||
|
||||
var netdevConfig string
|
||||
switch netMode[0] {
|
||||
case qemuNetworkingUser:
|
||||
netdevConfig = "user"
|
||||
case qemuNetworkingTap:
|
||||
if len(netMode) != 2 {
|
||||
log.Fatalf("Not enough arugments for %q networking mode", qemuNetworkingTap)
|
||||
}
|
||||
if len(publishFlags) != 0 {
|
||||
log.Fatalf("Port publishing requires %q networking mode", qemuNetworkingUser)
|
||||
}
|
||||
netdevConfig = fmt.Sprintf("tap,ifname=%s,script=no,downscript=no", netMode[1])
|
||||
case qemuNetworkingBridge:
|
||||
if len(netMode) != 2 {
|
||||
log.Fatalf("Not enough arugments for %q networking mode", qemuNetworkingBridge)
|
||||
}
|
||||
if len(publishFlags) != 0 {
|
||||
log.Fatalf("Port publishing requires %q networking mode", qemuNetworkingUser)
|
||||
}
|
||||
netdevConfig = fmt.Sprintf("bridge,br=%s", netMode[1])
|
||||
case qemuNetworkingNone:
|
||||
if len(publishFlags) != 0 {
|
||||
log.Fatalf("Port publishing requires %q networking mode", qemuNetworkingUser)
|
||||
}
|
||||
netdevConfig = ""
|
||||
default:
|
||||
log.Fatalf("Invalid networking mode: %s", netMode[0])
|
||||
}
|
||||
|
||||
config := QemuConfig{
|
||||
Path: path,
|
||||
@ -222,7 +286,7 @@ func runQemu(args []string) {
|
||||
KVM: *enableKVM,
|
||||
Containerized: *qemuContainerized,
|
||||
PublishedPorts: publishFlags,
|
||||
TapDevice: *tapDevice,
|
||||
NetdevConfig: netdevConfig,
|
||||
UUID: vmUUID,
|
||||
}
|
||||
|
||||
@ -448,19 +512,16 @@ func buildQemuCmdline(config QemuConfig) (QemuConfig, []string) {
|
||||
}
|
||||
}
|
||||
|
||||
if config.PublishedPorts != nil && len(config.PublishedPorts) > 0 {
|
||||
if config.NetdevConfig == "" {
|
||||
qemuArgs = append(qemuArgs, "-net", "none")
|
||||
} else {
|
||||
mac := generateMAC()
|
||||
qemuArgs = append(qemuArgs, "-net", "nic,model=virtio,macaddr="+mac.String())
|
||||
forwardings, err := buildQemuForwardings(config.PublishedPorts, config.Containerized)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
qemuArgs = append(qemuArgs, "-net", forwardings)
|
||||
qemuArgs = append(qemuArgs, "-net", "nic")
|
||||
}
|
||||
|
||||
if config.TapDevice != "" {
|
||||
qemuArgs = append(qemuArgs, "-net", "nic,model=virtio")
|
||||
tapArg := fmt.Sprintf("tap,ifname=%s,script=no,downscript=no", config.TapDevice)
|
||||
qemuArgs = append(qemuArgs, "-net", tapArg)
|
||||
qemuArgs = append(qemuArgs, "-net", config.NetdevConfig+forwardings)
|
||||
}
|
||||
|
||||
if config.GUI != true {
|
||||
@ -554,7 +615,10 @@ func splitPublish(publish string) (publishedPorts, error) {
|
||||
}
|
||||
|
||||
func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string, error) {
|
||||
forwardings := "user"
|
||||
if len(publishFlags) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
var forwardings string
|
||||
for _, publish := range publishFlags {
|
||||
p, err := splitPublish(publish)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user