blob: 4bb72615cf9d3a01822dce1f7c2b64cf0ac2de6e [file] [log] [blame]
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01001package main
2
3import (
Filip Tehlarf3ee2b62023-01-09 12:07:09 +01004 "fmt"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01005 "github.com/edwarnicke/exechelper"
Maros Ondrejicka7550dd22023-02-07 20:40:27 +01006 "strings"
7 "time"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01008
9 "go.fd.io/govpp"
10 "go.fd.io/govpp/api"
11 "go.fd.io/govpp/binapi/af_packet"
12 interfaces "go.fd.io/govpp/binapi/interface"
13 "go.fd.io/govpp/binapi/interface_types"
14 "go.fd.io/govpp/binapi/session"
Maros Ondrejicka7550dd22023-02-07 20:40:27 +010015 "go.fd.io/govpp/binapi/tapv2"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010016 "go.fd.io/govpp/binapi/vpe"
17 "go.fd.io/govpp/core"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010018)
19
20const vppConfigTemplate = `unix {
21 nodaemon
22 log %[1]s/var/log/vpp/vpp.log
23 full-coredump
24 cli-listen %[1]s%[2]s
25 runtime-dir %[1]s/var/run
26 gid vpp
27}
28
29api-trace {
30 on
31}
32
33api-segment {
34 gid vpp
35}
36
37socksvr {
Maros Ondrejicka7d7ab102023-02-14 12:56:49 +010038 socket-name %[1]s%[3]s
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010039}
40
41statseg {
42 socket-name %[1]s/var/run/vpp/stats.sock
43}
44
45plugins {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010046 plugin default { disable }
47
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010048 plugin unittest_plugin.so { enable }
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010049 plugin quic_plugin.so { enable }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010050 plugin af_packet_plugin.so { enable }
51 plugin hs_apps_plugin.so { enable }
52 plugin http_plugin.so { enable }
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010053}
54
55`
56
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010057const (
58 defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010059 defaultApiSocketFilePath = "/var/run/vpp/api.sock"
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010060)
61
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010062type VppInstance struct {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +010063 container *Container
64 additionalConfig Stanza
65 connection *core.Connection
66 apiChannel api.Channel
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010067}
68
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010069func (vpp *VppInstance) Suite() *HstSuite {
70 return vpp.container.suite
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010071}
72
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010073func (vpp *VppInstance) getCliSocket() string {
Maros Ondrejicka7d7ab102023-02-14 12:56:49 +010074 return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), defaultCliSocketFilePath)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010075}
76
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010077func (vpp *VppInstance) getRunDir() string {
78 return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
79}
80
81func (vpp *VppInstance) getLogDir() string {
82 return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
83}
84
85func (vpp *VppInstance) getEtcDir() string {
86 return vpp.container.GetContainerWorkDir() + "/etc/vpp"
87}
88
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010089func (vpp *VppInstance) start() error {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010090 // Create folders
91 containerWorkDir := vpp.container.GetContainerWorkDir()
92
93 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
94 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
95 vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
96
97 // Create startup.conf inside the container
Maros Ondrejicka7d7ab102023-02-14 12:56:49 +010098 configContent := fmt.Sprintf(
Maros Ondrejicka300f70d2023-02-21 10:53:20 +010099 vppConfigTemplate,
100 containerWorkDir,
101 defaultCliSocketFilePath,
102 defaultApiSocketFilePath,
103 )
Maros Ondrejicka7d7ab102023-02-14 12:56:49 +0100104 configContent += vpp.additionalConfig.ToString()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100105 startupFileName := vpp.getEtcDir() + "/startup.conf"
106 vpp.container.createFile(startupFileName, configContent)
107
108 // Start VPP
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100109 vpp.container.execServer("vpp -c " + startupFileName)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100110
111 // Connect to VPP and store the connection
112 sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
113 conn, connEv, err := govpp.AsyncConnect(
114 sockAddress,
115 core.DefaultMaxReconnectAttempts,
116 core.DefaultReconnectInterval)
117 if err != nil {
118 fmt.Println("async connect error: ", err)
119 }
120 vpp.connection = conn
121
122 // ... wait for Connected event
123 e := <-connEv
124 if e.State != core.Connected {
125 fmt.Println("connecting to VPP failed: ", e.Error)
126 }
127
128 // ... check compatibility of used messages
129 ch, err := conn.NewAPIChannel()
130 if err != nil {
131 fmt.Println("creating channel failed: ", err)
132 }
133 if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
134 fmt.Println("compatibility error: ", err)
135 }
136 if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
137 fmt.Println("compatibility error: ", err)
138 }
139 vpp.apiChannel = ch
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100140
141 return nil
142}
143
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100144func (vpp *VppInstance) vppctl(command string, arguments ...any) string {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100145 vppCliCommand := fmt.Sprintf(command, arguments...)
146 containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
147 vpp.container.name, vpp.getCliSocket(), vppCliCommand)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100148 vpp.Suite().log(containerExecCommand)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100149 output, err := exechelper.CombinedOutput(containerExecCommand)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100150 vpp.Suite().assertNil(err)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100151
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100152 return string(output)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100153}
154
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100155func (vpp *VppInstance) waitForApp(appName string, timeout int) error {
156 for i := 0; i < timeout; i++ {
157 o := vpp.vppctl("show app")
158 if strings.Contains(o, appName) {
159 return nil
160 }
161 time.Sleep(1 * time.Second)
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100162 }
Maros Ondrejicka7d7ab102023-02-14 12:56:49 +0100163 return fmt.Errorf("timeout while waiting for app '%s'", appName)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100164}
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100165
166func (vpp *VppInstance) createAfPacket(
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100167 netInterface NetInterface,
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100168) (interface_types.InterfaceIndex, error) {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100169 veth := netInterface.(*NetworkInterfaceVeth)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100170
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100171 createReq := &af_packet.AfPacketCreateV2{
172 UseRandomHwAddr: true,
173 HostIfName: veth.Name(),
174 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100175 if veth.HwAddress() != (MacAddress{}) {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100176 createReq.UseRandomHwAddr = false
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100177 createReq.HwAddr = veth.HwAddress()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100178 }
179 createReply := &af_packet.AfPacketCreateV2Reply{}
180
181 if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
182 return 0, err
183 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100184 veth.SetIndex(createReply.SwIfIndex)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100185
186 // Set to up
187 upReq := &interfaces.SwInterfaceSetFlags{
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100188 SwIfIndex: veth.Index(),
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100189 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
190 }
191 upReply := &interfaces.SwInterfaceSetFlagsReply{}
192
193 if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
194 return 0, err
195 }
196
197 // Add address
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100198 if veth.AddressWithPrefix() == (AddressWithPrefix{}) {
199 var err error
200 var ip4Address string
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100201 if ip4Address, err = veth.addresser.NewIp4Address(veth.peerNetworkNumber); err == nil {
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100202 veth.SetAddress(ip4Address)
203 } else {
204 return 0, err
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100205 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100206 }
207 addressReq := &interfaces.SwInterfaceAddDelAddress{
208 IsAdd: true,
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100209 SwIfIndex: veth.Index(),
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100210 Prefix: veth.AddressWithPrefix(),
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100211 }
212 addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
213
214 if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
215 return 0, err
216 }
217
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100218 return veth.Index(), nil
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100219}
220
221func (vpp *VppInstance) addAppNamespace(
222 secret uint64,
223 ifx interface_types.InterfaceIndex,
224 namespaceId string,
225) error {
226 req := &session.AppNamespaceAddDelV2{
227 Secret: secret,
228 SwIfIndex: ifx,
229 NamespaceID: namespaceId,
230 }
231 reply := &session.AppNamespaceAddDelV2Reply{}
232
233 if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
234 return err
235 }
236
237 sessionReq := &session.SessionEnableDisable{
238 IsEnable: true,
239 }
240 sessionReply := &session.SessionEnableDisableReply{}
241
242 if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
243 return err
244 }
245
246 return nil
247}
248
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100249func (vpp *VppInstance) createTap(
250 hostInterfaceName string,
251 hostIp4Address IP4AddressWithPrefix,
252 vppIp4Address AddressWithPrefix,
253) error {
254 createTapReq := &tapv2.TapCreateV2{
255 HostIfNameSet: true,
256 HostIfName: hostInterfaceName,
257 HostIP4PrefixSet: true,
258 HostIP4Prefix: hostIp4Address,
259 }
260 createTapReply := &tapv2.TapCreateV2Reply{}
261
262 // Create tap interface
263 if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil {
264 return err
265 }
266
267 // Add address
268 addAddressReq := &interfaces.SwInterfaceAddDelAddress{
269 IsAdd: true,
270 SwIfIndex: createTapReply.SwIfIndex,
271 Prefix: vppIp4Address,
272 }
273 addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{}
274
275 if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil {
276 return err
277 }
278
279 // Set interface to up
280 upReq := &interfaces.SwInterfaceSetFlags{
281 SwIfIndex: createTapReply.SwIfIndex,
282 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
283 }
284 upReply := &interfaces.SwInterfaceSetFlagsReply{}
285
286 if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
287 return err
288 }
289
290 return nil
291}
292
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100293func (vpp *VppInstance) disconnect() {
294 vpp.connection.Disconnect()
295 vpp.apiChannel.Close()
296}