cilium

Форк
0
274 строки · 8.6 Кб
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Cilium
3

4
package genericveth
5

6
import (
7
	"context"
8
	"errors"
9
	"fmt"
10

11
	cniTypes "github.com/containernetworking/cni/pkg/types"
12
	cniTypesVer "github.com/containernetworking/cni/pkg/types/100"
13
	cniVersion "github.com/containernetworking/cni/pkg/version"
14
	"github.com/containernetworking/plugins/pkg/ns"
15
	"github.com/sirupsen/logrus"
16
	"github.com/vishvananda/netlink"
17

18
	"github.com/cilium/cilium/api/v1/models"
19
	"github.com/cilium/cilium/pkg/client"
20
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
21
	"github.com/cilium/cilium/pkg/logging/logfields"
22
	"github.com/cilium/cilium/pkg/netns"
23
	chainingapi "github.com/cilium/cilium/plugins/cilium-cni/chaining/api"
24
	"github.com/cilium/cilium/plugins/cilium-cni/lib"
25
	"github.com/cilium/cilium/plugins/cilium-cni/types"
26
)
27

28
type GenericVethChainer struct{}
29

30
func (f *GenericVethChainer) Add(ctx context.Context, pluginCtx chainingapi.PluginContext, cli *client.Client) (res *cniTypesVer.Result, err error) {
31
	err = cniVersion.ParsePrevResult(&pluginCtx.NetConf.NetConf)
32
	if err != nil {
33
		err = fmt.Errorf("unable to understand network config: %s", err)
34
		return
35
	}
36

37
	var prevRes *cniTypesVer.Result
38
	prevRes, err = cniTypesVer.NewResultFromResult(pluginCtx.NetConf.PrevResult)
39
	if err != nil {
40
		err = fmt.Errorf("unable to get previous network result: %s", err)
41
		return
42
	}
43

44
	defer func() {
45
		if err != nil {
46
			pluginCtx.Logger.WithError(err).
47
				WithFields(logrus.Fields{"cni-pre-result": pluginCtx.NetConf.PrevResult}).
48
				Errorf("Unable to create endpoint")
49
		}
50
	}()
51
	var (
52
		hostMac, vethHostName, vethLXCMac, vethLXCName, vethIP, vethIPv6 string
53
		vethHostIdx, peerIndex                                           int
54
		peer                                                             netlink.Link
55
		netNs                                                            ns.NetNS
56
	)
57

58
	netNs, err = ns.GetNS(pluginCtx.Args.Netns)
59
	if err != nil {
60
		err = fmt.Errorf("failed to open netns %q: %s", pluginCtx.Args.Netns, err)
61
		return
62
	}
63
	defer netNs.Close()
64

65
	if err = netNs.Do(func(_ ns.NetNS) error {
66
		links, err := netlink.LinkList()
67
		if err != nil {
68
			return err
69
		}
70

71
		linkFound := false
72
		for _, link := range links {
73
			pluginCtx.Logger.Debugf("Found interface in container %+v", link.Attrs())
74

75
			if link.Type() != "veth" {
76
				continue
77
			}
78

79
			vethLXCMac = link.Attrs().HardwareAddr.String()
80
			vethLXCName = link.Attrs().Name
81

82
			veth, ok := link.(*netlink.Veth)
83
			if !ok {
84
				return fmt.Errorf("link %s is not a veth interface", vethHostName)
85
			}
86

87
			peerIndex, err = netlink.VethPeerIndex(veth)
88
			if err != nil {
89
				return fmt.Errorf("unable to retrieve index of veth peer %s: %s", vethHostName, err)
90
			}
91

92
			addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
93
			if err == nil && len(addrs) > 0 {
94
				vethIP = addrs[0].IPNet.IP.String()
95
			} else if err != nil {
96
				pluginCtx.Logger.WithError(err).WithFields(logrus.Fields{
97
					logfields.Interface: link.Attrs().Name}).Warn("No valid IPv4 address found")
98
			}
99

100
			addrsv6, err := netlink.AddrList(link, netlink.FAMILY_V6)
101
			if err == nil && len(addrsv6) > 0 {
102
				vethIPv6 = addrsv6[0].IPNet.IP.String()
103
			} else if err != nil {
104
				pluginCtx.Logger.WithError(err).WithFields(logrus.Fields{
105
					logfields.Interface: link.Attrs().Name}).Warn("No valid IPv6 address found")
106
			}
107

108
			linkFound = true
109
			break
110
		}
111

112
		if !linkFound {
113
			return errors.New("no link found inside container")
114
		}
115

116
		if pluginCtx.NetConf.EnableRouteMTU {
117
			routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
118
			if err != nil {
119
				err = fmt.Errorf("unable to list the IPv4 routes: %s", err.Error())
120
				return err
121
			}
122
			for _, rt := range routes {
123
				if rt.MTU != int(pluginCtx.CiliumConf.RouteMTU) {
124
					rt.MTU = int(pluginCtx.CiliumConf.RouteMTU)
125
					err = netlink.RouteReplace(&rt)
126
					if err != nil {
127
						err = fmt.Errorf("unable to replace the mtu %d for the route %s: %s", rt.MTU, rt.String(), err.Error())
128
						return err
129
					}
130
				}
131
			}
132

133
			routes, err = netlink.RouteList(nil, netlink.FAMILY_V6)
134
			if err != nil {
135
				err = fmt.Errorf("unable to list the IPv6 routes: %s", err.Error())
136
				return err
137
			}
138
			for _, rt := range routes {
139
				if rt.MTU != int(pluginCtx.CiliumConf.RouteMTU) {
140
					rt.MTU = int(pluginCtx.CiliumConf.RouteMTU)
141
					err = netlink.RouteReplace(&rt)
142
					if err != nil {
143
						err = fmt.Errorf("unable to replace the mtu %d for the route %s: %s", rt.MTU, rt.String(), err.Error())
144
						return err
145
					}
146
				}
147
			}
148
		}
149

150
		return nil
151
	}); err != nil {
152
		return
153
	}
154

155
	peer, err = netlink.LinkByIndex(peerIndex)
156
	if err != nil {
157
		err = fmt.Errorf("unable to lookup link %d: %s", peerIndex, err)
158
		return
159
	}
160

161
	hostMac = peer.Attrs().HardwareAddr.String()
162
	vethHostName = peer.Attrs().Name
163
	vethHostIdx = peer.Attrs().Index
164

165
	switch {
166
	case vethHostName == "":
167
		err = errors.New("unable to determine name of veth pair on the host side")
168
		return
169
	case vethLXCMac == "":
170
		err = errors.New("unable to determine MAC address of veth pair on the container side")
171
		return
172
	case vethIP == "" && vethIPv6 == "":
173
		err = errors.New("unable to determine IP address of the container")
174
		return
175
	case vethHostIdx == 0:
176
		err = errors.New("unable to determine index interface of veth pair on the host side")
177
		return
178
	}
179

180
	var disabled = false
181
	ep := &models.EndpointChangeRequest{
182
		Addressing: &models.AddressPair{
183
			IPV4: vethIP,
184
			IPV6: vethIPv6,
185
		},
186
		ContainerID:            pluginCtx.Args.ContainerID,
187
		State:                  models.EndpointStateWaitingDashForDashIdentity.Pointer(),
188
		HostMac:                hostMac,
189
		InterfaceIndex:         int64(vethHostIdx),
190
		Mac:                    vethLXCMac,
191
		InterfaceName:          vethHostName,
192
		ContainerInterfaceName: vethLXCName,
193
		K8sPodName:             string(pluginCtx.CniArgs.K8S_POD_NAME),
194
		K8sNamespace:           string(pluginCtx.CniArgs.K8S_POD_NAMESPACE),
195
		SyncBuildEndpoint:      true,
196
		DatapathConfiguration: &models.EndpointDatapathConfiguration{
197
			// aws-cni requires ARP passthrough between Linux and
198
			// the pod
199
			RequireArpPassthrough: true,
200

201
			// The route is pointing directly into the veth of the
202
			// pod, install a host-facing egress program to
203
			// implement ingress policy and to provide reverse NAT
204
			RequireEgressProg: true,
205

206
			// The IP is managed by the aws-cni plugin, no need for
207
			// Cilium to manage any aspect of addressing
208
			ExternalIpam: true,
209

210
			// All routing is performed by the Linux stack
211
			RequireRouting: &disabled,
212
		},
213
	}
214

215
	scopedLog := pluginCtx.Logger.WithFields(logrus.Fields{
216
		logfields.ContainerID:        ep.ContainerID,
217
		logfields.ContainerInterface: ep.ContainerInterfaceName,
218
	})
219
	var newEp *models.Endpoint
220
	newEp, err = cli.EndpointCreate(ep)
221
	if err != nil {
222
		scopedLog.WithError(err).Warn("Unable to create endpoint")
223
		err = fmt.Errorf("unable to create endpoint: %s", err)
224
		return
225
	}
226
	if newEp != nil && newEp.Status != nil && newEp.Status.Networking != nil && newEp.Status.Networking.Mac != "" &&
227
		newEp.Status.Networking.Mac != vethLXCMac {
228
		err = netns.ReplaceMacAddressWithLinkName(netNs, vethLXCName, newEp.Status.Networking.Mac)
229
		if err != nil {
230
			err = fmt.Errorf("unable to set MAC address on interface %s: %w", vethLXCName, err)
231
			return
232
		}
233
		for i := range prevRes.Interfaces {
234
			if prevRes.Interfaces[i].Name == vethLXCName {
235
				prevRes.Interfaces[i].Mac = newEp.Status.Networking.Mac
236
			}
237
		}
238
	}
239
	scopedLog.Debug("Endpoint successfully created")
240

241
	res = prevRes
242

243
	return
244
}
245

