1 package netplan
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net"
8 "sort"
9
10 v1core "k8s.io/api/core/v1"
11 "sigs.k8s.io/controller-runtime/pkg/client"
12
13 calico "edge-infra.dev/pkg/k8s/net/calico"
14 "edge-infra.dev/pkg/lib/kernel/netlink/ip"
15 "edge-infra.dev/pkg/lib/kernel/netlink/link"
16 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
17 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/netplan/internal"
18 nodemeta "edge-infra.dev/pkg/sds/ien/node"
19 "edge-infra.dev/pkg/sds/lib/networking"
20 "edge-infra.dev/pkg/sds/lib/networking/routing"
21 )
22
23 var (
24 ErrNoEthernetInterfaces = errors.New("no ethernet interfaces have been configured")
25 )
26
27 func GenerateTargetNetplanConfig(ienode *v1ien.IENode, nodes []v1core.Node, apiConfigured, gatewayEnabled bool, tunnelNetwork *net.IPNet) (*Config, error) {
28 netplanConfig := Template()
29 if err := configureEthernetInterfaces(netplanConfig, ienode, apiConfigured, gatewayEnabled); err != nil {
30 return nil, err
31 }
32
33 if apiConfigured {
34 return netplanConfig, nil
35 }
36
37 nodes = sortK8sNodesByCreationTimestamp(nodes)
38 gatewayNodeIndexes, nonGatewayNodeIndexes, err := filterGatewayNodeIndexes(nodes)
39 if err != nil {
40 return nil, err
41 }
42
43 subnets, err := internal.PairTunnelIPs(tunnelNetwork)
44 if err != nil {
45 return nil, err
46 }
47 if !gatewayEnabled {
48 return netplanConfig, nil
49 }
50 if ienode.Spec.IsGatewayNode() {
51 return configureGatewayNodeTunnels(netplanConfig, ienode, nodes, nonGatewayNodeIndexes, subnets)
52 }
53 return configureNonGatewayNodeTunnels(netplanConfig, ienode, nodes, gatewayNodeIndexes, subnets)
54 }
55
56
57 func configureEthernetInterfaces(netplanConfig *Config, ienode *v1ien.IENode, apiConfigured, gatewayEnabled bool) error {
58
59 if err := setDefaultMTU(ienode, apiConfigured); err != nil {
60 return err
61 }
62
63 var mtu = ienode.Status.Network.DefaultMTU
64
65
66 if !ienode.Spec.IsGatewayNode() && gatewayEnabled && mtu != 0 {
67 mtu = mtu - ip.IPIPHeaderSize
68 }
69
70
71 if apiConfigured {
72 return configureAPIManagedEthernet(netplanConfig, ienode, mtu)
73 }
74
75 netlink := link.NetLink{}
76
77
78 links, err := netlink.GetAllNetworkDeviceLinks()
79 if err != nil {
80 return err
81 }
82
83 for _, link := range links {
84 if iface, isPrimary, apiManaged := isInterfaceAPIManaged(ienode.Spec.Network, link.Attrs().HardwareAddr.String(), ienode.Spec.PrimaryInterface); apiManaged {
85 eth, err := createEthernet(ienode, iface, isPrimary, mtu)
86 if err != nil {
87 return err
88 }
89 netplanConfig.AddEthernet(link.Attrs().Name, eth)
90 } else {
91
92 netplanConfig.AddEthernet(link.Attrs().Name, Ethernet{DHCP4: true, ActivationMode: "off"})
93 }
94 }
95 if len(netplanConfig.Ethernets) == 0 {
96 return ErrNoEthernetInterfaces
97 }
98
99 return nil
100 }
101
102
103
104 func isInterfaceAPIManaged(managedInterfaces []v1ien.Network, macaddress string, primaryInterface *v1ien.PrimaryInterface) (v1ien.Network, bool, bool) {
105 for idx, iface := range managedInterfaces {
106 if iface.MacAddress == macaddress {
107 isPrimary := false
108 if primaryInterface == nil || len(primaryInterface.MacAddresses) == 0 {
109 isPrimary = idx == 0
110 } else {
111 for _, mac := range primaryInterface.MacAddresses {
112 if iface.MacAddress == mac {
113 isPrimary = true
114 break
115 }
116 }
117 }
118 return iface, isPrimary, true
119 }
120 }
121
122 return v1ien.Network{}, false, false
123 }
124
125
126
127 func configureAPIManagedEthernet(netplanConfig *Config, ienode *v1ien.IENode, mtu int) error {
128 for idx, iface := range ienode.Spec.Network {
129 isPrimary := false
130 if ienode.Spec.PrimaryInterface == nil || len(ienode.Spec.PrimaryInterface.MacAddresses) == 0 {
131 isPrimary = idx == 0
132 } else {
133 for _, mac := range ienode.Spec.PrimaryInterface.MacAddresses {
134 if iface.MacAddress == mac {
135 isPrimary = true
136 break
137 }
138 }
139 }
140 eth, err := createEthernet(ienode, iface, isPrimary, mtu)
141 if err != nil {
142 return err
143 }
144 tempName := fmt.Sprintf("eth%d", idx)
145 netplanConfig.AddEthernet(tempName, eth)
146 }
147 return nil
148 }
149
150 func configureGatewayNodeTunnels(netplanConfig *Config, localNode *v1ien.IENode, nodes []v1core.Node, nonGatewayNodeIndexes []int, subnets []*net.IPNet) (*Config, error) {
151 localNodeIP, err := localNodeIP(localNode.ObjectMeta.Name, nodes)
152 if err != nil {
153 return nil, err
154 }
155
156 for _, idx := range nonGatewayNodeIndexes {
157 remoteIPCIDR, err := getNodeIP(nodes[idx])
158 if err != nil {
159 return nil, err
160 }
161
162 remoteIP, _, err := net.ParseCIDR(remoteIPCIDR)
163 if err != nil {
164 return nil, err
165 }
166
167 subnet := subnets[idx]
168
169
170 tunnelIP, err := networking.GetAddressFromSubnet(subnet, 0)
171 if err != nil {
172 return nil, err
173 }
174
175 tunnel := createEgressTunnel(localNodeIP, remoteIP.String(), tunnelIP)
176 netplanConfig.AddTunnel(fmt.Sprintf("tun%d", idx), tunnel)
177 }
178 return netplanConfig, nil
179 }
180
181 func configureNonGatewayNodeTunnels(netplanConfig *Config, localNode *v1ien.IENode, nodes []v1core.Node, gatewayNodeIndexes []int, subnets []*net.IPNet) (*Config, error) {
182 localNodeIP, err := localNodeIP(localNode.ObjectMeta.Name, nodes)
183 if err != nil {
184 return nil, err
185 }
186
187 localNodeIdx, err := localNodeIdx(localNode.ObjectMeta.Name, nodes)
188 if err != nil {
189 return nil, err
190 }
191
192
193 for _, idx := range gatewayNodeIndexes {
194 remoteIPCIDR, err := getNodeIP(nodes[idx])
195 if err != nil {
196 return nil, err
197 }
198
199 remoteIP, _, err := net.ParseCIDR(remoteIPCIDR)
200 if err != nil {
201 return nil, err
202 }
203
204 subnet := subnets[localNodeIdx]
205
206
207 tunnelIP, err := networking.GetAddressFromSubnet(subnet, 1)
208 if err != nil {
209 return nil, err
210 }
211
212
213 gatewayTunnelCIDR, err := networking.GetAddressFromSubnet(subnet, 0)
214 if err != nil {
215 return nil, err
216 }
217
218 gatewayTunnelIP, _, err := net.ParseCIDR(gatewayTunnelCIDR)
219 if err != nil {
220 return nil, err
221 }
222
223 tunnel := createEgressTunnel(localNodeIP, remoteIP.String(), tunnelIP)
224 tunnel.AddRoute(Route{
225 To: "default",
226 Table: routing.EgressGatewayTable,
227 Via: gatewayTunnelIP.String(),
228 })
229 tunnel.AddRoutingPolicy(RoutingPolicy{
230 Mark: 512,
231 To: "0.0.0.0/0",
232 Table: routing.EgressGatewayTable,
233 Priority: 32765,
234 })
235 netplanConfig.AddTunnel(fmt.Sprintf("tun%d", localNodeIdx), tunnel)
236 }
237 return netplanConfig, nil
238 }
239
240 func sortK8sNodesByCreationTimestamp(nodes []v1core.Node) []v1core.Node {
241 sort.Slice(nodes, func(i, j int) bool {
242 return nodes[i].ObjectMeta.CreationTimestamp.Before(&nodes[j].ObjectMeta.CreationTimestamp)
243 })
244 return nodes
245 }
246
247
248 func filterGatewayNodeIndexes(nodes []v1core.Node) ([]int, []int, error) {
249 var gatewayNodeIndexList, nonGatewayNodeIndexList []int
250 for idx, node := range nodes {
251 role, ok := nodes[idx].ObjectMeta.Labels[nodemeta.RoleLabel]
252 if !ok {
253 return nil, nil, fmt.Errorf("role label %s for node %s", nodemeta.RoleLabel, node.ObjectMeta.Name)
254 }
255 if role == string(v1ien.ControlPlane) {
256 gatewayNodeIndexList = append(gatewayNodeIndexList, idx)
257 }
258 if role == string(v1ien.Worker) {
259 nonGatewayNodeIndexList = append(nonGatewayNodeIndexList, idx)
260 }
261 }
262 return gatewayNodeIndexList, nonGatewayNodeIndexList, nil
263 }
264
265 func localNodeIdx(localNodeName string, nodes []v1core.Node) (int, error) {
266 localNodeIdx := -1
267 for idx, k8sNode := range nodes {
268 if k8sNode.ObjectMeta.Name == localNodeName {
269 localNodeIdx = idx
270 }
271 }
272
273 if localNodeIdx == -1 {
274 return -1, errors.New("could not find owning k8s node")
275 }
276
277 return localNodeIdx, nil
278 }
279
280 func localNodeIP(localNodeName string, nodes []v1core.Node) (string, error) {
281 localNodeIdx, err := localNodeIdx(localNodeName, nodes)
282 if err != nil {
283 return "", err
284 }
285
286 localNodeIP, err := getNodeIP(nodes[localNodeIdx])
287 if err != nil {
288 return "", err
289 }
290
291 ip, _, err := net.ParseCIDR(localNodeIP)
292 if err != nil {
293 return "", err
294 }
295
296 return ip.String(), nil
297 }
298
299 func getNodeIP(node v1core.Node) (string, error) {
300 nodeIP, ok := node.ObjectMeta.Annotations[calico.IPAnnotation]
301 if !ok {
302 return "", fmt.Errorf("could not find node ip address via %s annotation for node %s", calico.IPAnnotation, node.ObjectMeta.Name)
303 }
304 return nodeIP, nil
305 }
306
307 func createEthernet(ienode *v1ien.IENode, iface v1ien.Network, isPrimary bool, mtu int) (Ethernet, error) {
308 if !isPrimary {
309 return NewEthernet(
310 iface.Addresses,
311 iface.DHCP4,
312 []Route{},
313 []RoutingPolicy{},
314 ienode.Spec.DNSServers,
315 false,
316 0,
317 Match{
318 MacAddress: iface.MacAddress,
319 },
320 ), nil
321 }
322 routeList := []Route{}
323 if !iface.DHCP4 && iface.Gateway4 != "" {
324 staticRoutes, err := staticRoutes(iface)
325 if err != nil {
326 return Ethernet{}, err
327 }
328 routeList = append(routeList, staticRoutes...)
329 }
330 return NewEthernet(
331 iface.Addresses,
332 iface.DHCP4,
333 routeList,
334 []RoutingPolicy{},
335 ienode.Spec.DNSServers,
336 true,
337 mtu,
338 Match{
339 MacAddress: iface.MacAddress,
340 },
341 ), nil
342 }
343
344 func createEgressTunnel(local, remote, address string) Tunnel {
345 return Tunnel{
346 Mode: "ipip",
347 Local: local,
348 Remote: remote,
349 Addresses: []string{address},
350 }
351 }
352
353 func setDefaultMTU(ienode *v1ien.IENode, apiConfigured bool) error {
354 if ienode.Status == nil {
355 ienode.Status = &v1ien.IENodeStatus{Network: v1ien.NetworkStatus{}}
356 }
357
358 if apiConfigured {
359 return nil
360 }
361
362
363 if ienode.Status.Network.DefaultMTU != 0 {
364 return nil
365 }
366
367 hardwareAddress, err := ienode.Spec.DefaultMacAddress()
368 if err != nil {
369 return err
370 }
371
372 netlink := link.NetLink{}
373
374 defaultLink, err := netlink.GetFromHardwareAddr(hardwareAddress)
375 if err != nil {
376 return err
377 }
378
379 ienode.Status.Network.DefaultMTU = defaultLink.Attrs().MTU
380 ienode.Status.Network.DefaultInterfaceName = defaultLink.Attrs().Name
381 return nil
382 }
383
384
385 func patchIENode(ctx context.Context, k8sClient client.Client, ienode *v1ien.IENode) error {
386 currentIENode := &v1ien.IENode{}
387 if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(ienode), currentIENode); err != nil {
388 return err
389 }
390 patch := client.MergeFrom(currentIENode.DeepCopy())
391 currentIENode.Status = ienode.Status
392 return k8sClient.Status().Patch(ctx, currentIENode, patch)
393 }
394
View as plain text