Andrey Smirnov fae5e6915d chore: rework firecracker code around upstream Go SDK + PRs
This removes use of private fork with custom `ip=` kernel argument
handling and switches fully to upstream version of it.

Firecracker Go SDK version is `master` + following PRs:

* https://github.com/firecracker-microvm/firecracker-go-sdk/pull/167
* https://github.com/firecracker-microvm/firecracker-go-sdk/pull/177
* https://github.com/firecracker-microvm/firecracker-go-sdk/pull/178

MTU handling support was implemented as well.

Changes:

* hostname to each node is passed via `talos.hostname=` kernel arg
* IP configuration is generated by SDK from CNI result
* fixed bugs with wrong netmask
* nameservers & MTU is passed via Talos config

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-01-29 02:35:15 +03:00

181 lines
4.4 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package firecracker
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"net"
"strconv"
"text/template"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/google/uuid"
"github.com/jsimonetti/rtnetlink"
"github.com/talos-systems/talos/internal/pkg/provision"
talosnet "github.com/talos-systems/talos/pkg/net"
)
func (p *provisioner) createNetwork(ctx context.Context, state *state, network provision.NetworkRequest) error {
// build bridge interface name by taking part of checksum of the network name
// so that interface name is defined by network name, and different networks have
// different bridge interfaces
networkNameHash := sha256.Sum256([]byte(network.Name))
state.BridgeName = fmt.Sprintf("%s%s", "talos", hex.EncodeToString(networkNameHash[:])[:8])
// bring up the bridge interface for the first time to get gateway IP assigned
t := template.Must(template.New("bridge").Parse(bridgeTemplate))
var buf bytes.Buffer
err := t.Execute(&buf, struct {
NetworkName string
InterfaceName string
MTU string
}{
NetworkName: network.Name,
InterfaceName: state.BridgeName,
MTU: strconv.Itoa(network.MTU),
})
if err != nil {
return fmt.Errorf("error templating bridge CNI config: %w", err)
}
bridgeConfig, err := libcni.ConfFromBytes(buf.Bytes())
if err != nil {
return fmt.Errorf("error parsing bridge CNI config: %w", err)
}
cniConfig := libcni.NewCNIConfigWithCacheDir(network.CNI.BinPath, network.CNI.CacheDir, nil)
ns, err := testutils.NewNS()
if err != nil {
return err
}
defer func() {
ns.Close() //nolint: errcheck
testutils.UnmountNS(ns) //nolint: errcheck
}()
// pick a fake address to use for provisioning an interface
fakeIP, err := talosnet.NthIPInNetwork(&network.CIDR, 2)
if err != nil {
return err
}
ones, _ := network.CIDR.Mask.Size()
containerID := uuid.New().String()
runtimeConf := libcni.RuntimeConf{
ContainerID: containerID,
NetNS: ns.Path(),
IfName: "veth0",
Args: [][2]string{
{"IP", fmt.Sprintf("%s/%d", fakeIP, ones)},
{"GATEWAY", network.GatewayAddr.String()},
},
}
_, err = cniConfig.AddNetwork(ctx, bridgeConfig, &runtimeConf)
if err != nil {
return fmt.Errorf("error provisioning bridge CNI network: %w", err)
}
err = cniConfig.DelNetwork(ctx, bridgeConfig, &runtimeConf)
if err != nil {
return fmt.Errorf("error deleting bridge CNI network: %w", err)
}
// prepare an actual network config to be used by the VMs
t = template.Must(template.New("network").Parse(networkTemplate))
buf.Reset()
err = t.Execute(&buf, struct {
NetworkName string
InterfaceName string
MTU string
}{
NetworkName: network.Name,
InterfaceName: state.BridgeName,
MTU: strconv.Itoa(network.MTU),
})
if err != nil {
return fmt.Errorf("error templating VM CNI config: %w", err)
}
if state.vmCNIConfig, err = libcni.ConfListFromBytes(buf.Bytes()); err != nil {
return fmt.Errorf("error parsing VM CNI config: %w", err)
}
return nil
}
func (p *provisioner) destroyNetwork(state *state) error {
// destroy bridge interface by name to clean up
iface, err := net.InterfaceByName(state.BridgeName)
if err != nil {
return fmt.Errorf("error looking up bridge interface %q: %w", state.BridgeName, err)
}
rtconn, err := rtnetlink.Dial(nil)
if err != nil {
return fmt.Errorf("error dialing rnetlink: %w", err)
}
if err = rtconn.Link.Delete(uint32(iface.Index)); err != nil {
return fmt.Errorf("error deleting bridge interface: %w", err)
}
return nil
}
const bridgeTemplate = `
{
"name": "{{ .NetworkName }}",
"cniVersion": "0.4.0",
"type": "bridge",
"bridge": "{{ .InterfaceName }}",
"ipMasq": true,
"isGateway": true,
"isDefaultGateway": true,
"ipam": {
"type": "static"
},
"mtu": {{ .MTU }}
}
`
const networkTemplate = `
{
"name": "{{ .NetworkName }}",
"cniVersion": "0.4.0",
"plugins": [
{
"type": "bridge",
"bridge": "{{ .InterfaceName }}",
"ipMasq": true,
"isGateway": true,
"isDefaultGateway": true,
"ipam": {
"type": "static"
},
"mtu": {{ .MTU }}
},
{
"type": "firewall"
},
{
"type": "tc-redirect-tap"
}
]
}
`