blob: 2466e05d627815670a23d17f8185620277e88a9c [file] [log] [blame]
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package network
18
19import (
20 "context"
21 "fmt"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020022 "net"
23 "os"
24
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020025 "github.com/vishvananda/netlink"
26 "go.uber.org/zap"
27 "golang.org/x/sys/unix"
Lorenz Brun52f7f292020-06-24 16:42:02 +020028
29 "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
30 "git.monogon.dev/source/nexantic.git/core/internal/network/dhcp"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020031)
32
33const (
34 resolvConfPath = "/etc/resolv.conf"
35 resolvConfSwapPath = "/etc/resolv.conf.new"
36)
37
38type Service struct {
Serge Bazanskicdb8c782020-02-17 12:34:02 +010039 config Config
Lorenz Brun52f7f292020-06-24 16:42:02 +020040 dhcp *dhcp.Client
Serge Bazanskicdb8c782020-02-17 12:34:02 +010041
Serge Bazanskib1b742f2020-03-24 13:58:19 +010042 logger *zap.Logger
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020043}
44
45type Config struct {
46}
47
Serge Bazanskib1b742f2020-03-24 13:58:19 +010048func New(config Config) *Service {
49 return &Service{
Serge Bazanskicdb8c782020-02-17 12:34:02 +010050 config: config,
Lorenz Brun52f7f292020-06-24 16:42:02 +020051 dhcp: dhcp.New(),
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020052 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020053}
54
55func setResolvconf(nameservers []net.IP, searchDomains []string) error {
Leopold Schabel68c58752019-11-14 21:00:59 +010056 _ = os.Mkdir("/etc", 0755)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020057 newResolvConf, err := os.Create(resolvConfSwapPath)
58 if err != nil {
59 return err
60 }
61 defer newResolvConf.Close()
62 defer os.Remove(resolvConfSwapPath)
63 for _, ns := range nameservers {
64 if _, err := newResolvConf.WriteString(fmt.Sprintf("nameserver %v\n", ns)); err != nil {
65 return err
66 }
67 }
68 for _, searchDomain := range searchDomains {
69 if _, err := newResolvConf.WriteString(fmt.Sprintf("search %v", searchDomain)); err != nil {
70 return err
71 }
72 }
73 newResolvConf.Close()
74 // Atomically swap in new config
75 return unix.Rename(resolvConfSwapPath, resolvConfPath)
76}
77
Serge Bazanski1a5a6672020-02-18 10:09:43 +010078func (s *Service) addNetworkRoutes(link netlink.Link, addr net.IPNet, gw net.IP) error {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020079 if err := netlink.AddrReplace(link, &netlink.Addr{IPNet: &addr}); err != nil {
Lorenz Brun52f7f292020-06-24 16:42:02 +020080 return fmt.Errorf("failed to add DHCP address to network interface \"%v\": %w", link.Attrs().Name, err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020081 }
Serge Bazanski1a5a6672020-02-18 10:09:43 +010082
83 if gw.IsUnspecified() {
Serge Bazanskib1b742f2020-03-24 13:58:19 +010084 s.logger.Info("No default route set, only local network will be reachable", zap.String("local", addr.String()))
Serge Bazanski1a5a6672020-02-18 10:09:43 +010085 return nil
86 }
87
88 route := &netlink.Route{
Lorenz Brun45333b62019-11-11 15:26:27 +010089 Dst: &net.IPNet{IP: net.IPv4(0, 0, 0, 0), Mask: net.IPv4Mask(0, 0, 0, 0)},
90 Gw: gw,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020091 Scope: netlink.SCOPE_UNIVERSE,
Serge Bazanski1a5a6672020-02-18 10:09:43 +010092 }
93 if err := netlink.RouteAdd(route); err != nil {
94 return fmt.Errorf("could not add default route: netlink.RouteAdd(%+v): %v", route, err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020095 }
96 return nil
97}
98
Serge Bazanskib1b742f2020-03-24 13:58:19 +010099func (s *Service) useInterface(ctx context.Context, iface netlink.Link) error {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200100 err := supervisor.Run(ctx, "dhcp", s.dhcp.Run(iface))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200101 if err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100102 return err
103 }
Lorenz Brun52f7f292020-06-24 16:42:02 +0200104 status, err := s.dhcp.Status(ctx, true)
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100105 if err != nil {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200106 return fmt.Errorf("could not get DHCP Status: %w", err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200107 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100108
Lorenz Brun52f7f292020-06-24 16:42:02 +0200109 if err := setResolvconf(status.DNS, []string{}); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100110 s.logger.Warn("failed to set resolvconf", zap.Error(err))
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100111 }
112
Lorenz Brun52f7f292020-06-24 16:42:02 +0200113 if err := s.addNetworkRoutes(iface, status.Address, status.Gateway); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100114 s.logger.Warn("failed to add routes", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200115 }
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100116
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200117 return nil
118}
119
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100120// GetIP returns the current IP (and optionally waits for one to be assigned)
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100121func (s *Service) GetIP(ctx context.Context, wait bool) (*net.IP, error) {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200122 status, err := s.dhcp.Status(ctx, wait)
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100123 if err != nil {
124 return nil, err
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100125 }
Lorenz Brun52f7f292020-06-24 16:42:02 +0200126 return &status.Address.IP, nil
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100127}
128
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100129func (s *Service) Run(ctx context.Context) error {
130 s.logger = supervisor.Logger(ctx)
131 s.logger.Info("Starting network service")
132
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200133 links, err := netlink.LinkList()
134 if err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100135 s.logger.Fatal("Failed to list network links", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200136 }
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100137
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200138 var ethernetLinks []netlink.Link
139 for _, link := range links {
140 attrs := link.Attrs()
141 if link.Type() == "device" && len(attrs.HardwareAddr) > 0 {
142 if len(attrs.HardwareAddr) == 6 { // Ethernet
143 if attrs.Flags&net.FlagUp != net.FlagUp {
144 netlink.LinkSetUp(link) // Attempt to take up all ethernet links
145 }
146 ethernetLinks = append(ethernetLinks, link)
147 } else {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100148 s.logger.Info("Ignoring non-Ethernet interface", zap.String("interface", attrs.Name))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200149 }
Lorenz Brun45333b62019-11-11 15:26:27 +0100150 } else if link.Attrs().Name == "lo" {
151 if err := netlink.LinkSetUp(link); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100152 s.logger.Error("Failed to take up loopback interface", zap.Error(err))
Lorenz Brun45333b62019-11-11 15:26:27 +0100153 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200154 }
155 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100156 if len(ethernetLinks) != 1 {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100157 s.logger.Warn("Network service needs exactly one link, bailing")
158 } else {
159 link := ethernetLinks[0]
160 if err := s.useInterface(ctx, link); err != nil {
161 return fmt.Errorf("failed to bring up link %s: %w", link.Attrs().Name, err)
162 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200163 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100164
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100165 supervisor.Signal(ctx, supervisor.SignalHealthy)
166 supervisor.Signal(ctx, supervisor.SignalDone)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200167 return nil
168}