mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-23 21:41:11 +02:00
Gives a better abstraction on rtnetlink interaction Signed-off-by: Brad Beam <brad.beam@talos-systems.com>
165 lines
4.1 KiB
Go
165 lines
4.1 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 address
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
|
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
|
"github.com/talos-systems/talos/internal/pkg/kernel"
|
|
"github.com/talos-systems/talos/pkg/constants"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// DHCP implements the Addressing interface
|
|
type DHCP struct {
|
|
Ack *dhcpv4.DHCPv4
|
|
NetIf *net.Interface
|
|
}
|
|
|
|
// Name returns back the name of the address method.
|
|
func (d *DHCP) Name() string {
|
|
return "dhcp"
|
|
}
|
|
|
|
// Link returns the underlying net.Interface that this address
|
|
// method is configured for
|
|
func (d *DHCP) Link() *net.Interface {
|
|
return d.NetIf
|
|
}
|
|
|
|
// Discover handles the DHCP client exchange stores the DHCP Ack.
|
|
func (d *DHCP) Discover(ctx context.Context) error {
|
|
// TODO do something with context
|
|
ack, err := d.discover()
|
|
d.Ack = ack
|
|
return err
|
|
}
|
|
|
|
// Address returns back the IP address from the received DHCP offer.
|
|
func (d *DHCP) Address() *net.IPNet {
|
|
return &net.IPNet{
|
|
IP: d.Ack.YourIPAddr,
|
|
Mask: d.Mask(),
|
|
}
|
|
}
|
|
|
|
// Mask returns the netmask from the DHCP offer.
|
|
func (d *DHCP) Mask() net.IPMask {
|
|
return d.Ack.SubnetMask()
|
|
}
|
|
|
|
// MTU returs the MTU size from the DHCP offer.
|
|
func (d *DHCP) MTU() uint32 {
|
|
// TODO do we need to implement dhcpv4.GetUint32 upstream?
|
|
mtu, err := dhcpv4.GetUint16(dhcpv4.OptionInterfaceMTU, d.Ack.Options)
|
|
if err != nil {
|
|
return uint32(d.NetIf.MTU)
|
|
}
|
|
return uint32(mtu)
|
|
}
|
|
|
|
// TTL denotes how long a DHCP offer is valid for.
|
|
func (d *DHCP) TTL() time.Duration {
|
|
if d.Ack == nil {
|
|
return 0
|
|
}
|
|
return d.Ack.IPAddressLeaseTime(time.Minute * 30)
|
|
}
|
|
|
|
// Family qualifies the address as ipv4 or ipv6
|
|
func (d *DHCP) Family() int {
|
|
if d.Ack.YourIPAddr.To4() != nil {
|
|
return unix.AF_INET
|
|
}
|
|
return unix.AF_INET6
|
|
}
|
|
|
|
// Scope sets the address scope
|
|
func (d *DHCP) Scope() uint8 {
|
|
return unix.RT_SCOPE_UNIVERSE
|
|
}
|
|
|
|
// Routes aggregates all Routers and ClasslessStaticRoutes retrieved from
|
|
// the DHCP offer.
|
|
// rfc3442:
|
|
// If the DHCP server returns both a Classless Static Routes option and
|
|
// a Router option, the DHCP client MUST ignore the Router option.
|
|
func (d *DHCP) Routes() (routes []*Route) {
|
|
if len(d.Ack.ClasslessStaticRoute()) > 0 {
|
|
return d.Ack.ClasslessStaticRoute()
|
|
}
|
|
|
|
defRoute := &net.IPNet{
|
|
IP: net.IPv4zero,
|
|
Mask: net.IPv4Mask(0, 0, 0, 0),
|
|
}
|
|
|
|
for _, router := range d.Ack.Router() {
|
|
routes = append(routes, &Route{Router: router, Dest: defRoute})
|
|
}
|
|
|
|
return routes
|
|
}
|
|
|
|
// Resolvers returns the DNS resolvers from the DHCP offer.
|
|
func (d *DHCP) Resolvers() []net.IP {
|
|
return d.Ack.DNS()
|
|
}
|
|
|
|
// Hostname returns the hostname from the DHCP offer.
|
|
func (d *DHCP) Hostname() string {
|
|
// Truncate the returned hostname to only return
|
|
// the actual host entry
|
|
return strings.Split(d.Ack.HostName(), ".")[0]
|
|
}
|
|
|
|
// discover handles the actual DHCP conversation.
|
|
func (d *DHCP) discover() (*dhcpv4.DHCPv4, error) {
|
|
opts := []dhcpv4.OptionCode{
|
|
dhcpv4.OptionClasslessStaticRoute,
|
|
dhcpv4.OptionDomainNameServer,
|
|
dhcpv4.OptionDNSDomainSearchList,
|
|
dhcpv4.OptionHostName,
|
|
// TODO: handle these options
|
|
dhcpv4.OptionNTPServers,
|
|
dhcpv4.OptionDomainName,
|
|
}
|
|
|
|
// <3 azure
|
|
// When including dhcp.OptionInterfaceMTU we don't get a dhcp offer back on azure.
|
|
// So we'll need to explicitly exclude adding this option for azure.
|
|
if p := kernel.ProcCmdline().Get(constants.KernelParamPlatform).First(); p != nil {
|
|
if *p != "azure" {
|
|
opts = append(opts, dhcpv4.OptionInterfaceMTU)
|
|
}
|
|
}
|
|
|
|
mods := []dhcpv4.Modifier{dhcpv4.WithRequestedOptions(opts...)}
|
|
|
|
// TODO expose this ( nclient4.WithDebugLogger() ) with some
|
|
// debug logging option
|
|
cli, err := nclient4.New(d.NetIf.Name, nclient4.WithTimeout(2*time.Second))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// nolint: errcheck
|
|
defer cli.Close()
|
|
|
|
_, ack, err := cli.Request(context.Background(), mods...)
|
|
if err != nil {
|
|
// TODO: Make this a well defined error so we can make it not fatal
|
|
log.Println("failed dhcp request")
|
|
return nil, err
|
|
}
|
|
|
|
return ack, err
|
|
}
|