1 package vpn
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net"
8 "net/netip"
9
10 corev1 "k8s.io/api/core/v1"
11 "sigs.k8s.io/controller-runtime/pkg/client"
12
13 "edge-infra.dev/pkg/sds/remoteaccess/constants"
14 v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1"
15 )
16
17 var ErrNoIPAddressesAvailable = errors.New("no IP addresses available in subnet")
18 var ErrSubnetNotConfigured = errors.New("subnet has not been configured yet, run `ConfigureSubnet()`")
19
20
21 type IPAddressPool map[string]string
22
23 func (v *VPN) ConfigureSubnet(ctx context.Context, c client.Client) error {
24 subnetCIDR, err := getSubnetCIDRFromConfigMap(ctx, c)
25 if err != nil {
26 return err
27 }
28
29
30 if v.AvailableIPAddressPool != nil {
31 if subnetCIDR == v.GetSubnetCIDR() {
32 return nil
33 }
34 }
35
36
37 v.SubnetCIDR = subnetCIDR
38 return v.populateAvailableIPAddressPool(ctx, c)
39 }
40
41 func getSubnetCIDRFromConfigMap(ctx context.Context, c client.Client) (string, error) {
42 configMap := &corev1.ConfigMap{}
43 if err := c.Get(ctx, vpnConfigMapKey, configMap); err != nil {
44 return "", err
45 }
46
47 subnetCIDR, ok := configMap.Data[constants.VPNConfigMapCIDRKey]
48 if !ok {
49 return "", fmt.Errorf("expected key '%s' in ConfigMap %s/%s", constants.VPNConfigMapCIDRKey, constants.VPNNamespace, vpnConfigMapKey.String())
50 }
51 return subnetCIDR, nil
52 }
53
54 func (v *VPN) populateAvailableIPAddressPool(ctx context.Context, c client.Client) error {
55 if err := v.createInitialIPAddressPool(ctx, c); err != nil {
56 return err
57 }
58
59 vpnConfigs, err := getVPNConfigList(ctx, c)
60 if err != nil {
61 return err
62 }
63
64
65 for _, vpnConfig := range vpnConfigs.Items {
66 isInSubnet, err := v.IPAddressIsInSubnet(vpnConfig.IP())
67 if err != nil {
68 return err
69 }
70 if isInSubnet && v.CheckIPAddressIsAvailable(vpnConfig.IP()) {
71 v.setIPAddressUnavailable(vpnConfig.IP(), vpnConfig.ClusterEdgeID())
72 }
73 }
74
75 return nil
76 }
77
78 func (v *VPN) createInitialIPAddressPool(ctx context.Context, c client.Client) error {
79 v.AvailableIPAddressPool = IPAddressPool{}
80
81 subnet, err := netip.ParsePrefix(v.GetSubnetCIDR())
82 if err != nil {
83 return err
84 }
85
86
87 idx := 0
88 for nextIP := subnet.Addr(); subnet.Contains(nextIP); nextIP = nextIP.Next() {
89 ip := net.IP(nextIP.AsSlice())
90 v.setIPAddressAvailable(ip)
91 if err := v.reserveIPAddresses(ctx, c, ip, idx); err != nil {
92 return err
93 }
94 idx++
95 }
96 return nil
97 }
98
99
100 func (v *VPN) reserveIPAddresses(ctx context.Context, c client.Client, ip net.IP, idx int) error {
101 if idx == 0 {
102 if err := v.Relay().UpdateIPAddress(ctx, c, constants.RelayName, "cluster-infra", ip); err != nil {
103 return err
104 }
105 v.setIPAddressUnavailable(ip, "relay")
106 } else if idx == 1 {
107 if err := v.Client().UpdateIPAddress(ctx, c, constants.ClientName, "cluster-infra", ip); err != nil {
108 return err
109 }
110 v.setIPAddressUnavailable(ip, "client")
111 }
112 return nil
113 }
114
115
116 func getVPNConfigList(ctx context.Context, c client.Client) (*v1vpnconfig.VPNConfigList, error) {
117 vpnConfigs := &v1vpnconfig.VPNConfigList{}
118 if err := c.List(ctx, vpnConfigs, &client.ListOptions{Namespace: constants.VPNNamespace}); err != nil {
119 return nil, err
120 }
121 return vpnConfigs, nil
122 }
123
124
125 func (v *VPN) IPAddressIsInSubnet(ip net.IP) (bool, error) {
126 if ip == nil {
127 return false, nil
128 }
129
130 subnetCIDR := v.GetSubnetCIDR()
131 subnet, err := netip.ParsePrefix(subnetCIDR)
132 if err != nil {
133 return false, err
134 }
135
136 address, err := netip.ParseAddr(ip.String())
137 if err != nil {
138 return false, err
139 }
140
141 return subnet.Contains(address), nil
142 }
143
144
145 func (v *VPN) RequestAvailableIPAddress(clusterEdgeID string) (net.IP, error) {
146 if v.AvailableIPAddressPool == nil {
147 return nil, ErrSubnetNotConfigured
148 }
149
150 for nextIP := range v.AvailableIPAddressPool {
151 ip := net.ParseIP(nextIP)
152 if v.CheckIPAddressIsAvailable(ip) {
153 v.setIPAddressUnavailable(ip, clusterEdgeID)
154 return ip, nil
155 }
156 }
157 return nil, ErrNoIPAddressesAvailable
158 }
159
160 func (v *VPN) setIPAddressUnavailable(ip net.IP, clusterEdgeID string) {
161 if v.AvailableIPAddressPool != nil {
162 v.AvailableIPAddressPool[ip.String()] = clusterEdgeID
163 }
164 }
165
166 func (v *VPN) setIPAddressAvailable(ip net.IP) {
167 if v.AvailableIPAddressPool != nil {
168 v.AvailableIPAddressPool[ip.String()] = ""
169 }
170 }
171
172 func (v *VPN) CheckIPAddressIsAvailable(ip net.IP) bool {
173 if v.AvailableIPAddressPool == nil {
174 return false
175 }
176 return v.AvailableIPAddressPool[ip.String()] == ""
177 }
178
179 func (v *VPN) CheckIPAddressIsAssignedToStore(ip net.IP, clusterEdgeID string) bool {
180 if v.AvailableIPAddressPool == nil {
181 return false
182 }
183 return v.AvailableIPAddressPool[ip.String()] == clusterEdgeID
184 }
185
View as plain text