package main import ( "bytes" "context" "fmt" "os" "path/filepath" "git.fd.io/govpp.git/api" "github.com/edwarnicke/exechelper" "github.com/edwarnicke/govpp/binapi/af_packet" "github.com/edwarnicke/govpp/binapi/ethernet_types" interfaces "github.com/edwarnicke/govpp/binapi/interface" "github.com/edwarnicke/govpp/binapi/interface_types" ip_types "github.com/edwarnicke/govpp/binapi/ip_types" "github.com/edwarnicke/govpp/binapi/session" "github.com/edwarnicke/govpp/binapi/tapv2" "github.com/edwarnicke/govpp/binapi/vlib" "github.com/edwarnicke/vpphelper" ) var ( workDir, _ = os.Getwd() ) type ConfFn func(context.Context, api.Connection) error type Actions struct { } func configureProxyTcp(ifName0, ipAddr0, ifName1, ipAddr1 string) ConfFn { return func(ctx context.Context, vppConn api.Connection) error { _, err := configureAfPacket(ctx, vppConn, ifName0, ipAddr0) if err != nil { fmt.Printf("failed to create af packet: %v", err) return err } _, err = configureAfPacket(ctx, vppConn, ifName1, ipAddr1) if err != nil { fmt.Printf("failed to create af packet: %v", err) return err } return nil } } func (a *Actions) RunHttpCliSrv(args []string) *ActionResult { cmd := fmt.Sprintf("http cli server") return ApiCliInband(workDir, cmd) } func (a *Actions) RunHttpCliCln(args []string) *ActionResult { cmd := fmt.Sprintf("http cli client uri http://10.10.10.1/80 query %s", getArgs()) fmt.Println(cmd) return ApiCliInband(workDir, cmd) } func (a *Actions) ConfigureVppProxy(args []string) *ActionResult { ctx, cancel := newVppContext() defer cancel() con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(configTemplate), vpphelper.WithRootDir(workDir)) exitOnErrCh(ctx, cancel, vppErrCh) confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") err := confFn(ctx, con) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } writeSyncFile(OkResult()) <-ctx.Done() return nil } func (a *Actions) ConfigureEnvoyProxy(args []string) *ActionResult { var startup Stanza startup. NewStanza("session"). Append("enable"). Append("use-app-socket-api"). Append("evt_qs_memfd_seg"). Append("event-queue-length 100000").Close() ctx, cancel := newVppContext() defer cancel() con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(configTemplate+startup.ToString()), vpphelper.WithRootDir(workDir)) exitOnErrCh(ctx, cancel, vppErrCh) confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") err := confFn(ctx, con) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } err0 := exechelper.Run("chmod 777 -R " + workDir) if err0 != nil { return NewActionResult(err, ActionResultWithDesc("setting permissions failed")) } writeSyncFile(OkResult()) <-ctx.Done() return nil } func getArgs() string { s := "" for i := 2; i < len(os.Args); i++ { s = s + " " + os.Args[i] } return s } func ApiCliInband(root, cmd string) *ActionResult { ctx, _ := newVppContext() con := vpphelper.DialContext(ctx, filepath.Join(root, "/var/run/vpp/api.sock")) cliInband := vlib.CliInband{Cmd: cmd} cliInbandReply, err := vlib.NewServiceClient(con).CliInband(ctx, &cliInband) return NewActionResult(err, ActionResultWithStdout(cliInbandReply.Reply)) } func (a *Actions) RunEchoClient(args []string) *ActionResult { outBuff := bytes.NewBuffer([]byte{}) errBuff := bytes.NewBuffer([]byte{}) cmd := fmt.Sprintf("vpp_echo client socket-name %s/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2]) err := exechelper.Run(cmd, exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff), exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr)) return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())), ActionResultWithStderr(string(errBuff.String()))) } func (a *Actions) RunEchoServer(args []string) *ActionResult { cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name %s/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2]) errCh := exechelper.Start(cmd) select { case err := <-errCh: writeSyncFile(NewActionResult(err, ActionResultWithDesc("echo_server: "))) default: } writeSyncFile(OkResult()) return nil } func (a *Actions) RunEchoSrvInternal(args []string) *ActionResult { cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs()) return ApiCliInband(workDir, cmd) } func (a *Actions) RunEchoClnInternal(args []string) *ActionResult { cmd := fmt.Sprintf("test echo client %s uri tcp://10.10.10.1/1234", getArgs()) return ApiCliInband(workDir, cmd) } func (a *Actions) RunVclEchoServer(args []string) *ActionResult { f, err := os.Create("vcl_1.conf") if err != nil { return NewActionResult(err, ActionResultWithStderr(("create vcl config: "))) } socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/1", workDir) fmt.Fprintf(f, vclTemplate, socketPath, "1") f.Close() os.Setenv("VCL_CONFIG", "./vcl_1.conf") cmd := fmt.Sprintf("vcl_test_server -p %s 12346", args[2]) errCh := exechelper.Start(cmd) select { case err := <-errCh: writeSyncFile(NewActionResult(err, ActionResultWithDesc("vcl_test_server: "))) default: } writeSyncFile(OkResult()) return nil } func (a *Actions) RunVclEchoClient(args []string) *ActionResult { outBuff := bytes.NewBuffer([]byte{}) errBuff := bytes.NewBuffer([]byte{}) f, err := os.Create("vcl_2.conf") if err != nil { return NewActionResult(err, ActionResultWithStderr(("create vcl config: "))) } socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/2", workDir) fmt.Fprintf(f, vclTemplate, socketPath, "2") f.Close() os.Setenv("VCL_CONFIG", "./vcl_2.conf") cmd := fmt.Sprintf("vcl_test_client -U -p %s 10.10.10.1 12346", args[2]) err = exechelper.Run(cmd, exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff), exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr)) return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())), ActionResultWithStderr(string(errBuff.String()))) } func configure2vethsTopo(ifName, interfaceAddress, namespaceId string, secret uint64, optionalHardwareAddress ...string) ConfFn { return func(ctx context.Context, vppConn api.Connection) error { var swIfIndex interface_types.InterfaceIndex var err error if optionalHardwareAddress == nil { swIfIndex, err = configureAfPacket(ctx, vppConn, ifName, interfaceAddress) } else { swIfIndex, err = configureAfPacket(ctx, vppConn, ifName, interfaceAddress, optionalHardwareAddress[0]) } if err != nil { fmt.Printf("failed to create af packet: %v", err) } _, er := session.NewServiceClient(vppConn).AppNamespaceAddDelV2(ctx, &session.AppNamespaceAddDelV2{ Secret: secret, SwIfIndex: swIfIndex, NamespaceID: namespaceId, }) if er != nil { fmt.Printf("add app namespace: %v", err) return err } _, er1 := session.NewServiceClient(vppConn).SessionEnableDisable(ctx, &session.SessionEnableDisable{ IsEnable: true, }) if er1 != nil { fmt.Printf("session enable %v", err) return err } return nil } } func (a *Actions) Configure2Veths(args []string) *ActionResult { var startup Stanza startup. NewStanza("session"). Append("enable"). Append("use-app-socket-api").Close() ctx, cancel := newVppContext() defer cancel() vppConfig, err := deserializeVppConfig(args[2]) if err != nil { return NewActionResult(err, ActionResultWithDesc("deserializing configuration failed")) } con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(vppConfig.getTemplate()+startup.ToString()), vpphelper.WithRootDir(workDir)) exitOnErrCh(ctx, cancel, vppErrCh) var fn func(context.Context, api.Connection) error switch vppConfig.Variant { case "srv": fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1) case "srv-with-preset-hw-addr": fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1, "00:00:5e:00:53:01") case "cln": fallthrough default: fn = configure2vethsTopo("vppcln", "10.10.10.2/24", "2", 2) } err = fn(ctx, con) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } writeSyncFile(OkResult()) <-ctx.Done() return nil } func configureAfPacket(ctx context.Context, vppCon api.Connection, name, interfaceAddress string, optionalHardwareAddress ...string) (interface_types.InterfaceIndex, error) { var err error ifaceClient := interfaces.NewServiceClient(vppCon) afPacketCreate := af_packet.AfPacketCreateV2{ UseRandomHwAddr: true, HostIfName: name, NumRxQueues: 1, } if len(optionalHardwareAddress) > 0 { afPacketCreate.HwAddr, err = ethernet_types.ParseMacAddress(optionalHardwareAddress[0]) if err != nil { fmt.Printf("failed to parse mac address: %v", err) return 0, err } afPacketCreate.UseRandomHwAddr = false } afPacketCreateRsp, err := af_packet.NewServiceClient(vppCon).AfPacketCreateV2(ctx, &afPacketCreate) if err != nil { fmt.Printf("failed to create af packet: %v", err) return 0, err } _, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{ SwIfIndex: afPacketCreateRsp.SwIfIndex, Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, }) if err != nil { fmt.Printf("set interface state up failed: %v\n", err) return 0, err } ipPrefix, err := ip_types.ParseAddressWithPrefix(interfaceAddress) if err != nil { fmt.Printf("parse ip address %v\n", err) return 0, err } ipAddress := &interfaces.SwInterfaceAddDelAddress{ IsAdd: true, SwIfIndex: afPacketCreateRsp.SwIfIndex, Prefix: ipPrefix, } _, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress) if errx != nil { fmt.Printf("add ip address %v\n", err) return 0, err } return afPacketCreateRsp.SwIfIndex, nil } func (a *Actions) ConfigureHttpTps(args []string) *ActionResult { ctx, cancel := newVppContext() defer cancel() con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(configTemplate)) exitOnErrCh(ctx, cancel, vppErrCh) confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") err := confFn(ctx, con) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } _, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{ IsEnable: true, }) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } Vppcli("", "http tps uri tcp://0.0.0.0/8080") writeSyncFile(OkResult()) <-ctx.Done() return nil } func (a *Actions) ConfigureTap(args []string) *ActionResult { var startup Stanza startup. NewStanza("session"). Append("enable"). Append("use-app-socket-api").Close() ctx, cancel := newVppContext() defer cancel() con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithRootDir(workDir), vpphelper.WithVppConfig(configTemplate+startup.ToString())) exitOnErrCh(ctx, cancel, vppErrCh) ifaceClient := interfaces.NewServiceClient(con) pref, err := ip_types.ParseIP4Prefix("10.10.10.2/24") if err != nil { return NewActionResult(err, ActionResultWithDesc("failed to parse ip4 address")) } createTapReply, err := tapv2.NewServiceClient(con).TapCreateV2(ctx, &tapv2.TapCreateV2{ HostIfNameSet: true, HostIfName: "tap0", HostIP4PrefixSet: true, HostIP4Prefix: ip_types.IP4AddressWithPrefix(pref), }) if err != nil { return NewActionResult(err, ActionResultWithDesc("failed to configure tap")) } ipPrefix, err := ip_types.ParseAddressWithPrefix("10.10.10.1/24") if err != nil { return NewActionResult(err, ActionResultWithDesc("parsing ip address failed")) } ipAddress := &interfaces.SwInterfaceAddDelAddress{ IsAdd: true, SwIfIndex: createTapReply.SwIfIndex, Prefix: ipPrefix, } _, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress) if errx != nil { return NewActionResult(err, ActionResultWithDesc("configuring ip address failed")) } _, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{ SwIfIndex: createTapReply.SwIfIndex, Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, }) if err != nil { return NewActionResult(err, ActionResultWithDesc("failed to set interface state")) } _, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{ IsEnable: true, }) if err != nil { return NewActionResult(err, ActionResultWithDesc("configuration failed")) } writeSyncFile(OkResult()) <-ctx.Done() return nil }