Merge pull request #2490 from MagnusS/update-vpnkit

Update to latest Hyperkit API
This commit is contained in:
Rolf Neugebauer 2017-09-07 17:08:53 +01:00 committed by GitHub
commit c793ea973c
4 changed files with 132 additions and 38 deletions

View File

@ -41,11 +41,13 @@ func runHyperKit(args []string) {
var disks Disks
flags.Var(&disks, "disk", "Disk config. [file=]path[,size=1G]")
data := flags.String("data", "", "Metadata to pass to VM (either a path to a file or a string)")
ipStr := flags.String("ip", "", "IP address for the VM")
ipStr := flags.String("ip", "", "Preferred IPv4 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", 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.`")
vpnKitUUID := flags.String("vpnkit-uuid", "", "Optional UUID used to identify the VPNKit connection. Overrides 'uuid.vpnkit' in the state directory.")
// Boot type; we try to determine automatically
uefiBoot := flags.Bool("uefi", false, "Use UEFI boot")
isoBoot := flags.Bool("iso", false, "Boot image is an ISO")
@ -149,17 +151,22 @@ func runHyperKit(args []string) {
isoPaths = append(isoPaths, isoPath)
}
vpnKitKey := ""
if *ipStr != "" {
// If an IP address was requested construct a "special" UUID
// for the VM.
if ip := net.ParseIP(*ipStr); len(ip) > 0 {
uuid := make([]byte, 16)
uuid[12] = ip.To4()[0]
uuid[13] = ip.To4()[1]
uuid[14] = ip.To4()[2]
uuid[15] = ip.To4()[3]
vpnKitKey = fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
// Create UUID for VPNKit or reuse an existing one from state dir. IP addresses are
// assigned to the UUID, so to get the same IP we have to store the initial UUID. If
// has specified a VPNKit UUID the file is ignored.
if *vpnKitUUID == "" {
vpnKitUUIDFile := filepath.Join(*state, "uuid.vpnkit")
if _, err := os.Stat(vpnKitUUIDFile); os.IsNotExist(err) {
*vpnKitUUID = uuid.NewV4().String()
if err := ioutil.WriteFile(vpnKitUUIDFile, []byte(*vpnKitUUID), 0600); err != nil {
log.Fatalf("Unable to write to %s: %v", vpnKitUUIDFile, err)
}
} else {
uuid, err := ioutil.ReadFile(vpnKitUUIDFile)
if err != nil {
log.Fatalf("Unable to read VPNKit UUID from %s: %v", vpnKitUUIDFile, err)
}
*vpnKitUUID = string(uuid)
}
}
@ -252,13 +259,21 @@ func runHyperKit(args []string) {
} else {
h.Bootrom = *fw
}
h.VPNKitKey = vpnKitKey
h.UUID = vmUUID
h.ISOImages = isoPaths
h.VSock = true
h.CPUs = *cpus
h.Memory = *mem
h.VPNKitUUID = *vpnKitUUID
if *ipStr != "" {
if ip := net.ParseIP(*ipStr); len(ip) > 0 && ip.To4() != nil {
h.VPNKitPreferredIPv4 = ip.String()
} else {
log.Fatalf("Unable to parse IPv4 address: %v", *ipStr)
}
}
err = h.Run(string(cmdline))
if err != nil {
log.Fatalf("Cannot run hyperkit: %v", err)

View File

@ -11,7 +11,7 @@ github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751
github.com/gophercloud/gophercloud 2804b72cf099b41d2e25c8afcca786f9f962ddee
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062
github.com/moby/hyperkit a82b409a87f12fa3306813410c37f4eed270efac
github.com/moby/hyperkit 3e31617ae866c93925e2b3bc5d8006b60985e920
github.com/packethost/packngo 131798f2804a1b3e895ca98047d56f0d7e094e2a
github.com/radu-matei/azure-sdk-for-go 3b12823551999669c9a325a32472508e0af7978e
github.com/radu-matei/azure-vhd-utils e52754d5569d2a643a7775f72ff2a6cf524f4c25

View File

@ -28,6 +28,7 @@ import (
"io"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"os/user"
@ -85,8 +86,10 @@ type HyperKit struct {
StateDir string `json:"state_dir"`
// VPNKitSock is the location of the VPNKit socket used for networking.
VPNKitSock string `json:"vpnkit_sock"`
// VPNKitKey is a string containing a UUID, it can be used in conjunction with VPNKit to get consistent IP address.
VPNKitKey string `json:"vpnkit_key"`
// VPNKitUUID is a string containing a UUID, it can be used in conjunction with VPNKit to get consistent IP address.
VPNKitUUID string `json:"vpnkit_uuid"`
// VPNKitPreferredIPv4 is a string containing an IPv4 address, it can be used to request a specific IP for a UUID from VPNKit.
VPNKitPreferredIPv4 string `json:"vpnkit_preferred_ipv4"`
// UUID is a string containing a UUID, it sets BIOS DMI UUID for the VM (as found in /sys/class/dmi/id/product_uuid on Linux).
UUID string `json:"uuid"`
// Disks contains disk images to use/create.
@ -249,6 +252,11 @@ func (h *HyperKit) execute(cmdline string) error {
return fmt.Errorf("Bootrom %s does not exist", h.Bootrom)
}
}
if h.VPNKitPreferredIPv4 != "" {
if ip := net.ParseIP(h.VPNKitPreferredIPv4); ip == nil {
return fmt.Errorf("Invalid VPNKit IP: %s", h.VPNKitPreferredIPv4)
}
}
// Create files
if h.StateDir != "" {
@ -420,11 +428,15 @@ func (h *HyperKit) buildArgs(cmdline string) {
nextSlot := 1
if h.VPNKitSock != "" {
if h.VPNKitKey == "" {
a = append(a, "-s", fmt.Sprintf("%d:0,virtio-vpnkit,path=%s", nextSlot, h.VPNKitSock))
} else {
a = append(a, "-s", fmt.Sprintf("%d:0,virtio-vpnkit,path=%s,uuid=%s", nextSlot, h.VPNKitSock, h.VPNKitKey))
var uuid string
if h.VPNKitUUID != "" {
uuid = fmt.Sprintf(",uuid=%s", h.VPNKitUUID)
}
var preferredIPv4 string
if h.VPNKitPreferredIPv4 != "" {
preferredIPv4 = fmt.Sprintf(",preferred_ipv4=%s", h.VPNKitPreferredIPv4)
}
a = append(a, "-s", fmt.Sprintf("%d:0,virtio-vpnkit,path=%s%s%s", nextSlot, h.VPNKitSock, uuid, preferredIPv4))
nextSlot++
}

View File

@ -77,6 +77,8 @@
#include <xhyve/pci_emul.h>
#include <xhyve/mevent.h>
#include <xhyve/virtio.h>
#include <arpa/inet.h>
#include <libkern/OSByteOrder.h>
#define WPRINTF(format, ...) printf(format, __VA_ARGS__)
@ -93,13 +95,18 @@ struct msg_init {
} __packed;
#define CMD_ETHERNET 1
struct msg_common {
uint8_t command;
#define CMD_PREFERRED_IPV4 8
#define RESP_VIF 1
#define RESP_DISCONNECT 2
struct cmd_ethernet {
char uuid[36];
} __packed;
struct msg_ethernet {
uint8_t command; /* CMD_ETHERNET */
struct cmd_preferred_ipv4 {
char uuid[36];
in_addr_t ip;
} __packed;
struct vif_info {
@ -108,6 +115,26 @@ struct vif_info {
uint8_t mac[6];
} __packed;
struct disconnect_reason {
uint8_t len;
char msg[256];
} __packed;
struct msg_command {
uint8_t command;
union {
struct cmd_ethernet ethernet;
struct cmd_preferred_ipv4 preferred_ipv4;
};
} __packed;
struct msg_response {
uint8_t response_type;
union {
struct disconnect_reason disconnect;
struct vif_info vif;
};
} __packed;
/*
* Host capabilities. Note that we only offer a few of these.
@ -286,11 +313,11 @@ err:
/*
* wire protocol
*/
static int vpnkit_connect(int fd, const char uuid[36], struct vif_info *vif)
static int vpnkit_connect(int fd, const char uuid[36], struct vif_info *vif, in_addr_t preferred_ipv4)
{
struct msg_init init_msg = {
.magic = { 'V', 'M', 'N', '3', 'T' },
.version = 1U,
.version = 22U,
};
/* msg.commit is not NULL terminated */
@ -324,21 +351,49 @@ static int vpnkit_connect(int fd, const char uuid[36], struct vif_info *vif)
init_reply.magic[4],
init_reply.version, (int)sizeof(init_reply.commit), init_reply.commit);
struct msg_ethernet cmd_ethernet = {
.command = CMD_ETHERNET,
};
memcpy(cmd_ethernet.uuid, uuid, sizeof(cmd_ethernet.uuid));
if (init_reply.version != init_msg.version) {
fprintf(stderr, "virtio-net-vpnkit: protocol version mismatch: version %d requested, got version %d.\n",
init_msg.version,
init_reply.version);
}
if (really_write(fd, (uint8_t*)&cmd_ethernet, sizeof(cmd_ethernet)) < 0) {
fprintf(stderr, "virtio-net-vpnkit: failed to write ethernet cmd\n");
struct msg_command cmd;
memset(&cmd, 0, sizeof(cmd));
if (preferred_ipv4 != 0) {
cmd.command = CMD_PREFERRED_IPV4;
memcpy(cmd.preferred_ipv4.uuid, uuid, sizeof(cmd.preferred_ipv4.uuid));
/* VPNKit uses LE, so swap the IP from network byte order */
cmd.preferred_ipv4.ip = OSSwapInt32(preferred_ipv4);
} else {
/* No preferred IPv4 address, falling back to requesting a dynamic address */
cmd.command = CMD_ETHERNET;
memcpy(cmd.ethernet.uuid, uuid, sizeof(cmd.ethernet.uuid));
}
if (really_write(fd, (uint8_t*)&cmd, sizeof(cmd)) < 0) {
fprintf(stderr, "virtio-net-vpnkit: failed to write command\n");
return -1;
}
if (really_read(fd, (uint8_t*)vif, sizeof(*vif)) < 0) {
fprintf(stderr, "virtio-net-vpnkit: failed to read vif info\n");
struct msg_response reply;
if (really_read(fd, (uint8_t*)&reply, sizeof(reply)) < 0) {
fprintf(stderr, "virtio-net-vpnkit: failed to read response message\n");
return -1;
}
switch (reply.response_type) {
case RESP_VIF:
memcpy((uint8_t*)vif, (uint8_t*)&reply.vif, sizeof(*vif));
break;
case RESP_DISCONNECT:
fprintf(stderr, "virtio-net-vpnkit: server disconnected: %*s\n", reply.disconnect.len, reply.disconnect.msg);
return -1;
default:
fprintf(stderr, "virtio-net-vpnkit: unknown response from server: %d\n", reply.response_type);
return -1;
}
return 0;
}
@ -362,9 +417,11 @@ vpnkit_create(struct pci_vtnet_softc *sc, const char *opts)
const char *path = "/var/tmp/com.docker.slirp.socket";
char *macfile = NULL;
char *tmp = NULL;
char *ipv4 = NULL;
uuid_t uuid;
char uuid_string[37];
struct sockaddr_un addr;
struct in_addr preferred_ipv4 = { .s_addr = 0 };
int fd;
struct vpnkit_state *state = malloc(sizeof(struct vpnkit_state));
if (!state) abort();
@ -393,11 +450,16 @@ vpnkit_create(struct pci_vtnet_softc *sc, const char *opts)
return 1;
}
memcpy(&uuid_string[0], &tmp[0], 36);
fprintf(stdout, "Interface will have uuid %s\n", tmp);
free(tmp);
tmp = NULL;
} else if (strncmp(opts, "macfile=", 8) == 0) {
macfile = copy_up_to_comma(opts + 8);
} else if (strncmp(opts, "preferred_ipv4=", 15) == 0) {
ipv4 = copy_up_to_comma(opts + 15);
if (inet_aton(ipv4, &preferred_ipv4) == 0) {
fprintf(stderr, "Unable to parse requested IP %s\n", ipv4);
return 1;
}
} else {
fprintf(stderr, "invalid option: %s\r\n", opts);
return 1;
@ -407,6 +469,11 @@ vpnkit_create(struct pci_vtnet_softc *sc, const char *opts)
opts = &next[1];
}
fprintf(stdout, "virtio-net-vpnkit: interface will have uuid %s\n", uuid_string);
if (ipv4 != NULL) {
fprintf(stdout, "virtio-net-vpnkit: requesting ip %s\n", ipv4);
}
state->vif.max_packet_size = 1500;
sc->state = state;
@ -423,7 +490,7 @@ vpnkit_create(struct pci_vtnet_softc *sc, const char *opts)
goto err;
}
if (vpnkit_connect(fd, uuid_string, &state->vif) == 0)
if (vpnkit_connect(fd, uuid_string, &state->vif, preferred_ipv4.s_addr) == 0)
/* success */
break;
@ -441,8 +508,8 @@ err:
state->fd = fd;
struct vif_info *info = &state->vif;
fprintf(stdout, "Connection established with MAC=%02x:%02x:%02x:%02x:%02x:%02x and MTU %d\n",
info->mac[0], info->mac[1], info->mac[2], info->mac[3], info->mac[4], info->mac[5],
fprintf(stdout, "virtio-net-vpnkit: Connection established with MAC=%02x:%02x:%02x:%02x:%02x:%02x and MTU %d\n",
info->mac[0], info->mac[1], info->mac[2], info->mac[3], info->mac[4], info->mac[5],
(int)info->mtu);
if (macfile) {