blob: 257798a4f3dd34c4edd37a386161ad670f5ce890 [file] [log] [blame]
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01001package main
2
3import (
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01004 "encoding/json"
Filip Tehlarf3ee2b62023-01-09 12:07:09 +01005 "fmt"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01006 "github.com/edwarnicke/exechelper"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01007
8 "go.fd.io/govpp"
9 "go.fd.io/govpp/api"
10 "go.fd.io/govpp/binapi/af_packet"
11 interfaces "go.fd.io/govpp/binapi/interface"
12 "go.fd.io/govpp/binapi/interface_types"
13 "go.fd.io/govpp/binapi/session"
14 "go.fd.io/govpp/binapi/vpe"
15 "go.fd.io/govpp/core"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010016)
17
18const vppConfigTemplate = `unix {
19 nodaemon
20 log %[1]s/var/log/vpp/vpp.log
21 full-coredump
22 cli-listen %[1]s%[2]s
23 runtime-dir %[1]s/var/run
24 gid vpp
25}
26
27api-trace {
28 on
29}
30
31api-segment {
32 gid vpp
33}
34
35socksvr {
36 socket-name %[1]s/var/run/vpp/api.sock
37}
38
39statseg {
40 socket-name %[1]s/var/run/vpp/stats.sock
41}
42
43plugins {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010044 plugin default { disable }
45
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010046 plugin unittest_plugin.so { enable }
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010047 plugin quic_plugin.so { enable }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010048 plugin af_packet_plugin.so { enable }
49 plugin hs_apps_plugin.so { enable }
50 plugin http_plugin.so { enable }
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010051}
52
53`
54
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010055const (
56 defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010057 defaultApiSocketFilePath = "/var/run/vpp/api.sock"
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010058)
59
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010060type VppInstance struct {
Filip Tehlarf3ee2b62023-01-09 12:07:09 +010061 container *Container
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010062 config *VppConfig
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010063 actionFuncName string
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010064 connection *core.Connection
65 apiChannel api.Channel
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010066}
67
68type VppConfig struct {
Filip Tehlarf3ee2b62023-01-09 12:07:09 +010069 Variant string
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010070 CliSocketFilePath string
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010071 additionalConfig Stanza
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010072}
73
74func (vc *VppConfig) getTemplate() string {
75 return fmt.Sprintf(vppConfigTemplate, "%[1]s", vc.CliSocketFilePath)
76}
77
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010078func (vpp *VppInstance) Suite() *HstSuite {
79 return vpp.container.suite
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010080}
81
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010082func (vpp *VppInstance) setVppProxy() {
83 vpp.actionFuncName = "ConfigureVppProxy"
84}
85
86func (vpp *VppInstance) setEnvoyProxy() {
87 vpp.actionFuncName = "ConfigureEnvoyProxy"
88}
89
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010090func (vpp *VppInstance) setCliSocket(filePath string) {
91 vpp.config.CliSocketFilePath = filePath
92}
93
94func (vpp *VppInstance) getCliSocket() string {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010095 return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), vpp.config.CliSocketFilePath)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010096}
97
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010098func (vpp *VppInstance) getRunDir() string {
99 return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
100}
101
102func (vpp *VppInstance) getLogDir() string {
103 return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
104}
105
106func (vpp *VppInstance) getEtcDir() string {
107 return vpp.container.GetContainerWorkDir() + "/etc/vpp"
108}
109
110func (vpp *VppInstance) legacyStart() error {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100111 serializedConfig, err := serializeVppConfig(vpp.config)
Filip Tehlar56160412023-01-25 13:56:38 +0100112 if err != nil {
113 return fmt.Errorf("serialize vpp config: %v", err)
114 }
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100115 args := fmt.Sprintf("%s '%s'", vpp.actionFuncName, serializedConfig)
116 _, err = vpp.container.execAction(args)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100117 if err != nil {
118 return fmt.Errorf("vpp start failed: %s", err)
119 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100120 return nil
121}
122
123func (vpp *VppInstance) start() error {
124 if vpp.actionFuncName != "" {
125 return vpp.legacyStart()
126 }
127
128 // Create folders
129 containerWorkDir := vpp.container.GetContainerWorkDir()
130
131 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
132 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
133 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
134
135 // Create startup.conf inside the container
136 configContent := fmt.Sprintf(vppConfigTemplate, containerWorkDir, vpp.config.CliSocketFilePath)
137 configContent += vpp.config.additionalConfig.ToString()
138 startupFileName := vpp.getEtcDir() + "/startup.conf"
139 vpp.container.createFile(startupFileName, configContent)
140
141 // Start VPP
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100142 vpp.container.execServer("vpp -c " + startupFileName)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100143
144 // Connect to VPP and store the connection
145 sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
146 conn, connEv, err := govpp.AsyncConnect(
147 sockAddress,
148 core.DefaultMaxReconnectAttempts,
149 core.DefaultReconnectInterval)
150 if err != nil {
151 fmt.Println("async connect error: ", err)
152 }
153 vpp.connection = conn
154
155 // ... wait for Connected event
156 e := <-connEv
157 if e.State != core.Connected {
158 fmt.Println("connecting to VPP failed: ", e.Error)
159 }
160
161 // ... check compatibility of used messages
162 ch, err := conn.NewAPIChannel()
163 if err != nil {
164 fmt.Println("creating channel failed: ", err)
165 }
166 if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
167 fmt.Println("compatibility error: ", err)
168 }
169 if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
170 fmt.Println("compatibility error: ", err)
171 }
172 vpp.apiChannel = ch
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100173
174 return nil
175}
176
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100177func (vpp *VppInstance) vppctl(command string, arguments ...any) string {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100178 vppCliCommand := fmt.Sprintf(command, arguments...)
179 containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
180 vpp.container.name, vpp.getCliSocket(), vppCliCommand)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100181 vpp.Suite().log(containerExecCommand)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100182 output, err := exechelper.CombinedOutput(containerExecCommand)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100183 vpp.Suite().assertNil(err)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100184
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100185 return string(output)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100186}
187
188func NewVppInstance(c *Container) *VppInstance {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100189 vppConfig := new(VppConfig)
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100190 vppConfig.CliSocketFilePath = defaultCliSocketFilePath
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100191 vpp := new(VppInstance)
192 vpp.container = c
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100193 vpp.config = vppConfig
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100194 return vpp
195}
196
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100197func serializeVppConfig(vppConfig *VppConfig) (string, error) {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100198 serializedConfig, err := json.Marshal(vppConfig)
199 if err != nil {
200 return "", fmt.Errorf("vpp start failed: serializing configuration failed: %s", err)
201 }
202 return string(serializedConfig), nil
203}
204
205func deserializeVppConfig(input string) (VppConfig, error) {
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100206 var vppConfig VppConfig
207 err := json.Unmarshal([]byte(input), &vppConfig)
208 if err != nil {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100209 // Since input is not a valid JSON it is going be used as a variant value
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100210 // for compatibility reasons
211 vppConfig.Variant = input
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100212 vppConfig.CliSocketFilePath = defaultCliSocketFilePath
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100213 }
214 return vppConfig, nil
215}
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100216
217func (vpp *VppInstance) createAfPacket(
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100218 netInterface NetInterface,
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100219) (interface_types.InterfaceIndex, error) {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100220 var veth *NetworkInterfaceVeth
221 veth = netInterface.(*NetworkInterfaceVeth)
222
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100223 createReq := &af_packet.AfPacketCreateV2{
224 UseRandomHwAddr: true,
225 HostIfName: veth.Name(),
226 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100227 if veth.HwAddress() != (MacAddress{}) {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100228 createReq.UseRandomHwAddr = false
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100229 createReq.HwAddr = veth.HwAddress()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100230 }
231 createReply := &af_packet.AfPacketCreateV2Reply{}
232
233 if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
234 return 0, err
235 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100236 veth.SetIndex(createReply.SwIfIndex)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100237
238 // Set to up
239 upReq := &interfaces.SwInterfaceSetFlags{
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100240 SwIfIndex: veth.Index(),
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100241 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
242 }
243 upReply := &interfaces.SwInterfaceSetFlagsReply{}
244
245 if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
246 return 0, err
247 }
248
249 // Add address
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100250 if veth.Ip4AddressWithPrefix() == (AddressWithPrefix{}) {
251 if veth.peerNetworkNamespace != "" {
252 ip4Address, err := veth.addresser.
253 NewIp4AddressWithNamespace(veth.peerNetworkNamespace)
254 if err == nil {
255 veth.SetAddress(ip4Address)
256 } else {
257 return 0, err
258 }
259 } else {
260 ip4Address, err := veth.addresser.
261 NewIp4Address()
262 if err == nil {
263 veth.SetAddress(ip4Address)
264 } else {
265 return 0, err
266 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100267 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100268 }
269 addressReq := &interfaces.SwInterfaceAddDelAddress{
270 IsAdd: true,
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100271 SwIfIndex: veth.Index(),
272 Prefix: veth.Ip4AddressWithPrefix(),
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100273 }
274 addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
275
276 if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
277 return 0, err
278 }
279
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100280 return veth.Index(), nil
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100281}
282
283func (vpp *VppInstance) addAppNamespace(
284 secret uint64,
285 ifx interface_types.InterfaceIndex,
286 namespaceId string,
287) error {
288 req := &session.AppNamespaceAddDelV2{
289 Secret: secret,
290 SwIfIndex: ifx,
291 NamespaceID: namespaceId,
292 }
293 reply := &session.AppNamespaceAddDelV2Reply{}
294
295 if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
296 return err
297 }
298
299 sessionReq := &session.SessionEnableDisable{
300 IsEnable: true,
301 }
302 sessionReply := &session.SessionEnableDisableReply{}
303
304 if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
305 return err
306 }
307
308 return nil
309}
310
311func (vpp *VppInstance) disconnect() {
312 vpp.connection.Disconnect()
313 vpp.apiChannel.Close()
314}