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