netconfig: WireGuard: set up host routes instead of DHCP default

related to https://github.com/rtr7/router7/issues/52
This commit is contained in:
Michael Stapelberg 2022-06-07 23:22:08 +02:00
parent 7d936f4844
commit f4dd972e54

View File

@ -59,7 +59,7 @@ func subnetMaskSize(mask string) (int, error) {
return ones, nil
}
func applyDhcp4(dir string) error {
func applyDhcp4(dir string, cfg InterfaceConfig) error {
b, err := ioutil.ReadFile(filepath.Join(dir, "dhcp4/wire/lease.json"))
if err != nil {
if os.IsNotExist(err) {
@ -137,6 +137,51 @@ func applyDhcp4(dir string) error {
return fmt.Errorf("RouteReplace(router): %v", err)
}
if defaultViaWireguard(cfg) {
// The default route is on a WireGuard interface, so do not install the
// default route from the DHCP reply. Instead, set up a host route for
// the WireGuard endpoint(s).
log.Printf("IPv4 traffic is routed via WireGuard, setting host route instead of default route")
b, err := ioutil.ReadFile(filepath.Join(dir, "wireguard.json"))
if err != nil {
return err
}
var wgcfg wireguardInterfaces
if err := json.Unmarshal(b, &wgcfg); err != nil {
return err
}
for _, iface := range wgcfg.Interfaces {
for _, p := range iface.Peers {
addr, err := net.ResolveUDPAddr("udp", p.Endpoint)
if err != nil {
return err
}
log.Printf(" WireGuard endpoint %s", addr.IP)
router := net.ParseIP(got.Router)
if addr.IP.Equal(router) {
continue // endpoint == router, no route required
}
if err := h.RouteReplace(&netlink.Route{
LinkIndex: link.Attrs().Index,
Dst: &net.IPNet{
IP: addr.IP,
Mask: net.CIDRMask(32, 32),
},
Gw: net.ParseIP(got.Router),
Src: net.ParseIP(got.ClientIP),
Protocol: RTPROT_DHCP,
}); err != nil {
return fmt.Errorf("RouteReplace(default): %v", err)
}
}
}
} else {
if err := h.RouteReplace(&netlink.Route{
LinkIndex: link.Attrs().Index,
Dst: &net.IPNet{
@ -149,10 +194,30 @@ func applyDhcp4(dir string) error {
}); err != nil {
return fmt.Errorf("RouteReplace(default): %v", err)
}
}
return nil
}
func defaultViaWireguard(cfg InterfaceConfig) bool {
for _, iface := range cfg.Interfaces {
if !strings.HasPrefix(iface.Name, "wg") {
continue
}
for _, route := range iface.ExtraRoutes {
_, n, err := net.ParseCIDR(route.Destination)
if err != nil {
continue
}
ones, bits := n.Mask.Size()
if n.IP.Equal(net.IPv4zero) && ones == 0 && bits == 32 {
return true
}
}
}
return false
}
func applyDhcp6(dir string) error {
b, err := ioutil.ReadFile(filepath.Join(dir, "dhcp6/wire/lease.json"))
if err != nil {
@ -305,18 +370,7 @@ func applyBridges(cfg *InterfaceConfig) error {
return nil
}
func applyInterfaces(dir, root string) error {
b, err := ioutil.ReadFile(filepath.Join(dir, "interfaces.json"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
var cfg InterfaceConfig
if err := json.Unmarshal(b, &cfg); err != nil {
return err
}
func applyInterfaces(dir, root string, cfg InterfaceConfig) error {
byName := make(map[string]InterfaceDetails)
byHardwareAddr := make(map[string]InterfaceDetails)
for _, details := range cfg.Interfaces {
@ -994,11 +1048,21 @@ func applySysctl(ifname string) error {
}
func Apply(dir, root string) error {
var cfg InterfaceConfig
b, err := ioutil.ReadFile(filepath.Join(dir, "interfaces.json"))
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil || os.IsNotExist(err) {
if err := json.Unmarshal(b, &cfg); err != nil {
return err
}
// TODO: split into two parts: delay the up until later
if err := applyInterfaces(dir, root); err != nil {
// TODO: split apply into two parts: delay the up until later
if err := applyInterfaces(dir, root, cfg); err != nil {
return fmt.Errorf("interfaces: %v", err)
}
}
var errors []error
appendError := func(err error) {
@ -1006,7 +1070,7 @@ func Apply(dir, root string) error {
log.Println(err)
}
if err := applyDhcp4(dir); err != nil {
if err := applyDhcp4(dir, cfg); err != nil {
appendError(fmt.Errorf("dhcp4: %v", err))
}