blob: ac9ce46cb30d623a0ccbf40583b02e75e1e56738 [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"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020022 "io/ioutil"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020023 "net"
24 "os"
25
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020026 "github.com/vishvananda/netlink"
27 "go.uber.org/zap"
28 "golang.org/x/sys/unix"
Lorenz Brun52f7f292020-06-24 16:42:02 +020029
30 "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
31 "git.monogon.dev/source/nexantic.git/core/internal/network/dhcp"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020032)
33
34const (
35 resolvConfPath = "/etc/resolv.conf"
36 resolvConfSwapPath = "/etc/resolv.conf.new"
37)
38
39type Service struct {
Serge Bazanskicdb8c782020-02-17 12:34:02 +010040 config Config
Lorenz Brun52f7f292020-06-24 16:42:02 +020041 dhcp *dhcp.Client
Serge Bazanskicdb8c782020-02-17 12:34:02 +010042
Serge Bazanskib1b742f2020-03-24 13:58:19 +010043 logger *zap.Logger
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020044}
45
46type Config struct {
47}
48
Serge Bazanskib1b742f2020-03-24 13:58:19 +010049func New(config Config) *Service {
50 return &Service{
Serge Bazanskicdb8c782020-02-17 12:34:02 +010051 config: config,
Lorenz Brun52f7f292020-06-24 16:42:02 +020052 dhcp: dhcp.New(),
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020053 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020054}
55
56func setResolvconf(nameservers []net.IP, searchDomains []string) error {
Leopold Schabel68c58752019-11-14 21:00:59 +010057 _ = os.Mkdir("/etc", 0755)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020058 newResolvConf, err := os.Create(resolvConfSwapPath)
59 if err != nil {
60 return err
61 }
62 defer newResolvConf.Close()
63 defer os.Remove(resolvConfSwapPath)
64 for _, ns := range nameservers {
65 if _, err := newResolvConf.WriteString(fmt.Sprintf("nameserver %v\n", ns)); err != nil {
66 return err
67 }
68 }
69 for _, searchDomain := range searchDomains {
70 if _, err := newResolvConf.WriteString(fmt.Sprintf("search %v", searchDomain)); err != nil {
71 return err
72 }
73 }
74 newResolvConf.Close()
75 // Atomically swap in new config
76 return unix.Rename(resolvConfSwapPath, resolvConfPath)
77}
78
Serge Bazanski1a5a6672020-02-18 10:09:43 +010079func (s *Service) addNetworkRoutes(link netlink.Link, addr net.IPNet, gw net.IP) error {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020080 if err := netlink.AddrReplace(link, &netlink.Addr{IPNet: &addr}); err != nil {
Lorenz Brun52f7f292020-06-24 16:42:02 +020081 return fmt.Errorf("failed to add DHCP address to network interface \"%v\": %w", link.Attrs().Name, err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020082 }
Serge Bazanski1a5a6672020-02-18 10:09:43 +010083
84 if gw.IsUnspecified() {
Serge Bazanskib1b742f2020-03-24 13:58:19 +010085 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 +010086 return nil
87 }
88
89 route := &netlink.Route{
Lorenz Brun45333b62019-11-11 15:26:27 +010090 Dst: &net.IPNet{IP: net.IPv4(0, 0, 0, 0), Mask: net.IPv4Mask(0, 0, 0, 0)},
91 Gw: gw,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020092 Scope: netlink.SCOPE_UNIVERSE,
Serge Bazanski1a5a6672020-02-18 10:09:43 +010093 }
94 if err := netlink.RouteAdd(route); err != nil {
95 return fmt.Errorf("could not add default route: netlink.RouteAdd(%+v): %v", route, err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020096 }
97 return nil
98}
99
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100100func (s *Service) useInterface(ctx context.Context, iface netlink.Link) error {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200101 err := supervisor.Run(ctx, "dhcp", s.dhcp.Run(iface))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200102 if err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100103 return err
104 }
Lorenz Brun52f7f292020-06-24 16:42:02 +0200105 status, err := s.dhcp.Status(ctx, true)
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100106 if err != nil {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200107 return fmt.Errorf("could not get DHCP Status: %w", err)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200108 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100109
Lorenz Brun52f7f292020-06-24 16:42:02 +0200110 if err := setResolvconf(status.DNS, []string{}); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100111 s.logger.Warn("failed to set resolvconf", zap.Error(err))
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100112 }
113
Lorenz Brun52f7f292020-06-24 16:42:02 +0200114 if err := s.addNetworkRoutes(iface, status.Address, status.Gateway); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100115 s.logger.Warn("failed to add routes", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200116 }
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100117
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200118 return nil
119}
120
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100121// GetIP returns the current IP (and optionally waits for one to be assigned)
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100122func (s *Service) GetIP(ctx context.Context, wait bool) (*net.IP, error) {
Lorenz Brun52f7f292020-06-24 16:42:02 +0200123 status, err := s.dhcp.Status(ctx, wait)
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100124 if err != nil {
125 return nil, err
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100126 }
Lorenz Brun52f7f292020-06-24 16:42:02 +0200127 return &status.Address.IP, nil
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100128}
129
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100130func (s *Service) Run(ctx context.Context) error {
131 s.logger = supervisor.Logger(ctx)
132 s.logger.Info("Starting network service")
133
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200134 links, err := netlink.LinkList()
135 if err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100136 s.logger.Fatal("Failed to list network links", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200137 }
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100138
Lorenz Brunf042e6f2020-06-24 16:46:09 +0200139 if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1\n"), 0644); err != nil {
140 s.logger.Panic("Failed to enable IPv4 forwarding", zap.Error(err))
141 }
142
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200143 var ethernetLinks []netlink.Link
144 for _, link := range links {
145 attrs := link.Attrs()
146 if link.Type() == "device" && len(attrs.HardwareAddr) > 0 {
147 if len(attrs.HardwareAddr) == 6 { // Ethernet
148 if attrs.Flags&net.FlagUp != net.FlagUp {
149 netlink.LinkSetUp(link) // Attempt to take up all ethernet links
150 }
151 ethernetLinks = append(ethernetLinks, link)
152 } else {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100153 s.logger.Info("Ignoring non-Ethernet interface", zap.String("interface", attrs.Name))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200154 }
Lorenz Brun45333b62019-11-11 15:26:27 +0100155 } else if link.Attrs().Name == "lo" {
156 if err := netlink.LinkSetUp(link); err != nil {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100157 s.logger.Error("Failed to take up loopback interface", zap.Error(err))
Lorenz Brun45333b62019-11-11 15:26:27 +0100158 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200159 }
160 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100161 if len(ethernetLinks) != 1 {
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100162 s.logger.Warn("Network service needs exactly one link, bailing")
163 } else {
164 link := ethernetLinks[0]
165 if err := s.useInterface(ctx, link); err != nil {
166 return fmt.Errorf("failed to bring up link %s: %w", link.Attrs().Name, err)
167 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200168 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100169
Serge Bazanskib1b742f2020-03-24 13:58:19 +0100170 supervisor.Signal(ctx, supervisor.SignalHealthy)
171 supervisor.Signal(ctx, supervisor.SignalDone)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200172 return nil
173}