Skip to content

Commit

Permalink
feat: IP allocator and wireguard utils added
Browse files Browse the repository at this point in the history
  • Loading branch information
tanmoysrt committed Jan 25, 2025
1 parent 26af639 commit ef4a679
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,5 @@ require (
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
Expand Down
228 changes: 228 additions & 0 deletions pkg/ipam/address_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package ipam

import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)

/*
IP Allocation Rules
Swiftwave will utilize a large subnet subnet for the IP allocation.
By default, we will use 10.x.x.x/8 subnet
Template format -
00001010xxxyyyyyyyyyzzzzzzzzzzzz
xxx > Reserved bits. This need to be at minimum 3 bits for operations.
- 001 - Wireguard Private Network IP
- 010 - Docker Private Network IP
y > Bits for each server. We will increase the bit count and allocate the IPs for each server.
z > Bits for container in the server. We will increase the bit count and allocate the IPs for each container.
*/

const (
wireguardNetwork = "001"
dockerNetwork = "010"
)

type IPAllocationTemplate struct {
Template string
ServerBitsCount int
ServerBitsStartIndex int
ServerBitsEndIndex int
ServerMinValue int
ServerMaxValue int
ContainerBitsCount int
ContainerBitsStartIndex int
ContainerBitsEndIndex int
ContainerMinValue int
ContainerMaxValue int
}

func parseTemplate(template string) (*IPAllocationTemplate, error) {
if len(template) != 32 {
return nil, errors.New("invalid template length")
}
// Check for the reserved bits
if !strings.Contains(template, "xxx") {
return nil, errors.New("reserved bits not found")
}
// Validate the format
matched, _ := regexp.MatchString(`^([0|1]+)xxx(y+)(z+)$`, template)
if !matched {
return nil, errors.New("invalid template format")
}

serverBitsCount := strings.Count(template, "y")
serverBitsStartIndex := strings.Index(template, "y")
containerBitsCount := strings.Count(template, "z")
containerBitsStartIndex := strings.Index(template, "z")

return &IPAllocationTemplate{
Template: strings.ReplaceAll(strings.ReplaceAll(template, "y", "0"), "z", "0"),
ServerBitsCount: serverBitsCount,
ServerBitsStartIndex: serverBitsStartIndex,
ServerBitsEndIndex: serverBitsStartIndex + serverBitsCount,
ServerMinValue: 1,
ServerMaxValue: 1<<serverBitsCount - 1,
ContainerBitsCount: containerBitsCount,
ContainerBitsStartIndex: containerBitsStartIndex,
ContainerBitsEndIndex: containerBitsStartIndex + containerBitsCount,
ContainerMinValue: 2,
ContainerMaxValue: 1<<containerBitsCount - 1,
}, nil
}

func GenerateWireguardIP(template string, serverId int) (string, error) {
t, err := parseTemplate(template)
if err != nil {
return "", err
}

// Check the server id
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
return "", errors.New("server id is not in the valid range")
}

// Replace the reserved bits with the wireguard
templateString := t.Template
templateString = strings.Replace(templateString, "xxx", wireguardNetwork, 1)

// Change last bit of the template to 1
templateString = templateString[:len(templateString)-1] + "1"

// Convert the server id to binary and replace the bits in the template
serverIdBinary := fmt.Sprintf("%b", serverId)
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]

return binaryFormatToIP(templateString)
}

func GenerateWireguardSubnet(template string) (string, error) {
t, err := parseTemplate(template)
if err != nil {
return "", err
}

// Replace the reserved bits with the wireguard
templateString := t.Template
templateString = strings.Replace(templateString, "xxx", wireguardNetwork, 1)

ip, err := binaryFormatToIP(templateString)
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%d", ip, t.ServerBitsStartIndex), nil
}

func GenerateContainerGatewayIP(template string, serverId int) (string, error) {
t, err := parseTemplate(template)
if err != nil {
return "", err
}

// Check the server id
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
return "", errors.New("server id is not in the valid range")
}

// Replace the reserved bits with the wireguard
templateString := t.Template
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)

// Change last bit of the template to 1
templateString = templateString[:len(templateString)-1] + "1"

// Convert the server id to binary and replace the bits in the template
serverIdBinary := fmt.Sprintf("%b", serverId)
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]

return binaryFormatToIP(templateString)
}

func GenerateContainerSubnet(template string, serverId int) (string, error) {
t, err := parseTemplate(template)
if err != nil {
return "", err
}

// Check the server id
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
return "", errors.New("server id is not in the valid range")
}

// Replace the reserved bits with the wireguard
templateString := t.Template
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)

// Convert the server id to binary and replace the bits in the template
serverIdBinary := fmt.Sprintf("%b", serverId)
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]

ip, err := binaryFormatToIP(templateString)
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%d", ip, t.ContainerBitsStartIndex), nil
}

func GenerateContainerIP(template string, serverId int, containerId int) (string, error) {
t, err := parseTemplate(template)
if err != nil {
return "", err
}

// Validate the server id
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
return "", errors.New("server id is not in the valid range")
}

// Validate the container id
if containerId > t.ContainerMaxValue || containerId < t.ContainerMinValue {
return "", errors.New("container id is not in the valid range")
}

// Replace the reserved bits with the wireguard
templateString := t.Template
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)

// Convert the server id to binary and replace the bits in the template
serverIdBinary := fmt.Sprintf("%b", serverId)
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]

// Convert the container id to binary and replace the bits in the template
containerIdBinary := fmt.Sprintf("%b", containerId)
templateString = templateString[:(t.ContainerBitsEndIndex-len(containerIdBinary))] + containerIdBinary + templateString[t.ContainerBitsEndIndex:]

return binaryFormatToIP(templateString)
}

func binaryFormatToIP(ip string) (string, error) {
if len(ip) != 32 {
return "", errors.New("invalid binary presentation")
}
// Split the template into 4 parts, each part is 8 bits
parts := []string{
ip[:8],
ip[8:16],
ip[16:24],
ip[24:],
}
// Convert each binary string to decimal
for i, part := range parts {
decimal, err := strconv.ParseInt(part, 2, 64)
if err != nil {
return "", err
}
parts[i] = fmt.Sprintf("%d", decimal)
}
// Join the parts using "."
return strings.Join(parts, "."), nil
}
19 changes: 19 additions & 0 deletions pkg/ipam/wireguard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ipam

import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"

func GenerateWGPrivateKey() (string, error) {
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return "", err
}
return key.String(), nil
}

func GenerateWGPublicKey(privateKey string) (string, error) {
key, err := wgtypes.ParseKey(privateKey)
if err != nil {
return "", err
}
return key.PublicKey().String(), nil
}

0 comments on commit ef4a679

Please sign in to comment.