Adding cnishim and cniserver
[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         "strconv"
10         "strings"
11
12         "github.com/sirupsen/logrus"
13         "github.com/containernetworking/cni/pkg/types/current"
14         "github.com/containernetworking/plugins/pkg/ip"
15         "github.com/containernetworking/plugins/pkg/ns"
16         "github.com/vishvananda/netlink"
17 )
18
19 func renameLink(curName, newName string) error {
20         link, err := netlink.LinkByName(curName)
21         if err != nil {
22                 return err
23         }
24
25         if err := netlink.LinkSetDown(link); err != nil {
26                 return err
27         }
28         if err := netlink.LinkSetName(link, newName); err != nil {
29                 return err
30         }
31         if err := netlink.LinkSetUp(link); err != nil {
32                 return err
33         }
34
35         return nil
36 }
37
38 func setupInterface(netns ns.NetNS, containerID, ifName, macAddress, ipAddress, gatewayIP, defaultGateway string, idx, mtu int) (*current.Interface, *current.Interface, error) {
39         hostIface := &current.Interface{}
40         contIface := &current.Interface{}
41
42         var oldHostVethName string
43         err := netns.Do(func(hostNS ns.NetNS) error {
44                 // create the veth pair in the container and move host end into host netns
45                 hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
46                 if err != nil {
47                         return fmt.Errorf("failed to setup veth %s: %v", ifName, err)
48                         //return err
49                 }
50                 hostIface.Mac = hostVeth.HardwareAddr.String()
51                 contIface.Name = containerVeth.Name
52
53                 link, err := netlink.LinkByName(contIface.Name)
54                 if err != nil {
55                         return fmt.Errorf("failed to lookup %s: %v", contIface.Name, err)
56                 }
57
58                 hwAddr, err := net.ParseMAC(macAddress)
59                 if err != nil {
60                         return fmt.Errorf("failed to parse mac address for %s: %v", contIface.Name, err)
61                 }
62                 err = netlink.LinkSetHardwareAddr(link, hwAddr)
63                 if err != nil {
64                         return fmt.Errorf("failed to add mac address %s to %s: %v", macAddress, contIface.Name, err)
65                 }
66                 contIface.Mac = macAddress
67                 contIface.Sandbox = netns.Path()
68
69                 addr, err := netlink.ParseAddr(ipAddress)
70                 if err != nil {
71                         return err
72                 }
73                 err = netlink.AddrAdd(link, addr)
74                 if err != nil {
75                         return fmt.Errorf("failed to add IP addr %s to %s: %v", ipAddress, contIface.Name, err)
76                 }
77
78                 if defaultGateway == "true" {
79                         gw := net.ParseIP(gatewayIP)
80                         if gw == nil {
81                                 return fmt.Errorf("parse ip of gateway failed")
82                         }
83                         err = ip.AddRoute(nil, gw, link)
84                         if err != nil {
85                                 logrus.Errorf("ip.AddRoute failed %v gw %v link %v", err, gw, link)
86                                 return err
87                         }
88                 }
89                 oldHostVethName = hostVeth.Name
90
91                 return nil
92         })
93         if err != nil {
94                 return nil, nil, err
95         }
96
97         // rename the host end of veth pair
98         hostIface.Name = containerID[:14] + strconv.Itoa(idx)
99         if err := renameLink(oldHostVethName, hostIface.Name); err != nil {
100                 return nil, nil, fmt.Errorf("failed to rename %s to %s: %v", oldHostVethName, hostIface.Name, err)
101         }
102
103         return hostIface, contIface, nil
104 }
105
106 // ConfigureInterface sets up the container interface
107 var ConfigureInterface = func(containerNetns, containerID, ifName, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway string, idx, mtu int) ([]*current.Interface, error) {
108         netns, err := ns.GetNS(containerNetns)
109         if err != nil {
110                 return nil, fmt.Errorf("failed to open netns %q: %v", containerNetns, err)
111         }
112         defer netns.Close()
113
114         var ifaceID string
115         if interfaceName != "*" {
116                 ifaceID = fmt.Sprintf("%s_%s_%s", namespace, podName, interfaceName)
117         } else {
118                 ifaceID = fmt.Sprintf("%s_%s", namespace, podName)
119                 interfaceName = ifName
120                 defaultGateway = "true"
121         }
122         hostIface, contIface, err := setupInterface(netns, containerID, interfaceName, macAddress, ipAddress, gatewayIP, defaultGateway, idx, mtu)
123         if err != nil {
124                 return nil, err
125         }
126
127         ovsArgs := []string{
128                 "add-port", "br-int", hostIface.Name, "--", "set",
129                 "interface", hostIface.Name,
130                 fmt.Sprintf("external_ids:attached_mac=%s", macAddress),
131                 fmt.Sprintf("external_ids:iface-id=%s", ifaceID),
132                 fmt.Sprintf("external_ids:ip_address=%s", ipAddress),
133                 fmt.Sprintf("external_ids:sandbox=%s", containerID),
134         }
135
136         var out []byte
137         out, err = exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
138         if err != nil {
139                 return nil, fmt.Errorf("failure in plugging pod interface: %v\n  %q", err, string(out))
140         }
141
142         return []*current.Interface{hostIface, contIface}, nil
143 }
144
145 func setupRoute(netns ns.NetNS, dst, gw, dev string) error {
146         // Add Route to the namespace
147         err := netns.Do(func(_ ns.NetNS) error {
148                 dstAddr, dstAddrNet, _ := net.ParseCIDR(dst)
149                 ipNet := net.IPNet{IP: dstAddr, Mask: dstAddrNet.Mask}
150                 link, err := netlink.LinkByName(dev)
151                 err = ip.AddRoute(&ipNet, net.ParseIP(gw), link)
152                 if err != nil {
153                         logrus.Errorf("ip.AddRoute failed %v dst %v gw %v", err, dst, gw)
154                 }
155                 return err
156         })
157         return err
158 }
159
160 // ConfigureRoute sets up the container routes
161 var ConfigureRoute = func(containerNetns, dst, gw, dev string) error {
162         netns, err := ns.GetNS(containerNetns)
163         if err != nil {
164                 return fmt.Errorf("failed to open netns %q: %v", containerNetns, err)
165         }
166         defer netns.Close()
167         err = setupRoute(netns, dst, gw, dev)
168         return err
169 }
170
171 // PlatformSpecificCleanup deletes the OVS port
172 func PlatformSpecificCleanup(ifaceName string) (bool, error) {
173         done := false
174         ovsArgs := []string{
175                 "del-port", "br-int", ifaceName,
176         }
177         out, err := exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
178         if err != nil && !strings.Contains(string(out), "no port named") {
179                 // DEL should be idempotent; don't return an error just log it
180                 logrus.Warningf("failed to delete OVS port %s: %v\n  %q", ifaceName, err, string(out))
181                 done = true
182         }
183
184         return done, nil
185 }