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