246
func (f *GenericVethChainer) Delete(ctx context.Context, pluginCtx chainingapi.PluginContext, delClient *lib.DeletionFallbackClient) (err error) {
247
	id := endpointid.NewCNIAttachmentID(pluginCtx.Args.ContainerID, pluginCtx.Args.IfName)
248
	if err := delClient.EndpointDelete(id); err != nil {
249
		pluginCtx.Logger.WithError(err).Warning("Errors encountered while deleting endpoint")
250
	}
251
	return nil
252
}
253

254
func (f *GenericVethChainer) Check(ctx context.Context, pluginCtx chainingapi.PluginContext, cli *client.Client) error {
255
	// Just confirm that the endpoint is healthy
256
	eID := endpointid.NewCNIAttachmentID(pluginCtx.Args.ContainerID, pluginCtx.Args.IfName)
257
	pluginCtx.Logger.Debugf("Asking agent for healthz for %s", eID)
258
	epHealth, err := cli.EndpointHealthGet(eID)
259
	if err != nil {
260
		return cniTypes.NewError(types.CniErrHealthzGet, "HealthzFailed",
261
			fmt.Sprintf("failed to retrieve container health: %s", err))
262
	}
263

264
	if epHealth.OverallHealth == models.EndpointHealthStatusFailure {
265
		return cniTypes.NewError(types.CniErrUnhealthy, "Unhealthy",
266
			"container is unhealthy in agent")
267
	}
268
	pluginCtx.Logger.Debugf("Container %s:%s has a healthy agent endpoint", pluginCtx.Args.ContainerID, pluginCtx.Args.IfName)
269
	return nil
270
}
271

272
func init() {
273
	chainingapi.Register("generic-veth", &GenericVethChainer{})
274
}
275

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.