Andrey Smirnov 01981eb1c6
feat: update Talos to 1.6.0
Also update CAPI to 1.6.0.

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2023-12-15 17:59:33 +04:00

268 lines
6.2 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 main
import (
"context"
"fmt"
"log"
"net"
"os"
"strings"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/jsimonetti/rtnetlink"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/go-retry/retry"
"golang.org/x/sys/unix"
"github.com/siderolabs/sidero/app/sidero-controller-manager/pkg/constants"
)
func setupNetworking() error {
var bootMACAddress string
if found := procfs.ProcCmdline().Get(constants.AgentMACArg).First(); found != nil {
bootMACAddress = *found
} else {
return fmt.Errorf("no MAC found")
}
link, err := waitForLink(bootMACAddress)
if err != nil {
return err
}
if err = brinkLinkUp(link.Index); err != nil {
return err
}
return runDHCP(link)
}
func waitForLink(hwaddr string) (net.Interface, error) {
log.Printf("waiting for network link with MAC %q...", hwaddr)
var foundLink net.Interface
err := retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry(func() error {
links, err := net.Interfaces()
if err != nil {
return err
}
for _, link := range links {
if link.HardwareAddr.String() == hwaddr {
foundLink = link
return nil
}
}
return retry.ExpectedErrorf("link with MAC %q not found", hwaddr)
})
return foundLink, err
}
func brinkLinkUp(linkIndex int) error {
log.Printf("bringing link up...")
conn, err := rtnetlink.Dial(nil)
if err != nil {
return fmt.Errorf("error dialing rtnetlink socket: %w", err)
}
defer conn.Close() //nolint:errcheck
if err := conn.Link.Set(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: unix.ARPHRD_ETHER,
Index: uint32(linkIndex),
Flags: unix.IFF_UP,
Change: unix.IFF_UP,
}); err != nil {
return fmt.Errorf("error setting link up: %w", err)
}
return nil
}
func runDHCP(link net.Interface) error {
log.Printf("running DHCP on %q...", link.Name)
var lease *nclient4.Lease
if err := retry.Constant(5*time.Minute,
retry.WithUnits(10*time.Second),
retry.WithAttemptTimeout(30*time.Second),
).RetryWithContext(context.Background(), func(ctx context.Context) error {
var err error
lease, err = acquireLease(ctx, link.Name)
if err != nil {
return retry.ExpectedError(err)
}
return nil
}); err != nil {
return err
}
log.Printf("got DHCP lease: %s", lease.ACK.Summary())
return configureNetworking(link.Index, lease)
}
func acquireLease(ctx context.Context, linkName string) (*nclient4.Lease, error) {
opts := []dhcpv4.OptionCode{
dhcpv4.OptionClasslessStaticRoute,
dhcpv4.OptionDomainNameServer,
dhcpv4.OptionInterfaceMTU,
dhcpv4.OptionHostName,
}
mods := []dhcpv4.Modifier{dhcpv4.WithRequestedOptions(opts...)}
cli, err := nclient4.New(linkName)
if err != nil {
return nil, fmt.Errorf("error creating DHCP client: %w", err)
}
//nolint:errcheck
defer cli.Close()
lease, err := cli.Request(ctx, mods...)
if err != nil {
return nil, fmt.Errorf("error requesting DHCP lease: %w", err)
}
return lease, nil
}
func configureNetworking(linkIndex int, lease *nclient4.Lease) error {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return fmt.Errorf("error dialing rtnetlink socket: %w", err)
}
defer conn.Close() //nolint:errcheck
if err := conn.Link.Set(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: unix.ARPHRD_ETHER,
Index: uint32(linkIndex),
Flags: unix.IFF_UP,
Change: unix.IFF_UP,
}); err != nil {
return fmt.Errorf("error setting link up: %w", err)
}
prefixLen, _ := lease.ACK.SubnetMask().Size()
log.Printf("assigning address %s/%d", lease.ACK.YourIPAddr, prefixLen)
if err := conn.Address.New(&rtnetlink.AddressMessage{
Family: unix.AF_INET,
PrefixLength: uint8(prefixLen),
Scope: unix.RT_SCOPE_UNIVERSE,
Index: uint32(linkIndex),
Attributes: &rtnetlink.AddressAttributes{
Address: lease.ACK.YourIPAddr,
Local: lease.ACK.YourIPAddr,
},
}); err != nil {
return fmt.Errorf("error adding address: %w", err)
}
mtu, err := dhcpv4.GetUint16(dhcpv4.OptionInterfaceMTU, lease.ACK.Options)
if err == nil {
log.Printf("setting MTU to %d", mtu)
if err := conn.Link.Set(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: unix.ARPHRD_ETHER,
Index: uint32(linkIndex),
Attributes: &rtnetlink.LinkAttributes{
MTU: uint32(mtu),
},
}); err != nil {
return fmt.Errorf("error setting MTU: %w", err)
}
}
addRoute := func(destination *net.IPNet, gateway net.IP) error {
log.Printf("adding route %s via %s", destination, gateway)
var dstLength int
if destination != nil {
dstLength, _ = destination.Mask.Size()
}
var dstAddr net.IP
if destination != nil {
dstAddr = destination.IP
}
if err := conn.Route.Add(&rtnetlink.RouteMessage{
Family: unix.AF_INET,
DstLength: uint8(dstLength),
Scope: unix.RT_SCOPE_UNIVERSE,
Table: unix.RT_TABLE_MAIN,
Protocol: unix.RTPROT_BOOT,
Type: unix.RTN_UNICAST,
Attributes: rtnetlink.RouteAttributes{
Dst: dstAddr,
Gateway: gateway,
OutIface: uint32(linkIndex),
},
}); err != nil {
return fmt.Errorf("error adding route: %w", err)
}
return nil
}
if len(lease.ACK.ClasslessStaticRoute()) > 0 {
for _, route := range lease.ACK.ClasslessStaticRoute() {
if err := addRoute(route.Dest, route.Router); err != nil {
return err
}
}
} else {
for _, router := range lease.ACK.Router() {
if err := addRoute(nil, router); err != nil {
return err
}
}
}
if lease.ACK.HostName() != "" {
log.Printf("setting hostname to %q", lease.ACK.HostName())
unix.Sethostname([]byte(lease.ACK.HostName())) //nolint:errcheck
}
if len(lease.ACK.DNS()) > 0 {
log.Printf("setting DNS servers to %s", lease.ACK.DNS())
contents := strings.Join(xslices.Map(lease.ACK.DNS(),
func(ns net.IP) string {
return fmt.Sprintf("nameserver %s\n", ns)
}), "")
if err := os.WriteFile("/etc/resolv.conf", []byte(contents), 0o777); err != nil {
return fmt.Errorf("error writing /etc/resolv.conf: %w", err)
}
}
return nil
}