aboutsummaryrefslogtreecommitdiffstats
path: root/binapigen
diff options
context:
space:
mode:
Diffstat (limited to 'binapigen')
-rw-r--r--binapigen/binapigen.go369
-rw-r--r--binapigen/definitions.go153
-rw-r--r--binapigen/definitions_test.go39
-rw-r--r--binapigen/generate.go1241
-rw-r--r--binapigen/generate_rpc.go188
-rw-r--r--binapigen/generate_test.go446
-rw-r--r--binapigen/generator.go172
-rw-r--r--binapigen/generator_test.go110
-rw-r--r--binapigen/run.go89
-rw-r--r--binapigen/types.go271
-rw-r--r--binapigen/validate.go66
-rw-r--r--binapigen/vppapi/api.go94
-rw-r--r--binapigen/vppapi/integration_test.go43
-rw-r--r--binapigen/vppapi/parse_json.go552
-rw-r--r--binapigen/vppapi/parser.go111
-rw-r--r--binapigen/vppapi/parser_test.go114
-rw-r--r--binapigen/vppapi/testdata/acl.api.json929
-rw-r--r--binapigen/vppapi/testdata/af_packet.api.json157
-rw-r--r--binapigen/vppapi/testdata/input-generate-error.json3
-rw-r--r--binapigen/vppapi/testdata/input-read-json-error.json1
-rw-r--r--binapigen/vppapi/testdata/input.txt0
-rw-r--r--binapigen/vppapi/testdata/ip.api.json2246
-rw-r--r--binapigen/vppapi/testdata/test-all.api.json3240
-rw-r--r--binapigen/vppapi/testdata/vpe.api.json775
24 files changed, 11409 insertions, 0 deletions
diff --git a/binapigen/binapigen.go b/binapigen/binapigen.go
new file mode 100644
index 0000000..0178476
--- /dev/null
+++ b/binapigen/binapigen.go
@@ -0,0 +1,369 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "fmt"
+ "path"
+ "sort"
+ "strings"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+type File struct {
+ vppapi.File
+
+ Generate bool
+
+ PackageName string
+ Imports []string
+
+ Enums []*Enum
+ Unions []*Union
+ Structs []*Struct
+ Aliases []*Alias
+ Messages []*Message
+
+ imports map[string]string
+ refmap map[string]string
+}
+
+func newFile(gen *Generator, apifile *vppapi.File) (*File, error) {
+ file := &File{
+ File: *apifile,
+ PackageName: sanitizedName(apifile.Name),
+ imports: make(map[string]string),
+ refmap: make(map[string]string),
+ }
+
+ sortFileObjects(&file.File)
+
+ for _, imp := range apifile.Imports {
+ file.Imports = append(file.Imports, normalizeImport(imp))
+ }
+ for _, enum := range apifile.EnumTypes {
+ file.Enums = append(file.Enums, newEnum(gen, file, enum))
+ }
+ for _, alias := range apifile.AliasTypes {
+ file.Aliases = append(file.Aliases, newAlias(gen, file, alias))
+ }
+ for _, structType := range apifile.StructTypes {
+ file.Structs = append(file.Structs, newStruct(gen, file, structType))
+ }
+ for _, union := range apifile.UnionTypes {
+ file.Unions = append(file.Unions, newUnion(gen, file, union))
+ }
+ for _, msg := range apifile.Messages {
+ file.Messages = append(file.Messages, newMessage(gen, file, msg))
+ }
+
+ return file, nil
+}
+
+func (file *File) isTypes() bool {
+ return strings.HasSuffix(file.File.Name, "_types")
+}
+
+func (file *File) hasService() bool {
+ return file.Service != nil && len(file.Service.RPCs) > 0
+}
+
+func (file *File) addRef(typ string, name string, ref interface{}) {
+ apiName := toApiType(name)
+ if _, ok := file.refmap[apiName]; ok {
+ logf("%s type %v already in refmap", typ, apiName)
+ return
+ }
+ file.refmap[apiName] = name
+}
+
+func (file *File) importedFiles(gen *Generator) []*File {
+ var files []*File
+ for _, imp := range file.Imports {
+ impFile, ok := gen.FilesByName[imp]
+ if !ok {
+ logf("file %s import %s not found API files", file.Name, imp)
+ continue
+ }
+ //if gen.ImportTypes || impFile.Generate {
+ files = append(files, impFile)
+ //}
+ }
+ return files
+}
+
+func (file *File) loadTypeImports(gen *Generator, typeFiles []*File) {
+ if len(typeFiles) == 0 {
+ return
+ }
+ for _, t := range file.Structs {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.StructTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if len(at.Fields) != len(t.Fields) {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.AliasTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.AliasTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if at.Length != t.Length {
+ continue
+ }
+ if at.Type != t.Type {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.EnumTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.EnumTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if at.Type != t.Type {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.UnionTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.UnionTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ /*if gen.ImportTypes {
+ imp.Generate = true
+ }*/
+ }
+ }
+ }
+}
+
+type Enum struct {
+ vppapi.EnumType
+
+ GoName string
+}
+
+func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum {
+ typ := &Enum{
+ EnumType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.enumsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("enum", typ.Name, typ)
+ return typ
+}
+
+type Alias struct {
+ vppapi.AliasType
+
+ GoName string
+}
+
+func newAlias(gen *Generator, file *File, apitype vppapi.AliasType) *Alias {
+ typ := &Alias{
+ AliasType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.aliasesByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("alias", typ.Name, typ)
+ return typ
+}
+
+type Struct struct {
+ vppapi.StructType
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct {
+ typ := &Struct{
+ StructType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentStruct = typ
+ typ.Fields = append(typ.Fields, field)
+ }
+ gen.structsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("struct", typ.Name, typ)
+ return typ
+}
+
+type Union struct {
+ vppapi.UnionType
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union {
+ typ := &Union{
+ UnionType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.unionsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentUnion = typ
+ typ.Fields = append(typ.Fields, field)
+ }
+ file.addRef("union", typ.Name, typ)
+ return typ
+}
+
+type Message struct {
+ vppapi.Message
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message {
+ msg := &Message{
+ Message: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentMessage = msg
+ msg.Fields = append(msg.Fields, field)
+ }
+ return msg
+}
+
+type Field struct {
+ vppapi.Field
+
+ GoName string
+
+ // Field parent
+ ParentMessage *Message
+ ParentStruct *Struct
+ ParentUnion *Union
+
+ // Type reference
+ Enum *Enum
+ Alias *Alias
+ Struct *Struct
+ Union *Union
+}
+
+func newField(gen *Generator, file *File, apitype vppapi.Field) *Field {
+ typ := &Field{
+ Field: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ return typ
+}
+
+func (f *Field) resolveType(gen *Generator) error {
+ switch {
+
+ }
+ return nil
+}
+
+type Service = vppapi.Service
+type RPC = vppapi.RPC
+
+func sortFileObjects(file *vppapi.File) {
+ // sort imports
+ sort.SliceStable(file.Imports, func(i, j int) bool {
+ return file.Imports[i] < file.Imports[j]
+ })
+ // sort enum types
+ sort.SliceStable(file.EnumTypes, func(i, j int) bool {
+ return file.EnumTypes[i].Name < file.EnumTypes[j].Name
+ })
+ // sort alias types
+ sort.Slice(file.AliasTypes, func(i, j int) bool {
+ return file.AliasTypes[i].Name < file.AliasTypes[j].Name
+ })
+ // sort struct types
+ sort.SliceStable(file.StructTypes, func(i, j int) bool {
+ return file.StructTypes[i].Name < file.StructTypes[j].Name
+ })
+ // sort union types
+ sort.SliceStable(file.UnionTypes, func(i, j int) bool {
+ return file.UnionTypes[i].Name < file.UnionTypes[j].Name
+ })
+ // sort messages
+ sort.SliceStable(file.Messages, func(i, j int) bool {
+ return file.Messages[i].Name < file.Messages[j].Name
+ })
+ // sort services
+ if file.Service != nil {
+ sort.Slice(file.Service.RPCs, func(i, j int) bool {
+ // dumps first
+ if file.Service.RPCs[i].Stream != file.Service.RPCs[j].Stream {
+ return file.Service.RPCs[i].Stream
+ }
+ return file.Service.RPCs[i].RequestMsg < file.Service.RPCs[j].RequestMsg
+ })
+ }
+}
+
+func sanitizedName(name string) string {
+ switch name {
+ case "interface":
+ return "interfaces"
+ case "map":
+ return "maps"
+ default:
+ return name
+ }
+}
+
+func normalizeImport(imp string) string {
+ imp = path.Base(imp)
+ if idx := strings.Index(imp, "."); idx >= 0 {
+ imp = imp[:idx]
+ }
+ return imp
+}
diff --git a/binapigen/definitions.go b/binapigen/definitions.go
new file mode 100644
index 0000000..3c8a874
--- /dev/null
+++ b/binapigen/definitions.go
@@ -0,0 +1,153 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "strings"
+ "unicode"
+)
+
+// commonInitialisms is a set of common initialisms that need to stay in upper case.
+var commonInitialisms = map[string]bool{
+ "ACL": true,
+ "API": true,
+ // NOTE: There are only two occurences of the word 'ascii' and
+ // these already have initialism before and after ASCII part,
+ // thus disabling initialism for this case.
+ "ASCII": false,
+ "CPU": true,
+ "CSS": true,
+ "DNS": true,
+ "DHCP": true,
+ "EOF": true,
+ "GUID": true,
+ "HTML": true,
+ "HTTP": true,
+ "HTTPS": true,
+ "ID": true,
+ "IP": true,
+ "ICMP": true,
+ "JSON": true,
+ "LHS": true,
+ "QPS": true,
+ "PID": true,
+ "RAM": true,
+ "RHS": true,
+ "RPC": true,
+ "SLA": true,
+ "SMTP": true,
+ "SQL": true,
+ "SSH": true,
+ "TCP": true,
+ "TLS": true,
+ "TTL": true,
+ "UDP": true,
+ "UI": true,
+ "UID": true,
+ "UUID": true,
+ "URI": true,
+ "URL": true,
+ "UTF8": true,
+ "VM": true,
+ "VPN": true,
+ "XML": true,
+ "XMPP": true,
+ "XSRF": true,
+ "XSS": true,
+}
+
+// specialInitialisms is a set of special initialisms that need part to stay in upper case.
+var specialInitialisms = map[string]string{
+ "IPV": "IPv",
+}
+
+func usesInitialism(s string) string {
+ if u := strings.ToUpper(s); commonInitialisms[u] {
+ return u
+ } else if su, ok := specialInitialisms[u]; ok {
+ return su
+ }
+ return ""
+}
+
+// camelCaseName returns correct name identifier (camelCase).
+func camelCaseName(name string) (should string) {
+ name = strings.Title(name)
+
+ // Fast path for simple cases: "_" and all lowercase.
+ if name == "_" {
+ return name
+ }
+ allLower := true
+ for _, r := range name {
+ if !unicode.IsLower(r) {
+ allLower = false
+ break
+ }
+ }
+ if allLower {
+ return name
+ }
+
+ // Split camelCase at any lower->upper transition, and split on underscores.
+ // Check each word for common initialisms.
+ runes := []rune(name)
+ w, i := 0, 0 // index of start of word, scan
+ for i+1 <= len(runes) {
+ eow := false // whether we hit the end of a word
+ if i+1 == len(runes) {
+ eow = true
+ } else if runes[i+1] == '_' {
+ // underscore; shift the remainder forward over any run of underscores
+ eow = true
+ n := 1
+ for i+n+1 < len(runes) && runes[i+n+1] == '_' {
+ n++
+ }
+
+ // Leave at most one underscore if the underscore is between two digits
+ if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
+ n--
+ }
+
+ copy(runes[i+1:], runes[i+n+1:])
+ runes = runes[:len(runes)-n]
+ } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
+ // lower->non-lower
+ eow = true
+ }
+ i++
+ if !eow {
+ continue
+ }
+
+ // [w,i) is a word.
+ word := string(runes[w:i])
+ if u := usesInitialism(word); u != "" {
+ // Keep consistent case, which is lowercase only at the start.
+ if w == 0 && unicode.IsLower(runes[w]) {
+ u = strings.ToLower(u)
+ }
+ // All the common initialisms are ASCII,
+ // so we can replace the bytes exactly.
+ copy(runes[w:], []rune(u))
+ } else if w > 0 && strings.ToLower(word) == word {
+ // already all lowercase, and not the first word, so uppercase the first character.
+ runes[w] = unicode.ToUpper(runes[w])
+ }
+ w = i
+ }
+ return string(runes)
+}
diff --git a/binapigen/definitions_test.go b/binapigen/definitions_test.go
new file mode 100644
index 0000000..761c95f
--- /dev/null
+++ b/binapigen/definitions_test.go
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "testing"
+)
+
+func TestInitialism(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expOutput string
+ }{
+ {name: "id", input: "id", expOutput: "ID"},
+ {name: "ipv6", input: "is_ipv6", expOutput: "IsIPv6"},
+ {name: "ip6", input: "is_ip6", expOutput: "IsIP6"},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ output := camelCaseName(test.input)
+ if output != test.expOutput {
+ t.Errorf("expected %q, got %q", test.expOutput, output)
+ }
+ })
+ }
+}
diff --git a/binapigen/generate.go b/binapigen/generate.go
new file mode 100644
index 0000000..1f9b89a
--- /dev/null
+++ b/binapigen/generate.go
@@ -0,0 +1,1241 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "git.fd.io/govpp.git/version"
+)
+
+// generatedCodeVersion indicates a version of the generated code.
+// It is incremented whenever an incompatibility between the generated code and
+// GoVPP api package is introduced; the generated code references
+// a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
+const generatedCodeVersion = 2
+
+// message field names
+const (
+ msgIdField = "_vl_msg_id"
+ clientIndexField = "client_index"
+ contextField = "context"
+ retvalField = "retval"
+)
+
+const (
+ outputFileExt = ".ba.go" // file extension of the Go generated files
+ rpcFileSuffix = "_rpc" // file name suffix for the RPC services
+
+ constModuleName = "ModuleName" // module name constant
+ constAPIVersion = "APIVersion" // API version constant
+ constVersionCrc = "VersionCrc" // version CRC constant
+
+ unionDataField = "XXX_UnionData" // name for the union data field
+
+ serviceApiName = "RPCService" // name for the RPC service interface
+ serviceImplName = "serviceClient" // name for the RPC service implementation
+ serviceClientName = "ServiceClient" // name for the RPC service client
+
+ // TODO: register service descriptor
+ //serviceDescType = "ServiceDesc" // name for service descriptor type
+ //serviceDescName = "_ServiceRPC_serviceDesc" // name for service descriptor var
+)
+
+// MessageType represents the type of a VPP message
+type MessageType int
+
+const (
+ requestMessage MessageType = iota // VPP request message
+ replyMessage // VPP reply message
+ eventMessage // VPP event message
+ otherMessage // other VPP message
+)
+
+type GenFile struct {
+ *Generator
+ filename string
+ file *File
+ packageDir string
+ buf bytes.Buffer
+}
+
+func generatePackage(ctx *GenFile, w io.Writer) {
+ logf("----------------------------")
+ logf("generating binapi package: %q", ctx.file.PackageName)
+ logf("----------------------------")
+
+ generateHeader(ctx, w)
+ generateImports(ctx, w)
+
+ // generate module desc
+ fmt.Fprintln(w, "const (")
+ fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
+ fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
+
+ if ctx.IncludeAPIVersion {
+ fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
+ fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
+ fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
+ fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
+ }
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ // generate enums
+ if len(ctx.file.Enums) > 0 {
+ for _, enum := range ctx.file.Enums {
+ if imp, ok := ctx.file.imports[enum.Name]; ok {
+ generateImportedAlias(ctx, w, enum.GoName, imp)
+ continue
+ }
+ generateEnum(ctx, w, enum)
+ }
+ }
+
+ // generate aliases
+ if len(ctx.file.Aliases) > 0 {
+ for _, alias := range ctx.file.Aliases {
+ if imp, ok := ctx.file.imports[alias.Name]; ok {
+ generateImportedAlias(ctx, w, alias.GoName, imp)
+ continue
+ }
+ generateAlias(ctx, w, alias)
+ }
+ }
+
+ // generate types
+ if len(ctx.file.Structs) > 0 {
+ for _, typ := range ctx.file.Structs {
+ if imp, ok := ctx.file.imports[typ.Name]; ok {
+ generateImportedAlias(ctx, w, typ.GoName, imp)
+ continue
+ }
+ generateStruct(ctx, w, typ)
+ }
+ }
+
+ // generate unions
+ if len(ctx.file.Unions) > 0 {
+ for _, union := range ctx.file.Unions {
+ if imp, ok := ctx.file.imports[union.Name]; ok {
+ generateImportedAlias(ctx, w, union.GoName, imp)
+ continue
+ }
+ generateUnion(ctx, w, union)
+ }
+ }
+
+ // generate messages
+ if len(ctx.file.Messages) > 0 {
+ for _, msg := range ctx.file.Messages {
+ generateMessage(ctx, w, msg)
+ }
+
+ initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
+
+ // generate message registrations
+ fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
+ fmt.Fprintf(w, "func %s() {\n", initFnName)
+ for _, msg := range ctx.file.Messages {
+ fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
+ msg.GoName, ctx.file.Name+"."+msg.GoName)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate list of messages
+ fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
+ fmt.Fprintln(w, "func AllMessages() []api.Message {")
+ fmt.Fprintln(w, "\treturn []api.Message{")
+ for _, msg := range ctx.file.Messages {
+ fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w, "}")
+ }
+
+ generateFooter(ctx, w)
+
+}
+
+func generateHeader(ctx *GenFile, w io.Writer) {
+ fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
+ fmt.Fprintln(w, "// versions:")
+ fmt.Fprintf(w, "// binapi-generator: %s\n", version.Version())
+ if ctx.IncludeVppVersion {
+ fmt.Fprintf(w, "// VPP: %s\n", ctx.VPPVersion)
+ }
+ fmt.Fprintf(w, "// source: %s\n", ctx.file.Path)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, "/*")
+ fmt.Fprintf(w, "Package %s contains generated code for VPP binary API defined by %s.api (version %s).\n",
+ ctx.file.PackageName, ctx.file.Name, ctx.file.Version())
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "It consists of:")
+ printObjNum := func(obj string, num int) {
+ if num > 0 {
+ if num > 1 {
+ if strings.HasSuffix(obj, "s") {
+
+ obj += "es"
+ } else {
+ obj += "s"
+ }
+ }
+ fmt.Fprintf(w, "\t%3d %s\n", num, obj)
+ }
+ }
+ //printObjNum("RPC", len(ctx.file.Service.RPCs))
+ printObjNum("alias", len(ctx.file.Aliases))
+ printObjNum("enum", len(ctx.file.Enums))
+ printObjNum("message", len(ctx.file.Messages))
+ printObjNum("type", len(ctx.file.Structs))
+ printObjNum("union", len(ctx.file.Unions))
+ fmt.Fprintln(w, "*/")
+ fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
+ fmt.Fprintln(w)
+}
+
+func generateImports(ctx *GenFile, w io.Writer) {
+ fmt.Fprintln(w, "import (")
+ fmt.Fprintln(w, ` "bytes"`)
+ fmt.Fprintln(w, ` "context"`)
+ fmt.Fprintln(w, ` "encoding/binary"`)
+ fmt.Fprintln(w, ` "io"`)
+ fmt.Fprintln(w, ` "math"`)
+ fmt.Fprintln(w, ` "strconv"`)
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
+ fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
+ fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
+ if len(ctx.file.Imports) > 0 {
+ fmt.Fprintln(w)
+ for _, imp := range ctx.file.Imports {
+ importPath := path.Join(ctx.ImportPrefix, imp)
+ if ctx.ImportPrefix == "" {
+ importPath = getImportPath(ctx.packageDir, imp)
+ }
+ fmt.Fprintf(w, "\t%s \"%s\"\n", imp, strings.TrimSpace(importPath))
+ }
+ }
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
+ fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
+ fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
+ fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
+ fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
+ fmt.Fprintln(w)
+}
+
+func getImportPath(outputDir string, pkg string) string {
+ absPath, err := filepath.Abs(filepath.Join(outputDir, "..", pkg))
+ if err != nil {
+ panic(err)
+ }
+ cmd := exec.Command("go", "list", absPath)
+ var errbuf, outbuf bytes.Buffer
+ cmd.Stdout = &outbuf
+ cmd.Stderr = &errbuf
+ if err := cmd.Run(); err != nil {
+ fmt.Printf("ERR: %v\n", errbuf.String())
+ panic(err)
+ }
+ return outbuf.String()
+}
+
+func generateFooter(ctx *GenFile, w io.Writer) {
+ fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
+ fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
+ fmt.Fprintf(w, "var _ = codec.DecodeString\n")
+ fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
+ fmt.Fprintf(w, "var _ = context.Background\n")
+ fmt.Fprintf(w, "var _ = io.Copy\n")
+ fmt.Fprintf(w, "var _ = strconv.Itoa\n")
+ fmt.Fprintf(w, "var _ = struc.Pack\n")
+ fmt.Fprintf(w, "var _ = binary.BigEndian\n")
+ fmt.Fprintf(w, "var _ = math.Float32bits\n")
+}
+
+func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
+ if objKind == "service" {
+ fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
+ } else {
+ fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
+ }
+}
+
+func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
+ name := enum.GoName
+ typ := binapiTypes[enum.Type]
+
+ logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
+
+ // generate enum comment
+ generateComment(ctx, w, name, enum.Name, "enum")
+
+ // generate enum definition
+ fmt.Fprintf(w, "type %s %s\n", name, typ)
+ fmt.Fprintln(w)
+
+ // generate enum entries
+ fmt.Fprintln(w, "const (")
+ for _, entry := range enum.Entries {
+ fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
+ }
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ // generate enum conversion maps
+ fmt.Fprintln(w, "var (")
+ fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
+ for _, entry := range enum.Entries {
+ fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
+ for _, entry := range enum.Entries {
+ fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "func (x %s) String() string {\n", name)
+ fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
+ fmt.Fprintf(w, "\tif ok { return s }\n")
+ fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+}
+
+func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
+ fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
+ fmt.Fprintln(w)
+}
+
+func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
+ name := alias.GoName
+
+ logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
+
+ // generate struct comment
+ generateComment(ctx, w, name, alias.Name, "alias")
+
+ // generate struct definition
+ fmt.Fprintf(w, "type %s ", name)
+
+ if alias.Length > 0 {
+ fmt.Fprintf(w, "[%d]", alias.Length)
+ }
+
+ dataType := convertToGoType(ctx.file, alias.Type)
+ fmt.Fprintf(w, "%s\n", dataType)
+
+ fmt.Fprintln(w)
+}
+
+func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
+ name := typ.GoName
+
+ logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
+
+ // generate struct comment
+ generateComment(ctx, w, name, typ.Name, "type")
+
+ // generate struct definition
+ fmt.Fprintf(w, "type %s struct {\n", name)
+
+ // generate struct fields
+ for i := range typ.Fields {
+ // skip internal fields
+ switch strings.ToLower(typ.Name) {
+ case msgIdField:
+ continue
+ }
+
+ generateField(ctx, w, typ.Fields, i)
+ }
+
+ // generate end of the struct
+ fmt.Fprintln(w, "}")
+
+ // generate name getter
+ generateTypeNameGetter(w, name, typ.Name)
+
+ fmt.Fprintln(w)
+}
+
+// generateUnionMethods generates methods that implement struc.Custom
+// interface to allow having XXX_uniondata field unexported
+// TODO: do more testing when unions are actually used in some messages
+/*func generateUnionMethods(w io.Writer, structName string) {
+ // generate struc.Custom implementation for union
+ fmt.Fprintf(w, `
+func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
+ var b = new(bytes.Buffer)
+ if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
+ return 0, err
+ }
+ copy(p, b.Bytes())
+ return b.Len(), nil
+}
+func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
+ return struc.UnpackWithOptions(r, u.union_data[:], opt)
+}
+func (u *%[1]s) Size(opt *struc.Options) int {
+ return len(u.union_data)
+}
+func (u *%[1]s) String() string {
+ return string(u.union_data[:])
+}
+`, structName)
+}*/
+
+/*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
+ fmt.Fprintf(w, `
+func %[1]s%[2]s(a %[3]s) (u %[1]s) {
+ u.Set%[2]s(a)
+ return
+}
+func (u *%[1]s) Set%[2]s(a %[3]s) {
+ copy(u.%[4]s[:], a[:])
+}
+func (u *%[1]s) Get%[2]s() (a %[3]s) {
+ copy(a[:], u.%[4]s[:])
+ return
+}
+`, structName, getterField, getterStruct, unionDataField)
+}*/
+
+func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
+ name := union.GoName
+
+ logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
+
+ // generate struct comment
+ generateComment(ctx, w, name, union.Name, "union")
+
+ // generate struct definition
+ fmt.Fprintln(w, "type", name, "struct {")
+
+ // maximum size for union
+ maxSize := getUnionSize(ctx.file, union)
+
+ // generate data field
+ fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
+
+ // generate end of the struct
+ fmt.Fprintln(w, "}")
+
+ // generate name getter
+ generateTypeNameGetter(w, name, union.Name)
+
+ // generate getters for fields
+ for _, field := range union.Fields {
+ fieldType := convertToGoType(ctx.file, field.Type)
+ generateUnionGetterSetter(w, name, field.GoName, fieldType)
+ }
+
+ // generate union methods
+ //generateUnionMethods(w, name)
+
+ fmt.Fprintln(w)
+}
+
+func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
+ fmt.Fprintf(w, `
+func %[1]s%[2]s(a %[3]s) (u %[1]s) {
+ u.Set%[2]s(a)
+ return
+}
+func (u *%[1]s) Set%[2]s(a %[3]s) {
+ var b = new(bytes.Buffer)
+ if err := struc.Pack(b, &a); err != nil {
+ return
+ }
+ copy(u.%[4]s[:], b.Bytes())
+}
+func (u *%[1]s) Get%[2]s() (a %[3]s) {
+ var b = bytes.NewReader(u.%[4]s[:])
+ struc.Unpack(b, &a)
+ return
+}
+`, structName, getterField, getterStruct, unionDataField)
+}
+
+func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
+ name := msg.GoName
+
+ logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
+
+ // generate struct comment
+ generateComment(ctx, w, name, msg.Name, "message")
+
+ // generate struct definition
+ fmt.Fprintf(w, "type %s struct {", name)
+
+ msgType := otherMessage
+ wasClientIndex := false
+
+ // generate struct fields
+ n := 0
+ for i, field := range msg.Fields {
+ if i == 1 {
+ if field.Name == clientIndexField {
+ // "client_index" as the second member,
+ // this might be an event message or a request
+ msgType = eventMessage
+ wasClientIndex = true
+ } else if field.Name == contextField {
+ // reply needs "context" as the second member
+ msgType = replyMessage
+ }
+ } else if i == 2 {
+ if wasClientIndex && field.Name == contextField {
+ // request needs "client_index" as the second member
+ // and "context" as the third member
+ msgType = requestMessage
+ }
+ }
+
+ // skip internal fields
+ switch strings.ToLower(field.Name) {
+ case /*crcField,*/ msgIdField:
+ continue
+ case clientIndexField, contextField:
+ if n == 0 {
+ continue
+ }
+ }
+ n++
+ if n == 1 {
+ fmt.Fprintln(w)
+ }
+
+ generateField(ctx, w, msg.Fields, i)
+ }
+
+ // generate end of the struct
+ fmt.Fprintln(w, "}")
+
+ // generate message methods
+ generateMessageResetMethod(w, name)
+ generateMessageNameGetter(w, name, msg.Name)
+ generateCrcGetter(w, name, msg.CRC)
+ generateMessageTypeGetter(w, name, msgType)
+ generateMessageSize(ctx, w, name, msg.Fields)
+ generateMessageMarshal(ctx, w, name, msg.Fields)
+ generateMessageUnmarshal(ctx, w, name, msg.Fields)
+
+ fmt.Fprintln(w)
+}
+
+func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
+ fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
+
+ fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
+ fmt.Fprintf(w, "\tvar size int\n")
+
+ encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
+ t, ok := BaseTypeNames[typ]
+ if !ok {
+ return false
+ }
+
+ var s = BaseTypeSizes[t]
+ switch t {
+ case STRING:
+ if length > 0 {
+ s = length
+ fmt.Fprintf(w, "\tsize += %d\n", s)
+ } else {
+ s = 4
+ fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
+ }
+ default:
+ if sizefrom != "" {
+ //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
+ fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
+ } else {
+ if length > 0 {
+ s = BaseTypeSizes[t] * length
+ }
+ fmt.Fprintf(w, "\tsize += %d\n", s)
+ }
+ }
+
+ return true
+ }
+
+ lvl := 0
+ var encodeFields func(fields []*Field, parentName string)
+ encodeFields = func(fields []*Field, parentName string) {
+ lvl++
+ defer func() { lvl-- }()
+
+ n := 0
+ for _, field := range fields {
+ // skip internal fields
+ switch strings.ToLower(field.Name) {
+ case /*crcField,*/ msgIdField:
+ continue
+ case clientIndexField, contextField:
+ if n == 0 {
+ continue
+ }
+ }
+ n++
+
+ fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
+ name := fmt.Sprintf("%s.%s", parentName, fieldName)
+ sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
+ var sizeFromName string
+ if sizeFrom != "" {
+ sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
+ }
+
+ fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
+
+ if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
+ continue
+ }
+
+ char := fmt.Sprintf("s%d", lvl)
+ index := fmt.Sprintf("j%d", lvl)
+
+ if field.Array {
+ if field.Length > 0 {
+ fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
+ } else if field.SizeFrom != "" {
+ //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
+ fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
+ }
+
+ fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
+ fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
+ name = char
+ }
+
+ if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
+ if encodeBaseType(enum.Type, name, 0, "") {
+ } else {
+ fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
+ }
+ } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
+ if encodeBaseType(alias.Type, name, alias.Length, "") {
+ } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
+ encodeFields(typ.Fields, name)
+ } else {
+ fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
+ }
+ } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
+ encodeFields(typ.Fields, name)
+ } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
+ maxSize := getUnionSize(ctx.file, union)
+ fmt.Fprintf(w, "\tsize += %d\n", maxSize)
+ } else {
+ fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
+ }
+
+ if field.Array {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ }
+ }
+
+ encodeFields(fields, "m")
+
+ fmt.Fprintf(w, "return size\n")
+
+ fmt.Fprintf(w, "}\n")
+}
+
+func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
+ fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
+
+ fmt.Fprintf(w, "\to := binary.BigEndian\n")
+ fmt.Fprintf(w, "\t_ = o\n")
+ fmt.Fprintf(w, "\tpos := 0\n")
+ fmt.Fprintf(w, "\t_ = pos\n")
+
+ var buf = new(strings.Builder)
+
+ encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
+ t, ok := BaseTypeNames[typ]
+ if !ok {
+ return false
+ }
+
+ isArray := length > 0 || sizefrom != ""
+
+ switch t {
+ case I8, U8, I16, U16, I32, U32, I64, U64, F64:
+ if isArray {
+ if length != 0 {
+ fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
+ } else if sizefrom != "" {
+ //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
+ fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
+ }
+ }
+ }
+
+ switch t {
+ case I8, U8:
+ if isArray {
+ fmt.Fprintf(buf, "\tvar x uint8\n")
+ fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
+ name = "x"
+ }
+ fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
+ fmt.Fprintf(buf, "\tpos += 1\n")
+ if isArray {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ case I16, U16:
+ if isArray {
+ fmt.Fprintf(buf, "\tvar x uint16\n")
+ fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
+ name = "x"
+ }
+ fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
+ fmt.Fprintf(buf, "\tpos += 2\n")
+ if isArray {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ case I32, U32:
+ if isArray {
+ fmt.Fprintf(buf, "\tvar x uint32\n")
+ fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
+ name = "x"
+ }
+ fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
+ fmt.Fprintf(buf, "\tpos += 4\n")
+ if isArray {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ case I64, U64:
+ if isArray {
+ fmt.Fprintf(buf, "\tvar x uint64\n")
+ fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
+ name = "x"
+ }
+ fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
+ fmt.Fprintf(buf, "\tpos += 8\n")
+ if isArray {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ case F64:
+ if isArray {
+ fmt.Fprintf(buf, "\tvar x float64\n")
+ fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
+ name = "x"
+ }
+ fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
+ fmt.Fprintf(buf, "\tpos += 8\n")
+ if isArray {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ case BOOL:
+ fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
+ fmt.Fprintf(buf, "\tpos += 1\n")
+ case STRING:
+ if length != 0 {
+ fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
+ fmt.Fprintf(buf, "\tpos += %d\n", length)
+ } else {
+ fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
+ fmt.Fprintf(buf, "\tpos += 4\n")
+ fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
+ fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
+ }
+ default:
+ fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
+ return false
+ }
+ return true
+ }
+
+ lvl := 0
+ var encodeFields func(fields []*Field, parentName string)
+ encodeFields = func(fields []*Field, parentName string) {
+ lvl++
+ defer func() { lvl-- }()
+
+ n := 0
+ for _, field := range fields {
+ // skip internal fields
+ switch strings.ToLower(field.Name) {
+ case /*crcField,*/ msgIdField:
+ continue
+ case clientIndexField, contextField:
+ if n == 0 {
+ continue
+ }
+ }
+ n++
+
+ getFieldName := func(name string) string {
+ fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
+ return fmt.Sprintf("%s.%s", parentName, fieldName)
+ }
+
+ fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
+ name := fmt.Sprintf("%s.%s", parentName, fieldName)
+ sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
+ var sizeFromName string
+ if sizeFrom != "" {
+ sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
+ }
+
+ fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
+
+ getSizeOfField := func() *Field {
+ for _, f := range fields {
+ if f.SizeFrom == field.Name {
+ return f
+ }
+ }
+ return nil
+ }
+ if f := getSizeOfField(); f != nil {
+ if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
+ continue
+ }
+ panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
+ }
+
+ if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
+ continue
+ }
+
+ char := fmt.Sprintf("v%d", lvl)
+ index := fmt.Sprintf("j%d", lvl)
+
+ if field.Array {
+ if field.Length > 0 {
+ fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
+ } else if field.SizeFrom != "" {
+ //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
+ fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
+ }
+
+ fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
+ fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
+ name = char
+ }
+
+ if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
+ if encodeBaseType(enum.Type, name, 0, "") {
+ } else {
+ fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
+ }
+ } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
+ if encodeBaseType(alias.Type, name, alias.Length, "") {
+ } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
+ encodeFields(typ.Fields, name)
+ } else {
+ fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
+ }
+ } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
+ encodeFields(typ.Fields, name)
+ } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
+ maxSize := getUnionSize(ctx.file, union)
+ fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
+ fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
+ } else {
+ fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
+ }
+
+ if field.Array {
+ fmt.Fprintf(buf, "\t}\n")
+ }
+ }
+ }
+
+ encodeFields(fields, "m")
+
+ fmt.Fprintf(w, "\tvar buf []byte\n")
+ fmt.Fprintf(w, "\tif b == nil {\n")
+ fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
+ fmt.Fprintf(w, "\t} else {\n")
+ fmt.Fprintf(w, "\tbuf = b\n")
+ fmt.Fprintf(w, "\t}\n")
+ fmt.Fprint(w, buf.String())
+
+ fmt.Fprintf(w, "return buf, nil\n")
+
+ fmt.Fprintf(w, "}\n")
+}
+
+func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
+ fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
+
+ fmt.Fprintf(w, "\to := binary.BigEndian\n")
+ fmt.Fprintf(w, "\t_ = o\n")
+ fmt.Fprintf(w, "\tpos := 0\n")
+ fmt.Fprintf(w, "\t_ = pos\n")
+
+ decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
+ t, ok := BaseTypeNames[typ]
+ if !ok {
+ return false
+ }
+
+ isArray := length > 0 || sizefrom != ""
+
+ switch t {
+ case I8, U8, I16, U16, I32, U32, I64, U64, F64:
+ if isArray {
+ if alloc {
+ if length != 0 {
+ fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
+ } else if sizefrom != "" {
+ fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
+ }
+ }
+ fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
+ }
+ }
+
+ switch t {
+ case I8, U8:
+ if isArray {
+ fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
+ } else {
+ fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
+ }
+ fmt.Fprintf(w, "\tpos += 1\n")
+ if isArray {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ case I16, U16:
+ if isArray {
+ fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
+ } else {
+ fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
+ }
+ fmt.Fprintf(w, "\tpos += 2\n")
+ if isArray {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ case I32, U32:
+ if isArray {
+ fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
+ } else {
+ fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
+ }
+ fmt.Fprintf(w, "\tpos += 4\n")
+ if isArray {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ case I64, U64:
+ if isArray {
+ fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
+ } else {
+ fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
+ }
+ fmt.Fprintf(w, "\tpos += 8\n")
+ if isArray {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ case F64:
+ if isArray {
+ fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
+ } else {
+ fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
+ }
+ fmt.Fprintf(w, "\tpos += 8\n")
+ if isArray {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ case BOOL:
+ fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
+ fmt.Fprintf(w, "\tpos += 1\n")
+ case STRING:
+ if length != 0 {
+ fmt.Fprintf(w, "\t{\n")
+ fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
+ fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
+ fmt.Fprintf(w, "\tpos += %d\n", length)
+ fmt.Fprintf(w, "\t}\n")
+ } else {
+ fmt.Fprintf(w, "\t{\n")
+ fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
+ fmt.Fprintf(w, "\tpos += 4\n")
+ fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
+ fmt.Fprintf(w, "\tpos += len(%s)\n", name)
+ fmt.Fprintf(w, "\t}\n")
+ }
+ default:
+ fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
+ return false
+ }
+ return true
+ }
+
+ lvl := 0
+ var decodeFields func(fields []*Field, parentName string)
+ decodeFields = func(fields []*Field, parentName string) {
+ lvl++
+ defer func() { lvl-- }()
+
+ n := 0
+ for _, field := range fields {
+ // skip internal fields
+ switch strings.ToLower(field.Name) {
+ case /*crcField,*/ msgIdField:
+ continue
+ case clientIndexField, contextField:
+ if n == 0 {
+ continue
+ }
+ }
+ n++
+
+ fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
+ name := fmt.Sprintf("%s.%s", parentName, fieldName)
+ sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
+ var sizeFromName string
+ if sizeFrom != "" {
+ sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
+ }
+
+ fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
+
+ if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
+ continue
+ }
+
+ //char := fmt.Sprintf("v%d", lvl)
+ index := fmt.Sprintf("j%d", lvl)
+
+ if field.Array {
+ if field.Length > 0 {
+ fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
+ } else if field.SizeFrom != "" {
+ fieldType := getFieldType(ctx, field)
+ if strings.HasPrefix(fieldType, "[]") {
+ fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
+ }
+ fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
+ }
+
+ /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
+ fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
+ name = char*/
+ name = fmt.Sprintf("%s[%s]", name, index)
+ }
+
+ if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
+ if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
+ } else {
+ fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
+ }
+ } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
+ if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
+ } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
+ decodeFields(typ.Fields, name)
+ } else {
+ fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
+ }
+ } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
+ decodeFields(typ.Fields, name)
+ } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
+ maxSize := getUnionSize(ctx.file, union)
+ fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
+ fmt.Fprintf(w, "\tpos += %d\n", maxSize)
+ } else {
+ fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
+ }
+
+ if field.Array {
+ fmt.Fprintf(w, "\t}\n")
+ }
+ }
+ }
+
+ decodeFields(fields, "m")
+
+ fmt.Fprintf(w, "return nil\n")
+
+ fmt.Fprintf(w, "}\n")
+}
+
+func getFieldType(ctx *GenFile, field *Field) string {
+ //fieldName := strings.TrimPrefix(field.Name, "_")
+ //fieldName = camelCaseName(fieldName)
+ //fieldName := field.GoName
+
+ dataType := convertToGoType(ctx.file, field.Type)
+ fieldType := dataType
+
+ // check if it is array
+ if field.Length > 0 || field.SizeFrom != "" {
+ if dataType == "uint8" {
+ dataType = "byte"
+ }
+ if dataType == "string" && field.Array {
+ fieldType = "string"
+ dataType = "byte"
+ } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
+ fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
+ } else {
+ fieldType = "[]" + dataType
+ }
+ }
+
+ return fieldType
+}
+
+func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
+ field := fields[i]
+
+ //fieldName := strings.TrimPrefix(field.Name, "_")
+ //fieldName = camelCaseName(fieldName)
+ fieldName := field.GoName
+
+ dataType := convertToGoType(ctx.file, field.Type)
+ fieldType := dataType
+
+ // generate length field for strings
+ if field.Type == "string" && field.Length == 0 {
+ fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
+ }
+
+ // check if it is array
+ if field.Length > 0 || field.SizeFrom != "" {
+ if dataType == "uint8" {
+ dataType = "byte"
+ }
+ if dataType == "string" && field.Array {
+ fieldType = "string"
+ dataType = "byte"
+ } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
+ fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
+ } else {
+ fieldType = "[]" + dataType
+ }
+ }
+ fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
+
+ fieldTags := map[string]string{}
+
+ if field.Length > 0 && field.Array {
+ // fixed size array
+ fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
+ } else {
+ for _, f := range fields {
+ if f.SizeFrom == field.Name {
+ // variable sized array
+ //sizeOfName := camelCaseName(f.Name)
+ fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
+ }
+ }
+ }
+
+ if ctx.IncludeBinapiNames {
+ typ := fromApiType(field.Type)
+ if field.Array {
+ if field.Length > 0 {
+ fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
+ } else if field.SizeFrom != "" {
+ fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
+ }
+ } else {
+ fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
+ }
+ }
+ if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
+ fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
+ }
+ if def, ok := field.Meta["default"]; ok && def != nil {
+ actual := getActualType(ctx.file, fieldType)
+ if t, ok := binapiTypes[actual]; ok && t != "float64" {
+ defnum := int(def.(float64))
+ fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
+ } else {
+ fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
+ }
+ }
+
+ fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
+
+ if len(fieldTags) > 0 {
+ fmt.Fprintf(w, "\t`")
+ var keys []string
+ for k := range fieldTags {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ var n int
+ for _, tt := range keys {
+ t, ok := fieldTags[tt]
+ if !ok {
+ continue
+ }
+ if n > 0 {
+ fmt.Fprintf(w, " ")
+ }
+ n++
+ fmt.Fprintf(w, `%s:"%s"`, tt, t)
+ }
+ fmt.Fprintf(w, "`")
+ }
+
+ fmt.Fprintln(w)
+}
+
+func generateMessageResetMethod(w io.Writer, structName string) {
+ fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
+}
+
+func generateMessageNameGetter(w io.Writer, structName, msgName string) {
+ fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
+}
+
+func generateTypeNameGetter(w io.Writer, structName, msgName string) {
+ fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
+}
+
+func generateCrcGetter(w io.Writer, structName, crc string) {
+ crc = strings.TrimPrefix(crc, "0x")
+ fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
+}
+
+func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
+ fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
+ if msgType == requestMessage {
+ fmt.Fprintf(w, "\treturn api.RequestMessage")
+ } else if msgType == replyMessage {
+ fmt.Fprintf(w, "\treturn api.ReplyMessage")
+ } else if msgType == eventMessage {
+ fmt.Fprintf(w, "\treturn api.EventMessage")
+ } else {
+ fmt.Fprintf(w, "\treturn api.OtherMessage")
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+}
diff --git a/binapigen/generate_rpc.go b/binapigen/generate_rpc.go
new file mode 100644
index 0000000..b480f4a
--- /dev/null
+++ b/binapigen/generate_rpc.go
@@ -0,0 +1,188 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+func generatePackageRPC(ctx *GenFile, w io.Writer) {
+ logf("----------------------------")
+ logf("generating RPC package: %q", ctx.file.PackageName)
+ logf("----------------------------")
+
+ fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, "import (")
+ fmt.Fprintln(w, ` "context"`)
+ fmt.Fprintln(w, ` "io"`)
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ // generate services
+ if ctx.file.Service != nil && len(ctx.file.Service.RPCs) > 0 {
+ generateServiceMethods(ctx, w, ctx.file.Service.RPCs)
+ }
+
+ // generate message registrations
+ /*fmt.Fprintln(w, "var _RPCService_desc = api.RPCDesc{")
+
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)*/
+
+ fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
+ fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
+ fmt.Fprintf(w, "var _ = context.Background\n")
+ fmt.Fprintf(w, "var _ = io.Copy\n")
+
+}
+
+func generateServiceMethods(ctx *GenFile, w io.Writer, methods []RPC) {
+ // generate services comment
+ generateComment(ctx, w, serviceApiName, "services", "service")
+
+ // generate service api
+ fmt.Fprintf(w, "type %s interface {\n", serviceApiName)
+ for _, svc := range methods {
+ generateServiceMethod(ctx, w, &svc)
+ fmt.Fprintln(w)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate client implementation
+ fmt.Fprintf(w, "type %s struct {\n", serviceImplName)
+ fmt.Fprintf(w, "\tch api.Channel\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate client constructor
+ fmt.Fprintf(w, "func New%s(ch api.Channel) %s {\n", serviceClientName, serviceApiName)
+ fmt.Fprintf(w, "\treturn &%s{ch}\n", serviceImplName)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ for _, met := range methods {
+ method := camelCaseName(met.RequestMsg)
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+
+ fmt.Fprintf(w, "func (c *%s) ", serviceImplName)
+ generateServiceMethod(ctx, w, &met)
+ fmt.Fprintln(w, " {")
+ if met.Stream {
+ streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
+ fmt.Fprintf(w, "\tstream := c.ch.SendMultiRequest(in)\n")
+ fmt.Fprintf(w, "\tx := &%s{stream}\n", streamImpl)
+ fmt.Fprintf(w, "\treturn x, nil\n")
+ } else if replyTyp := camelCaseName(met.ReplyMsg); replyTyp != "" {
+ fmt.Fprintf(w, "\tout := new(%s)\n", replyTyp)
+ fmt.Fprintf(w, "\terr:= c.ch.SendRequest(in).ReceiveReply(out)\n")
+ fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
+ fmt.Fprintf(w, "\treturn out, nil\n")
+ } else {
+ fmt.Fprintf(w, "\tc.ch.SendRequest(in)\n")
+ fmt.Fprintf(w, "\treturn nil\n")
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ if met.Stream {
+ replyTyp := camelCaseName(met.ReplyMsg)
+ method := camelCaseName(met.RequestMsg)
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+ streamApi := fmt.Sprintf("%s_%sClient", serviceApiName, method)
+
+ fmt.Fprintf(w, "type %s interface {\n", streamApi)
+ fmt.Fprintf(w, "\tRecv() (*%s, error)\n", replyTyp)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
+ fmt.Fprintf(w, "type %s struct {\n", streamImpl)
+ fmt.Fprintf(w, "\tapi.MultiRequestCtx\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "func (c *%s) Recv() (*%s, error) {\n", streamImpl, replyTyp)
+ fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp)
+ fmt.Fprintf(w, "\tstop, err := c.MultiRequestCtx.ReceiveReply(m)\n")
+ fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
+ fmt.Fprintf(w, "\tif stop { return nil, io.EOF }\n")
+ fmt.Fprintf(w, "\treturn m, nil\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+ }
+ }
+
+ // TODO: generate service descriptor
+ /*fmt.Fprintf(w, "var %s = api.%s{\n", serviceDescName, serviceDescType)
+ fmt.Fprintf(w, "\tServiceName: \"%s\",\n", ctx.moduleName)
+ fmt.Fprintf(w, "\tHandlerType: (*%s)(nil),\n", serviceApiName)
+ fmt.Fprintf(w, "\tMethods: []api.MethodDesc{\n")
+ for _, method := range methods {
+ fmt.Fprintf(w, "\t {\n")
+ fmt.Fprintf(w, "\t MethodName: \"%s\",\n", method.Name)
+ fmt.Fprintf(w, "\t },\n")
+ }
+ fmt.Fprintf(w, "\t},\n")
+ //fmt.Fprintf(w, "\tCompatibility: %s,\n", messageCrcName)
+ //fmt.Fprintf(w, "\tMetadata: reflect.TypeOf((*%s)(nil)).Elem().PkgPath(),\n", serviceApiName)
+ fmt.Fprintf(w, "\tMetadata: \"%s\",\n", ctx.inputFile)
+ fmt.Fprintln(w, "}")*/
+
+ fmt.Fprintln(w)
+}
+
+func generateServiceMethod(ctx *GenFile, w io.Writer, rpc *RPC) {
+ reqTyp := camelCaseName(rpc.RequestMsg)
+
+ logf(" writing RPC: %+v", reqTyp)
+
+ // method name is same as parameter type name by default
+ method := reqTyp
+ if rpc.Stream {
+ // use Dump as prefix instead of suffix for stream services
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+ }
+
+ params := fmt.Sprintf("in *%s", reqTyp)
+ returns := "error"
+
+ if replyType := camelCaseName(rpc.ReplyMsg); replyType != "" {
+ var replyTyp string
+ if rpc.Stream {
+ replyTyp = fmt.Sprintf("%s_%sClient", serviceApiName, method)
+ } else {
+ replyTyp = fmt.Sprintf("*%s", replyType)
+ }
+ returns = fmt.Sprintf("(%s, error)", replyTyp)
+ }
+
+ fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns)
+}
diff --git a/binapigen/generate_test.go b/binapigen/generate_test.go
new file mode 100644
index 0000000..5a2a07a
--- /dev/null
+++ b/binapigen/generate_test.go
@@ -0,0 +1,446 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "os"
+ "testing"
+
+ . "github.com/onsi/gomega"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+const testOutputDir = "test_output_directory"
+
+func GenerateFromFile(apiDir, outputDir string, opts Options) error {
+ // parse API files
+ apifiles, err := vppapi.ParseDir(apiDir)
+ if err != nil {
+ return err
+ }
+
+ g, err := New(opts, apifiles)
+ if err != nil {
+ return err
+ }
+ for _, file := range g.Files {
+ if !file.Generate {
+ continue
+ }
+ GenerateBinapiFile(g, file, outputDir)
+ if file.Service != nil {
+ GenerateRPC(g, file, outputDir)
+ }
+ }
+
+ if err = g.Generate(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func TestGenerateFromFile(t *testing.T) {
+ RegisterTestingT(t)
+
+ // remove directory created during test
+ defer os.RemoveAll(testOutputDir)
+
+ err := GenerateFromFile("testdata/acl.api.json", testOutputDir, Options{})
+ Expect(err).ShouldNot(HaveOccurred())
+ fileInfo, err := os.Stat(testOutputDir + "/acl/acl.ba.go")
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(fileInfo.IsDir()).To(BeFalse())
+ Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go"))
+}
+
+func TestGenerateFromFileInputError(t *testing.T) {
+ RegisterTestingT(t)
+
+ err := GenerateFromFile("testdata/nonexisting.json", testOutputDir, Options{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid input file name"))
+}
+
+func TestGenerateFromFileReadJsonError(t *testing.T) {
+ RegisterTestingT(t)
+
+ err := GenerateFromFile("testdata/input-read-json-error.json", testOutputDir, Options{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid input file name"))
+}
+
+func TestGenerateFromFileGeneratePackageError(t *testing.T) {
+ RegisterTestingT(t)
+
+ // generate package throws panic, recover after it
+ defer func() {
+ if recovery := recover(); recovery != nil {
+ t.Logf("Recovered from panic: %v", recovery)
+ }
+ os.RemoveAll(testOutputDir)
+ }()
+
+ err := GenerateFromFile("testdata/input-generate-error.json", testOutputDir, Options{})
+ Expect(err).Should(HaveOccurred())
+}
+
+/*func TestGetContext(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ result, err := newContext("testdata/af_packet.api.json", outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(result).ToNot(BeNil())
+ Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.ba.go"))
+}
+
+func TestGetContextNoJsonFile(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ result, err := newContext("testdata/input.txt", outDir)
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid input file name"))
+ Expect(result).To(BeNil())
+}
+
+func TestGetContextInterfaceJson(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ result, err := newContext("testdata/ip.api.json", outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(result).ToNot(BeNil())
+ Expect(result.outputFile)
+ Expect(result.outputFile).To(BeEquivalentTo(outDir + "/ip/ip.ba.go"))
+}*/
+
+/*func TestGeneratePackage(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(GenFile)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := ioutil.ReadFile("testdata/ip.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ jsonRoot, err := parseInputJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ testCtx.file, err = parseModule(testCtx, jsonRoot)
+ Expect(err).ShouldNot(HaveOccurred())
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ defer os.RemoveAll(outDir)
+
+ // prepare writer
+ writer := bufio.NewWriter(outFile)
+ Expect(writer.Buffered()).To(BeZero())
+ err = generatePackage(testCtx, writer)
+ Expect(err).ShouldNot(HaveOccurred())
+}
+
+func TestGenerateMessageType(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(GenFile)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := ioutil.ReadFile("testdata/ip.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ jsonRoot, err := parseInputJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ testCtx.file, err = parseModule(testCtx, jsonRoot)
+ Expect(err).ShouldNot(HaveOccurred())
+ defer os.RemoveAll(outDir)
+
+ // prepare writer
+ writer := bufio.NewWriter(outFile)
+
+ for _, msg := range testCtx.file.Messages {
+ generateMessage(testCtx, writer, &msg)
+ Expect(writer.Buffered()).ToNot(BeZero())
+ }
+}*/
+
+/*func TestGenerateMessageName(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := readFile("testdata/ip.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ testCtx.inputBuff = bytes.NewBuffer(inputData)
+ inFile, _ := parseJSON(inputData)
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ defer os.RemoveAll(outDir)
+
+ // prepare writer
+ writer := bufio.NewWriter(outFile)
+
+ types := inFile.Map("types")
+ Expect(types.Len()).To(BeEquivalentTo(1))
+ for i := 0; i < types.Len(); i++ {
+ typ := types.At(i)
+ Expect(writer.Buffered()).To(BeZero())
+ err := generateMessage(testCtx, writer, typ, false)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(writer.Buffered()).ToNot(BeZero())
+
+ }
+}
+
+func TestGenerateMessageFieldTypes(t *testing.T) {
+ // expected results according to acl.api.json in testdata
+ expectedTypes := []string{
+ "\tIsPermit uint8",
+ "\tIsIpv6 uint8",
+ "\tSrcIPAddr []byte `struc:\"[16]byte\"`",
+ "\tSrcIPPrefixLen uint8",
+ "\tDstIPAddr []byte `struc:\"[16]byte\"`",
+ "\tDstIPPrefixLen uint8",
+ "\tProto uint8",
+ "\tSrcportOrIcmptypeFirst uint16",
+ "\tSrcportOrIcmptypeLast uint16",
+ "\tDstportOrIcmpcodeFirst uint16",
+ "\tDstportOrIcmpcodeLast uint16",
+ "\tTCPFlagsMask uint8",
+ "\tTCPFlagsValue uint8"}
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := readFile("testdata/acl.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ inFile, err := parseJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(inFile).ToNot(BeNil())
+
+ // test types
+ types := inFile.Map("types")
+ fields := make([]string, 0)
+ for i := 0; i < types.Len(); i++ {
+ for j := 0; j < types.At(i).Len(); j++ {
+ field := types.At(i).At(j)
+ if field.GetType() == jsongo.TypeArray {
+ err := processMessageField(testCtx, &fields, field, false)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1]))
+ }
+ }
+ }
+}
+
+func TestGenerateMessageFieldMessages(t *testing.T) {
+ // expected results according to acl.api.json in testdata
+ expectedFields := []string{"\tMajor uint32", "\tMinor uint32", "\tRetval int32",
+ "\tVpePid uint32", "\tACLIndex uint32", "\tTag []byte `struc:\"[64]byte\"`",
+ "\tCount uint32"}
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := readFile("testdata/acl.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ inFile, err := parseJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(inFile).ToNot(BeNil())
+
+ // test message fields
+ messages := inFile.Map("messages")
+ customIndex := 0
+ fields := make([]string, 0)
+ for i := 0; i < messages.Len(); i++ {
+ for j := 0; j < messages.At(i).Len(); j++ {
+ field := messages.At(i).At(j)
+ if field.GetType() == jsongo.TypeArray {
+ specificFieldName := field.At(1).Get().(string)
+ if specificFieldName == "crc" || specificFieldName == "_vl_msg_id" ||
+ specificFieldName == "client_index" || specificFieldName == "context" {
+ continue
+ }
+ err := processMessageField(testCtx, &fields, field, false)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(fields[customIndex]).To(BeEquivalentTo(expectedFields[customIndex]))
+ customIndex++
+ if customIndex >= len(expectedFields) {
+ // there is too much fields now for one UT...
+ return
+ }
+ }
+ }
+ }
+}
+
+func TestGeneratePackageHeader(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+
+ // prepare input/output output files
+ inputData, err := readFile("testdata/acl.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ inFile, err := parseJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ defer os.RemoveAll(outDir)
+ // prepare writer
+ writer := bufio.NewWriter(outFile)
+ Expect(writer.Buffered()).To(BeZero())
+ generateHeader(testCtx, writer, inFile)
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestGenerateMessageCommentType(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+ testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
+
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ writer := bufio.NewWriter(outFile)
+ defer os.RemoveAll(outDir)
+ Expect(writer.Buffered()).To(BeZero())
+ generateMessageComment(testCtx, writer, "test-struct", "msg-name", true)
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestGenerateMessageCommentMessage(t *testing.T) {
+ RegisterTestingT(t)
+ // prepare context
+ testCtx := new(context)
+ testCtx.packageName = "test-package-name"
+ testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
+
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ writer := bufio.NewWriter(outFile)
+ defer os.RemoveAll(outDir)
+ Expect(writer.Buffered()).To(BeZero())
+ generateMessageComment(testCtx, writer, "test-struct", "msg-name", false)
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestGenerateMessageNameGetter(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ writer := bufio.NewWriter(outFile)
+ defer os.RemoveAll(outDir)
+ Expect(writer.Buffered()).To(BeZero())
+ generateMessageNameGetter(writer, "test-struct", "msg-name")
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestGenerateTypeNameGetter(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ writer := bufio.NewWriter(outFile)
+ defer os.RemoveAll(outDir)
+ Expect(writer.Buffered()).To(BeZero())
+ generateTypeNameGetter(writer, "test-struct", "msg-name")
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestGenerateCrcGetter(t *testing.T) {
+ RegisterTestingT(t)
+ outDir := "test_output_directory"
+ outFile, err := os.Create(outDir)
+ Expect(err).ShouldNot(HaveOccurred())
+ writer := bufio.NewWriter(outFile)
+ defer os.RemoveAll(outDir)
+ Expect(writer.Buffered()).To(BeZero())
+ generateCrcGetter(writer, "test-struct", "msg-name")
+ Expect(writer.Buffered()).ToNot(BeZero())
+}
+
+func TestTranslateVppType(t *testing.T) {
+ RegisterTestingT(t)
+ context := new(context)
+ typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"}
+ expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"}
+ var translated []string
+ for _, value := range typesToTranslate {
+ translated = append(translated, convertToGoType(context, value, false))
+ }
+ for index, value := range expected {
+ Expect(value).To(BeEquivalentTo(translated[index]))
+ }
+
+}
+
+func TestTranslateVppTypeArray(t *testing.T) {
+ RegisterTestingT(t)
+ context := new(context)
+ translated := convertToGoType(context, "u8", true)
+ Expect(translated).To(BeEquivalentTo("byte"))
+}
+
+func TestTranslateVppUnknownType(t *testing.T) {
+ defer func() {
+ if recovery := recover(); recovery != nil {
+ t.Logf("Recovered from panic: %v", recovery)
+ }
+ }()
+ context := new(context)
+ convertToGoType(context, "?", false)
+}
+
+func TestCamelCase(t *testing.T) {
+ RegisterTestingT(t)
+ // test camel case functionality
+ expected := "allYourBaseAreBelongToUs"
+ result := camelCaseName("all_your_base_are_belong_to_us")
+ Expect(expected).To(BeEquivalentTo(result))
+ // test underscore
+ expected = "_"
+ result = camelCaseName(expected)
+ Expect(expected).To(BeEquivalentTo(result))
+ // test all lower
+ expected = "lower"
+ result = camelCaseName(expected)
+ Expect(expected).To(BeEquivalentTo(result))
+}
+
+func TestCommonInitialisms(t *testing.T) {
+ RegisterTestingT(t)
+
+ for key, value := range commonInitialisms {
+ Expect(value).ShouldNot(BeFalse())
+ Expect(key).ShouldNot(BeEmpty())
+ }
+}
+*/
diff --git a/binapigen/generator.go b/binapigen/generator.go
new file mode 100644
index 0000000..9471462
--- /dev/null
+++ b/binapigen/generator.go
@@ -0,0 +1,172 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/sirupsen/logrus"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+type Options struct {
+ VPPVersion string // version of VPP that produced API files
+
+ FilesToGenerate []string // list of API files to generate
+
+ ImportPrefix string // defines import path prefix for importing types
+ ImportTypes bool // generate packages for import types
+ IncludeAPIVersion bool // include constant with API version string
+ IncludeComments bool // include parts of original source in comments
+ IncludeBinapiNames bool // include binary API names as struct tag
+ IncludeServices bool // include service interface with client implementation
+ IncludeVppVersion bool // include info about used VPP version
+}
+
+type Generator struct {
+ Options
+
+ Files []*File
+ FilesByPath map[string]*File
+ FilesByName map[string]*File
+
+ enumsByName map[string]*Enum
+ aliasesByName map[string]*Alias
+ structsByName map[string]*Struct
+ unionsByName map[string]*Union
+
+ genfiles []*GenFile
+}
+
+func New(opts Options, apifiles []*vppapi.File) (*Generator, error) {
+ g := &Generator{
+ Options: opts,
+ FilesByPath: make(map[string]*File),
+ FilesByName: make(map[string]*File),
+ enumsByName: map[string]*Enum{},
+ aliasesByName: map[string]*Alias{},
+ structsByName: map[string]*Struct{},
+ unionsByName: map[string]*Union{},
+ }
+
+ logrus.Debugf("adding %d VPP API files to generator", len(apifiles))
+ for _, apifile := range apifiles {
+ filename := apifile.Path
+ if filename == "" {
+ filename = apifile.Name
+ }
+ if _, ok := g.FilesByPath[filename]; ok {
+ return nil, fmt.Errorf("duplicate file name: %q", filename)
+ }
+ if _, ok := g.FilesByName[apifile.Name]; ok {
+ return nil, fmt.Errorf("duplicate file: %q", apifile.Name)
+ }
+
+ file, err := newFile(g, apifile)
+ if err != nil {
+ return nil, err
+ }
+ g.Files = append(g.Files, file)
+ g.FilesByPath[filename] = file
+ g.FilesByName[apifile.Name] = file
+
+ logrus.Debugf("added file %q (path: %v)", apifile.Name, apifile.Path)
+ if len(file.Imports) > 0 {
+ logrus.Debugf(" - %d imports: %v", len(file.Imports), file.Imports)
+ }
+ }
+
+ logrus.Debugf("Checking %d files to generate: %v", len(opts.FilesToGenerate), opts.FilesToGenerate)
+ for _, genfile := range opts.FilesToGenerate {
+ file, ok := g.FilesByPath[genfile]
+ if !ok {
+ file, ok = g.FilesByName[genfile]
+ if !ok {
+ return nil, fmt.Errorf("no API file found for: %v", genfile)
+ }
+ }
+ file.Generate = true
+ if opts.ImportTypes {
+ for _, impFile := range file.importedFiles(g) {
+ impFile.Generate = true
+ }
+ }
+ }
+
+ logrus.Debugf("Resolving imported types")
+ for _, file := range g.Files {
+ if !file.Generate {
+ continue
+ }
+ importedFiles := file.importedFiles(g)
+ file.loadTypeImports(g, importedFiles)
+ }
+
+ return g, nil
+}
+
+func (g *Generator) Generate() error {
+ if len(g.genfiles) == 0 {
+ return fmt.Errorf("no files to generate")
+ }
+
+ logrus.Infof("Generating %d files", len(g.genfiles))
+ for _, genfile := range g.genfiles {
+ if err := writeSourceTo(genfile.filename, genfile.buf.Bytes()); err != nil {
+ return fmt.Errorf("writing source for RPC package %s failed: %v", genfile.filename, err)
+ }
+ }
+ return nil
+}
+
+func (g *Generator) NewGenFile(filename string) *GenFile {
+ f := &GenFile{
+ Generator: g,
+ filename: filename,
+ }
+ g.genfiles = append(g.genfiles, f)
+ return f
+}
+
+func writeSourceTo(outputFile string, b []byte) error {
+ // create output directory
+ packageDir := filepath.Dir(outputFile)
+ if err := os.MkdirAll(packageDir, 0775); err != nil {
+ return fmt.Errorf("creating output dir %s failed: %v", packageDir, err)
+ }
+
+ // format generated source code
+ gosrc, err := format.Source(b)
+ if err != nil {
+ _ = ioutil.WriteFile(outputFile, b, 0666)
+ return fmt.Errorf("formatting source code failed: %v", err)
+ }
+
+ // write generated code to output file
+ if err := ioutil.WriteFile(outputFile, gosrc, 0666); err != nil {
+ return fmt.Errorf("writing to output file %s failed: %v", outputFile, err)
+ }
+
+ lines := bytes.Count(gosrc, []byte("\n"))
+ logf("wrote %d lines (%d bytes) of code to: %q", lines, len(gosrc), outputFile)
+
+ return nil
+}
diff --git a/binapigen/generator_test.go b/binapigen/generator_test.go
new file mode 100644
index 0000000..ddbda99
--- /dev/null
+++ b/binapigen/generator_test.go
@@ -0,0 +1,110 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "testing"
+)
+
+func TestBinapiTypeSizes(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expsize int
+ }{
+ {name: "basic1", input: "u8", expsize: 1},
+ {name: "basic2", input: "i8", expsize: 1},
+ {name: "basic3", input: "u16", expsize: 2},
+ {name: "basic4", input: "i32", expsize: 4},
+ {name: "string", input: "string", expsize: 1},
+ {name: "invalid1", input: "x", expsize: 0},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ size := getBinapiTypeSize(test.input)
+ if size != test.expsize {
+ t.Errorf("expected %d, got %d", test.expsize, size)
+ }
+ })
+ }
+}
+
+/*func TestSizeOfType(t *testing.T) {
+ tests := []struct {
+ name string
+ input StructType
+ expsize int
+ }{
+ {
+ name: "basic1",
+ input: StructType{
+ Fields: []Field{
+ {Type: "u8"},
+ },
+ },
+ expsize: 1,
+ },
+ {
+ name: "basic2",
+ input: Type{
+ Fields: []Field{
+ {Type: "u8", Length: 4},
+ },
+ },
+ expsize: 4,
+ },
+ {
+ name: "basic3",
+ input: Type{
+ Fields: []Field{
+ {Type: "u8", Length: 16},
+ },
+ },
+ expsize: 16,
+ },
+ {
+ name: "withEnum",
+ input: Type{
+ Fields: []Field{
+ {Type: "u16"},
+ {Type: "vl_api_myenum_t"},
+ },
+ },
+ expsize: 6,
+ },
+ {
+ name: "invalid1",
+ input: Type{
+ Fields: []Field{
+ {Type: "x", Length: 16},
+ },
+ },
+ expsize: 0,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ module := &File{
+ Enums: []Enum{
+ {Name: "myenum", Type: "u32"},
+ },
+ }
+ size := getSizeOfType(module, &test.input)
+ if size != test.expsize {
+ t.Errorf("expected %d, got %d", test.expsize, size)
+ }
+ })
+ }
+}*/
diff --git a/binapigen/run.go b/binapigen/run.go
new file mode 100644
index 0000000..441c43d
--- /dev/null
+++ b/binapigen/run.go
@@ -0,0 +1,89 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/sirupsen/logrus"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+var debugMode = true
+
+func logf(f string, v ...interface{}) {
+ if debugMode {
+ logrus.Debugf(f, v...)
+ }
+}
+
+func GenerateBinapiFile(gen *Generator, file *File, outputDir string) *GenFile {
+ packageDir := filepath.Join(outputDir, file.PackageName)
+ filename := filepath.Join(packageDir, file.PackageName+outputFileExt)
+
+ g := gen.NewGenFile(filename)
+ g.file = file
+ g.packageDir = filepath.Join(outputDir, file.PackageName)
+
+ generatePackage(g, &g.buf)
+
+ return g
+}
+
+func GenerateRPC(gen *Generator, file *File, outputDir string) *GenFile {
+ packageDir := filepath.Join(outputDir, file.PackageName)
+ filename := filepath.Join(packageDir, file.PackageName+rpcFileSuffix+outputFileExt)
+
+ g := gen.NewGenFile(filename)
+ g.file = file
+ g.packageDir = filepath.Join(outputDir, file.PackageName)
+
+ generatePackageRPC(g, &g.buf)
+
+ return g
+}
+
+func Run(apiDir string, opts Options, f func(*Generator) error) {
+ if err := run(apiDir, opts, f); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
+ os.Exit(1)
+ }
+}
+
+func run(apiDir string, opts Options, f func(*Generator) error) error {
+ // parse API files
+ apifiles, err := vppapi.ParseDir(apiDir)
+ if err != nil {
+ return err
+ }
+
+ g, err := New(opts, apifiles)
+ if err != nil {
+ return err
+ }
+
+ if err := f(g); err != nil {
+ return err
+ }
+
+ if err = g.Generate(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/binapigen/types.go b/binapigen/types.go
new file mode 100644
index 0000000..0dbbeb1
--- /dev/null
+++ b/binapigen/types.go
@@ -0,0 +1,271 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "strings"
+
+ "github.com/sirupsen/logrus"
+)
+
+// define api
+const (
+ defineApiPrefix = "vl_api_"
+ defineApiSuffix = "_t"
+)
+
+// BaseType represents base types in VPP binary API.
+type BaseType int
+
+const (
+ U8 BaseType = iota + 1
+ I8
+ U16
+ I16
+ U32
+ I32
+ U64
+ I64
+ F64
+ BOOL
+ STRING
+)
+
+var (
+ BaseTypes = map[BaseType]string{
+ U8: "u8",
+ I8: "i8",
+ U16: "u16",
+ I16: "i16",
+ U32: "u32",
+ I32: "i32",
+ U64: "u64",
+ I64: "i64",
+ F64: "f64",
+ BOOL: "bool",
+ STRING: "string",
+ }
+ BaseTypeNames = map[string]BaseType{
+ "u8": U8,
+ "i8": I8,
+ "u16": U16,
+ "i16": I16,
+ "u32": U32,
+ "i32": I32,
+ "u64": U64,
+ "i64": I64,
+ "f64": F64,
+ "bool": BOOL,
+ "string": STRING,
+ }
+)
+
+var BaseTypeSizes = map[BaseType]int{
+ U8: 1,
+ I8: 1,
+ U16: 2,
+ I16: 2,
+ U32: 4,
+ I32: 4,
+ U64: 8,
+ I64: 8,
+ F64: 8,
+ BOOL: 1,
+ STRING: 1,
+}
+
+type Kind int
+
+const (
+ _ = iota
+ Uint8Kind
+ Int8Kind
+ Uint16Kind
+ Int16Kind
+ Uint32Kind
+ Int32Kind
+ Uint64Kind
+ Int64Kind
+ Float64Kind
+ BoolKind
+ StringKind
+ EnumKind
+ AliasKind
+ StructKind
+ UnionKind
+ MessageKind
+)
+
+// toApiType returns name that is used as type reference in VPP binary API
+func toApiType(name string) string {
+ return defineApiPrefix + name + defineApiSuffix
+}
+
+func fromApiType(typ string) string {
+ name := typ
+ name = strings.TrimPrefix(name, defineApiPrefix)
+ name = strings.TrimSuffix(name, defineApiSuffix)
+ return name
+}
+
+func getSizeOfType(module *File, typ *Struct) (size int) {
+ for _, field := range typ.Fields {
+ enum := getEnumByRef(module, field.Type)
+ if enum != nil {
+ size += getSizeOfBinapiTypeLength(enum.Type, field.Length)
+ continue
+ }
+ size += getSizeOfBinapiTypeLength(field.Type, field.Length)
+ }
+ return size
+}
+
+func getEnumByRef(file *File, ref string) *Enum {
+ for _, typ := range file.Enums {
+ if ref == toApiType(typ.Name) {
+ return typ
+ }
+ }
+ return nil
+}
+
+func getTypeByRef(file *File, ref string) *Struct {
+ for _, typ := range file.Structs {
+ if ref == toApiType(typ.Name) {
+ return typ
+ }
+ }
+ return nil
+}
+
+func getAliasByRef(file *File, ref string) *Alias {
+ for _, alias := range file.Aliases {
+ if ref == toApiType(alias.Name) {
+ return alias
+ }
+ }
+ return nil
+}
+
+func getUnionByRef(file *File, ref string) *Union {
+ for _, union := range file.Unions {
+ if ref == toApiType(union.Name) {
+ return union
+ }
+ }
+ return nil
+}
+
+func getBinapiTypeSize(binapiType string) (size int) {
+ typName := BaseTypeNames[binapiType]
+ return BaseTypeSizes[typName]
+}
+
+// binapiTypes is a set of types used VPP binary API for translation to Go types
+var binapiTypes = map[string]string{
+ "u8": "uint8",
+ "i8": "int8",
+ "u16": "uint16",
+ "i16": "int16",
+ "u32": "uint32",
+ "i32": "int32",
+ "u64": "uint64",
+ "i64": "int64",
+ "f64": "float64",
+}
+var BaseTypesGo = map[BaseType]string{
+ U8: "uint8",
+ I8: "int8",
+ U16: "uint16",
+ I16: "int16",
+ U32: "uint32",
+ I32: "int32",
+ U64: "uint64",
+ I64: "int64",
+ F64: "float64",
+ BOOL: "bool",
+ STRING: "string",
+}
+
+func getActualType(file *File, typ string) (actual string) {
+ for _, enum := range file.Enums {
+ if enum.GoName == typ {
+ return enum.Type
+ }
+ }
+ for _, alias := range file.Aliases {
+ if alias.GoName == typ {
+ return alias.Type
+ }
+ }
+ return typ
+}
+
+// convertToGoType translates the VPP binary API type into Go type
+func convertToGoType(file *File, binapiType string) (typ string) {
+ if t, ok := binapiTypes[binapiType]; ok {
+ // basic types
+ typ = t
+ } else if r, ok := file.refmap[binapiType]; ok {
+ // specific types (enums/types/unions)
+ typ = camelCaseName(r)
+ } else {
+ switch binapiType {
+ case "bool", "string":
+ typ = binapiType
+ default:
+ // fallback type
+ logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType)
+ typ = "byte"
+ }
+ }
+ return typ
+}
+
+func getSizeOfBinapiTypeLength(typ string, length int) (size int) {
+ if n := getBinapiTypeSize(typ); n > 0 {
+ if length > 0 {
+ return n * length
+ } else {
+ return n
+ }
+ }
+
+ return
+}
+
+func getUnionSize(file *File, union *Union) (maxSize int) {
+ for _, field := range union.Fields {
+ typ := getTypeByRef(file, field.Type)
+ if typ != nil {
+ if size := getSizeOfType(file, typ); size > maxSize {
+ maxSize = size
+ }
+ continue
+ }
+ alias := getAliasByRef(file, field.Type)
+ if alias != nil {
+ if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize {
+ maxSize = size
+ }
+ continue
+ } else {
+ logf("no type or alias found for union %s field type %q", union.Name, field.Type)
+ continue
+ }
+ }
+ logf("getUnionSize: %s %+v max=%v", union.Name, union.Fields, maxSize)
+ return
+}
diff --git a/binapigen/validate.go b/binapigen/validate.go
new file mode 100644
index 0000000..2dae903
--- /dev/null
+++ b/binapigen/validate.go
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "strings"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ serviceEventPrefix = "want_"
+ serviceDumpSuffix = "_dump"
+ serviceDetailsSuffix = "_details"
+ serviceReplySuffix = "_reply"
+)
+
+func validateService(svc vppapi.Service) {
+ for _, rpc := range svc.RPCs {
+ validateRPC(rpc)
+ }
+}
+
+func validateRPC(rpc vppapi.RPC) {
+ if len(rpc.Events) > 0 {
+ // EVENT service
+ if !strings.HasPrefix(rpc.RequestMsg, serviceEventPrefix) {
+ logrus.Warnf("unusual EVENTS service: %+v\n"+
+ "- events service %q does not have %q prefix in request.",
+ rpc, rpc.Name, serviceEventPrefix)
+ }
+ } else if rpc.Stream {
+ // STREAM service
+ if !strings.HasSuffix(rpc.RequestMsg, serviceDumpSuffix) {
+ logrus.Warnf("unusual STREAM service: %+v\n"+
+ "- stream service %q does not have %q suffix in request.",
+ rpc, rpc.Name, serviceDumpSuffix)
+ }
+ if !strings.HasSuffix(rpc.ReplyMsg, serviceDetailsSuffix) && !strings.HasSuffix(rpc.StreamMsg, serviceDetailsSuffix) {
+ logrus.Warnf("unusual STREAM service: %+v\n"+
+ "- stream service %q does not have %q suffix in reply or stream msg.",
+ rpc, rpc.Name, serviceDetailsSuffix)
+ }
+ } else if rpc.ReplyMsg != "" {
+ // REQUEST service
+ // some messages might have `null` reply (for example: memclnt)
+ if !strings.HasSuffix(rpc.ReplyMsg, serviceReplySuffix) {
+ logrus.Warnf("unusual REQUEST service: %+v\n"+
+ "- service %q does not have %q suffix in reply.",
+ rpc, rpc.Name, serviceReplySuffix)
+ }
+ }
+}
diff --git a/binapigen/vppapi/api.go b/binapigen/vppapi/api.go
new file mode 100644
index 0000000..06d9046
--- /dev/null
+++ b/binapigen/vppapi/api.go
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package vppapi
+
+type File struct {
+ Name string
+ Path string
+
+ CRC string
+ Options map[string]string `json:",omitempty"`
+
+ Imports []string `json:",omitempty"`
+
+ AliasTypes []AliasType `json:",omitempty"`
+ EnumTypes []EnumType `json:",omitempty"`
+ StructTypes []StructType `json:",omitempty"`
+ UnionTypes []UnionType `json:",omitempty"`
+ Messages []Message `json:",omitempty"`
+ Service *Service `json:",omitempty"`
+}
+
+func (x File) Version() string {
+ if x.Options != nil {
+ return x.Options[fileOptionVersion]
+ }
+ return ""
+}
+
+type AliasType struct {
+ Name string
+ Type string
+ Length int `json:",omitempty"`
+}
+
+type EnumType struct {
+ Name string
+ Type string
+ Entries []EnumEntry
+}
+
+type EnumEntry struct {
+ Name string
+ Value uint32
+}
+
+type StructType struct {
+ Name string
+ Fields []Field
+}
+
+type UnionType struct {
+ Name string
+ Fields []Field
+}
+
+type Message struct {
+ Name string
+ Fields []Field
+ CRC string
+}
+
+type Field struct {
+ Name string
+ Type string
+ Length int `json:",omitempty"`
+ Array bool `json:",omitempty"`
+ SizeFrom string `json:",omitempty"`
+ Meta map[string]interface{} `json:",omitempty"`
+}
+
+type Service struct {
+ RPCs []RPC `json:",omitempty"`
+}
+
+type RPC struct {
+ Name string
+ RequestMsg string
+ ReplyMsg string
+ Stream bool `json:",omitempty"`
+ StreamMsg string `json:",omitempty"`
+ Events []string `json:",omitempty"`
+}
diff --git a/binapigen/vppapi/integration_test.go b/binapigen/vppapi/integration_test.go
new file mode 100644
index 0000000..142017a
--- /dev/null
+++ b/binapigen/vppapi/integration_test.go
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build integration
+
+package vppapi_test
+
+import (
+ "encoding/json"
+ "testing"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+func TestParse(t *testing.T) {
+ files, err := vppapi.Parse()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, file := range files {
+ //t.Logf(" - %s: %+v", path, module)
+ b, err := json.MarshalIndent(file, "\t", " ")
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf(" - %s:\n%s", file.Name, b)
+ }
+
+ t.Logf("parsed %d files", len(files))
+
+}
diff --git a/binapigen/vppapi/parse_json.go b/binapigen/vppapi/parse_json.go
new file mode 100644
index 0000000..45b5796
--- /dev/null
+++ b/binapigen/vppapi/parse_json.go
@@ -0,0 +1,552 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package vppapi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/bennyscetbun/jsongo"
+ "github.com/sirupsen/logrus"
+)
+
+var Logger *logrus.Logger
+
+func init() {
+ if strings.Contains(os.Getenv("DEBUG_GOVPP"), "parser") {
+ Logger = logrus.StandardLogger()
+ }
+}
+
+func logf(f string, v ...interface{}) {
+ if Logger != nil {
+ Logger.Debugf(f, v...)
+ }
+}
+
+const (
+ // file
+ objAPIVersion = "vl_api_version"
+ objTypes = "types"
+ objMessages = "messages"
+ objUnions = "unions"
+ objEnums = "enums"
+ objServices = "services"
+ objAliases = "aliases"
+ objOptions = "options"
+ objImports = "imports"
+
+ // message
+ messageFieldCrc = "crc"
+
+ // alias
+ aliasFieldLength = "length"
+ aliasFieldType = "type"
+
+ // service
+ serviceFieldReply = "reply"
+ serviceFieldStream = "stream"
+ serviceFieldStreamMsg = "stream_msg"
+ serviceFieldEvents = "events"
+)
+
+const (
+ // file
+ fileOptionVersion = "version"
+
+ // field
+ fieldOptionLimit = "limit"
+ fieldOptionDefault = "default"
+
+ // service
+ serviceReplyNull = "null"
+)
+
+func parseJSON(data []byte) (module *File, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = fmt.Errorf("recovered panic: %v", e)
+ }
+ }()
+
+ // parse JSON data into objects
+ jsonRoot := new(jsongo.Node)
+ if err := json.Unmarshal(data, jsonRoot); err != nil {
+ return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
+ }
+
+ logf("file contents:")
+ for _, key := range jsonRoot.GetKeys() {
+ if jsonRoot.At(key).Len() > 0 {
+ logf(" - %2d %s", jsonRoot.At(key).Len(), key)
+ }
+ }
+
+ module = new(File)
+
+ // parse CRC
+ if crc := jsonRoot.At(objAPIVersion); crc.GetType() == jsongo.TypeValue {
+ module.CRC = crc.Get().(string)
+ }
+
+ // parse options
+ opt := jsonRoot.Map(objOptions)
+ if opt.GetType() == jsongo.TypeMap {
+ module.Options = make(map[string]string, 0)
+ for _, key := range opt.GetKeys() {
+ optionsNode := opt.At(key)
+ optionKey := key.(string)
+ optionValue := optionsNode.Get().(string)
+ module.Options[optionKey] = optionValue
+ }
+ }
+
+ // parse imports
+ imports := jsonRoot.Map(objImports)
+ module.Imports = make([]string, 0)
+ imported := make(map[string]struct{})
+ for i := 0; i < imports.Len(); i++ {
+ importNode := imports.At(i)
+ imp, err := parseImport(importNode)
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := imported[*imp]; ok {
+ logf("duplicate import found: %v", *imp)
+ continue
+ }
+ imported[*imp] = struct{}{}
+ module.Imports = append(module.Imports, *imp)
+ }
+
+ // avoid duplicate objects
+ known := make(map[string]struct{})
+ exists := func(name string) bool {
+ if _, ok := known[name]; ok {
+ logf("duplicate object found: %v", name)
+ return true
+ }
+ known[name] = struct{}{}
+ return false
+ }
+
+ // parse enum types
+ enumsNode := jsonRoot.Map(objEnums)
+ module.EnumTypes = make([]EnumType, 0)
+ for i := 0; i < enumsNode.Len(); i++ {
+ enumNode := enumsNode.At(i)
+ enum, err := parseEnum(enumNode)
+ if err != nil {
+ return nil, err
+ }
+ if exists(enum.Name) {
+ continue
+ }
+ module.EnumTypes = append(module.EnumTypes, *enum)
+ }
+
+ // parse alias types
+ aliasesNode := jsonRoot.Map(objAliases)
+ if aliasesNode.GetType() == jsongo.TypeMap {
+ module.AliasTypes = make([]AliasType, 0)
+ for _, key := range aliasesNode.GetKeys() {
+ aliasNode := aliasesNode.At(key)
+ aliasName := key.(string)
+ alias, err := parseAlias(aliasName, aliasNode)
+ if err != nil {
+ return nil, err
+ }
+ if exists(alias.Name) {
+ continue
+ }
+ module.AliasTypes = append(module.AliasTypes, *alias)
+ }
+ }
+
+ // parse struct types
+ typesNode := jsonRoot.Map(objTypes)
+ module.StructTypes = make([]StructType, 0)
+ for i := 0; i < typesNode.Len(); i++ {
+ typNode := typesNode.At(i)
+ structyp, err := parseStruct(typNode)
+ if err != nil {
+ return nil, err
+ }
+ if exists(structyp.Name) {
+ continue
+ }
+ module.StructTypes = append(module.StructTypes, *structyp)
+ }
+
+ // parse union types
+ unionsNode := jsonRoot.Map(objUnions)
+ module.UnionTypes = make([]UnionType, 0)
+ for i := 0; i < unionsNode.Len(); i++ {
+ unionNode := unionsNode.At(i)
+ union, err := parseUnion(unionNode)
+ if err != nil {
+ return nil, err
+ }
+ if exists(union.Name) {
+ continue
+ }
+ module.UnionTypes = append(module.UnionTypes, *union)
+ }
+
+ // parse messages
+ messagesNode := jsonRoot.Map(objMessages)
+ if messagesNode.GetType() == jsongo.TypeArray {
+ module.Messages = make([]Message, messagesNode.Len())
+ for i := 0; i < messagesNode.Len(); i++ {
+ msgNode := messagesNode.At(i)
+ msg, err := parseMessage(msgNode)
+ if err != nil {
+ return nil, err
+ }
+ module.Messages[i] = *msg
+ }
+ }
+
+ // parse services
+ servicesNode := jsonRoot.Map(objServices)
+ if servicesNode.GetType() == jsongo.TypeMap {
+ module.Service = &Service{
+ RPCs: make([]RPC, servicesNode.Len()),
+ }
+ for i, key := range servicesNode.GetKeys() {
+ rpcNode := servicesNode.At(key)
+ rpcName := key.(string)
+ svc, err := parseServiceRPC(rpcName, rpcNode)
+ if err != nil {
+ return nil, err
+ }
+ module.Service.RPCs[i] = *svc
+ }
+ }
+
+ return module, nil
+}
+
+// parseImport parses VPP binary API import from JSON node
+func parseImport(importNode *jsongo.Node) (*string, error) {
+ if importNode.GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for import specified")
+ }
+
+ importName, ok := importNode.Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("import name is %T, not a string", importNode.Get())
+ }
+
+ return &importName, nil
+}
+
+// parseEnum parses VPP binary API enum object from JSON node
+func parseEnum(enumNode *jsongo.Node) (*EnumType, error) {
+ if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for enum specified")
+ }
+
+ enumName, ok := enumNode.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("enum name is %T, not a string", enumNode.At(0).Get())
+ }
+ enumType, ok := enumNode.At(enumNode.Len() - 1).At("enumtype").Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("enum type invalid or missing")
+ }
+
+ enum := EnumType{
+ Name: enumName,
+ Type: enumType,
+ }
+
+ // loop through enum entries, skip first (name) and last (enumtype)
+ for j := 1; j < enumNode.Len()-1; j++ {
+ if enumNode.At(j).GetType() == jsongo.TypeArray {
+ entry := enumNode.At(j)
+
+ if entry.Len() < 2 || entry.At(0).GetType() != jsongo.TypeValue || entry.At(1).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for enum entry specified")
+ }
+
+ entryName, ok := entry.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("enum entry name is %T, not a string", entry.At(0).Get())
+ }
+ entryVal := entry.At(1).Get().(float64)
+
+ enum.Entries = append(enum.Entries, EnumEntry{
+ Name: entryName,
+ Value: uint32(entryVal),
+ })
+ }
+ }
+
+ return &enum, nil
+}
+
+// parseUnion parses VPP binary API union object from JSON node
+func parseUnion(unionNode *jsongo.Node) (*UnionType, error) {
+ if unionNode.Len() == 0 || unionNode.At(0).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for union specified")
+ }
+
+ unionName, ok := unionNode.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("union name is %T, not a string", unionNode.At(0).Get())
+ }
+
+ union := UnionType{
+ Name: unionName,
+ }
+
+ // loop through union fields, skip first (name)
+ for j := 1; j < unionNode.Len(); j++ {
+ if unionNode.At(j).GetType() == jsongo.TypeArray {
+ fieldNode := unionNode.At(j)
+
+ field, err := parseField(fieldNode)
+ if err != nil {
+ return nil, err
+ }
+
+ union.Fields = append(union.Fields, *field)
+ }
+ }
+
+ return &union, nil
+}
+
+// parseStruct parses VPP binary API type object from JSON node
+func parseStruct(typeNode *jsongo.Node) (*StructType, error) {
+ if typeNode.Len() == 0 || typeNode.At(0).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for type specified")
+ }
+
+ typeName, ok := typeNode.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("type name is %T, not a string", typeNode.At(0).Get())
+ }
+
+ typ := StructType{
+ Name: typeName,
+ }
+
+ // loop through type fields, skip first (name)
+ for j := 1; j < typeNode.Len(); j++ {
+ if typeNode.At(j).GetType() == jsongo.TypeArray {
+ fieldNode := typeNode.At(j)
+
+ field, err := parseField(fieldNode)
+ if err != nil {
+ return nil, err
+ }
+
+ typ.Fields = append(typ.Fields, *field)
+ }
+ }
+
+ return &typ, nil
+}
+
+// parseAlias parses VPP binary API alias object from JSON node
+func parseAlias(aliasName string, aliasNode *jsongo.Node) (*AliasType, error) {
+ if aliasNode.Len() == 0 || aliasNode.At(aliasFieldType).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for alias specified")
+ }
+
+ alias := AliasType{
+ Name: aliasName,
+ }
+
+ if typeNode := aliasNode.At(aliasFieldType); typeNode.GetType() == jsongo.TypeValue {
+ typ, ok := typeNode.Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get())
+ }
+ if typ != "null" {
+ alias.Type = typ
+ }
+ }
+
+ if lengthNode := aliasNode.At(aliasFieldLength); lengthNode.GetType() == jsongo.TypeValue {
+ length, ok := lengthNode.Get().(float64)
+ if !ok {
+ return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get())
+ }
+ alias.Length = int(length)
+ }
+
+ return &alias, nil
+}
+
+// parseMessage parses VPP binary API message object from JSON node
+func parseMessage(msgNode *jsongo.Node) (*Message, error) {
+ if msgNode.Len() == 0 || msgNode.At(0).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for message specified")
+ }
+
+ msgName, ok := msgNode.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("message name is %T, not a string", msgNode.At(0).Get())
+ }
+ msgCRC, ok := msgNode.At(msgNode.Len() - 1).At(messageFieldCrc).Get().(string)
+ if !ok {
+
+ return nil, fmt.Errorf("message crc invalid or missing")
+ }
+
+ msg := Message{
+ Name: msgName,
+ CRC: msgCRC,
+ }
+
+ // loop through message fields, skip first (name) and last (crc)
+ for j := 1; j < msgNode.Len()-1; j++ {
+ if msgNode.At(j).GetType() == jsongo.TypeArray {
+ fieldNode := msgNode.At(j)
+
+ field, err := parseField(fieldNode)
+ if err != nil {
+ return nil, err
+ }
+
+ msg.Fields = append(msg.Fields, *field)
+ }
+ }
+
+ return &msg, nil
+}
+
+// parseField parses VPP binary API object field from JSON node
+func parseField(field *jsongo.Node) (*Field, error) {
+ if field.Len() < 2 || field.At(0).GetType() != jsongo.TypeValue || field.At(1).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for field specified")
+ }
+
+ fieldType, ok := field.At(0).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("field type is %T, not a string", field.At(0).Get())
+ }
+ fieldName, ok := field.At(1).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("field name is %T, not a string", field.At(1).Get())
+ }
+
+ f := &Field{
+ Name: fieldName,
+ Type: fieldType,
+ }
+
+ if field.Len() >= 3 {
+ switch field.At(2).GetType() {
+ case jsongo.TypeValue:
+ fieldLength, ok := field.At(2).Get().(float64)
+ if !ok {
+ return nil, fmt.Errorf("field length is %T, not float64", field.At(2).Get())
+ }
+ f.Length = int(fieldLength)
+ f.Array = true
+
+ case jsongo.TypeMap:
+ fieldMeta := field.At(2)
+
+ for _, key := range fieldMeta.GetKeys() {
+ metaNode := fieldMeta.At(key)
+ metaName := key.(string)
+ metaValue := metaNode.Get()
+
+ switch metaName {
+ case fieldOptionLimit:
+ metaValue = int(metaNode.Get().(float64))
+ case fieldOptionDefault:
+ metaValue = metaNode.Get()
+ default:
+ logrus.Warnf("unknown meta info (%s=%v) for field (%s)", metaName, metaValue, fieldName)
+ }
+
+ if f.Meta == nil {
+ f.Meta = map[string]interface{}{}
+ }
+ f.Meta[metaName] = metaValue
+ }
+ default:
+ return nil, errors.New("invalid JSON for field specified")
+ }
+ }
+ if field.Len() >= 4 {
+ fieldLengthFrom, ok := field.At(3).Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("field length from is %T, not a string", field.At(3).Get())
+ }
+ f.SizeFrom = fieldLengthFrom
+ }
+
+ return f, nil
+}
+
+// parseServiceRPC parses VPP binary API service object from JSON node
+func parseServiceRPC(rpcName string, rpcNode *jsongo.Node) (*RPC, error) {
+ if rpcNode.Len() == 0 || rpcNode.At(serviceFieldReply).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for service RPC specified")
+ }
+
+ rpc := RPC{
+ Name: rpcName,
+ RequestMsg: rpcName,
+ }
+
+ if replyNode := rpcNode.At(serviceFieldReply); replyNode.GetType() == jsongo.TypeValue {
+ reply, ok := replyNode.Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("service RPC reply is %T, not a string", replyNode.Get())
+ }
+ if reply != serviceReplyNull {
+ rpc.ReplyMsg = reply
+ }
+ }
+
+ // is stream (dump)
+ if streamNode := rpcNode.At(serviceFieldStream); streamNode.GetType() == jsongo.TypeValue {
+ var ok bool
+ rpc.Stream, ok = streamNode.Get().(bool)
+ if !ok {
+ return nil, fmt.Errorf("service RPC stream is %T, not a boolean", streamNode.Get())
+ }
+ }
+
+ // stream message
+ if streamMsgNode := rpcNode.At(serviceFieldStreamMsg); streamMsgNode.GetType() == jsongo.TypeValue {
+ var ok bool
+ rpc.StreamMsg, ok = streamMsgNode.Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("service RPC stream msg is %T, not a string", streamMsgNode.Get())
+ }
+ }
+
+ // events service (event subscription)
+ if eventsNode := rpcNode.At(serviceFieldEvents); eventsNode.GetType() == jsongo.TypeArray {
+ for j := 0; j < eventsNode.Len(); j++ {
+ event := eventsNode.At(j).Get().(string)
+ rpc.Events = append(rpc.Events, event)
+ }
+ }
+
+ return &rpc, nil
+}
diff --git a/binapigen/vppapi/parser.go b/binapigen/vppapi/parser.go
new file mode 100644
index 0000000..312dd0e
--- /dev/null
+++ b/binapigen/vppapi/parser.go
@@ -0,0 +1,111 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package vppapi
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ DefaultAPIDir = "/usr/share/vpp/api"
+)
+
+const apifileSuffixJson = ".api.json"
+
+// FindFiles returns all input files located in specified directory
+func FindFiles(dir string, deep int) (paths []string, err error) {
+ entries, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, fmt.Errorf("reading directory %s failed: %v", dir, err)
+ }
+ for _, e := range entries {
+ if e.IsDir() && deep > 0 {
+ nestedDir := filepath.Join(dir, e.Name())
+ if nested, err := FindFiles(nestedDir, deep-1); err != nil {
+ return nil, err
+ } else {
+ paths = append(paths, nested...)
+ }
+ } else if strings.HasSuffix(e.Name(), apifileSuffixJson) {
+ paths = append(paths, filepath.Join(dir, e.Name()))
+ }
+ }
+ return paths, nil
+}
+
+func Parse() ([]*File, error) {
+ return ParseDir(DefaultAPIDir)
+}
+
+func ParseDir(apidir string) ([]*File, error) {
+ files, err := FindFiles(apidir, 1)
+ if err != nil {
+ return nil, err
+ }
+
+ logrus.Infof("found %d files in API dir %q", len(files), apidir)
+
+ var modules []*File
+
+ for _, file := range files {
+ module, err := ParseFile(file)
+ if err != nil {
+ return nil, err
+ }
+ modules = append(modules, module)
+ }
+
+ return modules, nil
+}
+
+// ParseFile parses API file contents and returns File.
+func ParseFile(apifile string) (*File, error) {
+ if !strings.HasSuffix(apifile, apifileSuffixJson) {
+ return nil, fmt.Errorf("unsupported file format: %q", apifile)
+ }
+
+ data, err := ioutil.ReadFile(apifile)
+ if err != nil {
+ return nil, fmt.Errorf("reading file %s failed: %v", apifile, err)
+ }
+
+ base := filepath.Base(apifile)
+ name := base[:strings.Index(base, ".")]
+
+ logf("parsing file %q", base)
+
+ module, err := ParseRaw(data)
+ if err != nil {
+ return nil, fmt.Errorf("parsing file %s failed: %v", base, err)
+ }
+ module.Name = name
+ module.Path = apifile
+
+ return module, nil
+}
+
+func ParseRaw(data []byte) (file *File, err error) {
+ file, err = parseJSON(data)
+ if err != nil {
+ return nil, err
+ }
+
+ return file, nil
+}
diff --git a/binapigen/vppapi/parser_test.go b/binapigen/vppapi/parser_test.go
new file mode 100644
index 0000000..2dc82e4
--- /dev/null
+++ b/binapigen/vppapi/parser_test.go
@@ -0,0 +1,114 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package vppapi
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "testing"
+
+ . "github.com/onsi/gomega"
+)
+
+func TestGetInputFiles(t *testing.T) {
+ RegisterTestingT(t)
+
+ result, err := FindFiles("testdata", 1)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(result).To(HaveLen(5))
+ for _, file := range result {
+ Expect(file).To(BeAnExistingFile())
+ }
+}
+
+func TestGetInputFilesError(t *testing.T) {
+ RegisterTestingT(t)
+
+ result, err := FindFiles("nonexisting_directory", 1)
+ Expect(err).Should(HaveOccurred())
+ Expect(result).To(BeNil())
+}
+
+func TestReadJson(t *testing.T) {
+ RegisterTestingT(t)
+
+ inputData, err := ioutil.ReadFile("testdata/af_packet.api.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ result, err := parseJSON(inputData)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(result).ToNot(BeNil())
+ Expect(result.EnumTypes).To(HaveLen(0))
+ Expect(result.StructTypes).To(HaveLen(0))
+ Expect(result.Messages).To(HaveLen(6))
+ Expect(result.Service.RPCs).To(HaveLen(3))
+}
+
+func TestReadJsonError(t *testing.T) {
+ RegisterTestingT(t)
+
+ inputData, err := ioutil.ReadFile("testdata/input-read-json-error.json")
+ Expect(err).ShouldNot(HaveOccurred())
+ result, err := parseJSON(inputData)
+ Expect(err).Should(HaveOccurred())
+ Expect(result).To(BeNil())
+}
+
+func TestParseFile(t *testing.T) {
+ module, err := ParseFile("testdata/vpe.api.json")
+ if err != nil {
+ t.Fatal("unexpected error:", err)
+ }
+
+ b, err := json.MarshalIndent(module, "", " ")
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("parsed module: %s", b)
+
+ if module.Name != "vpe" {
+ t.Errorf("expected Name=%s, got %v", "vpe", module.Name)
+ }
+ if module.CRC != "0xbd2c94f4" {
+ t.Errorf("expected CRC=%s, got %v", "0xbd2c94f4", module.CRC)
+ }
+ if module.Version() != "1.6.1" {
+ t.Errorf("expected Version=%s, got %v", "1.6.1", module.Version())
+ }
+ if len(module.Imports) == 0 {
+ t.Errorf("expected imports, got none")
+ }
+ if len(module.Options) == 0 {
+ t.Errorf("expected options, got none")
+ }
+ if len(module.AliasTypes) == 0 {
+ t.Errorf("expected aliases, got none")
+ }
+ if len(module.StructTypes) == 0 {
+ t.Errorf("expected types, got none")
+ }
+ if len(module.Service.RPCs) == 0 {
+ t.Errorf("expected service method, got none")
+ }
+ if len(module.Messages) == 0 {
+ t.Errorf("expected messages, got none")
+ }
+}
+
+func TestParseFileUnsupported(t *testing.T) {
+ _, err := ParseFile("testdata/input.txt")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+}
diff --git a/binapigen/vppapi/testdata/acl.api.json b/binapigen/vppapi/testdata/acl.api.json
new file mode 100644
index 0000000..4c6653c
--- /dev/null
+++ b/binapigen/vppapi/testdata/acl.api.json
@@ -0,0 +1,929 @@
+{
+ "services": [
+ {
+ "acl_interface_add_del": {
+ "reply": "acl_interface_add_del_reply"
+ }
+ },
+ {
+ "acl_del": {
+ "reply": "acl_del_reply"
+ }
+ },
+ {
+ "macip_acl_del": {
+ "reply": "macip_acl_del_reply"
+ }
+ },
+ {
+ "acl_plugin_get_version": {
+ "reply": "acl_plugin_get_version_reply"
+ }
+ },
+ {
+ "macip_acl_interface_add_del": {
+ "reply": "macip_acl_interface_add_del_reply"
+ }
+ },
+ {
+ "acl_interface_set_acl_list": {
+ "reply": "acl_interface_set_acl_list_reply"
+ }
+ },
+ {
+ "acl_dump": {
+ "reply": "acl_details",
+ "stream": true
+ }
+ },
+ {
+ "acl_interface_list_dump": {
+ "reply": "acl_interface_list_details",
+ "stream": true
+ }
+ },
+ {
+ "macip_acl_interface_list_dump": {
+ "reply": "macip_acl_interface_list_details",
+ "stream": true
+ }
+ },
+ {
+ "acl_add_replace": {
+ "reply": "acl_add_replace_reply"
+ }
+ },
+ {
+ "acl_plugin_control_ping": {
+ "reply": "acl_plugin_control_ping_reply"
+ }
+ },
+ {
+ "macip_acl_interface_get": {
+ "reply": "macip_acl_interface_get_reply"
+ }
+ },
+ {
+ "macip_acl_add": {
+ "reply": "macip_acl_add_reply"
+ }
+ },
+ {
+ "macip_acl_add_replace": {
+ "reply": "macip_acl_add_replace_reply"
+ }
+ },
+ {
+ "macip_acl_dump": {
+ "reply": "macip_acl_details",
+ "stream": true
+ }
+ }
+ ],
+ "vl_api_version": "0x1db2ece9",
+ "enums": [],
+ "messages": [
+ [
+ "acl_plugin_get_version",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "acl_plugin_get_version_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "major"
+ ],
+ [
+ "u32",
+ "minor"
+ ],
+ {
+ "crc": "0x9b32cf86"
+ }
+ ],
+ [
+ "acl_plugin_control_ping",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "acl_plugin_control_ping_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "vpe_pid"
+ ],
+ {
+ "crc": "0xf6b0b8ca"
+ }
+ ],
+ [
+ "acl_add_replace",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "u8",
+ "tag",
+ 64
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_acl_rule_t",
+ "r",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xe839997e"
+ }
+ ],
+ [
+ "acl_add_replace_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xac407b0c"
+ }
+ ],
+ [
+ "acl_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0xef34fea4"
+ }
+ ],
+ [
+ "acl_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "acl_interface_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_input"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0x0b2aedd1"
+ }
+ ],
+ [
+ "acl_interface_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "acl_interface_set_acl_list",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "count"
+ ],
+ [
+ "u8",
+ "n_input"
+ ],
+ [
+ "u32",
+ "acls",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x8baece38"
+ }
+ ],
+ [
+ "acl_interface_set_acl_list_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "acl_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0xef34fea4"
+ }
+ ],
+ [
+ "acl_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "u8",
+ "tag",
+ 64
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_acl_rule_t",
+ "r",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x5bd895be"
+ }
+ ],
+ [
+ "acl_interface_list_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ {
+ "crc": "0x529cb13f"
+ }
+ ],
+ [
+ "acl_interface_list_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "count"
+ ],
+ [
+ "u8",
+ "n_input"
+ ],
+ [
+ "u32",
+ "acls",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xd5e80809"
+ }
+ ],
+ [
+ "macip_acl_add",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "tag",
+ 64
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_macip_acl_rule_t",
+ "r",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xb3d3d65a"
+ }
+ ],
+ [
+ "macip_acl_add_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xac407b0c"
+ }
+ ],
+ [
+ "macip_acl_add_replace",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "u8",
+ "tag",
+ 64
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_macip_acl_rule_t",
+ "r",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xa0e8c01b"
+ }
+ ],
+ [
+ "macip_acl_add_replace_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xac407b0c"
+ }
+ ],
+ [
+ "macip_acl_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0xef34fea4"
+ }
+ ],
+ [
+ "macip_acl_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "macip_acl_interface_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0x6a6be97c"
+ }
+ ],
+ [
+ "macip_acl_interface_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "macip_acl_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ {
+ "crc": "0xef34fea4"
+ }
+ ],
+ [
+ "macip_acl_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "acl_index"
+ ],
+ [
+ "u8",
+ "tag",
+ 64
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_macip_acl_rule_t",
+ "r",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xdd2b55ba"
+ }
+ ],
+ [
+ "macip_acl_interface_get",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "macip_acl_interface_get_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "u32",
+ "acls",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xaccf9b05"
+ }
+ ],
+ [
+ "macip_acl_interface_list_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ {
+ "crc": "0x529cb13f"
+ }
+ ],
+ [
+ "macip_acl_interface_list_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "count"
+ ],
+ [
+ "u32",
+ "acls",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x29783fa0"
+ }
+ ]
+ ],
+ "types": [
+ [
+ "acl_rule",
+ [
+ "u8",
+ "is_permit"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "src_ip_addr",
+ 16
+ ],
+ [
+ "u8",
+ "src_ip_prefix_len"
+ ],
+ [
+ "u8",
+ "dst_ip_addr",
+ 16
+ ],
+ [
+ "u8",
+ "dst_ip_prefix_len"
+ ],
+ [
+ "u8",
+ "proto"
+ ],
+ [
+ "u16",
+ "srcport_or_icmptype_first"
+ ],
+ [
+ "u16",
+ "srcport_or_icmptype_last"
+ ],
+ [
+ "u16",
+ "dstport_or_icmpcode_first"
+ ],
+ [
+ "u16",
+ "dstport_or_icmpcode_last"
+ ],
+ [
+ "u8",
+ "tcp_flags_mask"
+ ],
+ [
+ "u8",
+ "tcp_flags_value"
+ ],
+ {
+ "crc": "0x6f99bf4d"
+ }
+ ],
+ [
+ "macip_acl_rule",
+ [
+ "u8",
+ "is_permit"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "src_mac",
+ 6
+ ],
+ [
+ "u8",
+ "src_mac_mask",
+ 6
+ ],
+ [
+ "u8",
+ "src_ip_addr",
+ 16
+ ],
+ [
+ "u8",
+ "src_ip_prefix_len"
+ ],
+ {
+ "crc": "0x70589f1e"
+ }
+ ]
+ ]
+}
diff --git a/binapigen/vppapi/testdata/af_packet.api.json b/binapigen/vppapi/testdata/af_packet.api.json
new file mode 100644
index 0000000..dc0de16
--- /dev/null
+++ b/binapigen/vppapi/testdata/af_packet.api.json
@@ -0,0 +1,157 @@
+{
+ "services": {
+ "af_packet_delete": {
+ "reply": "af_packet_delete_reply"
+ },
+ "af_packet_create": {
+ "reply": "af_packet_create_reply"
+ },
+ "af_packet_set_l4_cksum_offload": {
+ "reply": "af_packet_set_l4_cksum_offload_reply"
+ }
+ },
+ "vl_api_version": "0x8957ca8b",
+ "enums": [],
+ "messages": [
+ [
+ "af_packet_create",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "host_if_name",
+ 64
+ ],
+ [
+ "u8",
+ "hw_addr",
+ 6
+ ],
+ [
+ "u8",
+ "use_random_hw_addr"
+ ],
+ {
+ "crc": "0x6d5d30d6"
+ }
+ ],
+ [
+ "af_packet_create_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ {
+ "crc": "0xfda5941f"
+ }
+ ],
+ [
+ "af_packet_delete",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "host_if_name",
+ 64
+ ],
+ {
+ "crc": "0x3efceda3"
+ }
+ ],
+ [
+ "af_packet_delete_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "af_packet_set_l4_cksum_offload",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "set"
+ ],
+ {
+ "crc": "0x86538585"
+ }
+ ],
+ [
+ "af_packet_set_l4_cksum_offload_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ]
+ ],
+ "types": []
+}
diff --git a/binapigen/vppapi/testdata/input-generate-error.json b/binapigen/vppapi/testdata/input-generate-error.json
new file mode 100644
index 0000000..d5df76e
--- /dev/null
+++ b/binapigen/vppapi/testdata/input-generate-error.json
@@ -0,0 +1,3 @@
+{
+ "key": "value"
+} \ No newline at end of file
diff --git a/binapigen/vppapi/testdata/input-read-json-error.json b/binapigen/vppapi/testdata/input-read-json-error.json
new file mode 100644
index 0000000..02691e3
--- /dev/null
+++ b/binapigen/vppapi/testdata/input-read-json-error.json
@@ -0,0 +1 @@
+% \ No newline at end of file
diff --git a/binapigen/vppapi/testdata/input.txt b/binapigen/vppapi/testdata/input.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/binapigen/vppapi/testdata/input.txt
diff --git a/binapigen/vppapi/testdata/ip.api.json b/binapigen/vppapi/testdata/ip.api.json
new file mode 100644
index 0000000..530b6d6
--- /dev/null
+++ b/binapigen/vppapi/testdata/ip.api.json
@@ -0,0 +1,2246 @@
+{
+ "services": [
+ {
+ "ip_source_and_port_range_check_add_del": {
+ "reply": "ip_source_and_port_range_check_add_del_reply"
+ }
+ },
+ {
+ "ip6_fib_dump": {
+ "reply": "ip6_fib_details",
+ "stream": true
+ }
+ },
+ {
+ "want_ip6_nd_events": {
+ "reply": "want_ip6_nd_events_reply"
+ }
+ },
+ {
+ "ip_punt_police": {
+ "reply": "ip_punt_police_reply"
+ }
+ },
+ {
+ "set_arp_neighbor_limit": {
+ "reply": "set_arp_neighbor_limit_reply"
+ }
+ },
+ {
+ "ip6nd_proxy_add_del": {
+ "reply": "ip6nd_proxy_add_del_reply"
+ }
+ },
+ {
+ "ioam_disable": {
+ "reply": "ioam_disable_reply"
+ }
+ },
+ {
+ "ip_table_add_del": {
+ "reply": "ip_table_add_del_reply"
+ }
+ },
+ {
+ "ip_neighbor_dump": {
+ "reply": "ip_neighbor_details",
+ "stream": true
+ }
+ },
+ {
+ "ip4_arp_event": {
+ "reply": null
+ }
+ },
+ {
+ "ip_punt_redirect": {
+ "reply": "ip_punt_redirect_reply"
+ }
+ },
+ {
+ "sw_interface_ip6nd_ra_prefix": {
+ "reply": "sw_interface_ip6nd_ra_prefix_reply"
+ }
+ },
+ {
+ "reset_fib": {
+ "reply": "reset_fib_reply"
+ }
+ },
+ {
+ "ip6_mfib_dump": {
+ "reply": "ip6_mfib_details",
+ "stream": true
+ }
+ },
+ {
+ "sw_interface_ip6nd_ra_config": {
+ "reply": "sw_interface_ip6nd_ra_config_reply"
+ }
+ },
+ {
+ "sw_interface_ip6_enable_disable": {
+ "reply": "sw_interface_ip6_enable_disable_reply"
+ }
+ },
+ {
+ "sw_interface_ip6_set_link_local_address": {
+ "reply": "sw_interface_ip6_set_link_local_address_reply"
+ }
+ },
+ {
+ "mfib_signal_dump": {
+ "reply": "mfib_signal_details",
+ "stream": true
+ }
+ },
+ {
+ "ip_container_proxy_add_del": {
+ "reply": "ip_container_proxy_add_del_reply"
+ }
+ },
+ {
+ "ip_mfib_dump": {
+ "reply": "ip_mfib_details",
+ "stream": true
+ }
+ },
+ {
+ "ip_address_dump": {
+ "reply": "ip_address_details",
+ "stream": true
+ }
+ },
+ {
+ "ip_dump": {
+ "reply": "ip_details",
+ "stream": true
+ }
+ },
+ {
+ "ip_neighbor_add_del": {
+ "reply": "ip_neighbor_add_del_reply"
+ }
+ },
+ {
+ "proxy_arp_intfc_enable_disable": {
+ "reply": "proxy_arp_intfc_enable_disable_reply"
+ }
+ },
+ {
+ "proxy_arp_add_del": {
+ "reply": "proxy_arp_add_del_reply"
+ }
+ },
+ {
+ "ip_add_del_route": {
+ "reply": "ip_add_del_route_reply"
+ }
+ },
+ {
+ "ip6nd_proxy_dump": {
+ "reply": "ip6nd_proxy_details",
+ "stream": true
+ }
+ },
+ {
+ "ip_fib_dump": {
+ "reply": "ip_fib_details",
+ "stream": true
+ }
+ },
+ {
+ "want_ip4_arp_events": {
+ "reply": "want_ip4_arp_events_reply"
+ }
+ },
+ {
+ "ioam_enable": {
+ "reply": "ioam_enable_reply"
+ }
+ },
+ {
+ "ip6_nd_event": {
+ "reply": null
+ }
+ },
+ {
+ "ip_mroute_add_del": {
+ "reply": "ip_mroute_add_del_reply"
+ }
+ },
+ {
+ "ip_source_and_port_range_check_interface_add_del": {
+ "reply": "ip_source_and_port_range_check_interface_add_del_reply"
+ }
+ },
+ {
+ "set_ip_flow_hash": {
+ "reply": "set_ip_flow_hash_reply"
+ }
+ }
+ ],
+ "vl_api_version": "0xb395c625",
+ "enums": [],
+ "messages": [
+ [
+ "ip_table_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "name",
+ 64
+ ],
+ {
+ "crc": "0x0240c89d"
+ }
+ ],
+ [
+ "ip_table_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_fib_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip_fib_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "table_name",
+ 64
+ ],
+ [
+ "u8",
+ "address_length"
+ ],
+ [
+ "u8",
+ "address",
+ 4
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "path",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x99dfd73b"
+ }
+ ],
+ [
+ "ip6_fib_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip6_fib_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "table_name",
+ 64
+ ],
+ [
+ "u8",
+ "address_length"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "path",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xabd0060e"
+ }
+ ],
+ [
+ "ip_neighbor_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x6b7bcd0a"
+ }
+ ],
+ [
+ "ip_neighbor_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_static"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "mac_address",
+ 6
+ ],
+ [
+ "u8",
+ "ip_address",
+ 16
+ ],
+ {
+ "crc": "0x85e32a72"
+ }
+ ],
+ [
+ "ip_neighbor_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "is_static"
+ ],
+ [
+ "u8",
+ "is_no_adj_fib"
+ ],
+ [
+ "u8",
+ "mac_address",
+ 6
+ ],
+ [
+ "u8",
+ "dst_address",
+ 16
+ ],
+ {
+ "crc": "0x4711eb25"
+ }
+ ],
+ [
+ "ip_neighbor_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "set_ip_flow_hash",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "src"
+ ],
+ [
+ "u8",
+ "dst"
+ ],
+ [
+ "u8",
+ "sport"
+ ],
+ [
+ "u8",
+ "dport"
+ ],
+ [
+ "u8",
+ "proto"
+ ],
+ [
+ "u8",
+ "reverse"
+ ],
+ {
+ "crc": "0x32ebf737"
+ }
+ ],
+ [
+ "set_ip_flow_hash_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "sw_interface_ip6nd_ra_config",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "suppress"
+ ],
+ [
+ "u8",
+ "managed"
+ ],
+ [
+ "u8",
+ "other"
+ ],
+ [
+ "u8",
+ "ll_option"
+ ],
+ [
+ "u8",
+ "send_unicast"
+ ],
+ [
+ "u8",
+ "cease"
+ ],
+ [
+ "u8",
+ "is_no"
+ ],
+ [
+ "u8",
+ "default_router"
+ ],
+ [
+ "u32",
+ "max_interval"
+ ],
+ [
+ "u32",
+ "min_interval"
+ ],
+ [
+ "u32",
+ "lifetime"
+ ],
+ [
+ "u32",
+ "initial_count"
+ ],
+ [
+ "u32",
+ "initial_interval"
+ ],
+ {
+ "crc": "0xc3f02daa"
+ }
+ ],
+ [
+ "sw_interface_ip6nd_ra_config_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "sw_interface_ip6nd_ra_prefix",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ [
+ "u8",
+ "address_length"
+ ],
+ [
+ "u8",
+ "use_default"
+ ],
+ [
+ "u8",
+ "no_advertise"
+ ],
+ [
+ "u8",
+ "off_link"
+ ],
+ [
+ "u8",
+ "no_autoconfig"
+ ],
+ [
+ "u8",
+ "no_onlink"
+ ],
+ [
+ "u8",
+ "is_no"
+ ],
+ [
+ "u32",
+ "val_lifetime"
+ ],
+ [
+ "u32",
+ "pref_lifetime"
+ ],
+ {
+ "crc": "0xca763c9a"
+ }
+ ],
+ [
+ "sw_interface_ip6nd_ra_prefix_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip6nd_proxy_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_del"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ {
+ "crc": "0xd95f0fa0"
+ }
+ ],
+ [
+ "ip6nd_proxy_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip6nd_proxy_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ {
+ "crc": "0xd73bf1ab"
+ }
+ ],
+ [
+ "ip6nd_proxy_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "sw_interface_ip6_enable_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "enable"
+ ],
+ {
+ "crc": "0xa36fadc0"
+ }
+ ],
+ [
+ "sw_interface_ip6_enable_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "sw_interface_ip6_set_link_local_address",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ {
+ "crc": "0xd73bf1ab"
+ }
+ ],
+ [
+ "sw_interface_ip6_set_link_local_address_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_add_del_route",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "next_hop_sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "classify_table_index"
+ ],
+ [
+ "u32",
+ "next_hop_table_id"
+ ],
+ [
+ "u32",
+ "next_hop_id"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_drop"
+ ],
+ [
+ "u8",
+ "is_unreach"
+ ],
+ [
+ "u8",
+ "is_prohibit"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "is_local"
+ ],
+ [
+ "u8",
+ "is_classify"
+ ],
+ [
+ "u8",
+ "is_multipath"
+ ],
+ [
+ "u8",
+ "is_resolve_host"
+ ],
+ [
+ "u8",
+ "is_resolve_attached"
+ ],
+ [
+ "u8",
+ "is_dvr"
+ ],
+ [
+ "u8",
+ "is_source_lookup"
+ ],
+ [
+ "u8",
+ "is_udp_encap"
+ ],
+ [
+ "u8",
+ "next_hop_weight"
+ ],
+ [
+ "u8",
+ "next_hop_preference"
+ ],
+ [
+ "u8",
+ "next_hop_proto"
+ ],
+ [
+ "u8",
+ "dst_address_length"
+ ],
+ [
+ "u8",
+ "dst_address",
+ 16
+ ],
+ [
+ "u8",
+ "next_hop_address",
+ 16
+ ],
+ [
+ "u8",
+ "next_hop_n_out_labels"
+ ],
+ [
+ "u32",
+ "next_hop_via_label"
+ ],
+ [
+ "u32",
+ "next_hop_out_label_stack",
+ 0,
+ "next_hop_n_out_labels"
+ ],
+ {
+ "crc": "0xc85f8290"
+ }
+ ],
+ [
+ "ip_add_del_route_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_mroute_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "next_hop_sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "entry_flags"
+ ],
+ [
+ "u32",
+ "itf_flags"
+ ],
+ [
+ "u32",
+ "rpf_id"
+ ],
+ [
+ "u32",
+ "bier_imp"
+ ],
+ [
+ "u16",
+ "grp_address_length"
+ ],
+ [
+ "u8",
+ "next_hop_afi"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "is_local"
+ ],
+ [
+ "u8",
+ "grp_address",
+ 16
+ ],
+ [
+ "u8",
+ "src_address",
+ 16
+ ],
+ {
+ "crc": "0xc37112f7"
+ }
+ ],
+ [
+ "ip_mroute_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_mfib_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip_mfib_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "entry_flags"
+ ],
+ [
+ "u32",
+ "rpf_id"
+ ],
+ [
+ "u8",
+ "address_length"
+ ],
+ [
+ "u8",
+ "grp_address",
+ 4
+ ],
+ [
+ "u8",
+ "src_address",
+ 4
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "path",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x5e530d5e"
+ }
+ ],
+ [
+ "ip6_mfib_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip6_mfib_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "address_length"
+ ],
+ [
+ "u8",
+ "grp_address",
+ 16
+ ],
+ [
+ "u8",
+ "src_address",
+ 16
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "path",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xe02dcb4b"
+ }
+ ],
+ [
+ "ip_address_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "ip",
+ 16
+ ],
+ [
+ "u8",
+ "prefix_length"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0xbc7442f2"
+ }
+ ],
+ [
+ "ip_address_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x6b7bcd0a"
+ }
+ ],
+ [
+ "ip_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x452ffc5a"
+ }
+ ],
+ [
+ "ip_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0xde883da4"
+ }
+ ],
+ [
+ "mfib_signal_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "mfib_signal_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u16",
+ "grp_address_len"
+ ],
+ [
+ "u8",
+ "grp_address",
+ 16
+ ],
+ [
+ "u8",
+ "src_address",
+ 16
+ ],
+ [
+ "u16",
+ "ip_packet_len"
+ ],
+ [
+ "u8",
+ "ip_packet_data",
+ 256
+ ],
+ {
+ "crc": "0x791bbeab"
+ }
+ ],
+ [
+ "ip_punt_police",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "policer_index"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_ip6"
+ ],
+ {
+ "crc": "0x38691592"
+ }
+ ],
+ [
+ "ip_punt_police_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_punt_redirect",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "rx_sw_if_index"
+ ],
+ [
+ "u32",
+ "tx_sw_if_index"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "is_ip6"
+ ],
+ [
+ "u8",
+ "nh",
+ 16
+ ],
+ {
+ "crc": "0x996b6603"
+ }
+ ],
+ [
+ "ip_punt_redirect_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_container_proxy_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "ip",
+ 16
+ ],
+ [
+ "u8",
+ "is_ip4"
+ ],
+ [
+ "u8",
+ "plen"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ {
+ "crc": "0x0a355d39"
+ }
+ ],
+ [
+ "ip_container_proxy_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "mask_length"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ [
+ "u8",
+ "number_of_ranges"
+ ],
+ [
+ "u16",
+ "low_ports",
+ 32
+ ],
+ [
+ "u16",
+ "high_ports",
+ 32
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ {
+ "crc": "0x03d6b03a"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_interface_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "tcp_in_vrf_id"
+ ],
+ [
+ "u32",
+ "tcp_out_vrf_id"
+ ],
+ [
+ "u32",
+ "udp_in_vrf_id"
+ ],
+ [
+ "u32",
+ "udp_out_vrf_id"
+ ],
+ {
+ "crc": "0x6966bc44"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_interface_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "want_ip4_arp_events",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "enable_disable"
+ ],
+ [
+ "u32",
+ "pid"
+ ],
+ [
+ "u32",
+ "address"
+ ],
+ {
+ "crc": "0x77e06379"
+ }
+ ],
+ [
+ "want_ip4_arp_events_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip4_arp_event",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "address"
+ ],
+ [
+ "u32",
+ "pid"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "new_mac",
+ 6
+ ],
+ [
+ "u8",
+ "mac_ip"
+ ],
+ {
+ "crc": "0xef7235f7"
+ }
+ ],
+ [
+ "want_ip6_nd_events",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "enable_disable"
+ ],
+ [
+ "u32",
+ "pid"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ {
+ "crc": "0x1cf65fbb"
+ }
+ ],
+ [
+ "want_ip6_nd_events_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip6_nd_event",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "pid"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "address",
+ 16
+ ],
+ [
+ "u8",
+ "new_mac",
+ 6
+ ],
+ [
+ "u8",
+ "mac_ip"
+ ],
+ {
+ "crc": "0x96ab2fdd"
+ }
+ ],
+ [
+ "proxy_arp_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ [
+ "u8",
+ "is_add"
+ ],
+ [
+ "u8",
+ "low_address",
+ 4
+ ],
+ [
+ "u8",
+ "hi_address",
+ 4
+ ],
+ {
+ "crc": "0xc2442918"
+ }
+ ],
+ [
+ "proxy_arp_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "proxy_arp_intfc_enable_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u8",
+ "enable_disable"
+ ],
+ {
+ "crc": "0x69d24598"
+ }
+ ],
+ [
+ "proxy_arp_intfc_enable_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "reset_fib",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x8553ebd9"
+ }
+ ],
+ [
+ "reset_fib_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "set_arp_neighbor_limit",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u8",
+ "is_ipv6"
+ ],
+ [
+ "u32",
+ "arp_neighbor_limit"
+ ],
+ {
+ "crc": "0x97d01fd6"
+ }
+ ],
+ [
+ "set_arp_neighbor_limit_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ioam_enable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u16",
+ "id"
+ ],
+ [
+ "u8",
+ "seqno"
+ ],
+ [
+ "u8",
+ "analyse"
+ ],
+ [
+ "u8",
+ "pot_enable"
+ ],
+ [
+ "u8",
+ "trace_enable"
+ ],
+ [
+ "u32",
+ "node_id"
+ ],
+ {
+ "crc": "0x9392e032"
+ }
+ ],
+ [
+ "ioam_enable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ioam_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u16",
+ "id"
+ ],
+ {
+ "crc": "0x6b16a45e"
+ }
+ ],
+ [
+ "ioam_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ]
+ ],
+ "types": [
+ [
+ "fib_path",
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "weight"
+ ],
+ [
+ "u8",
+ "preference"
+ ],
+ [
+ "u8",
+ "is_local"
+ ],
+ [
+ "u8",
+ "is_drop"
+ ],
+ [
+ "u8",
+ "is_unreach"
+ ],
+ [
+ "u8",
+ "is_prohibit"
+ ],
+ [
+ "u8",
+ "afi"
+ ],
+ [
+ "u8",
+ "next_hop",
+ 16
+ ],
+ {
+ "crc": "0xcd899e0a"
+ }
+ ]
+ ]
+}
diff --git a/binapigen/vppapi/testdata/test-all.api.json b/binapigen/vppapi/testdata/test-all.api.json
new file mode 100644
index 0000000..4e858b8
--- /dev/null
+++ b/binapigen/vppapi/testdata/test-all.api.json
@@ -0,0 +1,3240 @@
+[
+ {
+ "types": [
+ [
+ "address",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "vl_api_address_union_t",
+ "un"
+ ]
+ ],
+ [
+ "prefix",
+ [
+ "vl_api_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "mprefix",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "u16",
+ "grp_address_length"
+ ],
+ [
+ "vl_api_address_union_t",
+ "grp_address"
+ ],
+ [
+ "vl_api_address_union_t",
+ "src_address"
+ ]
+ ],
+ [
+ "ip6_prefix",
+ [
+ "vl_api_ip6_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "ip4_prefix",
+ [
+ "vl_api_ip4_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "prefix_matcher",
+ [
+ "u8",
+ "le"
+ ],
+ [
+ "u8",
+ "ge"
+ ]
+ ],
+ [
+ "fib_mpls_label",
+ [
+ "u8",
+ "is_uniform"
+ ],
+ [
+ "u32",
+ "label"
+ ],
+ [
+ "u8",
+ "ttl"
+ ],
+ [
+ "u8",
+ "exp"
+ ]
+ ],
+ [
+ "fib_path_nh",
+ [
+ "vl_api_address_union_t",
+ "address"
+ ],
+ [
+ "u32",
+ "via_label"
+ ],
+ [
+ "u32",
+ "obj_id"
+ ],
+ [
+ "u32",
+ "classify_table_index"
+ ]
+ ],
+ [
+ "fib_path",
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "rpf_id"
+ ],
+ [
+ "u8",
+ "weight"
+ ],
+ [
+ "u8",
+ "preference"
+ ],
+ [
+ "vl_api_fib_path_type_t",
+ "type"
+ ],
+ [
+ "vl_api_fib_path_flags_t",
+ "flags"
+ ],
+ [
+ "vl_api_fib_path_nh_proto_t",
+ "proto"
+ ],
+ [
+ "vl_api_fib_path_nh_t",
+ "nh"
+ ],
+ [
+ "u8",
+ "n_labels"
+ ],
+ [
+ "vl_api_fib_mpls_label_t",
+ "label_stack",
+ 16
+ ]
+ ],
+ [
+ "address",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "vl_api_address_union_t",
+ "un"
+ ]
+ ],
+ [
+ "prefix",
+ [
+ "vl_api_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "mprefix",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "u16",
+ "grp_address_length"
+ ],
+ [
+ "vl_api_address_union_t",
+ "grp_address"
+ ],
+ [
+ "vl_api_address_union_t",
+ "src_address"
+ ]
+ ],
+ [
+ "ip6_prefix",
+ [
+ "vl_api_ip6_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "ip4_prefix",
+ [
+ "vl_api_ip4_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "prefix_matcher",
+ [
+ "u8",
+ "le"
+ ],
+ [
+ "u8",
+ "ge"
+ ]
+ ],
+ [
+ "fib_mpls_label",
+ [
+ "u8",
+ "is_uniform"
+ ],
+ [
+ "u32",
+ "label"
+ ],
+ [
+ "u8",
+ "ttl"
+ ],
+ [
+ "u8",
+ "exp"
+ ]
+ ],
+ [
+ "fib_path_nh",
+ [
+ "vl_api_address_union_t",
+ "address"
+ ],
+ [
+ "u32",
+ "via_label"
+ ],
+ [
+ "u32",
+ "obj_id"
+ ],
+ [
+ "u32",
+ "classify_table_index"
+ ]
+ ],
+ [
+ "fib_path",
+ [
+ "u32",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "rpf_id"
+ ],
+ [
+ "u8",
+ "weight"
+ ],
+ [
+ "u8",
+ "preference"
+ ],
+ [
+ "vl_api_fib_path_type_t",
+ "type"
+ ],
+ [
+ "vl_api_fib_path_flags_t",
+ "flags"
+ ],
+ [
+ "vl_api_fib_path_nh_proto_t",
+ "proto"
+ ],
+ [
+ "vl_api_fib_path_nh_t",
+ "nh"
+ ],
+ [
+ "u8",
+ "n_labels"
+ ],
+ [
+ "vl_api_fib_mpls_label_t",
+ "label_stack",
+ 16
+ ]
+ ],
+ [
+ "address",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "vl_api_address_union_t",
+ "un"
+ ]
+ ],
+ [
+ "prefix",
+ [
+ "vl_api_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "mprefix",
+ [
+ "vl_api_address_family_t",
+ "af"
+ ],
+ [
+ "u16",
+ "grp_address_length"
+ ],
+ [
+ "vl_api_address_union_t",
+ "grp_address"
+ ],
+ [
+ "vl_api_address_union_t",
+ "src_address"
+ ]
+ ],
+ [
+ "ip6_prefix",
+ [
+ "vl_api_ip6_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "ip4_prefix",
+ [
+ "vl_api_ip4_address_t",
+ "address"
+ ],
+ [
+ "u8",
+ "len"
+ ]
+ ],
+ [
+ "prefix_matcher",
+ [
+ "u8",
+ "le"
+ ],
+ [
+ "u8",
+ "ge"
+ ]
+ ],
+ [
+ "mfib_path",
+ [
+ "vl_api_mfib_itf_flags_t",
+ "itf_flags"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "path"
+ ]
+ ],
+ [
+ "ip_table",
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "bool",
+ "is_ip6"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ]
+ ],
+ [
+ "ip_route",
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "stats_index"
+ ],
+ [
+ "vl_api_prefix_t",
+ "prefix"
+ ],
+ [
+ "u8",
+ "n_paths"
+ ],
+ [
+ "vl_api_fib_path_t",
+ "paths",
+ 0,
+ "n_paths"
+ ]
+ ],
+ [
+ "ip_mroute",
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u32",
+ "entry_flags"
+ ],
+ [
+ "u32",
+ "rpf_id"
+ ],
+ [
+ "vl_api_mprefix_t",
+ "prefix"
+ ],
+ [
+ "u8",
+ "n_paths"
+ ],
+ [
+ "vl_api_mfib_path_t",
+ "paths",
+ 0,
+ "n_paths"
+ ]
+ ],
+ [
+ "punt_redirect",
+ [
+ "vl_api_interface_index_t",
+ "rx_sw_if_index"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "tx_sw_if_index"
+ ],
+ [
+ "vl_api_address_t",
+ "nh"
+ ]
+ ]
+ ],
+ "messages": [
+ [
+ "ip_table_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0x0ffdaec0"
+ }
+ ],
+ [
+ "ip_table_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_table_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip_table_replace_begin",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_table_replace_begin_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_table_replace_end",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_table_replace_end_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_table_flush",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_table_flush_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_table_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xc79fca0f"
+ }
+ ],
+ [
+ "ip_route_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "bool",
+ "is_multipath"
+ ],
+ [
+ "vl_api_ip_route_t",
+ "route"
+ ],
+ {
+ "crc": "0xc1ff832d"
+ }
+ ],
+ [
+ "ip_route_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "stats_index"
+ ],
+ {
+ "crc": "0x1992deab"
+ }
+ ],
+ [
+ "ip_route_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_route_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_route_t",
+ "route"
+ ],
+ {
+ "crc": "0xd1ffaae1"
+ }
+ ],
+ [
+ "ip_route_lookup",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "u8",
+ "exact"
+ ],
+ [
+ "vl_api_prefix_t",
+ "prefix"
+ ],
+ {
+ "crc": "0xe2986185"
+ }
+ ],
+ [
+ "ip_route_lookup_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "vl_api_ip_route_t",
+ "route"
+ ],
+ {
+ "crc": "0xae99de8e"
+ }
+ ],
+ [
+ "set_ip_flow_hash",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ [
+ "bool",
+ "is_ipv6"
+ ],
+ [
+ "bool",
+ "src"
+ ],
+ [
+ "bool",
+ "dst"
+ ],
+ [
+ "bool",
+ "sport"
+ ],
+ [
+ "bool",
+ "dport"
+ ],
+ [
+ "bool",
+ "proto"
+ ],
+ [
+ "bool",
+ "reverse"
+ ],
+ [
+ "bool",
+ "symmetric"
+ ],
+ {
+ "crc": "0x084ee09e"
+ }
+ ],
+ [
+ "set_ip_flow_hash_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "sw_interface_ip6_enable_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "enable"
+ ],
+ {
+ "crc": "0xae6cfcfb"
+ }
+ ],
+ [
+ "sw_interface_ip6_enable_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_mtable_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip_mtable_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_mroute_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "bool",
+ "is_multipath"
+ ],
+ [
+ "vl_api_ip_mroute_t",
+ "route"
+ ],
+ {
+ "crc": "0xf6627d17"
+ }
+ ],
+ [
+ "ip_mroute_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "stats_index"
+ ],
+ {
+ "crc": "0x1992deab"
+ }
+ ],
+ [
+ "ip_mroute_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_table_t",
+ "table"
+ ],
+ {
+ "crc": "0xb9d2e09e"
+ }
+ ],
+ [
+ "ip_mroute_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_ip_mroute_t",
+ "route"
+ ],
+ {
+ "crc": "0xc1cb4b44"
+ }
+ ],
+ [
+ "ip_address_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "vl_api_address_with_prefix_t",
+ "prefix"
+ ],
+ {
+ "crc": "0xb1199745"
+ }
+ ],
+ [
+ "ip_address_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x2d033de4"
+ }
+ ],
+ [
+ "ip_unnumbered_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "ip_sw_if_index"
+ ],
+ {
+ "crc": "0xaa12a483"
+ }
+ ],
+ [
+ "ip_unnumbered_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index",
+ {
+ "default": 4294967295
+ }
+ ],
+ {
+ "crc": "0xf9e6675e"
+ }
+ ],
+ [
+ "ip_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0xeb152d07"
+ }
+ ],
+ [
+ "ip_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x98d231ca"
+ }
+ ],
+ [
+ "mfib_signal_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "mfib_signal_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "table_id"
+ ],
+ [
+ "vl_api_mprefix_t",
+ "prefix"
+ ],
+ [
+ "u16",
+ "ip_packet_len"
+ ],
+ [
+ "u8",
+ "ip_packet_data",
+ 256
+ ],
+ {
+ "crc": "0x64398a9a"
+ }
+ ],
+ [
+ "ip_punt_police",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "policer_index"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "bool",
+ "is_ip6"
+ ],
+ {
+ "crc": "0xdb867cea"
+ }
+ ],
+ [
+ "ip_punt_police_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_punt_redirect",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_punt_redirect_t",
+ "punt"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ {
+ "crc": "0xa9a5592c"
+ }
+ ],
+ [
+ "ip_punt_redirect_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_punt_redirect_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "is_ipv6"
+ ],
+ {
+ "crc": "0x2d033de4"
+ }
+ ],
+ [
+ "ip_punt_redirect_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_punt_redirect_t",
+ "punt"
+ ],
+ {
+ "crc": "0x3924f5d3"
+ }
+ ],
+ [
+ "ip_container_proxy_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_prefix_t",
+ "pfx"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ {
+ "crc": "0x91189f40"
+ }
+ ],
+ [
+ "ip_container_proxy_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_container_proxy_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "ip_container_proxy_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "vl_api_prefix_t",
+ "prefix"
+ ],
+ {
+ "crc": "0x0ee460e8"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "vl_api_prefix_t",
+ "prefix"
+ ],
+ [
+ "u8",
+ "number_of_ranges"
+ ],
+ [
+ "u16",
+ "low_ports",
+ 32
+ ],
+ [
+ "u16",
+ "high_ports",
+ 32
+ ],
+ [
+ "u32",
+ "vrf_id"
+ ],
+ {
+ "crc": "0x8bfc76f2"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_interface_add_del",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_add",
+ {
+ "default": "true"
+ }
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "u32",
+ "tcp_in_vrf_id"
+ ],
+ [
+ "u32",
+ "tcp_out_vrf_id"
+ ],
+ [
+ "u32",
+ "udp_in_vrf_id"
+ ],
+ [
+ "u32",
+ "udp_out_vrf_id"
+ ],
+ {
+ "crc": "0xe1ba8987"
+ }
+ ],
+ [
+ "ip_source_and_port_range_check_interface_add_del_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "sw_interface_ip6_set_link_local_address",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "vl_api_ip6_address_t",
+ "ip"
+ ],
+ {
+ "crc": "0x2931d9fa"
+ }
+ ],
+ [
+ "sw_interface_ip6_set_link_local_address_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ioam_enable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u16",
+ "id"
+ ],
+ [
+ "bool",
+ "seqno"
+ ],
+ [
+ "bool",
+ "analyse"
+ ],
+ [
+ "bool",
+ "pot_enable"
+ ],
+ [
+ "bool",
+ "trace_enable"
+ ],
+ [
+ "u32",
+ "node_id"
+ ],
+ {
+ "crc": "0x51ccd868"
+ }
+ ],
+ [
+ "ioam_enable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ioam_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u16",
+ "id"
+ ],
+ {
+ "crc": "0x6b16a45e"
+ }
+ ],
+ [
+ "ioam_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_reassembly_set",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "timeout_ms"
+ ],
+ [
+ "u32",
+ "max_reassemblies"
+ ],
+ [
+ "u32",
+ "max_reassembly_length"
+ ],
+ [
+ "u32",
+ "expire_walk_interval_ms"
+ ],
+ [
+ "bool",
+ "is_ip6"
+ ],
+ [
+ "vl_api_ip_reass_type_t",
+ "type"
+ ],
+ {
+ "crc": "0x16467d25"
+ }
+ ],
+ [
+ "ip_reassembly_set_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ],
+ [
+ "ip_reassembly_get",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "bool",
+ "is_ip6"
+ ],
+ [
+ "vl_api_ip_reass_type_t",
+ "type"
+ ],
+ {
+ "crc": "0xea13ff63"
+ }
+ ],
+ [
+ "ip_reassembly_get_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "timeout_ms"
+ ],
+ [
+ "u32",
+ "max_reassemblies"
+ ],
+ [
+ "u32",
+ "max_reassembly_length"
+ ],
+ [
+ "u32",
+ "expire_walk_interval_ms"
+ ],
+ [
+ "bool",
+ "is_ip6"
+ ],
+ {
+ "crc": "0xd5eb8d34"
+ }
+ ],
+ [
+ "ip_reassembly_enable_disable",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_interface_index_t",
+ "sw_if_index"
+ ],
+ [
+ "bool",
+ "enable_ip4"
+ ],
+ [
+ "bool",
+ "enable_ip6"
+ ],
+ [
+ "vl_api_ip_reass_type_t",
+ "type"
+ ],
+ {
+ "crc": "0x885c85a6"
+ }
+ ],
+ [
+ "ip_reassembly_enable_disable_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804"
+ }
+ ]
+ ],
+ "unions": [
+ [
+ "address_union",
+ [
+ "vl_api_ip4_address_t",
+ "ip4"
+ ],
+ [
+ "vl_api_ip6_address_t",
+ "ip6"
+ ]
+ ],
+ [
+ "address_union",
+ [
+ "vl_api_ip4_address_t",
+ "ip4"
+ ],
+ [
+ "vl_api_ip6_address_t",
+ "ip6"
+ ]
+ ],
+ [
+ "address_union",
+ [
+ "vl_api_ip4_address_t",
+ "ip4"
+ ],
+ [
+ "vl_api_ip6_address_t",
+ "ip6"
+ ]
+ ]
+ ],
+ "enums": [
+ [
+ "if_status_flags",
+ [
+ "IF_STATUS_API_FLAG_ADMIN_UP",
+ 1
+ ],
+ [
+ "IF_STATUS_API_FLAG_LINK_UP",
+ 2
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "mtu_proto",
+ [
+ "MTU_PROTO_API_L3",
+ 0
+ ],
+ [
+ "MTU_PROTO_API_IP4",
+ 1
+ ],
+ [
+ "MTU_PROTO_API_IP6",
+ 2
+ ],
+ [
+ "MTU_PROTO_API_MPLS",
+ 3
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "link_duplex",
+ [
+ "LINK_DUPLEX_API_UNKNOWN",
+ 0
+ ],
+ [
+ "LINK_DUPLEX_API_HALF",
+ 1
+ ],
+ [
+ "LINK_DUPLEX_API_FULL",
+ 2
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "sub_if_flags",
+ [
+ "SUB_IF_API_FLAG_NO_TAGS",
+ 1
+ ],
+ [
+ "SUB_IF_API_FLAG_ONE_TAG",
+ 2
+ ],
+ [
+ "SUB_IF_API_FLAG_TWO_TAGS",
+ 4
+ ],
+ [
+ "SUB_IF_API_FLAG_DOT1AD",
+ 8
+ ],
+ [
+ "SUB_IF_API_FLAG_EXACT_MATCH",
+ 16
+ ],
+ [
+ "SUB_IF_API_FLAG_DEFAULT",
+ 32
+ ],
+ [
+ "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY",
+ 64
+ ],
+ [
+ "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY",
+ 128
+ ],
+ [
+ "SUB_IF_API_FLAG_MASK_VNET",
+ 254
+ ],
+ [
+ "SUB_IF_API_FLAG_DOT1AH",
+ 256
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "rx_mode",
+ [
+ "RX_MODE_API_UNKNOWN",
+ 0
+ ],
+ [
+ "RX_MODE_API_POLLING",
+ 1
+ ],
+ [
+ "RX_MODE_API_INTERRUPT",
+ 2
+ ],
+ [
+ "RX_MODE_API_ADAPTIVE",
+ 3
+ ],
+ [
+ "RX_MODE_API_DEFAULT",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "if_type",
+ [
+ "IF_API_TYPE_HARDWARE",
+ 0
+ ],
+ [
+ "IF_API_TYPE_SUB",
+ 1
+ ],
+ [
+ "IF_API_TYPE_P2P",
+ 2
+ ],
+ [
+ "IF_API_TYPE_PIPE",
+ 3
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "address_family",
+ [
+ "ADDRESS_IP4",
+ 0
+ ],
+ [
+ "ADDRESS_IP6",
+ 1
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_ecn",
+ [
+ "IP_API_ECN_NONE",
+ 0
+ ],
+ [
+ "IP_API_ECN_ECT0",
+ 1
+ ],
+ [
+ "IP_API_ECN_ECT1",
+ 2
+ ],
+ [
+ "IP_API_ECN_CE",
+ 3
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_dscp",
+ [
+ "IP_API_DSCP_CS0",
+ 0
+ ],
+ [
+ "IP_API_DSCP_CS1",
+ 8
+ ],
+ [
+ "IP_API_DSCP_AF11",
+ 10
+ ],
+ [
+ "IP_API_DSCP_AF12",
+ 12
+ ],
+ [
+ "IP_API_DSCP_AF13",
+ 14
+ ],
+ [
+ "IP_API_DSCP_CS2",
+ 16
+ ],
+ [
+ "IP_API_DSCP_AF21",
+ 18
+ ],
+ [
+ "IP_API_DSCP_AF22",
+ 20
+ ],
+ [
+ "IP_API_DSCP_AF23",
+ 22
+ ],
+ [
+ "IP_API_DSCP_CS3",
+ 24
+ ],
+ [
+ "IP_API_DSCP_AF31",
+ 26
+ ],
+ [
+ "IP_API_DSCP_AF32",
+ 28
+ ],
+ [
+ "IP_API_DSCP_AF33",
+ 30
+ ],
+ [
+ "IP_API_DSCP_CS4",
+ 32
+ ],
+ [
+ "IP_API_DSCP_AF41",
+ 34
+ ],
+ [
+ "IP_API_DSCP_AF42",
+ 36
+ ],
+ [
+ "IP_API_DSCP_AF43",
+ 38
+ ],
+ [
+ "IP_API_DSCP_CS5",
+ 40
+ ],
+ [
+ "IP_API_DSCP_EF",
+ 46
+ ],
+ [
+ "IP_API_DSCP_CS6",
+ 48
+ ],
+ [
+ "IP_API_DSCP_CS7",
+ 50
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_proto",
+ [
+ "IP_API_PROTO_HOPOPT",
+ 0
+ ],
+ [
+ "IP_API_PROTO_ICMP",
+ 1
+ ],
+ [
+ "IP_API_PROTO_IGMP",
+ 2
+ ],
+ [
+ "IP_API_PROTO_TCP",
+ 6
+ ],
+ [
+ "IP_API_PROTO_UDP",
+ 17
+ ],
+ [
+ "IP_API_PROTO_GRE",
+ 47
+ ],
+ [
+ "IP_API_PROTO_ESP",
+ 50
+ ],
+ [
+ "IP_API_PROTO_AH",
+ 51
+ ],
+ [
+ "IP_API_PROTO_ICMP6",
+ 58
+ ],
+ [
+ "IP_API_PROTO_EIGRP",
+ 88
+ ],
+ [
+ "IP_API_PROTO_OSPF",
+ 89
+ ],
+ [
+ "IP_API_PROTO_SCTP",
+ 132
+ ],
+ [
+ "IP_API_PROTO_RESERVED",
+ 255
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "fib_path_nh_proto",
+ [
+ "FIB_API_PATH_NH_PROTO_IP4",
+ 0
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_IP6",
+ 1
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_MPLS",
+ 2
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_ETHERNET",
+ 3
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_BIER",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "fib_path_flags",
+ [
+ "FIB_API_PATH_FLAG_NONE",
+ 0
+ ],
+ [
+ "FIB_API_PATH_FLAG_RESOLVE_VIA_ATTACHED",
+ 1
+ ],
+ [
+ "FIB_API_PATH_FLAG_RESOLVE_VIA_HOST",
+ 2
+ ],
+ [
+ "FIB_API_PATH_FLAG_POP_PW_CW",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "fib_path_type",
+ [
+ "FIB_API_PATH_TYPE_NORMAL",
+ 0
+ ],
+ [
+ "FIB_API_PATH_TYPE_LOCAL",
+ 1
+ ],
+ [
+ "FIB_API_PATH_TYPE_DROP",
+ 2
+ ],
+ [
+ "FIB_API_PATH_TYPE_UDP_ENCAP",
+ 3
+ ],
+ [
+ "FIB_API_PATH_TYPE_BIER_IMP",
+ 4
+ ],
+ [
+ "FIB_API_PATH_TYPE_ICMP_UNREACH",
+ 5
+ ],
+ [
+ "FIB_API_PATH_TYPE_ICMP_PROHIBIT",
+ 6
+ ],
+ [
+ "FIB_API_PATH_TYPE_SOURCE_LOOKUP",
+ 7
+ ],
+ [
+ "FIB_API_PATH_TYPE_DVR",
+ 8
+ ],
+ [
+ "FIB_API_PATH_TYPE_INTERFACE_RX",
+ 9
+ ],
+ [
+ "FIB_API_PATH_TYPE_CLASSIFY",
+ 10
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "address_family",
+ [
+ "ADDRESS_IP4",
+ 0
+ ],
+ [
+ "ADDRESS_IP6",
+ 1
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_ecn",
+ [
+ "IP_API_ECN_NONE",
+ 0
+ ],
+ [
+ "IP_API_ECN_ECT0",
+ 1
+ ],
+ [
+ "IP_API_ECN_ECT1",
+ 2
+ ],
+ [
+ "IP_API_ECN_CE",
+ 3
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_dscp",
+ [
+ "IP_API_DSCP_CS0",
+ 0
+ ],
+ [
+ "IP_API_DSCP_CS1",
+ 8
+ ],
+ [
+ "IP_API_DSCP_AF11",
+ 10
+ ],
+ [
+ "IP_API_DSCP_AF12",
+ 12
+ ],
+ [
+ "IP_API_DSCP_AF13",
+ 14
+ ],
+ [
+ "IP_API_DSCP_CS2",
+ 16
+ ],
+ [
+ "IP_API_DSCP_AF21",
+ 18
+ ],
+ [
+ "IP_API_DSCP_AF22",
+ 20
+ ],
+ [
+ "IP_API_DSCP_AF23",
+ 22
+ ],
+ [
+ "IP_API_DSCP_CS3",
+ 24
+ ],
+ [
+ "IP_API_DSCP_AF31",
+ 26
+ ],
+ [
+ "IP_API_DSCP_AF32",
+ 28
+ ],
+ [
+ "IP_API_DSCP_AF33",
+ 30
+ ],
+ [
+ "IP_API_DSCP_CS4",
+ 32
+ ],
+ [
+ "IP_API_DSCP_AF41",
+ 34
+ ],
+ [
+ "IP_API_DSCP_AF42",
+ 36
+ ],
+ [
+ "IP_API_DSCP_AF43",
+ 38
+ ],
+ [
+ "IP_API_DSCP_CS5",
+ 40
+ ],
+ [
+ "IP_API_DSCP_EF",
+ 46
+ ],
+ [
+ "IP_API_DSCP_CS6",
+ 48
+ ],
+ [
+ "IP_API_DSCP_CS7",
+ 50
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_proto",
+ [
+ "IP_API_PROTO_HOPOPT",
+ 0
+ ],
+ [
+ "IP_API_PROTO_ICMP",
+ 1
+ ],
+ [
+ "IP_API_PROTO_IGMP",
+ 2
+ ],
+ [
+ "IP_API_PROTO_TCP",
+ 6
+ ],
+ [
+ "IP_API_PROTO_UDP",
+ 17
+ ],
+ [
+ "IP_API_PROTO_GRE",
+ 47
+ ],
+ [
+ "IP_API_PROTO_ESP",
+ 50
+ ],
+ [
+ "IP_API_PROTO_AH",
+ 51
+ ],
+ [
+ "IP_API_PROTO_ICMP6",
+ 58
+ ],
+ [
+ "IP_API_PROTO_EIGRP",
+ 88
+ ],
+ [
+ "IP_API_PROTO_OSPF",
+ 89
+ ],
+ [
+ "IP_API_PROTO_SCTP",
+ 132
+ ],
+ [
+ "IP_API_PROTO_RESERVED",
+ 255
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "fib_path_nh_proto",
+ [
+ "FIB_API_PATH_NH_PROTO_IP4",
+ 0
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_IP6",
+ 1
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_MPLS",
+ 2
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_ETHERNET",
+ 3
+ ],
+ [
+ "FIB_API_PATH_NH_PROTO_BIER",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "fib_path_flags",
+ [
+ "FIB_API_PATH_FLAG_NONE",
+ 0
+ ],
+ [
+ "FIB_API_PATH_FLAG_RESOLVE_VIA_ATTACHED",
+ 1
+ ],
+ [
+ "FIB_API_PATH_FLAG_RESOLVE_VIA_HOST",
+ 2
+ ],
+ [
+ "FIB_API_PATH_FLAG_POP_PW_CW",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "fib_path_type",
+ [
+ "FIB_API_PATH_TYPE_NORMAL",
+ 0
+ ],
+ [
+ "FIB_API_PATH_TYPE_LOCAL",
+ 1
+ ],
+ [
+ "FIB_API_PATH_TYPE_DROP",
+ 2
+ ],
+ [
+ "FIB_API_PATH_TYPE_UDP_ENCAP",
+ 3
+ ],
+ [
+ "FIB_API_PATH_TYPE_BIER_IMP",
+ 4
+ ],
+ [
+ "FIB_API_PATH_TYPE_ICMP_UNREACH",
+ 5
+ ],
+ [
+ "FIB_API_PATH_TYPE_ICMP_PROHIBIT",
+ 6
+ ],
+ [
+ "FIB_API_PATH_TYPE_SOURCE_LOOKUP",
+ 7
+ ],
+ [
+ "FIB_API_PATH_TYPE_DVR",
+ 8
+ ],
+ [
+ "FIB_API_PATH_TYPE_INTERFACE_RX",
+ 9
+ ],
+ [
+ "FIB_API_PATH_TYPE_CLASSIFY",
+ 10
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "address_family",
+ [
+ "ADDRESS_IP4",
+ 0
+ ],
+ [
+ "ADDRESS_IP6",
+ 1
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_ecn",
+ [
+ "IP_API_ECN_NONE",
+ 0
+ ],
+ [
+ "IP_API_ECN_ECT0",
+ 1
+ ],
+ [
+ "IP_API_ECN_ECT1",
+ 2
+ ],
+ [
+ "IP_API_ECN_CE",
+ 3
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_dscp",
+ [
+ "IP_API_DSCP_CS0",
+ 0
+ ],
+ [
+ "IP_API_DSCP_CS1",
+ 8
+ ],
+ [
+ "IP_API_DSCP_AF11",
+ 10
+ ],
+ [
+ "IP_API_DSCP_AF12",
+ 12
+ ],
+ [
+ "IP_API_DSCP_AF13",
+ 14
+ ],
+ [
+ "IP_API_DSCP_CS2",
+ 16
+ ],
+ [
+ "IP_API_DSCP_AF21",
+ 18
+ ],
+ [
+ "IP_API_DSCP_AF22",
+ 20
+ ],
+ [
+ "IP_API_DSCP_AF23",
+ 22
+ ],
+ [
+ "IP_API_DSCP_CS3",
+ 24
+ ],
+ [
+ "IP_API_DSCP_AF31",
+ 26
+ ],
+ [
+ "IP_API_DSCP_AF32",
+ 28
+ ],
+ [
+ "IP_API_DSCP_AF33",
+ 30
+ ],
+ [
+ "IP_API_DSCP_CS4",
+ 32
+ ],
+ [
+ "IP_API_DSCP_AF41",
+ 34
+ ],
+ [
+ "IP_API_DSCP_AF42",
+ 36
+ ],
+ [
+ "IP_API_DSCP_AF43",
+ 38
+ ],
+ [
+ "IP_API_DSCP_CS5",
+ 40
+ ],
+ [
+ "IP_API_DSCP_EF",
+ 46
+ ],
+ [
+ "IP_API_DSCP_CS6",
+ 48
+ ],
+ [
+ "IP_API_DSCP_CS7",
+ 50
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "ip_proto",
+ [
+ "IP_API_PROTO_HOPOPT",
+ 0
+ ],
+ [
+ "IP_API_PROTO_ICMP",
+ 1
+ ],
+ [
+ "IP_API_PROTO_IGMP",
+ 2
+ ],
+ [
+ "IP_API_PROTO_TCP",
+ 6
+ ],
+ [
+ "IP_API_PROTO_UDP",
+ 17
+ ],
+ [
+ "IP_API_PROTO_GRE",
+ 47
+ ],
+ [
+ "IP_API_PROTO_ESP",
+ 50
+ ],
+ [
+ "IP_API_PROTO_AH",
+ 51
+ ],
+ [
+ "IP_API_PROTO_ICMP6",
+ 58
+ ],
+ [
+ "IP_API_PROTO_EIGRP",
+ 88
+ ],
+ [
+ "IP_API_PROTO_OSPF",
+ 89
+ ],
+ [
+ "IP_API_PROTO_SCTP",
+ 132
+ ],
+ [
+ "IP_API_PROTO_RESERVED",
+ 255
+ ],
+ {
+ "enumtype": "u8"
+ }
+ ],
+ [
+ "mfib_itf_flags",
+ [
+ "MFIB_API_ITF_FLAG_NONE",
+ 0
+ ],
+ [
+ "MFIB_API_ITF_FLAG_NEGATE_SIGNAL",
+ 1
+ ],
+ [
+ "MFIB_API_ITF_FLAG_ACCEPT",
+ 2
+ ],
+ [
+ "MFIB_API_ITF_FLAG_FORWARD",
+ 4
+ ],
+ [
+ "MFIB_API_ITF_FLAG_SIGNAL_PRESENT",
+ 8
+ ],
+ [
+ "MFIB_API_ITF_FLAG_DONT_PRESERVE",
+ 16
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "if_status_flags",
+ [
+ "IF_STATUS_API_FLAG_ADMIN_UP",
+ 1
+ ],
+ [
+ "IF_STATUS_API_FLAG_LINK_UP",
+ 2
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "mtu_proto",
+ [
+ "MTU_PROTO_API_L3",
+ 0
+ ],
+ [
+ "MTU_PROTO_API_IP4",
+ 1
+ ],
+ [
+ "MTU_PROTO_API_IP6",
+ 2
+ ],
+ [
+ "MTU_PROTO_API_MPLS",
+ 3
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "link_duplex",
+ [
+ "LINK_DUPLEX_API_UNKNOWN",
+ 0
+ ],
+ [
+ "LINK_DUPLEX_API_HALF",
+ 1
+ ],
+ [
+ "LINK_DUPLEX_API_FULL",
+ 2
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "sub_if_flags",
+ [
+ "SUB_IF_API_FLAG_NO_TAGS",
+ 1
+ ],
+ [
+ "SUB_IF_API_FLAG_ONE_TAG",
+ 2
+ ],
+ [
+ "SUB_IF_API_FLAG_TWO_TAGS",
+ 4
+ ],
+ [
+ "SUB_IF_API_FLAG_DOT1AD",
+ 8
+ ],
+ [
+ "SUB_IF_API_FLAG_EXACT_MATCH",
+ 16
+ ],
+ [
+ "SUB_IF_API_FLAG_DEFAULT",
+ 32
+ ],
+ [
+ "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY",
+ 64
+ ],
+ [
+ "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY",
+ 128
+ ],
+ [
+ "SUB_IF_API_FLAG_MASK_VNET",
+ 254
+ ],
+ [
+ "SUB_IF_API_FLAG_DOT1AH",
+ 256
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "rx_mode",
+ [
+ "RX_MODE_API_UNKNOWN",
+ 0
+ ],
+ [
+ "RX_MODE_API_POLLING",
+ 1
+ ],
+ [
+ "RX_MODE_API_INTERRUPT",
+ 2
+ ],
+ [
+ "RX_MODE_API_ADAPTIVE",
+ 3
+ ],
+ [
+ "RX_MODE_API_DEFAULT",
+ 4
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "if_type",
+ [
+ "IF_API_TYPE_HARDWARE",
+ 0
+ ],
+ [
+ "IF_API_TYPE_SUB",
+ 1
+ ],
+ [
+ "IF_API_TYPE_P2P",
+ 2
+ ],
+ [
+ "IF_API_TYPE_PIPE",
+ 3
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ],
+ [
+ "ip_reass_type",
+ [
+ "IP_REASS_TYPE_FULL",
+ 0
+ ],
+ [
+ "IP_REASS_TYPE_SHALLOW_VIRTUAL",
+ 1
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ]
+ ],
+ "services": {
+ "ip_table_add_del": {
+ "reply": "ip_table_add_del_reply"
+ },
+ "ip_table_dump": {
+ "reply": "ip_table_details",
+ "stream": true
+ },
+ "ip_table_replace_begin": {
+ "reply": "ip_table_replace_begin_reply"
+ },
+ "ip_table_replace_end": {
+ "reply": "ip_table_replace_end_reply"
+ },
+ "ip_table_flush": {
+ "reply": "ip_table_flush_reply"
+ },
+ "ip_route_add_del": {
+ "reply": "ip_route_add_del_reply"
+ },
+ "ip_route_dump": {
+ "reply": "ip_route_details",
+ "stream": true
+ },
+ "ip_route_lookup": {
+ "reply": "ip_route_lookup_reply"
+ },
+ "set_ip_flow_hash": {
+ "reply": "set_ip_flow_hash_reply"
+ },
+ "sw_interface_ip6_enable_disable": {
+ "reply": "sw_interface_ip6_enable_disable_reply"
+ },
+ "ip_mtable_dump": {
+ "reply": "ip_mtable_details",
+ "stream": true
+ },
+ "ip_mroute_add_del": {
+ "reply": "ip_mroute_add_del_reply"
+ },
+ "ip_mroute_dump": {
+ "reply": "ip_mroute_details",
+ "stream": true
+ },
+ "ip_address_dump": {
+ "reply": "ip_address_details",
+ "stream": true
+ },
+ "ip_unnumbered_dump": {
+ "reply": "ip_unnumbered_details",
+ "stream": true
+ },
+ "ip_dump": {
+ "reply": "ip_details",
+ "stream": true
+ },
+ "mfib_signal_dump": {
+ "reply": "mfib_signal_details",
+ "stream": true
+ },
+ "ip_punt_police": {
+ "reply": "ip_punt_police_reply"
+ },
+ "ip_punt_redirect": {
+ "reply": "ip_punt_redirect_reply"
+ },
+ "ip_punt_redirect_dump": {
+ "reply": "ip_punt_redirect_details",
+ "stream": true
+ },
+ "ip_container_proxy_add_del": {
+ "reply": "ip_container_proxy_add_del_reply"
+ },
+ "ip_container_proxy_dump": {
+ "reply": "ip_container_proxy_details",
+ "stream": true
+ },
+ "ip_source_and_port_range_check_add_del": {
+ "reply": "ip_source_and_port_range_check_add_del_reply"
+ },
+ "ip_source_and_port_range_check_interface_add_del": {
+ "reply": "ip_source_and_port_range_check_interface_add_del_reply"
+ },
+ "sw_interface_ip6_set_link_local_address": {
+ "reply": "sw_interface_ip6_set_link_local_address_reply"
+ },
+ "ioam_enable": {
+ "reply": "ioam_enable_reply"
+ },
+ "ioam_disable": {
+ "reply": "ioam_disable_reply"
+ },
+ "ip_reassembly_set": {
+ "reply": "ip_reassembly_set_reply"
+ },
+ "ip_reassembly_get": {
+ "reply": "ip_reassembly_get_reply"
+ },
+ "ip_reassembly_enable_disable": {
+ "reply": "ip_reassembly_enable_disable_reply"
+ }
+ },
+ "options": {
+ "version": "3.0.1"
+ },
+ "aliases": {
+ "interface_index": {
+ "type": "u32"
+ },
+ "ip4_address": {
+ "type": "u8",
+ "length": 4
+ },
+ "ip6_address": {
+ "type": "u8",
+ "length": 16
+ },
+ "address_with_prefix": {
+ "type": "vl_api_prefix_t"
+ },
+ "ip4_address_with_prefix": {
+ "type": "vl_api_ip4_prefix_t"
+ },
+ "ip6_address_with_prefix": {
+ "type": "vl_api_ip6_prefix_t"
+ },
+ "mac_address": {
+ "type": "u8",
+ "length": 6
+ }
+ },
+ "vl_api_version": "0x765d74b1",
+ "imports": [
+ "vnet/interface_types.api",
+ "vnet/fib/fib_types.api",
+ "vnet/ip/ip_types.api",
+ "vnet/ethernet/ethernet_types.api",
+ "vnet/mfib/mfib_types.api",
+ "vnet/fib/fib_types.api",
+ "vnet/ip/ip_types.api",
+ "vnet/ip/ip_types.api",
+ "vnet/interface_types.api"
+ ]
+ }
+]
diff --git a/binapigen/vppapi/testdata/vpe.api.json b/binapigen/vppapi/testdata/vpe.api.json
new file mode 100644
index 0000000..960ba12
--- /dev/null
+++ b/binapigen/vppapi/testdata/vpe.api.json
@@ -0,0 +1,775 @@
+{
+ "types": [
+ [
+ "version",
+ [
+ "u32",
+ "major"
+ ],
+ [
+ "u32",
+ "minor"
+ ],
+ [
+ "u32",
+ "patch"
+ ],
+ [
+ "u8",
+ "pre_release",
+ 17
+ ],
+ [
+ "u8",
+ "build_metadata",
+ 17
+ ]
+ ],
+ [
+ "thread_data",
+ [
+ "u32",
+ "id"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ [
+ "string",
+ "type",
+ 64
+ ],
+ [
+ "u32",
+ "pid"
+ ],
+ [
+ "u32",
+ "cpu_id"
+ ],
+ [
+ "u32",
+ "core"
+ ],
+ [
+ "u32",
+ "cpu_socket"
+ ]
+ ]
+ ],
+ "messages": [
+ [
+ "control_ping",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "control_ping_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "vpe_pid"
+ ],
+ {
+ "crc": "0xf6b0b8ca"
+ }
+ ],
+ [
+ "cli",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u64",
+ "cmd_in_shmem"
+ ],
+ {
+ "crc": "0x23bfbfff"
+ }
+ ],
+ [
+ "cli_inband",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "cmd",
+ 0
+ ],
+ {
+ "crc": "0xf8377302"
+ }
+ ],
+ [
+ "cli_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u64",
+ "reply_in_shmem"
+ ],
+ {
+ "crc": "0x06d68297"
+ }
+ ],
+ [
+ "cli_inband_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "string",
+ "reply",
+ 0
+ ],
+ {
+ "crc": "0x05879051"
+ }
+ ],
+ [
+ "get_node_index",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "node_name",
+ 64
+ ],
+ {
+ "crc": "0xf1984c64"
+ }
+ ],
+ [
+ "get_node_index_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "node_index"
+ ],
+ {
+ "crc": "0xa8600b89"
+ }
+ ],
+ [
+ "add_node_next",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "node_name",
+ 64
+ ],
+ [
+ "string",
+ "next_name",
+ 64
+ ],
+ {
+ "crc": "0x2457116d"
+ }
+ ],
+ [
+ "add_node_next_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "next_index"
+ ],
+ {
+ "crc": "0x2ed75f32"
+ }
+ ],
+ [
+ "show_version",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "show_version_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "string",
+ "program",
+ 32
+ ],
+ [
+ "string",
+ "version",
+ 32
+ ],
+ [
+ "string",
+ "build_date",
+ 32
+ ],
+ [
+ "string",
+ "build_directory",
+ 256
+ ],
+ {
+ "crc": "0xc919bde1"
+ }
+ ],
+ [
+ "show_threads",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "show_threads_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_thread_data_t",
+ "thread_data",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0xefd78e83"
+ }
+ ],
+ [
+ "get_node_graph",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "get_node_graph_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u64",
+ "reply_in_shmem"
+ ],
+ {
+ "crc": "0x06d68297"
+ }
+ ],
+ [
+ "get_next_index",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "node_name",
+ 64
+ ],
+ [
+ "string",
+ "next_name",
+ 64
+ ],
+ {
+ "crc": "0x2457116d"
+ }
+ ],
+ [
+ "get_next_index_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "next_index"
+ ],
+ {
+ "crc": "0x2ed75f32"
+ }
+ ],
+ [
+ "log_dump",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_timestamp_t",
+ "start_timestamp"
+ ],
+ {
+ "crc": "0x6ab31753"
+ }
+ ],
+ [
+ "log_details",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "vl_api_timestamp_t",
+ "timestamp"
+ ],
+ [
+ "vl_api_log_level_t",
+ "level"
+ ],
+ [
+ "string",
+ "msg_class",
+ 32
+ ],
+ [
+ "string",
+ "message",
+ 256
+ ],
+ {
+ "crc": "0x255827a1"
+ }
+ ],
+ [
+ "show_vpe_system_time",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14"
+ }
+ ],
+ [
+ "show_vpe_system_time_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "vl_api_timestamp_t",
+ "vpe_system_time"
+ ],
+ {
+ "crc": "0x7ffd8193"
+ }
+ ],
+ [
+ "get_f64_endian_value",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "f64",
+ "f64_one",
+ {
+ "default": 1.0
+ }
+ ],
+ {
+ "crc": "0x809fcd44"
+ }
+ ],
+ [
+ "get_f64_endian_value_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "retval"
+ ],
+ [
+ "f64",
+ "f64_one_result"
+ ],
+ {
+ "crc": "0x7e02e404"
+ }
+ ],
+ [
+ "get_f64_increment_by_one",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "f64",
+ "f64_value",
+ {
+ "default": 1.0
+ }
+ ],
+ {
+ "crc": "0xb64f027e"
+ }
+ ],
+ [
+ "get_f64_increment_by_one_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "retval"
+ ],
+ [
+ "f64",
+ "f64_value"
+ ],
+ {
+ "crc": "0xd25dbaa3"
+ }
+ ]
+ ],
+ "unions": [],
+ "enums": [
+ [
+ "log_level",
+ [
+ "VPE_API_LOG_LEVEL_EMERG",
+ 0
+ ],
+ [
+ "VPE_API_LOG_LEVEL_ALERT",
+ 1
+ ],
+ [
+ "VPE_API_LOG_LEVEL_CRIT",
+ 2
+ ],
+ [
+ "VPE_API_LOG_LEVEL_ERR",
+ 3
+ ],
+ [
+ "VPE_API_LOG_LEVEL_WARNING",
+ 4
+ ],
+ [
+ "VPE_API_LOG_LEVEL_NOTICE",
+ 5
+ ],
+ [
+ "VPE_API_LOG_LEVEL_INFO",
+ 6
+ ],
+ [
+ "VPE_API_LOG_LEVEL_DEBUG",
+ 7
+ ],
+ [
+ "VPE_API_LOG_LEVEL_DISABLED",
+ 8
+ ],
+ {
+ "enumtype": "u32"
+ }
+ ]
+ ],
+ "services": {
+ "control_ping": {
+ "reply": "control_ping_reply"
+ },
+ "cli": {
+ "reply": "cli_reply"
+ },
+ "cli_inband": {
+ "reply": "cli_inband_reply"
+ },
+ "get_node_index": {
+ "reply": "get_node_index_reply"
+ },
+ "add_node_next": {
+ "reply": "add_node_next_reply"
+ },
+ "show_version": {
+ "reply": "show_version_reply"
+ },
+ "show_threads": {
+ "reply": "show_threads_reply"
+ },
+ "get_node_graph": {
+ "reply": "get_node_graph_reply"
+ },
+ "get_next_index": {
+ "reply": "get_next_index_reply"
+ },
+ "log_dump": {
+ "reply": "log_details",
+ "stream": true
+ },
+ "show_vpe_system_time": {
+ "reply": "show_vpe_system_time_reply"
+ },
+ "get_f64_endian_value": {
+ "reply": "get_f64_endian_value_reply"
+ },
+ "get_f64_increment_by_one": {
+ "reply": "get_f64_increment_by_one_reply"
+ }
+ },
+ "options": {
+ "version": "1.6.1"
+ },
+ "aliases": {
+ "timestamp": {
+ "type": "f64"
+ },
+ "timedelta": {
+ "type": "f64"
+ }
+ },
+ "vl_api_version": "0xbd2c94f4",
+ "imports": [
+ "vpp/api/vpe_types.api"
+ ]
+}