fixing centos container images to point cni-proxy fix
[ovn4nfv-k8s-plugin.git] / app / helper_linux.go
1 // +build linux
2
3 package app
4
5 import (
6         "fmt"
7         "net"
8         "os/exec"
9         "ovn4nfv-k8s-plugin/internal/pkg/config"
10         "ovn4nfv-k8s-plugin/internal/pkg/network"
11         "ovn4nfv-k8s-plugin/internal/pkg/ovn"
12         "strconv"
13         "strings"
14
15         "github.com/containernetworking/cni/pkg/types/current"
16         "github.com/containernetworking/plugins/pkg/ip"
17         "github.com/containernetworking/plugins/pkg/ns"
18         "github.com/prometheus/common/log"
19         "github.com/sirupsen/logrus"
20         "github.com/vishvananda/netlink"
21 )
22
23 func renameLink(curName, newName string) error {
24         link, err := netlink.LinkByName(curName)
25         if err != nil {
26                 return err
27         }
28
29         if err := netlink.LinkSetDown(link); err != nil {
30                 return err
31         }
32         if err := netlink.LinkSetName(link, newName); err != nil {
33                 return err
34         }
35         if err := netlink.LinkSetUp(link); err != nil {
36                 return err
37         }
38
39         return nil
40 }
41
42 //Todo Comments
43 func CreateNodeOVSInternalPort(nodeintfipaddr, nodeintfmacaddr, node string) error {
44         nodeName := strings.ToLower(node)
45         nodeOVSInternalIntfName := config.GetNodeIntfName(nodeName)
46
47         hwAddr, err := net.ParseMAC(nodeintfmacaddr)
48         if err != nil {
49                 logrus.Errorf("Error is converting %q to net hwaddr: %v", nodeOVSInternalIntfName, err)
50                 return fmt.Errorf("Error is converting %q to net hwaddr: %v", nodeOVSInternalIntfName, err)
51         }
52
53         ovsArgs := []string{
54                 "add-port", "br-int", nodeOVSInternalIntfName, "--", "set",
55                 "interface", nodeOVSInternalIntfName, "type=internal",
56                 fmt.Sprintf("mac_in_use=%s", strings.ReplaceAll(hwAddr.String(), ":", "\\:")),
57                 fmt.Sprintf("mac=%s", strings.ReplaceAll(hwAddr.String(), ":", "\\:")),
58                 fmt.Sprintf("external_ids:iface-id=%s", nodeOVSInternalIntfName),
59         }
60         logrus.Infof("ovs-vsctl args - %v", ovsArgs)
61
62         //var out []byte
63         out, err := exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
64         if err != nil {
65                 logrus.Errorf("failure in creating Node OVS internal port - %s: %v - %q", nodeOVSInternalIntfName, err, string(out))
66                 return fmt.Errorf("failure in creating Node OVS internal port - %s: %v - %q", nodeOVSInternalIntfName, err, string(out))
67         }
68         logrus.Infof("ovs-vsctl args - %v output:%v", ovsArgs, string(out))
69
70         link, err := netlink.LinkByName(nodeOVSInternalIntfName)
71         if err != nil {
72                 logrus.Errorf("failed to get netlink for Node OVS internal port %s: %v", nodeOVSInternalIntfName, err)
73                 return fmt.Errorf("failed to get netlink for Node OVS internal port %s: %v", nodeOVSInternalIntfName, err)
74         }
75
76         if err := netlink.LinkSetUp(link); err != nil {
77                 logrus.Errorf("failed to set up netlink for Node OVS internal port %s: %v", nodeOVSInternalIntfName, err)
78                 return fmt.Errorf("failed to set up netlink for Node OVS internal port %s: %v", nodeOVSInternalIntfName, err)
79         }
80
81         addr, err := netlink.ParseAddr(nodeintfipaddr)
82         if err != nil {
83                 logrus.Errorf("failed to parse IP addr %s: %v", nodeintfipaddr, err)
84                 return fmt.Errorf("failed to parse IP addr %s: %v", nodeintfipaddr, err)
85         }
86         err = netlink.AddrAdd(link, addr)
87         if err != nil {
88                 logrus.Errorf("failed to parse IP addr %s: %v", nodeintfipaddr, err)
89                 return fmt.Errorf("failed to add IP addr %s to %s: %v", nodeintfipaddr, nodeOVSInternalIntfName, err)
90         }
91
92         err = network.SetupAndEnsureIPTables(network.MasqRules(nodeOVSInternalIntfName))
93         if err != nil {
94                 logrus.Errorf("failed to apply snat rule for %s: %v", nodeOVSInternalIntfName, err)
95                 return fmt.Errorf("failed to apply snat rule for %s: %v", nodeOVSInternalIntfName, err)
96         }
97
98         return nil
99 }
100
101 func setupInterface(netns ns.NetNS, containerID, ifName, macAddress, ipAddress, gatewayIP, defaultGateway string, idx, mtu int, isDefaultGW bool) (*current.Interface, *current.Interface, error) {
102         hostIface := &current.Interface{}
103         contIface := &current.Interface{}
104         var hostNet string
105
106         if defaultGateway == "false" && isDefaultGW == true && ifName == "eth0" {
107                 var err error
108                 hostNet, err = network.GetHostNetwork()
109                 if err != nil {
110                         log.Error(err, "Failed to get host network")
111                         return nil, nil, fmt.Errorf("failed to get host network: %v", err)
112                 }
113         }
114
115         var oldHostVethName string
116         err := netns.Do(func(hostNS ns.NetNS) error {
117                 // create the veth pair in the container and move host end into host netns
118                 hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
119                 if err != nil {
120                         return fmt.Errorf("failed to setup veth %s: %v", ifName, err)
121                         //return err
122                 }
123                 hostIface.Mac = hostVeth.HardwareAddr.String()
124                 contIface.Name = containerVeth.Name
125
126                 link, err := netlink.LinkByName(contIface.Name)
127                 if err != nil {
128                         return fmt.Errorf("failed to lookup %s: %v", contIface.Name, err)
129                 }
130
131                 hwAddr, err := net.ParseMAC(macAddress)
132                 if err != nil {
133                         return fmt.Errorf("failed to parse mac address for %s: %v", contIface.Name, err)
134                 }
135                 err = netlink.LinkSetHardwareAddr(link, hwAddr)
136                 if err != nil {
137                         return fmt.Errorf("failed to add mac address %s to %s: %v", macAddress, contIface.Name, err)
138                 }
139                 contIface.Mac = macAddress
140                 contIface.Sandbox = netns.Path()
141
142                 addr, err := netlink.ParseAddr(ipAddress)
143                 if err != nil {
144                         return err
145                 }
146                 err = netlink.AddrAdd(link, addr)
147                 if err != nil {
148                         return fmt.Errorf("failed to add IP addr %s to %s: %v", ipAddress, contIface.Name, err)
149                 }
150
151                 if defaultGateway == "true" {
152                         gw := net.ParseIP(gatewayIP)
153                         if gw == nil {
154                                 return fmt.Errorf("parse ip of gateway failed")
155                         }
156                         err = ip.AddRoute(nil, gw, link)
157                         if err != nil {
158                                 logrus.Errorf("ip.AddRoute failed %v gw %v link %v", err, gw, link)
159                                 return err
160                         }
161                 }
162
163                 if defaultGateway == "false" && isDefaultGW == true && ifName == "eth0" {
164                         stdout, stderr, err := ovn.RunIP("route", "add", hostNet, "via", gatewayIP)
165                         if err != nil && !strings.Contains(stderr, "RTNETLINK answers: File exists") {
166                                 logrus.Errorf("Failed to ip route add stout %s, stderr %s, err %v", stdout, stderr, err)
167                                 return fmt.Errorf("Failed to ip route add stout %s, stderr %s, err %v", stdout, stderr, err)
168                         }
169                 }
170
171                 oldHostVethName = hostVeth.Name
172
173                 return nil
174         })
175         if err != nil {
176                 return nil, nil, err
177         }
178
179         // rename the host end of veth pair
180         hostIface.Name = containerID[:14] + strconv.Itoa(idx)
181         if err := renameLink(oldHostVethName, hostIface.Name); err != nil {
182                 return nil, nil, fmt.Errorf("failed to rename %s to %s: %v", oldHostVethName, hostIface.Name, err)
183         }
184
185         return hostIface, contIface, nil
186 }
187
188 // ConfigureInterface sets up the container interface
189 var ConfigureInterface = func(containerNetns, containerID, ifName, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway string, idx, mtu int, isDefaultGW bool) ([]*current.Interface, error) {
190         netns, err := ns.GetNS(containerNetns)
191         if err != nil {
192                 return nil, fmt.Errorf("failed to open netns %q: %v", containerNetns, err)
193         }
194         defer netns.Close()
195
196         var ifaceID string
197         if interfaceName != "*" {
198                 ifaceID = fmt.Sprintf("%s_%s_%s", namespace, podName, interfaceName)
199         } else {
200                 ifaceID = fmt.Sprintf("%s_%s", namespace, podName)
201                 interfaceName = ifName
202         }
203         hostIface, contIface, err := setupInterface(netns, containerID, interfaceName, macAddress, ipAddress, gatewayIP, defaultGateway, idx, mtu, isDefaultGW)
204         if err != nil {
205                 return nil, err
206         }
207
208         ovsArgs := []string{
209                 "add-port", "br-int", hostIface.Name, "--", "set",
210                 "interface", hostIface.Name,
211                 fmt.Sprintf("external_ids:attached_mac=%s", macAddress),
212                 fmt.Sprintf("external_ids:iface-id=%s", ifaceID),
213                 fmt.Sprintf("external_ids:ip_address=%s", ipAddress),
214                 fmt.Sprintf("external_ids:sandbox=%s", containerID),
215         }
216
217         var out []byte
218         out, err = exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
219         if err != nil {
220                 return nil, fmt.Errorf("failure in plugging pod interface: %v\n  %q", err, string(out))
221         }
222
223         return []*current.Interface{hostIface, contIface}, nil
224 }
225
226 func setupRoute(netns ns.NetNS, dst, gw, dev string) error {
227         // Add Route to the namespace
228         err := netns.Do(func(_ ns.NetNS) error {
229                 dstAddr, dstAddrNet, _ := net.ParseCIDR(dst)
230                 ipNet := net.IPNet{IP: dstAddr, Mask: dstAddrNet.Mask}
231                 link, err := netlink.LinkByName(dev)
232                 err = ip.AddRoute(&ipNet, net.ParseIP(gw), link)
233                 if err != nil {
234                         logrus.Errorf("ip.AddRoute failed %v dst %v gw %v", err, dst, gw)
235                 }
236                 return err
237         })
238         return err
239 }
240
241 // ConfigureRoute sets up the container routes
242 var ConfigureRoute = func(containerNetns, dst, gw, dev string) error {
243         netns, err := ns.GetNS(containerNetns)
244         if err != nil {
245                 return fmt.Errorf("failed to open netns %q: %v", containerNetns, err)
246         }
247         defer netns.Close()
248         err = setupRoute(netns, dst, gw, dev)
249         return err
250 }
251
252 // PlatformSpecificCleanup deletes the OVS port
253 func PlatformSpecificCleanup(ifaceName string) (bool, error) {
254         done := false
255         ovsArgs := []string{
256                 "del-port", "br-int", ifaceName,
257         }
258         out, err := exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
259         if err != nil && !strings.Contains(string(out), "no port named") {
260                 // DEL should be idempotent; don't return an error just log it
261                 logrus.Warningf("failed to delete OVS port %s: %v\n  %q", ifaceName, err, string(out))
262                 done = true
263         }
264
265         return done, nil
266 }