From 5da657917f8d762e9c66db08d3f953bfc2db4428 Mon Sep 17 00:00:00 2001 From: Grische Date: Tue, 12 Nov 2024 13:31:38 +0100 Subject: [PATCH] etcdconfigweb: use IPv4/IPv6 for range env vars v2 v2 fixes some bugs that were present in both the original code and v1 - combine IPv4 and IPv6 calculations into one - the new version can handle abitrary IPv4 or IPv6 subnet sizes and is not bound to specific sizes --- etcdconfigweb/config.go | 94 ++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/etcdconfigweb/config.go b/etcdconfigweb/config.go index 96c5dae..837e0b0 100644 --- a/etcdconfigweb/config.go +++ b/etcdconfigweb/config.go @@ -3,11 +3,11 @@ package main import ( "context" "encoding/base64" - "encoding/binary" "encoding/json" "errors" "fmt" "log" + "math/big" "net" "net/http" "net/url" @@ -35,6 +35,31 @@ var MISSING_V6MTU = errors.New("Missing v6mtu query parameter") var MISSING_PUBKEY = errors.New("Missing pubkey query parameter") var MISSING_NONCE = errors.New("Missing nonce query parameter") +func getNthSubnet(cidr string, nthSubnet uint, subnetSize uint) (net.IP, error) { + _, ipCidr, err := net.ParseCIDR(cidr) + if err != nil { + return nil, fmt.Errorf("invalid CIDR notation: %s", cidr) + } + + baseIP := ipCidr.IP + rangeSize, totalIPSize := ipCidr.Mask.Size() + if subnetSize <= uint(rangeSize) { + return nil, fmt.Errorf("subnet size %d too large for CIDR range %s", subnetSize, ipCidr.String()) + } + + subnetShiftSize := subnetSize - uint(rangeSize) + if nthSubnet >= (1 << subnetShiftSize) { + return nil, fmt.Errorf("cannot allocate %dx /%d subnets in the CIDR range %s", nthSubnet+1, subnetSize, ipCidr.String()) + } + + nthSubnetID := big.NewInt(int64(nthSubnet)) + nthSubnetID.Lsh(nthSubnetID, uint(totalIPSize)-subnetSize) + nthSubnetAddr := new(big.Int).SetBytes(baseIP) + nthSubnetAddr.Add(nthSubnetAddr, nthSubnetID) + + return net.IP(nthSubnetAddr.Bytes()), nil +} + func (ch ConfigHandler) handleRequest(ctx context.Context, query url.Values, headers http.Header) (*ConfigResponse, error) { var v6mtu uint64 var err error @@ -82,54 +107,45 @@ func (ch ConfigHandler) handleRequest(ctx context.Context, query url.Values, hea // insert new node err := ch.etcdHandler.CreateNode(ctx, pubkey, func(info *ffbs.NodeInfo) { - v4_range_str := os.Getenv("PARKER_V4_RANGE") - v4_range_size_str := os.Getenv("PARKER_V4_RANGE_SIZE") - v6_range_str := os.Getenv("PARKER_V6_RANGE") + const CLIENT_V4_RANGE_SIZE uint = 22 + const CLIENT_V6_RANGE_SIZE uint = 64 - if v4_range_str == "" { - v4_range_str = "10.0.0.0" - } - if v4_range_size_str == "" { - v4_range_size_str = "10" - } - if v6_range_str == "" { - v6_range_str = "2001:bf7:381::" + // IPv4 handling + v4RangeStr := os.Getenv("PARKER_V4_RANGE") + if v4RangeStr == "" { + v4RangeStr = "10.0.0.0/8" // default IPv4 range for backwards compatibility } - v4_base_ip := net.ParseIP(v4_range_str).To4() - if v4_base_ip == nil { - panic("Invalid v4 base address" + v4_range_str) + // IPv6 handling + v6RangeStr := os.Getenv("PARKER_V6_RANGE") + if v6RangeStr == "" { + v6RangeStr = "2001:bf7:381::/48" // default IPv6 range for backwards compatibility } - V4_BASE := binary.BigEndian.Uint32(v4_base_ip) - v4_range_uint8, err := strconv.ParseInt(v4_range_size_str, 0, 8) - V4_RANGE_SIZE := uint8(v4_range_uint8) + num := *info.ID + + v4ClientSubnet, err := getNthSubnet(v4RangeStr, num, CLIENT_V4_RANGE_SIZE) if err != nil { panic(err) } + v4ClientRangeStr := fmt.Sprintf("%s/%d", v4ClientSubnet, CLIENT_V4_RANGE_SIZE) + v4BigAddr := new(big.Int).SetBytes(v4ClientSubnet) + v4BigAddr.Add(v4BigAddr, big.NewInt(1)) // use the next free IPv4 + v4ClientAddrStr := net.IP(v4BigAddr.Bytes()).String() - v6Addr := net.ParseIP(v6_range_str).To16() - if v6Addr == nil { - panic("Invalid v6 base address: " + v6_range_str) + v6ClientSubnet, err := getNthSubnet(v6RangeStr, num, CLIENT_V6_RANGE_SIZE) + if err != nil { + panic(err) } - - num := *info.ID - - var v4Addr [net.IPv4len]byte - binary.BigEndian.PutUint32(v4Addr[:], V4_BASE|(uint32(num)<