Skip to content

Commit ef4a679

Browse files
committed
feat: IP allocator and wireguard utils added
1 parent 26af639 commit ef4a679

File tree

4 files changed

+250
-0
lines changed

4 files changed

+250
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,5 @@ require (
150150
golang.org/x/sys v0.29.0 // indirect
151151
golang.org/x/text v0.21.0 // indirect
152152
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
153+
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
153154
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
451451
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
452452
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
453453
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
454+
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
455+
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
454456
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
455457
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
456458
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=

pkg/ipam/address_gen.go

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package ipam
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"regexp"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
/*
12+
IP Allocation Rules
13+
14+
Swiftwave will utilize a large subnet subnet for the IP allocation.
15+
By default, we will use 10.x.x.x/8 subnet
16+
17+
Template format -
18+
19+
00001010xxxyyyyyyyyyzzzzzzzzzzzz
20+
21+
xxx > Reserved bits. This need to be at minimum 3 bits for operations.
22+
- 001 - Wireguard Private Network IP
23+
- 010 - Docker Private Network IP
24+
25+
y > Bits for each server. We will increase the bit count and allocate the IPs for each server.
26+
z > Bits for container in the server. We will increase the bit count and allocate the IPs for each container.
27+
28+
*/
29+
30+
const (
31+
wireguardNetwork = "001"
32+
dockerNetwork = "010"
33+
)
34+
35+
type IPAllocationTemplate struct {
36+
Template string
37+
ServerBitsCount int
38+
ServerBitsStartIndex int
39+
ServerBitsEndIndex int
40+
ServerMinValue int
41+
ServerMaxValue int
42+
ContainerBitsCount int
43+
ContainerBitsStartIndex int
44+
ContainerBitsEndIndex int
45+
ContainerMinValue int
46+
ContainerMaxValue int
47+
}
48+
49+
func parseTemplate(template string) (*IPAllocationTemplate, error) {
50+
if len(template) != 32 {
51+
return nil, errors.New("invalid template length")
52+
}
53+
// Check for the reserved bits
54+
if !strings.Contains(template, "xxx") {
55+
return nil, errors.New("reserved bits not found")
56+
}
57+
// Validate the format
58+
matched, _ := regexp.MatchString(`^([0|1]+)xxx(y+)(z+)$`, template)
59+
if !matched {
60+
return nil, errors.New("invalid template format")
61+
}
62+
63+
serverBitsCount := strings.Count(template, "y")
64+
serverBitsStartIndex := strings.Index(template, "y")
65+
containerBitsCount := strings.Count(template, "z")
66+
containerBitsStartIndex := strings.Index(template, "z")
67+
68+
return &IPAllocationTemplate{
69+
Template: strings.ReplaceAll(strings.ReplaceAll(template, "y", "0"), "z", "0"),
70+
ServerBitsCount: serverBitsCount,
71+
ServerBitsStartIndex: serverBitsStartIndex,
72+
ServerBitsEndIndex: serverBitsStartIndex + serverBitsCount,
73+
ServerMinValue: 1,
74+
ServerMaxValue: 1<<serverBitsCount - 1,
75+
ContainerBitsCount: containerBitsCount,
76+
ContainerBitsStartIndex: containerBitsStartIndex,
77+
ContainerBitsEndIndex: containerBitsStartIndex + containerBitsCount,
78+
ContainerMinValue: 2,
79+
ContainerMaxValue: 1<<containerBitsCount - 1,
80+
}, nil
81+
}
82+
83+
func GenerateWireguardIP(template string, serverId int) (string, error) {
84+
t, err := parseTemplate(template)
85+
if err != nil {
86+
return "", err
87+
}
88+
89+
// Check the server id
90+
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
91+
return "", errors.New("server id is not in the valid range")
92+
}
93+
94+
// Replace the reserved bits with the wireguard
95+
templateString := t.Template
96+
templateString = strings.Replace(templateString, "xxx", wireguardNetwork, 1)
97+
98+
// Change last bit of the template to 1
99+
templateString = templateString[:len(templateString)-1] + "1"
100+
101+
// Convert the server id to binary and replace the bits in the template
102+
serverIdBinary := fmt.Sprintf("%b", serverId)
103+
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]
104+
105+
return binaryFormatToIP(templateString)
106+
}
107+
108+
func GenerateWireguardSubnet(template string) (string, error) {
109+
t, err := parseTemplate(template)
110+
if err != nil {
111+
return "", err
112+
}
113+
114+
// Replace the reserved bits with the wireguard
115+
templateString := t.Template
116+
templateString = strings.Replace(templateString, "xxx", wireguardNetwork, 1)
117+
118+
ip, err := binaryFormatToIP(templateString)
119+
if err != nil {
120+
return "", err
121+
}
122+
return fmt.Sprintf("%s/%d", ip, t.ServerBitsStartIndex), nil
123+
}
124+
125+
func GenerateContainerGatewayIP(template string, serverId int) (string, error) {
126+
t, err := parseTemplate(template)
127+
if err != nil {
128+
return "", err
129+
}
130+
131+
// Check the server id
132+
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
133+
return "", errors.New("server id is not in the valid range")
134+
}
135+
136+
// Replace the reserved bits with the wireguard
137+
templateString := t.Template
138+
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)
139+
140+
// Change last bit of the template to 1
141+
templateString = templateString[:len(templateString)-1] + "1"
142+
143+
// Convert the server id to binary and replace the bits in the template
144+
serverIdBinary := fmt.Sprintf("%b", serverId)
145+
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]
146+
147+
return binaryFormatToIP(templateString)
148+
}
149+
150+
func GenerateContainerSubnet(template string, serverId int) (string, error) {
151+
t, err := parseTemplate(template)
152+
if err != nil {
153+
return "", err
154+
}
155+
156+
// Check the server id
157+
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
158+
return "", errors.New("server id is not in the valid range")
159+
}
160+
161+
// Replace the reserved bits with the wireguard
162+
templateString := t.Template
163+
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)
164+
165+
// Convert the server id to binary and replace the bits in the template
166+
serverIdBinary := fmt.Sprintf("%b", serverId)
167+
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]
168+
169+
ip, err := binaryFormatToIP(templateString)
170+
if err != nil {
171+
return "", err
172+
}
173+
return fmt.Sprintf("%s/%d", ip, t.ContainerBitsStartIndex), nil
174+
}
175+
176+
func GenerateContainerIP(template string, serverId int, containerId int) (string, error) {
177+
t, err := parseTemplate(template)
178+
if err != nil {
179+
return "", err
180+
}
181+
182+
// Validate the server id
183+
if serverId > t.ServerMaxValue || serverId < t.ServerMinValue {
184+
return "", errors.New("server id is not in the valid range")
185+
}
186+
187+
// Validate the container id
188+
if containerId > t.ContainerMaxValue || containerId < t.ContainerMinValue {
189+
return "", errors.New("container id is not in the valid range")
190+
}
191+
192+
// Replace the reserved bits with the wireguard
193+
templateString := t.Template
194+
templateString = strings.Replace(templateString, "xxx", dockerNetwork, 1)
195+
196+
// Convert the server id to binary and replace the bits in the template
197+
serverIdBinary := fmt.Sprintf("%b", serverId)
198+
templateString = templateString[:(t.ServerBitsEndIndex-len(serverIdBinary))] + serverIdBinary + templateString[t.ServerBitsEndIndex:]
199+
200+
// Convert the container id to binary and replace the bits in the template
201+
containerIdBinary := fmt.Sprintf("%b", containerId)
202+
templateString = templateString[:(t.ContainerBitsEndIndex-len(containerIdBinary))] + containerIdBinary + templateString[t.ContainerBitsEndIndex:]
203+
204+
return binaryFormatToIP(templateString)
205+
}
206+
207+
func binaryFormatToIP(ip string) (string, error) {
208+
if len(ip) != 32 {
209+
return "", errors.New("invalid binary presentation")
210+
}
211+
// Split the template into 4 parts, each part is 8 bits
212+
parts := []string{
213+
ip[:8],
214+
ip[8:16],
215+
ip[16:24],
216+
ip[24:],
217+
}
218+
// Convert each binary string to decimal
219+
for i, part := range parts {
220+
decimal, err := strconv.ParseInt(part, 2, 64)
221+
if err != nil {
222+
return "", err
223+
}
224+
parts[i] = fmt.Sprintf("%d", decimal)
225+
}
226+
// Join the parts using "."
227+
return strings.Join(parts, "."), nil
228+
}

pkg/ipam/wireguard.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package ipam
2+
3+
import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
4+
5+
func GenerateWGPrivateKey() (string, error) {
6+
key, err := wgtypes.GeneratePrivateKey()
7+
if err != nil {
8+
return "", err
9+
}
10+
return key.String(), nil
11+
}
12+
13+
func GenerateWGPublicKey(privateKey string) (string, error) {
14+
key, err := wgtypes.ParseKey(privateKey)
15+
if err != nil {
16+
return "", err
17+
}
18+
return key.PublicKey().String(), nil
19+
}

0 commit comments

Comments
 (0)