github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/network/fan.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network
     5  
     6  import (
     7  	"encoding/binary"
     8  	"fmt"
     9  	"net"
    10  	"strings"
    11  
    12  	"github.com/juju/errors"
    13  )
    14  
    15  // FanConfigEntry defines a configuration for single fan.
    16  type FanConfigEntry struct {
    17  	Underlay *net.IPNet
    18  	Overlay  *net.IPNet
    19  }
    20  
    21  // FanConfig defines a set of fan configurations for the model.
    22  type FanConfig []FanConfigEntry
    23  
    24  // ParseFanConfig parses fan configuration from model-config in the format:
    25  // "underlay1=overlay1 underlay2=overlay2" eg. "172.16.0.0/16=253.0.0.0/8 10.0.0.0/12:254.0.0.0/7"
    26  func ParseFanConfig(line string) (config FanConfig, err error) {
    27  	if line == "" {
    28  		return nil, nil
    29  	}
    30  	entries := strings.Split(line, " ")
    31  	config = make([]FanConfigEntry, len(entries))
    32  	for i, line := range entries {
    33  		cidrs := strings.Split(line, "=")
    34  		if len(cidrs) != 2 {
    35  			return nil, fmt.Errorf("invalid FAN config entry: %v", line)
    36  		}
    37  		if _, config[i].Underlay, err = net.ParseCIDR(strings.TrimSpace(cidrs[0])); err != nil {
    38  			return nil, errors.Annotatef(err, "invalid address in FAN config")
    39  		}
    40  		if _, config[i].Overlay, err = net.ParseCIDR(strings.TrimSpace(cidrs[1])); err != nil {
    41  			return nil, errors.Annotatef(err, "invalid address in FAN config")
    42  		}
    43  		underlaySize, _ := config[i].Underlay.Mask.Size()
    44  		overlaySize, _ := config[i].Overlay.Mask.Size()
    45  		if underlaySize <= overlaySize {
    46  			return nil, fmt.Errorf("invalid FAN config, underlay mask must be larger than overlay: %s", line)
    47  		}
    48  	}
    49  	return config, nil
    50  }
    51  
    52  func (fc *FanConfig) String() (line string) {
    53  	configs := make([]string, len(*fc))
    54  	for i, fan := range *fc {
    55  		configs[i] = fmt.Sprintf("%s=%s", fan.Underlay.String(), fan.Overlay.String())
    56  	}
    57  	return strings.Join(configs, " ")
    58  }
    59  
    60  // CalculateOverlaySegment takes underlay CIDR and FAN config entry and
    61  // cuts the segment of overlay that corresponds to this underlay:
    62  // eg. for FAN 172.31/16 -> 243/8 and physical subnet 172.31.64/20
    63  // we get FAN subnet 243.64/12.
    64  func CalculateOverlaySegment(underlayCIDR string, fan FanConfigEntry) (*net.IPNet, error) {
    65  	_, underlayNet, err := net.ParseCIDR(underlayCIDR)
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	subnetSize, _ := underlayNet.Mask.Size()
    70  	underlaySize, _ := fan.Underlay.Mask.Size()
    71  	if underlaySize <= subnetSize && fan.Underlay.Contains(underlayNet.IP) {
    72  		overlaySize, _ := fan.Overlay.Mask.Size()
    73  		newOverlaySize := overlaySize + (subnetSize - underlaySize)
    74  		fanSize := uint(underlaySize - overlaySize)
    75  		newFanIP := underlayNet.IP.To4()
    76  		if newFanIP == nil {
    77  			return nil, errors.New("fan address is not an IPv4 address.")
    78  		}
    79  		for i := 0; i < 4; i++ {
    80  			newFanIP[i] &^= fan.Underlay.Mask[i]
    81  		}
    82  		numIp := binary.BigEndian.Uint32(newFanIP)
    83  		numIp <<= fanSize
    84  		binary.BigEndian.PutUint32(newFanIP, numIp)
    85  		for i := 0; i < 4; i++ {
    86  			newFanIP[i] += fan.Overlay.IP[i]
    87  		}
    88  		return &net.IPNet{IP: newFanIP, Mask: net.CIDRMask(newOverlaySize, 32)}, nil
    89  	} else {
    90  		return nil, nil
    91  	}
    92  }