5 "github.com/edwarnicke/exechelper"
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"
15 "go.fd.io/govpp/binapi/tapv2"
16 "go.fd.io/govpp/binapi/vpe"
20 const vppConfigTemplate = `unix {
22 log %[1]s/var/log/vpp/vpp.log
25 runtime-dir %[1]s/var/run
38 socket-name %[1]s%[3]s
42 socket-name %[1]s/var/run/vpp/stats.sock
46 plugin default { disable }
48 plugin unittest_plugin.so { enable }
49 plugin quic_plugin.so { enable }
50 plugin af_packet_plugin.so { enable }
51 plugin hs_apps_plugin.so { enable }
52 plugin http_plugin.so { enable }
58 defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
59 defaultApiSocketFilePath = "/var/run/vpp/api.sock"
62 type VppInstance struct {
64 additionalConfig Stanza
65 connection *core.Connection
66 apiChannel api.Channel
69 func (vpp *VppInstance) Suite() *HstSuite {
70 return vpp.container.suite
73 func (vpp *VppInstance) getCliSocket() string {
74 return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), defaultCliSocketFilePath)
77 func (vpp *VppInstance) getRunDir() string {
78 return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
81 func (vpp *VppInstance) getLogDir() string {
82 return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
85 func (vpp *VppInstance) getEtcDir() string {
86 return vpp.container.GetContainerWorkDir() + "/etc/vpp"
89 func (vpp *VppInstance) start() error {
91 containerWorkDir := vpp.container.GetContainerWorkDir()
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())
97 // Create startup.conf inside the container
98 configContent := fmt.Sprintf(
101 defaultCliSocketFilePath,
102 defaultApiSocketFilePath,
104 configContent += vpp.additionalConfig.ToString()
105 startupFileName := vpp.getEtcDir() + "/startup.conf"
106 vpp.container.createFile(startupFileName, configContent)
109 vpp.container.execServer("vpp -c " + startupFileName)
111 // Connect to VPP and store the connection
112 sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
113 conn, connEv, err := govpp.AsyncConnect(
115 core.DefaultMaxReconnectAttempts,
116 core.DefaultReconnectInterval)
118 fmt.Println("async connect error: ", err)
120 vpp.connection = conn
122 // ... wait for Connected event
124 if e.State != core.Connected {
125 fmt.Println("connecting to VPP failed: ", e.Error)
128 // ... check compatibility of used messages
129 ch, err := conn.NewAPIChannel()
131 fmt.Println("creating channel failed: ", err)
133 if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
134 fmt.Println("compatibility error: ", err)
136 if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
137 fmt.Println("compatibility error: ", err)
144 func (vpp *VppInstance) vppctl(command string, arguments ...any) string {
145 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)
148 vpp.Suite().log(containerExecCommand)
149 output, err := exechelper.CombinedOutput(containerExecCommand)
150 vpp.Suite().assertNil(err)
152 return string(output)
155 func (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) {
161 time.Sleep(1 * time.Second)
163 return fmt.Errorf("timeout while waiting for app '%s'", appName)
166 func (vpp *VppInstance) createAfPacket(
167 netInterface NetInterface,
168 ) (interface_types.InterfaceIndex, error) {
169 veth := netInterface.(*NetworkInterfaceVeth)
171 createReq := &af_packet.AfPacketCreateV2{
172 UseRandomHwAddr: true,
173 HostIfName: veth.Name(),
175 if veth.HwAddress() != (MacAddress{}) {
176 createReq.UseRandomHwAddr = false
177 createReq.HwAddr = veth.HwAddress()
179 createReply := &af_packet.AfPacketCreateV2Reply{}
181 if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
184 veth.SetIndex(createReply.SwIfIndex)
187 upReq := &interfaces.SwInterfaceSetFlags{
188 SwIfIndex: veth.Index(),
189 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
191 upReply := &interfaces.SwInterfaceSetFlagsReply{}
193 if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
198 if veth.AddressWithPrefix() == (AddressWithPrefix{}) {
200 var ip4Address string
201 if ip4Address, err = veth.addresser.NewIp4Address(veth.peerNetworkNumber); err == nil {
202 veth.SetAddress(ip4Address)
207 addressReq := &interfaces.SwInterfaceAddDelAddress{
209 SwIfIndex: veth.Index(),
210 Prefix: veth.AddressWithPrefix(),
212 addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
214 if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
218 return veth.Index(), nil
221 func (vpp *VppInstance) addAppNamespace(
223 ifx interface_types.InterfaceIndex,
226 req := &session.AppNamespaceAddDelV2{
229 NamespaceID: namespaceId,
231 reply := &session.AppNamespaceAddDelV2Reply{}
233 if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
237 sessionReq := &session.SessionEnableDisable{
240 sessionReply := &session.SessionEnableDisableReply{}
242 if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
249 func (vpp *VppInstance) createTap(
250 hostInterfaceName string,
251 hostIp4Address IP4AddressWithPrefix,
252 vppIp4Address AddressWithPrefix,
254 createTapReq := &tapv2.TapCreateV2{
256 HostIfName: hostInterfaceName,
257 HostIP4PrefixSet: true,
258 HostIP4Prefix: hostIp4Address,
260 createTapReply := &tapv2.TapCreateV2Reply{}
262 // Create tap interface
263 if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil {
268 addAddressReq := &interfaces.SwInterfaceAddDelAddress{
270 SwIfIndex: createTapReply.SwIfIndex,
271 Prefix: vppIp4Address,
273 addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{}
275 if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil {
279 // Set interface to up
280 upReq := &interfaces.SwInterfaceSetFlags{
281 SwIfIndex: createTapReply.SwIfIndex,
282 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
284 upReply := &interfaces.SwInterfaceSetFlagsReply{}
286 if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
293 func (vpp *VppInstance) disconnect() {
294 vpp.connection.Disconnect()
295 vpp.apiChannel.Close()