From a8fd79c0e1eb1f253112bc6c46ae0f38de7f18e5 Mon Sep 17 00:00:00 2001 From: Mayuresh Gaitonde Date: Wed, 16 Oct 2019 15:57:55 -0700 Subject: [PATCH] Add app source, add vendoring and module support --- Gopkg.lock | 356 - Gopkg.toml | 42 - Makefile | 15 +- controller/app.go | 5 +- controller/consul.go | 2 +- controller/monitor.go | 7 +- go.mod | 37 + server/server.go | 2 +- vendor/github.com/armon/go-radix/.gitignore | 22 + vendor/github.com/armon/go-radix/.travis.yml | 3 + vendor/github.com/armon/go-radix/LICENSE | 20 + vendor/github.com/armon/go-radix/README.md | 38 + vendor/github.com/armon/go-radix/go.mod | 1 + vendor/github.com/armon/go-radix/radix.go | 540 + vendor/github.com/dgryski/go-farm/.gitignore | 24 + vendor/github.com/dgryski/go-farm/.travis.yml | 38 + vendor/github.com/dgryski/go-farm/LICENSE | 23 + vendor/github.com/dgryski/go-farm/Makefile | 203 + vendor/github.com/dgryski/go-farm/README.md | 41 + vendor/github.com/dgryski/go-farm/VERSION | 1 + vendor/github.com/dgryski/go-farm/basics.go | 30 + .../github.com/dgryski/go-farm/farmhashcc.go | 199 + .../github.com/dgryski/go-farm/farmhashmk.go | 102 + .../github.com/dgryski/go-farm/farmhashna.go | 156 + .../github.com/dgryski/go-farm/farmhashuo.go | 117 + vendor/github.com/dgryski/go-farm/platform.go | 18 + vendor/github.com/eapache/channels/.gitignore | 22 + .../github.com/eapache/channels/.travis.yml | 11 + .../github.com/eapache/channels/CHANGELOG.md | 17 + vendor/github.com/eapache/channels/LICENSE | 20 + vendor/github.com/eapache/channels/README.md | 27 + .../eapache/channels/batching_channel.go | 87 + .../github.com/eapache/channels/black_hole.go | 54 + .../github.com/eapache/channels/channels.go | 277 + .../eapache/channels/infinite_channel.go | 72 + .../eapache/channels/native_channel.go | 92 + .../eapache/channels/overflowing_channel.go | 113 + .../eapache/channels/resizable_channel.go | 109 + .../eapache/channels/ring_channel.go | 114 + .../eapache/channels/shared_buffer.go | 167 + vendor/github.com/eapache/queue/.gitignore | 23 + vendor/github.com/eapache/queue/.travis.yml | 7 + vendor/github.com/eapache/queue/LICENSE | 21 + vendor/github.com/eapache/queue/README.md | 16 + vendor/github.com/eapache/queue/queue.go | 102 + .../fsnotify/fsnotify/.editorconfig | 5 + .../github.com/fsnotify/fsnotify/.gitignore | 6 + .../github.com/fsnotify/fsnotify/.travis.yml | 30 + vendor/github.com/fsnotify/fsnotify/AUTHORS | 52 + .../github.com/fsnotify/fsnotify/CHANGELOG.md | 317 + .../fsnotify/fsnotify/CONTRIBUTING.md | 77 + vendor/github.com/fsnotify/fsnotify/LICENSE | 28 + vendor/github.com/fsnotify/fsnotify/README.md | 79 + vendor/github.com/fsnotify/fsnotify/fen.go | 37 + .../github.com/fsnotify/fsnotify/fsnotify.go | 66 + .../github.com/fsnotify/fsnotify/inotify.go | 337 + .../fsnotify/fsnotify/inotify_poller.go | 187 + vendor/github.com/fsnotify/fsnotify/kqueue.go | 521 + .../fsnotify/fsnotify/open_mode_bsd.go | 11 + .../fsnotify/fsnotify/open_mode_darwin.go | 12 + .../github.com/fsnotify/fsnotify/windows.go | 561 + vendor/github.com/golang/glog/LICENSE | 191 + vendor/github.com/golang/glog/README | 44 + vendor/github.com/golang/glog/glog.go | 1180 + vendor/github.com/golang/glog/glog_file.go | 124 + vendor/github.com/golang/protobuf/AUTHORS | 3 + .../github.com/golang/protobuf/CONTRIBUTORS | 3 + vendor/github.com/golang/protobuf/LICENSE | 28 + .../github.com/golang/protobuf/proto/clone.go | 253 + .../golang/protobuf/proto/decode.go | 427 + .../golang/protobuf/proto/deprecated.go | 38 + .../golang/protobuf/proto/discard.go | 350 + .../golang/protobuf/proto/encode.go | 203 + .../github.com/golang/protobuf/proto/equal.go | 300 + .../golang/protobuf/proto/extensions.go | 543 + .../github.com/golang/protobuf/proto/lib.go | 959 + .../golang/protobuf/proto/message_set.go | 314 + .../golang/protobuf/proto/pointer_reflect.go | 357 + .../golang/protobuf/proto/pointer_unsafe.go | 308 + .../golang/protobuf/proto/properties.go | 535 + .../golang/protobuf/proto/table_marshal.go | 2767 + .../golang/protobuf/proto/table_merge.go | 654 + .../golang/protobuf/proto/table_unmarshal.go | 2051 + .../github.com/golang/protobuf/proto/text.go | 843 + .../golang/protobuf/proto/text_parser.go | 880 + .../github.com/golang/protobuf/ptypes/any.go | 141 + .../golang/protobuf/ptypes/any/any.pb.go | 195 + .../golang/protobuf/ptypes/any/any.proto | 149 + .../github.com/golang/protobuf/ptypes/doc.go | 35 + .../golang/protobuf/ptypes/duration.go | 102 + .../protobuf/ptypes/duration/duration.pb.go | 161 + .../protobuf/ptypes/duration/duration.proto | 117 + .../golang/protobuf/ptypes/empty/empty.pb.go | 83 + .../golang/protobuf/ptypes/empty/empty.proto | 52 + .../golang/protobuf/ptypes/timestamp.go | 134 + .../protobuf/ptypes/timestamp/timestamp.pb.go | 177 + .../protobuf/ptypes/timestamp/timestamp.proto | 133 + vendor/github.com/hashicorp/hcl/.gitignore | 9 + vendor/github.com/hashicorp/hcl/.travis.yml | 13 + vendor/github.com/hashicorp/hcl/LICENSE | 354 + vendor/github.com/hashicorp/hcl/Makefile | 18 + vendor/github.com/hashicorp/hcl/README.md | 125 + vendor/github.com/hashicorp/hcl/appveyor.yml | 19 + vendor/github.com/hashicorp/hcl/decoder.go | 729 + vendor/github.com/hashicorp/hcl/go.mod | 3 + vendor/github.com/hashicorp/hcl/go.sum | 2 + vendor/github.com/hashicorp/hcl/hcl.go | 11 + .../github.com/hashicorp/hcl/hcl/ast/ast.go | 219 + .../github.com/hashicorp/hcl/hcl/ast/walk.go | 52 + .../hashicorp/hcl/hcl/parser/error.go | 17 + .../hashicorp/hcl/hcl/parser/parser.go | 532 + .../hashicorp/hcl/hcl/printer/nodes.go | 789 + .../hashicorp/hcl/hcl/printer/printer.go | 66 + .../hashicorp/hcl/hcl/scanner/scanner.go | 652 + .../hashicorp/hcl/hcl/strconv/quote.go | 241 + .../hashicorp/hcl/hcl/token/position.go | 46 + .../hashicorp/hcl/hcl/token/token.go | 219 + .../hashicorp/hcl/json/parser/flatten.go | 117 + .../hashicorp/hcl/json/parser/parser.go | 313 + .../hashicorp/hcl/json/scanner/scanner.go | 451 + .../hashicorp/hcl/json/token/position.go | 46 + .../hashicorp/hcl/json/token/token.go | 118 + vendor/github.com/hashicorp/hcl/lex.go | 38 + vendor/github.com/hashicorp/hcl/parse.go | 39 + vendor/github.com/influxdata/influxdb/LICENSE | 20 + .../influxdb/LICENSE_OF_DEPENDENCIES.md | 63 + .../influxdata/influxdb/client/v2/client.go | 662 + .../influxdata/influxdb/client/v2/udp.go | 112 + .../influxdata/influxdb/models/consistency.go | 48 + .../influxdata/influxdb/models/inline_fnv.go | 32 + .../influxdb/models/inline_strconv_parse.go | 44 + .../influxdata/influxdb/models/points.go | 2463 + .../influxdata/influxdb/models/rows.go | 62 + .../influxdata/influxdb/models/statistic.go | 42 + .../influxdata/influxdb/models/time.go | 74 + .../influxdb/models/uint_support.go | 7 + .../influxdata/influxdb/pkg/escape/bytes.go | 115 + .../influxdata/influxdb/pkg/escape/strings.go | 21 + .../go-windows-terminal-sequences/LICENSE | 9 + .../go-windows-terminal-sequences/README.md | 40 + .../go-windows-terminal-sequences/go.mod | 1 + .../sequences.go | 36 + .../magiconair/properties/.gitignore | 6 + .../magiconair/properties/.travis.yml | 10 + .../magiconair/properties/CHANGELOG.md | 131 + .../github.com/magiconair/properties/LICENSE | 25 + .../magiconair/properties/README.md | 129 + .../magiconair/properties/decode.go | 289 + .../github.com/magiconair/properties/doc.go | 156 + .../magiconair/properties/integrate.go | 34 + .../github.com/magiconair/properties/lex.go | 407 + .../github.com/magiconair/properties/load.go | 292 + .../magiconair/properties/parser.go | 95 + .../magiconair/properties/properties.go | 833 + .../magiconair/properties/rangecheck.go | 31 + .../mitchellh/mapstructure/.travis.yml | 8 + .../mitchellh/mapstructure/CHANGELOG.md | 21 + .../github.com/mitchellh/mapstructure/LICENSE | 21 + .../mitchellh/mapstructure/README.md | 46 + .../mitchellh/mapstructure/decode_hooks.go | 217 + .../mitchellh/mapstructure/error.go | 50 + .../github.com/mitchellh/mapstructure/go.mod | 1 + .../mitchellh/mapstructure/mapstructure.go | 1149 + vendor/github.com/osrg/gobgp/LICENSE | 201 + .../github.com/osrg/gobgp/api/attribute.pb.go | 2088 + .../github.com/osrg/gobgp/api/attribute.proto | 505 + .../osrg/gobgp/api/capability.pb.go | 380 + .../osrg/gobgp/api/capability.proto | 100 + vendor/github.com/osrg/gobgp/api/gobgp.pb.go | 8400 ++ vendor/github.com/osrg/gobgp/api/gobgp.proto | 1175 + .../gobgp/internal/pkg/apiutil/attribute.go | 1303 + .../gobgp/internal/pkg/apiutil/capability.go | 246 + .../osrg/gobgp/internal/pkg/apiutil/util.go | 125 + .../gobgp/internal/pkg/config/bgp_configs.go | 6335 ++ .../osrg/gobgp/internal/pkg/config/default.go | 524 + .../internal/pkg/config/default_linux.go | 72 + .../internal/pkg/config/default_nonlinux.go | 25 + .../osrg/gobgp/internal/pkg/config/serve.go | 159 + .../osrg/gobgp/internal/pkg/config/util.go | 264 + .../osrg/gobgp/internal/pkg/table/adj.go | 186 + .../gobgp/internal/pkg/table/destination.go | 1041 + .../osrg/gobgp/internal/pkg/table/message.go | 502 + .../osrg/gobgp/internal/pkg/table/path.go | 1179 + .../osrg/gobgp/internal/pkg/table/policy.go | 3895 + .../osrg/gobgp/internal/pkg/table/roa.go | 60 + .../osrg/gobgp/internal/pkg/table/table.go | 451 + .../gobgp/internal/pkg/table/table_manager.go | 370 + .../osrg/gobgp/internal/pkg/table/vrf.go | 53 + .../gobgp/internal/pkg/zebra/afi_string.go | 17 + .../internal/pkg/zebra/api_type_string.go | 16 + .../internal/pkg/zebra/link_type_string.go | 16 + .../internal/pkg/zebra/nexthop_flag_string.go | 41 + .../internal/pkg/zebra/nexthop_type_string.go | 17 + .../internal/pkg/zebra/ptm_enable_string.go | 16 + .../internal/pkg/zebra/ptm_status_string.go | 16 + .../internal/pkg/zebra/route_type_string.go | 16 + .../gobgp/internal/pkg/zebra/safi_string.go | 17 + .../osrg/gobgp/internal/pkg/zebra/zapi.go | 2534 + .../osrg/gobgp/internal/pkg/zebra/zapi_bsd.go | 58 + .../gobgp/internal/pkg/zebra/zapi_darwin.go | 59 + .../gobgp/internal/pkg/zebra/zapi_linux.go | 83 + .../gobgp/internal/pkg/zebra/zapi_windows.go | 38 + .../osrg/gobgp/pkg/packet/bgp/bgp.go | 9677 ++ .../pkg/packet/bgp/bgpattrtype_string.go | 28 + .../osrg/gobgp/pkg/packet/bgp/constant.go | 327 + .../gobgp/pkg/packet/bgp/esitype_string.go | 16 + .../gobgp/pkg/packet/bgp/fsmstate_string.go | 16 + .../osrg/gobgp/pkg/packet/bgp/helper.go | 126 + .../osrg/gobgp/pkg/packet/bgp/validate.go | 337 + .../osrg/gobgp/pkg/packet/bmp/bmp.go | 1072 + .../osrg/gobgp/pkg/packet/mrt/mrt.go | 1006 + .../osrg/gobgp/pkg/packet/rtr/rtr.go | 392 + .../github.com/osrg/gobgp/pkg/server/bmp.go | 358 + .../osrg/gobgp/pkg/server/collector.go | 222 + .../github.com/osrg/gobgp/pkg/server/fsm.go | 1935 + .../osrg/gobgp/pkg/server/grpc_server.go | 2401 + .../github.com/osrg/gobgp/pkg/server/mrt.go | 409 + .../github.com/osrg/gobgp/pkg/server/peer.go | 596 + .../github.com/osrg/gobgp/pkg/server/rpki.go | 712 + .../osrg/gobgp/pkg/server/server.go | 3731 + .../osrg/gobgp/pkg/server/sockopt.go | 90 + .../osrg/gobgp/pkg/server/sockopt_bsd.go | 69 + .../osrg/gobgp/pkg/server/sockopt_darwin.go | 53 + .../osrg/gobgp/pkg/server/sockopt_linux.go | 289 + .../osrg/gobgp/pkg/server/sockopt_openbsd.go | 454 + .../osrg/gobgp/pkg/server/sockopt_stub.go | 38 + .../github.com/osrg/gobgp/pkg/server/util.go | 116 + .../osrg/gobgp/pkg/server/zclient.go | 456 + .../github.com/pelletier/go-toml/.gitignore | 2 + .../github.com/pelletier/go-toml/.travis.yml | 23 + vendor/github.com/pelletier/go-toml/LICENSE | 21 + vendor/github.com/pelletier/go-toml/README.md | 131 + .../pelletier/go-toml/benchmark.json | 164 + .../github.com/pelletier/go-toml/benchmark.sh | 32 + .../pelletier/go-toml/benchmark.toml | 244 + .../pelletier/go-toml/benchmark.yml | 121 + vendor/github.com/pelletier/go-toml/doc.go | 23 + .../pelletier/go-toml/example-crlf.toml | 29 + .../github.com/pelletier/go-toml/example.toml | 29 + vendor/github.com/pelletier/go-toml/fuzz.go | 31 + vendor/github.com/pelletier/go-toml/fuzz.sh | 15 + .../pelletier/go-toml/keysparsing.go | 85 + vendor/github.com/pelletier/go-toml/lexer.go | 750 + .../github.com/pelletier/go-toml/marshal.go | 609 + .../pelletier/go-toml/marshal_test.toml | 38 + vendor/github.com/pelletier/go-toml/parser.go | 430 + .../github.com/pelletier/go-toml/position.go | 29 + vendor/github.com/pelletier/go-toml/test.sh | 88 + vendor/github.com/pelletier/go-toml/token.go | 144 + vendor/github.com/pelletier/go-toml/toml.go | 367 + .../pelletier/go-toml/tomltree_create.go | 142 + .../pelletier/go-toml/tomltree_write.go | 333 + vendor/github.com/satori/go.uuid/.travis.yml | 21 + vendor/github.com/satori/go.uuid/LICENSE | 20 + vendor/github.com/satori/go.uuid/README.md | 75 + vendor/github.com/satori/go.uuid/codec.go | 206 + vendor/github.com/satori/go.uuid/generator.go | 265 + vendor/github.com/satori/go.uuid/sql.go | 78 + vendor/github.com/satori/go.uuid/uuid.go | 161 + vendor/github.com/sirupsen/logrus/.gitignore | 1 + vendor/github.com/sirupsen/logrus/.travis.yml | 51 + .../github.com/sirupsen/logrus/CHANGELOG.md | 158 + vendor/github.com/sirupsen/logrus/LICENSE | 21 + vendor/github.com/sirupsen/logrus/README.md | 461 + vendor/github.com/sirupsen/logrus/alt_exit.go | 64 + .../github.com/sirupsen/logrus/appveyor.yml | 14 + vendor/github.com/sirupsen/logrus/doc.go | 26 + vendor/github.com/sirupsen/logrus/entry.go | 312 + vendor/github.com/sirupsen/logrus/exported.go | 198 + .../github.com/sirupsen/logrus/formatter.go | 64 + vendor/github.com/sirupsen/logrus/go.mod | 10 + vendor/github.com/sirupsen/logrus/go.sum | 12 + vendor/github.com/sirupsen/logrus/hooks.go | 34 + .../sirupsen/logrus/json_formatter.go | 100 + vendor/github.com/sirupsen/logrus/logger.go | 367 + vendor/github.com/sirupsen/logrus/logrus.go | 150 + .../logrus/terminal_check_appengine.go | 11 + .../sirupsen/logrus/terminal_check_js.go | 11 + .../logrus/terminal_check_notappengine.go | 19 + .../sirupsen/logrus/terminal_check_windows.go | 20 + .../sirupsen/logrus/terminal_notwindows.go | 8 + .../sirupsen/logrus/terminal_windows.go | 18 + .../sirupsen/logrus/text_formatter.go | 254 + vendor/github.com/sirupsen/logrus/writer.go | 62 + vendor/github.com/spf13/afero/.travis.yml | 21 + vendor/github.com/spf13/afero/LICENSE.txt | 174 + vendor/github.com/spf13/afero/README.md | 452 + vendor/github.com/spf13/afero/afero.go | 108 + vendor/github.com/spf13/afero/appveyor.yml | 15 + vendor/github.com/spf13/afero/basepath.go | 180 + .../github.com/spf13/afero/cacheOnReadFs.go | 290 + vendor/github.com/spf13/afero/const_bsds.go | 22 + .../github.com/spf13/afero/const_win_unix.go | 25 + .../github.com/spf13/afero/copyOnWriteFs.go | 292 + vendor/github.com/spf13/afero/go.mod | 1 + vendor/github.com/spf13/afero/httpFs.go | 110 + vendor/github.com/spf13/afero/ioutil.go | 230 + vendor/github.com/spf13/afero/lstater.go | 27 + vendor/github.com/spf13/afero/match.go | 110 + vendor/github.com/spf13/afero/mem/dir.go | 37 + vendor/github.com/spf13/afero/mem/dirmap.go | 43 + vendor/github.com/spf13/afero/mem/file.go | 317 + vendor/github.com/spf13/afero/memmap.go | 365 + vendor/github.com/spf13/afero/os.go | 101 + vendor/github.com/spf13/afero/path.go | 106 + vendor/github.com/spf13/afero/readonlyfs.go | 80 + vendor/github.com/spf13/afero/regexpfs.go | 214 + vendor/github.com/spf13/afero/unionFile.go | 305 + vendor/github.com/spf13/afero/util.go | 330 + vendor/github.com/spf13/cast/.gitignore | 25 + vendor/github.com/spf13/cast/.travis.yml | 14 + vendor/github.com/spf13/cast/LICENSE | 21 + vendor/github.com/spf13/cast/Makefile | 38 + vendor/github.com/spf13/cast/README.md | 75 + vendor/github.com/spf13/cast/cast.go | 159 + vendor/github.com/spf13/cast/caste.go | 1166 + .../spf13/jwalterweatherman/.gitignore | 22 + .../spf13/jwalterweatherman/LICENSE | 21 + .../spf13/jwalterweatherman/README.md | 148 + .../jwalterweatherman/default_notepad.go | 113 + .../github.com/spf13/jwalterweatherman/go.mod | 1 + .../spf13/jwalterweatherman/log_counter.go | 55 + .../spf13/jwalterweatherman/notepad.go | 194 + vendor/github.com/spf13/pflag/.gitignore | 2 + vendor/github.com/spf13/pflag/.travis.yml | 21 + vendor/github.com/spf13/pflag/LICENSE | 28 + vendor/github.com/spf13/pflag/README.md | 296 + vendor/github.com/spf13/pflag/bool.go | 94 + vendor/github.com/spf13/pflag/bool_slice.go | 147 + vendor/github.com/spf13/pflag/bytes.go | 209 + vendor/github.com/spf13/pflag/count.go | 96 + vendor/github.com/spf13/pflag/duration.go | 86 + .../github.com/spf13/pflag/duration_slice.go | 128 + vendor/github.com/spf13/pflag/flag.go | 1227 + vendor/github.com/spf13/pflag/float32.go | 88 + vendor/github.com/spf13/pflag/float64.go | 84 + vendor/github.com/spf13/pflag/golangflag.go | 105 + vendor/github.com/spf13/pflag/int.go | 84 + vendor/github.com/spf13/pflag/int16.go | 88 + vendor/github.com/spf13/pflag/int32.go | 88 + vendor/github.com/spf13/pflag/int64.go | 84 + vendor/github.com/spf13/pflag/int8.go | 88 + vendor/github.com/spf13/pflag/int_slice.go | 128 + vendor/github.com/spf13/pflag/ip.go | 94 + vendor/github.com/spf13/pflag/ip_slice.go | 148 + vendor/github.com/spf13/pflag/ipmask.go | 122 + vendor/github.com/spf13/pflag/ipnet.go | 98 + vendor/github.com/spf13/pflag/string.go | 80 + vendor/github.com/spf13/pflag/string_array.go | 103 + vendor/github.com/spf13/pflag/string_slice.go | 149 + .../github.com/spf13/pflag/string_to_int.go | 149 + .../spf13/pflag/string_to_string.go | 160 + vendor/github.com/spf13/pflag/uint.go | 88 + vendor/github.com/spf13/pflag/uint16.go | 88 + vendor/github.com/spf13/pflag/uint32.go | 88 + vendor/github.com/spf13/pflag/uint64.go | 88 + vendor/github.com/spf13/pflag/uint8.go | 88 + vendor/github.com/spf13/pflag/uint_slice.go | 126 + vendor/github.com/spf13/viper/.gitignore | 29 + vendor/github.com/spf13/viper/.travis.yml | 27 + vendor/github.com/spf13/viper/LICENSE | 21 + vendor/github.com/spf13/viper/README.md | 686 + vendor/github.com/spf13/viper/flags.go | 57 + vendor/github.com/spf13/viper/go.mod | 16 + vendor/github.com/spf13/viper/go.sum | 26 + vendor/github.com/spf13/viper/util.go | 221 + vendor/github.com/spf13/viper/viper.go | 1830 + .../vishvananda/netlink/.travis.yml | 14 + .../vishvananda/netlink/CHANGELOG.md | 5 + vendor/github.com/vishvananda/netlink/LICENSE | 192 + .../github.com/vishvananda/netlink/Makefile | 30 + .../github.com/vishvananda/netlink/README.md | 92 + vendor/github.com/vishvananda/netlink/addr.go | 56 + .../vishvananda/netlink/addr_linux.go | 374 + .../vishvananda/netlink/bpf_linux.go | 53 + .../vishvananda/netlink/bridge_linux.go | 115 + .../github.com/vishvananda/netlink/class.go | 211 + .../vishvananda/netlink/class_linux.go | 384 + .../vishvananda/netlink/conntrack_linux.go | 395 + .../netlink/conntrack_unspecified.go | 53 + .../github.com/vishvananda/netlink/filter.go | 290 + .../vishvananda/netlink/filter_linux.go | 652 + vendor/github.com/vishvananda/netlink/fou.go | 21 + .../vishvananda/netlink/fou_linux.go | 215 + .../vishvananda/netlink/fou_unspecified.go | 15 + .../vishvananda/netlink/genetlink_linux.go | 168 + .../netlink/genetlink_unspecified.go | 25 + .../vishvananda/netlink/gtp_linux.go | 239 + .../vishvananda/netlink/handle_linux.go | 144 + .../vishvananda/netlink/handle_unspecified.go | 258 + .../vishvananda/netlink/ioctl_linux.go | 98 + vendor/github.com/vishvananda/netlink/link.go | 863 + .../vishvananda/netlink/link_linux.go | 2542 + .../vishvananda/netlink/link_tuntap_linux.go | 14 + .../github.com/vishvananda/netlink/neigh.go | 31 + .../vishvananda/netlink/neigh_linux.go | 382 + .../github.com/vishvananda/netlink/netlink.go | 39 + .../vishvananda/netlink/netlink_linux.go | 11 + .../netlink/netlink_unspecified.go | 225 + .../vishvananda/netlink/netns_linux.go | 141 + .../vishvananda/netlink/netns_unspecified.go | 19 + .../vishvananda/netlink/nl/addr_linux.go | 77 + .../vishvananda/netlink/nl/bridge_linux.go | 74 + .../vishvananda/netlink/nl/conntrack_linux.go | 206 + .../vishvananda/netlink/nl/genetlink_linux.go | 89 + .../vishvananda/netlink/nl/link_linux.go | 575 + .../vishvananda/netlink/nl/mpls_linux.go | 36 + .../vishvananda/netlink/nl/nl_linux.go | 742 + .../vishvananda/netlink/nl/nl_unspecified.go | 11 + .../vishvananda/netlink/nl/rdma_link_linux.go | 30 + .../vishvananda/netlink/nl/route_linux.go | 107 + .../vishvananda/netlink/nl/seg6_linux.go | 154 + .../vishvananda/netlink/nl/seg6local_linux.go | 76 + .../vishvananda/netlink/nl/syscall.go | 79 + .../vishvananda/netlink/nl/tc_linux.go | 778 + .../vishvananda/netlink/nl/xfrm_linux.go | 296 + .../netlink/nl/xfrm_monitor_linux.go | 32 + .../netlink/nl/xfrm_policy_linux.go | 119 + .../netlink/nl/xfrm_state_linux.go | 334 + .../github.com/vishvananda/netlink/order.go | 32 + .../vishvananda/netlink/protinfo.go | 62 + .../vishvananda/netlink/protinfo_linux.go | 75 + .../github.com/vishvananda/netlink/qdisc.go | 340 + .../vishvananda/netlink/qdisc_linux.go | 668 + .../vishvananda/netlink/rdma_link_linux.go | 112 + .../github.com/vishvananda/netlink/route.go | 180 + .../vishvananda/netlink/route_linux.go | 1075 + .../vishvananda/netlink/route_unspecified.go | 11 + vendor/github.com/vishvananda/netlink/rule.go | 42 + .../vishvananda/netlink/rule_linux.go | 234 + .../github.com/vishvananda/netlink/socket.go | 27 + .../vishvananda/netlink/socket_linux.go | 159 + vendor/github.com/vishvananda/netlink/xfrm.go | 75 + .../vishvananda/netlink/xfrm_monitor_linux.go | 97 + .../vishvananda/netlink/xfrm_policy.go | 95 + .../vishvananda/netlink/xfrm_policy_linux.go | 260 + .../vishvananda/netlink/xfrm_state.go | 129 + .../vishvananda/netlink/xfrm_state_linux.go | 457 + vendor/github.com/vishvananda/netns/LICENSE | 192 + vendor/github.com/vishvananda/netns/README.md | 51 + vendor/github.com/vishvananda/netns/netns.go | 80 + .../vishvananda/netns/netns_linux.go | 230 + .../vishvananda/netns/netns_unspecified.go | 43 + vendor/golang.org/x/crypto/AUTHORS | 3 + vendor/golang.org/x/crypto/CONTRIBUTORS | 3 + vendor/golang.org/x/crypto/LICENSE | 27 + vendor/golang.org/x/crypto/PATENTS | 22 + .../x/crypto/ssh/terminal/terminal.go | 951 + .../golang.org/x/crypto/ssh/terminal/util.go | 114 + .../x/crypto/ssh/terminal/util_bsd.go | 12 + .../x/crypto/ssh/terminal/util_linux.go | 10 + .../x/crypto/ssh/terminal/util_plan9.go | 58 + .../x/crypto/ssh/terminal/util_solaris.go | 124 + .../x/crypto/ssh/terminal/util_windows.go | 103 + vendor/golang.org/x/net/AUTHORS | 3 + vendor/golang.org/x/net/CONTRIBUTORS | 3 + vendor/golang.org/x/net/LICENSE | 27 + vendor/golang.org/x/net/PATENTS | 22 + vendor/golang.org/x/net/context/context.go | 56 + vendor/golang.org/x/net/context/go17.go | 72 + vendor/golang.org/x/net/context/go19.go | 20 + vendor/golang.org/x/net/context/pre_go17.go | 300 + vendor/golang.org/x/net/context/pre_go19.go | 109 + vendor/golang.org/x/net/http/httpguts/guts.go | 50 + .../golang.org/x/net/http/httpguts/httplex.go | 346 + vendor/golang.org/x/net/http2/.gitignore | 2 + vendor/golang.org/x/net/http2/Dockerfile | 51 + vendor/golang.org/x/net/http2/Makefile | 3 + vendor/golang.org/x/net/http2/README | 20 + vendor/golang.org/x/net/http2/ciphers.go | 641 + .../x/net/http2/client_conn_pool.go | 282 + .../x/net/http2/configure_transport.go | 82 + vendor/golang.org/x/net/http2/databuffer.go | 146 + vendor/golang.org/x/net/http2/errors.go | 133 + vendor/golang.org/x/net/http2/flow.go | 50 + vendor/golang.org/x/net/http2/frame.go | 1614 + vendor/golang.org/x/net/http2/go111.go | 26 + vendor/golang.org/x/net/http2/go16.go | 16 + vendor/golang.org/x/net/http2/go17.go | 121 + vendor/golang.org/x/net/http2/go17_not18.go | 36 + vendor/golang.org/x/net/http2/go18.go | 56 + vendor/golang.org/x/net/http2/go19.go | 16 + vendor/golang.org/x/net/http2/gotrack.go | 170 + vendor/golang.org/x/net/http2/headermap.go | 88 + vendor/golang.org/x/net/http2/hpack/encode.go | 240 + vendor/golang.org/x/net/http2/hpack/hpack.go | 496 + .../golang.org/x/net/http2/hpack/huffman.go | 222 + vendor/golang.org/x/net/http2/hpack/tables.go | 479 + vendor/golang.org/x/net/http2/http2.go | 384 + vendor/golang.org/x/net/http2/not_go111.go | 17 + vendor/golang.org/x/net/http2/not_go16.go | 21 + vendor/golang.org/x/net/http2/not_go17.go | 95 + vendor/golang.org/x/net/http2/not_go18.go | 29 + vendor/golang.org/x/net/http2/not_go19.go | 16 + vendor/golang.org/x/net/http2/pipe.go | 163 + vendor/golang.org/x/net/http2/server.go | 2890 + vendor/golang.org/x/net/http2/transport.go | 2453 + vendor/golang.org/x/net/http2/write.go | 365 + vendor/golang.org/x/net/http2/writesched.go | 242 + .../x/net/http2/writesched_priority.go | 452 + .../x/net/http2/writesched_random.go | 72 + vendor/golang.org/x/net/idna/idna.go | 732 + vendor/golang.org/x/net/idna/punycode.go | 203 + vendor/golang.org/x/net/idna/tables.go | 4557 + vendor/golang.org/x/net/idna/trie.go | 72 + vendor/golang.org/x/net/idna/trieval.go | 119 + .../x/net/internal/timeseries/timeseries.go | 525 + vendor/golang.org/x/net/trace/events.go | 532 + vendor/golang.org/x/net/trace/histogram.go | 365 + vendor/golang.org/x/net/trace/trace.go | 1111 + vendor/golang.org/x/net/trace/trace_go16.go | 21 + vendor/golang.org/x/net/trace/trace_go17.go | 21 + vendor/golang.org/x/sys/AUTHORS | 3 + vendor/golang.org/x/sys/CONTRIBUTORS | 3 + vendor/golang.org/x/sys/LICENSE | 27 + vendor/golang.org/x/sys/PATENTS | 22 + vendor/golang.org/x/sys/unix/.gitignore | 2 + vendor/golang.org/x/sys/unix/README.md | 173 + .../golang.org/x/sys/unix/affinity_linux.go | 124 + vendor/golang.org/x/sys/unix/aliases.go | 14 + vendor/golang.org/x/sys/unix/asm_aix_ppc64.s | 17 + vendor/golang.org/x/sys/unix/asm_darwin_386.s | 29 + .../golang.org/x/sys/unix/asm_darwin_amd64.s | 29 + vendor/golang.org/x/sys/unix/asm_darwin_arm.s | 30 + .../golang.org/x/sys/unix/asm_darwin_arm64.s | 30 + .../x/sys/unix/asm_dragonfly_amd64.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_386.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_amd64.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_arm.s | 29 + vendor/golang.org/x/sys/unix/asm_linux_386.s | 65 + .../golang.org/x/sys/unix/asm_linux_amd64.s | 57 + vendor/golang.org/x/sys/unix/asm_linux_arm.s | 56 + .../golang.org/x/sys/unix/asm_linux_arm64.s | 52 + .../golang.org/x/sys/unix/asm_linux_mips64x.s | 56 + .../golang.org/x/sys/unix/asm_linux_mipsx.s | 54 + .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 56 + .../golang.org/x/sys/unix/asm_linux_s390x.s | 56 + vendor/golang.org/x/sys/unix/asm_netbsd_386.s | 29 + .../golang.org/x/sys/unix/asm_netbsd_amd64.s | 29 + vendor/golang.org/x/sys/unix/asm_netbsd_arm.s | 29 + .../golang.org/x/sys/unix/asm_openbsd_386.s | 29 + .../golang.org/x/sys/unix/asm_openbsd_amd64.s | 29 + .../golang.org/x/sys/unix/asm_openbsd_arm.s | 29 + .../golang.org/x/sys/unix/asm_solaris_amd64.s | 17 + .../golang.org/x/sys/unix/bluetooth_linux.go | 35 + vendor/golang.org/x/sys/unix/cap_freebsd.go | 195 + vendor/golang.org/x/sys/unix/constants.go | 13 + vendor/golang.org/x/sys/unix/dev_aix_ppc.go | 27 + vendor/golang.org/x/sys/unix/dev_aix_ppc64.go | 29 + vendor/golang.org/x/sys/unix/dev_darwin.go | 24 + vendor/golang.org/x/sys/unix/dev_dragonfly.go | 30 + vendor/golang.org/x/sys/unix/dev_freebsd.go | 30 + vendor/golang.org/x/sys/unix/dev_linux.go | 42 + vendor/golang.org/x/sys/unix/dev_netbsd.go | 29 + vendor/golang.org/x/sys/unix/dev_openbsd.go | 29 + vendor/golang.org/x/sys/unix/dirent.go | 17 + vendor/golang.org/x/sys/unix/endian_big.go | 9 + vendor/golang.org/x/sys/unix/endian_little.go | 9 + vendor/golang.org/x/sys/unix/env_unix.go | 31 + .../x/sys/unix/errors_freebsd_386.go | 227 + .../x/sys/unix/errors_freebsd_amd64.go | 227 + .../x/sys/unix/errors_freebsd_arm.go | 226 + vendor/golang.org/x/sys/unix/fcntl.go | 32 + .../x/sys/unix/fcntl_linux_32bit.go | 13 + vendor/golang.org/x/sys/unix/gccgo.go | 62 + vendor/golang.org/x/sys/unix/gccgo_c.c | 39 + .../x/sys/unix/gccgo_linux_amd64.go | 20 + vendor/golang.org/x/sys/unix/ioctl.go | 30 + vendor/golang.org/x/sys/unix/mkall.sh | 204 + vendor/golang.org/x/sys/unix/mkerrors.sh | 654 + vendor/golang.org/x/sys/unix/mkpost.go | 98 + vendor/golang.org/x/sys/unix/mksyscall.pl | 341 + .../x/sys/unix/mksyscall_aix_ppc.pl | 384 + .../x/sys/unix/mksyscall_aix_ppc64.pl | 579 + .../x/sys/unix/mksyscall_solaris.pl | 294 + .../golang.org/x/sys/unix/mksysctl_openbsd.pl | 265 + .../golang.org/x/sys/unix/mksysnum_darwin.pl | 39 + .../x/sys/unix/mksysnum_dragonfly.pl | 50 + .../golang.org/x/sys/unix/mksysnum_freebsd.pl | 50 + .../golang.org/x/sys/unix/mksysnum_netbsd.pl | 58 + .../golang.org/x/sys/unix/mksysnum_openbsd.pl | 50 + .../golang.org/x/sys/unix/openbsd_pledge.go | 95 + vendor/golang.org/x/sys/unix/pagesize_unix.go | 15 + vendor/golang.org/x/sys/unix/race.go | 30 + vendor/golang.org/x/sys/unix/race0.go | 25 + .../golang.org/x/sys/unix/sockcmsg_linux.go | 36 + vendor/golang.org/x/sys/unix/sockcmsg_unix.go | 104 + vendor/golang.org/x/sys/unix/str.go | 26 + vendor/golang.org/x/sys/unix/syscall.go | 54 + vendor/golang.org/x/sys/unix/syscall_aix.go | 547 + .../golang.org/x/sys/unix/syscall_aix_ppc.go | 34 + .../x/sys/unix/syscall_aix_ppc64.go | 34 + vendor/golang.org/x/sys/unix/syscall_bsd.go | 624 + .../golang.org/x/sys/unix/syscall_darwin.go | 700 + .../x/sys/unix/syscall_darwin_386.go | 68 + .../x/sys/unix/syscall_darwin_amd64.go | 68 + .../x/sys/unix/syscall_darwin_arm.go | 66 + .../x/sys/unix/syscall_darwin_arm64.go | 68 + .../x/sys/unix/syscall_dragonfly.go | 523 + .../x/sys/unix/syscall_dragonfly_amd64.go | 52 + .../golang.org/x/sys/unix/syscall_freebsd.go | 535 + .../x/sys/unix/syscall_freebsd_386.go | 52 + .../x/sys/unix/syscall_freebsd_amd64.go | 52 + .../x/sys/unix/syscall_freebsd_arm.go | 52 + vendor/golang.org/x/sys/unix/syscall_linux.go | 1628 + .../x/sys/unix/syscall_linux_386.go | 385 + .../x/sys/unix/syscall_linux_amd64.go | 175 + .../x/sys/unix/syscall_linux_amd64_gc.go | 13 + .../x/sys/unix/syscall_linux_arm.go | 259 + .../x/sys/unix/syscall_linux_arm64.go | 212 + .../golang.org/x/sys/unix/syscall_linux_gc.go | 14 + .../x/sys/unix/syscall_linux_gc_386.go | 16 + .../x/sys/unix/syscall_linux_gccgo_386.go | 30 + .../x/sys/unix/syscall_linux_gccgo_arm.go | 20 + .../x/sys/unix/syscall_linux_mips64x.go | 214 + .../x/sys/unix/syscall_linux_mipsx.go | 233 + .../x/sys/unix/syscall_linux_ppc64x.go | 151 + .../x/sys/unix/syscall_linux_riscv64.go | 212 + .../x/sys/unix/syscall_linux_s390x.go | 337 + .../x/sys/unix/syscall_linux_sparc64.go | 146 + .../golang.org/x/sys/unix/syscall_netbsd.go | 597 + .../x/sys/unix/syscall_netbsd_386.go | 33 + .../x/sys/unix/syscall_netbsd_amd64.go | 33 + .../x/sys/unix/syscall_netbsd_arm.go | 33 + .../golang.org/x/sys/unix/syscall_openbsd.go | 385 + .../x/sys/unix/syscall_openbsd_386.go | 37 + .../x/sys/unix/syscall_openbsd_amd64.go | 37 + .../x/sys/unix/syscall_openbsd_arm.go | 37 + .../golang.org/x/sys/unix/syscall_solaris.go | 730 + .../x/sys/unix/syscall_solaris_amd64.go | 23 + vendor/golang.org/x/sys/unix/syscall_unix.go | 394 + .../golang.org/x/sys/unix/syscall_unix_gc.go | 15 + vendor/golang.org/x/sys/unix/timestruct.go | 82 + vendor/golang.org/x/sys/unix/types_aix.go | 236 + vendor/golang.org/x/sys/unix/types_darwin.go | 277 + .../golang.org/x/sys/unix/types_dragonfly.go | 263 + vendor/golang.org/x/sys/unix/types_freebsd.go | 385 + vendor/golang.org/x/sys/unix/types_netbsd.go | 287 + vendor/golang.org/x/sys/unix/types_openbsd.go | 272 + vendor/golang.org/x/sys/unix/types_solaris.go | 266 + vendor/golang.org/x/sys/unix/xattr_bsd.go | 231 + .../golang.org/x/sys/unix/zerrors_aix_ppc.go | 1372 + .../x/sys/unix/zerrors_aix_ppc64.go | 1373 + .../x/sys/unix/zerrors_darwin_386.go | 1783 + .../x/sys/unix/zerrors_darwin_amd64.go | 1783 + .../x/sys/unix/zerrors_darwin_arm.go | 1783 + .../x/sys/unix/zerrors_darwin_arm64.go | 1783 + .../x/sys/unix/zerrors_dragonfly_amd64.go | 1650 + .../x/sys/unix/zerrors_freebsd_386.go | 1793 + .../x/sys/unix/zerrors_freebsd_amd64.go | 1794 + .../x/sys/unix/zerrors_freebsd_arm.go | 1802 + .../x/sys/unix/zerrors_linux_386.go | 2695 + .../x/sys/unix/zerrors_linux_amd64.go | 2695 + .../x/sys/unix/zerrors_linux_arm.go | 2701 + .../x/sys/unix/zerrors_linux_arm64.go | 2686 + .../x/sys/unix/zerrors_linux_mips.go | 2702 + .../x/sys/unix/zerrors_linux_mips64.go | 2702 + .../x/sys/unix/zerrors_linux_mips64le.go | 2702 + .../x/sys/unix/zerrors_linux_mipsle.go | 2702 + .../x/sys/unix/zerrors_linux_ppc64.go | 2755 + .../x/sys/unix/zerrors_linux_ppc64le.go | 2755 + .../x/sys/unix/zerrors_linux_riscv64.go | 2682 + .../x/sys/unix/zerrors_linux_s390x.go | 2755 + .../x/sys/unix/zerrors_linux_sparc64.go | 2150 + .../x/sys/unix/zerrors_netbsd_386.go | 1772 + .../x/sys/unix/zerrors_netbsd_amd64.go | 1762 + .../x/sys/unix/zerrors_netbsd_arm.go | 1751 + .../x/sys/unix/zerrors_openbsd_386.go | 1654 + .../x/sys/unix/zerrors_openbsd_amd64.go | 1765 + .../x/sys/unix/zerrors_openbsd_arm.go | 1656 + .../x/sys/unix/zerrors_solaris_amd64.go | 1532 + .../golang.org/x/sys/unix/zptrace386_linux.go | 80 + .../golang.org/x/sys/unix/zptracearm_linux.go | 41 + .../x/sys/unix/zptracemips_linux.go | 50 + .../x/sys/unix/zptracemipsle_linux.go | 50 + .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 1450 + .../x/sys/unix/zsyscall_aix_ppc64.go | 1408 + .../x/sys/unix/zsyscall_aix_ppc64_gc.go | 1162 + .../x/sys/unix/zsyscall_aix_ppc64_gccgo.go | 1042 + .../x/sys/unix/zsyscall_darwin_386.go | 1769 + .../x/sys/unix/zsyscall_darwin_amd64.go | 1769 + .../x/sys/unix/zsyscall_darwin_arm.go | 1769 + .../x/sys/unix/zsyscall_darwin_arm64.go | 1769 + .../x/sys/unix/zsyscall_dragonfly_amd64.go | 1508 + .../x/sys/unix/zsyscall_freebsd_386.go | 1935 + .../x/sys/unix/zsyscall_freebsd_amd64.go | 1935 + .../x/sys/unix/zsyscall_freebsd_arm.go | 1935 + .../x/sys/unix/zsyscall_linux_386.go | 2131 + .../x/sys/unix/zsyscall_linux_amd64.go | 2313 + .../x/sys/unix/zsyscall_linux_arm.go | 2233 + .../x/sys/unix/zsyscall_linux_arm64.go | 2140 + .../x/sys/unix/zsyscall_linux_mips.go | 2311 + .../x/sys/unix/zsyscall_linux_mips64.go | 2282 + .../x/sys/unix/zsyscall_linux_mips64le.go | 2282 + .../x/sys/unix/zsyscall_linux_mipsle.go | 2311 + .../x/sys/unix/zsyscall_linux_ppc64.go | 2360 + .../x/sys/unix/zsyscall_linux_ppc64le.go | 2360 + .../x/sys/unix/zsyscall_linux_riscv64.go | 2140 + .../x/sys/unix/zsyscall_linux_s390x.go | 2130 + .../x/sys/unix/zsyscall_linux_sparc64.go | 2172 + .../x/sys/unix/zsyscall_netbsd_386.go | 1653 + .../x/sys/unix/zsyscall_netbsd_amd64.go | 1653 + .../x/sys/unix/zsyscall_netbsd_arm.go | 1653 + .../x/sys/unix/zsyscall_openbsd_386.go | 1508 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 1508 + .../x/sys/unix/zsyscall_openbsd_arm.go | 1508 + .../x/sys/unix/zsyscall_solaris_amd64.go | 1953 + .../x/sys/unix/zsysctl_openbsd_386.go | 270 + .../x/sys/unix/zsysctl_openbsd_amd64.go | 270 + .../x/sys/unix/zsysctl_openbsd_arm.go | 270 + .../x/sys/unix/zsysnum_darwin_386.go | 436 + .../x/sys/unix/zsysnum_darwin_amd64.go | 436 + .../x/sys/unix/zsysnum_darwin_arm.go | 436 + .../x/sys/unix/zsysnum_darwin_arm64.go | 436 + .../x/sys/unix/zsysnum_dragonfly_amd64.go | 315 + .../x/sys/unix/zsysnum_freebsd_386.go | 403 + .../x/sys/unix/zsysnum_freebsd_amd64.go | 403 + .../x/sys/unix/zsysnum_freebsd_arm.go | 403 + .../x/sys/unix/zsysnum_linux_386.go | 392 + .../x/sys/unix/zsysnum_linux_amd64.go | 344 + .../x/sys/unix/zsysnum_linux_arm.go | 363 + .../x/sys/unix/zsysnum_linux_arm64.go | 287 + .../x/sys/unix/zsysnum_linux_mips.go | 377 + .../x/sys/unix/zsysnum_linux_mips64.go | 337 + .../x/sys/unix/zsysnum_linux_mips64le.go | 337 + .../x/sys/unix/zsysnum_linux_mipsle.go | 377 + .../x/sys/unix/zsysnum_linux_ppc64.go | 375 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 375 + .../x/sys/unix/zsysnum_linux_riscv64.go | 286 + .../x/sys/unix/zsysnum_linux_s390x.go | 337 + .../x/sys/unix/zsysnum_linux_sparc64.go | 348 + .../x/sys/unix/zsysnum_netbsd_386.go | 274 + .../x/sys/unix/zsysnum_netbsd_amd64.go | 274 + .../x/sys/unix/zsysnum_netbsd_arm.go | 274 + .../x/sys/unix/zsysnum_openbsd_386.go | 218 + .../x/sys/unix/zsysnum_openbsd_amd64.go | 218 + .../x/sys/unix/zsysnum_openbsd_arm.go | 218 + .../golang.org/x/sys/unix/ztypes_aix_ppc.go | 345 + .../golang.org/x/sys/unix/ztypes_aix_ppc64.go | 354 + .../x/sys/unix/ztypes_darwin_386.go | 489 + .../x/sys/unix/ztypes_darwin_amd64.go | 499 + .../x/sys/unix/ztypes_darwin_arm.go | 490 + .../x/sys/unix/ztypes_darwin_arm64.go | 499 + .../x/sys/unix/ztypes_dragonfly_amd64.go | 469 + .../x/sys/unix/ztypes_freebsd_386.go | 536 + .../x/sys/unix/ztypes_freebsd_amd64.go | 539 + .../x/sys/unix/ztypes_freebsd_arm.go | 539 + .../golang.org/x/sys/unix/ztypes_linux_386.go | 1988 + .../x/sys/unix/ztypes_linux_amd64.go | 2010 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 1978 + .../x/sys/unix/ztypes_linux_arm64.go | 1989 + .../x/sys/unix/ztypes_linux_mips.go | 1983 + .../x/sys/unix/ztypes_linux_mips64.go | 1991 + .../x/sys/unix/ztypes_linux_mips64le.go | 1991 + .../x/sys/unix/ztypes_linux_mipsle.go | 1983 + .../x/sys/unix/ztypes_linux_ppc64.go | 1999 + .../x/sys/unix/ztypes_linux_ppc64le.go | 1999 + .../x/sys/unix/ztypes_linux_riscv64.go | 2016 + .../x/sys/unix/ztypes_linux_s390x.go | 2016 + .../x/sys/unix/ztypes_linux_sparc64.go | 690 + .../x/sys/unix/ztypes_netbsd_386.go | 458 + .../x/sys/unix/ztypes_netbsd_amd64.go | 465 + .../x/sys/unix/ztypes_netbsd_arm.go | 463 + .../x/sys/unix/ztypes_openbsd_386.go | 558 + .../x/sys/unix/ztypes_openbsd_amd64.go | 558 + .../x/sys/unix/ztypes_openbsd_arm.go | 551 + .../x/sys/unix/ztypes_solaris_amd64.go | 442 + vendor/golang.org/x/sys/windows/aliases.go | 13 + .../x/sys/windows/asm_windows_386.s | 13 + .../x/sys/windows/asm_windows_amd64.s | 13 + .../x/sys/windows/asm_windows_arm.s | 11 + .../golang.org/x/sys/windows/dll_windows.go | 378 + .../golang.org/x/sys/windows/env_windows.go | 29 + vendor/golang.org/x/sys/windows/eventlog.go | 20 + .../golang.org/x/sys/windows/exec_windows.go | 97 + .../x/sys/windows/memory_windows.go | 26 + vendor/golang.org/x/sys/windows/mksyscall.go | 7 + vendor/golang.org/x/sys/windows/race.go | 30 + vendor/golang.org/x/sys/windows/race0.go | 25 + .../x/sys/windows/security_windows.go | 478 + vendor/golang.org/x/sys/windows/service.go | 183 + vendor/golang.org/x/sys/windows/str.go | 22 + vendor/golang.org/x/sys/windows/syscall.go | 74 + .../x/sys/windows/syscall_windows.go | 1205 + .../golang.org/x/sys/windows/types_windows.go | 1469 + .../x/sys/windows/types_windows_386.go | 22 + .../x/sys/windows/types_windows_amd64.go | 22 + .../x/sys/windows/types_windows_arm.go | 22 + .../x/sys/windows/zsyscall_windows.go | 2700 + vendor/golang.org/x/text/AUTHORS | 3 + vendor/golang.org/x/text/CONTRIBUTORS | 3 + vendor/golang.org/x/text/LICENSE | 27 + vendor/golang.org/x/text/PATENTS | 22 + .../x/text/collate/build/builder.go | 702 + .../x/text/collate/build/colelem.go | 294 + .../x/text/collate/build/contract.go | 309 + .../golang.org/x/text/collate/build/order.go | 393 + .../golang.org/x/text/collate/build/table.go | 81 + .../golang.org/x/text/collate/build/trie.go | 290 + vendor/golang.org/x/text/collate/collate.go | 403 + vendor/golang.org/x/text/collate/index.go | 32 + .../golang.org/x/text/collate/maketables.go | 553 + vendor/golang.org/x/text/collate/option.go | 239 + vendor/golang.org/x/text/collate/sort.go | 81 + vendor/golang.org/x/text/collate/tables.go | 73789 ++++++++++++++++ .../x/text/internal/colltab/collelem.go | 371 + .../x/text/internal/colltab/colltab.go | 105 + .../x/text/internal/colltab/contract.go | 145 + .../x/text/internal/colltab/iter.go | 178 + .../x/text/internal/colltab/numeric.go | 236 + .../x/text/internal/colltab/table.go | 275 + .../x/text/internal/colltab/trie.go | 159 + .../x/text/internal/colltab/weighter.go | 31 + vendor/golang.org/x/text/internal/gen/code.go | 369 + vendor/golang.org/x/text/internal/gen/gen.go | 333 + vendor/golang.org/x/text/internal/tag/tag.go | 100 + .../x/text/internal/triegen/compact.go | 58 + .../x/text/internal/triegen/print.go | 251 + .../x/text/internal/triegen/triegen.go | 494 + vendor/golang.org/x/text/internal/ucd/ucd.go | 371 + vendor/golang.org/x/text/language/Makefile | 16 + vendor/golang.org/x/text/language/common.go | 16 + vendor/golang.org/x/text/language/coverage.go | 197 + vendor/golang.org/x/text/language/doc.go | 102 + vendor/golang.org/x/text/language/gen.go | 1712 + .../golang.org/x/text/language/gen_common.go | 20 + .../golang.org/x/text/language/gen_index.go | 162 + vendor/golang.org/x/text/language/go1_1.go | 38 + vendor/golang.org/x/text/language/go1_2.go | 11 + vendor/golang.org/x/text/language/index.go | 783 + vendor/golang.org/x/text/language/language.go | 907 + vendor/golang.org/x/text/language/lookup.go | 396 + vendor/golang.org/x/text/language/match.go | 933 + vendor/golang.org/x/text/language/parse.go | 859 + vendor/golang.org/x/text/language/tables.go | 3686 + vendor/golang.org/x/text/language/tags.go | 143 + .../x/text/secure/bidirule/bidirule.go | 336 + .../x/text/secure/bidirule/bidirule10.0.0.go | 11 + .../x/text/secure/bidirule/bidirule9.0.0.go | 14 + .../golang.org/x/text/transform/transform.go | 705 + vendor/golang.org/x/text/unicode/bidi/bidi.go | 198 + .../golang.org/x/text/unicode/bidi/bracket.go | 335 + vendor/golang.org/x/text/unicode/bidi/core.go | 1058 + vendor/golang.org/x/text/unicode/bidi/gen.go | 133 + .../x/text/unicode/bidi/gen_ranges.go | 57 + .../x/text/unicode/bidi/gen_trieval.go | 64 + vendor/golang.org/x/text/unicode/bidi/prop.go | 206 + .../x/text/unicode/bidi/tables10.0.0.go | 1815 + .../x/text/unicode/bidi/tables9.0.0.go | 1781 + .../golang.org/x/text/unicode/bidi/trieval.go | 60 + vendor/golang.org/x/text/unicode/cldr/base.go | 105 + vendor/golang.org/x/text/unicode/cldr/cldr.go | 130 + .../golang.org/x/text/unicode/cldr/collate.go | 359 + .../golang.org/x/text/unicode/cldr/decode.go | 171 + .../golang.org/x/text/unicode/cldr/makexml.go | 400 + .../golang.org/x/text/unicode/cldr/resolve.go | 602 + .../golang.org/x/text/unicode/cldr/slice.go | 144 + vendor/golang.org/x/text/unicode/cldr/xml.go | 1494 + .../x/text/unicode/norm/composition.go | 508 + .../x/text/unicode/norm/forminfo.go | 259 + .../golang.org/x/text/unicode/norm/input.go | 109 + vendor/golang.org/x/text/unicode/norm/iter.go | 457 + .../x/text/unicode/norm/maketables.go | 976 + .../x/text/unicode/norm/normalize.go | 609 + .../x/text/unicode/norm/readwriter.go | 125 + .../x/text/unicode/norm/tables10.0.0.go | 7653 ++ .../x/text/unicode/norm/tables9.0.0.go | 7633 ++ .../x/text/unicode/norm/transform.go | 88 + vendor/golang.org/x/text/unicode/norm/trie.go | 54 + .../golang.org/x/text/unicode/norm/triegen.go | 117 + .../x/text/unicode/rangetable/gen.go | 115 + .../x/text/unicode/rangetable/merge.go | 260 + .../x/text/unicode/rangetable/rangetable.go | 70 + .../x/text/unicode/rangetable/tables10.0.0.go | 6378 ++ .../x/text/unicode/rangetable/tables9.0.0.go | 5737 ++ vendor/google.golang.org/genproto/LICENSE | 202 + .../googleapis/rpc/status/status.pb.go | 159 + vendor/google.golang.org/grpc/.travis.yml | 38 + vendor/google.golang.org/grpc/AUTHORS | 1 + vendor/google.golang.org/grpc/CONTRIBUTING.md | 36 + vendor/google.golang.org/grpc/LICENSE | 202 + vendor/google.golang.org/grpc/Makefile | 60 + vendor/google.golang.org/grpc/README.md | 45 + vendor/google.golang.org/grpc/backoff.go | 38 + vendor/google.golang.org/grpc/balancer.go | 391 + .../grpc/balancer/balancer.go | 274 + .../grpc/balancer/base/balancer.go | 208 + .../grpc/balancer/base/base.go | 52 + .../grpc/balancer/roundrobin/roundrobin.go | 79 + .../grpc/balancer_conn_wrappers.go | 300 + .../grpc/balancer_v1_wrapper.go | 328 + vendor/google.golang.org/grpc/call.go | 74 + vendor/google.golang.org/grpc/clientconn.go | 1277 + vendor/google.golang.org/grpc/codec.go | 50 + vendor/google.golang.org/grpc/codegen.sh | 17 + .../grpc/codes/code_string.go | 62 + vendor/google.golang.org/grpc/codes/codes.go | 197 + .../grpc/connectivity/connectivity.go | 72 + .../grpc/credentials/credentials.go | 293 + .../grpc/credentials/go16.go | 57 + .../grpc/credentials/go17.go | 59 + .../grpc/credentials/go18.go | 46 + .../grpc/credentials/go19.go | 35 + vendor/google.golang.org/grpc/dialoptions.go | 453 + vendor/google.golang.org/grpc/doc.go | 24 + .../grpc/encoding/encoding.go | 118 + .../grpc/encoding/proto/proto.go | 110 + vendor/google.golang.org/grpc/go.mod | 21 + vendor/google.golang.org/grpc/go.sum | 34 + vendor/google.golang.org/grpc/go16.go | 71 + vendor/google.golang.org/grpc/go17.go | 72 + .../google.golang.org/grpc/grpclog/grpclog.go | 126 + .../google.golang.org/grpc/grpclog/logger.go | 85 + .../grpc/grpclog/loggerv2.go | 195 + vendor/google.golang.org/grpc/install_gae.sh | 6 + vendor/google.golang.org/grpc/interceptor.go | 77 + .../grpc/internal/backoff/backoff.go | 78 + .../grpc/internal/channelz/funcs.go | 573 + .../grpc/internal/channelz/types.go | 419 + .../grpc/internal/channelz/types_linux.go | 53 + .../grpc/internal/channelz/types_nonlinux.go | 38 + .../grpc/internal/channelz/util_linux_go19.go | 39 + .../channelz/util_nonlinux_pre_go19.go | 26 + .../grpc/internal/envconfig/envconfig.go | 35 + .../grpc/internal/grpcrand/grpcrand.go | 56 + .../grpc/internal/internal.go | 28 + .../grpc/internal/transport/bdp_estimator.go | 140 + .../grpc/internal/transport/controlbuf.go | 852 + .../grpc/internal/transport/defaults.go | 49 + .../grpc/internal/transport/flowcontrol.go | 218 + .../grpc/internal/transport/go16.go | 52 + .../grpc/internal/transport/go17.go | 53 + .../grpc/internal/transport/handler_server.go | 449 + .../grpc/internal/transport/http2_client.go | 1337 + .../grpc/internal/transport/http2_server.go | 1180 + .../grpc/internal/transport/http_util.go | 613 + .../grpc/internal/transport/log.go | 44 + .../grpc/internal/transport/transport.go | 708 + .../grpc/keepalive/keepalive.go | 65 + .../grpc/metadata/metadata.go | 210 + .../grpc/naming/dns_resolver.go | 290 + vendor/google.golang.org/grpc/naming/go17.go | 34 + vendor/google.golang.org/grpc/naming/go18.go | 28 + .../google.golang.org/grpc/naming/naming.go | 69 + vendor/google.golang.org/grpc/peer/peer.go | 51 + .../google.golang.org/grpc/picker_wrapper.go | 180 + vendor/google.golang.org/grpc/pickfirst.go | 108 + vendor/google.golang.org/grpc/proxy.go | 130 + .../grpc/resolver/dns/dns_resolver.go | 397 + .../grpc/resolver/dns/go17.go | 35 + .../grpc/resolver/dns/go18.go | 29 + .../grpc/resolver/passthrough/passthrough.go | 57 + .../grpc/resolver/resolver.go | 158 + .../grpc/resolver_conn_wrapper.go | 158 + vendor/google.golang.org/grpc/rpc_util.go | 778 + vendor/google.golang.org/grpc/server.go | 1447 + .../google.golang.org/grpc/service_config.go | 358 + .../google.golang.org/grpc/stats/handlers.go | 64 + vendor/google.golang.org/grpc/stats/stats.go | 296 + vendor/google.golang.org/grpc/status/go16.go | 42 + vendor/google.golang.org/grpc/status/go17.go | 44 + .../google.golang.org/grpc/status/status.go | 189 + vendor/google.golang.org/grpc/stream.go | 1023 + vendor/google.golang.org/grpc/tap/tap.go | 51 + vendor/google.golang.org/grpc/trace.go | 113 + vendor/google.golang.org/grpc/version.go | 22 + vendor/google.golang.org/grpc/vet.sh | 122 + vendor/gopkg.in/tomb.v2/LICENSE | 29 + vendor/gopkg.in/tomb.v2/README.md | 4 + vendor/gopkg.in/tomb.v2/context.go | 74 + vendor/gopkg.in/tomb.v2/context16.go | 74 + vendor/gopkg.in/tomb.v2/tomb.go | 237 + vendor/gopkg.in/yaml.v2/.travis.yml | 12 + vendor/gopkg.in/yaml.v2/LICENSE | 201 + vendor/gopkg.in/yaml.v2/LICENSE.libyaml | 31 + vendor/gopkg.in/yaml.v2/NOTICE | 13 + vendor/gopkg.in/yaml.v2/README.md | 133 + vendor/gopkg.in/yaml.v2/apic.go | 739 + vendor/gopkg.in/yaml.v2/decode.go | 775 + vendor/gopkg.in/yaml.v2/emitterc.go | 1685 + vendor/gopkg.in/yaml.v2/encode.go | 362 + vendor/gopkg.in/yaml.v2/go.mod | 5 + vendor/gopkg.in/yaml.v2/parserc.go | 1095 + vendor/gopkg.in/yaml.v2/readerc.go | 412 + vendor/gopkg.in/yaml.v2/resolve.go | 258 + vendor/gopkg.in/yaml.v2/scannerc.go | 2696 + vendor/gopkg.in/yaml.v2/sorter.go | 113 + vendor/gopkg.in/yaml.v2/writerc.go | 26 + vendor/gopkg.in/yaml.v2/yaml.go | 466 + vendor/gopkg.in/yaml.v2/yamlh.go | 738 + vendor/gopkg.in/yaml.v2/yamlprivateh.go | 173 + 991 files changed, 505284 insertions(+), 415 deletions(-) delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml create mode 100644 go.mod create mode 100644 vendor/github.com/armon/go-radix/.gitignore create mode 100644 vendor/github.com/armon/go-radix/.travis.yml create mode 100644 vendor/github.com/armon/go-radix/LICENSE create mode 100644 vendor/github.com/armon/go-radix/README.md create mode 100644 vendor/github.com/armon/go-radix/go.mod create mode 100644 vendor/github.com/armon/go-radix/radix.go create mode 100644 vendor/github.com/dgryski/go-farm/.gitignore create mode 100644 vendor/github.com/dgryski/go-farm/.travis.yml create mode 100644 vendor/github.com/dgryski/go-farm/LICENSE create mode 100644 vendor/github.com/dgryski/go-farm/Makefile create mode 100644 vendor/github.com/dgryski/go-farm/README.md create mode 100644 vendor/github.com/dgryski/go-farm/VERSION create mode 100644 vendor/github.com/dgryski/go-farm/basics.go create mode 100644 vendor/github.com/dgryski/go-farm/farmhashcc.go create mode 100644 vendor/github.com/dgryski/go-farm/farmhashmk.go create mode 100644 vendor/github.com/dgryski/go-farm/farmhashna.go create mode 100644 vendor/github.com/dgryski/go-farm/farmhashuo.go create mode 100644 vendor/github.com/dgryski/go-farm/platform.go create mode 100644 vendor/github.com/eapache/channels/.gitignore create mode 100644 vendor/github.com/eapache/channels/.travis.yml create mode 100644 vendor/github.com/eapache/channels/CHANGELOG.md create mode 100644 vendor/github.com/eapache/channels/LICENSE create mode 100644 vendor/github.com/eapache/channels/README.md create mode 100644 vendor/github.com/eapache/channels/batching_channel.go create mode 100644 vendor/github.com/eapache/channels/black_hole.go create mode 100644 vendor/github.com/eapache/channels/channels.go create mode 100644 vendor/github.com/eapache/channels/infinite_channel.go create mode 100644 vendor/github.com/eapache/channels/native_channel.go create mode 100644 vendor/github.com/eapache/channels/overflowing_channel.go create mode 100644 vendor/github.com/eapache/channels/resizable_channel.go create mode 100644 vendor/github.com/eapache/channels/ring_channel.go create mode 100644 vendor/github.com/eapache/channels/shared_buffer.go create mode 100644 vendor/github.com/eapache/queue/.gitignore create mode 100644 vendor/github.com/eapache/queue/.travis.yml create mode 100644 vendor/github.com/eapache/queue/LICENSE create mode 100644 vendor/github.com/eapache/queue/README.md create mode 100644 vendor/github.com/eapache/queue/queue.go create mode 100644 vendor/github.com/fsnotify/fsnotify/.editorconfig create mode 100644 vendor/github.com/fsnotify/fsnotify/.gitignore create mode 100644 vendor/github.com/fsnotify/fsnotify/.travis.yml create mode 100644 vendor/github.com/fsnotify/fsnotify/AUTHORS create mode 100644 vendor/github.com/fsnotify/fsnotify/CHANGELOG.md create mode 100644 vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md create mode 100644 vendor/github.com/fsnotify/fsnotify/LICENSE create mode 100644 vendor/github.com/fsnotify/fsnotify/README.md create mode 100644 vendor/github.com/fsnotify/fsnotify/fen.go create mode 100644 vendor/github.com/fsnotify/fsnotify/fsnotify.go create mode 100644 vendor/github.com/fsnotify/fsnotify/inotify.go create mode 100644 vendor/github.com/fsnotify/fsnotify/inotify_poller.go create mode 100644 vendor/github.com/fsnotify/fsnotify/kqueue.go create mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go create mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go create mode 100644 vendor/github.com/fsnotify/fsnotify/windows.go create mode 100644 vendor/github.com/golang/glog/LICENSE create mode 100644 vendor/github.com/golang/glog/README create mode 100644 vendor/github.com/golang/glog/glog.go create mode 100644 vendor/github.com/golang/glog/glog_file.go create mode 100644 vendor/github.com/golang/protobuf/AUTHORS create mode 100644 vendor/github.com/golang/protobuf/CONTRIBUTORS create mode 100644 vendor/github.com/golang/protobuf/LICENSE create mode 100644 vendor/github.com/golang/protobuf/proto/clone.go create mode 100644 vendor/github.com/golang/protobuf/proto/decode.go create mode 100644 vendor/github.com/golang/protobuf/proto/deprecated.go create mode 100644 vendor/github.com/golang/protobuf/proto/discard.go create mode 100644 vendor/github.com/golang/protobuf/proto/encode.go create mode 100644 vendor/github.com/golang/protobuf/proto/equal.go create mode 100644 vendor/github.com/golang/protobuf/proto/extensions.go create mode 100644 vendor/github.com/golang/protobuf/proto/lib.go create mode 100644 vendor/github.com/golang/protobuf/proto/message_set.go create mode 100644 vendor/github.com/golang/protobuf/proto/pointer_reflect.go create mode 100644 vendor/github.com/golang/protobuf/proto/pointer_unsafe.go create mode 100644 vendor/github.com/golang/protobuf/proto/properties.go create mode 100644 vendor/github.com/golang/protobuf/proto/table_marshal.go create mode 100644 vendor/github.com/golang/protobuf/proto/table_merge.go create mode 100644 vendor/github.com/golang/protobuf/proto/table_unmarshal.go create mode 100644 vendor/github.com/golang/protobuf/proto/text.go create mode 100644 vendor/github.com/golang/protobuf/proto/text_parser.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/any.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/any/any.pb.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/any/any.proto create mode 100644 vendor/github.com/golang/protobuf/ptypes/doc.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/duration.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/duration/duration.proto create mode 100644 vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/empty/empty.proto create mode 100644 vendor/github.com/golang/protobuf/ptypes/timestamp.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto create mode 100644 vendor/github.com/hashicorp/hcl/.gitignore create mode 100644 vendor/github.com/hashicorp/hcl/.travis.yml create mode 100644 vendor/github.com/hashicorp/hcl/LICENSE create mode 100644 vendor/github.com/hashicorp/hcl/Makefile create mode 100644 vendor/github.com/hashicorp/hcl/README.md create mode 100644 vendor/github.com/hashicorp/hcl/appveyor.yml create mode 100644 vendor/github.com/hashicorp/hcl/decoder.go create mode 100644 vendor/github.com/hashicorp/hcl/go.mod create mode 100644 vendor/github.com/hashicorp/hcl/go.sum create mode 100644 vendor/github.com/hashicorp/hcl/hcl.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/ast.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/walk.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/error.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/parser.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/printer.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/position.go create mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/token.go create mode 100644 vendor/github.com/hashicorp/hcl/json/parser/flatten.go create mode 100644 vendor/github.com/hashicorp/hcl/json/parser/parser.go create mode 100644 vendor/github.com/hashicorp/hcl/json/scanner/scanner.go create mode 100644 vendor/github.com/hashicorp/hcl/json/token/position.go create mode 100644 vendor/github.com/hashicorp/hcl/json/token/token.go create mode 100644 vendor/github.com/hashicorp/hcl/lex.go create mode 100644 vendor/github.com/hashicorp/hcl/parse.go create mode 100644 vendor/github.com/influxdata/influxdb/LICENSE create mode 100644 vendor/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md create mode 100644 vendor/github.com/influxdata/influxdb/client/v2/client.go create mode 100644 vendor/github.com/influxdata/influxdb/client/v2/udp.go create mode 100644 vendor/github.com/influxdata/influxdb/models/consistency.go create mode 100644 vendor/github.com/influxdata/influxdb/models/inline_fnv.go create mode 100644 vendor/github.com/influxdata/influxdb/models/inline_strconv_parse.go create mode 100644 vendor/github.com/influxdata/influxdb/models/points.go create mode 100644 vendor/github.com/influxdata/influxdb/models/rows.go create mode 100644 vendor/github.com/influxdata/influxdb/models/statistic.go create mode 100644 vendor/github.com/influxdata/influxdb/models/time.go create mode 100644 vendor/github.com/influxdata/influxdb/models/uint_support.go create mode 100644 vendor/github.com/influxdata/influxdb/pkg/escape/bytes.go create mode 100644 vendor/github.com/influxdata/influxdb/pkg/escape/strings.go create mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE create mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/README.md create mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod create mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go create mode 100644 vendor/github.com/magiconair/properties/.gitignore create mode 100644 vendor/github.com/magiconair/properties/.travis.yml create mode 100644 vendor/github.com/magiconair/properties/CHANGELOG.md create mode 100644 vendor/github.com/magiconair/properties/LICENSE create mode 100644 vendor/github.com/magiconair/properties/README.md create mode 100644 vendor/github.com/magiconair/properties/decode.go create mode 100644 vendor/github.com/magiconair/properties/doc.go create mode 100644 vendor/github.com/magiconair/properties/integrate.go create mode 100644 vendor/github.com/magiconair/properties/lex.go create mode 100644 vendor/github.com/magiconair/properties/load.go create mode 100644 vendor/github.com/magiconair/properties/parser.go create mode 100644 vendor/github.com/magiconair/properties/properties.go create mode 100644 vendor/github.com/magiconair/properties/rangecheck.go create mode 100644 vendor/github.com/mitchellh/mapstructure/.travis.yml create mode 100644 vendor/github.com/mitchellh/mapstructure/CHANGELOG.md create mode 100644 vendor/github.com/mitchellh/mapstructure/LICENSE create mode 100644 vendor/github.com/mitchellh/mapstructure/README.md create mode 100644 vendor/github.com/mitchellh/mapstructure/decode_hooks.go create mode 100644 vendor/github.com/mitchellh/mapstructure/error.go create mode 100644 vendor/github.com/mitchellh/mapstructure/go.mod create mode 100644 vendor/github.com/mitchellh/mapstructure/mapstructure.go create mode 100644 vendor/github.com/osrg/gobgp/LICENSE create mode 100644 vendor/github.com/osrg/gobgp/api/attribute.pb.go create mode 100644 vendor/github.com/osrg/gobgp/api/attribute.proto create mode 100644 vendor/github.com/osrg/gobgp/api/capability.pb.go create mode 100644 vendor/github.com/osrg/gobgp/api/capability.proto create mode 100644 vendor/github.com/osrg/gobgp/api/gobgp.pb.go create mode 100644 vendor/github.com/osrg/gobgp/api/gobgp.proto create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/apiutil/attribute.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/apiutil/capability.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/apiutil/util.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/bgp_configs.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/default.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/default_linux.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/default_nonlinux.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/serve.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/config/util.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/adj.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/destination.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/message.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/path.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/policy.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/roa.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/table.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/table_manager.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/table/vrf.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/afi_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/api_type_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/link_type_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_flag_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_type_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_enable_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_status_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/route_type_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/safi_string.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_bsd.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_darwin.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_linux.go create mode 100644 vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_windows.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/bgp.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/bgpattrtype_string.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/constant.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/esitype_string.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/fsmstate_string.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/helper.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bgp/validate.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/bmp/bmp.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/mrt/mrt.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/packet/rtr/rtr.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/bmp.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/collector.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/fsm.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/grpc_server.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/mrt.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/peer.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/rpki.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/server.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt_bsd.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt_darwin.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt_linux.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt_openbsd.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/sockopt_stub.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/util.go create mode 100644 vendor/github.com/osrg/gobgp/pkg/server/zclient.go create mode 100644 vendor/github.com/pelletier/go-toml/.gitignore create mode 100644 vendor/github.com/pelletier/go-toml/.travis.yml create mode 100644 vendor/github.com/pelletier/go-toml/LICENSE create mode 100644 vendor/github.com/pelletier/go-toml/README.md create mode 100644 vendor/github.com/pelletier/go-toml/benchmark.json create mode 100755 vendor/github.com/pelletier/go-toml/benchmark.sh create mode 100644 vendor/github.com/pelletier/go-toml/benchmark.toml create mode 100644 vendor/github.com/pelletier/go-toml/benchmark.yml create mode 100644 vendor/github.com/pelletier/go-toml/doc.go create mode 100644 vendor/github.com/pelletier/go-toml/example-crlf.toml create mode 100644 vendor/github.com/pelletier/go-toml/example.toml create mode 100644 vendor/github.com/pelletier/go-toml/fuzz.go create mode 100755 vendor/github.com/pelletier/go-toml/fuzz.sh create mode 100644 vendor/github.com/pelletier/go-toml/keysparsing.go create mode 100644 vendor/github.com/pelletier/go-toml/lexer.go create mode 100644 vendor/github.com/pelletier/go-toml/marshal.go create mode 100644 vendor/github.com/pelletier/go-toml/marshal_test.toml create mode 100644 vendor/github.com/pelletier/go-toml/parser.go create mode 100644 vendor/github.com/pelletier/go-toml/position.go create mode 100755 vendor/github.com/pelletier/go-toml/test.sh create mode 100644 vendor/github.com/pelletier/go-toml/token.go create mode 100644 vendor/github.com/pelletier/go-toml/toml.go create mode 100644 vendor/github.com/pelletier/go-toml/tomltree_create.go create mode 100644 vendor/github.com/pelletier/go-toml/tomltree_write.go create mode 100644 vendor/github.com/satori/go.uuid/.travis.yml create mode 100644 vendor/github.com/satori/go.uuid/LICENSE create mode 100644 vendor/github.com/satori/go.uuid/README.md create mode 100644 vendor/github.com/satori/go.uuid/codec.go create mode 100644 vendor/github.com/satori/go.uuid/generator.go create mode 100644 vendor/github.com/satori/go.uuid/sql.go create mode 100644 vendor/github.com/satori/go.uuid/uuid.go create mode 100644 vendor/github.com/sirupsen/logrus/.gitignore create mode 100644 vendor/github.com/sirupsen/logrus/.travis.yml create mode 100644 vendor/github.com/sirupsen/logrus/CHANGELOG.md create mode 100644 vendor/github.com/sirupsen/logrus/LICENSE create mode 100644 vendor/github.com/sirupsen/logrus/README.md create mode 100644 vendor/github.com/sirupsen/logrus/alt_exit.go create mode 100644 vendor/github.com/sirupsen/logrus/appveyor.yml create mode 100644 vendor/github.com/sirupsen/logrus/doc.go create mode 100644 vendor/github.com/sirupsen/logrus/entry.go create mode 100644 vendor/github.com/sirupsen/logrus/exported.go create mode 100644 vendor/github.com/sirupsen/logrus/formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/go.mod create mode 100644 vendor/github.com/sirupsen/logrus/go.sum create mode 100644 vendor/github.com/sirupsen/logrus/hooks.go create mode 100644 vendor/github.com/sirupsen/logrus/json_formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/logger.go create mode 100644 vendor/github.com/sirupsen/logrus/logrus.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_appengine.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_js.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_windows.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_notwindows.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_windows.go create mode 100644 vendor/github.com/sirupsen/logrus/text_formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/writer.go create mode 100644 vendor/github.com/spf13/afero/.travis.yml create mode 100644 vendor/github.com/spf13/afero/LICENSE.txt create mode 100644 vendor/github.com/spf13/afero/README.md create mode 100644 vendor/github.com/spf13/afero/afero.go create mode 100644 vendor/github.com/spf13/afero/appveyor.yml create mode 100644 vendor/github.com/spf13/afero/basepath.go create mode 100644 vendor/github.com/spf13/afero/cacheOnReadFs.go create mode 100644 vendor/github.com/spf13/afero/const_bsds.go create mode 100644 vendor/github.com/spf13/afero/const_win_unix.go create mode 100644 vendor/github.com/spf13/afero/copyOnWriteFs.go create mode 100644 vendor/github.com/spf13/afero/go.mod create mode 100644 vendor/github.com/spf13/afero/httpFs.go create mode 100644 vendor/github.com/spf13/afero/ioutil.go create mode 100644 vendor/github.com/spf13/afero/lstater.go create mode 100644 vendor/github.com/spf13/afero/match.go create mode 100644 vendor/github.com/spf13/afero/mem/dir.go create mode 100644 vendor/github.com/spf13/afero/mem/dirmap.go create mode 100644 vendor/github.com/spf13/afero/mem/file.go create mode 100644 vendor/github.com/spf13/afero/memmap.go create mode 100644 vendor/github.com/spf13/afero/os.go create mode 100644 vendor/github.com/spf13/afero/path.go create mode 100644 vendor/github.com/spf13/afero/readonlyfs.go create mode 100644 vendor/github.com/spf13/afero/regexpfs.go create mode 100644 vendor/github.com/spf13/afero/unionFile.go create mode 100644 vendor/github.com/spf13/afero/util.go create mode 100644 vendor/github.com/spf13/cast/.gitignore create mode 100644 vendor/github.com/spf13/cast/.travis.yml create mode 100644 vendor/github.com/spf13/cast/LICENSE create mode 100644 vendor/github.com/spf13/cast/Makefile create mode 100644 vendor/github.com/spf13/cast/README.md create mode 100644 vendor/github.com/spf13/cast/cast.go create mode 100644 vendor/github.com/spf13/cast/caste.go create mode 100644 vendor/github.com/spf13/jwalterweatherman/.gitignore create mode 100644 vendor/github.com/spf13/jwalterweatherman/LICENSE create mode 100644 vendor/github.com/spf13/jwalterweatherman/README.md create mode 100644 vendor/github.com/spf13/jwalterweatherman/default_notepad.go create mode 100644 vendor/github.com/spf13/jwalterweatherman/go.mod create mode 100644 vendor/github.com/spf13/jwalterweatherman/log_counter.go create mode 100644 vendor/github.com/spf13/jwalterweatherman/notepad.go create mode 100644 vendor/github.com/spf13/pflag/.gitignore create mode 100644 vendor/github.com/spf13/pflag/.travis.yml create mode 100644 vendor/github.com/spf13/pflag/LICENSE create mode 100644 vendor/github.com/spf13/pflag/README.md create mode 100644 vendor/github.com/spf13/pflag/bool.go create mode 100644 vendor/github.com/spf13/pflag/bool_slice.go create mode 100644 vendor/github.com/spf13/pflag/bytes.go create mode 100644 vendor/github.com/spf13/pflag/count.go create mode 100644 vendor/github.com/spf13/pflag/duration.go create mode 100644 vendor/github.com/spf13/pflag/duration_slice.go create mode 100644 vendor/github.com/spf13/pflag/flag.go create mode 100644 vendor/github.com/spf13/pflag/float32.go create mode 100644 vendor/github.com/spf13/pflag/float64.go create mode 100644 vendor/github.com/spf13/pflag/golangflag.go create mode 100644 vendor/github.com/spf13/pflag/int.go create mode 100644 vendor/github.com/spf13/pflag/int16.go create mode 100644 vendor/github.com/spf13/pflag/int32.go create mode 100644 vendor/github.com/spf13/pflag/int64.go create mode 100644 vendor/github.com/spf13/pflag/int8.go create mode 100644 vendor/github.com/spf13/pflag/int_slice.go create mode 100644 vendor/github.com/spf13/pflag/ip.go create mode 100644 vendor/github.com/spf13/pflag/ip_slice.go create mode 100644 vendor/github.com/spf13/pflag/ipmask.go create mode 100644 vendor/github.com/spf13/pflag/ipnet.go create mode 100644 vendor/github.com/spf13/pflag/string.go create mode 100644 vendor/github.com/spf13/pflag/string_array.go create mode 100644 vendor/github.com/spf13/pflag/string_slice.go create mode 100644 vendor/github.com/spf13/pflag/string_to_int.go create mode 100644 vendor/github.com/spf13/pflag/string_to_string.go create mode 100644 vendor/github.com/spf13/pflag/uint.go create mode 100644 vendor/github.com/spf13/pflag/uint16.go create mode 100644 vendor/github.com/spf13/pflag/uint32.go create mode 100644 vendor/github.com/spf13/pflag/uint64.go create mode 100644 vendor/github.com/spf13/pflag/uint8.go create mode 100644 vendor/github.com/spf13/pflag/uint_slice.go create mode 100644 vendor/github.com/spf13/viper/.gitignore create mode 100644 vendor/github.com/spf13/viper/.travis.yml create mode 100644 vendor/github.com/spf13/viper/LICENSE create mode 100644 vendor/github.com/spf13/viper/README.md create mode 100644 vendor/github.com/spf13/viper/flags.go create mode 100644 vendor/github.com/spf13/viper/go.mod create mode 100644 vendor/github.com/spf13/viper/go.sum create mode 100644 vendor/github.com/spf13/viper/util.go create mode 100644 vendor/github.com/spf13/viper/viper.go create mode 100644 vendor/github.com/vishvananda/netlink/.travis.yml create mode 100644 vendor/github.com/vishvananda/netlink/CHANGELOG.md create mode 100644 vendor/github.com/vishvananda/netlink/LICENSE create mode 100644 vendor/github.com/vishvananda/netlink/Makefile create mode 100644 vendor/github.com/vishvananda/netlink/README.md create mode 100644 vendor/github.com/vishvananda/netlink/addr.go create mode 100644 vendor/github.com/vishvananda/netlink/addr_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/bpf_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/bridge_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/class.go create mode 100644 vendor/github.com/vishvananda/netlink/class_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/conntrack_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/conntrack_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/filter.go create mode 100644 vendor/github.com/vishvananda/netlink/filter_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/fou.go create mode 100644 vendor/github.com/vishvananda/netlink/fou_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/fou_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/genetlink_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/genetlink_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/gtp_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/handle_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/handle_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/ioctl_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/link.go create mode 100644 vendor/github.com/vishvananda/netlink/link_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/link_tuntap_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/neigh.go create mode 100644 vendor/github.com/vishvananda/netlink/neigh_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/netlink.go create mode 100644 vendor/github.com/vishvananda/netlink/netlink_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/netlink_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/netns_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/netns_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/addr_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/bridge_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/genetlink_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/link_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/mpls_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/nl_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/rdma_link_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/route_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/seg6_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/seg6local_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/syscall.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/tc_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/order.go create mode 100644 vendor/github.com/vishvananda/netlink/protinfo.go create mode 100644 vendor/github.com/vishvananda/netlink/protinfo_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/qdisc.go create mode 100644 vendor/github.com/vishvananda/netlink/qdisc_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/rdma_link_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/route.go create mode 100644 vendor/github.com/vishvananda/netlink/route_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/route_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/rule.go create mode 100644 vendor/github.com/vishvananda/netlink/rule_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/socket.go create mode 100644 vendor/github.com/vishvananda/netlink/socket_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_policy.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_state.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_state_linux.go create mode 100644 vendor/github.com/vishvananda/netns/LICENSE create mode 100644 vendor/github.com/vishvananda/netns/README.md create mode 100644 vendor/github.com/vishvananda/netns/netns.go create mode 100644 vendor/github.com/vishvananda/netns/netns_linux.go create mode 100644 vendor/github.com/vishvananda/netns/netns_unspecified.go create mode 100644 vendor/golang.org/x/crypto/AUTHORS create mode 100644 vendor/golang.org/x/crypto/CONTRIBUTORS create mode 100644 vendor/golang.org/x/crypto/LICENSE create mode 100644 vendor/golang.org/x/crypto/PATENTS create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/terminal.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_linux.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_windows.go create mode 100644 vendor/golang.org/x/net/AUTHORS create mode 100644 vendor/golang.org/x/net/CONTRIBUTORS create mode 100644 vendor/golang.org/x/net/LICENSE create mode 100644 vendor/golang.org/x/net/PATENTS create mode 100644 vendor/golang.org/x/net/context/context.go create mode 100644 vendor/golang.org/x/net/context/go17.go create mode 100644 vendor/golang.org/x/net/context/go19.go create mode 100644 vendor/golang.org/x/net/context/pre_go17.go create mode 100644 vendor/golang.org/x/net/context/pre_go19.go create mode 100644 vendor/golang.org/x/net/http/httpguts/guts.go create mode 100644 vendor/golang.org/x/net/http/httpguts/httplex.go create mode 100644 vendor/golang.org/x/net/http2/.gitignore create mode 100644 vendor/golang.org/x/net/http2/Dockerfile create mode 100644 vendor/golang.org/x/net/http2/Makefile create mode 100644 vendor/golang.org/x/net/http2/README create mode 100644 vendor/golang.org/x/net/http2/ciphers.go create mode 100644 vendor/golang.org/x/net/http2/client_conn_pool.go create mode 100644 vendor/golang.org/x/net/http2/configure_transport.go create mode 100644 vendor/golang.org/x/net/http2/databuffer.go create mode 100644 vendor/golang.org/x/net/http2/errors.go create mode 100644 vendor/golang.org/x/net/http2/flow.go create mode 100644 vendor/golang.org/x/net/http2/frame.go create mode 100644 vendor/golang.org/x/net/http2/go111.go create mode 100644 vendor/golang.org/x/net/http2/go16.go create mode 100644 vendor/golang.org/x/net/http2/go17.go create mode 100644 vendor/golang.org/x/net/http2/go17_not18.go create mode 100644 vendor/golang.org/x/net/http2/go18.go create mode 100644 vendor/golang.org/x/net/http2/go19.go create mode 100644 vendor/golang.org/x/net/http2/gotrack.go create mode 100644 vendor/golang.org/x/net/http2/headermap.go create mode 100644 vendor/golang.org/x/net/http2/hpack/encode.go create mode 100644 vendor/golang.org/x/net/http2/hpack/hpack.go create mode 100644 vendor/golang.org/x/net/http2/hpack/huffman.go create mode 100644 vendor/golang.org/x/net/http2/hpack/tables.go create mode 100644 vendor/golang.org/x/net/http2/http2.go create mode 100644 vendor/golang.org/x/net/http2/not_go111.go create mode 100644 vendor/golang.org/x/net/http2/not_go16.go create mode 100644 vendor/golang.org/x/net/http2/not_go17.go create mode 100644 vendor/golang.org/x/net/http2/not_go18.go create mode 100644 vendor/golang.org/x/net/http2/not_go19.go create mode 100644 vendor/golang.org/x/net/http2/pipe.go create mode 100644 vendor/golang.org/x/net/http2/server.go create mode 100644 vendor/golang.org/x/net/http2/transport.go create mode 100644 vendor/golang.org/x/net/http2/write.go create mode 100644 vendor/golang.org/x/net/http2/writesched.go create mode 100644 vendor/golang.org/x/net/http2/writesched_priority.go create mode 100644 vendor/golang.org/x/net/http2/writesched_random.go create mode 100644 vendor/golang.org/x/net/idna/idna.go create mode 100644 vendor/golang.org/x/net/idna/punycode.go create mode 100644 vendor/golang.org/x/net/idna/tables.go create mode 100644 vendor/golang.org/x/net/idna/trie.go create mode 100644 vendor/golang.org/x/net/idna/trieval.go create mode 100644 vendor/golang.org/x/net/internal/timeseries/timeseries.go create mode 100644 vendor/golang.org/x/net/trace/events.go create mode 100644 vendor/golang.org/x/net/trace/histogram.go create mode 100644 vendor/golang.org/x/net/trace/trace.go create mode 100644 vendor/golang.org/x/net/trace/trace_go16.go create mode 100644 vendor/golang.org/x/net/trace/trace_go17.go create mode 100644 vendor/golang.org/x/sys/AUTHORS create mode 100644 vendor/golang.org/x/sys/CONTRIBUTORS create mode 100644 vendor/golang.org/x/sys/LICENSE create mode 100644 vendor/golang.org/x/sys/PATENTS create mode 100644 vendor/golang.org/x/sys/unix/.gitignore create mode 100644 vendor/golang.org/x/sys/unix/README.md create mode 100644 vendor/golang.org/x/sys/unix/affinity_linux.go create mode 100644 vendor/golang.org/x/sys/unix/aliases.go create mode 100644 vendor/golang.org/x/sys/unix/asm_aix_ppc64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_386.s create mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_arm.s create mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_arm64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_386.s create mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_arm.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_386.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_arm.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_arm64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_mips64x.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_mipsx.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_s390x.s create mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_386.s create mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_arm.s create mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_386.s create mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_arm.s create mode 100644 vendor/golang.org/x/sys/unix/asm_solaris_amd64.s create mode 100644 vendor/golang.org/x/sys/unix/bluetooth_linux.go create mode 100644 vendor/golang.org/x/sys/unix/cap_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/constants.go create mode 100644 vendor/golang.org/x/sys/unix/dev_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/dev_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/dev_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/dev_dragonfly.go create mode 100644 vendor/golang.org/x/sys/unix/dev_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/dev_linux.go create mode 100644 vendor/golang.org/x/sys/unix/dev_netbsd.go create mode 100644 vendor/golang.org/x/sys/unix/dev_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/dirent.go create mode 100644 vendor/golang.org/x/sys/unix/endian_big.go create mode 100644 vendor/golang.org/x/sys/unix/endian_little.go create mode 100644 vendor/golang.org/x/sys/unix/env_unix.go create mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/fcntl.go create mode 100644 vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go create mode 100644 vendor/golang.org/x/sys/unix/gccgo.go create mode 100644 vendor/golang.org/x/sys/unix/gccgo_c.c create mode 100644 vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ioctl.go create mode 100755 vendor/golang.org/x/sys/unix/mkall.sh create mode 100755 vendor/golang.org/x/sys/unix/mkerrors.sh create mode 100644 vendor/golang.org/x/sys/unix/mkpost.go create mode 100755 vendor/golang.org/x/sys/unix/mksyscall.pl create mode 100755 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.pl create mode 100755 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.pl create mode 100755 vendor/golang.org/x/sys/unix/mksyscall_solaris.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysnum_darwin.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl create mode 100755 vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl create mode 100644 vendor/golang.org/x/sys/unix/openbsd_pledge.go create mode 100644 vendor/golang.org/x/sys/unix/pagesize_unix.go create mode 100644 vendor/golang.org/x/sys/unix/race.go create mode 100644 vendor/golang.org/x/sys/unix/race0.go create mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_linux.go create mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_unix.go create mode 100644 vendor/golang.org/x/sys/unix/str.go create mode 100644 vendor/golang.org/x/sys/unix/syscall.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_aix.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_bsd.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_dragonfly.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gc.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_solaris.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_unix.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_unix_gc.go create mode 100644 vendor/golang.org/x/sys/unix/timestruct.go create mode 100644 vendor/golang.org/x/sys/unix/types_aix.go create mode 100644 vendor/golang.org/x/sys/unix/types_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/types_dragonfly.go create mode 100644 vendor/golang.org/x/sys/unix/types_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_netbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_solaris.go create mode 100644 vendor/golang.org/x/sys/unix/xattr_bsd.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_386.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_386.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zptrace386_linux.go create mode 100644 vendor/golang.org/x/sys/unix/zptracearm_linux.go create mode 100644 vendor/golang.org/x/sys/unix/zptracemips_linux.go create mode 100644 vendor/golang.org/x/sys/unix/zptracemipsle_linux.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_386.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_386.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go create mode 100644 vendor/golang.org/x/sys/windows/aliases.go create mode 100644 vendor/golang.org/x/sys/windows/asm_windows_386.s create mode 100644 vendor/golang.org/x/sys/windows/asm_windows_amd64.s create mode 100644 vendor/golang.org/x/sys/windows/asm_windows_arm.s create mode 100644 vendor/golang.org/x/sys/windows/dll_windows.go create mode 100644 vendor/golang.org/x/sys/windows/env_windows.go create mode 100644 vendor/golang.org/x/sys/windows/eventlog.go create mode 100644 vendor/golang.org/x/sys/windows/exec_windows.go create mode 100644 vendor/golang.org/x/sys/windows/memory_windows.go create mode 100644 vendor/golang.org/x/sys/windows/mksyscall.go create mode 100644 vendor/golang.org/x/sys/windows/race.go create mode 100644 vendor/golang.org/x/sys/windows/race0.go create mode 100644 vendor/golang.org/x/sys/windows/security_windows.go create mode 100644 vendor/golang.org/x/sys/windows/service.go create mode 100644 vendor/golang.org/x/sys/windows/str.go create mode 100644 vendor/golang.org/x/sys/windows/syscall.go create mode 100644 vendor/golang.org/x/sys/windows/syscall_windows.go create mode 100644 vendor/golang.org/x/sys/windows/types_windows.go create mode 100644 vendor/golang.org/x/sys/windows/types_windows_386.go create mode 100644 vendor/golang.org/x/sys/windows/types_windows_amd64.go create mode 100644 vendor/golang.org/x/sys/windows/types_windows_arm.go create mode 100644 vendor/golang.org/x/sys/windows/zsyscall_windows.go create mode 100644 vendor/golang.org/x/text/AUTHORS create mode 100644 vendor/golang.org/x/text/CONTRIBUTORS create mode 100644 vendor/golang.org/x/text/LICENSE create mode 100644 vendor/golang.org/x/text/PATENTS create mode 100644 vendor/golang.org/x/text/collate/build/builder.go create mode 100644 vendor/golang.org/x/text/collate/build/colelem.go create mode 100644 vendor/golang.org/x/text/collate/build/contract.go create mode 100644 vendor/golang.org/x/text/collate/build/order.go create mode 100644 vendor/golang.org/x/text/collate/build/table.go create mode 100644 vendor/golang.org/x/text/collate/build/trie.go create mode 100644 vendor/golang.org/x/text/collate/collate.go create mode 100644 vendor/golang.org/x/text/collate/index.go create mode 100644 vendor/golang.org/x/text/collate/maketables.go create mode 100644 vendor/golang.org/x/text/collate/option.go create mode 100644 vendor/golang.org/x/text/collate/sort.go create mode 100644 vendor/golang.org/x/text/collate/tables.go create mode 100644 vendor/golang.org/x/text/internal/colltab/collelem.go create mode 100644 vendor/golang.org/x/text/internal/colltab/colltab.go create mode 100644 vendor/golang.org/x/text/internal/colltab/contract.go create mode 100644 vendor/golang.org/x/text/internal/colltab/iter.go create mode 100644 vendor/golang.org/x/text/internal/colltab/numeric.go create mode 100644 vendor/golang.org/x/text/internal/colltab/table.go create mode 100644 vendor/golang.org/x/text/internal/colltab/trie.go create mode 100644 vendor/golang.org/x/text/internal/colltab/weighter.go create mode 100644 vendor/golang.org/x/text/internal/gen/code.go create mode 100644 vendor/golang.org/x/text/internal/gen/gen.go create mode 100644 vendor/golang.org/x/text/internal/tag/tag.go create mode 100644 vendor/golang.org/x/text/internal/triegen/compact.go create mode 100644 vendor/golang.org/x/text/internal/triegen/print.go create mode 100644 vendor/golang.org/x/text/internal/triegen/triegen.go create mode 100644 vendor/golang.org/x/text/internal/ucd/ucd.go create mode 100644 vendor/golang.org/x/text/language/Makefile create mode 100644 vendor/golang.org/x/text/language/common.go create mode 100644 vendor/golang.org/x/text/language/coverage.go create mode 100644 vendor/golang.org/x/text/language/doc.go create mode 100644 vendor/golang.org/x/text/language/gen.go create mode 100644 vendor/golang.org/x/text/language/gen_common.go create mode 100644 vendor/golang.org/x/text/language/gen_index.go create mode 100644 vendor/golang.org/x/text/language/go1_1.go create mode 100644 vendor/golang.org/x/text/language/go1_2.go create mode 100644 vendor/golang.org/x/text/language/index.go create mode 100644 vendor/golang.org/x/text/language/language.go create mode 100644 vendor/golang.org/x/text/language/lookup.go create mode 100644 vendor/golang.org/x/text/language/match.go create mode 100644 vendor/golang.org/x/text/language/parse.go create mode 100644 vendor/golang.org/x/text/language/tables.go create mode 100644 vendor/golang.org/x/text/language/tags.go create mode 100644 vendor/golang.org/x/text/secure/bidirule/bidirule.go create mode 100644 vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go create mode 100644 vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go create mode 100644 vendor/golang.org/x/text/transform/transform.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/bidi.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/bracket.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/core.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_ranges.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_trieval.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/prop.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/trieval.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/base.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/cldr.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/collate.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/decode.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/makexml.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/resolve.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/slice.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/xml.go create mode 100644 vendor/golang.org/x/text/unicode/norm/composition.go create mode 100644 vendor/golang.org/x/text/unicode/norm/forminfo.go create mode 100644 vendor/golang.org/x/text/unicode/norm/input.go create mode 100644 vendor/golang.org/x/text/unicode/norm/iter.go create mode 100644 vendor/golang.org/x/text/unicode/norm/maketables.go create mode 100644 vendor/golang.org/x/text/unicode/norm/normalize.go create mode 100644 vendor/golang.org/x/text/unicode/norm/readwriter.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables10.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables9.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/transform.go create mode 100644 vendor/golang.org/x/text/unicode/norm/trie.go create mode 100644 vendor/golang.org/x/text/unicode/norm/triegen.go create mode 100644 vendor/golang.org/x/text/unicode/rangetable/gen.go create mode 100644 vendor/golang.org/x/text/unicode/rangetable/merge.go create mode 100644 vendor/golang.org/x/text/unicode/rangetable/rangetable.go create mode 100644 vendor/golang.org/x/text/unicode/rangetable/tables10.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/rangetable/tables9.0.0.go create mode 100644 vendor/google.golang.org/genproto/LICENSE create mode 100644 vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go create mode 100644 vendor/google.golang.org/grpc/.travis.yml create mode 100644 vendor/google.golang.org/grpc/AUTHORS create mode 100644 vendor/google.golang.org/grpc/CONTRIBUTING.md create mode 100644 vendor/google.golang.org/grpc/LICENSE create mode 100644 vendor/google.golang.org/grpc/Makefile create mode 100644 vendor/google.golang.org/grpc/README.md create mode 100644 vendor/google.golang.org/grpc/backoff.go create mode 100644 vendor/google.golang.org/grpc/balancer.go create mode 100644 vendor/google.golang.org/grpc/balancer/balancer.go create mode 100644 vendor/google.golang.org/grpc/balancer/base/balancer.go create mode 100644 vendor/google.golang.org/grpc/balancer/base/base.go create mode 100644 vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go create mode 100644 vendor/google.golang.org/grpc/balancer_conn_wrappers.go create mode 100644 vendor/google.golang.org/grpc/balancer_v1_wrapper.go create mode 100644 vendor/google.golang.org/grpc/call.go create mode 100644 vendor/google.golang.org/grpc/clientconn.go create mode 100644 vendor/google.golang.org/grpc/codec.go create mode 100755 vendor/google.golang.org/grpc/codegen.sh create mode 100644 vendor/google.golang.org/grpc/codes/code_string.go create mode 100644 vendor/google.golang.org/grpc/codes/codes.go create mode 100644 vendor/google.golang.org/grpc/connectivity/connectivity.go create mode 100644 vendor/google.golang.org/grpc/credentials/credentials.go create mode 100644 vendor/google.golang.org/grpc/credentials/go16.go create mode 100644 vendor/google.golang.org/grpc/credentials/go17.go create mode 100644 vendor/google.golang.org/grpc/credentials/go18.go create mode 100644 vendor/google.golang.org/grpc/credentials/go19.go create mode 100644 vendor/google.golang.org/grpc/dialoptions.go create mode 100644 vendor/google.golang.org/grpc/doc.go create mode 100644 vendor/google.golang.org/grpc/encoding/encoding.go create mode 100644 vendor/google.golang.org/grpc/encoding/proto/proto.go create mode 100644 vendor/google.golang.org/grpc/go.mod create mode 100644 vendor/google.golang.org/grpc/go.sum create mode 100644 vendor/google.golang.org/grpc/go16.go create mode 100644 vendor/google.golang.org/grpc/go17.go create mode 100644 vendor/google.golang.org/grpc/grpclog/grpclog.go create mode 100644 vendor/google.golang.org/grpc/grpclog/logger.go create mode 100644 vendor/google.golang.org/grpc/grpclog/loggerv2.go create mode 100755 vendor/google.golang.org/grpc/install_gae.sh create mode 100644 vendor/google.golang.org/grpc/interceptor.go create mode 100644 vendor/google.golang.org/grpc/internal/backoff/backoff.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/funcs.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/types.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/types_linux.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/util_linux_go19.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/util_nonlinux_pre_go19.go create mode 100644 vendor/google.golang.org/grpc/internal/envconfig/envconfig.go create mode 100644 vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go create mode 100644 vendor/google.golang.org/grpc/internal/internal.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/bdp_estimator.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/controlbuf.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/defaults.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/flowcontrol.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/go16.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/go17.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/handler_server.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/http2_client.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/http2_server.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/http_util.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/log.go create mode 100644 vendor/google.golang.org/grpc/internal/transport/transport.go create mode 100644 vendor/google.golang.org/grpc/keepalive/keepalive.go create mode 100644 vendor/google.golang.org/grpc/metadata/metadata.go create mode 100644 vendor/google.golang.org/grpc/naming/dns_resolver.go create mode 100644 vendor/google.golang.org/grpc/naming/go17.go create mode 100644 vendor/google.golang.org/grpc/naming/go18.go create mode 100644 vendor/google.golang.org/grpc/naming/naming.go create mode 100644 vendor/google.golang.org/grpc/peer/peer.go create mode 100644 vendor/google.golang.org/grpc/picker_wrapper.go create mode 100644 vendor/google.golang.org/grpc/pickfirst.go create mode 100644 vendor/google.golang.org/grpc/proxy.go create mode 100644 vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go create mode 100644 vendor/google.golang.org/grpc/resolver/dns/go17.go create mode 100644 vendor/google.golang.org/grpc/resolver/dns/go18.go create mode 100644 vendor/google.golang.org/grpc/resolver/passthrough/passthrough.go create mode 100644 vendor/google.golang.org/grpc/resolver/resolver.go create mode 100644 vendor/google.golang.org/grpc/resolver_conn_wrapper.go create mode 100644 vendor/google.golang.org/grpc/rpc_util.go create mode 100644 vendor/google.golang.org/grpc/server.go create mode 100644 vendor/google.golang.org/grpc/service_config.go create mode 100644 vendor/google.golang.org/grpc/stats/handlers.go create mode 100644 vendor/google.golang.org/grpc/stats/stats.go create mode 100644 vendor/google.golang.org/grpc/status/go16.go create mode 100644 vendor/google.golang.org/grpc/status/go17.go create mode 100644 vendor/google.golang.org/grpc/status/status.go create mode 100644 vendor/google.golang.org/grpc/stream.go create mode 100644 vendor/google.golang.org/grpc/tap/tap.go create mode 100644 vendor/google.golang.org/grpc/trace.go create mode 100644 vendor/google.golang.org/grpc/version.go create mode 100755 vendor/google.golang.org/grpc/vet.sh create mode 100644 vendor/gopkg.in/tomb.v2/LICENSE create mode 100644 vendor/gopkg.in/tomb.v2/README.md create mode 100644 vendor/gopkg.in/tomb.v2/context.go create mode 100644 vendor/gopkg.in/tomb.v2/context16.go create mode 100644 vendor/gopkg.in/tomb.v2/tomb.go create mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE.libyaml create mode 100644 vendor/gopkg.in/yaml.v2/NOTICE create mode 100644 vendor/gopkg.in/yaml.v2/README.md create mode 100644 vendor/gopkg.in/yaml.v2/apic.go create mode 100644 vendor/gopkg.in/yaml.v2/decode.go create mode 100644 vendor/gopkg.in/yaml.v2/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v2/encode.go create mode 100644 vendor/gopkg.in/yaml.v2/go.mod create mode 100644 vendor/gopkg.in/yaml.v2/parserc.go create mode 100644 vendor/gopkg.in/yaml.v2/readerc.go create mode 100644 vendor/gopkg.in/yaml.v2/resolve.go create mode 100644 vendor/gopkg.in/yaml.v2/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v2/sorter.go create mode 100644 vendor/gopkg.in/yaml.v2/writerc.go create mode 100644 vendor/gopkg.in/yaml.v2/yaml.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlprivateh.go diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index eca23be..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,356 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:c47f4964978e211c6e566596ec6246c329912ea92e9bb99c00798bb4564c5b09" - name = "github.com/armon/go-radix" - packages = ["."] - pruneopts = "UT" - revision = "1a2de0c21c94309923825da3df33a4381872c795" - -[[projects]] - branch = "master" - digest = "1:8583eab935e3d99d3a7ac489cd2ee7c8e95eecd7c64ab1fc8382746dacaf8563" - name = "github.com/dgryski/go-farm" - packages = ["."] - pruneopts = "UT" - revision = "2de33835d10275975374b37b2dcfd22c9020a1f5" - -[[projects]] - digest = "1:975a4480c40f2d0b95e1f83d3ec1aa29a2774e80179e08a9a4ba2aab86721b23" - name = "github.com/eapache/channels" - packages = ["."] - pruneopts = "UT" - revision = "47238d5aae8c0fefd518ef2bee46290909cf8263" - version = "v1.1.0" - -[[projects]] - digest = "1:444b82bfe35c83bbcaf84e310fb81a1f9ece03edfed586483c869e2c046aef69" - name = "github.com/eapache/queue" - packages = ["."] - pruneopts = "UT" - revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" - version = "v1.1.0" - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - branch = "master" - digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" - name = "github.com/golang/glog" - packages = ["."] - pruneopts = "UT" - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - -[[projects]] - branch = "master" - digest = "1:c095e448622bb061a09cb718e3dddc59acb624a67756392282374fa091c83343" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/empty", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "7be3631955993a734965532f776bad7093f6fc9d" - -[[projects]] - digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:65b0a07f85f7b5cc019c26775efc278a155a5dea0a8aa617c980e8308d16bc55" - name = "github.com/influxdata/influxdb" - packages = [ - "client/v2", - "models", - "pkg/escape", - ] - pruneopts = "UT" - revision = "c75cdfdfa6f71a08473fefcec71f6cbcbdef1ff4" - version = "v1.6.4" - -[[projects]] - digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" - name = "github.com/konsorten/go-windows-terminal-sequences" - packages = ["."] - pruneopts = "UT" - revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" - version = "v1.0.1" - -[[projects]] - digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" - name = "github.com/magiconair/properties" - packages = ["."] - pruneopts = "UT" - revision = "c2353362d570a7bfa228149c62842019201cfb71" - version = "v1.8.0" - -[[projects]] - digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - branch = "master" - digest = "1:7261338473c27eea9593dceecf6a949c689f6dbd2226cad46139ab9d5011bc4b" - name = "github.com/osrg/gobgp" - packages = [ - "api", - "internal/pkg/apiutil", - "internal/pkg/config", - "internal/pkg/table", - "internal/pkg/zebra", - "pkg/packet/bgp", - "pkg/packet/bmp", - "pkg/packet/mrt", - "pkg/packet/rtr", - "pkg/server", - ] - pruneopts = "UT" - revision = "329c2d316efecfed0331e30114d7086aa58e247e" - -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:dd6ba1917df517806c9dcee5c87f15643c9b1ca6260d5b3f25eb863c6fe092ce" - name = "github.com/satori/go.uuid" - packages = ["."] - pruneopts = "UT" - revision = "8ccf5352a842c034b1a69f28c863aff9b1cdb116" - -[[projects]] - digest = "1:3f53e9e4dfbb664cd62940c9c4b65a2171c66acd0b7621a1a6b8e78513525a52" - name = "github.com/sirupsen/logrus" - packages = ["."] - pruneopts = "UT" - revision = "ad15b42461921f1fb3529b058c6786c6a45d5162" - version = "v1.1.1" - -[[projects]] - digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" - version = "v1.1.2" - -[[projects]] - digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" - -[[projects]] - digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" - version = "v1.0.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - branch = "master" - digest = "1:748519c76ecc7b5d673d7ee8924ace736ec1717b93011c77171b8bd961ac280c" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "UT" - revision = "62edee319679b6ceaec16de03b966102d2dea709" - -[[projects]] - branch = "master" - digest = "1:b046e193dd6bb64f4df01dcc12eec2ec89ba32565f025f9cb9f1d54bc3945be9" - name = "github.com/vishvananda/netlink" - packages = [ - ".", - "nl", - ] - pruneopts = "UT" - revision = "d3a23fd178f1a0d9cf1f194af62864b1dfe02be5" - -[[projects]] - branch = "master" - digest = "1:e4e30678fb2560b5c62f6308c5023d6c294fc7713216fa379411cc74465e866f" - name = "github.com/vishvananda/netns" - packages = ["."] - pruneopts = "UT" - revision = "13995c7128ccc8e51e9a6bd2b551020a27180abd" - -[[projects]] - branch = "master" - digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - pruneopts = "UT" - revision = "0c41d7ab0a0ee717d4590a44bcb987dfd9e183eb" - -[[projects]] - branch = "master" - digest = "1:505dbee0833715a72a529bb57c354826ad42a4496fad787fa143699b4de1a6d0" - name = "golang.org/x/net" - packages = [ - "context", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "trace", - ] - pruneopts = "UT" - revision = "04a2e542c03f1d053ab3e4d6e5abcd4b66e2be8e" - -[[projects]] - branch = "master" - digest = "1:cfc31002d1ab36060fcd4a29d9f6bad6f9eeeab1dc6f5be78d37a0f825ba6dc1" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows", - ] - pruneopts = "UT" - revision = "eda9bb28ed513021f3e6a2a361031adc3d8a6301" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "94acd270e44e65579b9ee3cdab25034d33fed608" - -[[projects]] - digest = "1:ab8e92d746fb5c4c18846b0879842ac8e53b3d352449423d0924a11f1020ae1b" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/channelz", - "internal/envconfig", - "internal/grpcrand", - "internal/transport", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - ] - pruneopts = "UT" - revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1" - version = "v1.15.0" - -[[projects]] - branch = "v2" - digest = "1:5bb148b78468350091db2ffbb2370f35cc6dcd74d9378a31b1c7b86ff7528f08" - name = "gopkg.in/tomb.v2" - packages = ["."] - pruneopts = "UT" - revision = "d5d1b5820637886def9eef33e03a27a9f166942c" - -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/golang/glog", - "github.com/golang/protobuf/ptypes", - "github.com/golang/protobuf/ptypes/any", - "github.com/osrg/gobgp/api", - "github.com/osrg/gobgp/pkg/server", - "gopkg.in/yaml.v2", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index f215d19..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,42 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - branch = "master" - name = "github.com/golang/glog" - -[[constraint]] - branch = "master" - name = "github.com/golang/protobuf" - -[[constraint]] - branch = "master" - name = "github.com/osrg/gobgp" - -[prune] - go-tests = true - unused-packages = true diff --git a/Makefile b/Makefile index 50d742e..ec61d7f 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,16 @@ .PHONY: all gocast test all: - $(MAKE) deps $(MAKE) gocast -deps: - go get -u golang.org/x/lint/golint - go get -u github.com/golang/dep/cmd/dep - dep ensure - gocast: - go build . + go build -mod=vendor . debug: - dep ensure - go build -race . + go build -mod=vendor -race . test: - go test -v -race -short -failfast ./... + go test -v -race -short -failfast -mod=vendor ./... linux: - GOOS=linux GOARCH=amd64 go build -o gocast_linux . + GOOS=linux GOARCH=amd64 go build -o gocast_linux -mod=vendor . diff --git a/controller/app.go b/controller/app.go index 5c0dce7..bf2b0d5 100644 --- a/controller/app.go +++ b/controller/app.go @@ -53,6 +53,7 @@ type App struct { Vip *net.IPNet Monitors Monitors Nats []string + Source string } func (a *App) Equal(other *App) bool { @@ -67,11 +68,11 @@ func (a *App) Equal(other *App) bool { return a.Name == other.Name && a.Vip.String() == other.Vip.String() } -func NewApp(appName, vip string, monitors []string, nats []string) (*App, error) { +func NewApp(appName, vip string, monitors []string, nats []string, source string) (*App, error) { if appName == "" { return nil, fmt.Errorf("Invalid app name") } - app := &App{Name: appName, Nats: nats} + app := &App{Name: appName, Nats: nats, Source: source} _, ipnet, err := net.ParseCIDR(vip) if err != nil { return nil, fmt.Errorf("Invalid VIP specified, need ip/mask") diff --git a/controller/consul.go b/controller/consul.go index b09822d..e3f5d8c 100644 --- a/controller/consul.go +++ b/controller/consul.go @@ -86,7 +86,7 @@ func (c *ConsulMon) queryServices() ([]*App, error) { glog.Errorf("No vip Tag found in matched service :%s", service.Service) continue } - app, err := NewApp(service.Service, vip, monitors, nats) + app, err := NewApp(service.Service, vip, monitors, nats, "consul") if err != nil { glog.Errorf("Unable to add consul app: %v", err) continue diff --git a/controller/monitor.go b/controller/monitor.go index 3969a46..dbff4cb 100644 --- a/controller/monitor.go +++ b/controller/monitor.go @@ -97,7 +97,7 @@ func NewMonitor(config *c.Config) *MonitorMgr { mon.config = config // add apps defined in config for _, a := range config.Apps { - app, err := NewApp(a.Name, a.Vip, a.Monitors, a.Nats) + app, err := NewApp(a.Name, a.Vip, a.Monitors, a.Nats, "config") if err != nil { glog.Errorf("Failed to add configured app %s: %v", a.Name, err) continue @@ -119,7 +119,10 @@ func (m *MonitorMgr) consulMon() { // remove currently running apps that are not discovered in this pass var toRemove []string m.Lock() - for name := range m.monitors { + for name, mon := range m.monitors { + if mon.app.Source != "consul" { + continue + } var found bool for _, app := range apps { if name == app.Name { diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e6078e5 --- /dev/null +++ b/go.mod @@ -0,0 +1,37 @@ +module github.com/mayuresh82/gocast + +go 1.12 + +require ( + github.com/armon/go-radix v1.0.0 + github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 + github.com/eapache/channels v1.1.0 + github.com/eapache/queue v1.1.0 + github.com/fsnotify/fsnotify v1.4.7 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/protobuf v0.0.0-20181022004443-7be363195599 + github.com/hashicorp/hcl v1.0.0 + github.com/influxdata/influxdb v1.6.4 + github.com/konsorten/go-windows-terminal-sequences v1.0.1 + github.com/magiconair/properties v1.8.0 + github.com/mitchellh/mapstructure v1.1.2 + github.com/osrg/gobgp v0.0.0-20180929145215-329c2d316efe + github.com/pelletier/go-toml v1.2.0 + github.com/satori/go.uuid v0.0.0-20181016184021-8ccf5352a842 + github.com/sirupsen/logrus v1.1.1 + github.com/spf13/afero v1.1.2 + github.com/spf13/cast v1.2.0 + github.com/spf13/jwalterweatherman v1.0.0 + github.com/spf13/pflag v1.0.3 + github.com/spf13/viper v0.0.0-20180930044127-62edee319679 + github.com/vishvananda/netlink v0.0.0-20181018205019-d3a23fd178f1 + github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc + golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e + golang.org/x/net v0.0.0-20181017193950-04a2e542c03f + golang.org/x/sys v0.0.0-20181021155630-eda9bb28ed51 + golang.org/x/text v0.3.0 + google.golang.org/genproto v0.0.0-20181016170114-94acd270e44e + google.golang.org/grpc v1.15.0 + gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/server/server.go b/server/server.go index aa08872..7556ae6 100644 --- a/server/server.go +++ b/server/server.go @@ -46,7 +46,7 @@ func (s *Server) Serve(ctx context.Context) { func (s *Server) registerHandler(w http.ResponseWriter, r *http.Request) { queries := r.URL.Query() - app, err := controller.NewApp(queries["name"][0], queries["vip"][0], queries["monitor"], queries["nat"]) + app, err := controller.NewApp(queries["name"][0], queries["vip"][0], queries["monitor"], queries["nat"], "http") if err != nil { http.Error(w, fmt.Sprintf("Invalid request: %v", err), http.StatusBadRequest) return diff --git a/vendor/github.com/armon/go-radix/.gitignore b/vendor/github.com/armon/go-radix/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/vendor/github.com/armon/go-radix/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/armon/go-radix/.travis.yml b/vendor/github.com/armon/go-radix/.travis.yml new file mode 100644 index 0000000..1a0bbea --- /dev/null +++ b/vendor/github.com/armon/go-radix/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - tip diff --git a/vendor/github.com/armon/go-radix/LICENSE b/vendor/github.com/armon/go-radix/LICENSE new file mode 100644 index 0000000..a5df10e --- /dev/null +++ b/vendor/github.com/armon/go-radix/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Armon Dadgar + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/armon/go-radix/README.md b/vendor/github.com/armon/go-radix/README.md new file mode 100644 index 0000000..26f42a2 --- /dev/null +++ b/vendor/github.com/armon/go-radix/README.md @@ -0,0 +1,38 @@ +go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) +========= + +Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). +The package only provides a single `Tree` implementation, optimized for sparse nodes. + +As a radix tree, it provides the following: + * O(k) operations. In many cases, this can be faster than a hash table since + the hash function is an O(k) operation, and hash tables have very poor cache locality. + * Minimum / Maximum value lookups + * Ordered iteration + +For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). + +Documentation +============= + +The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). + +Example +======= + +Below is a simple example of usage + +```go +// Create a tree +r := radix.New() +r.Insert("foo", 1) +r.Insert("bar", 2) +r.Insert("foobar", 2) + +// Find the longest prefix match +m, _, _ := r.LongestPrefix("foozip") +if m != "foo" { + panic("should be foo") +} +``` + diff --git a/vendor/github.com/armon/go-radix/go.mod b/vendor/github.com/armon/go-radix/go.mod new file mode 100644 index 0000000..4336aa2 --- /dev/null +++ b/vendor/github.com/armon/go-radix/go.mod @@ -0,0 +1 @@ +module github.com/armon/go-radix diff --git a/vendor/github.com/armon/go-radix/radix.go b/vendor/github.com/armon/go-radix/radix.go new file mode 100644 index 0000000..e2bb22e --- /dev/null +++ b/vendor/github.com/armon/go-radix/radix.go @@ -0,0 +1,540 @@ +package radix + +import ( + "sort" + "strings" +) + +// WalkFn is used when walking the tree. Takes a +// key and value, returning if iteration should +// be terminated. +type WalkFn func(s string, v interface{}) bool + +// leafNode is used to represent a value +type leafNode struct { + key string + val interface{} +} + +// edge is used to represent an edge node +type edge struct { + label byte + node *node +} + +type node struct { + // leaf is used to store possible leaf + leaf *leafNode + + // prefix is the common prefix we ignore + prefix string + + // Edges should be stored in-order for iteration. + // We avoid a fully materialized slice to save memory, + // since in most cases we expect to be sparse + edges edges +} + +func (n *node) isLeaf() bool { + return n.leaf != nil +} + +func (n *node) addEdge(e edge) { + n.edges = append(n.edges, e) + n.edges.Sort() +} + +func (n *node) updateEdge(label byte, node *node) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + n.edges[idx].node = node + return + } + panic("replacing missing edge") +} + +func (n *node) getEdge(label byte) *node { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + return n.edges[idx].node + } + return nil +} + +func (n *node) delEdge(label byte) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + copy(n.edges[idx:], n.edges[idx+1:]) + n.edges[len(n.edges)-1] = edge{} + n.edges = n.edges[:len(n.edges)-1] + } +} + +type edges []edge + +func (e edges) Len() int { + return len(e) +} + +func (e edges) Less(i, j int) bool { + return e[i].label < e[j].label +} + +func (e edges) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + +func (e edges) Sort() { + sort.Sort(e) +} + +// Tree implements a radix tree. This can be treated as a +// Dictionary abstract data type. The main advantage over +// a standard hash map is prefix-based lookups and +// ordered iteration, +type Tree struct { + root *node + size int +} + +// New returns an empty Tree +func New() *Tree { + return NewFromMap(nil) +} + +// NewFromMap returns a new tree containing the keys +// from an existing map +func NewFromMap(m map[string]interface{}) *Tree { + t := &Tree{root: &node{}} + for k, v := range m { + t.Insert(k, v) + } + return t +} + +// Len is used to return the number of elements in the tree +func (t *Tree) Len() int { + return t.size +} + +// longestPrefix finds the length of the shared prefix +// of two strings +func longestPrefix(k1, k2 string) int { + max := len(k1) + if l := len(k2); l < max { + max = l + } + var i int + for i = 0; i < max; i++ { + if k1[i] != k2[i] { + break + } + } + return i +} + +// Insert is used to add a newentry or update +// an existing entry. Returns if updated. +func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { + var parent *node + n := t.root + search := s + for { + // Handle key exhaution + if len(search) == 0 { + if n.isLeaf() { + old := n.leaf.val + n.leaf.val = v + return old, true + } + + n.leaf = &leafNode{ + key: s, + val: v, + } + t.size++ + return nil, false + } + + // Look for the edge + parent = n + n = n.getEdge(search[0]) + + // No edge, create one + if n == nil { + e := edge{ + label: search[0], + node: &node{ + leaf: &leafNode{ + key: s, + val: v, + }, + prefix: search, + }, + } + parent.addEdge(e) + t.size++ + return nil, false + } + + // Determine longest prefix of the search key on match + commonPrefix := longestPrefix(search, n.prefix) + if commonPrefix == len(n.prefix) { + search = search[commonPrefix:] + continue + } + + // Split the node + t.size++ + child := &node{ + prefix: search[:commonPrefix], + } + parent.updateEdge(search[0], child) + + // Restore the existing node + child.addEdge(edge{ + label: n.prefix[commonPrefix], + node: n, + }) + n.prefix = n.prefix[commonPrefix:] + + // Create a new leaf node + leaf := &leafNode{ + key: s, + val: v, + } + + // If the new key is a subset, add to to this node + search = search[commonPrefix:] + if len(search) == 0 { + child.leaf = leaf + return nil, false + } + + // Create a new edge for the node + child.addEdge(edge{ + label: search[0], + node: &node{ + leaf: leaf, + prefix: search, + }, + }) + return nil, false + } +} + +// Delete is used to delete a key, returning the previous +// value and if it was deleted +func (t *Tree) Delete(s string) (interface{}, bool) { + var parent *node + var label byte + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if !n.isLeaf() { + break + } + goto DELETE + } + + // Look for an edge + parent = n + label = search[0] + n = n.getEdge(label) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false + +DELETE: + // Delete the leaf + leaf := n.leaf + n.leaf = nil + t.size-- + + // Check if we should delete this node from the parent + if parent != nil && len(n.edges) == 0 { + parent.delEdge(label) + } + + // Check if we should merge this node + if n != t.root && len(n.edges) == 1 { + n.mergeChild() + } + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + + return leaf.val, true +} + +// DeletePrefix is used to delete the subtree under a prefix +// Returns how many nodes were deleted +// Use this to delete large subtrees efficiently +func (t *Tree) DeletePrefix(s string) int { + return t.deletePrefix(nil, t.root, s) +} + +// delete does a recursive deletion +func (t *Tree) deletePrefix(parent, n *node, prefix string) int { + // Check for key exhaustion + if len(prefix) == 0 { + // Remove the leaf node + subTreeSize := 0 + //recursively walk from all edges of the node to be deleted + recursiveWalk(n, func(s string, v interface{}) bool { + subTreeSize++ + return false + }) + if n.isLeaf() { + n.leaf = nil + } + n.edges = nil // deletes the entire subtree + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + t.size -= subTreeSize + return subTreeSize + } + + // Look for an edge + label := prefix[0] + child := n.getEdge(label) + if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { + return 0 + } + + // Consume the search prefix + if len(child.prefix) > len(prefix) { + prefix = prefix[len(prefix):] + } else { + prefix = prefix[len(child.prefix):] + } + return t.deletePrefix(n, child, prefix) +} + +func (n *node) mergeChild() { + e := n.edges[0] + child := e.node + n.prefix = n.prefix + child.prefix + n.leaf = child.leaf + n.edges = child.edges +} + +// Get is used to lookup a specific key, returning +// the value and if it was found +func (t *Tree) Get(s string) (interface{}, bool) { + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if n.isLeaf() { + return n.leaf.val, true + } + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false +} + +// LongestPrefix is like Get, but instead of an +// exact match, it will return the longest prefix match. +func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { + var last *leafNode + n := t.root + search := s + for { + // Look for a leaf node + if n.isLeaf() { + last = n.leaf + } + + // Check for key exhaution + if len(search) == 0 { + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + if last != nil { + return last.key, last.val, true + } + return "", nil, false +} + +// Minimum is used to return the minimum value in the tree +func (t *Tree) Minimum() (string, interface{}, bool) { + n := t.root + for { + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + if len(n.edges) > 0 { + n = n.edges[0].node + } else { + break + } + } + return "", nil, false +} + +// Maximum is used to return the maximum value in the tree +func (t *Tree) Maximum() (string, interface{}, bool) { + n := t.root + for { + if num := len(n.edges); num > 0 { + n = n.edges[num-1].node + continue + } + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + break + } + return "", nil, false +} + +// Walk is used to walk the tree +func (t *Tree) Walk(fn WalkFn) { + recursiveWalk(t.root, fn) +} + +// WalkPrefix is used to walk the tree under a prefix +func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { + n := t.root + search := prefix + for { + // Check for key exhaution + if len(search) == 0 { + recursiveWalk(n, fn) + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + + } else if strings.HasPrefix(n.prefix, search) { + // Child may be under our search prefix + recursiveWalk(n, fn) + return + } else { + break + } + } + +} + +// WalkPath is used to walk the tree, but only visiting nodes +// from the root down to a given leaf. Where WalkPrefix walks +// all the entries *under* the given prefix, this walks the +// entries *above* the given prefix. +func (t *Tree) WalkPath(path string, fn WalkFn) { + n := t.root + search := path + for { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return + } + + // Check for key exhaution + if len(search) == 0 { + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + return + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } +} + +// recursiveWalk is used to do a pre-order walk of a node +// recursively. Returns true if the walk should be aborted +func recursiveWalk(n *node, fn WalkFn) bool { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return true + } + + // Recurse on the children + for _, e := range n.edges { + if recursiveWalk(e.node, fn) { + return true + } + } + return false +} + +// ToMap is used to walk the tree and convert it into a map +func (t *Tree) ToMap() map[string]interface{} { + out := make(map[string]interface{}, t.size) + t.Walk(func(k string, v interface{}) bool { + out[k] = v + return false + }) + return out +} diff --git a/vendor/github.com/dgryski/go-farm/.gitignore b/vendor/github.com/dgryski/go-farm/.gitignore new file mode 100644 index 0000000..36029ab --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +*.exe +*.test +*.prof + +target diff --git a/vendor/github.com/dgryski/go-farm/.travis.yml b/vendor/github.com/dgryski/go-farm/.travis.yml new file mode 100644 index 0000000..10c8a83 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/.travis.yml @@ -0,0 +1,38 @@ +language: go + +sudo: false + +branches: + except: + - release + +branches: + only: + - master + - develop + - travis + +go: + - 1.9 + - tip + +matrix: + allow_failures: + - go: tip + +before_install: + - if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi; + - if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi; + - go get github.com/mattn/goveralls + +before_script: + - make deps + +script: + - make qa + +after_failure: + - cat ./target/test/report.xml + +after_success: + - if [ "$TRAVIS_GO_VERSION" = "1.9" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi; diff --git a/vendor/github.com/dgryski/go-farm/LICENSE b/vendor/github.com/dgryski/go-farm/LICENSE new file mode 100644 index 0000000..3d07f66 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/LICENSE @@ -0,0 +1,23 @@ +As this is a highly derivative work, I have placed it under the same license as the original implementation: + +Copyright (c) 2014-2017 Damian Gryski +Copyright (c) 2016-2017 Nicola Asuni - Tecnick.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/github.com/dgryski/go-farm/Makefile b/vendor/github.com/dgryski/go-farm/Makefile new file mode 100644 index 0000000..f01244b --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/Makefile @@ -0,0 +1,203 @@ +# MAKEFILE +# +# @author Nicola Asuni +# @link https://github.com/dgryski/go-farm +# +# This file is intended to be executed in a Linux-compatible system. +# It also assumes that the project has been cloned in the right path under GOPATH: +# $GOPATH/src/github.com/dgryski/go-farm +# +# ------------------------------------------------------------------------------ + +# List special make targets that are not associated with files +.PHONY: help all test format fmtcheck vet lint coverage cyclo ineffassign misspell structcheck varcheck errcheck gosimple astscan qa deps clean nuke + +# Use bash as shell (Note: Ubuntu now uses dash which doesn't support PIPESTATUS). +SHELL=/bin/bash + +# CVS path (path to the parent dir containing the project) +CVSPATH=github.com/dgryski + +# Project owner +OWNER=dgryski + +# Project vendor +VENDOR=dgryski + +# Project name +PROJECT=go-farm + +# Project version +VERSION=$(shell cat VERSION) + +# Name of RPM or DEB package +PKGNAME=${VENDOR}-${PROJECT} + +# Current directory +CURRENTDIR=$(shell pwd) + +# GO lang path +ifneq ($(GOPATH),) + ifeq ($(findstring $(GOPATH),$(CURRENTDIR)),) + # the defined GOPATH is not valid + GOPATH= + endif +endif +ifeq ($(GOPATH),) + # extract the GOPATH + GOPATH=$(firstword $(subst /src/, ,$(CURRENTDIR))) +endif + +# --- MAKE TARGETS --- + +# Display general help about this command +help: + @echo "" + @echo "$(PROJECT) Makefile." + @echo "GOPATH=$(GOPATH)" + @echo "The following commands are available:" + @echo "" + @echo " make qa : Run all the tests" + @echo " make test : Run the unit tests" + @echo "" + @echo " make format : Format the source code" + @echo " make fmtcheck : Check if the source code has been formatted" + @echo " make vet : Check for suspicious constructs" + @echo " make lint : Check for style errors" + @echo " make coverage : Generate the coverage report" + @echo " make cyclo : Generate the cyclomatic complexity report" + @echo " make ineffassign : Detect ineffectual assignments" + @echo " make misspell : Detect commonly misspelled words in source files" + @echo " make structcheck : Find unused struct fields" + @echo " make varcheck : Find unused global variables and constants" + @echo " make errcheck : Check that error return values are used" + @echo " make gosimple : Suggest code simplifications" + @echo " make astscan : GO AST scanner" + @echo "" + @echo " make docs : Generate source code documentation" + @echo "" + @echo " make deps : Get the dependencies" + @echo " make clean : Remove any build artifact" + @echo " make nuke : Deletes any intermediate file" + @echo "" + + +# Alias for help target +all: help + +# Run the unit tests +test: + @mkdir -p target/test + @mkdir -p target/report + GOPATH=$(GOPATH) \ + go test \ + -covermode=atomic \ + -bench=. \ + -race \ + -cpuprofile=target/report/cpu.out \ + -memprofile=target/report/mem.out \ + -mutexprofile=target/report/mutex.out \ + -coverprofile=target/report/coverage.out \ + -v ./... | \ + tee >(PATH=$(GOPATH)/bin:$(PATH) go-junit-report > target/test/report.xml); \ + test $${PIPESTATUS[0]} -eq 0 + +# Format the source code +format: + @find . -type f -name "*.go" -exec gofmt -s -w {} \; + +# Check if the source code has been formatted +fmtcheck: + @mkdir -p target + @find . -type f -name "*.go" -exec gofmt -s -d {} \; | tee target/format.diff + @test ! -s target/format.diff || { echo "ERROR: the source code has not been formatted - please use 'make format' or 'gofmt'"; exit 1; } + +# Check for syntax errors +vet: + GOPATH=$(GOPATH) go vet . + +# Check for style errors +lint: + GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) golint . + +# Generate the coverage report +coverage: + @mkdir -p target/report + GOPATH=$(GOPATH) \ + go tool cover -html=target/report/coverage.out -o target/report/coverage.html + +# Report cyclomatic complexity +cyclo: + @mkdir -p target/report + GOPATH=$(GOPATH) gocyclo -avg ./ | tee target/report/cyclo.txt ; test $${PIPESTATUS[0]} -eq 0 + +# Detect ineffectual assignments +ineffassign: + @mkdir -p target/report + GOPATH=$(GOPATH) ineffassign ./ | tee target/report/ineffassign.txt ; test $${PIPESTATUS[0]} -eq 0 + +# Detect commonly misspelled words in source files +misspell: + @mkdir -p target/report + GOPATH=$(GOPATH) misspell -error ./ | tee target/report/misspell.txt ; test $${PIPESTATUS[0]} -eq 0 + +# Find unused struct fields +structcheck: + @mkdir -p target/report + GOPATH=$(GOPATH) structcheck -a ./ | tee target/report/structcheck.txt + +# Find unused global variables and constants +varcheck: + @mkdir -p target/report + GOPATH=$(GOPATH) varcheck -e ./ | tee target/report/varcheck.txt + +# Check that error return values are used +errcheck: + @mkdir -p target/report + GOPATH=$(GOPATH) errcheck ./ | tee target/report/errcheck.txt + +# Suggest code simplifications +gosimple: + @mkdir -p target/report + GOPATH=$(GOPATH) gosimple ./ | tee target/report/gosimple.txt + +# AST scanner +astscan: + @mkdir -p target/report + GOPATH=$(GOPATH) gas .//*.go | tee target/report/astscan.txt + +# Generate source docs +docs: + @mkdir -p target/docs + nohup sh -c 'GOPATH=$(GOPATH) godoc -http=127.0.0.1:6060' > target/godoc_server.log 2>&1 & + wget --directory-prefix=target/docs/ --execute robots=off --retry-connrefused --recursive --no-parent --adjust-extension --page-requisites --convert-links http://127.0.0.1:6060/pkg/github.com/${VENDOR}/${PROJECT}/ ; kill -9 `lsof -ti :6060` + @echo ''${PKGNAME}' Documentation ...' > target/docs/index.html + +# Alias to run all quality-assurance checks +qa: fmtcheck test vet lint coverage cyclo ineffassign misspell structcheck varcheck errcheck gosimple astscan + +# --- INSTALL --- + +# Get the dependencies +deps: + GOPATH=$(GOPATH) go get ./... + GOPATH=$(GOPATH) go get github.com/golang/lint/golint + GOPATH=$(GOPATH) go get github.com/jstemmer/go-junit-report + GOPATH=$(GOPATH) go get github.com/axw/gocov/gocov + GOPATH=$(GOPATH) go get github.com/fzipp/gocyclo + GOPATH=$(GOPATH) go get github.com/gordonklaus/ineffassign + GOPATH=$(GOPATH) go get github.com/client9/misspell/cmd/misspell + GOPATH=$(GOPATH) go get github.com/opennota/check/cmd/structcheck + GOPATH=$(GOPATH) go get github.com/opennota/check/cmd/varcheck + GOPATH=$(GOPATH) go get github.com/kisielk/errcheck + GOPATH=$(GOPATH) go get honnef.co/go/tools/cmd/gosimple + GOPATH=$(GOPATH) go get github.com/GoASTScanner/gas + +# Remove any build artifact +clean: + GOPATH=$(GOPATH) go clean ./... + +# Deletes any intermediate file +nuke: + rm -rf ./target + GOPATH=$(GOPATH) go clean -i ./... diff --git a/vendor/github.com/dgryski/go-farm/README.md b/vendor/github.com/dgryski/go-farm/README.md new file mode 100644 index 0000000..dd07d6f --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/README.md @@ -0,0 +1,41 @@ +# go-farm + +*Google's FarmHash hash functions implemented in Go* + +[![Master Branch](https://img.shields.io/badge/-master:-gray.svg)](https://github.com/dgryski/go-farm/tree/master) +[![Master Build Status](https://secure.travis-ci.org/dgryski/go-farm.png?branch=master)](https://travis-ci.org/dgryski/go-farm?branch=master) +[![Master Coverage Status](https://coveralls.io/repos/dgryski/go-farm/badge.svg?branch=master&service=github)](https://coveralls.io/github/dgryski/go-farm?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/dgryski/go-farm)](https://goreportcard.com/report/github.com/dgryski/go-farm) +[![GoDoc](https://godoc.org/github.com/dgryski/go-farm?status.svg)](http://godoc.org/github.com/dgryski/go-farm) + +## Description + +FarmHash, a family of hash functions. + +This is a (mechanical) translation of the non-SSE4/non-AESNI hash functions from Google's FarmHash (https://github.com/google/farmhash). + + +FarmHash provides hash functions for strings and other data. +The functions mix the input bits thoroughly but are not suitable for cryptography. + +All members of the FarmHash family were designed with heavy reliance on previous work by Jyrki Alakuijala, Austin Appleby, Bob Jenkins, and others. + +For more information please consult https://github.com/google/farmhash + + +## Getting started + +This application is written in Go language, please refer to the guides in https://golang.org for getting started. + +This project include a Makefile that allows you to test and build the project with simple commands. +To see all available options: +```bash +make help +``` + +## Running all tests + +Before committing the code, please check if it passes all tests using +```bash +make qa +``` diff --git a/vendor/github.com/dgryski/go-farm/VERSION b/vendor/github.com/dgryski/go-farm/VERSION new file mode 100644 index 0000000..38f77a6 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/VERSION @@ -0,0 +1 @@ +2.0.1 diff --git a/vendor/github.com/dgryski/go-farm/basics.go b/vendor/github.com/dgryski/go-farm/basics.go new file mode 100644 index 0000000..f27a422 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/basics.go @@ -0,0 +1,30 @@ +package farm + +// Some primes between 2^63 and 2^64 for various uses. +const k0 uint64 = 0xc3a5c85c97cb3127 +const k1 uint64 = 0xb492b66fbe98f273 +const k2 uint64 = 0x9ae16a3b2f90404f + +// Magic numbers for 32-bit hashing. Copied from Murmur3. +const c1 uint32 = 0xcc9e2d51 +const c2 uint32 = 0x1b873593 + +// A 32-bit to 32-bit integer hash copied from Murmur3. +func fmix(h uint32) uint32 { + h ^= h >> 16 + h *= 0x85ebca6b + h ^= h >> 13 + h *= 0xc2b2ae35 + h ^= h >> 16 + return h +} + +func mur(a, h uint32) uint32 { + // Helper from Murmur3 for combining two 32-bit values. + a *= c1 + a = rotate32(a, 17) + a *= c2 + h ^= a + h = rotate32(h, 19) + return h*5 + 0xe6546b64 +} diff --git a/vendor/github.com/dgryski/go-farm/farmhashcc.go b/vendor/github.com/dgryski/go-farm/farmhashcc.go new file mode 100644 index 0000000..12e5c04 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/farmhashcc.go @@ -0,0 +1,199 @@ +package farm + +// This file provides a 32-bit hash equivalent to CityHash32 (v1.1.1) +// and a 128-bit hash equivalent to CityHash128 (v1.1.1). It also provides +// a seeded 32-bit hash function similar to CityHash32. + +func hash32Len13to24Seed(s []byte, seed uint32) uint32 { + slen := len(s) + a := fetch32(s, -4+(slen>>1)) + b := fetch32(s, 4) + c := fetch32(s, slen-8) + d := fetch32(s, (slen >> 1)) + e := fetch32(s, 0) + f := fetch32(s, slen-4) + h := d*c1 + uint32(slen) + seed + a = rotate32(a, 12) + f + h = mur(c, h) + a + a = rotate32(a, 3) + c + h = mur(e, h) + a + a = rotate32(a+f, 12) + d + h = mur(b^seed, h) + a + return fmix(h) +} + +func hash32Len0to4(s []byte, seed uint32) uint32 { + slen := len(s) + b := seed + c := uint32(9) + for i := 0; i < slen; i++ { + v := int8(s[i]) + b = (b * c1) + uint32(v) + c ^= b + } + return fmix(mur(b, mur(uint32(slen), c))) +} + +func hash128to64(x uint128) uint64 { + // Murmur-inspired hashing. + const mul uint64 = 0x9ddfea08eb382d69 + a := (x.lo ^ x.hi) * mul + a ^= (a >> 47) + b := (x.hi ^ a) * mul + b ^= (b >> 47) + b *= mul + return b +} + +type uint128 struct { + lo uint64 + hi uint64 +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in signed long. Based on City and Murmur. +func cityMurmur(s []byte, seed uint128) uint128 { + slen := len(s) + a := seed.lo + b := seed.hi + var c uint64 + var d uint64 + l := slen - 16 + if l <= 0 { // len <= 16 + a = shiftMix(a*k1) * k1 + c = b*k1 + hashLen0to16(s) + if slen >= 8 { + d = shiftMix(a + fetch64(s, 0)) + } else { + d = shiftMix(a + c) + } + } else { // len > 16 + c = hashLen16(fetch64(s, slen-8)+k1, a) + d = hashLen16(b+uint64(slen), c+fetch64(s, slen-16)) + a += d + for { + a ^= shiftMix(fetch64(s, 0)*k1) * k1 + a *= k1 + b ^= a + c ^= shiftMix(fetch64(s, 8)*k1) * k1 + c *= k1 + d ^= c + s = s[16:] + l -= 16 + if l <= 0 { + break + } + } + } + a = hashLen16(a, c) + b = hashLen16(d, b) + return uint128{a ^ b, hashLen16(b, a)} +} + +func cityHash128WithSeed(s []byte, seed uint128) uint128 { + slen := len(s) + if slen < 128 { + return cityMurmur(s, seed) + } + + endIdx := ((slen - 1) / 128) * 128 + lastBlockIdx := endIdx + ((slen - 1) & 127) - 127 + last := s[lastBlockIdx:] + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + var v1, v2 uint64 + var w1, w2 uint64 + x := seed.lo + y := seed.hi + z := uint64(slen) * k1 + v1 = rotate64(y^k1, 49)*k1 + fetch64(s, 0) + v2 = rotate64(v1, 42)*k1 + fetch64(s, 8) + w1 = rotate64(y+z, 35)*k1 + x + w2 = rotate64(x+fetch64(s, 88), 53) * k1 + + // This is the same inner loop as CityHash64(), manually unrolled. + for { + x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1 + y = rotate64(y+v2+fetch64(s, 48), 42) * k1 + x ^= w2 + y += v1 + fetch64(s, 40) + z = rotate64(z+w1, 33) * k1 + v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1) + w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16)) + z, x = x, z + s = s[64:] + x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1 + y = rotate64(y+v2+fetch64(s, 48), 42) * k1 + x ^= w2 + y += v1 + fetch64(s, 40) + z = rotate64(z+w1, 33) * k1 + v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1) + w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16)) + z, x = x, z + s = s[64:] + slen -= 128 + if slen < 128 { + break + } + } + x += rotate64(v1+z, 49) * k0 + y = y*k0 + rotate64(w2, 37) + z = z*k0 + rotate64(w1, 27) + w1 *= 9 + v1 *= k0 + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for tailDone := 0; tailDone < slen; { + tailDone += 32 + y = rotate64(x+y, 42)*k0 + v2 + w1 += fetch64(last, 128-tailDone+16) + x = x*k0 + w1 + z += w2 + fetch64(last, 128-tailDone) + w2 += v1 + v1, v2 = weakHashLen32WithSeeds(last[128-tailDone:], v1+z, v2) + v1 *= k0 + } + + // At this point our 56 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 56-byte-to-8-byte hashes to get a 16-byte final result. + x = hashLen16(x, v1) + y = hashLen16(y+z, w1) + return uint128{hashLen16(x+v2, w2) + y, + hashLen16(x+w2, y+v2)} +} + +func cityHash128(s []byte) uint128 { + slen := len(s) + if slen >= 16 { + return cityHash128WithSeed(s[16:], uint128{fetch64(s, 0), fetch64(s, 8) + k0}) + } + return cityHash128WithSeed(s, uint128{k0, k1}) +} + +// Fingerprint128 is a 128-bit fingerprint function for byte-slices +func Fingerprint128(s []byte) (lo, hi uint64) { + h := cityHash128(s) + return h.lo, h.hi +} + +// Fingerprint64 is a 64-bit fingerprint function for byte-slices +func Fingerprint64(s []byte) uint64 { + return naHash64(s) +} + +// Fingerprint32 is a 32-bit fingerprint function for byte-slices +func Fingerprint32(s []byte) uint32 { + return Hash32(s) +} + +// Hash128 is a 128-bit hash function for byte-slices +func Hash128(s []byte) (lo, hi uint64) { + return Fingerprint128(s) +} + +// Hash128WithSeed is a 128-bit hash function for byte-slices and a 128-bit seed +func Hash128WithSeed(s []byte, seed0, seed1 uint64) (lo, hi uint64) { + h := cityHash128WithSeed(s, uint128{seed0, seed1}) + return h.lo, h.hi +} diff --git a/vendor/github.com/dgryski/go-farm/farmhashmk.go b/vendor/github.com/dgryski/go-farm/farmhashmk.go new file mode 100644 index 0000000..ee41bd7 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/farmhashmk.go @@ -0,0 +1,102 @@ +package farm + +func hash32Len5to12(s []byte, seed uint32) uint32 { + slen := len(s) + a := uint32(len(s)) + b := uint32(len(s) * 5) + c := uint32(9) + d := b + seed + a += fetch32(s, 0) + b += fetch32(s, slen-4) + c += fetch32(s, ((slen >> 1) & 4)) + return fmix(seed ^ mur(c, mur(b, mur(a, d)))) +} + +// Hash32 hashes a byte slice and returns a uint32 hash value +func Hash32(s []byte) uint32 { + + slen := len(s) + + if slen <= 24 { + if slen <= 12 { + if slen <= 4 { + return hash32Len0to4(s, 0) + } + return hash32Len5to12(s, 0) + } + return hash32Len13to24Seed(s, 0) + } + + // len > 24 + h := uint32(slen) + g := c1 * uint32(slen) + f := g + a0 := rotate32(fetch32(s, slen-4)*c1, 17) * c2 + a1 := rotate32(fetch32(s, slen-8)*c1, 17) * c2 + a2 := rotate32(fetch32(s, slen-16)*c1, 17) * c2 + a3 := rotate32(fetch32(s, slen-12)*c1, 17) * c2 + a4 := rotate32(fetch32(s, slen-20)*c1, 17) * c2 + h ^= a0 + h = rotate32(h, 19) + h = h*5 + 0xe6546b64 + h ^= a2 + h = rotate32(h, 19) + h = h*5 + 0xe6546b64 + g ^= a1 + g = rotate32(g, 19) + g = g*5 + 0xe6546b64 + g ^= a3 + g = rotate32(g, 19) + g = g*5 + 0xe6546b64 + f += a4 + f = rotate32(f, 19) + 113 + iters := (slen - 1) / 20 + for { + a := fetch32(s, 0) + b := fetch32(s, 4) + c := fetch32(s, 8) + d := fetch32(s, 12) + e := fetch32(s, 16) + h += a + g += b + f += c + h = mur(d, h) + e + g = mur(c, g) + a + f = mur(b+e*c1, f) + d + f += g + g += f + s = s[20:] + iters-- + if iters == 0 { + break + } + } + g = rotate32(g, 11) * c1 + g = rotate32(g, 17) * c1 + f = rotate32(f, 11) * c1 + f = rotate32(f, 17) * c1 + h = rotate32(h+g, 19) + h = h*5 + 0xe6546b64 + h = rotate32(h, 17) * c1 + h = rotate32(h+f, 19) + h = h*5 + 0xe6546b64 + h = rotate32(h, 17) * c1 + return h +} + +// Hash32WithSeed hashes a byte slice and a uint32 seed and returns a uint32 hash value +func Hash32WithSeed(s []byte, seed uint32) uint32 { + slen := len(s) + + if slen <= 24 { + if slen >= 13 { + return hash32Len13to24Seed(s, seed*c1) + } + if slen >= 5 { + return hash32Len5to12(s, seed) + } + return hash32Len0to4(s, seed) + } + h := hash32Len13to24Seed(s[:24], seed^uint32(slen)) + return mur(Hash32(s[24:])+seed, h) +} diff --git a/vendor/github.com/dgryski/go-farm/farmhashna.go b/vendor/github.com/dgryski/go-farm/farmhashna.go new file mode 100644 index 0000000..574925e --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/farmhashna.go @@ -0,0 +1,156 @@ +package farm + +func shiftMix(val uint64) uint64 { + return val ^ (val >> 47) +} + +func hashLen16(u, v uint64) uint64 { + return hash128to64(uint128{u, v}) +} + +func hashLen16Mul(u, v, mul uint64) uint64 { + // Murmur-inspired hashing. + a := (u ^ v) * mul + a ^= (a >> 47) + b := (v ^ a) * mul + b ^= (b >> 47) + b *= mul + return b +} + +func hashLen0to16(s []byte) uint64 { + slen := uint64(len(s)) + if slen >= 8 { + mul := k2 + slen*2 + a := fetch64(s, 0) + k2 + b := fetch64(s, int(slen-8)) + c := rotate64(b, 37)*mul + a + d := (rotate64(a, 25) + b) * mul + return hashLen16Mul(c, d, mul) + } + + if slen >= 4 { + mul := k2 + slen*2 + a := fetch32(s, 0) + return hashLen16Mul(slen+(uint64(a)<<3), uint64(fetch32(s, int(slen-4))), mul) + } + if slen > 0 { + a := s[0] + b := s[slen>>1] + c := s[slen-1] + y := uint32(a) + (uint32(b) << 8) + z := uint32(slen) + (uint32(c) << 2) + return shiftMix(uint64(y)*k2^uint64(z)*k0) * k2 + } + return k2 +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +func hashLen17to32(s []byte) uint64 { + slen := len(s) + mul := k2 + uint64(slen*2) + a := fetch64(s, 0) * k1 + b := fetch64(s, 8) + c := fetch64(s, slen-8) * mul + d := fetch64(s, slen-16) * k2 + return hashLen16Mul(rotate64(a+b, 43)+rotate64(c, 30)+d, a+rotate64(b+k2, 18)+c, mul) +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +func weakHashLen32WithSeedsWords(w, x, y, z, a, b uint64) (uint64, uint64) { + a += w + b = rotate64(b+a+z, 21) + c := a + a += x + a += y + b += rotate64(a, 44) + return a + z, b + c +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +func weakHashLen32WithSeeds(s []byte, a, b uint64) (uint64, uint64) { + return weakHashLen32WithSeedsWords(fetch64(s, 0), + fetch64(s, 8), + fetch64(s, 16), + fetch64(s, 24), + a, + b) +} + +// Return an 8-byte hash for 33 to 64 bytes. +func hashLen33to64(s []byte) uint64 { + slen := len(s) + mul := k2 + uint64(slen)*2 + a := fetch64(s, 0) * k2 + b := fetch64(s, 8) + c := fetch64(s, slen-8) * mul + d := fetch64(s, slen-16) * k2 + y := rotate64(a+b, 43) + rotate64(c, 30) + d + z := hashLen16Mul(y, a+rotate64(b+k2, 18)+c, mul) + e := fetch64(s, 16) * mul + f := fetch64(s, 24) + g := (y + fetch64(s, slen-32)) * mul + h := (z + fetch64(s, slen-24)) * mul + return hashLen16Mul(rotate64(e+f, 43)+rotate64(g, 30)+h, e+rotate64(f+a, 18)+g, mul) +} + +func naHash64(s []byte) uint64 { + slen := len(s) + var seed uint64 = 81 + if slen <= 32 { + if slen <= 16 { + return hashLen0to16(s) + } + return hashLen17to32(s) + } + if slen <= 64 { + return hashLen33to64(s) + } + // For strings over 64 bytes we loop. + // Internal state consists of 56 bytes: v, w, x, y, and z. + v := uint128{0, 0} + w := uint128{0, 0} + x := seed*k2 + fetch64(s, 0) + y := seed*k1 + 113 + z := shiftMix(y*k2+113) * k2 + // Set end so that after the loop we have 1 to 64 bytes left to process. + endIdx := ((slen - 1) / 64) * 64 + last64Idx := endIdx + ((slen - 1) & 63) - 63 + last64 := s[last64Idx:] + for len(s) > 64 { + x = rotate64(x+y+v.lo+fetch64(s, 8), 37) * k1 + y = rotate64(y+v.hi+fetch64(s, 48), 42) * k1 + x ^= w.hi + y += v.lo + fetch64(s, 40) + z = rotate64(z+w.lo, 33) * k1 + v.lo, v.hi = weakHashLen32WithSeeds(s, v.hi*k1, x+w.lo) + w.lo, w.hi = weakHashLen32WithSeeds(s[32:], z+w.hi, y+fetch64(s, 16)) + x, z = z, x + s = s[64:] + } + mul := k1 + ((z & 0xff) << 1) + // Make s point to the last 64 bytes of input. + s = last64 + w.lo += (uint64(slen-1) & 63) + v.lo += w.lo + w.lo += v.lo + x = rotate64(x+y+v.lo+fetch64(s, 8), 37) * mul + y = rotate64(y+v.hi+fetch64(s, 48), 42) * mul + x ^= w.hi * 9 + y += v.lo*9 + fetch64(s, 40) + z = rotate64(z+w.lo, 33) * mul + v.lo, v.hi = weakHashLen32WithSeeds(s, v.hi*mul, x+w.lo) + w.lo, w.hi = weakHashLen32WithSeeds(s[32:], z+w.hi, y+fetch64(s, 16)) + x, z = z, x + return hashLen16Mul(hashLen16Mul(v.lo, w.lo, mul)+shiftMix(y)*k0+z, hashLen16Mul(v.hi, w.hi, mul)+x, mul) +} + +func naHash64WithSeed(s []byte, seed uint64) uint64 { + return naHash64WithSeeds(s, k2, seed) +} + +func naHash64WithSeeds(s []byte, seed0, seed1 uint64) uint64 { + return hashLen16(naHash64(s)-seed0, seed1) +} diff --git a/vendor/github.com/dgryski/go-farm/farmhashuo.go b/vendor/github.com/dgryski/go-farm/farmhashuo.go new file mode 100644 index 0000000..7bf3d42 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/farmhashuo.go @@ -0,0 +1,117 @@ +package farm + +func uoH(x, y, mul uint64, r uint) uint64 { + a := (x ^ y) * mul + a ^= (a >> 47) + b := (y ^ a) * mul + return rotate64(b, r) * mul +} + +// Hash64WithSeeds hashes a byte slice and two uint64 seeds and returns a uint64 hash value +func Hash64WithSeeds(s []byte, seed0, seed1 uint64) uint64 { + slen := len(s) + if slen <= 64 { + return naHash64WithSeeds(s, seed0, seed1) + } + + // For strings over 64 bytes we loop. + // Internal state consists of 64 bytes: u, v, w, x, y, and z. + x := seed0 + y := seed1*k2 + 113 + z := shiftMix(y*k2) * k2 + v := uint128{seed0, seed1} + var w uint128 + u := x - z + x *= k2 + mul := k2 + (u & 0x82) + + // Set end so that after the loop we have 1 to 64 bytes left to process. + endIdx := ((slen - 1) / 64) * 64 + last64Idx := endIdx + ((slen - 1) & 63) - 63 + last64 := s[last64Idx:] + + for len(s) > 64 { + a0 := fetch64(s, 0) + a1 := fetch64(s, 8) + a2 := fetch64(s, 16) + a3 := fetch64(s, 24) + a4 := fetch64(s, 32) + a5 := fetch64(s, 40) + a6 := fetch64(s, 48) + a7 := fetch64(s, 56) + x += a0 + a1 + y += a2 + z += a3 + v.lo += a4 + v.hi += a5 + a1 + w.lo += a6 + w.hi += a7 + + x = rotate64(x, 26) + x *= 9 + y = rotate64(y, 29) + z *= mul + v.lo = rotate64(v.lo, 33) + v.hi = rotate64(v.hi, 30) + w.lo ^= x + w.lo *= 9 + z = rotate64(z, 32) + z += w.hi + w.hi += z + z *= 9 + u, y = y, u + + z += a0 + a6 + v.lo += a2 + v.hi += a3 + w.lo += a4 + w.hi += a5 + a6 + x += a1 + y += a7 + + y += v.lo + v.lo += x - y + v.hi += w.lo + w.lo += v.hi + w.hi += x - y + x += w.hi + w.hi = rotate64(w.hi, 34) + u, z = z, u + s = s[64:] + } + // Make s point to the last 64 bytes of input. + s = last64 + u *= 9 + v.hi = rotate64(v.hi, 28) + v.lo = rotate64(v.lo, 20) + w.lo += (uint64(slen-1) & 63) + u += y + y += u + x = rotate64(y-x+v.lo+fetch64(s, 8), 37) * mul + y = rotate64(y^v.hi^fetch64(s, 48), 42) * mul + x ^= w.hi * 9 + y += v.lo + fetch64(s, 40) + z = rotate64(z+w.lo, 33) * mul + v.lo, v.hi = weakHashLen32WithSeeds(s, v.hi*mul, x+w.lo) + w.lo, w.hi = weakHashLen32WithSeeds(s[32:], z+w.hi, y+fetch64(s, 16)) + return uoH(hashLen16Mul(v.lo+x, w.lo^y, mul)+z-u, + uoH(v.hi+y, w.hi+z, k2, 30)^x, + k2, + 31) +} + +// Hash64WithSeed hashes a byte slice and a uint64 seed and returns a uint64 hash value +func Hash64WithSeed(s []byte, seed uint64) uint64 { + if len(s) <= 64 { + return naHash64WithSeed(s, seed) + } + return Hash64WithSeeds(s, 0, seed) +} + +// Hash64 hashes a byte slice and returns a uint64 hash value +func Hash64(s []byte) uint64 { + if len(s) <= 64 { + return naHash64(s) + } + return Hash64WithSeeds(s, 81, 0) +} diff --git a/vendor/github.com/dgryski/go-farm/platform.go b/vendor/github.com/dgryski/go-farm/platform.go new file mode 100644 index 0000000..6ddf458 --- /dev/null +++ b/vendor/github.com/dgryski/go-farm/platform.go @@ -0,0 +1,18 @@ +package farm + +func rotate32(val uint32, shift uint) uint32 { + return ((val >> shift) | (val << (32 - shift))) +} + +func rotate64(val uint64, shift uint) uint64 { + return ((val >> shift) | (val << (64 - shift))) +} + +func fetch32(s []byte, idx int) uint32 { + return uint32(s[idx+0]) | uint32(s[idx+1])<<8 | uint32(s[idx+2])<<16 | uint32(s[idx+3])<<24 +} + +func fetch64(s []byte, idx int) uint64 { + return uint64(s[idx+0]) | uint64(s[idx+1])<<8 | uint64(s[idx+2])<<16 | uint64(s[idx+3])<<24 | + uint64(s[idx+4])<<32 | uint64(s[idx+5])<<40 | uint64(s[idx+6])<<48 | uint64(s[idx+7])<<56 +} diff --git a/vendor/github.com/eapache/channels/.gitignore b/vendor/github.com/eapache/channels/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/vendor/github.com/eapache/channels/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/eapache/channels/.travis.yml b/vendor/github.com/eapache/channels/.travis.yml new file mode 100644 index 0000000..b072a4c --- /dev/null +++ b/vendor/github.com/eapache/channels/.travis.yml @@ -0,0 +1,11 @@ +language: go +sudo: false + +script: go test -v -race -timeout 10s ./... + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 diff --git a/vendor/github.com/eapache/channels/CHANGELOG.md b/vendor/github.com/eapache/channels/CHANGELOG.md new file mode 100644 index 0000000..63825cd --- /dev/null +++ b/vendor/github.com/eapache/channels/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +#### Version 1.1.0 (2015-11-22) + +Bug Fixes: + - The `Len()` and `Cap()` methods on several implementations were racy + ([#18](https://github.com/eapache/channels/issues/18)). + +Note: Fixing the above issue led to a fairly substantial performance hit +(anywhere from 10-25% in benchmarks depending on use case) and involved fairly +major refactoring, which is why this is being released as v1.1.0 instead +of v1.0.1. + +#### Version 1.0.0 (2015-01-24) + +Version 1.0.0 is the first tagged release. All core functionality was available +at this point. diff --git a/vendor/github.com/eapache/channels/LICENSE b/vendor/github.com/eapache/channels/LICENSE new file mode 100644 index 0000000..8c4bddf --- /dev/null +++ b/vendor/github.com/eapache/channels/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/eapache/channels/README.md b/vendor/github.com/eapache/channels/README.md new file mode 100644 index 0000000..aab2a53 --- /dev/null +++ b/vendor/github.com/eapache/channels/README.md @@ -0,0 +1,27 @@ +channels +======== + +[![Build Status](https://travis-ci.org/eapache/channels.svg?branch=master)](https://travis-ci.org/eapache/channels) +[![GoDoc](https://godoc.org/github.com/eapache/channels?status.png)](https://godoc.org/github.com/eapache/channels) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +A collection of helper functions and special types for working with and +extending [Go](https://golang.org/)'s existing channels. Due to limitations +of Go's type system, importing this library directly is often not practical for +production code. It serves equally well, however, as a reference guide and +template for implementing many common idioms; if you use it in this way I would +appreciate the inclusion of some sort of credit in the resulting code. + +See https://godoc.org/github.com/eapache/channels for full documentation or +https://gopkg.in/eapache/channels.v1 for a versioned import path. + +Requires Go version 1.1 or later, as certain necessary elements of the `reflect` +package were not present in 1.0. + +Most of the buffered channel types in this package are backed by a very fast +queue implementation that used to be built into this package but has now been +extracted into its own package at https://github.com/eapache/queue. + +*Note:* Several types in this package provide so-called "infinite" buffers. Be +very careful using these, as no buffer is truly infinite. If such a buffer +grows too large your program will run out of memory and crash. Caveat emptor. diff --git a/vendor/github.com/eapache/channels/batching_channel.go b/vendor/github.com/eapache/channels/batching_channel.go new file mode 100644 index 0000000..5be622f --- /dev/null +++ b/vendor/github.com/eapache/channels/batching_channel.go @@ -0,0 +1,87 @@ +package channels + +// BatchingChannel implements the Channel interface, with the change that instead of producing individual elements +// on Out(), it batches together the entire internal buffer each time. Trying to construct an unbuffered batching channel +// will panic, that configuration is not supported (and provides no benefit over an unbuffered NativeChannel). +type BatchingChannel struct { + input, output chan interface{} + length chan int + buffer []interface{} + size BufferCap +} + +func NewBatchingChannel(size BufferCap) *BatchingChannel { + if size == None { + panic("channels: BatchingChannel does not support unbuffered behaviour") + } + if size < 0 && size != Infinity { + panic("channels: invalid negative size in NewBatchingChannel") + } + ch := &BatchingChannel{ + input: make(chan interface{}), + output: make(chan interface{}), + length: make(chan int), + size: size, + } + go ch.batchingBuffer() + return ch +} + +func (ch *BatchingChannel) In() chan<- interface{} { + return ch.input +} + +// Out returns a <-chan interface{} in order that BatchingChannel conforms to the standard Channel interface provided +// by this package, however each output value is guaranteed to be of type []interface{} - a slice collecting the most +// recent batch of values sent on the In channel. The slice is guaranteed to not be empty or nil. In practice the net +// result is that you need an additional type assertion to access the underlying values. +func (ch *BatchingChannel) Out() <-chan interface{} { + return ch.output +} + +func (ch *BatchingChannel) Len() int { + return <-ch.length +} + +func (ch *BatchingChannel) Cap() BufferCap { + return ch.size +} + +func (ch *BatchingChannel) Close() { + close(ch.input) +} + +func (ch *BatchingChannel) batchingBuffer() { + var input, output, nextInput chan interface{} + nextInput = ch.input + input = nextInput + + for input != nil || output != nil { + select { + case elem, open := <-input: + if open { + ch.buffer = append(ch.buffer, elem) + } else { + input = nil + nextInput = nil + } + case output <- ch.buffer: + ch.buffer = nil + case ch.length <- len(ch.buffer): + } + + if len(ch.buffer) == 0 { + input = nextInput + output = nil + } else if ch.size != Infinity && len(ch.buffer) >= int(ch.size) { + input = nil + output = ch.output + } else { + input = nextInput + output = ch.output + } + } + + close(ch.output) + close(ch.length) +} diff --git a/vendor/github.com/eapache/channels/black_hole.go b/vendor/github.com/eapache/channels/black_hole.go new file mode 100644 index 0000000..0d1ba97 --- /dev/null +++ b/vendor/github.com/eapache/channels/black_hole.go @@ -0,0 +1,54 @@ +package channels + +// BlackHole implements the InChannel interface and provides an analogue for the "Discard" variable in +// the ioutil package - it never blocks, and simply discards every value it reads. The number of items +// discarded in this way is counted and returned from Len. +type BlackHole struct { + input chan interface{} + length chan int + count int +} + +func NewBlackHole() *BlackHole { + ch := &BlackHole{ + input: make(chan interface{}), + length: make(chan int), + } + go ch.discard() + return ch +} + +func (ch *BlackHole) In() chan<- interface{} { + return ch.input +} + +func (ch *BlackHole) Len() int { + val, open := <-ch.length + if open { + return val + } else { + return ch.count + } +} + +func (ch *BlackHole) Cap() BufferCap { + return Infinity +} + +func (ch *BlackHole) Close() { + close(ch.input) +} + +func (ch *BlackHole) discard() { + for { + select { + case _, open := <-ch.input: + if !open { + close(ch.length) + return + } + ch.count++ + case ch.length <- ch.count: + } + } +} diff --git a/vendor/github.com/eapache/channels/channels.go b/vendor/github.com/eapache/channels/channels.go new file mode 100644 index 0000000..efcb2b5 --- /dev/null +++ b/vendor/github.com/eapache/channels/channels.go @@ -0,0 +1,277 @@ +/* +Package channels provides a collection of helper functions, interfaces and implementations for +working with and extending the capabilities of golang's existing channels. The main interface of +interest is Channel, though sub-interfaces are also provided for cases where the full Channel interface +cannot be met (for example, InChannel for write-only channels). + +For integration with native typed golang channels, functions Wrap and Unwrap are provided which do the +appropriate type conversions. The NativeChannel, NativeInChannel and NativeOutChannel type definitions +are also provided for use with native channels which already carry values of type interface{}. + +The heart of the package consists of several distinct implementations of the Channel interface, including +channels backed by special buffers (resizable, infinite, ring buffers, etc) and other useful types. A +"black hole" channel for discarding unwanted values (similar in purpose to ioutil.Discard or /dev/null) +rounds out the set. + +Helper functions for operating on Channels include Pipe and Tee (which behave much like their Unix +namesakes), as well as Multiplex and Distribute. "Weak" versions of these functions also exist, which +do not close their output channel(s) on completion. + +Due to limitations of Go's type system, importing this library directly is often not practical for +production code. It serves equally well, however, as a reference guide and template for implementing +many common idioms; if you use it in this way I would appreciate the inclusion of some sort of credit +in the resulting code. + +Warning: several types in this package provide so-called "infinite" buffers. Be *very* careful using +these, as no buffer is truly infinite - if such a buffer grows too large your program will run out of +memory and crash. Caveat emptor. +*/ +package channels + +import "reflect" + +// BufferCap represents the capacity of the buffer backing a channel. Valid values consist of all +// positive integers, as well as the special values below. +type BufferCap int + +const ( + // None is the capacity for channels that have no buffer at all. + None BufferCap = 0 + // Infinity is the capacity for channels with no limit on their buffer size. + Infinity BufferCap = -1 +) + +// Buffer is an interface for any channel that provides access to query the state of its buffer. +// Even unbuffered channels can implement this interface by simply returning 0 from Len() and None from Cap(). +type Buffer interface { + Len() int // The number of elements currently buffered. + Cap() BufferCap // The maximum number of elements that can be buffered. +} + +// SimpleInChannel is an interface representing a writeable channel that does not necessarily +// implement the Buffer interface. +type SimpleInChannel interface { + In() chan<- interface{} // The writeable end of the channel. + Close() // Closes the channel. It is an error to write to In() after calling Close(). +} + +// InChannel is an interface representing a writeable channel with a buffer. +type InChannel interface { + SimpleInChannel + Buffer +} + +// SimpleOutChannel is an interface representing a readable channel that does not necessarily +// implement the Buffer interface. +type SimpleOutChannel interface { + Out() <-chan interface{} // The readable end of the channel. +} + +// OutChannel is an interface representing a readable channel implementing the Buffer interface. +type OutChannel interface { + SimpleOutChannel + Buffer +} + +// SimpleChannel is an interface representing a channel that is both readable and writeable, +// but does not necessarily implement the Buffer interface. +type SimpleChannel interface { + SimpleInChannel + SimpleOutChannel +} + +// Channel is an interface representing a channel that is readable, writeable and implements +// the Buffer interface +type Channel interface { + SimpleChannel + Buffer +} + +func pipe(input SimpleOutChannel, output SimpleInChannel, closeWhenDone bool) { + for elem := range input.Out() { + output.In() <- elem + } + if closeWhenDone { + output.Close() + } +} + +func multiplex(output SimpleInChannel, inputs []SimpleOutChannel, closeWhenDone bool) { + inputCount := len(inputs) + cases := make([]reflect.SelectCase, inputCount) + for i := range cases { + cases[i].Dir = reflect.SelectRecv + cases[i].Chan = reflect.ValueOf(inputs[i].Out()) + } + for inputCount > 0 { + chosen, recv, recvOK := reflect.Select(cases) + if recvOK { + output.In() <- recv.Interface() + } else { + cases[chosen].Chan = reflect.ValueOf(nil) + inputCount-- + } + } + if closeWhenDone { + output.Close() + } +} + +func tee(input SimpleOutChannel, outputs []SimpleInChannel, closeWhenDone bool) { + cases := make([]reflect.SelectCase, len(outputs)) + for i := range cases { + cases[i].Dir = reflect.SelectSend + } + for elem := range input.Out() { + for i := range cases { + cases[i].Chan = reflect.ValueOf(outputs[i].In()) + cases[i].Send = reflect.ValueOf(elem) + } + for _ = range cases { + chosen, _, _ := reflect.Select(cases) + cases[chosen].Chan = reflect.ValueOf(nil) + } + } + if closeWhenDone { + for i := range outputs { + outputs[i].Close() + } + } +} + +func distribute(input SimpleOutChannel, outputs []SimpleInChannel, closeWhenDone bool) { + cases := make([]reflect.SelectCase, len(outputs)) + for i := range cases { + cases[i].Dir = reflect.SelectSend + cases[i].Chan = reflect.ValueOf(outputs[i].In()) + } + for elem := range input.Out() { + for i := range cases { + cases[i].Send = reflect.ValueOf(elem) + } + reflect.Select(cases) + } + if closeWhenDone { + for i := range outputs { + outputs[i].Close() + } + } +} + +// Pipe connects the input channel to the output channel so that +// they behave as if a single channel. +func Pipe(input SimpleOutChannel, output SimpleInChannel) { + go pipe(input, output, true) +} + +// Multiplex takes an arbitrary number of input channels and multiplexes their output into a single output +// channel. When all input channels have been closed, the output channel is closed. Multiplex with a single +// input channel is equivalent to Pipe (though slightly less efficient). +func Multiplex(output SimpleInChannel, inputs ...SimpleOutChannel) { + if len(inputs) == 0 { + panic("channels: Multiplex requires at least one input") + } + go multiplex(output, inputs, true) +} + +// Tee (like its Unix namesake) takes a single input channel and an arbitrary number of output channels +// and duplicates each input into every output. When the input channel is closed, all outputs channels are closed. +// Tee with a single output channel is equivalent to Pipe (though slightly less efficient). +func Tee(input SimpleOutChannel, outputs ...SimpleInChannel) { + if len(outputs) == 0 { + panic("channels: Tee requires at least one output") + } + go tee(input, outputs, true) +} + +// Distribute takes a single input channel and an arbitrary number of output channels and duplicates each input +// into *one* available output. If multiple outputs are waiting for a value, one is chosen at random. When the +// input channel is closed, all outputs channels are closed. Distribute with a single output channel is +// equivalent to Pipe (though slightly less efficient). +func Distribute(input SimpleOutChannel, outputs ...SimpleInChannel) { + if len(outputs) == 0 { + panic("channels: Distribute requires at least one output") + } + go distribute(input, outputs, true) +} + +// WeakPipe behaves like Pipe (connecting the two channels) except that it does not close +// the output channel when the input channel is closed. +func WeakPipe(input SimpleOutChannel, output SimpleInChannel) { + go pipe(input, output, false) +} + +// WeakMultiplex behaves like Multiplex (multiplexing multiple inputs into a single output) except that it does not close +// the output channel when the input channels are closed. +func WeakMultiplex(output SimpleInChannel, inputs ...SimpleOutChannel) { + if len(inputs) == 0 { + panic("channels: WeakMultiplex requires at least one input") + } + go multiplex(output, inputs, false) +} + +// WeakTee behaves like Tee (duplicating a single input into multiple outputs) except that it does not close +// the output channels when the input channel is closed. +func WeakTee(input SimpleOutChannel, outputs ...SimpleInChannel) { + if len(outputs) == 0 { + panic("channels: WeakTee requires at least one output") + } + go tee(input, outputs, false) +} + +// WeakDistribute behaves like Distribute (distributing a single input amongst multiple outputs) except that +// it does not close the output channels when the input channel is closed. +func WeakDistribute(input SimpleOutChannel, outputs ...SimpleInChannel) { + if len(outputs) == 0 { + panic("channels: WeakDistribute requires at least one output") + } + go distribute(input, outputs, false) +} + +// Wrap takes any readable channel type (chan or <-chan but not chan<-) and +// exposes it as a SimpleOutChannel for easy integration with existing channel sources. +// It panics if the input is not a readable channel. +func Wrap(ch interface{}) SimpleOutChannel { + t := reflect.TypeOf(ch) + if t.Kind() != reflect.Chan || t.ChanDir()&reflect.RecvDir == 0 { + panic("channels: input to Wrap must be readable channel") + } + realChan := make(chan interface{}) + + go func() { + v := reflect.ValueOf(ch) + for { + x, ok := v.Recv() + if !ok { + close(realChan) + return + } + realChan <- x.Interface() + } + }() + + return NativeOutChannel(realChan) +} + +// Unwrap takes a SimpleOutChannel and uses reflection to pipe it to a typed native channel for +// easy integration with existing channel sources. Output can be any writable channel type (chan or chan<-). +// It panics if the output is not a writable channel, or if a value is received that cannot be sent on the +// output channel. +func Unwrap(input SimpleOutChannel, output interface{}) { + t := reflect.TypeOf(output) + if t.Kind() != reflect.Chan || t.ChanDir()&reflect.SendDir == 0 { + panic("channels: input to Unwrap must be readable channel") + } + + go func() { + v := reflect.ValueOf(output) + for { + x, ok := <-input.Out() + if !ok { + v.Close() + return + } + v.Send(reflect.ValueOf(x)) + } + }() +} diff --git a/vendor/github.com/eapache/channels/infinite_channel.go b/vendor/github.com/eapache/channels/infinite_channel.go new file mode 100644 index 0000000..3aa9e8e --- /dev/null +++ b/vendor/github.com/eapache/channels/infinite_channel.go @@ -0,0 +1,72 @@ +package channels + +import "github.com/eapache/queue" + +// InfiniteChannel implements the Channel interface with an infinite buffer between the input and the output. +type InfiniteChannel struct { + input, output chan interface{} + length chan int + buffer *queue.Queue +} + +func NewInfiniteChannel() *InfiniteChannel { + ch := &InfiniteChannel{ + input: make(chan interface{}), + output: make(chan interface{}), + length: make(chan int), + buffer: queue.New(), + } + go ch.infiniteBuffer() + return ch +} + +func (ch *InfiniteChannel) In() chan<- interface{} { + return ch.input +} + +func (ch *InfiniteChannel) Out() <-chan interface{} { + return ch.output +} + +func (ch *InfiniteChannel) Len() int { + return <-ch.length +} + +func (ch *InfiniteChannel) Cap() BufferCap { + return Infinity +} + +func (ch *InfiniteChannel) Close() { + close(ch.input) +} + +func (ch *InfiniteChannel) infiniteBuffer() { + var input, output chan interface{} + var next interface{} + input = ch.input + + for input != nil || output != nil { + select { + case elem, open := <-input: + if open { + ch.buffer.Add(elem) + } else { + input = nil + } + case output <- next: + ch.buffer.Remove() + case ch.length <- ch.buffer.Length(): + } + + if ch.buffer.Length() > 0 { + output = ch.output + next = ch.buffer.Peek() + } else { + output = nil + next = nil + } + } + + close(ch.output) + close(ch.length) +} diff --git a/vendor/github.com/eapache/channels/native_channel.go b/vendor/github.com/eapache/channels/native_channel.go new file mode 100644 index 0000000..3807a19 --- /dev/null +++ b/vendor/github.com/eapache/channels/native_channel.go @@ -0,0 +1,92 @@ +package channels + +// NativeInChannel implements the InChannel interface by wrapping a native go write-only channel. +type NativeInChannel chan<- interface{} + +func (ch NativeInChannel) In() chan<- interface{} { + return ch +} + +func (ch NativeInChannel) Len() int { + return len(ch) +} + +func (ch NativeInChannel) Cap() BufferCap { + return BufferCap(cap(ch)) +} + +func (ch NativeInChannel) Close() { + close(ch) +} + +// NativeOutChannel implements the OutChannel interface by wrapping a native go read-only channel. +type NativeOutChannel <-chan interface{} + +func (ch NativeOutChannel) Out() <-chan interface{} { + return ch +} + +func (ch NativeOutChannel) Len() int { + return len(ch) +} + +func (ch NativeOutChannel) Cap() BufferCap { + return BufferCap(cap(ch)) +} + +// NativeChannel implements the Channel interface by wrapping a native go channel. +type NativeChannel chan interface{} + +// NewNativeChannel makes a new NativeChannel with the given buffer size. Just a convenience wrapper +// to avoid having to cast the result of make(). +func NewNativeChannel(size BufferCap) NativeChannel { + return make(chan interface{}, size) +} + +func (ch NativeChannel) In() chan<- interface{} { + return ch +} + +func (ch NativeChannel) Out() <-chan interface{} { + return ch +} + +func (ch NativeChannel) Len() int { + return len(ch) +} + +func (ch NativeChannel) Cap() BufferCap { + return BufferCap(cap(ch)) +} + +func (ch NativeChannel) Close() { + close(ch) +} + +// DeadChannel is a placeholder implementation of the Channel interface with no buffer +// that is never ready for reading or writing. Closing a dead channel is a no-op. +// Behaves almost like NativeChannel(nil) except that closing a nil NativeChannel will panic. +type DeadChannel struct{} + +func NewDeadChannel() DeadChannel { + return DeadChannel{} +} + +func (ch DeadChannel) In() chan<- interface{} { + return nil +} + +func (ch DeadChannel) Out() <-chan interface{} { + return nil +} + +func (ch DeadChannel) Len() int { + return 0 +} + +func (ch DeadChannel) Cap() BufferCap { + return BufferCap(0) +} + +func (ch DeadChannel) Close() { +} diff --git a/vendor/github.com/eapache/channels/overflowing_channel.go b/vendor/github.com/eapache/channels/overflowing_channel.go new file mode 100644 index 0000000..35090f8 --- /dev/null +++ b/vendor/github.com/eapache/channels/overflowing_channel.go @@ -0,0 +1,113 @@ +package channels + +import "github.com/eapache/queue" + +// OverflowingChannel implements the Channel interface in a way that never blocks the writer. +// Specifically, if a value is written to an OverflowingChannel when its buffer is full +// (or, in an unbuffered case, when the recipient is not ready) then that value is simply discarded. +// Note that Go's scheduler can cause discarded values when they could be avoided, simply by scheduling +// the writer before the reader, so caveat emptor. +// For the opposite behaviour (discarding the oldest element, not the newest) see RingChannel. +type OverflowingChannel struct { + input, output chan interface{} + length chan int + buffer *queue.Queue + size BufferCap +} + +func NewOverflowingChannel(size BufferCap) *OverflowingChannel { + if size < 0 && size != Infinity { + panic("channels: invalid negative size in NewOverflowingChannel") + } + ch := &OverflowingChannel{ + input: make(chan interface{}), + output: make(chan interface{}), + length: make(chan int), + size: size, + } + if size == None { + go ch.overflowingDirect() + } else { + ch.buffer = queue.New() + go ch.overflowingBuffer() + } + return ch +} + +func (ch *OverflowingChannel) In() chan<- interface{} { + return ch.input +} + +func (ch *OverflowingChannel) Out() <-chan interface{} { + return ch.output +} + +func (ch *OverflowingChannel) Len() int { + if ch.size == None { + return 0 + } else { + return <-ch.length + } +} + +func (ch *OverflowingChannel) Cap() BufferCap { + return ch.size +} + +func (ch *OverflowingChannel) Close() { + close(ch.input) +} + +// for entirely unbuffered cases +func (ch *OverflowingChannel) overflowingDirect() { + for elem := range ch.input { + // if we can't write it immediately, drop it and move on + select { + case ch.output <- elem: + default: + } + } + close(ch.output) +} + +// for all buffered cases +func (ch *OverflowingChannel) overflowingBuffer() { + var input, output chan interface{} + var next interface{} + input = ch.input + + for input != nil || output != nil { + select { + // Prefer to write if possible, which is surprisingly effective in reducing + // dropped elements due to overflow. The naive read/write select chooses randomly + // when both channels are ready, which produces unnecessary drops 50% of the time. + case output <- next: + ch.buffer.Remove() + default: + select { + case elem, open := <-input: + if open { + if ch.size == Infinity || ch.buffer.Length() < int(ch.size) { + ch.buffer.Add(elem) + } + } else { + input = nil + } + case output <- next: + ch.buffer.Remove() + case ch.length <- ch.buffer.Length(): + } + } + + if ch.buffer.Length() > 0 { + output = ch.output + next = ch.buffer.Peek() + } else { + output = nil + next = nil + } + } + + close(ch.output) + close(ch.length) +} diff --git a/vendor/github.com/eapache/channels/resizable_channel.go b/vendor/github.com/eapache/channels/resizable_channel.go new file mode 100644 index 0000000..fafed0a --- /dev/null +++ b/vendor/github.com/eapache/channels/resizable_channel.go @@ -0,0 +1,109 @@ +package channels + +import "github.com/eapache/queue" + +// ResizableChannel implements the Channel interface with a resizable buffer between the input and the output. +// The channel initially has a buffer size of 1, but can be resized by calling Resize(). +// +// Resizing to a buffer capacity of None is, unfortunately, not supported and will panic +// (see https://github.com/eapache/channels/issues/1). +// Resizing back and forth between a finite and infinite buffer is fully supported. +type ResizableChannel struct { + input, output chan interface{} + length chan int + capacity, resize chan BufferCap + size BufferCap + buffer *queue.Queue +} + +func NewResizableChannel() *ResizableChannel { + ch := &ResizableChannel{ + input: make(chan interface{}), + output: make(chan interface{}), + length: make(chan int), + capacity: make(chan BufferCap), + resize: make(chan BufferCap), + size: 1, + buffer: queue.New(), + } + go ch.magicBuffer() + return ch +} + +func (ch *ResizableChannel) In() chan<- interface{} { + return ch.input +} + +func (ch *ResizableChannel) Out() <-chan interface{} { + return ch.output +} + +func (ch *ResizableChannel) Len() int { + return <-ch.length +} + +func (ch *ResizableChannel) Cap() BufferCap { + val, open := <-ch.capacity + if open { + return val + } else { + return ch.size + } +} + +func (ch *ResizableChannel) Close() { + close(ch.input) +} + +func (ch *ResizableChannel) Resize(newSize BufferCap) { + if newSize == None { + panic("channels: ResizableChannel does not support unbuffered behaviour") + } + if newSize < 0 && newSize != Infinity { + panic("channels: invalid negative size trying to resize channel") + } + ch.resize <- newSize +} + +func (ch *ResizableChannel) magicBuffer() { + var input, output, nextInput chan interface{} + var next interface{} + nextInput = ch.input + input = nextInput + + for input != nil || output != nil { + select { + case elem, open := <-input: + if open { + ch.buffer.Add(elem) + } else { + input = nil + nextInput = nil + } + case output <- next: + ch.buffer.Remove() + case ch.size = <-ch.resize: + case ch.length <- ch.buffer.Length(): + case ch.capacity <- ch.size: + } + + if ch.buffer.Length() == 0 { + output = nil + next = nil + } else { + output = ch.output + next = ch.buffer.Peek() + } + + if ch.size != Infinity && ch.buffer.Length() >= int(ch.size) { + input = nil + } else { + input = nextInput + } + } + + close(ch.output) + close(ch.resize) + close(ch.length) + close(ch.capacity) +} diff --git a/vendor/github.com/eapache/channels/ring_channel.go b/vendor/github.com/eapache/channels/ring_channel.go new file mode 100644 index 0000000..7aec207 --- /dev/null +++ b/vendor/github.com/eapache/channels/ring_channel.go @@ -0,0 +1,114 @@ +package channels + +import "github.com/eapache/queue" + +// RingChannel implements the Channel interface in a way that never blocks the writer. +// Specifically, if a value is written to a RingChannel when its buffer is full then the oldest +// value in the buffer is discarded to make room (just like a standard ring-buffer). +// Note that Go's scheduler can cause discarded values when they could be avoided, simply by scheduling +// the writer before the reader, so caveat emptor. +// For the opposite behaviour (discarding the newest element, not the oldest) see OverflowingChannel. +type RingChannel struct { + input, output chan interface{} + length chan int + buffer *queue.Queue + size BufferCap +} + +func NewRingChannel(size BufferCap) *RingChannel { + if size < 0 && size != Infinity { + panic("channels: invalid negative size in NewRingChannel") + } + ch := &RingChannel{ + input: make(chan interface{}), + output: make(chan interface{}), + buffer: queue.New(), + size: size, + } + if size == None { + go ch.overflowingDirect() + } else { + ch.length = make(chan int) + go ch.ringBuffer() + } + return ch +} + +func (ch *RingChannel) In() chan<- interface{} { + return ch.input +} + +func (ch *RingChannel) Out() <-chan interface{} { + return ch.output +} + +func (ch *RingChannel) Len() int { + if ch.size == None { + return 0 + } else { + return <-ch.length + } +} + +func (ch *RingChannel) Cap() BufferCap { + return ch.size +} + +func (ch *RingChannel) Close() { + close(ch.input) +} + +// for entirely unbuffered cases +func (ch *RingChannel) overflowingDirect() { + for elem := range ch.input { + // if we can't write it immediately, drop it and move on + select { + case ch.output <- elem: + default: + } + } + close(ch.output) +} + +// for all buffered cases +func (ch *RingChannel) ringBuffer() { + var input, output chan interface{} + var next interface{} + input = ch.input + + for input != nil || output != nil { + select { + // Prefer to write if possible, which is surprisingly effective in reducing + // dropped elements due to overflow. The naive read/write select chooses randomly + // when both channels are ready, which produces unnecessary drops 50% of the time. + case output <- next: + ch.buffer.Remove() + default: + select { + case elem, open := <-input: + if open { + ch.buffer.Add(elem) + if ch.size != Infinity && ch.buffer.Length() > int(ch.size) { + ch.buffer.Remove() + } + } else { + input = nil + } + case output <- next: + ch.buffer.Remove() + case ch.length <- ch.buffer.Length(): + } + } + + if ch.buffer.Length() > 0 { + output = ch.output + next = ch.buffer.Peek() + } else { + output = nil + next = nil + } + } + + close(ch.output) + close(ch.length) +} diff --git a/vendor/github.com/eapache/channels/shared_buffer.go b/vendor/github.com/eapache/channels/shared_buffer.go new file mode 100644 index 0000000..556dc19 --- /dev/null +++ b/vendor/github.com/eapache/channels/shared_buffer.go @@ -0,0 +1,167 @@ +package channels + +import ( + "reflect" + + "github.com/eapache/queue" +) + +//sharedBufferChannel implements SimpleChannel and is created by the public +//SharedBuffer type below +type sharedBufferChannel struct { + in chan interface{} + out chan interface{} + buf *queue.Queue + closed bool +} + +func (sch *sharedBufferChannel) In() chan<- interface{} { + return sch.in +} + +func (sch *sharedBufferChannel) Out() <-chan interface{} { + return sch.out +} + +func (sch *sharedBufferChannel) Close() { + close(sch.in) +} + +//SharedBuffer implements the Buffer interface, and permits multiple SimpleChannel instances to "share" a single buffer. +//Each channel spawned by NewChannel has its own internal queue (so values flowing through do not get mixed up with +//other channels) but the total number of elements buffered by all spawned channels is limited to a single capacity. This +//means *all* such channels block and unblock for writing together. The primary use case is for implementing pipeline-style +//parallelism with goroutines, limiting the total number of elements in the pipeline without limiting the number of elements +//at any particular step. +type SharedBuffer struct { + cases []reflect.SelectCase // 2n+1 of these; [0] is for control, [1,3,5...] for recv, [2,4,6...] for send + chans []*sharedBufferChannel // n of these + count int + size BufferCap + in chan *sharedBufferChannel +} + +func NewSharedBuffer(size BufferCap) *SharedBuffer { + if size < 0 && size != Infinity { + panic("channels: invalid negative size in NewSharedBuffer") + } else if size == None { + panic("channels: SharedBuffer does not support unbuffered behaviour") + } + + buf := &SharedBuffer{ + size: size, + in: make(chan *sharedBufferChannel), + } + + buf.cases = append(buf.cases, reflect.SelectCase{ + Dir: reflect.SelectRecv, + Chan: reflect.ValueOf(buf.in), + }) + + go buf.mainLoop() + + return buf +} + +//NewChannel spawns and returns a new channel sharing the underlying buffer. +func (buf *SharedBuffer) NewChannel() SimpleChannel { + ch := &sharedBufferChannel{ + in: make(chan interface{}), + out: make(chan interface{}), + buf: queue.New(), + } + buf.in <- ch + return ch +} + +//Close shuts down the SharedBuffer. It is an error to call Close while channels are still using +//the buffer (I'm not really sure what would happen if you do so). +func (buf *SharedBuffer) Close() { + // TODO: what if there are still active channels using this buffer? + close(buf.in) +} + +func (buf *SharedBuffer) mainLoop() { + for { + i, val, ok := reflect.Select(buf.cases) + + if i == 0 { + if !ok { + //Close was called on the SharedBuffer itself + return + } + + //NewChannel was called on the SharedBuffer + ch := val.Interface().(*sharedBufferChannel) + buf.chans = append(buf.chans, ch) + buf.cases = append(buf.cases, + reflect.SelectCase{Dir: reflect.SelectRecv}, + reflect.SelectCase{Dir: reflect.SelectSend}, + ) + if buf.size == Infinity || buf.count < int(buf.size) { + buf.cases[len(buf.cases)-2].Chan = reflect.ValueOf(ch.in) + } + } else if i%2 == 0 { + //Send + if buf.count == int(buf.size) { + //room in the buffer again, re-enable all recv cases + for j := range buf.chans { + if !buf.chans[j].closed { + buf.cases[(j*2)+1].Chan = reflect.ValueOf(buf.chans[j].in) + } + } + } + buf.count-- + ch := buf.chans[(i-1)/2] + if ch.buf.Length() > 0 { + buf.cases[i].Send = reflect.ValueOf(ch.buf.Peek()) + ch.buf.Remove() + } else { + //nothing left for this channel to send, disable sending + buf.cases[i].Chan = reflect.Value{} + buf.cases[i].Send = reflect.Value{} + if ch.closed { + // and it was closed, so close the output channel + //TODO: shrink slice + close(ch.out) + } + } + } else { + ch := buf.chans[i/2] + if ok { + //Receive + buf.count++ + if ch.buf.Length() == 0 && !buf.cases[i+1].Chan.IsValid() { + //this channel now has something to send + buf.cases[i+1].Chan = reflect.ValueOf(ch.out) + buf.cases[i+1].Send = val + } else { + ch.buf.Add(val.Interface()) + } + if buf.count == int(buf.size) { + //buffer full, disable recv cases + for j := range buf.chans { + buf.cases[(j*2)+1].Chan = reflect.Value{} + } + } + } else { + //Close + buf.cases[i].Chan = reflect.Value{} + ch.closed = true + if ch.buf.Length() == 0 && !buf.cases[i+1].Chan.IsValid() { + //nothing pending, close the out channel right away + //TODO: shrink slice + close(ch.out) + } + } + } + } +} + +func (buf *SharedBuffer) Len() int { + return buf.count +} + +func (buf *SharedBuffer) Cap() BufferCap { + return buf.size +} diff --git a/vendor/github.com/eapache/queue/.gitignore b/vendor/github.com/eapache/queue/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/eapache/queue/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/eapache/queue/.travis.yml b/vendor/github.com/eapache/queue/.travis.yml new file mode 100644 index 0000000..235a40a --- /dev/null +++ b/vendor/github.com/eapache/queue/.travis.yml @@ -0,0 +1,7 @@ +language: go +sudo: false + +go: + - 1.2 + - 1.3 + - 1.4 diff --git a/vendor/github.com/eapache/queue/LICENSE b/vendor/github.com/eapache/queue/LICENSE new file mode 100644 index 0000000..d5f36db --- /dev/null +++ b/vendor/github.com/eapache/queue/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/eapache/queue/README.md b/vendor/github.com/eapache/queue/README.md new file mode 100644 index 0000000..8e78233 --- /dev/null +++ b/vendor/github.com/eapache/queue/README.md @@ -0,0 +1,16 @@ +Queue +===== + +[![Build Status](https://travis-ci.org/eapache/queue.svg)](https://travis-ci.org/eapache/queue) +[![GoDoc](https://godoc.org/github.com/eapache/queue?status.png)](https://godoc.org/github.com/eapache/queue) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki. +Using this instead of other, simpler, queue implementations (slice+append or linked list) provides +substantial memory and time benefits, and fewer GC pauses. + +The queue implemented here is as fast as it is in part because it is *not* thread-safe. + +Follows semantic versioning using https://gopkg.in/ - import from +[`gopkg.in/eapache/queue.v1`](https://gopkg.in/eapache/queue.v1) +for guaranteed API stability. diff --git a/vendor/github.com/eapache/queue/queue.go b/vendor/github.com/eapache/queue/queue.go new file mode 100644 index 0000000..71d1acd --- /dev/null +++ b/vendor/github.com/eapache/queue/queue.go @@ -0,0 +1,102 @@ +/* +Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki. +Using this instead of other, simpler, queue implementations (slice+append or linked list) provides +substantial memory and time benefits, and fewer GC pauses. + +The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. +*/ +package queue + +// minQueueLen is smallest capacity that queue may have. +// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). +const minQueueLen = 16 + +// Queue represents a single instance of the queue data structure. +type Queue struct { + buf []interface{} + head, tail, count int +} + +// New constructs and returns a new Queue. +func New() *Queue { + return &Queue{ + buf: make([]interface{}, minQueueLen), + } +} + +// Length returns the number of elements currently stored in the queue. +func (q *Queue) Length() int { + return q.count +} + +// resizes the queue to fit exactly twice its current contents +// this can result in shrinking if the queue is less than half-full +func (q *Queue) resize() { + newBuf := make([]interface{}, q.count<<1) + + if q.tail > q.head { + copy(newBuf, q.buf[q.head:q.tail]) + } else { + n := copy(newBuf, q.buf[q.head:]) + copy(newBuf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = newBuf +} + +// Add puts an element on the end of the queue. +func (q *Queue) Add(elem interface{}) { + if q.count == len(q.buf) { + q.resize() + } + + q.buf[q.tail] = elem + // bitwise modulus + q.tail = (q.tail + 1) & (len(q.buf) - 1) + q.count++ +} + +// Peek returns the element at the head of the queue. This call panics +// if the queue is empty. +func (q *Queue) Peek() interface{} { + if q.count <= 0 { + panic("queue: Peek() called on empty queue") + } + return q.buf[q.head] +} + +// Get returns the element at index i in the queue. If the index is +// invalid, the call will panic. This method accepts both positive and +// negative index values. Index 0 refers to the first element, and +// index -1 refers to the last. +func (q *Queue) Get(i int) interface{} { + // If indexing backwards, convert to positive index. + if i < 0 { + i += q.count + } + if i < 0 || i >= q.count { + panic("queue: Get() called with index out of range") + } + // bitwise modulus + return q.buf[(q.head+i)&(len(q.buf)-1)] +} + +// Remove removes and returns the element from the front of the queue. If the +// queue is empty, the call will panic. +func (q *Queue) Remove() interface{} { + if q.count <= 0 { + panic("queue: Remove() called on empty queue") + } + ret := q.buf[q.head] + q.buf[q.head] = nil + // bitwise modulus + q.head = (q.head + 1) & (len(q.buf) - 1) + q.count-- + // Resize down if buffer 1/4 full. + if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) { + q.resize() + } + return ret +} diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig new file mode 100644 index 0000000..ba49e3c --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 diff --git a/vendor/github.com/fsnotify/fsnotify/.gitignore b/vendor/github.com/fsnotify/fsnotify/.gitignore new file mode 100644 index 0000000..4cd0cba --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/.gitignore @@ -0,0 +1,6 @@ +# Setup a Global .gitignore for OS and editor generated files: +# https://help.github.com/articles/ignoring-files +# git config --global core.excludesfile ~/.gitignore_global + +.vagrant +*.sublime-project diff --git a/vendor/github.com/fsnotify/fsnotify/.travis.yml b/vendor/github.com/fsnotify/fsnotify/.travis.yml new file mode 100644 index 0000000..981d1bb --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/.travis.yml @@ -0,0 +1,30 @@ +sudo: false +language: go + +go: + - 1.8.x + - 1.9.x + - tip + +matrix: + allow_failures: + - go: tip + fast_finish: true + +before_script: + - go get -u github.com/golang/lint/golint + +script: + - go test -v --race ./... + +after_script: + - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" + - test -z "$(golint ./... | tee /dev/stderr)" + - go vet ./... + +os: + - linux + - osx + +notifications: + email: false diff --git a/vendor/github.com/fsnotify/fsnotify/AUTHORS b/vendor/github.com/fsnotify/fsnotify/AUTHORS new file mode 100644 index 0000000..5ab5d41 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/AUTHORS @@ -0,0 +1,52 @@ +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# You can update this list using the following command: +# +# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' + +# Please keep the list sorted. + +Aaron L +Adrien Bustany +Amit Krishnan +Anmol Sethi +Bjørn Erik Pedersen +Bruno Bigras +Caleb Spare +Case Nelson +Chris Howey +Christoffer Buchholz +Daniel Wagner-Hall +Dave Cheney +Evan Phoenix +Francisco Souza +Hari haran +John C Barstow +Kelvin Fo +Ken-ichirou MATSUZAWA +Matt Layher +Nathan Youngman +Nickolai Zeldovich +Patrick +Paul Hammond +Pawel Knap +Pieter Droogendijk +Pursuit92 +Riku Voipio +Rob Figueiredo +Rodrigo Chiossi +Slawek Ligus +Soge Zhang +Tiffany Jernigan +Tilak Sharma +Tom Payne +Travis Cline +Tudor Golubenco +Vahe Khachikyan +Yukang +bronze1man +debrando +henrikedwards +铁哥 diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md new file mode 100644 index 0000000..be4d7ea --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -0,0 +1,317 @@ +# Changelog + +## v1.4.7 / 2018-01-09 + +* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) +* Tests: Fix missing verb on format string (thanks @rchiossi) +* Linux: Fix deadlock in Remove (thanks @aarondl) +* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne) +* Docs: Moved FAQ into the README (thanks @vahe) +* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich) +* Docs: replace references to OS X with macOS + +## v1.4.2 / 2016-10-10 + +* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack) + +## v1.4.1 / 2016-10-04 + +* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack) + +## v1.4.0 / 2016-10-01 + +* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie) + +## v1.3.1 / 2016-06-28 + +* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc) + +## v1.3.0 / 2016-04-19 + +* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) + +## v1.2.10 / 2016-03-02 + +* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj) + +## v1.2.9 / 2016-01-13 + +kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep) + +## v1.2.8 / 2015-12-17 + +* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) +* inotify: fix race in test +* enable race detection for continuous integration (Linux, Mac, Windows) + +## v1.2.5 / 2015-10-17 + +* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki) +* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken) +* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie) +* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion) + +## v1.2.1 / 2015-10-14 + +* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx) + +## v1.2.0 / 2015-02-08 + +* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD) +* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD) +* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59) + +## v1.1.1 / 2015-02-05 + +* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD) + +## v1.1.0 / 2014-12-12 + +* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43) + * add low-level functions + * only need to store flags on directories + * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13) + * done can be an unbuffered channel + * remove calls to os.NewSyscallError +* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher) +* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48) +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) + +## v1.0.4 / 2014-09-07 + +* kqueue: add dragonfly to the build tags. +* Rename source code files, rearrange code so exported APIs are at the top. +* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang) + +## v1.0.3 / 2014-08-19 + +* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36) + +## v1.0.2 / 2014-08-17 + +* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) +* [Fix] Make ./path and path equivalent. (thanks @zhsso) + +## v1.0.0 / 2014-08-15 + +* [API] Remove AddWatch on Windows, use Add. +* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30) +* Minor updates based on feedback from golint. + +## dev / 2014-07-09 + +* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify). +* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) + +## dev / 2014-07-04 + +* kqueue: fix incorrect mutex used in Close() +* Update example to demonstrate usage of Op. + +## dev / 2014-06-28 + +* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4) +* Fix for String() method on Event (thanks Alex Brainman) +* Don't build on Plan 9 or Solaris (thanks @4ad) + +## dev / 2014-06-21 + +* Events channel of type Event rather than *Event. +* [internal] use syscall constants directly for inotify and kqueue. +* [internal] kqueue: rename events to kevents and fileEvent to event. + +## dev / 2014-06-19 + +* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). +* [internal] remove cookie from Event struct (unused). +* [internal] Event struct has the same definition across every OS. +* [internal] remove internal watch and removeWatch methods. + +## dev / 2014-06-12 + +* [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). +* [API] Pluralized channel names: Events and Errors. +* [API] Renamed FileEvent struct to Event. +* [API] Op constants replace methods like IsCreate(). + +## dev / 2014-06-12 + +* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) + +## dev / 2014-05-23 + +* [API] Remove current implementation of WatchFlags. + * current implementation doesn't take advantage of OS for efficiency + * provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes + * no tests for the current implementation + * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) + +## v0.9.3 / 2014-12-31 + +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) + +## v0.9.2 / 2014-08-17 + +* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) + +## v0.9.1 / 2014-06-12 + +* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) + +## v0.9.0 / 2014-01-17 + +* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) +* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) +* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. + +## v0.8.12 / 2013-11-13 + +* [API] Remove FD_SET and friends from Linux adapter + +## v0.8.11 / 2013-11-02 + +* [Doc] Add Changelog [#72][] (thanks @nathany) +* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond) + +## v0.8.10 / 2013-10-19 + +* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) +* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) +* [Doc] specify OS-specific limits in README (thanks @debrando) + +## v0.8.9 / 2013-09-08 + +* [Doc] Contributing (thanks @nathany) +* [Doc] update package path in example code [#63][] (thanks @paulhammond) +* [Doc] GoCI badge in README (Linux only) [#60][] +* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) + +## v0.8.8 / 2013-06-17 + +* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) + +## v0.8.7 / 2013-06-03 + +* [API] Make syscall flags internal +* [Fix] inotify: ignore event changes +* [Fix] race in symlink test [#45][] (reported by @srid) +* [Fix] tests on Windows +* lower case error messages + +## v0.8.6 / 2013-05-23 + +* kqueue: Use EVT_ONLY flag on Darwin +* [Doc] Update README with full example + +## v0.8.5 / 2013-05-09 + +* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) + +## v0.8.4 / 2013-04-07 + +* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) + +## v0.8.3 / 2013-03-13 + +* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) +* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) + +## v0.8.2 / 2013-02-07 + +* [Doc] add Authors +* [Fix] fix data races for map access [#29][] (thanks @fsouza) + +## v0.8.1 / 2013-01-09 + +* [Fix] Windows path separators +* [Doc] BSD License + +## v0.8.0 / 2012-11-09 + +* kqueue: directory watching improvements (thanks @vmirage) +* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) +* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) + +## v0.7.4 / 2012-10-09 + +* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) +* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) +* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) +* [Fix] kqueue: modify after recreation of file + +## v0.7.3 / 2012-09-27 + +* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) +* [Fix] kqueue: no longer get duplicate CREATE events + +## v0.7.2 / 2012-09-01 + +* kqueue: events for created directories + +## v0.7.1 / 2012-07-14 + +* [Fix] for renaming files + +## v0.7.0 / 2012-07-02 + +* [Feature] FSNotify flags +* [Fix] inotify: Added file name back to event path + +## v0.6.0 / 2012-06-06 + +* kqueue: watch files after directory created (thanks @tmc) + +## v0.5.1 / 2012-05-22 + +* [Fix] inotify: remove all watches before Close() + +## v0.5.0 / 2012-05-03 + +* [API] kqueue: return errors during watch instead of sending over channel +* kqueue: match symlink behavior on Linux +* inotify: add `DELETE_SELF` (requested by @taralx) +* [Fix] kqueue: handle EINTR (reported by @robfig) +* [Doc] Godoc example [#1][] (thanks @davecheney) + +## v0.4.0 / 2012-03-30 + +* Go 1 released: build with go tool +* [Feature] Windows support using winfsnotify +* Windows does not have attribute change notifications +* Roll attribute notifications into IsModify + +## v0.3.0 / 2012-02-19 + +* kqueue: add files when watch directory + +## v0.2.0 / 2011-12-30 + +* update to latest Go weekly code + +## v0.1.0 / 2011-10-19 + +* kqueue: add watch on file creation to match inotify +* kqueue: create file event +* inotify: ignore `IN_IGNORED` events +* event String() +* linux: common FileEvent functions +* initial commit + +[#79]: https://github.com/howeyc/fsnotify/pull/79 +[#77]: https://github.com/howeyc/fsnotify/pull/77 +[#72]: https://github.com/howeyc/fsnotify/issues/72 +[#71]: https://github.com/howeyc/fsnotify/issues/71 +[#70]: https://github.com/howeyc/fsnotify/issues/70 +[#63]: https://github.com/howeyc/fsnotify/issues/63 +[#62]: https://github.com/howeyc/fsnotify/issues/62 +[#60]: https://github.com/howeyc/fsnotify/issues/60 +[#59]: https://github.com/howeyc/fsnotify/issues/59 +[#49]: https://github.com/howeyc/fsnotify/issues/49 +[#45]: https://github.com/howeyc/fsnotify/issues/45 +[#40]: https://github.com/howeyc/fsnotify/issues/40 +[#36]: https://github.com/howeyc/fsnotify/issues/36 +[#33]: https://github.com/howeyc/fsnotify/issues/33 +[#29]: https://github.com/howeyc/fsnotify/issues/29 +[#25]: https://github.com/howeyc/fsnotify/issues/25 +[#24]: https://github.com/howeyc/fsnotify/issues/24 +[#21]: https://github.com/howeyc/fsnotify/issues/21 diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md new file mode 100644 index 0000000..828a60b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md @@ -0,0 +1,77 @@ +# Contributing + +## Issues + +* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues). +* Please indicate the platform you are using fsnotify on. +* A code example to reproduce the problem is appreciated. + +## Pull Requests + +### Contributor License Agreement + +fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). + +Please indicate that you have signed the CLA in your pull request. + +### How fsnotify is Developed + +* Development is done on feature branches. +* Tests are run on BSD, Linux, macOS and Windows. +* Pull requests are reviewed and [applied to master][am] using [hub][]. + * Maintainers may modify or squash commits rather than asking contributors to. +* To issue a new release, the maintainers will: + * Update the CHANGELOG + * Tag a version, which will become available through gopkg.in. + +### How to Fork + +For smooth sailing, always use the original import path. Installing with `go get` makes this easy. + +1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`) +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Ensure everything works and the tests pass (see below) +4. Commit your changes (`git commit -am 'Add some feature'`) + +Contribute upstream: + +1. Fork fsnotify on GitHub +2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) +3. Push to the branch (`git push fork my-new-feature`) +4. Create a new Pull Request on GitHub + +This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). + +### Testing + +fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows. + +Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. + +To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. + +* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) +* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. +* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) +* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`. +* When you're done, you will want to halt or destroy the Vagrant boxes. + +Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. + +Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). + +### Maintainers + +Help maintaining fsnotify is welcome. To be a maintainer: + +* Submit a pull request and sign the CLA as above. +* You must be able to run the test suite on Mac, Windows, Linux and BSD. + +To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. + +All code changes should be internal pull requests. + +Releases are tagged using [Semantic Versioning](http://semver.org/). + +[hub]: https://github.com/github/hub +[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE new file mode 100644 index 0000000..f21e540 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2012 fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md new file mode 100644 index 0000000..3993207 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -0,0 +1,79 @@ +# File system notifications for Go + +[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) + +fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: + +```console +go get -u golang.org/x/sys/... +``` + +Cross platform: Windows, Linux, BSD and macOS. + +|Adapter |OS |Status | +|----------|----------|----------| +|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| +|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| +|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| +|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| +|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| +|fanotify |Linux 2.6.37+ | | +|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| +|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| + +\* Android and iOS are untested. + +Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. + +## API stability + +fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). + +All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. + +Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. + +## Contributing + +Please refer to [CONTRIBUTING][] before opening an issue or pull request. + +## Example + +See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). + +## FAQ + +**When a file is moved to another directory is it still being watched?** + +No (it shouldn't be, unless you are watching where it was moved to). + +**When I watch a directory, are all subdirectories watched as well?** + +No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). + +**Do I have to watch the Error and Event channels in a separate goroutine?** + +As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) + +**Why am I receiving multiple events for the same file on OS X?** + +Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). + +**How many files can be watched at once?** + +There are OS-specific limits as to how many watches can be created: +* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. +* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. + +[#62]: https://github.com/howeyc/fsnotify/issues/62 +[#18]: https://github.com/fsnotify/fsnotify/issues/18 +[#11]: https://github.com/fsnotify/fsnotify/issues/11 +[#7]: https://github.com/howeyc/fsnotify/issues/7 + +[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md + +## Related Projects + +* [notify](https://github.com/rjeczalik/notify) +* [fsevents](https://github.com/fsnotify/fsevents) + diff --git a/vendor/github.com/fsnotify/fsnotify/fen.go b/vendor/github.com/fsnotify/fsnotify/fen.go new file mode 100644 index 0000000..ced39cb --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/fen.go @@ -0,0 +1,37 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +package fsnotify + +import ( + "errors" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + return nil +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + return nil +} diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go new file mode 100644 index 0000000..190bf0d --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -0,0 +1,66 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +// Package fsnotify provides a platform-independent interface for file system notifications. +package fsnotify + +import ( + "bytes" + "errors" + "fmt" +) + +// Event represents a single file system notification. +type Event struct { + Name string // Relative path to the file or directory. + Op Op // File operation that triggered the event. +} + +// Op describes a set of file operations. +type Op uint32 + +// These are the generalized file operations that can trigger a notification. +const ( + Create Op = 1 << iota + Write + Remove + Rename + Chmod +) + +func (op Op) String() string { + // Use a buffer for efficient string concatenation + var buffer bytes.Buffer + + if op&Create == Create { + buffer.WriteString("|CREATE") + } + if op&Remove == Remove { + buffer.WriteString("|REMOVE") + } + if op&Write == Write { + buffer.WriteString("|WRITE") + } + if op&Rename == Rename { + buffer.WriteString("|RENAME") + } + if op&Chmod == Chmod { + buffer.WriteString("|CHMOD") + } + if buffer.Len() == 0 { + return "" + } + return buffer.String()[1:] // Strip leading pipe +} + +// String returns a string representation of the event in the form +// "file: REMOVE|WRITE|..." +func (e Event) String() string { + return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) +} + +// Common errors that can be reported by a watcher +var ErrEventOverflow = errors.New("fsnotify queue overflow") diff --git a/vendor/github.com/fsnotify/fsnotify/inotify.go b/vendor/github.com/fsnotify/fsnotify/inotify.go new file mode 100644 index 0000000..d9fd1b8 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/inotify.go @@ -0,0 +1,337 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + mu sync.Mutex // Map access + fd int + poller *fdPoller + watches map[string]*watch // Map of inotify watches (key: path) + paths map[int]string // Map of watched paths (key: watch descriptor) + done chan struct{} // Channel for sending a "quit message" to the reader goroutine + doneResp chan struct{} // Channel to respond to Close +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + // Create inotify fd + fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) + if fd == -1 { + return nil, errno + } + // Create epoll + poller, err := newFdPoller(fd) + if err != nil { + unix.Close(fd) + return nil, err + } + w := &Watcher{ + fd: fd, + poller: poller, + watches: make(map[string]*watch), + paths: make(map[int]string), + Events: make(chan Event), + Errors: make(chan error), + done: make(chan struct{}), + doneResp: make(chan struct{}), + } + + go w.readEvents() + return w, nil +} + +func (w *Watcher) isClosed() bool { + select { + case <-w.done: + return true + default: + return false + } +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + if w.isClosed() { + return nil + } + + // Send 'close' signal to goroutine, and set the Watcher to closed. + close(w.done) + + // Wake up goroutine + w.poller.wake() + + // Wait for goroutine to close + <-w.doneResp + + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + name = filepath.Clean(name) + if w.isClosed() { + return errors.New("inotify instance already closed") + } + + const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | + unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | + unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF + + var flags uint32 = agnosticEvents + + w.mu.Lock() + defer w.mu.Unlock() + watchEntry := w.watches[name] + if watchEntry != nil { + flags |= watchEntry.flags | unix.IN_MASK_ADD + } + wd, errno := unix.InotifyAddWatch(w.fd, name, flags) + if wd == -1 { + return errno + } + + if watchEntry == nil { + w.watches[name] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = name + } else { + watchEntry.wd = uint32(wd) + watchEntry.flags = flags + } + + return nil +} + +// Remove stops watching the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + name = filepath.Clean(name) + + // Fetch the watch. + w.mu.Lock() + defer w.mu.Unlock() + watch, ok := w.watches[name] + + // Remove it from inotify. + if !ok { + return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) + } + + // We successfully removed the watch if InotifyRmWatch doesn't return an + // error, we need to clean up our internal state to ensure it matches + // inotify's kernel state. + delete(w.paths, int(watch.wd)) + delete(w.watches, name) + + // inotify_rm_watch will return EINVAL if the file has been deleted; + // the inotify will already have been removed. + // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously + // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE + // so that EINVAL means that the wd is being rm_watch()ed or its file removed + // by another thread and we have not received IN_IGNORE event. + success, errno := unix.InotifyRmWatch(w.fd, watch.wd) + if success == -1 { + // TODO: Perhaps it's not helpful to return an error here in every case. + // the only two possible errors are: + // EBADF, which happens when w.fd is not a valid file descriptor of any kind. + // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. + // Watch descriptors are invalidated when they are removed explicitly or implicitly; + // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. + return errno + } + + return nil +} + +type watch struct { + wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) + flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) +} + +// readEvents reads from the inotify file descriptor, converts the +// received events into Event objects and sends them via the Events channel +func (w *Watcher) readEvents() { + var ( + buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events + n int // Number of bytes read with read() + errno error // Syscall errno + ok bool // For poller.wait + ) + + defer close(w.doneResp) + defer close(w.Errors) + defer close(w.Events) + defer unix.Close(w.fd) + defer w.poller.close() + + for { + // See if we have been closed. + if w.isClosed() { + return + } + + ok, errno = w.poller.wait() + if errno != nil { + select { + case w.Errors <- errno: + case <-w.done: + return + } + continue + } + + if !ok { + continue + } + + n, errno = unix.Read(w.fd, buf[:]) + // If a signal interrupted execution, see if we've been asked to close, and try again. + // http://man7.org/linux/man-pages/man7/signal.7.html : + // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" + if errno == unix.EINTR { + continue + } + + // unix.Read might have been woken up by Close. If so, we're done. + if w.isClosed() { + return + } + + if n < unix.SizeofInotifyEvent { + var err error + if n == 0 { + // If EOF is received. This should really never happen. + err = io.EOF + } else if n < 0 { + // If an error occurred while reading. + err = errno + } else { + // Read was too short. + err = errors.New("notify: short read in readEvents()") + } + select { + case w.Errors <- err: + case <-w.done: + return + } + continue + } + + var offset uint32 + // We don't know how many events we just read into the buffer + // While the offset points to at least one whole event... + for offset <= uint32(n-unix.SizeofInotifyEvent) { + // Point "raw" to the event in the buffer + raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) + + mask := uint32(raw.Mask) + nameLen := uint32(raw.Len) + + if mask&unix.IN_Q_OVERFLOW != 0 { + select { + case w.Errors <- ErrEventOverflow: + case <-w.done: + return + } + } + + // If the event happened to the watched directory or the watched file, the kernel + // doesn't append the filename to the event, but we would like to always fill the + // the "Name" field with a valid filename. We retrieve the path of the watch from + // the "paths" map. + w.mu.Lock() + name, ok := w.paths[int(raw.Wd)] + // IN_DELETE_SELF occurs when the file/directory being watched is removed. + // This is a sign to clean up the maps, otherwise we are no longer in sync + // with the inotify kernel state which has already deleted the watch + // automatically. + if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { + delete(w.paths, int(raw.Wd)) + delete(w.watches, name) + } + w.mu.Unlock() + + if nameLen > 0 { + // Point "bytes" at the first byte of the filename + bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) + // The filename is padded with NULL bytes. TrimRight() gets rid of those. + name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") + } + + event := newEvent(name, mask) + + // Send the events that are not ignored on the events channel + if !event.ignoreLinux(mask) { + select { + case w.Events <- event: + case <-w.done: + return + } + } + + // Move to the next event in the buffer + offset += unix.SizeofInotifyEvent + nameLen + } + } +} + +// Certain types of events can be "ignored" and not sent over the Events +// channel. Such as events marked ignore by the kernel, or MODIFY events +// against files that do not exist. +func (e *Event) ignoreLinux(mask uint32) bool { + // Ignore anything the inotify API says to ignore + if mask&unix.IN_IGNORED == unix.IN_IGNORED { + return true + } + + // If the event is not a DELETE or RENAME, the file must exist. + // Otherwise the event is ignored. + // *Note*: this was put in place because it was seen that a MODIFY + // event was sent after the DELETE. This ignores that MODIFY and + // assumes a DELETE will come or has come if the file doesn't exist. + if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { + _, statErr := os.Lstat(e.Name) + return os.IsNotExist(statErr) + } + return false +} + +// newEvent returns an platform-independent Event based on an inotify mask. +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { + e.Op |= Create + } + if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { + e.Op |= Remove + } + if mask&unix.IN_MODIFY == unix.IN_MODIFY { + e.Op |= Write + } + if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { + e.Op |= Rename + } + if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { + e.Op |= Chmod + } + return e +} diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go new file mode 100644 index 0000000..cc7db4b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -0,0 +1,187 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "errors" + + "golang.org/x/sys/unix" +) + +type fdPoller struct { + fd int // File descriptor (as returned by the inotify_init() syscall) + epfd int // Epoll file descriptor + pipe [2]int // Pipe for waking up +} + +func emptyPoller(fd int) *fdPoller { + poller := new(fdPoller) + poller.fd = fd + poller.epfd = -1 + poller.pipe[0] = -1 + poller.pipe[1] = -1 + return poller +} + +// Create a new inotify poller. +// This creates an inotify handler, and an epoll handler. +func newFdPoller(fd int) (*fdPoller, error) { + var errno error + poller := emptyPoller(fd) + defer func() { + if errno != nil { + poller.close() + } + }() + poller.fd = fd + + // Create epoll fd + poller.epfd, errno = unix.EpollCreate1(0) + if poller.epfd == -1 { + return nil, errno + } + // Create pipe; pipe[0] is the read end, pipe[1] the write end. + errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + if errno != nil { + return nil, errno + } + + // Register inotify fd with epoll + event := unix.EpollEvent{ + Fd: int32(poller.fd), + Events: unix.EPOLLIN, + } + errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) + if errno != nil { + return nil, errno + } + + // Register pipe fd with epoll + event = unix.EpollEvent{ + Fd: int32(poller.pipe[0]), + Events: unix.EPOLLIN, + } + errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) + if errno != nil { + return nil, errno + } + + return poller, nil +} + +// Wait using epoll. +// Returns true if something is ready to be read, +// false if there is not. +func (poller *fdPoller) wait() (bool, error) { + // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. + // I don't know whether epoll_wait returns the number of events returned, + // or the total number of events ready. + // I decided to catch both by making the buffer one larger than the maximum. + events := make([]unix.EpollEvent, 7) + for { + n, errno := unix.EpollWait(poller.epfd, events, -1) + if n == -1 { + if errno == unix.EINTR { + continue + } + return false, errno + } + if n == 0 { + // If there are no events, try again. + continue + } + if n > 6 { + // This should never happen. More events were returned than should be possible. + return false, errors.New("epoll_wait returned more events than I know what to do with") + } + ready := events[:n] + epollhup := false + epollerr := false + epollin := false + for _, event := range ready { + if event.Fd == int32(poller.fd) { + if event.Events&unix.EPOLLHUP != 0 { + // This should not happen, but if it does, treat it as a wakeup. + epollhup = true + } + if event.Events&unix.EPOLLERR != 0 { + // If an error is waiting on the file descriptor, we should pretend + // something is ready to read, and let unix.Read pick up the error. + epollerr = true + } + if event.Events&unix.EPOLLIN != 0 { + // There is data to read. + epollin = true + } + } + if event.Fd == int32(poller.pipe[0]) { + if event.Events&unix.EPOLLHUP != 0 { + // Write pipe descriptor was closed, by us. This means we're closing down the + // watcher, and we should wake up. + } + if event.Events&unix.EPOLLERR != 0 { + // If an error is waiting on the pipe file descriptor. + // This is an absolute mystery, and should never ever happen. + return false, errors.New("Error on the pipe descriptor.") + } + if event.Events&unix.EPOLLIN != 0 { + // This is a regular wakeup, so we have to clear the buffer. + err := poller.clearWake() + if err != nil { + return false, err + } + } + } + } + + if epollhup || epollerr || epollin { + return true, nil + } + return false, nil + } +} + +// Close the write end of the poller. +func (poller *fdPoller) wake() error { + buf := make([]byte, 1) + n, errno := unix.Write(poller.pipe[1], buf) + if n == -1 { + if errno == unix.EAGAIN { + // Buffer is full, poller will wake. + return nil + } + return errno + } + return nil +} + +func (poller *fdPoller) clearWake() error { + // You have to be woken up a LOT in order to get to 100! + buf := make([]byte, 100) + n, errno := unix.Read(poller.pipe[0], buf) + if n == -1 { + if errno == unix.EAGAIN { + // Buffer is empty, someone else cleared our wake. + return nil + } + return errno + } + return nil +} + +// Close all poller file descriptors, but not the one passed to it. +func (poller *fdPoller) close() { + if poller.pipe[1] != -1 { + unix.Close(poller.pipe[1]) + } + if poller.pipe[0] != -1 { + unix.Close(poller.pipe[0]) + } + if poller.epfd != -1 { + unix.Close(poller.epfd) + } +} diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/github.com/fsnotify/fsnotify/kqueue.go new file mode 100644 index 0000000..86e76a3 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/kqueue.go @@ -0,0 +1,521 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd openbsd netbsd dragonfly darwin + +package fsnotify + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + "time" + + "golang.org/x/sys/unix" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + done chan struct{} // Channel for sending a "quit message" to the reader goroutine + + kq int // File descriptor (as returned by the kqueue() syscall). + + mu sync.Mutex // Protects access to watcher data + watches map[string]int // Map of watched file descriptors (key: path). + externalWatches map[string]bool // Map of watches added by user of the library. + dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue. + paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events. + fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). + isClosed bool // Set to true when Close() is first called +} + +type pathInfo struct { + name string + isDir bool +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + kq, err := kqueue() + if err != nil { + return nil, err + } + + w := &Watcher{ + kq: kq, + watches: make(map[string]int), + dirFlags: make(map[string]uint32), + paths: make(map[int]pathInfo), + fileExists: make(map[string]bool), + externalWatches: make(map[string]bool), + Events: make(chan Event), + Errors: make(chan error), + done: make(chan struct{}), + } + + go w.readEvents() + return w, nil +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return nil + } + w.isClosed = true + + // copy paths to remove while locked + var pathsToRemove = make([]string, 0, len(w.watches)) + for name := range w.watches { + pathsToRemove = append(pathsToRemove, name) + } + w.mu.Unlock() + // unlock before calling Remove, which also locks + + for _, name := range pathsToRemove { + w.Remove(name) + } + + // send a "quit" message to the reader goroutine + close(w.done) + + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + w.mu.Lock() + w.externalWatches[name] = true + w.mu.Unlock() + _, err := w.addWatch(name, noteAllEvents) + return err +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + name = filepath.Clean(name) + w.mu.Lock() + watchfd, ok := w.watches[name] + w.mu.Unlock() + if !ok { + return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) + } + + const registerRemove = unix.EV_DELETE + if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { + return err + } + + unix.Close(watchfd) + + w.mu.Lock() + isDir := w.paths[watchfd].isDir + delete(w.watches, name) + delete(w.paths, watchfd) + delete(w.dirFlags, name) + w.mu.Unlock() + + // Find all watched paths that are in this directory that are not external. + if isDir { + var pathsToRemove []string + w.mu.Lock() + for _, path := range w.paths { + wdir, _ := filepath.Split(path.name) + if filepath.Clean(wdir) == name { + if !w.externalWatches[path.name] { + pathsToRemove = append(pathsToRemove, path.name) + } + } + } + w.mu.Unlock() + for _, name := range pathsToRemove { + // Since these are internal, not much sense in propagating error + // to the user, as that will just confuse them with an error about + // a path they did not explicitly watch themselves. + w.Remove(name) + } + } + + return nil +} + +// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) +const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME + +// keventWaitTime to block on each read from kevent +var keventWaitTime = durationToTimespec(100 * time.Millisecond) + +// addWatch adds name to the watched file set. +// The flags are interpreted as described in kevent(2). +// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. +func (w *Watcher) addWatch(name string, flags uint32) (string, error) { + var isDir bool + // Make ./name and name equivalent + name = filepath.Clean(name) + + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return "", errors.New("kevent instance already closed") + } + watchfd, alreadyWatching := w.watches[name] + // We already have a watch, but we can still override flags. + if alreadyWatching { + isDir = w.paths[watchfd].isDir + } + w.mu.Unlock() + + if !alreadyWatching { + fi, err := os.Lstat(name) + if err != nil { + return "", err + } + + // Don't watch sockets. + if fi.Mode()&os.ModeSocket == os.ModeSocket { + return "", nil + } + + // Don't watch named pipes. + if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { + return "", nil + } + + // Follow Symlinks + // Unfortunately, Linux can add bogus symlinks to watch list without + // issue, and Windows can't do symlinks period (AFAIK). To maintain + // consistency, we will act like everything is fine. There will simply + // be no file events for broken symlinks. + // Hence the returns of nil on errors. + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + name, err = filepath.EvalSymlinks(name) + if err != nil { + return "", nil + } + + w.mu.Lock() + _, alreadyWatching = w.watches[name] + w.mu.Unlock() + + if alreadyWatching { + return name, nil + } + + fi, err = os.Lstat(name) + if err != nil { + return "", nil + } + } + + watchfd, err = unix.Open(name, openMode, 0700) + if watchfd == -1 { + return "", err + } + + isDir = fi.IsDir() + } + + const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE + if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { + unix.Close(watchfd) + return "", err + } + + if !alreadyWatching { + w.mu.Lock() + w.watches[name] = watchfd + w.paths[watchfd] = pathInfo{name: name, isDir: isDir} + w.mu.Unlock() + } + + if isDir { + // Watch the directory if it has not been watched before, + // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) + w.mu.Lock() + + watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && + (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) + // Store flags so this watch can be updated later + w.dirFlags[name] = flags + w.mu.Unlock() + + if watchDir { + if err := w.watchDirectoryFiles(name); err != nil { + return "", err + } + } + } + return name, nil +} + +// readEvents reads from kqueue and converts the received kevents into +// Event values that it sends down the Events channel. +func (w *Watcher) readEvents() { + eventBuffer := make([]unix.Kevent_t, 10) + +loop: + for { + // See if there is a message on the "done" channel + select { + case <-w.done: + break loop + default: + } + + // Get new events + kevents, err := read(w.kq, eventBuffer, &keventWaitTime) + // EINTR is okay, the syscall was interrupted before timeout expired. + if err != nil && err != unix.EINTR { + select { + case w.Errors <- err: + case <-w.done: + break loop + } + continue + } + + // Flush the events we received to the Events channel + for len(kevents) > 0 { + kevent := &kevents[0] + watchfd := int(kevent.Ident) + mask := uint32(kevent.Fflags) + w.mu.Lock() + path := w.paths[watchfd] + w.mu.Unlock() + event := newEvent(path.name, mask) + + if path.isDir && !(event.Op&Remove == Remove) { + // Double check to make sure the directory exists. This can happen when + // we do a rm -fr on a recursively watched folders and we receive a + // modification event first but the folder has been deleted and later + // receive the delete event + if _, err := os.Lstat(event.Name); os.IsNotExist(err) { + // mark is as delete event + event.Op |= Remove + } + } + + if event.Op&Rename == Rename || event.Op&Remove == Remove { + w.Remove(event.Name) + w.mu.Lock() + delete(w.fileExists, event.Name) + w.mu.Unlock() + } + + if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { + w.sendDirectoryChangeEvents(event.Name) + } else { + // Send the event on the Events channel. + select { + case w.Events <- event: + case <-w.done: + break loop + } + } + + if event.Op&Remove == Remove { + // Look for a file that may have overwritten this. + // For example, mv f1 f2 will delete f2, then create f2. + if path.isDir { + fileDir := filepath.Clean(event.Name) + w.mu.Lock() + _, found := w.watches[fileDir] + w.mu.Unlock() + if found { + // make sure the directory exists before we watch for changes. When we + // do a recursive watch and perform rm -fr, the parent directory might + // have gone missing, ignore the missing directory and let the + // upcoming delete event remove the watch from the parent directory. + if _, err := os.Lstat(fileDir); err == nil { + w.sendDirectoryChangeEvents(fileDir) + } + } + } else { + filePath := filepath.Clean(event.Name) + if fileInfo, err := os.Lstat(filePath); err == nil { + w.sendFileCreatedEventIfNew(filePath, fileInfo) + } + } + } + + // Move to next event + kevents = kevents[1:] + } + } + + // cleanup + err := unix.Close(w.kq) + if err != nil { + // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. + select { + case w.Errors <- err: + default: + } + } + close(w.Events) + close(w.Errors) +} + +// newEvent returns an platform-independent Event based on kqueue Fflags. +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { + e.Op |= Remove + } + if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { + e.Op |= Write + } + if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { + e.Op |= Rename + } + if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { + e.Op |= Chmod + } + return e +} + +func newCreateEvent(name string) Event { + return Event{Name: name, Op: Create} +} + +// watchDirectoryFiles to mimic inotify when adding a watch on a directory +func (w *Watcher) watchDirectoryFiles(dirPath string) error { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + return err + } + + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + filePath, err = w.internalWatch(filePath, fileInfo) + if err != nil { + return err + } + + w.mu.Lock() + w.fileExists[filePath] = true + w.mu.Unlock() + } + + return nil +} + +// sendDirectoryEvents searches the directory for newly created files +// and sends them over the event channel. This functionality is to have +// the BSD version of fsnotify match Linux inotify which provides a +// create event for files created in a watched directory. +func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + select { + case w.Errors <- err: + case <-w.done: + return + } + } + + // Search for new files + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + err := w.sendFileCreatedEventIfNew(filePath, fileInfo) + + if err != nil { + return + } + } +} + +// sendFileCreatedEvent sends a create event if the file isn't already being tracked. +func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { + w.mu.Lock() + _, doesExist := w.fileExists[filePath] + w.mu.Unlock() + if !doesExist { + // Send create event + select { + case w.Events <- newCreateEvent(filePath): + case <-w.done: + return + } + } + + // like watchDirectoryFiles (but without doing another ReadDir) + filePath, err = w.internalWatch(filePath, fileInfo) + if err != nil { + return err + } + + w.mu.Lock() + w.fileExists[filePath] = true + w.mu.Unlock() + + return nil +} + +func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { + if fileInfo.IsDir() { + // mimic Linux providing delete events for subdirectories + // but preserve the flags used if currently watching subdirectory + w.mu.Lock() + flags := w.dirFlags[name] + w.mu.Unlock() + + flags |= unix.NOTE_DELETE | unix.NOTE_RENAME + return w.addWatch(name, flags) + } + + // watch file to mimic Linux inotify + return w.addWatch(name, noteAllEvents) +} + +// kqueue creates a new kernel event queue and returns a descriptor. +func kqueue() (kq int, err error) { + kq, err = unix.Kqueue() + if kq == -1 { + return kq, err + } + return kq, nil +} + +// register events with the queue +func register(kq int, fds []int, flags int, fflags uint32) error { + changes := make([]unix.Kevent_t, len(fds)) + + for i, fd := range fds { + // SetKevent converts int to the platform-specific types: + unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) + changes[i].Fflags = fflags + } + + // register the events + success, err := unix.Kevent(kq, changes, nil, nil) + if success == -1 { + return err + } + return nil +} + +// read retrieves pending events, or waits until an event occurs. +// A timeout of nil blocks indefinitely, while 0 polls the queue. +func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { + n, err := unix.Kevent(kq, nil, events, timeout) + if err != nil { + return nil, err + } + return events[0:n], nil +} + +// durationToTimespec prepares a timeout value +func durationToTimespec(d time.Duration) unix.Timespec { + return unix.NsecToTimespec(d.Nanoseconds()) +} diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go new file mode 100644 index 0000000..7d8de14 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd openbsd netbsd dragonfly + +package fsnotify + +import "golang.org/x/sys/unix" + +const openMode = unix.O_NONBLOCK | unix.O_RDONLY diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go new file mode 100644 index 0000000..9139e17 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package fsnotify + +import "golang.org/x/sys/unix" + +// note: this constant is not defined on BSD +const openMode = unix.O_EVTONLY diff --git a/vendor/github.com/fsnotify/fsnotify/windows.go b/vendor/github.com/fsnotify/fsnotify/windows.go new file mode 100644 index 0000000..09436f3 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/windows.go @@ -0,0 +1,561 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package fsnotify + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + isClosed bool // Set to true when Close() is first called + mu sync.Mutex // Map access + port syscall.Handle // Handle to completion port + watches watchMap // Map of watches (key: i-number) + input chan *input // Inputs to the reader are sent on this channel + quit chan chan<- error +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) + if e != nil { + return nil, os.NewSyscallError("CreateIoCompletionPort", e) + } + w := &Watcher{ + port: port, + watches: make(watchMap), + input: make(chan *input, 1), + Events: make(chan Event, 50), + Errors: make(chan error), + quit: make(chan chan<- error, 1), + } + go w.readEvents() + return w, nil +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + if w.isClosed { + return nil + } + w.isClosed = true + + // Send "quit" message to the reader goroutine + ch := make(chan error) + w.quit <- ch + if err := w.wakeupReader(); err != nil { + return err + } + return <-ch +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + if w.isClosed { + return errors.New("watcher already closed") + } + in := &input{ + op: opAddWatch, + path: filepath.Clean(name), + flags: sysFSALLEVENTS, + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + in := &input{ + op: opRemoveWatch, + path: filepath.Clean(name), + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +const ( + // Options for AddWatch + sysFSONESHOT = 0x80000000 + sysFSONLYDIR = 0x1000000 + + // Events + sysFSACCESS = 0x1 + sysFSALLEVENTS = 0xfff + sysFSATTRIB = 0x4 + sysFSCLOSE = 0x18 + sysFSCREATE = 0x100 + sysFSDELETE = 0x200 + sysFSDELETESELF = 0x400 + sysFSMODIFY = 0x2 + sysFSMOVE = 0xc0 + sysFSMOVEDFROM = 0x40 + sysFSMOVEDTO = 0x80 + sysFSMOVESELF = 0x800 + + // Special events + sysFSIGNORED = 0x8000 + sysFSQOVERFLOW = 0x4000 +) + +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { + e.Op |= Create + } + if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { + e.Op |= Remove + } + if mask&sysFSMODIFY == sysFSMODIFY { + e.Op |= Write + } + if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { + e.Op |= Rename + } + if mask&sysFSATTRIB == sysFSATTRIB { + e.Op |= Chmod + } + return e +} + +const ( + opAddWatch = iota + opRemoveWatch +) + +const ( + provisional uint64 = 1 << (32 + iota) +) + +type input struct { + op int + path string + flags uint32 + reply chan error +} + +type inode struct { + handle syscall.Handle + volume uint32 + index uint64 +} + +type watch struct { + ov syscall.Overlapped + ino *inode // i-number + path string // Directory path + mask uint64 // Directory itself is being watched with these notify flags + names map[string]uint64 // Map of names being watched and their notify flags + rename string // Remembers the old name while renaming a file + buf [4096]byte +} + +type indexMap map[uint64]*watch +type watchMap map[uint32]indexMap + +func (w *Watcher) wakeupReader() error { + e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) + if e != nil { + return os.NewSyscallError("PostQueuedCompletionStatus", e) + } + return nil +} + +func getDir(pathname string) (dir string, err error) { + attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) + if e != nil { + return "", os.NewSyscallError("GetFileAttributes", e) + } + if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + dir = pathname + } else { + dir, _ = filepath.Split(pathname) + dir = filepath.Clean(dir) + } + return +} + +func getIno(path string) (ino *inode, err error) { + h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), + syscall.FILE_LIST_DIRECTORY, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) + if e != nil { + return nil, os.NewSyscallError("CreateFile", e) + } + var fi syscall.ByHandleFileInformation + if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { + syscall.CloseHandle(h) + return nil, os.NewSyscallError("GetFileInformationByHandle", e) + } + ino = &inode{ + handle: h, + volume: fi.VolumeSerialNumber, + index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), + } + return ino, nil +} + +// Must run within the I/O thread. +func (m watchMap) get(ino *inode) *watch { + if i := m[ino.volume]; i != nil { + return i[ino.index] + } + return nil +} + +// Must run within the I/O thread. +func (m watchMap) set(ino *inode, watch *watch) { + i := m[ino.volume] + if i == nil { + i = make(indexMap) + m[ino.volume] = i + } + i[ino.index] = watch +} + +// Must run within the I/O thread. +func (w *Watcher) addWatch(pathname string, flags uint64) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + if flags&sysFSONLYDIR != 0 && pathname != dir { + return nil + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watchEntry := w.watches.get(ino) + w.mu.Unlock() + if watchEntry == nil { + if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { + syscall.CloseHandle(ino.handle) + return os.NewSyscallError("CreateIoCompletionPort", e) + } + watchEntry = &watch{ + ino: ino, + path: dir, + names: make(map[string]uint64), + } + w.mu.Lock() + w.watches.set(ino, watchEntry) + w.mu.Unlock() + flags |= provisional + } else { + syscall.CloseHandle(ino.handle) + } + if pathname == dir { + watchEntry.mask |= flags + } else { + watchEntry.names[filepath.Base(pathname)] |= flags + } + if err = w.startRead(watchEntry); err != nil { + return err + } + if pathname == dir { + watchEntry.mask &= ^provisional + } else { + watchEntry.names[filepath.Base(pathname)] &= ^provisional + } + return nil +} + +// Must run within the I/O thread. +func (w *Watcher) remWatch(pathname string) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watch := w.watches.get(ino) + w.mu.Unlock() + if watch == nil { + return fmt.Errorf("can't remove non-existent watch for: %s", pathname) + } + if pathname == dir { + w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + watch.mask = 0 + } else { + name := filepath.Base(pathname) + w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) + delete(watch.names, name) + } + return w.startRead(watch) +} + +// Must run within the I/O thread. +func (w *Watcher) deleteWatch(watch *watch) { + for name, mask := range watch.names { + if mask&provisional == 0 { + w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) + } + delete(watch.names, name) + } + if watch.mask != 0 { + if watch.mask&provisional == 0 { + w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + } + watch.mask = 0 + } +} + +// Must run within the I/O thread. +func (w *Watcher) startRead(watch *watch) error { + if e := syscall.CancelIo(watch.ino.handle); e != nil { + w.Errors <- os.NewSyscallError("CancelIo", e) + w.deleteWatch(watch) + } + mask := toWindowsFlags(watch.mask) + for _, m := range watch.names { + mask |= toWindowsFlags(m) + } + if mask == 0 { + if e := syscall.CloseHandle(watch.ino.handle); e != nil { + w.Errors <- os.NewSyscallError("CloseHandle", e) + } + w.mu.Lock() + delete(w.watches[watch.ino.volume], watch.ino.index) + w.mu.Unlock() + return nil + } + e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], + uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) + if e != nil { + err := os.NewSyscallError("ReadDirectoryChanges", e) + if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { + // Watched directory was probably removed + if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { + if watch.mask&sysFSONESHOT != 0 { + watch.mask = 0 + } + } + err = nil + } + w.deleteWatch(watch) + w.startRead(watch) + return err + } + return nil +} + +// readEvents reads from the I/O completion port, converts the +// received events into Event objects and sends them via the Events channel. +// Entry point to the I/O thread. +func (w *Watcher) readEvents() { + var ( + n, key uint32 + ov *syscall.Overlapped + ) + runtime.LockOSThread() + + for { + e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) + watch := (*watch)(unsafe.Pointer(ov)) + + if watch == nil { + select { + case ch := <-w.quit: + w.mu.Lock() + var indexes []indexMap + for _, index := range w.watches { + indexes = append(indexes, index) + } + w.mu.Unlock() + for _, index := range indexes { + for _, watch := range index { + w.deleteWatch(watch) + w.startRead(watch) + } + } + var err error + if e := syscall.CloseHandle(w.port); e != nil { + err = os.NewSyscallError("CloseHandle", e) + } + close(w.Events) + close(w.Errors) + ch <- err + return + case in := <-w.input: + switch in.op { + case opAddWatch: + in.reply <- w.addWatch(in.path, uint64(in.flags)) + case opRemoveWatch: + in.reply <- w.remWatch(in.path) + } + default: + } + continue + } + + switch e { + case syscall.ERROR_MORE_DATA: + if watch == nil { + w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") + } else { + // The i/o succeeded but the buffer is full. + // In theory we should be building up a full packet. + // In practice we can get away with just carrying on. + n = uint32(unsafe.Sizeof(watch.buf)) + } + case syscall.ERROR_ACCESS_DENIED: + // Watched directory was probably removed + w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) + w.deleteWatch(watch) + w.startRead(watch) + continue + case syscall.ERROR_OPERATION_ABORTED: + // CancelIo was called on this handle + continue + default: + w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) + continue + case nil: + } + + var offset uint32 + for { + if n == 0 { + w.Events <- newEvent("", sysFSQOVERFLOW) + w.Errors <- errors.New("short read in readEvents()") + break + } + + // Point "raw" to the event in the buffer + raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) + buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) + name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) + fullname := filepath.Join(watch.path, name) + + var mask uint64 + switch raw.Action { + case syscall.FILE_ACTION_REMOVED: + mask = sysFSDELETESELF + case syscall.FILE_ACTION_MODIFIED: + mask = sysFSMODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + watch.rename = name + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + if watch.names[watch.rename] != 0 { + watch.names[name] |= watch.names[watch.rename] + delete(watch.names, watch.rename) + mask = sysFSMOVESELF + } + } + + sendNameEvent := func() { + if w.sendEvent(fullname, watch.names[name]&mask) { + if watch.names[name]&sysFSONESHOT != 0 { + delete(watch.names, name) + } + } + } + if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { + sendNameEvent() + } + if raw.Action == syscall.FILE_ACTION_REMOVED { + w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) + delete(watch.names, name) + } + if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { + if watch.mask&sysFSONESHOT != 0 { + watch.mask = 0 + } + } + if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { + fullname = filepath.Join(watch.path, watch.rename) + sendNameEvent() + } + + // Move to the next event in the buffer + if raw.NextEntryOffset == 0 { + break + } + offset += raw.NextEntryOffset + + // Error! + if offset >= n { + w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") + break + } + } + + if err := w.startRead(watch); err != nil { + w.Errors <- err + } + } +} + +func (w *Watcher) sendEvent(name string, mask uint64) bool { + if mask == 0 { + return false + } + event := newEvent(name, uint32(mask)) + select { + case ch := <-w.quit: + w.quit <- ch + case w.Events <- event: + } + return true +} + +func toWindowsFlags(mask uint64) uint32 { + var m uint32 + if mask&sysFSACCESS != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS + } + if mask&sysFSMODIFY != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE + } + if mask&sysFSATTRIB != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES + } + if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME + } + return m +} + +func toFSnotifyFlags(action uint32) uint64 { + switch action { + case syscall.FILE_ACTION_ADDED: + return sysFSCREATE + case syscall.FILE_ACTION_REMOVED: + return sysFSDELETE + case syscall.FILE_ACTION_MODIFIED: + return sysFSMODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + return sysFSMOVEDFROM + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + return sysFSMOVEDTO + } + return 0 +} diff --git a/vendor/github.com/golang/glog/LICENSE b/vendor/github.com/golang/glog/LICENSE new file mode 100644 index 0000000..37ec93a --- /dev/null +++ b/vendor/github.com/golang/glog/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/golang/glog/README b/vendor/github.com/golang/glog/README new file mode 100644 index 0000000..387b4eb --- /dev/null +++ b/vendor/github.com/golang/glog/README @@ -0,0 +1,44 @@ +glog +==== + +Leveled execution logs for Go. + +This is an efficient pure Go implementation of leveled logs in the +manner of the open source C++ package + https://github.com/google/glog + +By binding methods to booleans it is possible to use the log package +without paying the expense of evaluating the arguments to the log. +Through the -vmodule flag, the package also provides fine-grained +control over logging at the file level. + +The comment from glog.go introduces the ideas: + + Package glog implements logging analogous to the Google-internal + C++ INFO/ERROR/V setup. It provides functions Info, Warning, + Error, Fatal, plus formatting variants such as Infof. It + also provides V-style logging controlled by the -v and + -vmodule=file=2 flags. + + Basic examples: + + glog.Info("Prepare to repel boarders") + + glog.Fatalf("Initialization failed: %s", err) + + See the documentation for the V function for an explanation + of these examples: + + if glog.V(2) { + glog.Info("Starting transaction...") + } + + glog.V(2).Infoln("Processed", nItems, "elements") + + +The repository contains an open source version of the log package +used inside Google. The master copy of the source lives inside +Google, not here. The code in this repo is for export only and is not itself +under development. Feature requests will be ignored. + +Send bug reports to golang-nuts@googlegroups.com. diff --git a/vendor/github.com/golang/glog/glog.go b/vendor/github.com/golang/glog/glog.go new file mode 100644 index 0000000..54bd7af --- /dev/null +++ b/vendor/github.com/golang/glog/glog.go @@ -0,0 +1,1180 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// 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 glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. +// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as +// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. +// +// Basic examples: +// +// glog.Info("Prepare to repel boarders") +// +// glog.Fatalf("Initialization failed: %s", err) +// +// See the documentation for the V function for an explanation of these examples: +// +// if glog.V(2) { +// glog.Info("Starting transaction...") +// } +// +// glog.V(2).Infoln("Processed", nItems, "elements") +// +// Log output is buffered and written periodically using Flush. Programs +// should call Flush before exiting to guarantee all log output is written. +// +// By default, all log statements write to files in a temporary directory. +// This package provides several flags that modify this behavior. +// As a result, flag.Parse must be called before any logging is done. +// +// -logtostderr=false +// Logs are written to standard error instead of to files. +// -alsologtostderr=false +// Logs are written to standard error as well as to files. +// -stderrthreshold=ERROR +// Log events at or above this severity are logged to standard +// error as well as to files. +// -log_dir="" +// Log files will be written to this directory instead of the +// default temporary directory. +// +// Other flags provide aids to debugging. +// +// -log_backtrace_at="" +// When set to a file and line number holding a logging statement, +// such as +// -log_backtrace_at=gopherflakes.go:234 +// a stack trace will be written to the Info log whenever execution +// hits that statement. (Unlike with -vmodule, the ".go" must be +// present.) +// -v=0 +// Enable V-leveled logging at the specified level. +// -vmodule="" +// The syntax of the argument is a comma-separated list of pattern=N, +// where pattern is a literal file name (minus the ".go" suffix) or +// "glob" pattern and N is a V level. For instance, +// -vmodule=gopher*=3 +// sets the V level to 3 in all Go files whose names begin "gopher". +// +package glog + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "io" + stdLog "log" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// severity identifies the sort of log: info, warning etc. It also implements +// the flag.Value interface. The -stderrthreshold flag is of type severity and +// should be modified only through the flag.Value interface. The values match +// the corresponding constants in C++. +type severity int32 // sync/atomic int32 + +// These constants identify the log levels in order of increasing severity. +// A message written to a high-severity log file is also written to each +// lower-severity log file. +const ( + infoLog severity = iota + warningLog + errorLog + fatalLog + numSeverity = 4 +) + +const severityChar = "IWEF" + +var severityName = []string{ + infoLog: "INFO", + warningLog: "WARNING", + errorLog: "ERROR", + fatalLog: "FATAL", +} + +// get returns the value of the severity. +func (s *severity) get() severity { + return severity(atomic.LoadInt32((*int32)(s))) +} + +// set sets the value of the severity. +func (s *severity) set(val severity) { + atomic.StoreInt32((*int32)(s), int32(val)) +} + +// String is part of the flag.Value interface. +func (s *severity) String() string { + return strconv.FormatInt(int64(*s), 10) +} + +// Get is part of the flag.Value interface. +func (s *severity) Get() interface{} { + return *s +} + +// Set is part of the flag.Value interface. +func (s *severity) Set(value string) error { + var threshold severity + // Is it a known name? + if v, ok := severityByName(value); ok { + threshold = v + } else { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + threshold = severity(v) + } + logging.stderrThreshold.set(threshold) + return nil +} + +func severityByName(s string) (severity, bool) { + s = strings.ToUpper(s) + for i, name := range severityName { + if name == s { + return severity(i), true + } + } + return 0, false +} + +// OutputStats tracks the number of output lines and bytes written. +type OutputStats struct { + lines int64 + bytes int64 +} + +// Lines returns the number of lines written. +func (s *OutputStats) Lines() int64 { + return atomic.LoadInt64(&s.lines) +} + +// Bytes returns the number of bytes written. +func (s *OutputStats) Bytes() int64 { + return atomic.LoadInt64(&s.bytes) +} + +// Stats tracks the number of lines of output and number of bytes +// per severity level. Values must be read with atomic.LoadInt64. +var Stats struct { + Info, Warning, Error OutputStats +} + +var severityStats = [numSeverity]*OutputStats{ + infoLog: &Stats.Info, + warningLog: &Stats.Warning, + errorLog: &Stats.Error, +} + +// Level is exported because it appears in the arguments to V and is +// the type of the v flag, which can be set programmatically. +// It's a distinct type because we want to discriminate it from logType. +// Variables of type level are only changed under logging.mu. +// The -v flag is read only with atomic ops, so the state of the logging +// module is consistent. + +// Level is treated as a sync/atomic int32. + +// Level specifies a level of verbosity for V logs. *Level implements +// flag.Value; the -v flag is of type Level and should be modified +// only through the flag.Value interface. +type Level int32 + +// get returns the value of the Level. +func (l *Level) get() Level { + return Level(atomic.LoadInt32((*int32)(l))) +} + +// set sets the value of the Level. +func (l *Level) set(val Level) { + atomic.StoreInt32((*int32)(l), int32(val)) +} + +// String is part of the flag.Value interface. +func (l *Level) String() string { + return strconv.FormatInt(int64(*l), 10) +} + +// Get is part of the flag.Value interface. +func (l *Level) Get() interface{} { + return *l +} + +// Set is part of the flag.Value interface. +func (l *Level) Set(value string) error { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(Level(v), logging.vmodule.filter, false) + return nil +} + +// moduleSpec represents the setting of the -vmodule flag. +type moduleSpec struct { + filter []modulePat +} + +// modulePat contains a filter for the -vmodule flag. +// It holds a verbosity level and a file pattern to match. +type modulePat struct { + pattern string + literal bool // The pattern is a literal string + level Level +} + +// match reports whether the file matches the pattern. It uses a string +// comparison if the pattern contains no metacharacters. +func (m *modulePat) match(file string) bool { + if m.literal { + return file == m.pattern + } + match, _ := filepath.Match(m.pattern, file) + return match +} + +func (m *moduleSpec) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + var b bytes.Buffer + for i, f := range m.filter { + if i > 0 { + b.WriteRune(',') + } + fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) + } + return b.String() +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported. +func (m *moduleSpec) Get() interface{} { + return nil +} + +var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") + +// Syntax: -vmodule=recordio=2,file=1,gfs*=3 +func (m *moduleSpec) Set(value string) error { + var filter []modulePat + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return errVmoduleSyntax + } + pattern := patLev[0] + v, err := strconv.Atoi(patLev[1]) + if err != nil { + return errors.New("syntax error: expect comma-separated list of filename=N") + } + if v < 0 { + return errors.New("negative value for vmodule level") + } + if v == 0 { + continue // Ignore. It's harmless but no point in paying the overhead. + } + // TODO: check syntax of filter? + filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(logging.verbosity, filter, true) + return nil +} + +// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters +// that require filepath.Match to be called to match the pattern. +func isLiteral(pattern string) bool { + return !strings.ContainsAny(pattern, `\*?[]`) +} + +// traceLocation represents the setting of the -log_backtrace_at flag. +type traceLocation struct { + file string + line int +} + +// isSet reports whether the trace location has been specified. +// logging.mu is held. +func (t *traceLocation) isSet() bool { + return t.line > 0 +} + +// match reports whether the specified file and line matches the trace location. +// The argument file name is the full path, not the basename specified in the flag. +// logging.mu is held. +func (t *traceLocation) match(file string, line int) bool { + if t.line != line { + return false + } + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + return t.file == file +} + +func (t *traceLocation) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + return fmt.Sprintf("%s:%d", t.file, t.line) +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported +func (t *traceLocation) Get() interface{} { + return nil +} + +var errTraceSyntax = errors.New("syntax error: expect file.go:234") + +// Syntax: -log_backtrace_at=gopherflakes.go:234 +// Note that unlike vmodule the file extension is included here. +func (t *traceLocation) Set(value string) error { + if value == "" { + // Unset. + t.line = 0 + t.file = "" + } + fields := strings.Split(value, ":") + if len(fields) != 2 { + return errTraceSyntax + } + file, line := fields[0], fields[1] + if !strings.Contains(file, ".") { + return errTraceSyntax + } + v, err := strconv.Atoi(line) + if err != nil { + return errTraceSyntax + } + if v <= 0 { + return errors.New("negative or zero value for level") + } + logging.mu.Lock() + defer logging.mu.Unlock() + t.line = v + t.file = file + return nil +} + +// flushSyncWriter is the interface satisfied by logging destinations. +type flushSyncWriter interface { + Flush() error + Sync() error + io.Writer +} + +func init() { + flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") + flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + flag.Var(&logging.verbosity, "v", "log level for V logs") + flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") + flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") + + // Default stderrThreshold is ERROR. + logging.stderrThreshold = errorLog + + logging.setVState(0, nil, false) + go logging.flushDaemon() +} + +// Flush flushes all pending log I/O. +func Flush() { + logging.lockAndFlushAll() +} + +// loggingT collects all the global state of the logging setup. +type loggingT struct { + // Boolean flags. Not handled atomically because the flag.Value interface + // does not let us avoid the =true, and that shorthand is necessary for + // compatibility. TODO: does this matter enough to fix? Seems unlikely. + toStderr bool // The -logtostderr flag. + alsoToStderr bool // The -alsologtostderr flag. + + // Level flag. Handled atomically. + stderrThreshold severity // The -stderrthreshold flag. + + // freeList is a list of byte buffers, maintained under freeListMu. + freeList *buffer + // freeListMu maintains the free list. It is separate from the main mutex + // so buffers can be grabbed and printed to without holding the main lock, + // for better parallelization. + freeListMu sync.Mutex + + // mu protects the remaining elements of this structure and is + // used to synchronize logging. + mu sync.Mutex + // file holds writer for each of the log types. + file [numSeverity]flushSyncWriter + // pcs is used in V to avoid an allocation when computing the caller's PC. + pcs [1]uintptr + // vmap is a cache of the V Level for each V() call site, identified by PC. + // It is wiped whenever the vmodule flag changes state. + vmap map[uintptr]Level + // filterLength stores the length of the vmodule filter chain. If greater + // than zero, it means vmodule is enabled. It may be read safely + // using sync.LoadInt32, but is only modified under mu. + filterLength int32 + // traceLocation is the state of the -log_backtrace_at flag. + traceLocation traceLocation + // These flags are modified only under lock, although verbosity may be fetched + // safely using atomic.LoadInt32. + vmodule moduleSpec // The state of the -vmodule flag. + verbosity Level // V logging level, the value of the -v flag/ +} + +// buffer holds a byte Buffer for reuse. The zero value is ready for use. +type buffer struct { + bytes.Buffer + tmp [64]byte // temporary byte array for creating headers. + next *buffer +} + +var logging loggingT + +// setVState sets a consistent state for V logging. +// l.mu is held. +func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { + // Turn verbosity off so V will not fire while we are in transition. + logging.verbosity.set(0) + // Ditto for filter length. + atomic.StoreInt32(&logging.filterLength, 0) + + // Set the new filters and wipe the pc->Level map if the filter has changed. + if setFilter { + logging.vmodule.filter = filter + logging.vmap = make(map[uintptr]Level) + } + + // Things are consistent now, so enable filtering and verbosity. + // They are enabled in order opposite to that in V. + atomic.StoreInt32(&logging.filterLength, int32(len(filter))) + logging.verbosity.set(verbosity) +} + +// getBuffer returns a new, ready-to-use buffer. +func (l *loggingT) getBuffer() *buffer { + l.freeListMu.Lock() + b := l.freeList + if b != nil { + l.freeList = b.next + } + l.freeListMu.Unlock() + if b == nil { + b = new(buffer) + } else { + b.next = nil + b.Reset() + } + return b +} + +// putBuffer returns a buffer to the free list. +func (l *loggingT) putBuffer(b *buffer) { + if b.Len() >= 256 { + // Let big buffers die a natural death. + return + } + l.freeListMu.Lock() + b.next = l.freeList + l.freeList = b + l.freeListMu.Unlock() +} + +var timeNow = time.Now // Stubbed out for testing. + +/* +header formats a log header as defined by the C++ implementation. +It returns a buffer containing the formatted header and the user's file and line number. +The depth specifies how many stack frames above lives the source line to be identified in the log message. + +Log lines have this form: + Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +where the fields are defined as follows: + L A single character, representing the log level (eg 'I' for INFO) + mm The month (zero padded; ie May is '05') + dd The day (zero padded) + hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds + threadid The space-padded thread ID as returned by GetTID() + file The file name + line The line number + msg The user-supplied message +*/ +func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { + _, file, line, ok := runtime.Caller(3 + depth) + if !ok { + file = "???" + line = 1 + } else { + slash := strings.LastIndex(file, "/") + if slash >= 0 { + file = file[slash+1:] + } + } + return l.formatHeader(s, file, line), file, line +} + +// formatHeader formats a log header using the provided file name and line number. +func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { + now := timeNow() + if line < 0 { + line = 0 // not a real line number, but acceptable to someDigits + } + if s > fatalLog { + s = infoLog // for safety. + } + buf := l.getBuffer() + + // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. + // It's worth about 3X. Fprintf is hard. + _, month, day := now.Date() + hour, minute, second := now.Clock() + // Lmmdd hh:mm:ss.uuuuuu threadid file:line] + buf.tmp[0] = severityChar[s] + buf.twoDigits(1, int(month)) + buf.twoDigits(3, day) + buf.tmp[5] = ' ' + buf.twoDigits(6, hour) + buf.tmp[8] = ':' + buf.twoDigits(9, minute) + buf.tmp[11] = ':' + buf.twoDigits(12, second) + buf.tmp[14] = '.' + buf.nDigits(6, 15, now.Nanosecond()/1000, '0') + buf.tmp[21] = ' ' + buf.nDigits(7, 22, pid, ' ') // TODO: should be TID + buf.tmp[29] = ' ' + buf.Write(buf.tmp[:30]) + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + return buf +} + +// Some custom tiny helper functions to print the log header efficiently. + +const digits = "0123456789" + +// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. +func (buf *buffer) twoDigits(i, d int) { + buf.tmp[i+1] = digits[d%10] + d /= 10 + buf.tmp[i] = digits[d%10] +} + +// nDigits formats an n-digit integer at buf.tmp[i], +// padding with pad on the left. +// It assumes d >= 0. +func (buf *buffer) nDigits(n, i, d int, pad byte) { + j := n - 1 + for ; j >= 0 && d > 0; j-- { + buf.tmp[i+j] = digits[d%10] + d /= 10 + } + for ; j >= 0; j-- { + buf.tmp[i+j] = pad + } +} + +// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. +func (buf *buffer) someDigits(i, d int) int { + // Print into the top, then copy down. We know there's space for at least + // a 10-digit number. + j := len(buf.tmp) + for { + j-- + buf.tmp[j] = digits[d%10] + d /= 10 + if d == 0 { + break + } + } + return copy(buf.tmp[i:], buf.tmp[j:]) +} + +func (l *loggingT) println(s severity, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintln(buf, args...) + l.output(s, buf, file, line, false) +} + +func (l *loggingT) print(s severity, args ...interface{}) { + l.printDepth(s, 1, args...) +} + +func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { + buf, file, line := l.header(s, depth) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +func (l *loggingT) printf(s severity, format string, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintf(buf, format, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +// printWithFileLine behaves like print but uses the provided file and line number. If +// alsoLogToStderr is true, the log message always appears on standard error; it +// will also appear in the log file unless --logtostderr is set. +func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { + buf := l.formatHeader(s, file, line) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, alsoToStderr) +} + +// output writes the data to the log files and releases the buffer. +func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { + l.mu.Lock() + if l.traceLocation.isSet() { + if l.traceLocation.match(file, line) { + buf.Write(stacks(false)) + } + } + data := buf.Bytes() + if !flag.Parsed() { + os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) + os.Stderr.Write(data) + } else if l.toStderr { + os.Stderr.Write(data) + } else { + if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { + os.Stderr.Write(data) + } + if l.file[s] == nil { + if err := l.createFiles(s); err != nil { + os.Stderr.Write(data) // Make sure the message appears somewhere. + l.exit(err) + } + } + switch s { + case fatalLog: + l.file[fatalLog].Write(data) + fallthrough + case errorLog: + l.file[errorLog].Write(data) + fallthrough + case warningLog: + l.file[warningLog].Write(data) + fallthrough + case infoLog: + l.file[infoLog].Write(data) + } + } + if s == fatalLog { + // If we got here via Exit rather than Fatal, print no stacks. + if atomic.LoadUint32(&fatalNoStacks) > 0 { + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(1) + } + // Dump all goroutine stacks before exiting. + // First, make sure we see the trace for the current goroutine on standard error. + // If -logtostderr has been specified, the loop below will do that anyway + // as the first stack in the full dump. + if !l.toStderr { + os.Stderr.Write(stacks(false)) + } + // Write the stack trace for all goroutines to the files. + trace := stacks(true) + logExitFunc = func(error) {} // If we get a write error, we'll still exit below. + for log := fatalLog; log >= infoLog; log-- { + if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. + f.Write(trace) + } + } + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. + } + l.putBuffer(buf) + l.mu.Unlock() + if stats := severityStats[s]; stats != nil { + atomic.AddInt64(&stats.lines, 1) + atomic.AddInt64(&stats.bytes, int64(len(data))) + } +} + +// timeoutFlush calls Flush and returns when it completes or after timeout +// elapses, whichever happens first. This is needed because the hooks invoked +// by Flush may deadlock when glog.Fatal is called from a hook that holds +// a lock. +func timeoutFlush(timeout time.Duration) { + done := make(chan bool, 1) + go func() { + Flush() // calls logging.lockAndFlushAll() + done <- true + }() + select { + case <-done: + case <-time.After(timeout): + fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) + } +} + +// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. +func stacks(all bool) []byte { + // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. + n := 10000 + if all { + n = 100000 + } + var trace []byte + for i := 0; i < 5; i++ { + trace = make([]byte, n) + nbytes := runtime.Stack(trace, all) + if nbytes < len(trace) { + return trace[:nbytes] + } + n *= 2 + } + return trace +} + +// logExitFunc provides a simple mechanism to override the default behavior +// of exiting on error. Used in testing and to guarantee we reach a required exit +// for fatal logs. Instead, exit could be a function rather than a method but that +// would make its use clumsier. +var logExitFunc func(error) + +// exit is called if there is trouble creating or writing log files. +// It flushes the logs and exits the program; there's no point in hanging around. +// l.mu is held. +func (l *loggingT) exit(err error) { + fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) + // If logExitFunc is set, we do that instead of exiting. + if logExitFunc != nil { + logExitFunc(err) + return + } + l.flushAll() + os.Exit(2) +} + +// syncBuffer joins a bufio.Writer to its underlying file, providing access to the +// file's Sync method and providing a wrapper for the Write method that provides log +// file rotation. There are conflicting methods, so the file cannot be embedded. +// l.mu is held for all its methods. +type syncBuffer struct { + logger *loggingT + *bufio.Writer + file *os.File + sev severity + nbytes uint64 // The number of bytes written to this file +} + +func (sb *syncBuffer) Sync() error { + return sb.file.Sync() +} + +func (sb *syncBuffer) Write(p []byte) (n int, err error) { + if sb.nbytes+uint64(len(p)) >= MaxSize { + if err := sb.rotateFile(time.Now()); err != nil { + sb.logger.exit(err) + } + } + n, err = sb.Writer.Write(p) + sb.nbytes += uint64(n) + if err != nil { + sb.logger.exit(err) + } + return +} + +// rotateFile closes the syncBuffer's file and starts a new one. +func (sb *syncBuffer) rotateFile(now time.Time) error { + if sb.file != nil { + sb.Flush() + sb.file.Close() + } + var err error + sb.file, _, err = create(severityName[sb.sev], now) + sb.nbytes = 0 + if err != nil { + return err + } + + sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) + + // Write header. + var buf bytes.Buffer + fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) + fmt.Fprintf(&buf, "Running on machine: %s\n", host) + fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") + n, err := sb.file.Write(buf.Bytes()) + sb.nbytes += uint64(n) + return err +} + +// bufferSize sizes the buffer associated with each log file. It's large +// so that log records can accumulate without the logging thread blocking +// on disk I/O. The flushDaemon will block instead. +const bufferSize = 256 * 1024 + +// createFiles creates all the log files for severity from sev down to infoLog. +// l.mu is held. +func (l *loggingT) createFiles(sev severity) error { + now := time.Now() + // Files are created in decreasing severity order, so as soon as we find one + // has already been created, we can stop. + for s := sev; s >= infoLog && l.file[s] == nil; s-- { + sb := &syncBuffer{ + logger: l, + sev: s, + } + if err := sb.rotateFile(now); err != nil { + return err + } + l.file[s] = sb + } + return nil +} + +const flushInterval = 30 * time.Second + +// flushDaemon periodically flushes the log file buffers. +func (l *loggingT) flushDaemon() { + for _ = range time.NewTicker(flushInterval).C { + l.lockAndFlushAll() + } +} + +// lockAndFlushAll is like flushAll but locks l.mu first. +func (l *loggingT) lockAndFlushAll() { + l.mu.Lock() + l.flushAll() + l.mu.Unlock() +} + +// flushAll flushes all the logs and attempts to "sync" their data to disk. +// l.mu is held. +func (l *loggingT) flushAll() { + // Flush from fatal down, in case there's trouble flushing. + for s := fatalLog; s >= infoLog; s-- { + file := l.file[s] + if file != nil { + file.Flush() // ignore error + file.Sync() // ignore error + } + } +} + +// CopyStandardLogTo arranges for messages written to the Go "log" package's +// default logs to also appear in the Google logs for the named and lower +// severities. Subsequent changes to the standard log's default output location +// or format may break this behavior. +// +// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not +// recognized, CopyStandardLogTo panics. +func CopyStandardLogTo(name string) { + sev, ok := severityByName(name) + if !ok { + panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) + } + // Set a log format that captures the user's file and line: + // d.go:23: message + stdLog.SetFlags(stdLog.Lshortfile) + stdLog.SetOutput(logBridge(sev)) +} + +// logBridge provides the Write method that enables CopyStandardLogTo to connect +// Go's standard logs to the logs provided by this package. +type logBridge severity + +// Write parses the standard logging line and passes its components to the +// logger for severity(lb). +func (lb logBridge) Write(b []byte) (n int, err error) { + var ( + file = "???" + line = 1 + text string + ) + // Split "d.go:23: message" into "d.go", "23", and "message". + if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { + text = fmt.Sprintf("bad log format: %s", b) + } else { + file = string(parts[0]) + text = string(parts[2][1:]) // skip leading space + line, err = strconv.Atoi(string(parts[1])) + if err != nil { + text = fmt.Sprintf("bad line number: %s", b) + line = 1 + } + } + // printWithFileLine with alsoToStderr=true, so standard log messages + // always appear on standard error. + logging.printWithFileLine(severity(lb), file, line, true, text) + return len(b), nil +} + +// setV computes and remembers the V level for a given PC +// when vmodule is enabled. +// File pattern matching takes the basename of the file, stripped +// of its .go suffix, and uses filepath.Match, which is a little more +// general than the *? matching used in C++. +// l.mu is held. +func (l *loggingT) setV(pc uintptr) Level { + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + // The file is something like /a/b/c/d.go. We want just the d. + if strings.HasSuffix(file, ".go") { + file = file[:len(file)-3] + } + if slash := strings.LastIndex(file, "/"); slash >= 0 { + file = file[slash+1:] + } + for _, filter := range l.vmodule.filter { + if filter.match(file) { + l.vmap[pc] = filter.level + return filter.level + } + } + l.vmap[pc] = 0 + return 0 +} + +// Verbose is a boolean type that implements Infof (like Printf) etc. +// See the documentation of V for more information. +type Verbose bool + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a boolean of type Verbose, which implements Info, Infoln +// and Infof. These methods will write to the Info log if called. +// Thus, one may write either +// if glog.V(2) { glog.Info("log this") } +// or +// glog.V(2).Info("log this") +// The second form is shorter but the first is cheaper if logging is off because it does +// not evaluate its arguments. +// +// Whether an individual call to V generates a log record depends on the setting of +// the -v and --vmodule flags; both are off by default. If the level in the call to +// V is at least the value of -v, or of -vmodule for the source file containing the +// call, the V call will log. +func V(level Level) Verbose { + // This function tries hard to be cheap unless there's work to do. + // The fast path is two atomic loads and compares. + + // Here is a cheap but safe test to see if V logging is enabled globally. + if logging.verbosity.get() >= level { + return Verbose(true) + } + + // It's off globally but it vmodule may still be set. + // Here is another cheap but safe test to see if vmodule is enabled. + if atomic.LoadInt32(&logging.filterLength) > 0 { + // Now we need a proper lock to use the logging structure. The pcs field + // is shared so we must lock before accessing it. This is fairly expensive, + // but if V logging is enabled we're slow anyway. + logging.mu.Lock() + defer logging.mu.Unlock() + if runtime.Callers(2, logging.pcs[:]) == 0 { + return Verbose(false) + } + v, ok := logging.vmap[logging.pcs[0]] + if !ok { + v = logging.setV(logging.pcs[0]) + } + return Verbose(v >= level) + } + return Verbose(false) +} + +// Info is equivalent to the global Info function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Info(args ...interface{}) { + if v { + logging.print(infoLog, args...) + } +} + +// Infoln is equivalent to the global Infoln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infoln(args ...interface{}) { + if v { + logging.println(infoLog, args...) + } +} + +// Infof is equivalent to the global Infof function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infof(format string, args ...interface{}) { + if v { + logging.printf(infoLog, format, args...) + } +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Info(args ...interface{}) { + logging.print(infoLog, args...) +} + +// InfoDepth acts as Info but uses depth to determine which call frame to log. +// InfoDepth(0, "msg") is the same as Info("msg"). +func InfoDepth(depth int, args ...interface{}) { + logging.printDepth(infoLog, depth, args...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Infoln(args ...interface{}) { + logging.println(infoLog, args...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Infof(format string, args ...interface{}) { + logging.printf(infoLog, format, args...) +} + +// Warning logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Warning(args ...interface{}) { + logging.print(warningLog, args...) +} + +// WarningDepth acts as Warning but uses depth to determine which call frame to log. +// WarningDepth(0, "msg") is the same as Warning("msg"). +func WarningDepth(depth int, args ...interface{}) { + logging.printDepth(warningLog, depth, args...) +} + +// Warningln logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Warningln(args ...interface{}) { + logging.println(warningLog, args...) +} + +// Warningf logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Warningf(format string, args ...interface{}) { + logging.printf(warningLog, format, args...) +} + +// Error logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Error(args ...interface{}) { + logging.print(errorLog, args...) +} + +// ErrorDepth acts as Error but uses depth to determine which call frame to log. +// ErrorDepth(0, "msg") is the same as Error("msg"). +func ErrorDepth(depth int, args ...interface{}) { + logging.printDepth(errorLog, depth, args...) +} + +// Errorln logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Errorln(args ...interface{}) { + logging.println(errorLog, args...) +} + +// Errorf logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Errorf(format string, args ...interface{}) { + logging.printf(errorLog, format, args...) +} + +// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Fatal(args ...interface{}) { + logging.print(fatalLog, args...) +} + +// FatalDepth acts as Fatal but uses depth to determine which call frame to log. +// FatalDepth(0, "msg") is the same as Fatal("msg"). +func FatalDepth(depth int, args ...interface{}) { + logging.printDepth(fatalLog, depth, args...) +} + +// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Fatalln(args ...interface{}) { + logging.println(fatalLog, args...) +} + +// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Fatalf(format string, args ...interface{}) { + logging.printf(fatalLog, format, args...) +} + +// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. +// It allows Exit and relatives to use the Fatal logs. +var fatalNoStacks uint32 + +// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Exit(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.print(fatalLog, args...) +} + +// ExitDepth acts as Exit but uses depth to determine which call frame to log. +// ExitDepth(0, "msg") is the same as Exit("msg"). +func ExitDepth(depth int, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printDepth(fatalLog, depth, args...) +} + +// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +func Exitln(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.println(fatalLog, args...) +} + +// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Exitf(format string, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printf(fatalLog, format, args...) +} diff --git a/vendor/github.com/golang/glog/glog_file.go b/vendor/github.com/golang/glog/glog_file.go new file mode 100644 index 0000000..65075d2 --- /dev/null +++ b/vendor/github.com/golang/glog/glog_file.go @@ -0,0 +1,124 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// 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. + +// File I/O for logs. + +package glog + +import ( + "errors" + "flag" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + "sync" + "time" +) + +// MaxSize is the maximum size of a log file in bytes. +var MaxSize uint64 = 1024 * 1024 * 1800 + +// logDirs lists the candidate directories for new log files. +var logDirs []string + +// If non-empty, overrides the choice of directory in which to write logs. +// See createLogDirs for the full list of possible destinations. +var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") + +func createLogDirs() { + if *logDir != "" { + logDirs = append(logDirs, *logDir) + } + logDirs = append(logDirs, os.TempDir()) +} + +var ( + pid = os.Getpid() + program = filepath.Base(os.Args[0]) + host = "unknownhost" + userName = "unknownuser" +) + +func init() { + h, err := os.Hostname() + if err == nil { + host = shortHostname(h) + } + + current, err := user.Current() + if err == nil { + userName = current.Username + } + + // Sanitize userName since it may contain filepath separators on Windows. + userName = strings.Replace(userName, `\`, "_", -1) +} + +// shortHostname returns its argument, truncating at the first period. +// For instance, given "www.google.com" it returns "www". +func shortHostname(hostname string) string { + if i := strings.Index(hostname, "."); i >= 0 { + return hostname[:i] + } + return hostname +} + +// logName returns a new log file name containing tag, with start time t, and +// the name for the symlink for tag. +func logName(tag string, t time.Time) (name, link string) { + name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", + program, + host, + userName, + tag, + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + pid) + return name, program + "." + tag +} + +var onceLogDirs sync.Once + +// create creates a new log file and returns the file and its filename, which +// contains tag ("INFO", "FATAL", etc.) and t. If the file is created +// successfully, create also attempts to update the symlink for that tag, ignoring +// errors. +func create(tag string, t time.Time) (f *os.File, filename string, err error) { + onceLogDirs.Do(createLogDirs) + if len(logDirs) == 0 { + return nil, "", errors.New("log: no log dirs") + } + name, link := logName(tag, t) + var lastErr error + for _, dir := range logDirs { + fname := filepath.Join(dir, name) + f, err := os.Create(fname) + if err == nil { + symlink := filepath.Join(dir, link) + os.Remove(symlink) // ignore err + os.Symlink(name, symlink) // ignore err + return f, fname, nil + } + lastErr = err + } + return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) +} diff --git a/vendor/github.com/golang/protobuf/AUTHORS b/vendor/github.com/golang/protobuf/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/github.com/golang/protobuf/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/github.com/golang/protobuf/CONTRIBUTORS b/vendor/github.com/golang/protobuf/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/github.com/golang/protobuf/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/github.com/golang/protobuf/LICENSE b/vendor/github.com/golang/protobuf/LICENSE new file mode 100644 index 0000000..0f64693 --- /dev/null +++ b/vendor/github.com/golang/protobuf/LICENSE @@ -0,0 +1,28 @@ +Copyright 2010 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/github.com/golang/protobuf/proto/clone.go b/vendor/github.com/golang/protobuf/proto/clone.go new file mode 100644 index 0000000..3cd3249 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/clone.go @@ -0,0 +1,253 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Protocol buffer deep copy and merge. +// TODO: RawMessage. + +package proto + +import ( + "fmt" + "log" + "reflect" + "strings" +) + +// Clone returns a deep copy of a protocol buffer. +func Clone(src Message) Message { + in := reflect.ValueOf(src) + if in.IsNil() { + return src + } + out := reflect.New(in.Type().Elem()) + dst := out.Interface().(Message) + Merge(dst, src) + return dst +} + +// Merger is the interface representing objects that can merge messages of the same type. +type Merger interface { + // Merge merges src into this message. + // Required and optional fields that are set in src will be set to that value in dst. + // Elements of repeated fields will be appended. + // + // Merge may panic if called with a different argument type than the receiver. + Merge(src Message) +} + +// generatedMerger is the custom merge method that generated protos will have. +// We must add this method since a generate Merge method will conflict with +// many existing protos that have a Merge data field already defined. +type generatedMerger interface { + XXX_Merge(src Message) +} + +// Merge merges src into dst. +// Required and optional fields that are set in src will be set to that value in dst. +// Elements of repeated fields will be appended. +// Merge panics if src and dst are not the same type, or if dst is nil. +func Merge(dst, src Message) { + if m, ok := dst.(Merger); ok { + m.Merge(src) + return + } + + in := reflect.ValueOf(src) + out := reflect.ValueOf(dst) + if out.IsNil() { + panic("proto: nil destination") + } + if in.Type() != out.Type() { + panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) + } + if in.IsNil() { + return // Merge from nil src is a noop + } + if m, ok := dst.(generatedMerger); ok { + m.XXX_Merge(src) + return + } + mergeStruct(out.Elem(), in.Elem()) +} + +func mergeStruct(out, in reflect.Value) { + sprop := GetProperties(in.Type()) + for i := 0; i < in.NumField(); i++ { + f := in.Type().Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) + } + + if emIn, err := extendable(in.Addr().Interface()); err == nil { + emOut, _ := extendable(out.Addr().Interface()) + mIn, muIn := emIn.extensionsRead() + if mIn != nil { + mOut := emOut.extensionsWrite() + muIn.Lock() + mergeExtension(mOut, mIn) + muIn.Unlock() + } + } + + uf := in.FieldByName("XXX_unrecognized") + if !uf.IsValid() { + return + } + uin := uf.Bytes() + if len(uin) > 0 { + out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) + } +} + +// mergeAny performs a merge between two values of the same type. +// viaPtr indicates whether the values were indirected through a pointer (implying proto2). +// prop is set if this is a struct field (it may be nil). +func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { + if in.Type() == protoMessageType { + if !in.IsNil() { + if out.IsNil() { + out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) + } else { + Merge(out.Interface().(Message), in.Interface().(Message)) + } + } + return + } + switch in.Kind() { + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, + reflect.String, reflect.Uint32, reflect.Uint64: + if !viaPtr && isProto3Zero(in) { + return + } + out.Set(in) + case reflect.Interface: + // Probably a oneof field; copy non-nil values. + if in.IsNil() { + return + } + // Allocate destination if it is not set, or set to a different type. + // Otherwise we will merge as normal. + if out.IsNil() || out.Elem().Type() != in.Elem().Type() { + out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) + } + mergeAny(out.Elem(), in.Elem(), false, nil) + case reflect.Map: + if in.Len() == 0 { + return + } + if out.IsNil() { + out.Set(reflect.MakeMap(in.Type())) + } + // For maps with value types of *T or []byte we need to deep copy each value. + elemKind := in.Type().Elem().Kind() + for _, key := range in.MapKeys() { + var val reflect.Value + switch elemKind { + case reflect.Ptr: + val = reflect.New(in.Type().Elem().Elem()) + mergeAny(val, in.MapIndex(key), false, nil) + case reflect.Slice: + val = in.MapIndex(key) + val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) + default: + val = in.MapIndex(key) + } + out.SetMapIndex(key, val) + } + case reflect.Ptr: + if in.IsNil() { + return + } + if out.IsNil() { + out.Set(reflect.New(in.Elem().Type())) + } + mergeAny(out.Elem(), in.Elem(), true, nil) + case reflect.Slice: + if in.IsNil() { + return + } + if in.Type().Elem().Kind() == reflect.Uint8 { + // []byte is a scalar bytes field, not a repeated field. + + // Edge case: if this is in a proto3 message, a zero length + // bytes field is considered the zero value, and should not + // be merged. + if prop != nil && prop.proto3 && in.Len() == 0 { + return + } + + // Make a deep copy. + // Append to []byte{} instead of []byte(nil) so that we never end up + // with a nil result. + out.SetBytes(append([]byte{}, in.Bytes()...)) + return + } + n := in.Len() + if out.IsNil() { + out.Set(reflect.MakeSlice(in.Type(), 0, n)) + } + switch in.Type().Elem().Kind() { + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, + reflect.String, reflect.Uint32, reflect.Uint64: + out.Set(reflect.AppendSlice(out, in)) + default: + for i := 0; i < n; i++ { + x := reflect.Indirect(reflect.New(in.Type().Elem())) + mergeAny(x, in.Index(i), false, nil) + out.Set(reflect.Append(out, x)) + } + } + case reflect.Struct: + mergeStruct(out, in) + default: + // unknown type, so not a protocol buffer + log.Printf("proto: don't know how to copy %v", in) + } +} + +func mergeExtension(out, in map[int32]Extension) { + for extNum, eIn := range in { + eOut := Extension{desc: eIn.desc} + if eIn.value != nil { + v := reflect.New(reflect.TypeOf(eIn.value)).Elem() + mergeAny(v, reflect.ValueOf(eIn.value), false, nil) + eOut.value = v.Interface() + } + if eIn.enc != nil { + eOut.enc = make([]byte, len(eIn.enc)) + copy(eOut.enc, eIn.enc) + } + + out[extNum] = eOut + } +} diff --git a/vendor/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/golang/protobuf/proto/decode.go new file mode 100644 index 0000000..63b0f08 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/decode.go @@ -0,0 +1,427 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for decoding protocol buffer data to construct in-memory representations. + */ + +import ( + "errors" + "fmt" + "io" +) + +// errOverflow is returned when an integer is too large to be represented. +var errOverflow = errors.New("proto: integer overflow") + +// ErrInternalBadWireType is returned by generated code when an incorrect +// wire type is encountered. It does not get returned to user code. +var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") + +// DecodeVarint reads a varint-encoded integer from the slice. +// It returns the integer and the number of bytes consumed, or +// zero if there is not enough. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func DecodeVarint(buf []byte) (x uint64, n int) { + for shift := uint(0); shift < 64; shift += 7 { + if n >= len(buf) { + return 0, 0 + } + b := uint64(buf[n]) + n++ + x |= (b & 0x7F) << shift + if (b & 0x80) == 0 { + return x, n + } + } + + // The number is too large to represent in a 64-bit value. + return 0, 0 +} + +func (p *Buffer) decodeVarintSlow() (x uint64, err error) { + i := p.index + l := len(p.buf) + + for shift := uint(0); shift < 64; shift += 7 { + if i >= l { + err = io.ErrUnexpectedEOF + return + } + b := p.buf[i] + i++ + x |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + p.index = i + return + } + } + + // The number is too large to represent in a 64-bit value. + err = errOverflow + return +} + +// DecodeVarint reads a varint-encoded integer from the Buffer. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func (p *Buffer) DecodeVarint() (x uint64, err error) { + i := p.index + buf := p.buf + + if i >= len(buf) { + return 0, io.ErrUnexpectedEOF + } else if buf[i] < 0x80 { + p.index++ + return uint64(buf[i]), nil + } else if len(buf)-i < 10 { + return p.decodeVarintSlow() + } + + var b uint64 + // we already checked the first byte + x = uint64(buf[i]) - 0x80 + i++ + + b = uint64(buf[i]) + i++ + x += b << 7 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 7 + + b = uint64(buf[i]) + i++ + x += b << 14 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 14 + + b = uint64(buf[i]) + i++ + x += b << 21 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 21 + + b = uint64(buf[i]) + i++ + x += b << 28 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 28 + + b = uint64(buf[i]) + i++ + x += b << 35 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 35 + + b = uint64(buf[i]) + i++ + x += b << 42 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 42 + + b = uint64(buf[i]) + i++ + x += b << 49 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 49 + + b = uint64(buf[i]) + i++ + x += b << 56 + if b&0x80 == 0 { + goto done + } + x -= 0x80 << 56 + + b = uint64(buf[i]) + i++ + x += b << 63 + if b&0x80 == 0 { + goto done + } + + return 0, errOverflow + +done: + p.index = i + return x, nil +} + +// DecodeFixed64 reads a 64-bit integer from the Buffer. +// This is the format for the +// fixed64, sfixed64, and double protocol buffer types. +func (p *Buffer) DecodeFixed64() (x uint64, err error) { + // x, err already 0 + i := p.index + 8 + if i < 0 || i > len(p.buf) { + err = io.ErrUnexpectedEOF + return + } + p.index = i + + x = uint64(p.buf[i-8]) + x |= uint64(p.buf[i-7]) << 8 + x |= uint64(p.buf[i-6]) << 16 + x |= uint64(p.buf[i-5]) << 24 + x |= uint64(p.buf[i-4]) << 32 + x |= uint64(p.buf[i-3]) << 40 + x |= uint64(p.buf[i-2]) << 48 + x |= uint64(p.buf[i-1]) << 56 + return +} + +// DecodeFixed32 reads a 32-bit integer from the Buffer. +// This is the format for the +// fixed32, sfixed32, and float protocol buffer types. +func (p *Buffer) DecodeFixed32() (x uint64, err error) { + // x, err already 0 + i := p.index + 4 + if i < 0 || i > len(p.buf) { + err = io.ErrUnexpectedEOF + return + } + p.index = i + + x = uint64(p.buf[i-4]) + x |= uint64(p.buf[i-3]) << 8 + x |= uint64(p.buf[i-2]) << 16 + x |= uint64(p.buf[i-1]) << 24 + return +} + +// DecodeZigzag64 reads a zigzag-encoded 64-bit integer +// from the Buffer. +// This is the format used for the sint64 protocol buffer type. +func (p *Buffer) DecodeZigzag64() (x uint64, err error) { + x, err = p.DecodeVarint() + if err != nil { + return + } + x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63) + return +} + +// DecodeZigzag32 reads a zigzag-encoded 32-bit integer +// from the Buffer. +// This is the format used for the sint32 protocol buffer type. +func (p *Buffer) DecodeZigzag32() (x uint64, err error) { + x, err = p.DecodeVarint() + if err != nil { + return + } + x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31)) + return +} + +// DecodeRawBytes reads a count-delimited byte buffer from the Buffer. +// This is the format used for the bytes protocol buffer +// type and for embedded messages. +func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) { + n, err := p.DecodeVarint() + if err != nil { + return nil, err + } + + nb := int(n) + if nb < 0 { + return nil, fmt.Errorf("proto: bad byte length %d", nb) + } + end := p.index + nb + if end < p.index || end > len(p.buf) { + return nil, io.ErrUnexpectedEOF + } + + if !alloc { + // todo: check if can get more uses of alloc=false + buf = p.buf[p.index:end] + p.index += nb + return + } + + buf = make([]byte, nb) + copy(buf, p.buf[p.index:]) + p.index += nb + return +} + +// DecodeStringBytes reads an encoded string from the Buffer. +// This is the format used for the proto2 string type. +func (p *Buffer) DecodeStringBytes() (s string, err error) { + buf, err := p.DecodeRawBytes(false) + if err != nil { + return + } + return string(buf), nil +} + +// Unmarshaler is the interface representing objects that can +// unmarshal themselves. The argument points to data that may be +// overwritten, so implementations should not keep references to the +// buffer. +// Unmarshal implementations should not clear the receiver. +// Any unmarshaled data should be merged into the receiver. +// Callers of Unmarshal that do not want to retain existing data +// should Reset the receiver before calling Unmarshal. +type Unmarshaler interface { + Unmarshal([]byte) error +} + +// newUnmarshaler is the interface representing objects that can +// unmarshal themselves. The semantics are identical to Unmarshaler. +// +// This exists to support protoc-gen-go generated messages. +// The proto package will stop type-asserting to this interface in the future. +// +// DO NOT DEPEND ON THIS. +type newUnmarshaler interface { + XXX_Unmarshal([]byte) error +} + +// Unmarshal parses the protocol buffer representation in buf and places the +// decoded result in pb. If the struct underlying pb does not match +// the data in buf, the results can be unpredictable. +// +// Unmarshal resets pb before starting to unmarshal, so any +// existing data in pb is always removed. Use UnmarshalMerge +// to preserve and append to existing data. +func Unmarshal(buf []byte, pb Message) error { + pb.Reset() + if u, ok := pb.(newUnmarshaler); ok { + return u.XXX_Unmarshal(buf) + } + if u, ok := pb.(Unmarshaler); ok { + return u.Unmarshal(buf) + } + return NewBuffer(buf).Unmarshal(pb) +} + +// UnmarshalMerge parses the protocol buffer representation in buf and +// writes the decoded result to pb. If the struct underlying pb does not match +// the data in buf, the results can be unpredictable. +// +// UnmarshalMerge merges into existing data in pb. +// Most code should use Unmarshal instead. +func UnmarshalMerge(buf []byte, pb Message) error { + if u, ok := pb.(newUnmarshaler); ok { + return u.XXX_Unmarshal(buf) + } + if u, ok := pb.(Unmarshaler); ok { + // NOTE: The history of proto have unfortunately been inconsistent + // whether Unmarshaler should or should not implicitly clear itself. + // Some implementations do, most do not. + // Thus, calling this here may or may not do what people want. + // + // See https://github.com/golang/protobuf/issues/424 + return u.Unmarshal(buf) + } + return NewBuffer(buf).Unmarshal(pb) +} + +// DecodeMessage reads a count-delimited message from the Buffer. +func (p *Buffer) DecodeMessage(pb Message) error { + enc, err := p.DecodeRawBytes(false) + if err != nil { + return err + } + return NewBuffer(enc).Unmarshal(pb) +} + +// DecodeGroup reads a tag-delimited group from the Buffer. +// StartGroup tag is already consumed. This function consumes +// EndGroup tag. +func (p *Buffer) DecodeGroup(pb Message) error { + b := p.buf[p.index:] + x, y := findEndGroup(b) + if x < 0 { + return io.ErrUnexpectedEOF + } + err := Unmarshal(b[:x], pb) + p.index += y + return err +} + +// Unmarshal parses the protocol buffer representation in the +// Buffer and places the decoded result in pb. If the struct +// underlying pb does not match the data in the buffer, the results can be +// unpredictable. +// +// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal. +func (p *Buffer) Unmarshal(pb Message) error { + // If the object can unmarshal itself, let it. + if u, ok := pb.(newUnmarshaler); ok { + err := u.XXX_Unmarshal(p.buf[p.index:]) + p.index = len(p.buf) + return err + } + if u, ok := pb.(Unmarshaler); ok { + // NOTE: The history of proto have unfortunately been inconsistent + // whether Unmarshaler should or should not implicitly clear itself. + // Some implementations do, most do not. + // Thus, calling this here may or may not do what people want. + // + // See https://github.com/golang/protobuf/issues/424 + err := u.Unmarshal(p.buf[p.index:]) + p.index = len(p.buf) + return err + } + + // Slow workaround for messages that aren't Unmarshalers. + // This includes some hand-coded .pb.go files and + // bootstrap protos. + // TODO: fix all of those and then add Unmarshal to + // the Message interface. Then: + // The cast above and code below can be deleted. + // The old unmarshaler can be deleted. + // Clients can call Unmarshal directly (can already do that, actually). + var info InternalMessageInfo + err := info.Unmarshal(pb, p.buf[p.index:]) + p.index = len(p.buf) + return err +} diff --git a/vendor/github.com/golang/protobuf/proto/deprecated.go b/vendor/github.com/golang/protobuf/proto/deprecated.go new file mode 100644 index 0000000..69de0ea --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/deprecated.go @@ -0,0 +1,38 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2018 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +// Deprecated: do not use. +type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } + +// Deprecated: do not use. +func GetStats() Stats { return Stats{} } diff --git a/vendor/github.com/golang/protobuf/proto/discard.go b/vendor/github.com/golang/protobuf/proto/discard.go new file mode 100644 index 0000000..dea2617 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/discard.go @@ -0,0 +1,350 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2017 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +type generatedDiscarder interface { + XXX_DiscardUnknown() +} + +// DiscardUnknown recursively discards all unknown fields from this message +// and all embedded messages. +// +// When unmarshaling a message with unrecognized fields, the tags and values +// of such fields are preserved in the Message. This allows a later call to +// marshal to be able to produce a message that continues to have those +// unrecognized fields. To avoid this, DiscardUnknown is used to +// explicitly clear the unknown fields after unmarshaling. +// +// For proto2 messages, the unknown fields of message extensions are only +// discarded from messages that have been accessed via GetExtension. +func DiscardUnknown(m Message) { + if m, ok := m.(generatedDiscarder); ok { + m.XXX_DiscardUnknown() + return + } + // TODO: Dynamically populate a InternalMessageInfo for legacy messages, + // but the master branch has no implementation for InternalMessageInfo, + // so it would be more work to replicate that approach. + discardLegacy(m) +} + +// DiscardUnknown recursively discards all unknown fields. +func (a *InternalMessageInfo) DiscardUnknown(m Message) { + di := atomicLoadDiscardInfo(&a.discard) + if di == nil { + di = getDiscardInfo(reflect.TypeOf(m).Elem()) + atomicStoreDiscardInfo(&a.discard, di) + } + di.discard(toPointer(&m)) +} + +type discardInfo struct { + typ reflect.Type + + initialized int32 // 0: only typ is valid, 1: everything is valid + lock sync.Mutex + + fields []discardFieldInfo + unrecognized field +} + +type discardFieldInfo struct { + field field // Offset of field, guaranteed to be valid + discard func(src pointer) +} + +var ( + discardInfoMap = map[reflect.Type]*discardInfo{} + discardInfoLock sync.Mutex +) + +func getDiscardInfo(t reflect.Type) *discardInfo { + discardInfoLock.Lock() + defer discardInfoLock.Unlock() + di := discardInfoMap[t] + if di == nil { + di = &discardInfo{typ: t} + discardInfoMap[t] = di + } + return di +} + +func (di *discardInfo) discard(src pointer) { + if src.isNil() { + return // Nothing to do. + } + + if atomic.LoadInt32(&di.initialized) == 0 { + di.computeDiscardInfo() + } + + for _, fi := range di.fields { + sfp := src.offset(fi.field) + fi.discard(sfp) + } + + // For proto2 messages, only discard unknown fields in message extensions + // that have been accessed via GetExtension. + if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil { + // Ignore lock since DiscardUnknown is not concurrency safe. + emm, _ := em.extensionsRead() + for _, mx := range emm { + if m, ok := mx.value.(Message); ok { + DiscardUnknown(m) + } + } + } + + if di.unrecognized.IsValid() { + *src.offset(di.unrecognized).toBytes() = nil + } +} + +func (di *discardInfo) computeDiscardInfo() { + di.lock.Lock() + defer di.lock.Unlock() + if di.initialized != 0 { + return + } + t := di.typ + n := t.NumField() + + for i := 0; i < n; i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + + dfi := discardFieldInfo{field: toField(&f)} + tf := f.Type + + // Unwrap tf to get its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name)) + } + + switch tf.Kind() { + case reflect.Struct: + switch { + case !isPointer: + panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name)) + case isSlice: // E.g., []*pb.T + di := getDiscardInfo(tf) + dfi.discard = func(src pointer) { + sps := src.getPointerSlice() + for _, sp := range sps { + if !sp.isNil() { + di.discard(sp) + } + } + } + default: // E.g., *pb.T + di := getDiscardInfo(tf) + dfi.discard = func(src pointer) { + sp := src.getPointer() + if !sp.isNil() { + di.discard(sp) + } + } + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name)) + default: // E.g., map[K]V + if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T) + dfi.discard = func(src pointer) { + sm := src.asPointerTo(tf).Elem() + if sm.Len() == 0 { + return + } + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + DiscardUnknown(val.Interface().(Message)) + } + } + } else { + dfi.discard = func(pointer) {} // Noop + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name)) + default: // E.g., interface{} + // TODO: Make this faster? + dfi.discard = func(src pointer) { + su := src.asPointerTo(tf).Elem() + if !su.IsNil() { + sv := su.Elem().Elem().Field(0) + if sv.Kind() == reflect.Ptr && sv.IsNil() { + return + } + switch sv.Type().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + DiscardUnknown(sv.Interface().(Message)) + } + } + } + } + default: + continue + } + di.fields = append(di.fields, dfi) + } + + di.unrecognized = invalidField + if f, ok := t.FieldByName("XXX_unrecognized"); ok { + if f.Type != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + di.unrecognized = toField(&f) + } + + atomic.StoreInt32(&di.initialized, 1) +} + +func discardLegacy(m Message) { + v := reflect.ValueOf(m) + if v.Kind() != reflect.Ptr || v.IsNil() { + return + } + v = v.Elem() + if v.Kind() != reflect.Struct { + return + } + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + vf := v.Field(i) + tf := f.Type + + // Unwrap tf to get its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name)) + } + + switch tf.Kind() { + case reflect.Struct: + switch { + case !isPointer: + panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name)) + case isSlice: // E.g., []*pb.T + for j := 0; j < vf.Len(); j++ { + discardLegacy(vf.Index(j).Interface().(Message)) + } + default: // E.g., *pb.T + discardLegacy(vf.Interface().(Message)) + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name)) + default: // E.g., map[K]V + tv := vf.Type().Elem() + if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T) + for _, key := range vf.MapKeys() { + val := vf.MapIndex(key) + discardLegacy(val.Interface().(Message)) + } + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name)) + default: // E.g., test_proto.isCommunique_Union interface + if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" { + vf = vf.Elem() // E.g., *test_proto.Communique_Msg + if !vf.IsNil() { + vf = vf.Elem() // E.g., test_proto.Communique_Msg + vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value + if vf.Kind() == reflect.Ptr { + discardLegacy(vf.Interface().(Message)) + } + } + } + } + } + } + + if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() { + if vf.Type() != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + vf.Set(reflect.ValueOf([]byte(nil))) + } + + // For proto2 messages, only discard unknown fields in message extensions + // that have been accessed via GetExtension. + if em, err := extendable(m); err == nil { + // Ignore lock since discardLegacy is not concurrency safe. + emm, _ := em.extensionsRead() + for _, mx := range emm { + if m, ok := mx.value.(Message); ok { + discardLegacy(m) + } + } + } +} diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go new file mode 100644 index 0000000..3abfed2 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/encode.go @@ -0,0 +1,203 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for encoding data into the wire format for protocol buffers. + */ + +import ( + "errors" + "reflect" +) + +var ( + // errRepeatedHasNil is the error returned if Marshal is called with + // a struct with a repeated field containing a nil element. + errRepeatedHasNil = errors.New("proto: repeated field has nil element") + + // errOneofHasNil is the error returned if Marshal is called with + // a struct with a oneof field containing a nil element. + errOneofHasNil = errors.New("proto: oneof field has nil value") + + // ErrNil is the error returned if Marshal is called with nil. + ErrNil = errors.New("proto: Marshal called with nil") + + // ErrTooLarge is the error returned if Marshal is called with a + // message that encodes to >2GB. + ErrTooLarge = errors.New("proto: message encodes to over 2 GB") +) + +// The fundamental encoders that put bytes on the wire. +// Those that take integer types all accept uint64 and are +// therefore of type valueEncoder. + +const maxVarintBytes = 10 // maximum length of a varint + +// EncodeVarint returns the varint encoding of x. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +// Not used by the package itself, but helpful to clients +// wishing to use the same encoding. +func EncodeVarint(x uint64) []byte { + var buf [maxVarintBytes]byte + var n int + for n = 0; x > 127; n++ { + buf[n] = 0x80 | uint8(x&0x7F) + x >>= 7 + } + buf[n] = uint8(x) + n++ + return buf[0:n] +} + +// EncodeVarint writes a varint-encoded integer to the Buffer. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func (p *Buffer) EncodeVarint(x uint64) error { + for x >= 1<<7 { + p.buf = append(p.buf, uint8(x&0x7f|0x80)) + x >>= 7 + } + p.buf = append(p.buf, uint8(x)) + return nil +} + +// SizeVarint returns the varint encoding size of an integer. +func SizeVarint(x uint64) int { + switch { + case x < 1<<7: + return 1 + case x < 1<<14: + return 2 + case x < 1<<21: + return 3 + case x < 1<<28: + return 4 + case x < 1<<35: + return 5 + case x < 1<<42: + return 6 + case x < 1<<49: + return 7 + case x < 1<<56: + return 8 + case x < 1<<63: + return 9 + } + return 10 +} + +// EncodeFixed64 writes a 64-bit integer to the Buffer. +// This is the format for the +// fixed64, sfixed64, and double protocol buffer types. +func (p *Buffer) EncodeFixed64(x uint64) error { + p.buf = append(p.buf, + uint8(x), + uint8(x>>8), + uint8(x>>16), + uint8(x>>24), + uint8(x>>32), + uint8(x>>40), + uint8(x>>48), + uint8(x>>56)) + return nil +} + +// EncodeFixed32 writes a 32-bit integer to the Buffer. +// This is the format for the +// fixed32, sfixed32, and float protocol buffer types. +func (p *Buffer) EncodeFixed32(x uint64) error { + p.buf = append(p.buf, + uint8(x), + uint8(x>>8), + uint8(x>>16), + uint8(x>>24)) + return nil +} + +// EncodeZigzag64 writes a zigzag-encoded 64-bit integer +// to the Buffer. +// This is the format used for the sint64 protocol buffer type. +func (p *Buffer) EncodeZigzag64(x uint64) error { + // use signed number to get arithmetic right shift. + return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} + +// EncodeZigzag32 writes a zigzag-encoded 32-bit integer +// to the Buffer. +// This is the format used for the sint32 protocol buffer type. +func (p *Buffer) EncodeZigzag32(x uint64) error { + // use signed number to get arithmetic right shift. + return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) +} + +// EncodeRawBytes writes a count-delimited byte buffer to the Buffer. +// This is the format used for the bytes protocol buffer +// type and for embedded messages. +func (p *Buffer) EncodeRawBytes(b []byte) error { + p.EncodeVarint(uint64(len(b))) + p.buf = append(p.buf, b...) + return nil +} + +// EncodeStringBytes writes an encoded string to the Buffer. +// This is the format used for the proto2 string type. +func (p *Buffer) EncodeStringBytes(s string) error { + p.EncodeVarint(uint64(len(s))) + p.buf = append(p.buf, s...) + return nil +} + +// Marshaler is the interface representing objects that can marshal themselves. +type Marshaler interface { + Marshal() ([]byte, error) +} + +// EncodeMessage writes the protocol buffer to the Buffer, +// prefixed by a varint-encoded length. +func (p *Buffer) EncodeMessage(pb Message) error { + siz := Size(pb) + p.EncodeVarint(uint64(siz)) + return p.Marshal(pb) +} + +// All protocol buffer fields are nillable, but be careful. +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + } + return false +} diff --git a/vendor/github.com/golang/protobuf/proto/equal.go b/vendor/github.com/golang/protobuf/proto/equal.go new file mode 100644 index 0000000..d4db5a1 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/equal.go @@ -0,0 +1,300 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Protocol buffer comparison. + +package proto + +import ( + "bytes" + "log" + "reflect" + "strings" +) + +/* +Equal returns true iff protocol buffers a and b are equal. +The arguments must both be pointers to protocol buffer structs. + +Equality is defined in this way: + - Two messages are equal iff they are the same type, + corresponding fields are equal, unknown field sets + are equal, and extensions sets are equal. + - Two set scalar fields are equal iff their values are equal. + If the fields are of a floating-point type, remember that + NaN != x for all x, including NaN. If the message is defined + in a proto3 .proto file, fields are not "set"; specifically, + zero length proto3 "bytes" fields are equal (nil == {}). + - Two repeated fields are equal iff their lengths are the same, + and their corresponding elements are equal. Note a "bytes" field, + although represented by []byte, is not a repeated field and the + rule for the scalar fields described above applies. + - Two unset fields are equal. + - Two unknown field sets are equal if their current + encoded state is equal. + - Two extension sets are equal iff they have corresponding + elements that are pairwise equal. + - Two map fields are equal iff their lengths are the same, + and they contain the same set of elements. Zero-length map + fields are equal. + - Every other combination of things are not equal. + +The return value is undefined if a and b are not protocol buffers. +*/ +func Equal(a, b Message) bool { + if a == nil || b == nil { + return a == b + } + v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b) + if v1.Type() != v2.Type() { + return false + } + if v1.Kind() == reflect.Ptr { + if v1.IsNil() { + return v2.IsNil() + } + if v2.IsNil() { + return false + } + v1, v2 = v1.Elem(), v2.Elem() + } + if v1.Kind() != reflect.Struct { + return false + } + return equalStruct(v1, v2) +} + +// v1 and v2 are known to have the same type. +func equalStruct(v1, v2 reflect.Value) bool { + sprop := GetProperties(v1.Type()) + for i := 0; i < v1.NumField(); i++ { + f := v1.Type().Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + f1, f2 := v1.Field(i), v2.Field(i) + if f.Type.Kind() == reflect.Ptr { + if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 { + // both unset + continue + } else if n1 != n2 { + // set/unset mismatch + return false + } + f1, f2 = f1.Elem(), f2.Elem() + } + if !equalAny(f1, f2, sprop.Prop[i]) { + return false + } + } + + if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() { + em2 := v2.FieldByName("XXX_InternalExtensions") + if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) { + return false + } + } + + if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { + em2 := v2.FieldByName("XXX_extensions") + if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { + return false + } + } + + uf := v1.FieldByName("XXX_unrecognized") + if !uf.IsValid() { + return true + } + + u1 := uf.Bytes() + u2 := v2.FieldByName("XXX_unrecognized").Bytes() + return bytes.Equal(u1, u2) +} + +// v1 and v2 are known to have the same type. +// prop may be nil. +func equalAny(v1, v2 reflect.Value, prop *Properties) bool { + if v1.Type() == protoMessageType { + m1, _ := v1.Interface().(Message) + m2, _ := v2.Interface().(Message) + return Equal(m1, m2) + } + switch v1.Kind() { + case reflect.Bool: + return v1.Bool() == v2.Bool() + case reflect.Float32, reflect.Float64: + return v1.Float() == v2.Float() + case reflect.Int32, reflect.Int64: + return v1.Int() == v2.Int() + case reflect.Interface: + // Probably a oneof field; compare the inner values. + n1, n2 := v1.IsNil(), v2.IsNil() + if n1 || n2 { + return n1 == n2 + } + e1, e2 := v1.Elem(), v2.Elem() + if e1.Type() != e2.Type() { + return false + } + return equalAny(e1, e2, nil) + case reflect.Map: + if v1.Len() != v2.Len() { + return false + } + for _, key := range v1.MapKeys() { + val2 := v2.MapIndex(key) + if !val2.IsValid() { + // This key was not found in the second map. + return false + } + if !equalAny(v1.MapIndex(key), val2, nil) { + return false + } + } + return true + case reflect.Ptr: + // Maps may have nil values in them, so check for nil. + if v1.IsNil() && v2.IsNil() { + return true + } + if v1.IsNil() != v2.IsNil() { + return false + } + return equalAny(v1.Elem(), v2.Elem(), prop) + case reflect.Slice: + if v1.Type().Elem().Kind() == reflect.Uint8 { + // short circuit: []byte + + // Edge case: if this is in a proto3 message, a zero length + // bytes field is considered the zero value. + if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { + return true + } + if v1.IsNil() != v2.IsNil() { + return false + } + return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte)) + } + + if v1.Len() != v2.Len() { + return false + } + for i := 0; i < v1.Len(); i++ { + if !equalAny(v1.Index(i), v2.Index(i), prop) { + return false + } + } + return true + case reflect.String: + return v1.Interface().(string) == v2.Interface().(string) + case reflect.Struct: + return equalStruct(v1, v2) + case reflect.Uint32, reflect.Uint64: + return v1.Uint() == v2.Uint() + } + + // unknown type, so not a protocol buffer + log.Printf("proto: don't know how to compare %v", v1) + return false +} + +// base is the struct type that the extensions are based on. +// x1 and x2 are InternalExtensions. +func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool { + em1, _ := x1.extensionsRead() + em2, _ := x2.extensionsRead() + return equalExtMap(base, em1, em2) +} + +func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { + if len(em1) != len(em2) { + return false + } + + for extNum, e1 := range em1 { + e2, ok := em2[extNum] + if !ok { + return false + } + + m1, m2 := e1.value, e2.value + + if m1 == nil && m2 == nil { + // Both have only encoded form. + if bytes.Equal(e1.enc, e2.enc) { + continue + } + // The bytes are different, but the extensions might still be + // equal. We need to decode them to compare. + } + + if m1 != nil && m2 != nil { + // Both are unencoded. + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { + return false + } + continue + } + + // At least one is encoded. To do a semantically correct comparison + // we need to unmarshal them first. + var desc *ExtensionDesc + if m := extensionMaps[base]; m != nil { + desc = m[extNum] + } + if desc == nil { + // If both have only encoded form and the bytes are the same, + // it is handled above. We get here when the bytes are different. + // We don't know how to decode it, so just compare them as byte + // slices. + log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) + return false + } + var err error + if m1 == nil { + m1, err = decodeExtension(e1.enc, desc) + } + if m2 == nil && err == nil { + m2, err = decodeExtension(e2.enc, desc) + } + if err != nil { + // The encoded form is invalid. + log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) + return false + } + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { + return false + } + } + + return true +} diff --git a/vendor/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/golang/protobuf/proto/extensions.go new file mode 100644 index 0000000..dacdd22 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/extensions.go @@ -0,0 +1,543 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Types and routines for supporting protocol buffer extensions. + */ + +import ( + "errors" + "fmt" + "io" + "reflect" + "strconv" + "sync" +) + +// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message. +var ErrMissingExtension = errors.New("proto: missing extension") + +// ExtensionRange represents a range of message extensions for a protocol buffer. +// Used in code generated by the protocol compiler. +type ExtensionRange struct { + Start, End int32 // both inclusive +} + +// extendableProto is an interface implemented by any protocol buffer generated by the current +// proto compiler that may be extended. +type extendableProto interface { + Message + ExtensionRangeArray() []ExtensionRange + extensionsWrite() map[int32]Extension + extensionsRead() (map[int32]Extension, sync.Locker) +} + +// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous +// version of the proto compiler that may be extended. +type extendableProtoV1 interface { + Message + ExtensionRangeArray() []ExtensionRange + ExtensionMap() map[int32]Extension +} + +// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto. +type extensionAdapter struct { + extendableProtoV1 +} + +func (e extensionAdapter) extensionsWrite() map[int32]Extension { + return e.ExtensionMap() +} + +func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { + return e.ExtensionMap(), notLocker{} +} + +// notLocker is a sync.Locker whose Lock and Unlock methods are nops. +type notLocker struct{} + +func (n notLocker) Lock() {} +func (n notLocker) Unlock() {} + +// extendable returns the extendableProto interface for the given generated proto message. +// If the proto message has the old extension format, it returns a wrapper that implements +// the extendableProto interface. +func extendable(p interface{}) (extendableProto, error) { + switch p := p.(type) { + case extendableProto: + if isNilPtr(p) { + return nil, fmt.Errorf("proto: nil %T is not extendable", p) + } + return p, nil + case extendableProtoV1: + if isNilPtr(p) { + return nil, fmt.Errorf("proto: nil %T is not extendable", p) + } + return extensionAdapter{p}, nil + } + // Don't allocate a specific error containing %T: + // this is the hot path for Clone and MarshalText. + return nil, errNotExtendable +} + +var errNotExtendable = errors.New("proto: not an extendable proto.Message") + +func isNilPtr(x interface{}) bool { + v := reflect.ValueOf(x) + return v.Kind() == reflect.Ptr && v.IsNil() +} + +// XXX_InternalExtensions is an internal representation of proto extensions. +// +// Each generated message struct type embeds an anonymous XXX_InternalExtensions field, +// thus gaining the unexported 'extensions' method, which can be called only from the proto package. +// +// The methods of XXX_InternalExtensions are not concurrency safe in general, +// but calls to logically read-only methods such as has and get may be executed concurrently. +type XXX_InternalExtensions struct { + // The struct must be indirect so that if a user inadvertently copies a + // generated message and its embedded XXX_InternalExtensions, they + // avoid the mayhem of a copied mutex. + // + // The mutex serializes all logically read-only operations to p.extensionMap. + // It is up to the client to ensure that write operations to p.extensionMap are + // mutually exclusive with other accesses. + p *struct { + mu sync.Mutex + extensionMap map[int32]Extension + } +} + +// extensionsWrite returns the extension map, creating it on first use. +func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension { + if e.p == nil { + e.p = new(struct { + mu sync.Mutex + extensionMap map[int32]Extension + }) + e.p.extensionMap = make(map[int32]Extension) + } + return e.p.extensionMap +} + +// extensionsRead returns the extensions map for read-only use. It may be nil. +// The caller must hold the returned mutex's lock when accessing Elements within the map. +func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) { + if e.p == nil { + return nil, nil + } + return e.p.extensionMap, &e.p.mu +} + +// ExtensionDesc represents an extension specification. +// Used in generated code from the protocol compiler. +type ExtensionDesc struct { + ExtendedType Message // nil pointer to the type that is being extended + ExtensionType interface{} // nil pointer to the extension type + Field int32 // field number + Name string // fully-qualified name of extension, for text formatting + Tag string // protobuf tag style + Filename string // name of the file in which the extension is defined +} + +func (ed *ExtensionDesc) repeated() bool { + t := reflect.TypeOf(ed.ExtensionType) + return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 +} + +// Extension represents an extension in a message. +type Extension struct { + // When an extension is stored in a message using SetExtension + // only desc and value are set. When the message is marshaled + // enc will be set to the encoded form of the message. + // + // When a message is unmarshaled and contains extensions, each + // extension will have only enc set. When such an extension is + // accessed using GetExtension (or GetExtensions) desc and value + // will be set. + desc *ExtensionDesc + value interface{} + enc []byte +} + +// SetRawExtension is for testing only. +func SetRawExtension(base Message, id int32, b []byte) { + epb, err := extendable(base) + if err != nil { + return + } + extmap := epb.extensionsWrite() + extmap[id] = Extension{enc: b} +} + +// isExtensionField returns true iff the given field number is in an extension range. +func isExtensionField(pb extendableProto, field int32) bool { + for _, er := range pb.ExtensionRangeArray() { + if er.Start <= field && field <= er.End { + return true + } + } + return false +} + +// checkExtensionTypes checks that the given extension is valid for pb. +func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error { + var pbi interface{} = pb + // Check the extended type. + if ea, ok := pbi.(extensionAdapter); ok { + pbi = ea.extendableProtoV1 + } + if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b { + return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a) + } + // Check the range. + if !isExtensionField(pb, extension.Field) { + return errors.New("proto: bad extension number; not in declared ranges") + } + return nil +} + +// extPropKey is sufficient to uniquely identify an extension. +type extPropKey struct { + base reflect.Type + field int32 +} + +var extProp = struct { + sync.RWMutex + m map[extPropKey]*Properties +}{ + m: make(map[extPropKey]*Properties), +} + +func extensionProperties(ed *ExtensionDesc) *Properties { + key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field} + + extProp.RLock() + if prop, ok := extProp.m[key]; ok { + extProp.RUnlock() + return prop + } + extProp.RUnlock() + + extProp.Lock() + defer extProp.Unlock() + // Check again. + if prop, ok := extProp.m[key]; ok { + return prop + } + + prop := new(Properties) + prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil) + extProp.m[key] = prop + return prop +} + +// HasExtension returns whether the given extension is present in pb. +func HasExtension(pb Message, extension *ExtensionDesc) bool { + // TODO: Check types, field numbers, etc.? + epb, err := extendable(pb) + if err != nil { + return false + } + extmap, mu := epb.extensionsRead() + if extmap == nil { + return false + } + mu.Lock() + _, ok := extmap[extension.Field] + mu.Unlock() + return ok +} + +// ClearExtension removes the given extension from pb. +func ClearExtension(pb Message, extension *ExtensionDesc) { + epb, err := extendable(pb) + if err != nil { + return + } + // TODO: Check types, field numbers, etc.? + extmap := epb.extensionsWrite() + delete(extmap, extension.Field) +} + +// GetExtension retrieves a proto2 extended field from pb. +// +// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil), +// then GetExtension parses the encoded field and returns a Go value of the specified type. +// If the field is not present, then the default value is returned (if one is specified), +// otherwise ErrMissingExtension is reported. +// +// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil), +// then GetExtension returns the raw encoded bytes of the field extension. +func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { + epb, err := extendable(pb) + if err != nil { + return nil, err + } + + if extension.ExtendedType != nil { + // can only check type if this is a complete descriptor + if err := checkExtensionTypes(epb, extension); err != nil { + return nil, err + } + } + + emap, mu := epb.extensionsRead() + if emap == nil { + return defaultExtensionValue(extension) + } + mu.Lock() + defer mu.Unlock() + e, ok := emap[extension.Field] + if !ok { + // defaultExtensionValue returns the default value or + // ErrMissingExtension if there is no default. + return defaultExtensionValue(extension) + } + + if e.value != nil { + // Already decoded. Check the descriptor, though. + if e.desc != extension { + // This shouldn't happen. If it does, it means that + // GetExtension was called twice with two different + // descriptors with the same field number. + return nil, errors.New("proto: descriptor conflict") + } + return e.value, nil + } + + if extension.ExtensionType == nil { + // incomplete descriptor + return e.enc, nil + } + + v, err := decodeExtension(e.enc, extension) + if err != nil { + return nil, err + } + + // Remember the decoded version and drop the encoded version. + // That way it is safe to mutate what we return. + e.value = v + e.desc = extension + e.enc = nil + emap[extension.Field] = e + return e.value, nil +} + +// defaultExtensionValue returns the default value for extension. +// If no default for an extension is defined ErrMissingExtension is returned. +func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { + if extension.ExtensionType == nil { + // incomplete descriptor, so no default + return nil, ErrMissingExtension + } + + t := reflect.TypeOf(extension.ExtensionType) + props := extensionProperties(extension) + + sf, _, err := fieldDefault(t, props) + if err != nil { + return nil, err + } + + if sf == nil || sf.value == nil { + // There is no default value. + return nil, ErrMissingExtension + } + + if t.Kind() != reflect.Ptr { + // We do not need to return a Ptr, we can directly return sf.value. + return sf.value, nil + } + + // We need to return an interface{} that is a pointer to sf.value. + value := reflect.New(t).Elem() + value.Set(reflect.New(value.Type().Elem())) + if sf.kind == reflect.Int32 { + // We may have an int32 or an enum, but the underlying data is int32. + // Since we can't set an int32 into a non int32 reflect.value directly + // set it as a int32. + value.Elem().SetInt(int64(sf.value.(int32))) + } else { + value.Elem().Set(reflect.ValueOf(sf.value)) + } + return value.Interface(), nil +} + +// decodeExtension decodes an extension encoded in b. +func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { + t := reflect.TypeOf(extension.ExtensionType) + unmarshal := typeUnmarshaler(t, extension.Tag) + + // t is a pointer to a struct, pointer to basic type or a slice. + // Allocate space to store the pointer/slice. + value := reflect.New(t).Elem() + + var err error + for { + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + wire := int(x) & 7 + + b, err = unmarshal(b, valToPointer(value.Addr()), wire) + if err != nil { + return nil, err + } + + if len(b) == 0 { + break + } + } + return value.Interface(), nil +} + +// GetExtensions returns a slice of the extensions present in pb that are also listed in es. +// The returned slice has the same length as es; missing extensions will appear as nil elements. +func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { + epb, err := extendable(pb) + if err != nil { + return nil, err + } + extensions = make([]interface{}, len(es)) + for i, e := range es { + extensions[i], err = GetExtension(epb, e) + if err == ErrMissingExtension { + err = nil + } + if err != nil { + return + } + } + return +} + +// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order. +// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing +// just the Field field, which defines the extension's field number. +func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { + epb, err := extendable(pb) + if err != nil { + return nil, err + } + registeredExtensions := RegisteredExtensions(pb) + + emap, mu := epb.extensionsRead() + if emap == nil { + return nil, nil + } + mu.Lock() + defer mu.Unlock() + extensions := make([]*ExtensionDesc, 0, len(emap)) + for extid, e := range emap { + desc := e.desc + if desc == nil { + desc = registeredExtensions[extid] + if desc == nil { + desc = &ExtensionDesc{Field: extid} + } + } + + extensions = append(extensions, desc) + } + return extensions, nil +} + +// SetExtension sets the specified extension of pb to the specified value. +func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { + epb, err := extendable(pb) + if err != nil { + return err + } + if err := checkExtensionTypes(epb, extension); err != nil { + return err + } + typ := reflect.TypeOf(extension.ExtensionType) + if typ != reflect.TypeOf(value) { + return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType) + } + // nil extension values need to be caught early, because the + // encoder can't distinguish an ErrNil due to a nil extension + // from an ErrNil due to a missing field. Extensions are + // always optional, so the encoder would just swallow the error + // and drop all the extensions from the encoded message. + if reflect.ValueOf(value).IsNil() { + return fmt.Errorf("proto: SetExtension called with nil value of type %T", value) + } + + extmap := epb.extensionsWrite() + extmap[extension.Field] = Extension{desc: extension, value: value} + return nil +} + +// ClearAllExtensions clears all extensions from pb. +func ClearAllExtensions(pb Message) { + epb, err := extendable(pb) + if err != nil { + return + } + m := epb.extensionsWrite() + for k := range m { + delete(m, k) + } +} + +// A global registry of extensions. +// The generated code will register the generated descriptors by calling RegisterExtension. + +var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc) + +// RegisterExtension is called from the generated code. +func RegisterExtension(desc *ExtensionDesc) { + st := reflect.TypeOf(desc.ExtendedType).Elem() + m := extensionMaps[st] + if m == nil { + m = make(map[int32]*ExtensionDesc) + extensionMaps[st] = m + } + if _, ok := m[desc.Field]; ok { + panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field))) + } + m[desc.Field] = desc +} + +// RegisteredExtensions returns a map of the registered extensions of a +// protocol buffer struct, indexed by the extension number. +// The argument pb should be a nil pointer to the struct type. +func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc { + return extensionMaps[reflect.TypeOf(pb).Elem()] +} diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go new file mode 100644 index 0000000..c076dbd --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/lib.go @@ -0,0 +1,959 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package proto converts data structures to and from the wire format of +protocol buffers. It works in concert with the Go source code generated +for .proto files by the protocol compiler. + +A summary of the properties of the protocol buffer interface +for a protocol buffer variable v: + + - Names are turned from camel_case to CamelCase for export. + - There are no methods on v to set fields; just treat + them as structure fields. + - There are getters that return a field's value if set, + and return the field's default value if unset. + The getters work even if the receiver is a nil message. + - The zero value for a struct is its correct initialization state. + All desired fields must be set before marshaling. + - A Reset() method will restore a protobuf struct to its zero state. + - Non-repeated fields are pointers to the values; nil means unset. + That is, optional or required field int32 f becomes F *int32. + - Repeated fields are slices. + - Helper functions are available to aid the setting of fields. + msg.Foo = proto.String("hello") // set field + - Constants are defined to hold the default values of all fields that + have them. They have the form Default_StructName_FieldName. + Because the getter methods handle defaulted values, + direct use of these constants should be rare. + - Enums are given type names and maps from names to values. + Enum values are prefixed by the enclosing message's name, or by the + enum's type name if it is a top-level enum. Enum types have a String + method, and a Enum method to assist in message construction. + - Nested messages, groups and enums have type names prefixed with the name of + the surrounding message type. + - Extensions are given descriptor names that start with E_, + followed by an underscore-delimited list of the nested messages + that contain it (if any) followed by the CamelCased name of the + extension field itself. HasExtension, ClearExtension, GetExtension + and SetExtension are functions for manipulating extensions. + - Oneof field sets are given a single field in their message, + with distinguished wrapper types for each possible field value. + - Marshal and Unmarshal are functions to encode and decode the wire format. + +When the .proto file specifies `syntax="proto3"`, there are some differences: + + - Non-repeated fields of non-message type are values instead of pointers. + - Enum types do not get an Enum method. + +The simplest way to describe this is to see an example. +Given file test.proto, containing + + package example; + + enum FOO { X = 17; } + + message Test { + required string label = 1; + optional int32 type = 2 [default=77]; + repeated int64 reps = 3; + optional group OptionalGroup = 4 { + required string RequiredField = 5; + } + oneof union { + int32 number = 6; + string name = 7; + } + } + +The resulting file, test.pb.go, is: + + package example + + import proto "github.com/golang/protobuf/proto" + import math "math" + + type FOO int32 + const ( + FOO_X FOO = 17 + ) + var FOO_name = map[int32]string{ + 17: "X", + } + var FOO_value = map[string]int32{ + "X": 17, + } + + func (x FOO) Enum() *FOO { + p := new(FOO) + *p = x + return p + } + func (x FOO) String() string { + return proto.EnumName(FOO_name, int32(x)) + } + func (x *FOO) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FOO_value, data) + if err != nil { + return err + } + *x = FOO(value) + return nil + } + + type Test struct { + Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` + Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` + Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` + Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` + // Types that are valid to be assigned to Union: + // *Test_Number + // *Test_Name + Union isTest_Union `protobuf_oneof:"union"` + XXX_unrecognized []byte `json:"-"` + } + func (m *Test) Reset() { *m = Test{} } + func (m *Test) String() string { return proto.CompactTextString(m) } + func (*Test) ProtoMessage() {} + + type isTest_Union interface { + isTest_Union() + } + + type Test_Number struct { + Number int32 `protobuf:"varint,6,opt,name=number"` + } + type Test_Name struct { + Name string `protobuf:"bytes,7,opt,name=name"` + } + + func (*Test_Number) isTest_Union() {} + func (*Test_Name) isTest_Union() {} + + func (m *Test) GetUnion() isTest_Union { + if m != nil { + return m.Union + } + return nil + } + const Default_Test_Type int32 = 77 + + func (m *Test) GetLabel() string { + if m != nil && m.Label != nil { + return *m.Label + } + return "" + } + + func (m *Test) GetType() int32 { + if m != nil && m.Type != nil { + return *m.Type + } + return Default_Test_Type + } + + func (m *Test) GetOptionalgroup() *Test_OptionalGroup { + if m != nil { + return m.Optionalgroup + } + return nil + } + + type Test_OptionalGroup struct { + RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` + } + func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } + func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } + + func (m *Test_OptionalGroup) GetRequiredField() string { + if m != nil && m.RequiredField != nil { + return *m.RequiredField + } + return "" + } + + func (m *Test) GetNumber() int32 { + if x, ok := m.GetUnion().(*Test_Number); ok { + return x.Number + } + return 0 + } + + func (m *Test) GetName() string { + if x, ok := m.GetUnion().(*Test_Name); ok { + return x.Name + } + return "" + } + + func init() { + proto.RegisterEnum("example.FOO", FOO_name, FOO_value) + } + +To create and play with a Test object: + + package main + + import ( + "log" + + "github.com/golang/protobuf/proto" + pb "./example.pb" + ) + + func main() { + test := &pb.Test{ + Label: proto.String("hello"), + Type: proto.Int32(17), + Reps: []int64{1, 2, 3}, + Optionalgroup: &pb.Test_OptionalGroup{ + RequiredField: proto.String("good bye"), + }, + Union: &pb.Test_Name{"fred"}, + } + data, err := proto.Marshal(test) + if err != nil { + log.Fatal("marshaling error: ", err) + } + newTest := &pb.Test{} + err = proto.Unmarshal(data, newTest) + if err != nil { + log.Fatal("unmarshaling error: ", err) + } + // Now test and newTest contain the same data. + if test.GetLabel() != newTest.GetLabel() { + log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) + } + // Use a type switch to determine which oneof was set. + switch u := test.Union.(type) { + case *pb.Test_Number: // u.Number contains the number. + case *pb.Test_Name: // u.Name contains the string. + } + // etc. + } +*/ +package proto + +import ( + "encoding/json" + "fmt" + "log" + "reflect" + "sort" + "strconv" + "sync" +) + +// RequiredNotSetError is an error type returned by either Marshal or Unmarshal. +// Marshal reports this when a required field is not initialized. +// Unmarshal reports this when a required field is missing from the wire data. +type RequiredNotSetError struct{ field string } + +func (e *RequiredNotSetError) Error() string { + if e.field == "" { + return fmt.Sprintf("proto: required field not set") + } + return fmt.Sprintf("proto: required field %q not set", e.field) +} +func (e *RequiredNotSetError) RequiredNotSet() bool { + return true +} + +type invalidUTF8Error struct{ field string } + +func (e *invalidUTF8Error) Error() string { + if e.field == "" { + return "proto: invalid UTF-8 detected" + } + return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) +} +func (e *invalidUTF8Error) InvalidUTF8() bool { + return true +} + +// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. +// This error should not be exposed to the external API as such errors should +// be recreated with the field information. +var errInvalidUTF8 = &invalidUTF8Error{} + +// isNonFatal reports whether the error is either a RequiredNotSet error +// or a InvalidUTF8 error. +func isNonFatal(err error) bool { + if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { + return true + } + if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { + return true + } + return false +} + +type nonFatal struct{ E error } + +// Merge merges err into nf and reports whether it was successful. +// Otherwise it returns false for any fatal non-nil errors. +func (nf *nonFatal) Merge(err error) (ok bool) { + if err == nil { + return true // not an error + } + if !isNonFatal(err) { + return false // fatal error + } + if nf.E == nil { + nf.E = err // store first instance of non-fatal error + } + return true +} + +// Message is implemented by generated protocol buffer messages. +type Message interface { + Reset() + String() string + ProtoMessage() +} + +// A Buffer is a buffer manager for marshaling and unmarshaling +// protocol buffers. It may be reused between invocations to +// reduce memory usage. It is not necessary to use a Buffer; +// the global functions Marshal and Unmarshal create a +// temporary Buffer and are fine for most applications. +type Buffer struct { + buf []byte // encode/decode byte stream + index int // read point + + deterministic bool +} + +// NewBuffer allocates a new Buffer and initializes its internal data to +// the contents of the argument slice. +func NewBuffer(e []byte) *Buffer { + return &Buffer{buf: e} +} + +// Reset resets the Buffer, ready for marshaling a new protocol buffer. +func (p *Buffer) Reset() { + p.buf = p.buf[0:0] // for reading/writing + p.index = 0 // for reading +} + +// SetBuf replaces the internal buffer with the slice, +// ready for unmarshaling the contents of the slice. +func (p *Buffer) SetBuf(s []byte) { + p.buf = s + p.index = 0 +} + +// Bytes returns the contents of the Buffer. +func (p *Buffer) Bytes() []byte { return p.buf } + +// SetDeterministic sets whether to use deterministic serialization. +// +// Deterministic serialization guarantees that for a given binary, equal +// messages will always be serialized to the same bytes. This implies: +// +// - Repeated serialization of a message will return the same bytes. +// - Different processes of the same binary (which may be executing on +// different machines) will serialize equal messages to the same bytes. +// +// Note that the deterministic serialization is NOT canonical across +// languages. It is not guaranteed to remain stable over time. It is unstable +// across different builds with schema changes due to unknown fields. +// Users who need canonical serialization (e.g., persistent storage in a +// canonical form, fingerprinting, etc.) should define their own +// canonicalization specification and implement their own serializer rather +// than relying on this API. +// +// If deterministic serialization is requested, map entries will be sorted +// by keys in lexographical order. This is an implementation detail and +// subject to change. +func (p *Buffer) SetDeterministic(deterministic bool) { + p.deterministic = deterministic +} + +/* + * Helper routines for simplifying the creation of optional fields of basic type. + */ + +// Bool is a helper routine that allocates a new bool value +// to store v and returns a pointer to it. +func Bool(v bool) *bool { + return &v +} + +// Int32 is a helper routine that allocates a new int32 value +// to store v and returns a pointer to it. +func Int32(v int32) *int32 { + return &v +} + +// Int is a helper routine that allocates a new int32 value +// to store v and returns a pointer to it, but unlike Int32 +// its argument value is an int. +func Int(v int) *int32 { + p := new(int32) + *p = int32(v) + return p +} + +// Int64 is a helper routine that allocates a new int64 value +// to store v and returns a pointer to it. +func Int64(v int64) *int64 { + return &v +} + +// Float32 is a helper routine that allocates a new float32 value +// to store v and returns a pointer to it. +func Float32(v float32) *float32 { + return &v +} + +// Float64 is a helper routine that allocates a new float64 value +// to store v and returns a pointer to it. +func Float64(v float64) *float64 { + return &v +} + +// Uint32 is a helper routine that allocates a new uint32 value +// to store v and returns a pointer to it. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint64 is a helper routine that allocates a new uint64 value +// to store v and returns a pointer to it. +func Uint64(v uint64) *uint64 { + return &v +} + +// String is a helper routine that allocates a new string value +// to store v and returns a pointer to it. +func String(v string) *string { + return &v +} + +// EnumName is a helper function to simplify printing protocol buffer enums +// by name. Given an enum map and a value, it returns a useful string. +func EnumName(m map[int32]string, v int32) string { + s, ok := m[v] + if ok { + return s + } + return strconv.Itoa(int(v)) +} + +// UnmarshalJSONEnum is a helper function to simplify recovering enum int values +// from their JSON-encoded representation. Given a map from the enum's symbolic +// names to its int values, and a byte buffer containing the JSON-encoded +// value, it returns an int32 that can be cast to the enum type by the caller. +// +// The function can deal with both JSON representations, numeric and symbolic. +func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { + if data[0] == '"' { + // New style: enums are strings. + var repr string + if err := json.Unmarshal(data, &repr); err != nil { + return -1, err + } + val, ok := m[repr] + if !ok { + return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) + } + return val, nil + } + // Old style: enums are ints. + var val int32 + if err := json.Unmarshal(data, &val); err != nil { + return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) + } + return val, nil +} + +// DebugPrint dumps the encoded data in b in a debugging format with a header +// including the string s. Used in testing but made available for general debugging. +func (p *Buffer) DebugPrint(s string, b []byte) { + var u uint64 + + obuf := p.buf + index := p.index + p.buf = b + p.index = 0 + depth := 0 + + fmt.Printf("\n--- %s ---\n", s) + +out: + for { + for i := 0; i < depth; i++ { + fmt.Print(" ") + } + + index := p.index + if index == len(p.buf) { + break + } + + op, err := p.DecodeVarint() + if err != nil { + fmt.Printf("%3d: fetching op err %v\n", index, err) + break out + } + tag := op >> 3 + wire := op & 7 + + switch wire { + default: + fmt.Printf("%3d: t=%3d unknown wire=%d\n", + index, tag, wire) + break out + + case WireBytes: + var r []byte + + r, err = p.DecodeRawBytes(false) + if err != nil { + break out + } + fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r)) + if len(r) <= 6 { + for i := 0; i < len(r); i++ { + fmt.Printf(" %.2x", r[i]) + } + } else { + for i := 0; i < 3; i++ { + fmt.Printf(" %.2x", r[i]) + } + fmt.Printf(" ..") + for i := len(r) - 3; i < len(r); i++ { + fmt.Printf(" %.2x", r[i]) + } + } + fmt.Printf("\n") + + case WireFixed32: + u, err = p.DecodeFixed32() + if err != nil { + fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) + + case WireFixed64: + u, err = p.DecodeFixed64() + if err != nil { + fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) + + case WireVarint: + u, err = p.DecodeVarint() + if err != nil { + fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) + + case WireStartGroup: + fmt.Printf("%3d: t=%3d start\n", index, tag) + depth++ + + case WireEndGroup: + depth-- + fmt.Printf("%3d: t=%3d end\n", index, tag) + } + } + + if depth != 0 { + fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth) + } + fmt.Printf("\n") + + p.buf = obuf + p.index = index +} + +// SetDefaults sets unset protocol buffer fields to their default values. +// It only modifies fields that are both unset and have defined defaults. +// It recursively sets default values in any non-nil sub-messages. +func SetDefaults(pb Message) { + setDefaults(reflect.ValueOf(pb), true, false) +} + +// v is a pointer to a struct. +func setDefaults(v reflect.Value, recur, zeros bool) { + v = v.Elem() + + defaultMu.RLock() + dm, ok := defaults[v.Type()] + defaultMu.RUnlock() + if !ok { + dm = buildDefaultMessage(v.Type()) + defaultMu.Lock() + defaults[v.Type()] = dm + defaultMu.Unlock() + } + + for _, sf := range dm.scalars { + f := v.Field(sf.index) + if !f.IsNil() { + // field already set + continue + } + dv := sf.value + if dv == nil && !zeros { + // no explicit default, and don't want to set zeros + continue + } + fptr := f.Addr().Interface() // **T + // TODO: Consider batching the allocations we do here. + switch sf.kind { + case reflect.Bool: + b := new(bool) + if dv != nil { + *b = dv.(bool) + } + *(fptr.(**bool)) = b + case reflect.Float32: + f := new(float32) + if dv != nil { + *f = dv.(float32) + } + *(fptr.(**float32)) = f + case reflect.Float64: + f := new(float64) + if dv != nil { + *f = dv.(float64) + } + *(fptr.(**float64)) = f + case reflect.Int32: + // might be an enum + if ft := f.Type(); ft != int32PtrType { + // enum + f.Set(reflect.New(ft.Elem())) + if dv != nil { + f.Elem().SetInt(int64(dv.(int32))) + } + } else { + // int32 field + i := new(int32) + if dv != nil { + *i = dv.(int32) + } + *(fptr.(**int32)) = i + } + case reflect.Int64: + i := new(int64) + if dv != nil { + *i = dv.(int64) + } + *(fptr.(**int64)) = i + case reflect.String: + s := new(string) + if dv != nil { + *s = dv.(string) + } + *(fptr.(**string)) = s + case reflect.Uint8: + // exceptional case: []byte + var b []byte + if dv != nil { + db := dv.([]byte) + b = make([]byte, len(db)) + copy(b, db) + } else { + b = []byte{} + } + *(fptr.(*[]byte)) = b + case reflect.Uint32: + u := new(uint32) + if dv != nil { + *u = dv.(uint32) + } + *(fptr.(**uint32)) = u + case reflect.Uint64: + u := new(uint64) + if dv != nil { + *u = dv.(uint64) + } + *(fptr.(**uint64)) = u + default: + log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind) + } + } + + for _, ni := range dm.nested { + f := v.Field(ni) + // f is *T or []*T or map[T]*T + switch f.Kind() { + case reflect.Ptr: + if f.IsNil() { + continue + } + setDefaults(f, recur, zeros) + + case reflect.Slice: + for i := 0; i < f.Len(); i++ { + e := f.Index(i) + if e.IsNil() { + continue + } + setDefaults(e, recur, zeros) + } + + case reflect.Map: + for _, k := range f.MapKeys() { + e := f.MapIndex(k) + if e.IsNil() { + continue + } + setDefaults(e, recur, zeros) + } + } + } +} + +var ( + // defaults maps a protocol buffer struct type to a slice of the fields, + // with its scalar fields set to their proto-declared non-zero default values. + defaultMu sync.RWMutex + defaults = make(map[reflect.Type]defaultMessage) + + int32PtrType = reflect.TypeOf((*int32)(nil)) +) + +// defaultMessage represents information about the default values of a message. +type defaultMessage struct { + scalars []scalarField + nested []int // struct field index of nested messages +} + +type scalarField struct { + index int // struct field index + kind reflect.Kind // element type (the T in *T or []T) + value interface{} // the proto-declared default value, or nil +} + +// t is a struct type. +func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { + sprop := GetProperties(t) + for _, prop := range sprop.Prop { + fi, ok := sprop.decoderTags.get(prop.Tag) + if !ok { + // XXX_unrecognized + continue + } + ft := t.Field(fi).Type + + sf, nested, err := fieldDefault(ft, prop) + switch { + case err != nil: + log.Print(err) + case nested: + dm.nested = append(dm.nested, fi) + case sf != nil: + sf.index = fi + dm.scalars = append(dm.scalars, *sf) + } + } + + return dm +} + +// fieldDefault returns the scalarField for field type ft. +// sf will be nil if the field can not have a default. +// nestedMessage will be true if this is a nested message. +// Note that sf.index is not set on return. +func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { + var canHaveDefault bool + switch ft.Kind() { + case reflect.Ptr: + if ft.Elem().Kind() == reflect.Struct { + nestedMessage = true + } else { + canHaveDefault = true // proto2 scalar field + } + + case reflect.Slice: + switch ft.Elem().Kind() { + case reflect.Ptr: + nestedMessage = true // repeated message + case reflect.Uint8: + canHaveDefault = true // bytes field + } + + case reflect.Map: + if ft.Elem().Kind() == reflect.Ptr { + nestedMessage = true // map with message values + } + } + + if !canHaveDefault { + if nestedMessage { + return nil, true, nil + } + return nil, false, nil + } + + // We now know that ft is a pointer or slice. + sf = &scalarField{kind: ft.Elem().Kind()} + + // scalar fields without defaults + if !prop.HasDefault { + return sf, false, nil + } + + // a scalar field: either *T or []byte + switch ft.Elem().Kind() { + case reflect.Bool: + x, err := strconv.ParseBool(prop.Default) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err) + } + sf.value = x + case reflect.Float32: + x, err := strconv.ParseFloat(prop.Default, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err) + } + sf.value = float32(x) + case reflect.Float64: + x, err := strconv.ParseFloat(prop.Default, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err) + } + sf.value = x + case reflect.Int32: + x, err := strconv.ParseInt(prop.Default, 10, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err) + } + sf.value = int32(x) + case reflect.Int64: + x, err := strconv.ParseInt(prop.Default, 10, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err) + } + sf.value = x + case reflect.String: + sf.value = prop.Default + case reflect.Uint8: + // []byte (not *uint8) + sf.value = []byte(prop.Default) + case reflect.Uint32: + x, err := strconv.ParseUint(prop.Default, 10, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err) + } + sf.value = uint32(x) + case reflect.Uint64: + x, err := strconv.ParseUint(prop.Default, 10, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err) + } + sf.value = x + default: + return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind()) + } + + return sf, false, nil +} + +// mapKeys returns a sort.Interface to be used for sorting the map keys. +// Map fields may have key types of non-float scalars, strings and enums. +func mapKeys(vs []reflect.Value) sort.Interface { + s := mapKeySorter{vs: vs} + + // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps. + if len(vs) == 0 { + return s + } + switch vs[0].Kind() { + case reflect.Int32, reflect.Int64: + s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } + case reflect.Uint32, reflect.Uint64: + s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } + case reflect.Bool: + s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true + case reflect.String: + s.less = func(a, b reflect.Value) bool { return a.String() < b.String() } + default: + panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind())) + } + + return s +} + +type mapKeySorter struct { + vs []reflect.Value + less func(a, b reflect.Value) bool +} + +func (s mapKeySorter) Len() int { return len(s.vs) } +func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } +func (s mapKeySorter) Less(i, j int) bool { + return s.less(s.vs[i], s.vs[j]) +} + +// isProto3Zero reports whether v is a zero proto3 value. +func isProto3Zero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return !v.Bool() + case reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint32, reflect.Uint64: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.String: + return v.String() == "" + } + return false +} + +// ProtoPackageIsVersion2 is referenced from generated protocol buffer files +// to assert that that code is compatible with this version of the proto package. +const ProtoPackageIsVersion2 = true + +// ProtoPackageIsVersion1 is referenced from generated protocol buffer files +// to assert that that code is compatible with this version of the proto package. +const ProtoPackageIsVersion1 = true + +// InternalMessageInfo is a type used internally by generated .pb.go files. +// This type is not intended to be used by non-generated code. +// This type is not subject to any compatibility guarantee. +type InternalMessageInfo struct { + marshal *marshalInfo + unmarshal *unmarshalInfo + merge *mergeInfo + discard *discardInfo +} diff --git a/vendor/github.com/golang/protobuf/proto/message_set.go b/vendor/github.com/golang/protobuf/proto/message_set.go new file mode 100644 index 0000000..3b6ca41 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/message_set.go @@ -0,0 +1,314 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Support for message sets. + */ + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "reflect" + "sort" + "sync" +) + +// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. +// A message type ID is required for storing a protocol buffer in a message set. +var errNoMessageTypeID = errors.New("proto does not have a message type ID") + +// The first two types (_MessageSet_Item and messageSet) +// model what the protocol compiler produces for the following protocol message: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// }; +// } +// That is the MessageSet wire format. We can't use a proto to generate these +// because that would introduce a circular dependency between it and this package. + +type _MessageSet_Item struct { + TypeId *int32 `protobuf:"varint,2,req,name=type_id"` + Message []byte `protobuf:"bytes,3,req,name=message"` +} + +type messageSet struct { + Item []*_MessageSet_Item `protobuf:"group,1,rep"` + XXX_unrecognized []byte + // TODO: caching? +} + +// Make sure messageSet is a Message. +var _ Message = (*messageSet)(nil) + +// messageTypeIder is an interface satisfied by a protocol buffer type +// that may be stored in a MessageSet. +type messageTypeIder interface { + MessageTypeId() int32 +} + +func (ms *messageSet) find(pb Message) *_MessageSet_Item { + mti, ok := pb.(messageTypeIder) + if !ok { + return nil + } + id := mti.MessageTypeId() + for _, item := range ms.Item { + if *item.TypeId == id { + return item + } + } + return nil +} + +func (ms *messageSet) Has(pb Message) bool { + return ms.find(pb) != nil +} + +func (ms *messageSet) Unmarshal(pb Message) error { + if item := ms.find(pb); item != nil { + return Unmarshal(item.Message, pb) + } + if _, ok := pb.(messageTypeIder); !ok { + return errNoMessageTypeID + } + return nil // TODO: return error instead? +} + +func (ms *messageSet) Marshal(pb Message) error { + msg, err := Marshal(pb) + if err != nil { + return err + } + if item := ms.find(pb); item != nil { + // reuse existing item + item.Message = msg + return nil + } + + mti, ok := pb.(messageTypeIder) + if !ok { + return errNoMessageTypeID + } + + mtid := mti.MessageTypeId() + ms.Item = append(ms.Item, &_MessageSet_Item{ + TypeId: &mtid, + Message: msg, + }) + return nil +} + +func (ms *messageSet) Reset() { *ms = messageSet{} } +func (ms *messageSet) String() string { return CompactTextString(ms) } +func (*messageSet) ProtoMessage() {} + +// Support for the message_set_wire_format message option. + +func skipVarint(buf []byte) []byte { + i := 0 + for ; buf[i]&0x80 != 0; i++ { + } + return buf[i+1:] +} + +// MarshalMessageSet encodes the extension map represented by m in the message set wire format. +// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option. +func MarshalMessageSet(exts interface{}) ([]byte, error) { + return marshalMessageSet(exts, false) +} + +// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal. +func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) { + switch exts := exts.(type) { + case *XXX_InternalExtensions: + var u marshalInfo + siz := u.sizeMessageSet(exts) + b := make([]byte, 0, siz) + return u.appendMessageSet(b, exts, deterministic) + + case map[int32]Extension: + // This is an old-style extension map. + // Wrap it in a new-style XXX_InternalExtensions. + ie := XXX_InternalExtensions{ + p: &struct { + mu sync.Mutex + extensionMap map[int32]Extension + }{ + extensionMap: exts, + }, + } + + var u marshalInfo + siz := u.sizeMessageSet(&ie) + b := make([]byte, 0, siz) + return u.appendMessageSet(b, &ie, deterministic) + + default: + return nil, errors.New("proto: not an extension map") + } +} + +// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. +// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. +func UnmarshalMessageSet(buf []byte, exts interface{}) error { + var m map[int32]Extension + switch exts := exts.(type) { + case *XXX_InternalExtensions: + m = exts.extensionsWrite() + case map[int32]Extension: + m = exts + default: + return errors.New("proto: not an extension map") + } + + ms := new(messageSet) + if err := Unmarshal(buf, ms); err != nil { + return err + } + for _, item := range ms.Item { + id := *item.TypeId + msg := item.Message + + // Restore wire type and field number varint, plus length varint. + // Be careful to preserve duplicate items. + b := EncodeVarint(uint64(id)<<3 | WireBytes) + if ext, ok := m[id]; ok { + // Existing data; rip off the tag and length varint + // so we join the new data correctly. + // We can assume that ext.enc is set because we are unmarshaling. + o := ext.enc[len(b):] // skip wire type and field number + _, n := DecodeVarint(o) // calculate length of length varint + o = o[n:] // skip length varint + msg = append(o, msg...) // join old data and new data + } + b = append(b, EncodeVarint(uint64(len(msg)))...) + b = append(b, msg...) + + m[id] = Extension{enc: b} + } + return nil +} + +// MarshalMessageSetJSON encodes the extension map represented by m in JSON format. +// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option. +func MarshalMessageSetJSON(exts interface{}) ([]byte, error) { + var m map[int32]Extension + switch exts := exts.(type) { + case *XXX_InternalExtensions: + var mu sync.Locker + m, mu = exts.extensionsRead() + if m != nil { + // Keep the extensions map locked until we're done marshaling to prevent + // races between marshaling and unmarshaling the lazily-{en,de}coded + // values. + mu.Lock() + defer mu.Unlock() + } + case map[int32]Extension: + m = exts + default: + return nil, errors.New("proto: not an extension map") + } + var b bytes.Buffer + b.WriteByte('{') + + // Process the map in key order for deterministic output. + ids := make([]int32, 0, len(m)) + for id := range m { + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) // int32Slice defined in text.go + + for i, id := range ids { + ext := m[id] + msd, ok := messageSetMap[id] + if !ok { + // Unknown type; we can't render it, so skip it. + continue + } + + if i > 0 && b.Len() > 1 { + b.WriteByte(',') + } + + fmt.Fprintf(&b, `"[%s]":`, msd.name) + + x := ext.value + if x == nil { + x = reflect.New(msd.t.Elem()).Interface() + if err := Unmarshal(ext.enc, x.(Message)); err != nil { + return nil, err + } + } + d, err := json.Marshal(x) + if err != nil { + return nil, err + } + b.Write(d) + } + b.WriteByte('}') + return b.Bytes(), nil +} + +// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format. +// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option. +func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error { + // Common-case fast path. + if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) { + return nil + } + + // This is fairly tricky, and it's not clear that it is needed. + return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented") +} + +// A global registry of types that can be used in a MessageSet. + +var messageSetMap = make(map[int32]messageSetDesc) + +type messageSetDesc struct { + t reflect.Type // pointer to struct + name string +} + +// RegisterMessageSetType is called from the generated code. +func RegisterMessageSetType(m Message, fieldNum int32, name string) { + messageSetMap[fieldNum] = messageSetDesc{ + t: reflect.TypeOf(m), + name: name, + } +} diff --git a/vendor/github.com/golang/protobuf/proto/pointer_reflect.go b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go new file mode 100644 index 0000000..b6cad90 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go @@ -0,0 +1,357 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2012 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +build purego appengine js + +// This file contains an implementation of proto field accesses using package reflect. +// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can +// be used on App Engine. + +package proto + +import ( + "reflect" + "sync" +) + +const unsafeAllowed = false + +// A field identifies a field in a struct, accessible from a pointer. +// In this implementation, a field is identified by the sequence of field indices +// passed to reflect's FieldByIndex. +type field []int + +// toField returns a field equivalent to the given reflect field. +func toField(f *reflect.StructField) field { + return f.Index +} + +// invalidField is an invalid field identifier. +var invalidField = field(nil) + +// zeroField is a noop when calling pointer.offset. +var zeroField = field([]int{}) + +// IsValid reports whether the field identifier is valid. +func (f field) IsValid() bool { return f != nil } + +// The pointer type is for the table-driven decoder. +// The implementation here uses a reflect.Value of pointer type to +// create a generic pointer. In pointer_unsafe.go we use unsafe +// instead of reflect to implement the same (but faster) interface. +type pointer struct { + v reflect.Value +} + +// toPointer converts an interface of pointer type to a pointer +// that points to the same target. +func toPointer(i *Message) pointer { + return pointer{v: reflect.ValueOf(*i)} +} + +// toAddrPointer converts an interface to a pointer that points to +// the interface data. +func toAddrPointer(i *interface{}, isptr bool) pointer { + v := reflect.ValueOf(*i) + u := reflect.New(v.Type()) + u.Elem().Set(v) + return pointer{v: u} +} + +// valToPointer converts v to a pointer. v must be of pointer type. +func valToPointer(v reflect.Value) pointer { + return pointer{v: v} +} + +// offset converts from a pointer to a structure to a pointer to +// one of its fields. +func (p pointer) offset(f field) pointer { + return pointer{v: p.v.Elem().FieldByIndex(f).Addr()} +} + +func (p pointer) isNil() bool { + return p.v.IsNil() +} + +// grow updates the slice s in place to make it one element longer. +// s must be addressable. +// Returns the (addressable) new element. +func grow(s reflect.Value) reflect.Value { + n, m := s.Len(), s.Cap() + if n < m { + s.SetLen(n + 1) + } else { + s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem()))) + } + return s.Index(n) +} + +func (p pointer) toInt64() *int64 { + return p.v.Interface().(*int64) +} +func (p pointer) toInt64Ptr() **int64 { + return p.v.Interface().(**int64) +} +func (p pointer) toInt64Slice() *[]int64 { + return p.v.Interface().(*[]int64) +} + +var int32ptr = reflect.TypeOf((*int32)(nil)) + +func (p pointer) toInt32() *int32 { + return p.v.Convert(int32ptr).Interface().(*int32) +} + +// The toInt32Ptr/Slice methods don't work because of enums. +// Instead, we must use set/get methods for the int32ptr/slice case. +/* + func (p pointer) toInt32Ptr() **int32 { + return p.v.Interface().(**int32) +} + func (p pointer) toInt32Slice() *[]int32 { + return p.v.Interface().(*[]int32) +} +*/ +func (p pointer) getInt32Ptr() *int32 { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + return p.v.Elem().Interface().(*int32) + } + // an enum + return p.v.Elem().Convert(int32PtrType).Interface().(*int32) +} +func (p pointer) setInt32Ptr(v int32) { + // Allocate value in a *int32. Possibly convert that to a *enum. + // Then assign it to a **int32 or **enum. + // Note: we can convert *int32 to *enum, but we can't convert + // **int32 to **enum! + p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem())) +} + +// getInt32Slice copies []int32 from p as a new slice. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) getInt32Slice() []int32 { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + return p.v.Elem().Interface().([]int32) + } + // an enum + // Allocate a []int32, then assign []enum's values into it. + // Note: we can't convert []enum to []int32. + slice := p.v.Elem() + s := make([]int32, slice.Len()) + for i := 0; i < slice.Len(); i++ { + s[i] = int32(slice.Index(i).Int()) + } + return s +} + +// setInt32Slice copies []int32 into p as a new slice. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) setInt32Slice(v []int32) { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + p.v.Elem().Set(reflect.ValueOf(v)) + return + } + // an enum + // Allocate a []enum, then assign []int32's values into it. + // Note: we can't convert []enum to []int32. + slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v)) + for i, x := range v { + slice.Index(i).SetInt(int64(x)) + } + p.v.Elem().Set(slice) +} +func (p pointer) appendInt32Slice(v int32) { + grow(p.v.Elem()).SetInt(int64(v)) +} + +func (p pointer) toUint64() *uint64 { + return p.v.Interface().(*uint64) +} +func (p pointer) toUint64Ptr() **uint64 { + return p.v.Interface().(**uint64) +} +func (p pointer) toUint64Slice() *[]uint64 { + return p.v.Interface().(*[]uint64) +} +func (p pointer) toUint32() *uint32 { + return p.v.Interface().(*uint32) +} +func (p pointer) toUint32Ptr() **uint32 { + return p.v.Interface().(**uint32) +} +func (p pointer) toUint32Slice() *[]uint32 { + return p.v.Interface().(*[]uint32) +} +func (p pointer) toBool() *bool { + return p.v.Interface().(*bool) +} +func (p pointer) toBoolPtr() **bool { + return p.v.Interface().(**bool) +} +func (p pointer) toBoolSlice() *[]bool { + return p.v.Interface().(*[]bool) +} +func (p pointer) toFloat64() *float64 { + return p.v.Interface().(*float64) +} +func (p pointer) toFloat64Ptr() **float64 { + return p.v.Interface().(**float64) +} +func (p pointer) toFloat64Slice() *[]float64 { + return p.v.Interface().(*[]float64) +} +func (p pointer) toFloat32() *float32 { + return p.v.Interface().(*float32) +} +func (p pointer) toFloat32Ptr() **float32 { + return p.v.Interface().(**float32) +} +func (p pointer) toFloat32Slice() *[]float32 { + return p.v.Interface().(*[]float32) +} +func (p pointer) toString() *string { + return p.v.Interface().(*string) +} +func (p pointer) toStringPtr() **string { + return p.v.Interface().(**string) +} +func (p pointer) toStringSlice() *[]string { + return p.v.Interface().(*[]string) +} +func (p pointer) toBytes() *[]byte { + return p.v.Interface().(*[]byte) +} +func (p pointer) toBytesSlice() *[][]byte { + return p.v.Interface().(*[][]byte) +} +func (p pointer) toExtensions() *XXX_InternalExtensions { + return p.v.Interface().(*XXX_InternalExtensions) +} +func (p pointer) toOldExtensions() *map[int32]Extension { + return p.v.Interface().(*map[int32]Extension) +} +func (p pointer) getPointer() pointer { + return pointer{v: p.v.Elem()} +} +func (p pointer) setPointer(q pointer) { + p.v.Elem().Set(q.v) +} +func (p pointer) appendPointer(q pointer) { + grow(p.v.Elem()).Set(q.v) +} + +// getPointerSlice copies []*T from p as a new []pointer. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) getPointerSlice() []pointer { + if p.v.IsNil() { + return nil + } + n := p.v.Elem().Len() + s := make([]pointer, n) + for i := 0; i < n; i++ { + s[i] = pointer{v: p.v.Elem().Index(i)} + } + return s +} + +// setPointerSlice copies []pointer into p as a new []*T. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) setPointerSlice(v []pointer) { + if v == nil { + p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem()) + return + } + s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v)) + for _, p := range v { + s = reflect.Append(s, p.v) + } + p.v.Elem().Set(s) +} + +// getInterfacePointer returns a pointer that points to the +// interface data of the interface pointed by p. +func (p pointer) getInterfacePointer() pointer { + if p.v.Elem().IsNil() { + return pointer{v: p.v.Elem()} + } + return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct +} + +func (p pointer) asPointerTo(t reflect.Type) reflect.Value { + // TODO: check that p.v.Type().Elem() == t? + return p.v +} + +func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v +} +func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v +} +func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v +} +func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v +} + +var atomicLock sync.Mutex diff --git a/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go new file mode 100644 index 0000000..d55a335 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go @@ -0,0 +1,308 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2012 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// +build !purego,!appengine,!js + +// This file contains the implementation of the proto field accesses using package unsafe. + +package proto + +import ( + "reflect" + "sync/atomic" + "unsafe" +) + +const unsafeAllowed = true + +// A field identifies a field in a struct, accessible from a pointer. +// In this implementation, a field is identified by its byte offset from the start of the struct. +type field uintptr + +// toField returns a field equivalent to the given reflect field. +func toField(f *reflect.StructField) field { + return field(f.Offset) +} + +// invalidField is an invalid field identifier. +const invalidField = ^field(0) + +// zeroField is a noop when calling pointer.offset. +const zeroField = field(0) + +// IsValid reports whether the field identifier is valid. +func (f field) IsValid() bool { + return f != invalidField +} + +// The pointer type below is for the new table-driven encoder/decoder. +// The implementation here uses unsafe.Pointer to create a generic pointer. +// In pointer_reflect.go we use reflect instead of unsafe to implement +// the same (but slower) interface. +type pointer struct { + p unsafe.Pointer +} + +// size of pointer +var ptrSize = unsafe.Sizeof(uintptr(0)) + +// toPointer converts an interface of pointer type to a pointer +// that points to the same target. +func toPointer(i *Message) pointer { + // Super-tricky - read pointer out of data word of interface value. + // Saves ~25ns over the equivalent: + // return valToPointer(reflect.ValueOf(*i)) + return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} +} + +// toAddrPointer converts an interface to a pointer that points to +// the interface data. +func toAddrPointer(i *interface{}, isptr bool) pointer { + // Super-tricky - read or get the address of data word of interface value. + if isptr { + // The interface is of pointer type, thus it is a direct interface. + // The data word is the pointer data itself. We take its address. + return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} + } + // The interface is not of pointer type. The data word is the pointer + // to the data. + return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} +} + +// valToPointer converts v to a pointer. v must be of pointer type. +func valToPointer(v reflect.Value) pointer { + return pointer{p: unsafe.Pointer(v.Pointer())} +} + +// offset converts from a pointer to a structure to a pointer to +// one of its fields. +func (p pointer) offset(f field) pointer { + // For safety, we should panic if !f.IsValid, however calling panic causes + // this to no longer be inlineable, which is a serious performance cost. + /* + if !f.IsValid() { + panic("invalid field") + } + */ + return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))} +} + +func (p pointer) isNil() bool { + return p.p == nil +} + +func (p pointer) toInt64() *int64 { + return (*int64)(p.p) +} +func (p pointer) toInt64Ptr() **int64 { + return (**int64)(p.p) +} +func (p pointer) toInt64Slice() *[]int64 { + return (*[]int64)(p.p) +} +func (p pointer) toInt32() *int32 { + return (*int32)(p.p) +} + +// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist. +/* + func (p pointer) toInt32Ptr() **int32 { + return (**int32)(p.p) + } + func (p pointer) toInt32Slice() *[]int32 { + return (*[]int32)(p.p) + } +*/ +func (p pointer) getInt32Ptr() *int32 { + return *(**int32)(p.p) +} +func (p pointer) setInt32Ptr(v int32) { + *(**int32)(p.p) = &v +} + +// getInt32Slice loads a []int32 from p. +// The value returned is aliased with the original slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) getInt32Slice() []int32 { + return *(*[]int32)(p.p) +} + +// setInt32Slice stores a []int32 to p. +// The value set is aliased with the input slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) setInt32Slice(v []int32) { + *(*[]int32)(p.p) = v +} + +// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead? +func (p pointer) appendInt32Slice(v int32) { + s := (*[]int32)(p.p) + *s = append(*s, v) +} + +func (p pointer) toUint64() *uint64 { + return (*uint64)(p.p) +} +func (p pointer) toUint64Ptr() **uint64 { + return (**uint64)(p.p) +} +func (p pointer) toUint64Slice() *[]uint64 { + return (*[]uint64)(p.p) +} +func (p pointer) toUint32() *uint32 { + return (*uint32)(p.p) +} +func (p pointer) toUint32Ptr() **uint32 { + return (**uint32)(p.p) +} +func (p pointer) toUint32Slice() *[]uint32 { + return (*[]uint32)(p.p) +} +func (p pointer) toBool() *bool { + return (*bool)(p.p) +} +func (p pointer) toBoolPtr() **bool { + return (**bool)(p.p) +} +func (p pointer) toBoolSlice() *[]bool { + return (*[]bool)(p.p) +} +func (p pointer) toFloat64() *float64 { + return (*float64)(p.p) +} +func (p pointer) toFloat64Ptr() **float64 { + return (**float64)(p.p) +} +func (p pointer) toFloat64Slice() *[]float64 { + return (*[]float64)(p.p) +} +func (p pointer) toFloat32() *float32 { + return (*float32)(p.p) +} +func (p pointer) toFloat32Ptr() **float32 { + return (**float32)(p.p) +} +func (p pointer) toFloat32Slice() *[]float32 { + return (*[]float32)(p.p) +} +func (p pointer) toString() *string { + return (*string)(p.p) +} +func (p pointer) toStringPtr() **string { + return (**string)(p.p) +} +func (p pointer) toStringSlice() *[]string { + return (*[]string)(p.p) +} +func (p pointer) toBytes() *[]byte { + return (*[]byte)(p.p) +} +func (p pointer) toBytesSlice() *[][]byte { + return (*[][]byte)(p.p) +} +func (p pointer) toExtensions() *XXX_InternalExtensions { + return (*XXX_InternalExtensions)(p.p) +} +func (p pointer) toOldExtensions() *map[int32]Extension { + return (*map[int32]Extension)(p.p) +} + +// getPointerSlice loads []*T from p as a []pointer. +// The value returned is aliased with the original slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) getPointerSlice() []pointer { + // Super-tricky - p should point to a []*T where T is a + // message type. We load it as []pointer. + return *(*[]pointer)(p.p) +} + +// setPointerSlice stores []pointer into p as a []*T. +// The value set is aliased with the input slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) setPointerSlice(v []pointer) { + // Super-tricky - p should point to a []*T where T is a + // message type. We store it as []pointer. + *(*[]pointer)(p.p) = v +} + +// getPointer loads the pointer at p and returns it. +func (p pointer) getPointer() pointer { + return pointer{p: *(*unsafe.Pointer)(p.p)} +} + +// setPointer stores the pointer q at p. +func (p pointer) setPointer(q pointer) { + *(*unsafe.Pointer)(p.p) = q.p +} + +// append q to the slice pointed to by p. +func (p pointer) appendPointer(q pointer) { + s := (*[]unsafe.Pointer)(p.p) + *s = append(*s, q.p) +} + +// getInterfacePointer returns a pointer that points to the +// interface data of the interface pointed by p. +func (p pointer) getInterfacePointer() pointer { + // Super-tricky - read pointer out of data word of interface value. + return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]} +} + +// asPointerTo returns a reflect.Value that is a pointer to an +// object of type t stored at p. +func (p pointer) asPointerTo(t reflect.Type) reflect.Value { + return reflect.NewAt(t, p.p) +} + +func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { + return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { + return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { + return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { + return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} diff --git a/vendor/github.com/golang/protobuf/proto/properties.go b/vendor/github.com/golang/protobuf/proto/properties.go new file mode 100644 index 0000000..dce098e --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/properties.go @@ -0,0 +1,535 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for encoding data into the wire format for protocol buffers. + */ + +import ( + "fmt" + "log" + "os" + "reflect" + "sort" + "strconv" + "strings" + "sync" +) + +const debug bool = false + +// Constants that identify the encoding of a value on the wire. +const ( + WireVarint = 0 + WireFixed64 = 1 + WireBytes = 2 + WireStartGroup = 3 + WireEndGroup = 4 + WireFixed32 = 5 +) + +// tagMap is an optimization over map[int]int for typical protocol buffer +// use-cases. Encoded protocol buffers are often in tag order with small tag +// numbers. +type tagMap struct { + fastTags []int + slowTags map[int]int +} + +// tagMapFastLimit is the upper bound on the tag number that will be stored in +// the tagMap slice rather than its map. +const tagMapFastLimit = 1024 + +func (p *tagMap) get(t int) (int, bool) { + if t > 0 && t < tagMapFastLimit { + if t >= len(p.fastTags) { + return 0, false + } + fi := p.fastTags[t] + return fi, fi >= 0 + } + fi, ok := p.slowTags[t] + return fi, ok +} + +func (p *tagMap) put(t int, fi int) { + if t > 0 && t < tagMapFastLimit { + for len(p.fastTags) < t+1 { + p.fastTags = append(p.fastTags, -1) + } + p.fastTags[t] = fi + return + } + if p.slowTags == nil { + p.slowTags = make(map[int]int) + } + p.slowTags[t] = fi +} + +// StructProperties represents properties for all the fields of a struct. +// decoderTags and decoderOrigNames should only be used by the decoder. +type StructProperties struct { + Prop []*Properties // properties for each field + reqCount int // required count + decoderTags tagMap // map from proto tag to struct field number + decoderOrigNames map[string]int // map from original name to struct field number + order []int // list of struct field numbers in tag order + + // OneofTypes contains information about the oneof fields in this message. + // It is keyed by the original name of a field. + OneofTypes map[string]*OneofProperties +} + +// OneofProperties represents information about a specific field in a oneof. +type OneofProperties struct { + Type reflect.Type // pointer to generated struct type for this oneof field + Field int // struct field number of the containing oneof in the message + Prop *Properties +} + +// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec. +// See encode.go, (*Buffer).enc_struct. + +func (sp *StructProperties) Len() int { return len(sp.order) } +func (sp *StructProperties) Less(i, j int) bool { + return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag +} +func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] } + +// Properties represents the protocol-specific behavior of a single struct field. +type Properties struct { + Name string // name of the field, for error messages + OrigName string // original name before protocol compiler (always set) + JSONName string // name to use for JSON; determined by protoc + Wire string + WireType int + Tag int + Required bool + Optional bool + Repeated bool + Packed bool // relevant for repeated primitives only + Enum string // set for enum types only + proto3 bool // whether this is known to be a proto3 field + oneof bool // whether this is a oneof field + + Default string // default value + HasDefault bool // whether an explicit default was provided + + stype reflect.Type // set for struct types only + sprop *StructProperties // set for struct types only + + mtype reflect.Type // set for map types only + MapKeyProp *Properties // set for map types only + MapValProp *Properties // set for map types only +} + +// String formats the properties in the protobuf struct field tag style. +func (p *Properties) String() string { + s := p.Wire + s += "," + s += strconv.Itoa(p.Tag) + if p.Required { + s += ",req" + } + if p.Optional { + s += ",opt" + } + if p.Repeated { + s += ",rep" + } + if p.Packed { + s += ",packed" + } + s += ",name=" + p.OrigName + if p.JSONName != p.OrigName { + s += ",json=" + p.JSONName + } + if p.proto3 { + s += ",proto3" + } + if p.oneof { + s += ",oneof" + } + if len(p.Enum) > 0 { + s += ",enum=" + p.Enum + } + if p.HasDefault { + s += ",def=" + p.Default + } + return s +} + +// Parse populates p by parsing a string in the protobuf struct field tag style. +func (p *Properties) Parse(s string) { + // "bytes,49,opt,name=foo,def=hello!" + fields := strings.Split(s, ",") // breaks def=, but handled below. + if len(fields) < 2 { + fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s) + return + } + + p.Wire = fields[0] + switch p.Wire { + case "varint": + p.WireType = WireVarint + case "fixed32": + p.WireType = WireFixed32 + case "fixed64": + p.WireType = WireFixed64 + case "zigzag32": + p.WireType = WireVarint + case "zigzag64": + p.WireType = WireVarint + case "bytes", "group": + p.WireType = WireBytes + // no numeric converter for non-numeric types + default: + fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s) + return + } + + var err error + p.Tag, err = strconv.Atoi(fields[1]) + if err != nil { + return + } + +outer: + for i := 2; i < len(fields); i++ { + f := fields[i] + switch { + case f == "req": + p.Required = true + case f == "opt": + p.Optional = true + case f == "rep": + p.Repeated = true + case f == "packed": + p.Packed = true + case strings.HasPrefix(f, "name="): + p.OrigName = f[5:] + case strings.HasPrefix(f, "json="): + p.JSONName = f[5:] + case strings.HasPrefix(f, "enum="): + p.Enum = f[5:] + case f == "proto3": + p.proto3 = true + case f == "oneof": + p.oneof = true + case strings.HasPrefix(f, "def="): + p.HasDefault = true + p.Default = f[4:] // rest of string + if i+1 < len(fields) { + // Commas aren't escaped, and def is always last. + p.Default += "," + strings.Join(fields[i+1:], ",") + break outer + } + } + } +} + +var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem() + +// setFieldProps initializes the field properties for submessages and maps. +func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) { + switch t1 := typ; t1.Kind() { + case reflect.Ptr: + if t1.Elem().Kind() == reflect.Struct { + p.stype = t1.Elem() + } + + case reflect.Slice: + if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct { + p.stype = t2.Elem() + } + + case reflect.Map: + p.mtype = t1 + p.MapKeyProp = &Properties{} + p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) + p.MapValProp = &Properties{} + vtype := p.mtype.Elem() + if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice { + // The value type is not a message (*T) or bytes ([]byte), + // so we need encoders for the pointer to this type. + vtype = reflect.PtrTo(vtype) + } + p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) + } + + if p.stype != nil { + if lockGetProp { + p.sprop = GetProperties(p.stype) + } else { + p.sprop = getPropertiesLocked(p.stype) + } + } +} + +var ( + marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() +) + +// Init populates the properties from a protocol buffer struct tag. +func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { + p.init(typ, name, tag, f, true) +} + +func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) { + // "bytes,49,opt,def=hello!" + p.Name = name + p.OrigName = name + if tag == "" { + return + } + p.Parse(tag) + p.setFieldProps(typ, f, lockGetProp) +} + +var ( + propertiesMu sync.RWMutex + propertiesMap = make(map[reflect.Type]*StructProperties) +) + +// GetProperties returns the list of properties for the type represented by t. +// t must represent a generated struct type of a protocol message. +func GetProperties(t reflect.Type) *StructProperties { + if t.Kind() != reflect.Struct { + panic("proto: type must have kind struct") + } + + // Most calls to GetProperties in a long-running program will be + // retrieving details for types we have seen before. + propertiesMu.RLock() + sprop, ok := propertiesMap[t] + propertiesMu.RUnlock() + if ok { + return sprop + } + + propertiesMu.Lock() + sprop = getPropertiesLocked(t) + propertiesMu.Unlock() + return sprop +} + +// getPropertiesLocked requires that propertiesMu is held. +func getPropertiesLocked(t reflect.Type) *StructProperties { + if prop, ok := propertiesMap[t]; ok { + return prop + } + + prop := new(StructProperties) + // in case of recursive protos, fill this in now. + propertiesMap[t] = prop + + // build properties + prop.Prop = make([]*Properties, t.NumField()) + prop.order = make([]int, t.NumField()) + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + p := new(Properties) + name := f.Name + p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false) + + oneof := f.Tag.Get("protobuf_oneof") // special case + if oneof != "" { + // Oneof fields don't use the traditional protobuf tag. + p.OrigName = oneof + } + prop.Prop[i] = p + prop.order[i] = i + if debug { + print(i, " ", f.Name, " ", t.String(), " ") + if p.Tag > 0 { + print(p.String()) + } + print("\n") + } + } + + // Re-order prop.order. + sort.Sort(prop) + + type oneofMessage interface { + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) + } + if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { + var oots []interface{} + _, _, _, oots = om.XXX_OneofFuncs() + + // Interpret oneof metadata. + prop.OneofTypes = make(map[string]*OneofProperties) + for _, oot := range oots { + oop := &OneofProperties{ + Type: reflect.ValueOf(oot).Type(), // *T + Prop: new(Properties), + } + sft := oop.Type.Elem().Field(0) + oop.Prop.Name = sft.Name + oop.Prop.Parse(sft.Tag.Get("protobuf")) + // There will be exactly one interface field that + // this new value is assignable to. + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Type.Kind() != reflect.Interface { + continue + } + if !oop.Type.AssignableTo(f.Type) { + continue + } + oop.Field = i + break + } + prop.OneofTypes[oop.Prop.OrigName] = oop + } + } + + // build required counts + // build tags + reqCount := 0 + prop.decoderOrigNames = make(map[string]int) + for i, p := range prop.Prop { + if strings.HasPrefix(p.Name, "XXX_") { + // Internal fields should not appear in tags/origNames maps. + // They are handled specially when encoding and decoding. + continue + } + if p.Required { + reqCount++ + } + prop.decoderTags.put(p.Tag, i) + prop.decoderOrigNames[p.OrigName] = i + } + prop.reqCount = reqCount + + return prop +} + +// A global registry of enum types. +// The generated code will register the generated maps by calling RegisterEnum. + +var enumValueMaps = make(map[string]map[string]int32) + +// RegisterEnum is called from the generated code to install the enum descriptor +// maps into the global table to aid parsing text format protocol buffers. +func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) { + if _, ok := enumValueMaps[typeName]; ok { + panic("proto: duplicate enum registered: " + typeName) + } + enumValueMaps[typeName] = valueMap +} + +// EnumValueMap returns the mapping from names to integers of the +// enum type enumType, or a nil if not found. +func EnumValueMap(enumType string) map[string]int32 { + return enumValueMaps[enumType] +} + +// A registry of all linked message types. +// The string is a fully-qualified proto name ("pkg.Message"). +var ( + protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers + protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types + revProtoTypes = make(map[reflect.Type]string) +) + +// RegisterType is called from generated code and maps from the fully qualified +// proto name to the type (pointer to struct) of the protocol buffer. +func RegisterType(x Message, name string) { + if _, ok := protoTypedNils[name]; ok { + // TODO: Some day, make this a panic. + log.Printf("proto: duplicate proto type registered: %s", name) + return + } + t := reflect.TypeOf(x) + if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 { + // Generated code always calls RegisterType with nil x. + // This check is just for extra safety. + protoTypedNils[name] = x + } else { + protoTypedNils[name] = reflect.Zero(t).Interface().(Message) + } + revProtoTypes[t] = name +} + +// RegisterMapType is called from generated code and maps from the fully qualified +// proto name to the native map type of the proto map definition. +func RegisterMapType(x interface{}, name string) { + if reflect.TypeOf(x).Kind() != reflect.Map { + panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name)) + } + if _, ok := protoMapTypes[name]; ok { + log.Printf("proto: duplicate proto type registered: %s", name) + return + } + t := reflect.TypeOf(x) + protoMapTypes[name] = t + revProtoTypes[t] = name +} + +// MessageName returns the fully-qualified proto name for the given message type. +func MessageName(x Message) string { + type xname interface { + XXX_MessageName() string + } + if m, ok := x.(xname); ok { + return m.XXX_MessageName() + } + return revProtoTypes[reflect.TypeOf(x)] +} + +// MessageType returns the message type (pointer to struct) for a named message. +// The type is not guaranteed to implement proto.Message if the name refers to a +// map entry. +func MessageType(name string) reflect.Type { + if t, ok := protoTypedNils[name]; ok { + return reflect.TypeOf(t) + } + return protoMapTypes[name] +} + +// A registry of all linked proto files. +var ( + protoFiles = make(map[string][]byte) // file name => fileDescriptor +) + +// RegisterFile is called from generated code and maps from the +// full file name of a .proto file to its compressed FileDescriptorProto. +func RegisterFile(filename string, fileDescriptor []byte) { + protoFiles[filename] = fileDescriptor +} + +// FileDescriptor returns the compressed FileDescriptorProto for a .proto file. +func FileDescriptor(filename string) []byte { return protoFiles[filename] } diff --git a/vendor/github.com/golang/protobuf/proto/table_marshal.go b/vendor/github.com/golang/protobuf/proto/table_marshal.go new file mode 100644 index 0000000..f3a2d16 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/table_marshal.go @@ -0,0 +1,2767 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "errors" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "unicode/utf8" +) + +// a sizer takes a pointer to a field and the size of its tag, computes the size of +// the encoded data. +type sizer func(pointer, int) int + +// a marshaler takes a byte slice, a pointer to a field, and its tag (in wire format), +// marshals the field to the end of the slice, returns the slice and error (if any). +type marshaler func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) + +// marshalInfo is the information used for marshaling a message. +type marshalInfo struct { + typ reflect.Type + fields []*marshalFieldInfo + unrecognized field // offset of XXX_unrecognized + extensions field // offset of XXX_InternalExtensions + v1extensions field // offset of XXX_extensions + sizecache field // offset of XXX_sizecache + initialized int32 // 0 -- only typ is set, 1 -- fully initialized + messageset bool // uses message set wire format + hasmarshaler bool // has custom marshaler + sync.RWMutex // protect extElems map, also for initialization + extElems map[int32]*marshalElemInfo // info of extension elements +} + +// marshalFieldInfo is the information used for marshaling a field of a message. +type marshalFieldInfo struct { + field field + wiretag uint64 // tag in wire format + tagsize int // size of tag in wire format + sizer sizer + marshaler marshaler + isPointer bool + required bool // field is required + name string // name of the field, for error reporting + oneofElems map[reflect.Type]*marshalElemInfo // info of oneof elements +} + +// marshalElemInfo is the information used for marshaling an extension or oneof element. +type marshalElemInfo struct { + wiretag uint64 // tag in wire format + tagsize int // size of tag in wire format + sizer sizer + marshaler marshaler + isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only) +} + +var ( + marshalInfoMap = map[reflect.Type]*marshalInfo{} + marshalInfoLock sync.Mutex +) + +// getMarshalInfo returns the information to marshal a given type of message. +// The info it returns may not necessarily initialized. +// t is the type of the message (NOT the pointer to it). +func getMarshalInfo(t reflect.Type) *marshalInfo { + marshalInfoLock.Lock() + u, ok := marshalInfoMap[t] + if !ok { + u = &marshalInfo{typ: t} + marshalInfoMap[t] = u + } + marshalInfoLock.Unlock() + return u +} + +// Size is the entry point from generated code, +// and should be ONLY called by generated code. +// It computes the size of encoded data of msg. +// a is a pointer to a place to store cached marshal info. +func (a *InternalMessageInfo) Size(msg Message) int { + u := getMessageMarshalInfo(msg, a) + ptr := toPointer(&msg) + if ptr.isNil() { + // We get here if msg is a typed nil ((*SomeMessage)(nil)), + // so it satisfies the interface, and msg == nil wouldn't + // catch it. We don't want crash in this case. + return 0 + } + return u.size(ptr) +} + +// Marshal is the entry point from generated code, +// and should be ONLY called by generated code. +// It marshals msg to the end of b. +// a is a pointer to a place to store cached marshal info. +func (a *InternalMessageInfo) Marshal(b []byte, msg Message, deterministic bool) ([]byte, error) { + u := getMessageMarshalInfo(msg, a) + ptr := toPointer(&msg) + if ptr.isNil() { + // We get here if msg is a typed nil ((*SomeMessage)(nil)), + // so it satisfies the interface, and msg == nil wouldn't + // catch it. We don't want crash in this case. + return b, ErrNil + } + return u.marshal(b, ptr, deterministic) +} + +func getMessageMarshalInfo(msg interface{}, a *InternalMessageInfo) *marshalInfo { + // u := a.marshal, but atomically. + // We use an atomic here to ensure memory consistency. + u := atomicLoadMarshalInfo(&a.marshal) + if u == nil { + // Get marshal information from type of message. + t := reflect.ValueOf(msg).Type() + if t.Kind() != reflect.Ptr { + panic(fmt.Sprintf("cannot handle non-pointer message type %v", t)) + } + u = getMarshalInfo(t.Elem()) + // Store it in the cache for later users. + // a.marshal = u, but atomically. + atomicStoreMarshalInfo(&a.marshal, u) + } + return u +} + +// size is the main function to compute the size of the encoded data of a message. +// ptr is the pointer to the message. +func (u *marshalInfo) size(ptr pointer) int { + if atomic.LoadInt32(&u.initialized) == 0 { + u.computeMarshalInfo() + } + + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if u.hasmarshaler { + m := ptr.asPointerTo(u.typ).Interface().(Marshaler) + b, _ := m.Marshal() + return len(b) + } + + n := 0 + for _, f := range u.fields { + if f.isPointer && ptr.offset(f.field).getPointer().isNil() { + // nil pointer always marshals to nothing + continue + } + n += f.sizer(ptr.offset(f.field), f.tagsize) + } + if u.extensions.IsValid() { + e := ptr.offset(u.extensions).toExtensions() + if u.messageset { + n += u.sizeMessageSet(e) + } else { + n += u.sizeExtensions(e) + } + } + if u.v1extensions.IsValid() { + m := *ptr.offset(u.v1extensions).toOldExtensions() + n += u.sizeV1Extensions(m) + } + if u.unrecognized.IsValid() { + s := *ptr.offset(u.unrecognized).toBytes() + n += len(s) + } + // cache the result for use in marshal + if u.sizecache.IsValid() { + atomic.StoreInt32(ptr.offset(u.sizecache).toInt32(), int32(n)) + } + return n +} + +// cachedsize gets the size from cache. If there is no cache (i.e. message is not generated), +// fall back to compute the size. +func (u *marshalInfo) cachedsize(ptr pointer) int { + if u.sizecache.IsValid() { + return int(atomic.LoadInt32(ptr.offset(u.sizecache).toInt32())) + } + return u.size(ptr) +} + +// marshal is the main function to marshal a message. It takes a byte slice and appends +// the encoded data to the end of the slice, returns the slice and error (if any). +// ptr is the pointer to the message. +// If deterministic is true, map is marshaled in deterministic order. +func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte, error) { + if atomic.LoadInt32(&u.initialized) == 0 { + u.computeMarshalInfo() + } + + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if u.hasmarshaler { + m := ptr.asPointerTo(u.typ).Interface().(Marshaler) + b1, err := m.Marshal() + b = append(b, b1...) + return b, err + } + + var err, errLater error + // The old marshaler encodes extensions at beginning. + if u.extensions.IsValid() { + e := ptr.offset(u.extensions).toExtensions() + if u.messageset { + b, err = u.appendMessageSet(b, e, deterministic) + } else { + b, err = u.appendExtensions(b, e, deterministic) + } + if err != nil { + return b, err + } + } + if u.v1extensions.IsValid() { + m := *ptr.offset(u.v1extensions).toOldExtensions() + b, err = u.appendV1Extensions(b, m, deterministic) + if err != nil { + return b, err + } + } + for _, f := range u.fields { + if f.required { + if ptr.offset(f.field).getPointer().isNil() { + // Required field is not set. + // We record the error but keep going, to give a complete marshaling. + if errLater == nil { + errLater = &RequiredNotSetError{f.name} + } + continue + } + } + if f.isPointer && ptr.offset(f.field).getPointer().isNil() { + // nil pointer always marshals to nothing + continue + } + b, err = f.marshaler(b, ptr.offset(f.field), f.wiretag, deterministic) + if err != nil { + if err1, ok := err.(*RequiredNotSetError); ok { + // Required field in submessage is not set. + // We record the error but keep going, to give a complete marshaling. + if errLater == nil { + errLater = &RequiredNotSetError{f.name + "." + err1.field} + } + continue + } + if err == errRepeatedHasNil { + err = errors.New("proto: repeated field " + f.name + " has nil element") + } + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } + return b, err + } + } + if u.unrecognized.IsValid() { + s := *ptr.offset(u.unrecognized).toBytes() + b = append(b, s...) + } + return b, errLater +} + +// computeMarshalInfo initializes the marshal info. +func (u *marshalInfo) computeMarshalInfo() { + u.Lock() + defer u.Unlock() + if u.initialized != 0 { // non-atomic read is ok as it is protected by the lock + return + } + + t := u.typ + u.unrecognized = invalidField + u.extensions = invalidField + u.v1extensions = invalidField + u.sizecache = invalidField + + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if reflect.PtrTo(t).Implements(marshalerType) { + u.hasmarshaler = true + atomic.StoreInt32(&u.initialized, 1) + return + } + + // get oneof implementers + var oneofImplementers []interface{} + if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { + _, _, _, oneofImplementers = m.XXX_OneofFuncs() + } + + n := t.NumField() + + // deal with XXX fields first + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if !strings.HasPrefix(f.Name, "XXX_") { + continue + } + switch f.Name { + case "XXX_sizecache": + u.sizecache = toField(&f) + case "XXX_unrecognized": + u.unrecognized = toField(&f) + case "XXX_InternalExtensions": + u.extensions = toField(&f) + u.messageset = f.Tag.Get("protobuf_messageset") == "1" + case "XXX_extensions": + u.v1extensions = toField(&f) + case "XXX_NoUnkeyedLiteral": + // nothing to do + default: + panic("unknown XXX field: " + f.Name) + } + n-- + } + + // normal fields + fields := make([]marshalFieldInfo, n) // batch allocation + u.fields = make([]*marshalFieldInfo, 0, n) + for i, j := 0, 0; i < t.NumField(); i++ { + f := t.Field(i) + + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + field := &fields[j] + j++ + field.name = f.Name + u.fields = append(u.fields, field) + if f.Tag.Get("protobuf_oneof") != "" { + field.computeOneofFieldInfo(&f, oneofImplementers) + continue + } + if f.Tag.Get("protobuf") == "" { + // field has no tag (not in generated message), ignore it + u.fields = u.fields[:len(u.fields)-1] + j-- + continue + } + field.computeMarshalFieldInfo(&f) + } + + // fields are marshaled in tag order on the wire. + sort.Sort(byTag(u.fields)) + + atomic.StoreInt32(&u.initialized, 1) +} + +// helper for sorting fields by tag +type byTag []*marshalFieldInfo + +func (a byTag) Len() int { return len(a) } +func (a byTag) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byTag) Less(i, j int) bool { return a[i].wiretag < a[j].wiretag } + +// getExtElemInfo returns the information to marshal an extension element. +// The info it returns is initialized. +func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo { + // get from cache first + u.RLock() + e, ok := u.extElems[desc.Field] + u.RUnlock() + if ok { + return e + } + + t := reflect.TypeOf(desc.ExtensionType) // pointer or slice to basic type or struct + tags := strings.Split(desc.Tag, ",") + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + sizer, marshaler := typeMarshaler(t, tags, false, false) + e = &marshalElemInfo{ + wiretag: uint64(tag)<<3 | wt, + tagsize: SizeVarint(uint64(tag) << 3), + sizer: sizer, + marshaler: marshaler, + isptr: t.Kind() == reflect.Ptr, + } + + // update cache + u.Lock() + if u.extElems == nil { + u.extElems = make(map[int32]*marshalElemInfo) + } + u.extElems[desc.Field] = e + u.Unlock() + return e +} + +// computeMarshalFieldInfo fills up the information to marshal a field. +func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) { + // parse protobuf tag of the field. + // tag has format of "bytes,49,opt,name=foo,def=hello!" + tags := strings.Split(f.Tag.Get("protobuf"), ",") + if tags[0] == "" { + return + } + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + if tags[2] == "req" { + fi.required = true + } + fi.setTag(f, tag, wt) + fi.setMarshaler(f, tags) +} + +func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) { + fi.field = toField(f) + fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire. + fi.isPointer = true + fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f) + fi.oneofElems = make(map[reflect.Type]*marshalElemInfo) + + ityp := f.Type // interface type + for _, o := range oneofImplementers { + t := reflect.TypeOf(o) + if !t.Implements(ityp) { + continue + } + sf := t.Elem().Field(0) // oneof implementer is a struct with a single field + tags := strings.Split(sf.Tag.Get("protobuf"), ",") + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + sizer, marshaler := typeMarshaler(sf.Type, tags, false, true) // oneof should not omit any zero value + fi.oneofElems[t.Elem()] = &marshalElemInfo{ + wiretag: uint64(tag)<<3 | wt, + tagsize: SizeVarint(uint64(tag) << 3), + sizer: sizer, + marshaler: marshaler, + } + } +} + +type oneofMessage interface { + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) +} + +// wiretype returns the wire encoding of the type. +func wiretype(encoding string) uint64 { + switch encoding { + case "fixed32": + return WireFixed32 + case "fixed64": + return WireFixed64 + case "varint", "zigzag32", "zigzag64": + return WireVarint + case "bytes": + return WireBytes + case "group": + return WireStartGroup + } + panic("unknown wire type " + encoding) +} + +// setTag fills up the tag (in wire format) and its size in the info of a field. +func (fi *marshalFieldInfo) setTag(f *reflect.StructField, tag int, wt uint64) { + fi.field = toField(f) + fi.wiretag = uint64(tag)<<3 | wt + fi.tagsize = SizeVarint(uint64(tag) << 3) +} + +// setMarshaler fills up the sizer and marshaler in the info of a field. +func (fi *marshalFieldInfo) setMarshaler(f *reflect.StructField, tags []string) { + switch f.Type.Kind() { + case reflect.Map: + // map field + fi.isPointer = true + fi.sizer, fi.marshaler = makeMapMarshaler(f) + return + case reflect.Ptr, reflect.Slice: + fi.isPointer = true + } + fi.sizer, fi.marshaler = typeMarshaler(f.Type, tags, true, false) +} + +// typeMarshaler returns the sizer and marshaler of a given field. +// t is the type of the field. +// tags is the generated "protobuf" tag of the field. +// If nozero is true, zero value is not marshaled to the wire. +// If oneof is true, it is a oneof field. +func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, marshaler) { + encoding := tags[0] + + pointer := false + slice := false + if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { + slice = true + t = t.Elem() + } + if t.Kind() == reflect.Ptr { + pointer = true + t = t.Elem() + } + + packed := false + proto3 := false + validateUTF8 := true + for i := 2; i < len(tags); i++ { + if tags[i] == "packed" { + packed = true + } + if tags[i] == "proto3" { + proto3 = true + } + } + validateUTF8 = validateUTF8 && proto3 + + switch t.Kind() { + case reflect.Bool: + if pointer { + return sizeBoolPtr, appendBoolPtr + } + if slice { + if packed { + return sizeBoolPackedSlice, appendBoolPackedSlice + } + return sizeBoolSlice, appendBoolSlice + } + if nozero { + return sizeBoolValueNoZero, appendBoolValueNoZero + } + return sizeBoolValue, appendBoolValue + case reflect.Uint32: + switch encoding { + case "fixed32": + if pointer { + return sizeFixed32Ptr, appendFixed32Ptr + } + if slice { + if packed { + return sizeFixed32PackedSlice, appendFixed32PackedSlice + } + return sizeFixed32Slice, appendFixed32Slice + } + if nozero { + return sizeFixed32ValueNoZero, appendFixed32ValueNoZero + } + return sizeFixed32Value, appendFixed32Value + case "varint": + if pointer { + return sizeVarint32Ptr, appendVarint32Ptr + } + if slice { + if packed { + return sizeVarint32PackedSlice, appendVarint32PackedSlice + } + return sizeVarint32Slice, appendVarint32Slice + } + if nozero { + return sizeVarint32ValueNoZero, appendVarint32ValueNoZero + } + return sizeVarint32Value, appendVarint32Value + } + case reflect.Int32: + switch encoding { + case "fixed32": + if pointer { + return sizeFixedS32Ptr, appendFixedS32Ptr + } + if slice { + if packed { + return sizeFixedS32PackedSlice, appendFixedS32PackedSlice + } + return sizeFixedS32Slice, appendFixedS32Slice + } + if nozero { + return sizeFixedS32ValueNoZero, appendFixedS32ValueNoZero + } + return sizeFixedS32Value, appendFixedS32Value + case "varint": + if pointer { + return sizeVarintS32Ptr, appendVarintS32Ptr + } + if slice { + if packed { + return sizeVarintS32PackedSlice, appendVarintS32PackedSlice + } + return sizeVarintS32Slice, appendVarintS32Slice + } + if nozero { + return sizeVarintS32ValueNoZero, appendVarintS32ValueNoZero + } + return sizeVarintS32Value, appendVarintS32Value + case "zigzag32": + if pointer { + return sizeZigzag32Ptr, appendZigzag32Ptr + } + if slice { + if packed { + return sizeZigzag32PackedSlice, appendZigzag32PackedSlice + } + return sizeZigzag32Slice, appendZigzag32Slice + } + if nozero { + return sizeZigzag32ValueNoZero, appendZigzag32ValueNoZero + } + return sizeZigzag32Value, appendZigzag32Value + } + case reflect.Uint64: + switch encoding { + case "fixed64": + if pointer { + return sizeFixed64Ptr, appendFixed64Ptr + } + if slice { + if packed { + return sizeFixed64PackedSlice, appendFixed64PackedSlice + } + return sizeFixed64Slice, appendFixed64Slice + } + if nozero { + return sizeFixed64ValueNoZero, appendFixed64ValueNoZero + } + return sizeFixed64Value, appendFixed64Value + case "varint": + if pointer { + return sizeVarint64Ptr, appendVarint64Ptr + } + if slice { + if packed { + return sizeVarint64PackedSlice, appendVarint64PackedSlice + } + return sizeVarint64Slice, appendVarint64Slice + } + if nozero { + return sizeVarint64ValueNoZero, appendVarint64ValueNoZero + } + return sizeVarint64Value, appendVarint64Value + } + case reflect.Int64: + switch encoding { + case "fixed64": + if pointer { + return sizeFixedS64Ptr, appendFixedS64Ptr + } + if slice { + if packed { + return sizeFixedS64PackedSlice, appendFixedS64PackedSlice + } + return sizeFixedS64Slice, appendFixedS64Slice + } + if nozero { + return sizeFixedS64ValueNoZero, appendFixedS64ValueNoZero + } + return sizeFixedS64Value, appendFixedS64Value + case "varint": + if pointer { + return sizeVarintS64Ptr, appendVarintS64Ptr + } + if slice { + if packed { + return sizeVarintS64PackedSlice, appendVarintS64PackedSlice + } + return sizeVarintS64Slice, appendVarintS64Slice + } + if nozero { + return sizeVarintS64ValueNoZero, appendVarintS64ValueNoZero + } + return sizeVarintS64Value, appendVarintS64Value + case "zigzag64": + if pointer { + return sizeZigzag64Ptr, appendZigzag64Ptr + } + if slice { + if packed { + return sizeZigzag64PackedSlice, appendZigzag64PackedSlice + } + return sizeZigzag64Slice, appendZigzag64Slice + } + if nozero { + return sizeZigzag64ValueNoZero, appendZigzag64ValueNoZero + } + return sizeZigzag64Value, appendZigzag64Value + } + case reflect.Float32: + if pointer { + return sizeFloat32Ptr, appendFloat32Ptr + } + if slice { + if packed { + return sizeFloat32PackedSlice, appendFloat32PackedSlice + } + return sizeFloat32Slice, appendFloat32Slice + } + if nozero { + return sizeFloat32ValueNoZero, appendFloat32ValueNoZero + } + return sizeFloat32Value, appendFloat32Value + case reflect.Float64: + if pointer { + return sizeFloat64Ptr, appendFloat64Ptr + } + if slice { + if packed { + return sizeFloat64PackedSlice, appendFloat64PackedSlice + } + return sizeFloat64Slice, appendFloat64Slice + } + if nozero { + return sizeFloat64ValueNoZero, appendFloat64ValueNoZero + } + return sizeFloat64Value, appendFloat64Value + case reflect.String: + if validateUTF8 { + if pointer { + return sizeStringPtr, appendUTF8StringPtr + } + if slice { + return sizeStringSlice, appendUTF8StringSlice + } + if nozero { + return sizeStringValueNoZero, appendUTF8StringValueNoZero + } + return sizeStringValue, appendUTF8StringValue + } + if pointer { + return sizeStringPtr, appendStringPtr + } + if slice { + return sizeStringSlice, appendStringSlice + } + if nozero { + return sizeStringValueNoZero, appendStringValueNoZero + } + return sizeStringValue, appendStringValue + case reflect.Slice: + if slice { + return sizeBytesSlice, appendBytesSlice + } + if oneof { + // Oneof bytes field may also have "proto3" tag. + // We want to marshal it as a oneof field. Do this + // check before the proto3 check. + return sizeBytesOneof, appendBytesOneof + } + if proto3 { + return sizeBytes3, appendBytes3 + } + return sizeBytes, appendBytes + case reflect.Struct: + switch encoding { + case "group": + if slice { + return makeGroupSliceMarshaler(getMarshalInfo(t)) + } + return makeGroupMarshaler(getMarshalInfo(t)) + case "bytes": + if slice { + return makeMessageSliceMarshaler(getMarshalInfo(t)) + } + return makeMessageMarshaler(getMarshalInfo(t)) + } + } + panic(fmt.Sprintf("unknown or mismatched type: type: %v, wire type: %v", t, encoding)) +} + +// Below are functions to size/marshal a specific type of a field. +// They are stored in the field's info, and called by function pointers. +// They have type sizer or marshaler. + +func sizeFixed32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFixed32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFixed32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFixed32Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + return (4 + tagsize) * len(s) +} +func sizeFixed32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFixedS32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFixedS32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFixedS32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFixedS32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + return (4 + tagsize) * len(s) +} +func sizeFixedS32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFloat32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFloat32ValueNoZero(ptr pointer, tagsize int) int { + v := math.Float32bits(*ptr.toFloat32()) + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFloat32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toFloat32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFloat32Slice(ptr pointer, tagsize int) int { + s := *ptr.toFloat32Slice() + return (4 + tagsize) * len(s) +} +func sizeFloat32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toFloat32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFixed64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFixed64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFixed64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFixed64Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + return (8 + tagsize) * len(s) +} +func sizeFixed64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeFixedS64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFixedS64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFixedS64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFixedS64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + return (8 + tagsize) * len(s) +} +func sizeFixedS64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeFloat64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFloat64ValueNoZero(ptr pointer, tagsize int) int { + v := math.Float64bits(*ptr.toFloat64()) + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFloat64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toFloat64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFloat64Slice(ptr pointer, tagsize int) int { + s := *ptr.toFloat64Slice() + return (8 + tagsize) * len(s) +} +func sizeFloat64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toFloat64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeVarint32Value(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarint32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarint32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint32Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarint32Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarint32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarintS32Value(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarintS32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarintS32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarint64Value(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + return SizeVarint(v) + tagsize +} +func sizeVarint64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + if v == 0 { + return 0 + } + return SizeVarint(v) + tagsize +} +func sizeVarint64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint64Ptr() + if p == nil { + return 0 + } + return SizeVarint(*p) + tagsize +} +func sizeVarint64Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(v) + tagsize + } + return n +} +func sizeVarint64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(v) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarintS64Value(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarintS64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarintS64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeZigzag32Value(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + v := *p + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize + } + return n +} +func sizeZigzag32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeZigzag64Value(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + v := *p + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize + } + return n +} +func sizeZigzag64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeBoolValue(_ pointer, tagsize int) int { + return 1 + tagsize +} +func sizeBoolValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toBool() + if !v { + return 0 + } + return 1 + tagsize +} +func sizeBoolPtr(ptr pointer, tagsize int) int { + p := *ptr.toBoolPtr() + if p == nil { + return 0 + } + return 1 + tagsize +} +func sizeBoolSlice(ptr pointer, tagsize int) int { + s := *ptr.toBoolSlice() + return (1 + tagsize) * len(s) +} +func sizeBoolPackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toBoolSlice() + if len(s) == 0 { + return 0 + } + return len(s) + SizeVarint(uint64(len(s))) + tagsize +} +func sizeStringValue(ptr pointer, tagsize int) int { + v := *ptr.toString() + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toString() + if v == "" { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringPtr(ptr pointer, tagsize int) int { + p := *ptr.toStringPtr() + if p == nil { + return 0 + } + v := *p + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringSlice(ptr pointer, tagsize int) int { + s := *ptr.toStringSlice() + n := 0 + for _, v := range s { + n += len(v) + SizeVarint(uint64(len(v))) + tagsize + } + return n +} +func sizeBytes(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + if v == nil { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytes3(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + if len(v) == 0 { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytesOneof(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytesSlice(ptr pointer, tagsize int) int { + s := *ptr.toBytesSlice() + n := 0 + for _, v := range s { + n += len(v) + SizeVarint(uint64(len(v))) + tagsize + } + return n +} + +// appendFixed32 appends an encoded fixed32 to b. +func appendFixed32(b []byte, v uint32) []byte { + b = append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24)) + return b +} + +// appendFixed64 appends an encoded fixed64 to b. +func appendFixed64(b []byte, v uint64) []byte { + b = append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24), + byte(v>>32), + byte(v>>40), + byte(v>>48), + byte(v>>56)) + return b +} + +// appendVarint appends an encoded varint to b. +func appendVarint(b []byte, v uint64) []byte { + // TODO: make 1-byte (maybe 2-byte) case inline-able, once we + // have non-leaf inliner. + switch { + case v < 1<<7: + b = append(b, byte(v)) + case v < 1<<14: + b = append(b, + byte(v&0x7f|0x80), + byte(v>>7)) + case v < 1<<21: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte(v>>14)) + case v < 1<<28: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte(v>>21)) + case v < 1<<35: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte(v>>28)) + case v < 1<<42: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte(v>>35)) + case v < 1<<49: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte(v>>42)) + case v < 1<<56: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte(v>>49)) + case v < 1<<63: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte((v>>49)&0x7f|0x80), + byte(v>>56)) + default: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte((v>>49)&0x7f|0x80), + byte((v>>56)&0x7f|0x80), + 1) + } + return b +} + +func appendFixed32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFixed32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFixed32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, *p) + return b, nil +} +func appendFixed32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + } + return b, nil +} +func appendFixed32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, v) + } + return b, nil +} +func appendFixedS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + return b, nil +} +func appendFixedS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + return b, nil +} +func appendFixedS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(*p)) + return b, nil +} +func appendFixedS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + } + return b, nil +} +func appendFixedS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, uint32(v)) + } + return b, nil +} +func appendFloat32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float32bits(*ptr.toFloat32()) + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFloat32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float32bits(*ptr.toFloat32()) + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFloat32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toFloat32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, math.Float32bits(*p)) + return b, nil +} +func appendFloat32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, math.Float32bits(v)) + } + return b, nil +} +func appendFloat32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, math.Float32bits(v)) + } + return b, nil +} +func appendFixed64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFixed64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFixed64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, *p) + return b, nil +} +func appendFixed64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + } + return b, nil +} +func appendFixed64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, v) + } + return b, nil +} +func appendFixedS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + return b, nil +} +func appendFixedS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + return b, nil +} +func appendFixedS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(*p)) + return b, nil +} +func appendFixedS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + } + return b, nil +} +func appendFixedS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, uint64(v)) + } + return b, nil +} +func appendFloat64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float64bits(*ptr.toFloat64()) + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFloat64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float64bits(*ptr.toFloat64()) + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFloat64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toFloat64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, math.Float64bits(*p)) + return b, nil +} +func appendFloat64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, math.Float64bits(v)) + } + return b, nil +} +func appendFloat64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, math.Float64bits(v)) + } + return b, nil +} +func appendVarint32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarint32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarint32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarint32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarint32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarintS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarint64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + return b, nil +} +func appendVarint64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + return b, nil +} +func appendVarint64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, *p) + return b, nil +} +func appendVarint64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + } + return b, nil +} +func appendVarint64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(v) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, v) + } + return b, nil +} +func appendVarintS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarintS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendZigzag32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + v := *p + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + } + return b, nil +} +func appendZigzag32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + } + return b, nil +} +func appendZigzag64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + v := *p + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + } + return b, nil +} +func appendZigzag64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + } + return b, nil +} +func appendBoolValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBool() + b = appendVarint(b, wiretag) + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + return b, nil +} +func appendBoolValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBool() + if !v { + return b, nil + } + b = appendVarint(b, wiretag) + b = append(b, 1) + return b, nil +} + +func appendBoolPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toBoolPtr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + if *p { + b = append(b, 1) + } else { + b = append(b, 0) + } + return b, nil +} +func appendBoolSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBoolSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + } + return b, nil +} +func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBoolSlice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(len(s))) + for _, v := range s { + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + } + return b, nil +} +func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + if v == "" { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toStringPtr() + if p == nil { + return b, nil + } + v := *p + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toStringSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + return b, nil +} +func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + v := *ptr.toString() + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + v := *ptr.toString() + if v == "" { + return b, nil + } + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + p := *ptr.toStringPtr() + if p == nil { + return b, nil + } + v := *p + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + s := *ptr.toStringSlice() + for _, v := range s { + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendBytes(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + if v == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytes3(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + if len(v) == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytesOneof(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytesSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBytesSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + return b, nil +} + +// makeGroupMarshaler returns the sizer and marshaler for a group. +// u is the marshal info of the underlying message. +func makeGroupMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + p := ptr.getPointer() + if p.isNil() { + return 0 + } + return u.size(p) + 2*tagsize + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + p := ptr.getPointer() + if p.isNil() { + return b, nil + } + var err error + b = appendVarint(b, wiretag) // start group + b, err = u.marshal(b, p, deterministic) + b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group + return b, err + } +} + +// makeGroupSliceMarshaler returns the sizer and marshaler for a group slice. +// u is the marshal info of the underlying message. +func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getPointerSlice() + n := 0 + for _, v := range s { + if v.isNil() { + continue + } + n += u.size(v) + 2*tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getPointerSlice() + var err error + var nerr nonFatal + for _, v := range s { + if v.isNil() { + return b, errRepeatedHasNil + } + b = appendVarint(b, wiretag) // start group + b, err = u.marshal(b, v, deterministic) + b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group + if !nerr.Merge(err) { + if err == ErrNil { + err = errRepeatedHasNil + } + return b, err + } + } + return b, nerr.E + } +} + +// makeMessageMarshaler returns the sizer and marshaler for a message field. +// u is the marshal info of the message. +func makeMessageMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + p := ptr.getPointer() + if p.isNil() { + return 0 + } + siz := u.size(p) + return siz + SizeVarint(uint64(siz)) + tagsize + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + p := ptr.getPointer() + if p.isNil() { + return b, nil + } + b = appendVarint(b, wiretag) + siz := u.cachedsize(p) + b = appendVarint(b, uint64(siz)) + return u.marshal(b, p, deterministic) + } +} + +// makeMessageSliceMarshaler returns the sizer and marshaler for a message slice. +// u is the marshal info of the message. +func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getPointerSlice() + n := 0 + for _, v := range s { + if v.isNil() { + continue + } + siz := u.size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getPointerSlice() + var err error + var nerr nonFatal + for _, v := range s { + if v.isNil() { + return b, errRepeatedHasNil + } + b = appendVarint(b, wiretag) + siz := u.cachedsize(v) + b = appendVarint(b, uint64(siz)) + b, err = u.marshal(b, v, deterministic) + + if !nerr.Merge(err) { + if err == ErrNil { + err = errRepeatedHasNil + } + return b, err + } + } + return b, nerr.E + } +} + +// makeMapMarshaler returns the sizer and marshaler for a map field. +// f is the pointer to the reflect data structure of the field. +func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) { + // figure out key and value type + t := f.Type + keyType := t.Key() + valType := t.Elem() + keyTags := strings.Split(f.Tag.Get("protobuf_key"), ",") + valTags := strings.Split(f.Tag.Get("protobuf_val"), ",") + keySizer, keyMarshaler := typeMarshaler(keyType, keyTags, false, false) // don't omit zero value in map + valSizer, valMarshaler := typeMarshaler(valType, valTags, false, false) // don't omit zero value in map + keyWireTag := 1<<3 | wiretype(keyTags[0]) + valWireTag := 2<<3 | wiretype(valTags[0]) + + // We create an interface to get the addresses of the map key and value. + // If value is pointer-typed, the interface is a direct interface, the + // idata itself is the value. Otherwise, the idata is the pointer to the + // value. + // Key cannot be pointer-typed. + valIsPtr := valType.Kind() == reflect.Ptr + + // If value is a message with nested maps, calling + // valSizer in marshal may be quadratic. We should use + // cached version in marshal (but not in size). + // If value is not message type, we don't have size cache, + // but it cannot be nested either. Just use valSizer. + valCachedSizer := valSizer + if valIsPtr && valType.Elem().Kind() == reflect.Struct { + u := getMarshalInfo(valType.Elem()) + valCachedSizer = func(ptr pointer, tagsize int) int { + // Same as message sizer, but use cache. + p := ptr.getPointer() + if p.isNil() { + return 0 + } + siz := u.cachedsize(p) + return siz + SizeVarint(uint64(siz)) + tagsize + } + } + return func(ptr pointer, tagsize int) int { + m := ptr.asPointerTo(t).Elem() // the map + n := 0 + for _, k := range m.MapKeys() { + ki := k.Interface() + vi := m.MapIndex(k).Interface() + kaddr := toAddrPointer(&ki, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, tag uint64, deterministic bool) ([]byte, error) { + m := ptr.asPointerTo(t).Elem() // the map + var err error + keys := m.MapKeys() + if len(keys) > 1 && deterministic { + sort.Sort(mapKeys(keys)) + } + + var nerr nonFatal + for _, k := range keys { + ki := k.Interface() + vi := m.MapIndex(k).Interface() + kaddr := toAddrPointer(&ki, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + b = appendVarint(b, tag) + siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) + b = appendVarint(b, uint64(siz)) + b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic) + if !nerr.Merge(err) { + return b, err + } + b, err = valMarshaler(b, vaddr, valWireTag, deterministic) + if err != ErrNil && !nerr.Merge(err) { // allow nil value in map + return b, err + } + } + return b, nerr.E + } +} + +// makeOneOfMarshaler returns the sizer and marshaler for a oneof field. +// fi is the marshal info of the field. +// f is the pointer to the reflect data structure of the field. +func makeOneOfMarshaler(fi *marshalFieldInfo, f *reflect.StructField) (sizer, marshaler) { + // Oneof field is an interface. We need to get the actual data type on the fly. + t := f.Type + return func(ptr pointer, _ int) int { + p := ptr.getInterfacePointer() + if p.isNil() { + return 0 + } + v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct + telem := v.Type() + e := fi.oneofElems[telem] + return e.sizer(p, e.tagsize) + }, + func(b []byte, ptr pointer, _ uint64, deterministic bool) ([]byte, error) { + p := ptr.getInterfacePointer() + if p.isNil() { + return b, nil + } + v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct + telem := v.Type() + if telem.Field(0).Type.Kind() == reflect.Ptr && p.getPointer().isNil() { + return b, errOneofHasNil + } + e := fi.oneofElems[telem] + return e.marshaler(b, p, e.wiretag, deterministic) + } +} + +// sizeExtensions computes the size of encoded data for a XXX_InternalExtensions field. +func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int { + m, mu := ext.extensionsRead() + if m == nil { + return 0 + } + mu.Lock() + + n := 0 + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + n += len(e.enc) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, ei.tagsize) + } + mu.Unlock() + return n +} + +// appendExtensions marshals a XXX_InternalExtensions field to the end of byte slice b. +func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { + m, mu := ext.extensionsRead() + if m == nil { + return b, nil + } + mu.Lock() + defer mu.Unlock() + + var err error + var nerr nonFatal + + // Fast-path for common cases: zero or one extensions. + // Don't bother sorting the keys. + if len(m) <= 1 { + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E + } + + // Sort the keys to provide a deterministic encoding. + // Not sure this is required, but the old code does it. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + for _, k := range keys { + e := m[int32(k)] + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// message set format is: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// }; +// } + +// sizeMessageSet computes the size of encoded data for a XXX_InternalExtensions field +// in message set format (above). +func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int { + m, mu := ext.extensionsRead() + if m == nil { + return 0 + } + mu.Lock() + + n := 0 + for id, e := range m { + n += 2 // start group, end group. tag = 1 (size=1) + n += SizeVarint(uint64(id)) + 1 // type_id, tag = 2 (size=1) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + siz := len(msgWithLen) + n += siz + 1 // message, tag = 3 (size=1) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, 1) // message, tag = 3 (size=1) + } + mu.Unlock() + return n +} + +// appendMessageSet marshals a XXX_InternalExtensions field in message set format (above) +// to the end of byte slice b. +func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { + m, mu := ext.extensionsRead() + if m == nil { + return b, nil + } + mu.Lock() + defer mu.Unlock() + + var err error + var nerr nonFatal + + // Fast-path for common cases: zero or one extensions. + // Don't bother sorting the keys. + if len(m) <= 1 { + for id, e := range m { + b = append(b, 1<<3|WireStartGroup) + b = append(b, 2<<3|WireVarint) + b = appendVarint(b, uint64(id)) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + b = append(b, 3<<3|WireBytes) + b = append(b, msgWithLen...) + b = append(b, 1<<3|WireEndGroup) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) + if !nerr.Merge(err) { + return b, err + } + b = append(b, 1<<3|WireEndGroup) + } + return b, nerr.E + } + + // Sort the keys to provide a deterministic encoding. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + for _, id := range keys { + e := m[int32(id)] + b = append(b, 1<<3|WireStartGroup) + b = append(b, 2<<3|WireVarint) + b = appendVarint(b, uint64(id)) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + b = append(b, 3<<3|WireBytes) + b = append(b, msgWithLen...) + b = append(b, 1<<3|WireEndGroup) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) + b = append(b, 1<<3|WireEndGroup) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// sizeV1Extensions computes the size of encoded data for a V1-API extension field. +func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int { + if m == nil { + return 0 + } + + n := 0 + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + n += len(e.enc) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, ei.tagsize) + } + return n +} + +// appendV1Extensions marshals a V1-API extension field to the end of byte slice b. +func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, deterministic bool) ([]byte, error) { + if m == nil { + return b, nil + } + + // Sort the keys to provide a deterministic encoding. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + var err error + var nerr nonFatal + for _, k := range keys { + e := m[int32(k)] + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// newMarshaler is the interface representing objects that can marshal themselves. +// +// This exists to support protoc-gen-go generated messages. +// The proto package will stop type-asserting to this interface in the future. +// +// DO NOT DEPEND ON THIS. +type newMarshaler interface { + XXX_Size() int + XXX_Marshal(b []byte, deterministic bool) ([]byte, error) +} + +// Size returns the encoded size of a protocol buffer message. +// This is the main entry point. +func Size(pb Message) int { + if m, ok := pb.(newMarshaler); ok { + return m.XXX_Size() + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + b, _ := m.Marshal() + return len(b) + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return 0 + } + var info InternalMessageInfo + return info.Size(pb) +} + +// Marshal takes a protocol buffer message +// and encodes it into the wire format, returning the data. +// This is the main entry point. +func Marshal(pb Message) ([]byte, error) { + if m, ok := pb.(newMarshaler); ok { + siz := m.XXX_Size() + b := make([]byte, 0, siz) + return m.XXX_Marshal(b, false) + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + return m.Marshal() + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return nil, ErrNil + } + var info InternalMessageInfo + siz := info.Size(pb) + b := make([]byte, 0, siz) + return info.Marshal(b, pb, false) +} + +// Marshal takes a protocol buffer message +// and encodes it into the wire format, writing the result to the +// Buffer. +// This is an alternative entry point. It is not necessary to use +// a Buffer for most applications. +func (p *Buffer) Marshal(pb Message) error { + var err error + if m, ok := pb.(newMarshaler); ok { + siz := m.XXX_Size() + p.grow(siz) // make sure buf has enough capacity + p.buf, err = m.XXX_Marshal(p.buf, p.deterministic) + return err + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + b, err := m.Marshal() + p.buf = append(p.buf, b...) + return err + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return ErrNil + } + var info InternalMessageInfo + siz := info.Size(pb) + p.grow(siz) // make sure buf has enough capacity + p.buf, err = info.Marshal(p.buf, pb, p.deterministic) + return err +} + +// grow grows the buffer's capacity, if necessary, to guarantee space for +// another n bytes. After grow(n), at least n bytes can be written to the +// buffer without another allocation. +func (p *Buffer) grow(n int) { + need := len(p.buf) + n + if need <= cap(p.buf) { + return + } + newCap := len(p.buf) * 2 + if newCap < need { + newCap = need + } + p.buf = append(make([]byte, 0, newCap), p.buf...) +} diff --git a/vendor/github.com/golang/protobuf/proto/table_merge.go b/vendor/github.com/golang/protobuf/proto/table_merge.go new file mode 100644 index 0000000..5525def --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/table_merge.go @@ -0,0 +1,654 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +// Merge merges the src message into dst. +// This assumes that dst and src of the same type and are non-nil. +func (a *InternalMessageInfo) Merge(dst, src Message) { + mi := atomicLoadMergeInfo(&a.merge) + if mi == nil { + mi = getMergeInfo(reflect.TypeOf(dst).Elem()) + atomicStoreMergeInfo(&a.merge, mi) + } + mi.merge(toPointer(&dst), toPointer(&src)) +} + +type mergeInfo struct { + typ reflect.Type + + initialized int32 // 0: only typ is valid, 1: everything is valid + lock sync.Mutex + + fields []mergeFieldInfo + unrecognized field // Offset of XXX_unrecognized +} + +type mergeFieldInfo struct { + field field // Offset of field, guaranteed to be valid + + // isPointer reports whether the value in the field is a pointer. + // This is true for the following situations: + // * Pointer to struct + // * Pointer to basic type (proto2 only) + // * Slice (first value in slice header is a pointer) + // * String (first value in string header is a pointer) + isPointer bool + + // basicWidth reports the width of the field assuming that it is directly + // embedded in the struct (as is the case for basic types in proto3). + // The possible values are: + // 0: invalid + // 1: bool + // 4: int32, uint32, float32 + // 8: int64, uint64, float64 + basicWidth int + + // Where dst and src are pointers to the types being merged. + merge func(dst, src pointer) +} + +var ( + mergeInfoMap = map[reflect.Type]*mergeInfo{} + mergeInfoLock sync.Mutex +) + +func getMergeInfo(t reflect.Type) *mergeInfo { + mergeInfoLock.Lock() + defer mergeInfoLock.Unlock() + mi := mergeInfoMap[t] + if mi == nil { + mi = &mergeInfo{typ: t} + mergeInfoMap[t] = mi + } + return mi +} + +// merge merges src into dst assuming they are both of type *mi.typ. +func (mi *mergeInfo) merge(dst, src pointer) { + if dst.isNil() { + panic("proto: nil destination") + } + if src.isNil() { + return // Nothing to do. + } + + if atomic.LoadInt32(&mi.initialized) == 0 { + mi.computeMergeInfo() + } + + for _, fi := range mi.fields { + sfp := src.offset(fi.field) + + // As an optimization, we can avoid the merge function call cost + // if we know for sure that the source will have no effect + // by checking if it is the zero value. + if unsafeAllowed { + if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string + continue + } + if fi.basicWidth > 0 { + switch { + case fi.basicWidth == 1 && !*sfp.toBool(): + continue + case fi.basicWidth == 4 && *sfp.toUint32() == 0: + continue + case fi.basicWidth == 8 && *sfp.toUint64() == 0: + continue + } + } + } + + dfp := dst.offset(fi.field) + fi.merge(dfp, sfp) + } + + // TODO: Make this faster? + out := dst.asPointerTo(mi.typ).Elem() + in := src.asPointerTo(mi.typ).Elem() + if emIn, err := extendable(in.Addr().Interface()); err == nil { + emOut, _ := extendable(out.Addr().Interface()) + mIn, muIn := emIn.extensionsRead() + if mIn != nil { + mOut := emOut.extensionsWrite() + muIn.Lock() + mergeExtension(mOut, mIn) + muIn.Unlock() + } + } + + if mi.unrecognized.IsValid() { + if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 { + *dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...) + } + } +} + +func (mi *mergeInfo) computeMergeInfo() { + mi.lock.Lock() + defer mi.lock.Unlock() + if mi.initialized != 0 { + return + } + t := mi.typ + n := t.NumField() + + props := GetProperties(t) + for i := 0; i < n; i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + + mfi := mergeFieldInfo{field: toField(&f)} + tf := f.Type + + // As an optimization, we can avoid the merge function call cost + // if we know for sure that the source will have no effect + // by checking if it is the zero value. + if unsafeAllowed { + switch tf.Kind() { + case reflect.Ptr, reflect.Slice, reflect.String: + // As a special case, we assume slices and strings are pointers + // since we know that the first field in the SliceSlice or + // StringHeader is a data pointer. + mfi.isPointer = true + case reflect.Bool: + mfi.basicWidth = 1 + case reflect.Int32, reflect.Uint32, reflect.Float32: + mfi.basicWidth = 4 + case reflect.Int64, reflect.Uint64, reflect.Float64: + mfi.basicWidth = 8 + } + } + + // Unwrap tf to get at its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic("both pointer and slice for basic type in " + tf.Name()) + } + + switch tf.Kind() { + case reflect.Int32: + switch { + case isSlice: // E.g., []int32 + mfi.merge = func(dst, src pointer) { + // NOTE: toInt32Slice is not defined (see pointer_reflect.go). + /* + sfsp := src.toInt32Slice() + if *sfsp != nil { + dfsp := dst.toInt32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []int64{} + } + } + */ + sfs := src.getInt32Slice() + if sfs != nil { + dfs := dst.getInt32Slice() + dfs = append(dfs, sfs...) + if dfs == nil { + dfs = []int32{} + } + dst.setInt32Slice(dfs) + } + } + case isPointer: // E.g., *int32 + mfi.merge = func(dst, src pointer) { + // NOTE: toInt32Ptr is not defined (see pointer_reflect.go). + /* + sfpp := src.toInt32Ptr() + if *sfpp != nil { + dfpp := dst.toInt32Ptr() + if *dfpp == nil { + *dfpp = Int32(**sfpp) + } else { + **dfpp = **sfpp + } + } + */ + sfp := src.getInt32Ptr() + if sfp != nil { + dfp := dst.getInt32Ptr() + if dfp == nil { + dst.setInt32Ptr(*sfp) + } else { + *dfp = *sfp + } + } + } + default: // E.g., int32 + mfi.merge = func(dst, src pointer) { + if v := *src.toInt32(); v != 0 { + *dst.toInt32() = v + } + } + } + case reflect.Int64: + switch { + case isSlice: // E.g., []int64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toInt64Slice() + if *sfsp != nil { + dfsp := dst.toInt64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []int64{} + } + } + } + case isPointer: // E.g., *int64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toInt64Ptr() + if *sfpp != nil { + dfpp := dst.toInt64Ptr() + if *dfpp == nil { + *dfpp = Int64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., int64 + mfi.merge = func(dst, src pointer) { + if v := *src.toInt64(); v != 0 { + *dst.toInt64() = v + } + } + } + case reflect.Uint32: + switch { + case isSlice: // E.g., []uint32 + mfi.merge = func(dst, src pointer) { + sfsp := src.toUint32Slice() + if *sfsp != nil { + dfsp := dst.toUint32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []uint32{} + } + } + } + case isPointer: // E.g., *uint32 + mfi.merge = func(dst, src pointer) { + sfpp := src.toUint32Ptr() + if *sfpp != nil { + dfpp := dst.toUint32Ptr() + if *dfpp == nil { + *dfpp = Uint32(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., uint32 + mfi.merge = func(dst, src pointer) { + if v := *src.toUint32(); v != 0 { + *dst.toUint32() = v + } + } + } + case reflect.Uint64: + switch { + case isSlice: // E.g., []uint64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toUint64Slice() + if *sfsp != nil { + dfsp := dst.toUint64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []uint64{} + } + } + } + case isPointer: // E.g., *uint64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toUint64Ptr() + if *sfpp != nil { + dfpp := dst.toUint64Ptr() + if *dfpp == nil { + *dfpp = Uint64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., uint64 + mfi.merge = func(dst, src pointer) { + if v := *src.toUint64(); v != 0 { + *dst.toUint64() = v + } + } + } + case reflect.Float32: + switch { + case isSlice: // E.g., []float32 + mfi.merge = func(dst, src pointer) { + sfsp := src.toFloat32Slice() + if *sfsp != nil { + dfsp := dst.toFloat32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []float32{} + } + } + } + case isPointer: // E.g., *float32 + mfi.merge = func(dst, src pointer) { + sfpp := src.toFloat32Ptr() + if *sfpp != nil { + dfpp := dst.toFloat32Ptr() + if *dfpp == nil { + *dfpp = Float32(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., float32 + mfi.merge = func(dst, src pointer) { + if v := *src.toFloat32(); v != 0 { + *dst.toFloat32() = v + } + } + } + case reflect.Float64: + switch { + case isSlice: // E.g., []float64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toFloat64Slice() + if *sfsp != nil { + dfsp := dst.toFloat64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []float64{} + } + } + } + case isPointer: // E.g., *float64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toFloat64Ptr() + if *sfpp != nil { + dfpp := dst.toFloat64Ptr() + if *dfpp == nil { + *dfpp = Float64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., float64 + mfi.merge = func(dst, src pointer) { + if v := *src.toFloat64(); v != 0 { + *dst.toFloat64() = v + } + } + } + case reflect.Bool: + switch { + case isSlice: // E.g., []bool + mfi.merge = func(dst, src pointer) { + sfsp := src.toBoolSlice() + if *sfsp != nil { + dfsp := dst.toBoolSlice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []bool{} + } + } + } + case isPointer: // E.g., *bool + mfi.merge = func(dst, src pointer) { + sfpp := src.toBoolPtr() + if *sfpp != nil { + dfpp := dst.toBoolPtr() + if *dfpp == nil { + *dfpp = Bool(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., bool + mfi.merge = func(dst, src pointer) { + if v := *src.toBool(); v { + *dst.toBool() = v + } + } + } + case reflect.String: + switch { + case isSlice: // E.g., []string + mfi.merge = func(dst, src pointer) { + sfsp := src.toStringSlice() + if *sfsp != nil { + dfsp := dst.toStringSlice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []string{} + } + } + } + case isPointer: // E.g., *string + mfi.merge = func(dst, src pointer) { + sfpp := src.toStringPtr() + if *sfpp != nil { + dfpp := dst.toStringPtr() + if *dfpp == nil { + *dfpp = String(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., string + mfi.merge = func(dst, src pointer) { + if v := *src.toString(); v != "" { + *dst.toString() = v + } + } + } + case reflect.Slice: + isProto3 := props.Prop[i].proto3 + switch { + case isPointer: + panic("bad pointer in byte slice case in " + tf.Name()) + case tf.Elem().Kind() != reflect.Uint8: + panic("bad element kind in byte slice case in " + tf.Name()) + case isSlice: // E.g., [][]byte + mfi.merge = func(dst, src pointer) { + sbsp := src.toBytesSlice() + if *sbsp != nil { + dbsp := dst.toBytesSlice() + for _, sb := range *sbsp { + if sb == nil { + *dbsp = append(*dbsp, nil) + } else { + *dbsp = append(*dbsp, append([]byte{}, sb...)) + } + } + if *dbsp == nil { + *dbsp = [][]byte{} + } + } + } + default: // E.g., []byte + mfi.merge = func(dst, src pointer) { + sbp := src.toBytes() + if *sbp != nil { + dbp := dst.toBytes() + if !isProto3 || len(*sbp) > 0 { + *dbp = append([]byte{}, *sbp...) + } + } + } + } + case reflect.Struct: + switch { + case !isPointer: + panic(fmt.Sprintf("message field %s without pointer", tf)) + case isSlice: // E.g., []*pb.T + mi := getMergeInfo(tf) + mfi.merge = func(dst, src pointer) { + sps := src.getPointerSlice() + if sps != nil { + dps := dst.getPointerSlice() + for _, sp := range sps { + var dp pointer + if !sp.isNil() { + dp = valToPointer(reflect.New(tf)) + mi.merge(dp, sp) + } + dps = append(dps, dp) + } + if dps == nil { + dps = []pointer{} + } + dst.setPointerSlice(dps) + } + } + default: // E.g., *pb.T + mi := getMergeInfo(tf) + mfi.merge = func(dst, src pointer) { + sp := src.getPointer() + if !sp.isNil() { + dp := dst.getPointer() + if dp.isNil() { + dp = valToPointer(reflect.New(tf)) + dst.setPointer(dp) + } + mi.merge(dp, sp) + } + } + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic("bad pointer or slice in map case in " + tf.Name()) + default: // E.g., map[K]V + mfi.merge = func(dst, src pointer) { + sm := src.asPointerTo(tf).Elem() + if sm.Len() == 0 { + return + } + dm := dst.asPointerTo(tf).Elem() + if dm.IsNil() { + dm.Set(reflect.MakeMap(tf)) + } + + switch tf.Elem().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + val = reflect.ValueOf(Clone(val.Interface().(Message))) + dm.SetMapIndex(key, val) + } + case reflect.Slice: // E.g. Bytes type (e.g., []byte) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) + dm.SetMapIndex(key, val) + } + default: // Basic type (e.g., string) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + dm.SetMapIndex(key, val) + } + } + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic("bad pointer or slice in interface case in " + tf.Name()) + default: // E.g., interface{} + // TODO: Make this faster? + mfi.merge = func(dst, src pointer) { + su := src.asPointerTo(tf).Elem() + if !su.IsNil() { + du := dst.asPointerTo(tf).Elem() + typ := su.Elem().Type() + if du.IsNil() || du.Elem().Type() != typ { + du.Set(reflect.New(typ.Elem())) // Initialize interface if empty + } + sv := su.Elem().Elem().Field(0) + if sv.Kind() == reflect.Ptr && sv.IsNil() { + return + } + dv := du.Elem().Elem().Field(0) + if dv.Kind() == reflect.Ptr && dv.IsNil() { + dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty + } + switch sv.Type().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + Merge(dv.Interface().(Message), sv.Interface().(Message)) + case reflect.Slice: // E.g. Bytes type (e.g., []byte) + dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...))) + default: // Basic type (e.g., string) + dv.Set(sv) + } + } + } + } + default: + panic(fmt.Sprintf("merger not found for type:%s", tf)) + } + mi.fields = append(mi.fields, mfi) + } + + mi.unrecognized = invalidField + if f, ok := t.FieldByName("XXX_unrecognized"); ok { + if f.Type != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + mi.unrecognized = toField(&f) + } + + atomic.StoreInt32(&mi.initialized, 1) +} diff --git a/vendor/github.com/golang/protobuf/proto/table_unmarshal.go b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go new file mode 100644 index 0000000..fd4afec --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go @@ -0,0 +1,2051 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "errors" + "fmt" + "io" + "math" + "reflect" + "strconv" + "strings" + "sync" + "sync/atomic" + "unicode/utf8" +) + +// Unmarshal is the entry point from the generated .pb.go files. +// This function is not intended to be used by non-generated code. +// This function is not subject to any compatibility guarantee. +// msg contains a pointer to a protocol buffer struct. +// b is the data to be unmarshaled into the protocol buffer. +// a is a pointer to a place to store cached unmarshal information. +func (a *InternalMessageInfo) Unmarshal(msg Message, b []byte) error { + // Load the unmarshal information for this message type. + // The atomic load ensures memory consistency. + u := atomicLoadUnmarshalInfo(&a.unmarshal) + if u == nil { + // Slow path: find unmarshal info for msg, update a with it. + u = getUnmarshalInfo(reflect.TypeOf(msg).Elem()) + atomicStoreUnmarshalInfo(&a.unmarshal, u) + } + // Then do the unmarshaling. + err := u.unmarshal(toPointer(&msg), b) + return err +} + +type unmarshalInfo struct { + typ reflect.Type // type of the protobuf struct + + // 0 = only typ field is initialized + // 1 = completely initialized + initialized int32 + lock sync.Mutex // prevents double initialization + dense []unmarshalFieldInfo // fields indexed by tag # + sparse map[uint64]unmarshalFieldInfo // fields indexed by tag # + reqFields []string // names of required fields + reqMask uint64 // 1< 0 { + // Read tag and wire type. + // Special case 1 and 2 byte varints. + var x uint64 + if b[0] < 128 { + x = uint64(b[0]) + b = b[1:] + } else if len(b) >= 2 && b[1] < 128 { + x = uint64(b[0]&0x7f) + uint64(b[1])<<7 + b = b[2:] + } else { + var n int + x, n = decodeVarint(b) + if n == 0 { + return io.ErrUnexpectedEOF + } + b = b[n:] + } + tag := x >> 3 + wire := int(x) & 7 + + // Dispatch on the tag to one of the unmarshal* functions below. + var f unmarshalFieldInfo + if tag < uint64(len(u.dense)) { + f = u.dense[tag] + } else { + f = u.sparse[tag] + } + if fn := f.unmarshal; fn != nil { + var err error + b, err = fn(b, m.offset(f.field), wire) + if err == nil { + reqMask |= f.reqMask + continue + } + if r, ok := err.(*RequiredNotSetError); ok { + // Remember this error, but keep parsing. We need to produce + // a full parse even if a required field is missing. + if errLater == nil { + errLater = r + } + reqMask |= f.reqMask + continue + } + if err != errInternalBadWireType { + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } + return err + } + // Fragments with bad wire type are treated as unknown fields. + } + + // Unknown tag. + if !u.unrecognized.IsValid() { + // Don't keep unrecognized data; just skip it. + var err error + b, err = skipField(b, wire) + if err != nil { + return err + } + continue + } + // Keep unrecognized data around. + // maybe in extensions, maybe in the unrecognized field. + z := m.offset(u.unrecognized).toBytes() + var emap map[int32]Extension + var e Extension + for _, r := range u.extensionRanges { + if uint64(r.Start) <= tag && tag <= uint64(r.End) { + if u.extensions.IsValid() { + mp := m.offset(u.extensions).toExtensions() + emap = mp.extensionsWrite() + e = emap[int32(tag)] + z = &e.enc + break + } + if u.oldExtensions.IsValid() { + p := m.offset(u.oldExtensions).toOldExtensions() + emap = *p + if emap == nil { + emap = map[int32]Extension{} + *p = emap + } + e = emap[int32(tag)] + z = &e.enc + break + } + panic("no extensions field available") + } + } + + // Use wire type to skip data. + var err error + b0 := b + b, err = skipField(b, wire) + if err != nil { + return err + } + *z = encodeVarint(*z, tag<<3|uint64(wire)) + *z = append(*z, b0[:len(b0)-len(b)]...) + + if emap != nil { + emap[int32(tag)] = e + } + } + if reqMask != u.reqMask && errLater == nil { + // A required field of this message is missing. + for _, n := range u.reqFields { + if reqMask&1 == 0 { + errLater = &RequiredNotSetError{n} + } + reqMask >>= 1 + } + } + return errLater +} + +// computeUnmarshalInfo fills in u with information for use +// in unmarshaling protocol buffers of type u.typ. +func (u *unmarshalInfo) computeUnmarshalInfo() { + u.lock.Lock() + defer u.lock.Unlock() + if u.initialized != 0 { + return + } + t := u.typ + n := t.NumField() + + // Set up the "not found" value for the unrecognized byte buffer. + // This is the default for proto3. + u.unrecognized = invalidField + u.extensions = invalidField + u.oldExtensions = invalidField + + // List of the generated type and offset for each oneof field. + type oneofField struct { + ityp reflect.Type // interface type of oneof field + field field // offset in containing message + } + var oneofFields []oneofField + + for i := 0; i < n; i++ { + f := t.Field(i) + if f.Name == "XXX_unrecognized" { + // The byte slice used to hold unrecognized input is special. + if f.Type != reflect.TypeOf(([]byte)(nil)) { + panic("bad type for XXX_unrecognized field: " + f.Type.Name()) + } + u.unrecognized = toField(&f) + continue + } + if f.Name == "XXX_InternalExtensions" { + // Ditto here. + if f.Type != reflect.TypeOf(XXX_InternalExtensions{}) { + panic("bad type for XXX_InternalExtensions field: " + f.Type.Name()) + } + u.extensions = toField(&f) + if f.Tag.Get("protobuf_messageset") == "1" { + u.isMessageSet = true + } + continue + } + if f.Name == "XXX_extensions" { + // An older form of the extensions field. + if f.Type != reflect.TypeOf((map[int32]Extension)(nil)) { + panic("bad type for XXX_extensions field: " + f.Type.Name()) + } + u.oldExtensions = toField(&f) + continue + } + if f.Name == "XXX_NoUnkeyedLiteral" || f.Name == "XXX_sizecache" { + continue + } + + oneof := f.Tag.Get("protobuf_oneof") + if oneof != "" { + oneofFields = append(oneofFields, oneofField{f.Type, toField(&f)}) + // The rest of oneof processing happens below. + continue + } + + tags := f.Tag.Get("protobuf") + tagArray := strings.Split(tags, ",") + if len(tagArray) < 2 { + panic("protobuf tag not enough fields in " + t.Name() + "." + f.Name + ": " + tags) + } + tag, err := strconv.Atoi(tagArray[1]) + if err != nil { + panic("protobuf tag field not an integer: " + tagArray[1]) + } + + name := "" + for _, tag := range tagArray[3:] { + if strings.HasPrefix(tag, "name=") { + name = tag[5:] + } + } + + // Extract unmarshaling function from the field (its type and tags). + unmarshal := fieldUnmarshaler(&f) + + // Required field? + var reqMask uint64 + if tagArray[2] == "req" { + bit := len(u.reqFields) + u.reqFields = append(u.reqFields, name) + reqMask = uint64(1) << uint(bit) + // TODO: if we have more than 64 required fields, we end up + // not verifying that all required fields are present. + // Fix this, perhaps using a count of required fields? + } + + // Store the info in the correct slot in the message. + u.setTag(tag, toField(&f), unmarshal, reqMask, name) + } + + // Find any types associated with oneof fields. + // TODO: XXX_OneofFuncs returns more info than we need. Get rid of some of it? + fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs") + if fn.IsValid() { + res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{} + for i := res.Len() - 1; i >= 0; i-- { + v := res.Index(i) // interface{} + tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X + typ := tptr.Elem() // Msg_X + + f := typ.Field(0) // oneof implementers have one field + baseUnmarshal := fieldUnmarshaler(&f) + tags := strings.Split(f.Tag.Get("protobuf"), ",") + fieldNum, err := strconv.Atoi(tags[1]) + if err != nil { + panic("protobuf tag field not an integer: " + tags[1]) + } + var name string + for _, tag := range tags { + if strings.HasPrefix(tag, "name=") { + name = strings.TrimPrefix(tag, "name=") + break + } + } + + // Find the oneof field that this struct implements. + // Might take O(n^2) to process all of the oneofs, but who cares. + for _, of := range oneofFields { + if tptr.Implements(of.ityp) { + // We have found the corresponding interface for this struct. + // That lets us know where this struct should be stored + // when we encounter it during unmarshaling. + unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal) + u.setTag(fieldNum, of.field, unmarshal, 0, name) + } + } + } + } + + // Get extension ranges, if any. + fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray") + if fn.IsValid() { + if !u.extensions.IsValid() && !u.oldExtensions.IsValid() { + panic("a message with extensions, but no extensions field in " + t.Name()) + } + u.extensionRanges = fn.Call(nil)[0].Interface().([]ExtensionRange) + } + + // Explicitly disallow tag 0. This will ensure we flag an error + // when decoding a buffer of all zeros. Without this code, we + // would decode and skip an all-zero buffer of even length. + // [0 0] is [tag=0/wiretype=varint varint-encoded-0]. + u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) { + return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w) + }, 0, "") + + // Set mask for required field check. + u.reqMask = uint64(1)<= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here? + for len(u.dense) <= tag { + u.dense = append(u.dense, unmarshalFieldInfo{}) + } + u.dense[tag] = i + return + } + if u.sparse == nil { + u.sparse = map[uint64]unmarshalFieldInfo{} + } + u.sparse[uint64(tag)] = i +} + +// fieldUnmarshaler returns an unmarshaler for the given field. +func fieldUnmarshaler(f *reflect.StructField) unmarshaler { + if f.Type.Kind() == reflect.Map { + return makeUnmarshalMap(f) + } + return typeUnmarshaler(f.Type, f.Tag.Get("protobuf")) +} + +// typeUnmarshaler returns an unmarshaler for the given field type / field tag pair. +func typeUnmarshaler(t reflect.Type, tags string) unmarshaler { + tagArray := strings.Split(tags, ",") + encoding := tagArray[0] + name := "unknown" + proto3 := false + validateUTF8 := true + for _, tag := range tagArray[3:] { + if strings.HasPrefix(tag, "name=") { + name = tag[5:] + } + if tag == "proto3" { + proto3 = true + } + } + validateUTF8 = validateUTF8 && proto3 + + // Figure out packaging (pointer, slice, or both) + slice := false + pointer := false + if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { + slice = true + t = t.Elem() + } + if t.Kind() == reflect.Ptr { + pointer = true + t = t.Elem() + } + + // We'll never have both pointer and slice for basic types. + if pointer && slice && t.Kind() != reflect.Struct { + panic("both pointer and slice for basic type in " + t.Name()) + } + + switch t.Kind() { + case reflect.Bool: + if pointer { + return unmarshalBoolPtr + } + if slice { + return unmarshalBoolSlice + } + return unmarshalBoolValue + case reflect.Int32: + switch encoding { + case "fixed32": + if pointer { + return unmarshalFixedS32Ptr + } + if slice { + return unmarshalFixedS32Slice + } + return unmarshalFixedS32Value + case "varint": + // this could be int32 or enum + if pointer { + return unmarshalInt32Ptr + } + if slice { + return unmarshalInt32Slice + } + return unmarshalInt32Value + case "zigzag32": + if pointer { + return unmarshalSint32Ptr + } + if slice { + return unmarshalSint32Slice + } + return unmarshalSint32Value + } + case reflect.Int64: + switch encoding { + case "fixed64": + if pointer { + return unmarshalFixedS64Ptr + } + if slice { + return unmarshalFixedS64Slice + } + return unmarshalFixedS64Value + case "varint": + if pointer { + return unmarshalInt64Ptr + } + if slice { + return unmarshalInt64Slice + } + return unmarshalInt64Value + case "zigzag64": + if pointer { + return unmarshalSint64Ptr + } + if slice { + return unmarshalSint64Slice + } + return unmarshalSint64Value + } + case reflect.Uint32: + switch encoding { + case "fixed32": + if pointer { + return unmarshalFixed32Ptr + } + if slice { + return unmarshalFixed32Slice + } + return unmarshalFixed32Value + case "varint": + if pointer { + return unmarshalUint32Ptr + } + if slice { + return unmarshalUint32Slice + } + return unmarshalUint32Value + } + case reflect.Uint64: + switch encoding { + case "fixed64": + if pointer { + return unmarshalFixed64Ptr + } + if slice { + return unmarshalFixed64Slice + } + return unmarshalFixed64Value + case "varint": + if pointer { + return unmarshalUint64Ptr + } + if slice { + return unmarshalUint64Slice + } + return unmarshalUint64Value + } + case reflect.Float32: + if pointer { + return unmarshalFloat32Ptr + } + if slice { + return unmarshalFloat32Slice + } + return unmarshalFloat32Value + case reflect.Float64: + if pointer { + return unmarshalFloat64Ptr + } + if slice { + return unmarshalFloat64Slice + } + return unmarshalFloat64Value + case reflect.Map: + panic("map type in typeUnmarshaler in " + t.Name()) + case reflect.Slice: + if pointer { + panic("bad pointer in slice case in " + t.Name()) + } + if slice { + return unmarshalBytesSlice + } + return unmarshalBytesValue + case reflect.String: + if validateUTF8 { + if pointer { + return unmarshalUTF8StringPtr + } + if slice { + return unmarshalUTF8StringSlice + } + return unmarshalUTF8StringValue + } + if pointer { + return unmarshalStringPtr + } + if slice { + return unmarshalStringSlice + } + return unmarshalStringValue + case reflect.Struct: + // message or group field + if !pointer { + panic(fmt.Sprintf("message/group field %s:%s without pointer", t, encoding)) + } + switch encoding { + case "bytes": + if slice { + return makeUnmarshalMessageSlicePtr(getUnmarshalInfo(t), name) + } + return makeUnmarshalMessagePtr(getUnmarshalInfo(t), name) + case "group": + if slice { + return makeUnmarshalGroupSlicePtr(getUnmarshalInfo(t), name) + } + return makeUnmarshalGroupPtr(getUnmarshalInfo(t), name) + } + } + panic(fmt.Sprintf("unmarshaler not found type:%s encoding:%s", t, encoding)) +} + +// Below are all the unmarshalers for individual fields of various types. + +func unmarshalInt64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + *f.toInt64() = v + return b, nil +} + +func unmarshalInt64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + *f.toInt64Ptr() = &v + return b, nil +} + +func unmarshalInt64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + s := f.toInt64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + s := f.toInt64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalSint64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + *f.toInt64() = v + return b, nil +} + +func unmarshalSint64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + *f.toInt64Ptr() = &v + return b, nil +} + +func unmarshalSint64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + s := f.toInt64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + s := f.toInt64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalUint64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + *f.toUint64() = v + return b, nil +} + +func unmarshalUint64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + *f.toUint64Ptr() = &v + return b, nil +} + +func unmarshalUint64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + s := f.toUint64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + s := f.toUint64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalInt32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + *f.toInt32() = v + return b, nil +} + +func unmarshalInt32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.setInt32Ptr(v) + return b, nil +} + +func unmarshalInt32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.appendInt32Slice(v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.appendInt32Slice(v) + return b, nil +} + +func unmarshalSint32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + *f.toInt32() = v + return b, nil +} + +func unmarshalSint32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.setInt32Ptr(v) + return b, nil +} + +func unmarshalSint32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.appendInt32Slice(v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.appendInt32Slice(v) + return b, nil +} + +func unmarshalUint32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + *f.toUint32() = v + return b, nil +} + +func unmarshalUint32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + *f.toUint32Ptr() = &v + return b, nil +} + +func unmarshalUint32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + s := f.toUint32Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + s := f.toUint32Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalFixed64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + *f.toUint64() = v + return b[8:], nil +} + +func unmarshalFixed64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + *f.toUint64Ptr() = &v + return b[8:], nil +} + +func unmarshalFixed64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + s := f.toUint64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + s := f.toUint64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFixedS64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + *f.toInt64() = v + return b[8:], nil +} + +func unmarshalFixedS64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + *f.toInt64Ptr() = &v + return b[8:], nil +} + +func unmarshalFixedS64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + s := f.toInt64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + s := f.toInt64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFixed32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + *f.toUint32() = v + return b[4:], nil +} + +func unmarshalFixed32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + *f.toUint32Ptr() = &v + return b[4:], nil +} + +func unmarshalFixed32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + s := f.toUint32Slice() + *s = append(*s, v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + s := f.toUint32Slice() + *s = append(*s, v) + return b[4:], nil +} + +func unmarshalFixedS32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + *f.toInt32() = v + return b[4:], nil +} + +func unmarshalFixedS32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.setInt32Ptr(v) + return b[4:], nil +} + +func unmarshalFixedS32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.appendInt32Slice(v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.appendInt32Slice(v) + return b[4:], nil +} + +func unmarshalBoolValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + // Note: any length varint is allowed, even though any sane + // encoder will use one byte. + // See https://github.com/golang/protobuf/issues/76 + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + // TODO: check if x>1? Tests seem to indicate no. + v := x != 0 + *f.toBool() = v + return b[n:], nil +} + +func unmarshalBoolPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + *f.toBoolPtr() = &v + return b[n:], nil +} + +func unmarshalBoolSlice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + s := f.toBoolSlice() + *s = append(*s, v) + b = b[n:] + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + s := f.toBoolSlice() + *s = append(*s, v) + return b[n:], nil +} + +func unmarshalFloat64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + *f.toFloat64() = v + return b[8:], nil +} + +func unmarshalFloat64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + *f.toFloat64Ptr() = &v + return b[8:], nil +} + +func unmarshalFloat64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + s := f.toFloat64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + s := f.toFloat64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFloat32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + *f.toFloat32() = v + return b[4:], nil +} + +func unmarshalFloat32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + *f.toFloat32Ptr() = &v + return b[4:], nil +} + +func unmarshalFloat32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + s := f.toFloat32Slice() + *s = append(*s, v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + s := f.toFloat32Slice() + *s = append(*s, v) + return b[4:], nil +} + +func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toString() = v + return b[x:], nil +} + +func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toStringPtr() = &v + return b[x:], nil +} + +func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + s := f.toStringSlice() + *s = append(*s, v) + return b[x:], nil +} + +func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toString() = v + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toStringPtr() = &v + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + s := f.toStringSlice() + *s = append(*s, v) + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +var emptyBuf [0]byte + +func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + // The use of append here is a trick which avoids the zeroing + // that would be required if we used a make/copy pair. + // We append to emptyBuf instead of nil because we want + // a non-nil result even when the length is 0. + v := append(emptyBuf[:], b[:x]...) + *f.toBytes() = v + return b[x:], nil +} + +func unmarshalBytesSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := append(emptyBuf[:], b[:x]...) + s := f.toBytesSlice() + *s = append(*s, v) + return b[x:], nil +} + +func makeUnmarshalMessagePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + // First read the message field to see if something is there. + // The semantics of multiple submessages are weird. Instead of + // the last one winning (as it is for all other fields), multiple + // submessages are merged. + v := f.getPointer() + if v.isNil() { + v = valToPointer(reflect.New(sub.typ)) + f.setPointer(v) + } + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + return b[x:], err + } +} + +func makeUnmarshalMessageSlicePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := valToPointer(reflect.New(sub.typ)) + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + f.appendPointer(v) + return b[x:], err + } +} + +func makeUnmarshalGroupPtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireStartGroup { + return b, errInternalBadWireType + } + x, y := findEndGroup(b) + if x < 0 { + return nil, io.ErrUnexpectedEOF + } + v := f.getPointer() + if v.isNil() { + v = valToPointer(reflect.New(sub.typ)) + f.setPointer(v) + } + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + return b[y:], err + } +} + +func makeUnmarshalGroupSlicePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireStartGroup { + return b, errInternalBadWireType + } + x, y := findEndGroup(b) + if x < 0 { + return nil, io.ErrUnexpectedEOF + } + v := valToPointer(reflect.New(sub.typ)) + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + f.appendPointer(v) + return b[y:], err + } +} + +func makeUnmarshalMap(f *reflect.StructField) unmarshaler { + t := f.Type + kt := t.Key() + vt := t.Elem() + unmarshalKey := typeUnmarshaler(kt, f.Tag.Get("protobuf_key")) + unmarshalVal := typeUnmarshaler(vt, f.Tag.Get("protobuf_val")) + return func(b []byte, f pointer, w int) ([]byte, error) { + // The map entry is a submessage. Figure out how big it is. + if w != WireBytes { + return nil, fmt.Errorf("proto: bad wiretype for map field: got %d want %d", w, WireBytes) + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + r := b[x:] // unused data to return + b = b[:x] // data for map entry + + // Note: we could use #keys * #values ~= 200 functions + // to do map decoding without reflection. Probably not worth it. + // Maps will be somewhat slow. Oh well. + + // Read key and value from data. + var nerr nonFatal + k := reflect.New(kt) + v := reflect.New(vt) + for len(b) > 0 { + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + wire := int(x) & 7 + b = b[n:] + + var err error + switch x >> 3 { + case 1: + b, err = unmarshalKey(b, valToPointer(k), wire) + case 2: + b, err = unmarshalVal(b, valToPointer(v), wire) + default: + err = errInternalBadWireType // skip unknown tag + } + + if nerr.Merge(err) { + continue + } + if err != errInternalBadWireType { + return nil, err + } + + // Skip past unknown fields. + b, err = skipField(b, wire) + if err != nil { + return nil, err + } + } + + // Get map, allocate if needed. + m := f.asPointerTo(t).Elem() // an addressable map[K]T + if m.IsNil() { + m.Set(reflect.MakeMap(t)) + } + + // Insert into map. + m.SetMapIndex(k.Elem(), v.Elem()) + + return r, nerr.E + } +} + +// makeUnmarshalOneof makes an unmarshaler for oneof fields. +// for: +// message Msg { +// oneof F { +// int64 X = 1; +// float64 Y = 2; +// } +// } +// typ is the type of the concrete entry for a oneof case (e.g. Msg_X). +// ityp is the interface type of the oneof field (e.g. isMsg_F). +// unmarshal is the unmarshaler for the base type of the oneof case (e.g. int64). +// Note that this function will be called once for each case in the oneof. +func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshaler { + sf := typ.Field(0) + field0 := toField(&sf) + return func(b []byte, f pointer, w int) ([]byte, error) { + // Allocate holder for value. + v := reflect.New(typ) + + // Unmarshal data into holder. + // We unmarshal into the first field of the holder object. + var err error + var nerr nonFatal + b, err = unmarshal(b, valToPointer(v).offset(field0), w) + if !nerr.Merge(err) { + return nil, err + } + + // Write pointer to holder into target field. + f.asPointerTo(ityp).Elem().Set(v) + + return b, nerr.E + } +} + +// Error used by decode internally. +var errInternalBadWireType = errors.New("proto: internal error: bad wiretype") + +// skipField skips past a field of type wire and returns the remaining bytes. +func skipField(b []byte, wire int) ([]byte, error) { + switch wire { + case WireVarint: + _, k := decodeVarint(b) + if k == 0 { + return b, io.ErrUnexpectedEOF + } + b = b[k:] + case WireFixed32: + if len(b) < 4 { + return b, io.ErrUnexpectedEOF + } + b = b[4:] + case WireFixed64: + if len(b) < 8 { + return b, io.ErrUnexpectedEOF + } + b = b[8:] + case WireBytes: + m, k := decodeVarint(b) + if k == 0 || uint64(len(b)-k) < m { + return b, io.ErrUnexpectedEOF + } + b = b[uint64(k)+m:] + case WireStartGroup: + _, i := findEndGroup(b) + if i == -1 { + return b, io.ErrUnexpectedEOF + } + b = b[i:] + default: + return b, fmt.Errorf("proto: can't skip unknown wire type %d", wire) + } + return b, nil +} + +// findEndGroup finds the index of the next EndGroup tag. +// Groups may be nested, so the "next" EndGroup tag is the first +// unpaired EndGroup. +// findEndGroup returns the indexes of the start and end of the EndGroup tag. +// Returns (-1,-1) if it can't find one. +func findEndGroup(b []byte) (int, int) { + depth := 1 + i := 0 + for { + x, n := decodeVarint(b[i:]) + if n == 0 { + return -1, -1 + } + j := i + i += n + switch x & 7 { + case WireVarint: + _, k := decodeVarint(b[i:]) + if k == 0 { + return -1, -1 + } + i += k + case WireFixed32: + if len(b)-4 < i { + return -1, -1 + } + i += 4 + case WireFixed64: + if len(b)-8 < i { + return -1, -1 + } + i += 8 + case WireBytes: + m, k := decodeVarint(b[i:]) + if k == 0 { + return -1, -1 + } + i += k + if uint64(len(b)-i) < m { + return -1, -1 + } + i += int(m) + case WireStartGroup: + depth++ + case WireEndGroup: + depth-- + if depth == 0 { + return j, i + } + default: + return -1, -1 + } + } +} + +// encodeVarint appends a varint-encoded integer to b and returns the result. +func encodeVarint(b []byte, x uint64) []byte { + for x >= 1<<7 { + b = append(b, byte(x&0x7f|0x80)) + x >>= 7 + } + return append(b, byte(x)) +} + +// decodeVarint reads a varint-encoded integer from b. +// Returns the decoded integer and the number of bytes read. +// If there is an error, it returns 0,0. +func decodeVarint(b []byte) (uint64, int) { + var x, y uint64 + if len(b) == 0 { + goto bad + } + x = uint64(b[0]) + if x < 0x80 { + return x, 1 + } + x -= 0x80 + + if len(b) <= 1 { + goto bad + } + y = uint64(b[1]) + x += y << 7 + if y < 0x80 { + return x, 2 + } + x -= 0x80 << 7 + + if len(b) <= 2 { + goto bad + } + y = uint64(b[2]) + x += y << 14 + if y < 0x80 { + return x, 3 + } + x -= 0x80 << 14 + + if len(b) <= 3 { + goto bad + } + y = uint64(b[3]) + x += y << 21 + if y < 0x80 { + return x, 4 + } + x -= 0x80 << 21 + + if len(b) <= 4 { + goto bad + } + y = uint64(b[4]) + x += y << 28 + if y < 0x80 { + return x, 5 + } + x -= 0x80 << 28 + + if len(b) <= 5 { + goto bad + } + y = uint64(b[5]) + x += y << 35 + if y < 0x80 { + return x, 6 + } + x -= 0x80 << 35 + + if len(b) <= 6 { + goto bad + } + y = uint64(b[6]) + x += y << 42 + if y < 0x80 { + return x, 7 + } + x -= 0x80 << 42 + + if len(b) <= 7 { + goto bad + } + y = uint64(b[7]) + x += y << 49 + if y < 0x80 { + return x, 8 + } + x -= 0x80 << 49 + + if len(b) <= 8 { + goto bad + } + y = uint64(b[8]) + x += y << 56 + if y < 0x80 { + return x, 9 + } + x -= 0x80 << 56 + + if len(b) <= 9 { + goto bad + } + y = uint64(b[9]) + x += y << 63 + if y < 2 { + return x, 10 + } + +bad: + return 0, 0 +} diff --git a/vendor/github.com/golang/protobuf/proto/text.go b/vendor/github.com/golang/protobuf/proto/text.go new file mode 100644 index 0000000..1aaee72 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/text.go @@ -0,0 +1,843 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +// Functions for writing the text protocol buffer format. + +import ( + "bufio" + "bytes" + "encoding" + "errors" + "fmt" + "io" + "log" + "math" + "reflect" + "sort" + "strings" +) + +var ( + newline = []byte("\n") + spaces = []byte(" ") + endBraceNewline = []byte("}\n") + backslashN = []byte{'\\', 'n'} + backslashR = []byte{'\\', 'r'} + backslashT = []byte{'\\', 't'} + backslashDQ = []byte{'\\', '"'} + backslashBS = []byte{'\\', '\\'} + posInf = []byte("inf") + negInf = []byte("-inf") + nan = []byte("nan") +) + +type writer interface { + io.Writer + WriteByte(byte) error +} + +// textWriter is an io.Writer that tracks its indentation level. +type textWriter struct { + ind int + complete bool // if the current position is a complete line + compact bool // whether to write out as a one-liner + w writer +} + +func (w *textWriter) WriteString(s string) (n int, err error) { + if !strings.Contains(s, "\n") { + if !w.compact && w.complete { + w.writeIndent() + } + w.complete = false + return io.WriteString(w.w, s) + } + // WriteString is typically called without newlines, so this + // codepath and its copy are rare. We copy to avoid + // duplicating all of Write's logic here. + return w.Write([]byte(s)) +} + +func (w *textWriter) Write(p []byte) (n int, err error) { + newlines := bytes.Count(p, newline) + if newlines == 0 { + if !w.compact && w.complete { + w.writeIndent() + } + n, err = w.w.Write(p) + w.complete = false + return n, err + } + + frags := bytes.SplitN(p, newline, newlines+1) + if w.compact { + for i, frag := range frags { + if i > 0 { + if err := w.w.WriteByte(' '); err != nil { + return n, err + } + n++ + } + nn, err := w.w.Write(frag) + n += nn + if err != nil { + return n, err + } + } + return n, nil + } + + for i, frag := range frags { + if w.complete { + w.writeIndent() + } + nn, err := w.w.Write(frag) + n += nn + if err != nil { + return n, err + } + if i+1 < len(frags) { + if err := w.w.WriteByte('\n'); err != nil { + return n, err + } + n++ + } + } + w.complete = len(frags[len(frags)-1]) == 0 + return n, nil +} + +func (w *textWriter) WriteByte(c byte) error { + if w.compact && c == '\n' { + c = ' ' + } + if !w.compact && w.complete { + w.writeIndent() + } + err := w.w.WriteByte(c) + w.complete = c == '\n' + return err +} + +func (w *textWriter) indent() { w.ind++ } + +func (w *textWriter) unindent() { + if w.ind == 0 { + log.Print("proto: textWriter unindented too far") + return + } + w.ind-- +} + +func writeName(w *textWriter, props *Properties) error { + if _, err := w.WriteString(props.OrigName); err != nil { + return err + } + if props.Wire != "group" { + return w.WriteByte(':') + } + return nil +} + +func requiresQuotes(u string) bool { + // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. + for _, ch := range u { + switch { + case ch == '.' || ch == '/' || ch == '_': + continue + case '0' <= ch && ch <= '9': + continue + case 'A' <= ch && ch <= 'Z': + continue + case 'a' <= ch && ch <= 'z': + continue + default: + return true + } + } + return false +} + +// isAny reports whether sv is a google.protobuf.Any message +func isAny(sv reflect.Value) bool { + type wkt interface { + XXX_WellKnownType() string + } + t, ok := sv.Addr().Interface().(wkt) + return ok && t.XXX_WellKnownType() == "Any" +} + +// writeProto3Any writes an expanded google.protobuf.Any message. +// +// It returns (false, nil) if sv value can't be unmarshaled (e.g. because +// required messages are not linked in). +// +// It returns (true, error) when sv was written in expanded format or an error +// was encountered. +func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) { + turl := sv.FieldByName("TypeUrl") + val := sv.FieldByName("Value") + if !turl.IsValid() || !val.IsValid() { + return true, errors.New("proto: invalid google.protobuf.Any message") + } + + b, ok := val.Interface().([]byte) + if !ok { + return true, errors.New("proto: invalid google.protobuf.Any message") + } + + parts := strings.Split(turl.String(), "/") + mt := MessageType(parts[len(parts)-1]) + if mt == nil { + return false, nil + } + m := reflect.New(mt.Elem()) + if err := Unmarshal(b, m.Interface().(Message)); err != nil { + return false, nil + } + w.Write([]byte("[")) + u := turl.String() + if requiresQuotes(u) { + writeString(w, u) + } else { + w.Write([]byte(u)) + } + if w.compact { + w.Write([]byte("]:<")) + } else { + w.Write([]byte("]: <\n")) + w.ind++ + } + if err := tm.writeStruct(w, m.Elem()); err != nil { + return true, err + } + if w.compact { + w.Write([]byte("> ")) + } else { + w.ind-- + w.Write([]byte(">\n")) + } + return true, nil +} + +func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { + if tm.ExpandAny && isAny(sv) { + if canExpand, err := tm.writeProto3Any(w, sv); canExpand { + return err + } + } + st := sv.Type() + sprops := GetProperties(st) + for i := 0; i < sv.NumField(); i++ { + fv := sv.Field(i) + props := sprops.Prop[i] + name := st.Field(i).Name + + if name == "XXX_NoUnkeyedLiteral" { + continue + } + + if strings.HasPrefix(name, "XXX_") { + // There are two XXX_ fields: + // XXX_unrecognized []byte + // XXX_extensions map[int32]proto.Extension + // The first is handled here; + // the second is handled at the bottom of this function. + if name == "XXX_unrecognized" && !fv.IsNil() { + if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil { + return err + } + } + continue + } + if fv.Kind() == reflect.Ptr && fv.IsNil() { + // Field not filled in. This could be an optional field or + // a required field that wasn't filled in. Either way, there + // isn't anything we can show for it. + continue + } + if fv.Kind() == reflect.Slice && fv.IsNil() { + // Repeated field that is empty, or a bytes field that is unused. + continue + } + + if props.Repeated && fv.Kind() == reflect.Slice { + // Repeated field. + for j := 0; j < fv.Len(); j++ { + if err := writeName(w, props); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + v := fv.Index(j) + if v.Kind() == reflect.Ptr && v.IsNil() { + // A nil message in a repeated field is not valid, + // but we can handle that more gracefully than panicking. + if _, err := w.Write([]byte("\n")); err != nil { + return err + } + continue + } + if err := tm.writeAny(w, v, props); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { + return err + } + } + continue + } + if fv.Kind() == reflect.Map { + // Map fields are rendered as a repeated struct with key/value fields. + keys := fv.MapKeys() + sort.Sort(mapKeys(keys)) + for _, key := range keys { + val := fv.MapIndex(key) + if err := writeName(w, props); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + // open struct + if err := w.WriteByte('<'); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte('\n'); err != nil { + return err + } + } + w.indent() + // key + if _, err := w.WriteString("key:"); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { + return err + } + // nil values aren't legal, but we can avoid panicking because of them. + if val.Kind() != reflect.Ptr || !val.IsNil() { + // value + if _, err := w.WriteString("value:"); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + if err := tm.writeAny(w, val, props.MapValProp); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { + return err + } + } + // close struct + w.unindent() + if err := w.WriteByte('>'); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { + return err + } + } + continue + } + if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 { + // empty bytes field + continue + } + if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { + // proto3 non-repeated scalar field; skip if zero value + if isProto3Zero(fv) { + continue + } + } + + if fv.Kind() == reflect.Interface { + // Check if it is a oneof. + if st.Field(i).Tag.Get("protobuf_oneof") != "" { + // fv is nil, or holds a pointer to generated struct. + // That generated struct has exactly one field, + // which has a protobuf struct tag. + if fv.IsNil() { + continue + } + inner := fv.Elem().Elem() // interface -> *T -> T + tag := inner.Type().Field(0).Tag.Get("protobuf") + props = new(Properties) // Overwrite the outer props var, but not its pointee. + props.Parse(tag) + // Write the value in the oneof, not the oneof itself. + fv = inner.Field(0) + + // Special case to cope with malformed messages gracefully: + // If the value in the oneof is a nil pointer, don't panic + // in writeAny. + if fv.Kind() == reflect.Ptr && fv.IsNil() { + // Use errors.New so writeAny won't render quotes. + msg := errors.New("/* nil */") + fv = reflect.ValueOf(&msg).Elem() + } + } + } + + if err := writeName(w, props); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + + // Enums have a String method, so writeAny will work fine. + if err := tm.writeAny(w, fv, props); err != nil { + return err + } + + if err := w.WriteByte('\n'); err != nil { + return err + } + } + + // Extensions (the XXX_extensions field). + pv := sv.Addr() + if _, err := extendable(pv.Interface()); err == nil { + if err := tm.writeExtensions(w, pv); err != nil { + return err + } + } + + return nil +} + +// writeAny writes an arbitrary field. +func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { + v = reflect.Indirect(v) + + // Floats have special cases. + if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { + x := v.Float() + var b []byte + switch { + case math.IsInf(x, 1): + b = posInf + case math.IsInf(x, -1): + b = negInf + case math.IsNaN(x): + b = nan + } + if b != nil { + _, err := w.Write(b) + return err + } + // Other values are handled below. + } + + // We don't attempt to serialise every possible value type; only those + // that can occur in protocol buffers. + switch v.Kind() { + case reflect.Slice: + // Should only be a []byte; repeated fields are handled in writeStruct. + if err := writeString(w, string(v.Bytes())); err != nil { + return err + } + case reflect.String: + if err := writeString(w, v.String()); err != nil { + return err + } + case reflect.Struct: + // Required/optional group/message. + var bra, ket byte = '<', '>' + if props != nil && props.Wire == "group" { + bra, ket = '{', '}' + } + if err := w.WriteByte(bra); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte('\n'); err != nil { + return err + } + } + w.indent() + if v.CanAddr() { + // Calling v.Interface on a struct causes the reflect package to + // copy the entire struct. This is racy with the new Marshaler + // since we atomically update the XXX_sizecache. + // + // Thus, we retrieve a pointer to the struct if possible to avoid + // a race since v.Interface on the pointer doesn't copy the struct. + // + // If v is not addressable, then we are not worried about a race + // since it implies that the binary Marshaler cannot possibly be + // mutating this value. + v = v.Addr() + } + if etm, ok := v.Interface().(encoding.TextMarshaler); ok { + text, err := etm.MarshalText() + if err != nil { + return err + } + if _, err = w.Write(text); err != nil { + return err + } + } else { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if err := tm.writeStruct(w, v); err != nil { + return err + } + } + w.unindent() + if err := w.WriteByte(ket); err != nil { + return err + } + default: + _, err := fmt.Fprint(w, v.Interface()) + return err + } + return nil +} + +// equivalent to C's isprint. +func isprint(c byte) bool { + return c >= 0x20 && c < 0x7f +} + +// writeString writes a string in the protocol buffer text format. +// It is similar to strconv.Quote except we don't use Go escape sequences, +// we treat the string as a byte sequence, and we use octal escapes. +// These differences are to maintain interoperability with the other +// languages' implementations of the text format. +func writeString(w *textWriter, s string) error { + // use WriteByte here to get any needed indent + if err := w.WriteByte('"'); err != nil { + return err + } + // Loop over the bytes, not the runes. + for i := 0; i < len(s); i++ { + var err error + // Divergence from C++: we don't escape apostrophes. + // There's no need to escape them, and the C++ parser + // copes with a naked apostrophe. + switch c := s[i]; c { + case '\n': + _, err = w.w.Write(backslashN) + case '\r': + _, err = w.w.Write(backslashR) + case '\t': + _, err = w.w.Write(backslashT) + case '"': + _, err = w.w.Write(backslashDQ) + case '\\': + _, err = w.w.Write(backslashBS) + default: + if isprint(c) { + err = w.w.WriteByte(c) + } else { + _, err = fmt.Fprintf(w.w, "\\%03o", c) + } + } + if err != nil { + return err + } + } + return w.WriteByte('"') +} + +func writeUnknownStruct(w *textWriter, data []byte) (err error) { + if !w.compact { + if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { + return err + } + } + b := NewBuffer(data) + for b.index < len(b.buf) { + x, err := b.DecodeVarint() + if err != nil { + _, err := fmt.Fprintf(w, "/* %v */\n", err) + return err + } + wire, tag := x&7, x>>3 + if wire == WireEndGroup { + w.unindent() + if _, err := w.Write(endBraceNewline); err != nil { + return err + } + continue + } + if _, err := fmt.Fprint(w, tag); err != nil { + return err + } + if wire != WireStartGroup { + if err := w.WriteByte(':'); err != nil { + return err + } + } + if !w.compact || wire == WireStartGroup { + if err := w.WriteByte(' '); err != nil { + return err + } + } + switch wire { + case WireBytes: + buf, e := b.DecodeRawBytes(false) + if e == nil { + _, err = fmt.Fprintf(w, "%q", buf) + } else { + _, err = fmt.Fprintf(w, "/* %v */", e) + } + case WireFixed32: + x, err = b.DecodeFixed32() + err = writeUnknownInt(w, x, err) + case WireFixed64: + x, err = b.DecodeFixed64() + err = writeUnknownInt(w, x, err) + case WireStartGroup: + err = w.WriteByte('{') + w.indent() + case WireVarint: + x, err = b.DecodeVarint() + err = writeUnknownInt(w, x, err) + default: + _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) + } + if err != nil { + return err + } + if err = w.WriteByte('\n'); err != nil { + return err + } + } + return nil +} + +func writeUnknownInt(w *textWriter, x uint64, err error) error { + if err == nil { + _, err = fmt.Fprint(w, x) + } else { + _, err = fmt.Fprintf(w, "/* %v */", err) + } + return err +} + +type int32Slice []int32 + +func (s int32Slice) Len() int { return len(s) } +func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// writeExtensions writes all the extensions in pv. +// pv is assumed to be a pointer to a protocol message struct that is extendable. +func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { + emap := extensionMaps[pv.Type().Elem()] + ep, _ := extendable(pv.Interface()) + + // Order the extensions by ID. + // This isn't strictly necessary, but it will give us + // canonical output, which will also make testing easier. + m, mu := ep.extensionsRead() + if m == nil { + return nil + } + mu.Lock() + ids := make([]int32, 0, len(m)) + for id := range m { + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) + mu.Unlock() + + for _, extNum := range ids { + ext := m[extNum] + var desc *ExtensionDesc + if emap != nil { + desc = emap[extNum] + } + if desc == nil { + // Unknown extension. + if err := writeUnknownStruct(w, ext.enc); err != nil { + return err + } + continue + } + + pb, err := GetExtension(ep, desc) + if err != nil { + return fmt.Errorf("failed getting extension: %v", err) + } + + // Repeated extensions will appear as a slice. + if !desc.repeated() { + if err := tm.writeExtension(w, desc.Name, pb); err != nil { + return err + } + } else { + v := reflect.ValueOf(pb) + for i := 0; i < v.Len(); i++ { + if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { + return err + } + } + } + } + return nil +} + +func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { + if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { + return err + } + return nil +} + +func (w *textWriter) writeIndent() { + if !w.complete { + return + } + remain := w.ind * 2 + for remain > 0 { + n := remain + if n > len(spaces) { + n = len(spaces) + } + w.w.Write(spaces[:n]) + remain -= n + } + w.complete = false +} + +// TextMarshaler is a configurable text format marshaler. +type TextMarshaler struct { + Compact bool // use compact text format (one line). + ExpandAny bool // expand google.protobuf.Any messages of known types +} + +// Marshal writes a given protocol buffer in text format. +// The only errors returned are from w. +func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { + val := reflect.ValueOf(pb) + if pb == nil || val.IsNil() { + w.Write([]byte("")) + return nil + } + var bw *bufio.Writer + ww, ok := w.(writer) + if !ok { + bw = bufio.NewWriter(w) + ww = bw + } + aw := &textWriter{ + w: ww, + complete: true, + compact: tm.Compact, + } + + if etm, ok := pb.(encoding.TextMarshaler); ok { + text, err := etm.MarshalText() + if err != nil { + return err + } + if _, err = aw.Write(text); err != nil { + return err + } + if bw != nil { + return bw.Flush() + } + return nil + } + // Dereference the received pointer so we don't have outer < and >. + v := reflect.Indirect(val) + if err := tm.writeStruct(aw, v); err != nil { + return err + } + if bw != nil { + return bw.Flush() + } + return nil +} + +// Text is the same as Marshal, but returns the string directly. +func (tm *TextMarshaler) Text(pb Message) string { + var buf bytes.Buffer + tm.Marshal(&buf, pb) + return buf.String() +} + +var ( + defaultTextMarshaler = TextMarshaler{} + compactTextMarshaler = TextMarshaler{Compact: true} +) + +// TODO: consider removing some of the Marshal functions below. + +// MarshalText writes a given protocol buffer in text format. +// The only errors returned are from w. +func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } + +// MarshalTextString is the same as MarshalText, but returns the string directly. +func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } + +// CompactText writes a given protocol buffer in compact text format (one line). +func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } + +// CompactTextString is the same as CompactText, but returns the string directly. +func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go new file mode 100644 index 0000000..bb55a3a --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/text_parser.go @@ -0,0 +1,880 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +// Functions for parsing the Text protocol buffer format. +// TODO: message sets. + +import ( + "encoding" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "unicode/utf8" +) + +// Error string emitted when deserializing Any and fields are already set +const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set" + +type ParseError struct { + Message string + Line int // 1-based line number + Offset int // 0-based byte offset from start of input +} + +func (p *ParseError) Error() string { + if p.Line == 1 { + // show offset only for first line + return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message) + } + return fmt.Sprintf("line %d: %v", p.Line, p.Message) +} + +type token struct { + value string + err *ParseError + line int // line number + offset int // byte number from start of input, not start of line + unquoted string // the unquoted version of value, if it was a quoted string +} + +func (t *token) String() string { + if t.err == nil { + return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset) + } + return fmt.Sprintf("parse error: %v", t.err) +} + +type textParser struct { + s string // remaining input + done bool // whether the parsing is finished (success or error) + backed bool // whether back() was called + offset, line int + cur token +} + +func newTextParser(s string) *textParser { + p := new(textParser) + p.s = s + p.line = 1 + p.cur.line = 1 + return p +} + +func (p *textParser) errorf(format string, a ...interface{}) *ParseError { + pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} + p.cur.err = pe + p.done = true + return pe +} + +// Numbers and identifiers are matched by [-+._A-Za-z0-9] +func isIdentOrNumberChar(c byte) bool { + switch { + case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': + return true + case '0' <= c && c <= '9': + return true + } + switch c { + case '-', '+', '.', '_': + return true + } + return false +} + +func isWhitespace(c byte) bool { + switch c { + case ' ', '\t', '\n', '\r': + return true + } + return false +} + +func isQuote(c byte) bool { + switch c { + case '"', '\'': + return true + } + return false +} + +func (p *textParser) skipWhitespace() { + i := 0 + for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { + if p.s[i] == '#' { + // comment; skip to end of line or input + for i < len(p.s) && p.s[i] != '\n' { + i++ + } + if i == len(p.s) { + break + } + } + if p.s[i] == '\n' { + p.line++ + } + i++ + } + p.offset += i + p.s = p.s[i:len(p.s)] + if len(p.s) == 0 { + p.done = true + } +} + +func (p *textParser) advance() { + // Skip whitespace + p.skipWhitespace() + if p.done { + return + } + + // Start of non-whitespace + p.cur.err = nil + p.cur.offset, p.cur.line = p.offset, p.line + p.cur.unquoted = "" + switch p.s[0] { + case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': + // Single symbol + p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] + case '"', '\'': + // Quoted string + i := 1 + for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { + if p.s[i] == '\\' && i+1 < len(p.s) { + // skip escaped char + i++ + } + i++ + } + if i >= len(p.s) || p.s[i] != p.s[0] { + p.errorf("unmatched quote") + return + } + unq, err := unquoteC(p.s[1:i], rune(p.s[0])) + if err != nil { + p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) + return + } + p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] + p.cur.unquoted = unq + default: + i := 0 + for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { + i++ + } + if i == 0 { + p.errorf("unexpected byte %#x", p.s[0]) + return + } + p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] + } + p.offset += len(p.cur.value) +} + +var ( + errBadUTF8 = errors.New("proto: bad UTF-8") +) + +func unquoteC(s string, quote rune) (string, error) { + // This is based on C++'s tokenizer.cc. + // Despite its name, this is *not* parsing C syntax. + // For instance, "\0" is an invalid quoted string. + + // Avoid allocation in trivial cases. + simple := true + for _, r := range s { + if r == '\\' || r == quote { + simple = false + break + } + } + if simple { + return s, nil + } + + buf := make([]byte, 0, 3*len(s)/2) + for len(s) > 0 { + r, n := utf8.DecodeRuneInString(s) + if r == utf8.RuneError && n == 1 { + return "", errBadUTF8 + } + s = s[n:] + if r != '\\' { + if r < utf8.RuneSelf { + buf = append(buf, byte(r)) + } else { + buf = append(buf, string(r)...) + } + continue + } + + ch, tail, err := unescape(s) + if err != nil { + return "", err + } + buf = append(buf, ch...) + s = tail + } + return string(buf), nil +} + +func unescape(s string) (ch string, tail string, err error) { + r, n := utf8.DecodeRuneInString(s) + if r == utf8.RuneError && n == 1 { + return "", "", errBadUTF8 + } + s = s[n:] + switch r { + case 'a': + return "\a", s, nil + case 'b': + return "\b", s, nil + case 'f': + return "\f", s, nil + case 'n': + return "\n", s, nil + case 'r': + return "\r", s, nil + case 't': + return "\t", s, nil + case 'v': + return "\v", s, nil + case '?': + return "?", s, nil // trigraph workaround + case '\'', '"', '\\': + return string(r), s, nil + case '0', '1', '2', '3', '4', '5', '6', '7': + if len(s) < 2 { + return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) + } + ss := string(r) + s[:2] + s = s[2:] + i, err := strconv.ParseUint(ss, 8, 8) + if err != nil { + return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) + } + return string([]byte{byte(i)}), s, nil + case 'x', 'X', 'u', 'U': + var n int + switch r { + case 'x', 'X': + n = 2 + case 'u': + n = 4 + case 'U': + n = 8 + } + if len(s) < n { + return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) + } + ss := s[:n] + s = s[n:] + i, err := strconv.ParseUint(ss, 16, 64) + if err != nil { + return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) + } + if r == 'x' || r == 'X' { + return string([]byte{byte(i)}), s, nil + } + if i > utf8.MaxRune { + return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) + } + return string(i), s, nil + } + return "", "", fmt.Errorf(`unknown escape \%c`, r) +} + +// Back off the parser by one token. Can only be done between calls to next(). +// It makes the next advance() a no-op. +func (p *textParser) back() { p.backed = true } + +// Advances the parser and returns the new current token. +func (p *textParser) next() *token { + if p.backed || p.done { + p.backed = false + return &p.cur + } + p.advance() + if p.done { + p.cur.value = "" + } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { + // Look for multiple quoted strings separated by whitespace, + // and concatenate them. + cat := p.cur + for { + p.skipWhitespace() + if p.done || !isQuote(p.s[0]) { + break + } + p.advance() + if p.cur.err != nil { + return &p.cur + } + cat.value += " " + p.cur.value + cat.unquoted += p.cur.unquoted + } + p.done = false // parser may have seen EOF, but we want to return cat + p.cur = cat + } + return &p.cur +} + +func (p *textParser) consumeToken(s string) error { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value != s { + p.back() + return p.errorf("expected %q, found %q", s, tok.value) + } + return nil +} + +// Return a RequiredNotSetError indicating which required field was not set. +func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError { + st := sv.Type() + sprops := GetProperties(st) + for i := 0; i < st.NumField(); i++ { + if !isNil(sv.Field(i)) { + continue + } + + props := sprops.Prop[i] + if props.Required { + return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)} + } + } + return &RequiredNotSetError{fmt.Sprintf("%v.", st)} // should not happen +} + +// Returns the index in the struct for the named field, as well as the parsed tag properties. +func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) { + i, ok := sprops.decoderOrigNames[name] + if ok { + return i, sprops.Prop[i], true + } + return -1, nil, false +} + +// Consume a ':' from the input stream (if the next token is a colon), +// returning an error if a colon is needed but not present. +func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value != ":" { + // Colon is optional when the field is a group or message. + needColon := true + switch props.Wire { + case "group": + needColon = false + case "bytes": + // A "bytes" field is either a message, a string, or a repeated field; + // those three become *T, *string and []T respectively, so we can check for + // this field being a pointer to a non-string. + if typ.Kind() == reflect.Ptr { + // *T or *string + if typ.Elem().Kind() == reflect.String { + break + } + } else if typ.Kind() == reflect.Slice { + // []T or []*T + if typ.Elem().Kind() != reflect.Ptr { + break + } + } else if typ.Kind() == reflect.String { + // The proto3 exception is for a string field, + // which requires a colon. + break + } + needColon = false + } + if needColon { + return p.errorf("expected ':', found %q", tok.value) + } + p.back() + } + return nil +} + +func (p *textParser) readStruct(sv reflect.Value, terminator string) error { + st := sv.Type() + sprops := GetProperties(st) + reqCount := sprops.reqCount + var reqFieldErr error + fieldSet := make(map[string]bool) + // A struct is a sequence of "name: value", terminated by one of + // '>' or '}', or the end of the input. A name may also be + // "[extension]" or "[type/url]". + // + // The whole struct can also be an expanded Any message, like: + // [type/url] < ... struct contents ... > + for { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == terminator { + break + } + if tok.value == "[" { + // Looks like an extension or an Any. + // + // TODO: Check whether we need to handle + // namespace rooted names (e.g. ".something.Foo"). + extName, err := p.consumeExtName() + if err != nil { + return err + } + + if s := strings.LastIndex(extName, "/"); s >= 0 { + // If it contains a slash, it's an Any type URL. + messageName := extName[s+1:] + mt := MessageType(messageName) + if mt == nil { + return p.errorf("unrecognized message %q in google.protobuf.Any", messageName) + } + tok = p.next() + if tok.err != nil { + return tok.err + } + // consume an optional colon + if tok.value == ":" { + tok = p.next() + if tok.err != nil { + return tok.err + } + } + var terminator string + switch tok.value { + case "<": + terminator = ">" + case "{": + terminator = "}" + default: + return p.errorf("expected '{' or '<', found %q", tok.value) + } + v := reflect.New(mt.Elem()) + if pe := p.readStruct(v.Elem(), terminator); pe != nil { + return pe + } + b, err := Marshal(v.Interface().(Message)) + if err != nil { + return p.errorf("failed to marshal message of type %q: %v", messageName, err) + } + if fieldSet["type_url"] { + return p.errorf(anyRepeatedlyUnpacked, "type_url") + } + if fieldSet["value"] { + return p.errorf(anyRepeatedlyUnpacked, "value") + } + sv.FieldByName("TypeUrl").SetString(extName) + sv.FieldByName("Value").SetBytes(b) + fieldSet["type_url"] = true + fieldSet["value"] = true + continue + } + + var desc *ExtensionDesc + // This could be faster, but it's functional. + // TODO: Do something smarter than a linear scan. + for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { + if d.Name == extName { + desc = d + break + } + } + if desc == nil { + return p.errorf("unrecognized extension %q", extName) + } + + props := &Properties{} + props.Parse(desc.Tag) + + typ := reflect.TypeOf(desc.ExtensionType) + if err := p.checkForColon(props, typ); err != nil { + return err + } + + rep := desc.repeated() + + // Read the extension structure, and set it in + // the value we're constructing. + var ext reflect.Value + if !rep { + ext = reflect.New(typ).Elem() + } else { + ext = reflect.New(typ.Elem()).Elem() + } + if err := p.readAny(ext, props); err != nil { + if _, ok := err.(*RequiredNotSetError); !ok { + return err + } + reqFieldErr = err + } + ep := sv.Addr().Interface().(Message) + if !rep { + SetExtension(ep, desc, ext.Interface()) + } else { + old, err := GetExtension(ep, desc) + var sl reflect.Value + if err == nil { + sl = reflect.ValueOf(old) // existing slice + } else { + sl = reflect.MakeSlice(typ, 0, 1) + } + sl = reflect.Append(sl, ext) + SetExtension(ep, desc, sl.Interface()) + } + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + continue + } + + // This is a normal, non-extension field. + name := tok.value + var dst reflect.Value + fi, props, ok := structFieldByName(sprops, name) + if ok { + dst = sv.Field(fi) + } else if oop, ok := sprops.OneofTypes[name]; ok { + // It is a oneof. + props = oop.Prop + nv := reflect.New(oop.Type.Elem()) + dst = nv.Elem().Field(0) + field := sv.Field(oop.Field) + if !field.IsNil() { + return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name) + } + field.Set(nv) + } + if !dst.IsValid() { + return p.errorf("unknown field name %q in %v", name, st) + } + + if dst.Kind() == reflect.Map { + // Consume any colon. + if err := p.checkForColon(props, dst.Type()); err != nil { + return err + } + + // Construct the map if it doesn't already exist. + if dst.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + key := reflect.New(dst.Type().Key()).Elem() + val := reflect.New(dst.Type().Elem()).Elem() + + // The map entry should be this sequence of tokens: + // < key : KEY value : VALUE > + // However, implementations may omit key or value, and technically + // we should support them in any order. See b/28924776 for a time + // this went wrong. + + tok := p.next() + var terminator string + switch tok.value { + case "<": + terminator = ">" + case "{": + terminator = "}" + default: + return p.errorf("expected '{' or '<', found %q", tok.value) + } + for { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == terminator { + break + } + switch tok.value { + case "key": + if err := p.consumeToken(":"); err != nil { + return err + } + if err := p.readAny(key, props.MapKeyProp); err != nil { + return err + } + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + case "value": + if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { + return err + } + if err := p.readAny(val, props.MapValProp); err != nil { + return err + } + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + default: + p.back() + return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) + } + } + + dst.SetMapIndex(key, val) + continue + } + + // Check that it's not already set if it's not a repeated field. + if !props.Repeated && fieldSet[name] { + return p.errorf("non-repeated field %q was repeated", name) + } + + if err := p.checkForColon(props, dst.Type()); err != nil { + return err + } + + // Parse into the field. + fieldSet[name] = true + if err := p.readAny(dst, props); err != nil { + if _, ok := err.(*RequiredNotSetError); !ok { + return err + } + reqFieldErr = err + } + if props.Required { + reqCount-- + } + + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + + } + + if reqCount > 0 { + return p.missingRequiredFieldError(sv) + } + return reqFieldErr +} + +// consumeExtName consumes extension name or expanded Any type URL and the +// following ']'. It returns the name or URL consumed. +func (p *textParser) consumeExtName() (string, error) { + tok := p.next() + if tok.err != nil { + return "", tok.err + } + + // If extension name or type url is quoted, it's a single token. + if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { + name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) + if err != nil { + return "", err + } + return name, p.consumeToken("]") + } + + // Consume everything up to "]" + var parts []string + for tok.value != "]" { + parts = append(parts, tok.value) + tok = p.next() + if tok.err != nil { + return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) + } + if p.done && tok.value != "]" { + return "", p.errorf("unclosed type_url or extension name") + } + } + return strings.Join(parts, ""), nil +} + +// consumeOptionalSeparator consumes an optional semicolon or comma. +// It is used in readStruct to provide backward compatibility. +func (p *textParser) consumeOptionalSeparator() error { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value != ";" && tok.value != "," { + p.back() + } + return nil +} + +func (p *textParser) readAny(v reflect.Value, props *Properties) error { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == "" { + return p.errorf("unexpected EOF") + } + + switch fv := v; fv.Kind() { + case reflect.Slice: + at := v.Type() + if at.Elem().Kind() == reflect.Uint8 { + // Special case for []byte + if tok.value[0] != '"' && tok.value[0] != '\'' { + // Deliberately written out here, as the error after + // this switch statement would write "invalid []byte: ...", + // which is not as user-friendly. + return p.errorf("invalid string: %v", tok.value) + } + bytes := []byte(tok.unquoted) + fv.Set(reflect.ValueOf(bytes)) + return nil + } + // Repeated field. + if tok.value == "[" { + // Repeated field with list notation, like [1,2,3]. + for { + fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) + err := p.readAny(fv.Index(fv.Len()-1), props) + if err != nil { + return err + } + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == "]" { + break + } + if tok.value != "," { + return p.errorf("Expected ']' or ',' found %q", tok.value) + } + } + return nil + } + // One value of the repeated field. + p.back() + fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) + return p.readAny(fv.Index(fv.Len()-1), props) + case reflect.Bool: + // true/1/t/True or false/f/0/False. + switch tok.value { + case "true", "1", "t", "True": + fv.SetBool(true) + return nil + case "false", "0", "f", "False": + fv.SetBool(false) + return nil + } + case reflect.Float32, reflect.Float64: + v := tok.value + // Ignore 'f' for compatibility with output generated by C++, but don't + // remove 'f' when the value is "-inf" or "inf". + if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" { + v = v[:len(v)-1] + } + if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil { + fv.SetFloat(f) + return nil + } + case reflect.Int32: + if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { + fv.SetInt(x) + return nil + } + + if len(props.Enum) == 0 { + break + } + m, ok := enumValueMaps[props.Enum] + if !ok { + break + } + x, ok := m[tok.value] + if !ok { + break + } + fv.SetInt(int64(x)) + return nil + case reflect.Int64: + if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { + fv.SetInt(x) + return nil + } + + case reflect.Ptr: + // A basic field (indirected through pointer), or a repeated message/group + p.back() + fv.Set(reflect.New(fv.Type().Elem())) + return p.readAny(fv.Elem(), props) + case reflect.String: + if tok.value[0] == '"' || tok.value[0] == '\'' { + fv.SetString(tok.unquoted) + return nil + } + case reflect.Struct: + var terminator string + switch tok.value { + case "{": + terminator = "}" + case "<": + terminator = ">" + default: + return p.errorf("expected '{' or '<', found %q", tok.value) + } + // TODO: Handle nested messages which implement encoding.TextUnmarshaler. + return p.readStruct(fv, terminator) + case reflect.Uint32: + if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { + fv.SetUint(uint64(x)) + return nil + } + case reflect.Uint64: + if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { + fv.SetUint(x) + return nil + } + } + return p.errorf("invalid %v: %v", v.Type(), tok.value) +} + +// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb +// before starting to unmarshal, so any existing data in pb is always removed. +// If a required field is not set and no other error occurs, +// UnmarshalText returns *RequiredNotSetError. +func UnmarshalText(s string, pb Message) error { + if um, ok := pb.(encoding.TextUnmarshaler); ok { + return um.UnmarshalText([]byte(s)) + } + pb.Reset() + v := reflect.ValueOf(pb) + return newTextParser(s).readStruct(v.Elem(), "") +} diff --git a/vendor/github.com/golang/protobuf/ptypes/any.go b/vendor/github.com/golang/protobuf/ptypes/any.go new file mode 100644 index 0000000..70276e8 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/any.go @@ -0,0 +1,141 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package ptypes + +// This file implements functions to marshal proto.Message to/from +// google.protobuf.Any message. + +import ( + "fmt" + "reflect" + "strings" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes/any" +) + +const googleApis = "type.googleapis.com/" + +// AnyMessageName returns the name of the message contained in a google.protobuf.Any message. +// +// Note that regular type assertions should be done using the Is +// function. AnyMessageName is provided for less common use cases like filtering a +// sequence of Any messages based on a set of allowed message type names. +func AnyMessageName(any *any.Any) (string, error) { + if any == nil { + return "", fmt.Errorf("message is nil") + } + slash := strings.LastIndex(any.TypeUrl, "/") + if slash < 0 { + return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl) + } + return any.TypeUrl[slash+1:], nil +} + +// MarshalAny takes the protocol buffer and encodes it into google.protobuf.Any. +func MarshalAny(pb proto.Message) (*any.Any, error) { + value, err := proto.Marshal(pb) + if err != nil { + return nil, err + } + return &any.Any{TypeUrl: googleApis + proto.MessageName(pb), Value: value}, nil +} + +// DynamicAny is a value that can be passed to UnmarshalAny to automatically +// allocate a proto.Message for the type specified in a google.protobuf.Any +// message. The allocated message is stored in the embedded proto.Message. +// +// Example: +// +// var x ptypes.DynamicAny +// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... } +// fmt.Printf("unmarshaled message: %v", x.Message) +type DynamicAny struct { + proto.Message +} + +// Empty returns a new proto.Message of the type specified in a +// google.protobuf.Any message. It returns an error if corresponding message +// type isn't linked in. +func Empty(any *any.Any) (proto.Message, error) { + aname, err := AnyMessageName(any) + if err != nil { + return nil, err + } + + t := proto.MessageType(aname) + if t == nil { + return nil, fmt.Errorf("any: message type %q isn't linked in", aname) + } + return reflect.New(t.Elem()).Interface().(proto.Message), nil +} + +// UnmarshalAny parses the protocol buffer representation in a google.protobuf.Any +// message and places the decoded result in pb. It returns an error if type of +// contents of Any message does not match type of pb message. +// +// pb can be a proto.Message, or a *DynamicAny. +func UnmarshalAny(any *any.Any, pb proto.Message) error { + if d, ok := pb.(*DynamicAny); ok { + if d.Message == nil { + var err error + d.Message, err = Empty(any) + if err != nil { + return err + } + } + return UnmarshalAny(any, d.Message) + } + + aname, err := AnyMessageName(any) + if err != nil { + return err + } + + mname := proto.MessageName(pb) + if aname != mname { + return fmt.Errorf("mismatched message type: got %q want %q", aname, mname) + } + return proto.Unmarshal(any.Value, pb) +} + +// Is returns true if any value contains a given message type. +func Is(any *any.Any, pb proto.Message) bool { + // The following is equivalent to AnyMessageName(any) == proto.MessageName(pb), + // but it avoids scanning TypeUrl for the slash. + if any == nil { + return false + } + name := proto.MessageName(pb) + prefix := len(any.TypeUrl) - len(name) + return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name +} diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go new file mode 100644 index 0000000..6e760e3 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go @@ -0,0 +1,195 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/any.proto + +package any + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := ptypes.MarshalAny(foo) +// ... +// foo := &pb.Foo{} +// if err := ptypes.UnmarshalAny(any, foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +type Any struct { + // A URL/resource name whose content describes the type of the + // serialized protocol buffer message. + // + // For URLs which use the scheme `http`, `https`, or no scheme, the + // following restrictions and interpretations apply: + // + // * If no scheme is provided, `https` is assumed. + // * The last segment of the URL's path must represent the fully + // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // Must be a valid serialized protocol buffer of the above specified type. + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Any) Reset() { *m = Any{} } +func (m *Any) String() string { return proto.CompactTextString(m) } +func (*Any) ProtoMessage() {} +func (*Any) Descriptor() ([]byte, []int) { + return fileDescriptor_b53526c13ae22eb4, []int{0} +} + +func (*Any) XXX_WellKnownType() string { return "Any" } + +func (m *Any) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Any.Unmarshal(m, b) +} +func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Any.Marshal(b, m, deterministic) +} +func (m *Any) XXX_Merge(src proto.Message) { + xxx_messageInfo_Any.Merge(m, src) +} +func (m *Any) XXX_Size() int { + return xxx_messageInfo_Any.Size(m) +} +func (m *Any) XXX_DiscardUnknown() { + xxx_messageInfo_Any.DiscardUnknown(m) +} + +var xxx_messageInfo_Any proto.InternalMessageInfo + +func (m *Any) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Any) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*Any)(nil), "google.protobuf.Any") +} + +func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) } + +var fileDescriptor_b53526c13ae22eb4 = []byte{ + // 185 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4, + 0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a, + 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, + 0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc2, 0x71, 0xca, 0xe7, 0x12, 0x4e, 0xce, + 0xcf, 0xd5, 0x43, 0x33, 0xce, 0x89, 0xc3, 0x31, 0xaf, 0x32, 0x00, 0xc4, 0x09, 0x60, 0x8c, 0x52, + 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, + 0x4b, 0x47, 0xb8, 0xa8, 0x00, 0x64, 0x7a, 0x31, 0xc8, 0x61, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c, + 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x8c, 0x0a, 0x80, 0x2a, 0xd1, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce, + 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0x29, 0x4d, 0x62, 0x03, 0xeb, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, + 0xff, 0x13, 0xf8, 0xe8, 0x42, 0xdd, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.proto b/vendor/github.com/golang/protobuf/ptypes/any/any.proto new file mode 100644 index 0000000..c748667 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.proto @@ -0,0 +1,149 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/any"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := ptypes.MarshalAny(foo) +// ... +// foo := &pb.Foo{} +// if err := ptypes.UnmarshalAny(any, foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name whose content describes the type of the + // serialized protocol buffer message. + // + // For URLs which use the scheme `http`, `https`, or no scheme, the + // following restrictions and interpretations apply: + // + // * If no scheme is provided, `https` is assumed. + // * The last segment of the URL's path must represent the fully + // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/vendor/github.com/golang/protobuf/ptypes/doc.go b/vendor/github.com/golang/protobuf/ptypes/doc.go new file mode 100644 index 0000000..c0d595d --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/doc.go @@ -0,0 +1,35 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package ptypes contains code for interacting with well-known types. +*/ +package ptypes diff --git a/vendor/github.com/golang/protobuf/ptypes/duration.go b/vendor/github.com/golang/protobuf/ptypes/duration.go new file mode 100644 index 0000000..65cb0f8 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/duration.go @@ -0,0 +1,102 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package ptypes + +// This file implements conversions between google.protobuf.Duration +// and time.Duration. + +import ( + "errors" + "fmt" + "time" + + durpb "github.com/golang/protobuf/ptypes/duration" +) + +const ( + // Range of a durpb.Duration in seconds, as specified in + // google/protobuf/duration.proto. This is about 10,000 years in seconds. + maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60) + minSeconds = -maxSeconds +) + +// validateDuration determines whether the durpb.Duration is valid according to the +// definition in google/protobuf/duration.proto. A valid durpb.Duration +// may still be too large to fit into a time.Duration (the range of durpb.Duration +// is about 10,000 years, and the range of time.Duration is about 290). +func validateDuration(d *durpb.Duration) error { + if d == nil { + return errors.New("duration: nil Duration") + } + if d.Seconds < minSeconds || d.Seconds > maxSeconds { + return fmt.Errorf("duration: %v: seconds out of range", d) + } + if d.Nanos <= -1e9 || d.Nanos >= 1e9 { + return fmt.Errorf("duration: %v: nanos out of range", d) + } + // Seconds and Nanos must have the same sign, unless d.Nanos is zero. + if (d.Seconds < 0 && d.Nanos > 0) || (d.Seconds > 0 && d.Nanos < 0) { + return fmt.Errorf("duration: %v: seconds and nanos have different signs", d) + } + return nil +} + +// Duration converts a durpb.Duration to a time.Duration. Duration +// returns an error if the durpb.Duration is invalid or is too large to be +// represented in a time.Duration. +func Duration(p *durpb.Duration) (time.Duration, error) { + if err := validateDuration(p); err != nil { + return 0, err + } + d := time.Duration(p.Seconds) * time.Second + if int64(d/time.Second) != p.Seconds { + return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) + } + if p.Nanos != 0 { + d += time.Duration(p.Nanos) + if (d < 0) != (p.Nanos < 0) { + return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) + } + } + return d, nil +} + +// DurationProto converts a time.Duration to a durpb.Duration. +func DurationProto(d time.Duration) *durpb.Duration { + nanos := d.Nanoseconds() + secs := nanos / 1e9 + nanos -= secs * 1e9 + return &durpb.Duration{ + Seconds: secs, + Nanos: int32(nanos), + } +} diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go new file mode 100644 index 0000000..5e841af --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go @@ -0,0 +1,161 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/duration.proto + +package duration + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (durations.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +// +type Duration struct { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Duration) Reset() { *m = Duration{} } +func (m *Duration) String() string { return proto.CompactTextString(m) } +func (*Duration) ProtoMessage() {} +func (*Duration) Descriptor() ([]byte, []int) { + return fileDescriptor_23597b2ebd7ac6c5, []int{0} +} + +func (*Duration) XXX_WellKnownType() string { return "Duration" } + +func (m *Duration) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Duration.Unmarshal(m, b) +} +func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Duration.Marshal(b, m, deterministic) +} +func (m *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) +} +func (m *Duration) XXX_Size() int { + return xxx_messageInfo_Duration.Size(m) +} +func (m *Duration) XXX_DiscardUnknown() { + xxx_messageInfo_Duration.DiscardUnknown(m) +} + +var xxx_messageInfo_Duration proto.InternalMessageInfo + +func (m *Duration) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Duration) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + +func init() { + proto.RegisterType((*Duration)(nil), "google.protobuf.Duration") +} + +func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) } + +var fileDescriptor_23597b2ebd7ac6c5 = []byte{ + // 190 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, + 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, + 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, + 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, + 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0x54, 0xc3, 0x25, 0x9c, 0x9c, + 0x9f, 0xab, 0x87, 0x66, 0xa4, 0x13, 0x2f, 0xcc, 0xc0, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x56, + 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, + 0x3a, 0xc2, 0x7d, 0x05, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x70, 0x67, 0xfe, 0x60, 0x64, 0x5c, 0xc4, + 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0x62, 0x6e, 0x00, 0x54, 0xa9, 0x5e, 0x78, + 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x4b, 0x12, 0x1b, 0xd8, 0x0c, 0x63, + 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x84, 0x30, 0xff, 0xf3, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto new file mode 100644 index 0000000..975fce4 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/duration"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (durations.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +// +message Duration { + + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go new file mode 100644 index 0000000..3085d76 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go @@ -0,0 +1,83 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/empty.proto + +package empty + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +type Empty struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Empty) Reset() { *m = Empty{} } +func (m *Empty) String() string { return proto.CompactTextString(m) } +func (*Empty) ProtoMessage() {} +func (*Empty) Descriptor() ([]byte, []int) { + return fileDescriptor_900544acb223d5b8, []int{0} +} + +func (*Empty) XXX_WellKnownType() string { return "Empty" } + +func (m *Empty) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Empty.Unmarshal(m, b) +} +func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Empty.Marshal(b, m, deterministic) +} +func (m *Empty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Empty.Merge(m, src) +} +func (m *Empty) XXX_Size() int { + return xxx_messageInfo_Empty.Size(m) +} +func (m *Empty) XXX_DiscardUnknown() { + xxx_messageInfo_Empty.DiscardUnknown(m) +} + +var xxx_messageInfo_Empty proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Empty)(nil), "google.protobuf.Empty") +} + +func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_900544acb223d5b8) } + +var fileDescriptor_900544acb223d5b8 = []byte{ + // 148 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28, + 0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57, + 0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36, + 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x4f, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, + 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0x47, 0x58, 0x53, 0x50, 0x52, 0x59, 0x90, 0x5a, 0x0c, + 0xb1, 0xed, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, + 0x13, 0x03, 0xa0, 0xea, 0xf4, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40, + 0xea, 0x93, 0xd8, 0xc0, 0x06, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x64, 0xd4, 0xb3, 0xa6, + 0xb7, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto b/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto new file mode 100644 index 0000000..03cacd2 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp.go b/vendor/github.com/golang/protobuf/ptypes/timestamp.go new file mode 100644 index 0000000..47f10db --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp.go @@ -0,0 +1,134 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package ptypes + +// This file implements operations on google.protobuf.Timestamp. + +import ( + "errors" + "fmt" + "time" + + tspb "github.com/golang/protobuf/ptypes/timestamp" +) + +const ( + // Seconds field of the earliest valid Timestamp. + // This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). + minValidSeconds = -62135596800 + // Seconds field just after the latest valid Timestamp. + // This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). + maxValidSeconds = 253402300800 +) + +// validateTimestamp determines whether a Timestamp is valid. +// A valid timestamp represents a time in the range +// [0001-01-01, 10000-01-01) and has a Nanos field +// in the range [0, 1e9). +// +// If the Timestamp is valid, validateTimestamp returns nil. +// Otherwise, it returns an error that describes +// the problem. +// +// Every valid Timestamp can be represented by a time.Time, but the converse is not true. +func validateTimestamp(ts *tspb.Timestamp) error { + if ts == nil { + return errors.New("timestamp: nil Timestamp") + } + if ts.Seconds < minValidSeconds { + return fmt.Errorf("timestamp: %v before 0001-01-01", ts) + } + if ts.Seconds >= maxValidSeconds { + return fmt.Errorf("timestamp: %v after 10000-01-01", ts) + } + if ts.Nanos < 0 || ts.Nanos >= 1e9 { + return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts) + } + return nil +} + +// Timestamp converts a google.protobuf.Timestamp proto to a time.Time. +// It returns an error if the argument is invalid. +// +// Unlike most Go functions, if Timestamp returns an error, the first return value +// is not the zero time.Time. Instead, it is the value obtained from the +// time.Unix function when passed the contents of the Timestamp, in the UTC +// locale. This may or may not be a meaningful time; many invalid Timestamps +// do map to valid time.Times. +// +// A nil Timestamp returns an error. The first return value in that case is +// undefined. +func Timestamp(ts *tspb.Timestamp) (time.Time, error) { + // Don't return the zero value on error, because corresponds to a valid + // timestamp. Instead return whatever time.Unix gives us. + var t time.Time + if ts == nil { + t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp + } else { + t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC() + } + return t, validateTimestamp(ts) +} + +// TimestampNow returns a google.protobuf.Timestamp for the current time. +func TimestampNow() *tspb.Timestamp { + ts, err := TimestampProto(time.Now()) + if err != nil { + panic("ptypes: time.Now() out of Timestamp range") + } + return ts +} + +// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. +// It returns an error if the resulting Timestamp is invalid. +func TimestampProto(t time.Time) (*tspb.Timestamp, error) { + seconds := t.Unix() + nanos := int32(t.Sub(time.Unix(seconds, 0))) + ts := &tspb.Timestamp{ + Seconds: seconds, + Nanos: nanos, + } + if err := validateTimestamp(ts); err != nil { + return nil, err + } + return ts, nil +} + +// TimestampString returns the RFC 3339 string for valid Timestamps. For invalid +// Timestamps, it returns an error message in parentheses. +func TimestampString(ts *tspb.Timestamp) string { + t, err := Timestamp(ts) + if err != nil { + return fmt.Sprintf("(%v)", err) + } + return t.Format(time.RFC3339Nano) +} diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go new file mode 100644 index 0000000..fe4fc28 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go @@ -0,0 +1,177 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/timestamp.proto + +package timestamp + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// A Timestamp represents a point in time independent of any time zone +// or calendar, represented as seconds and fractions of seconds at +// nanosecond resolution in UTC Epoch time. It is encoded using the +// Proleptic Gregorian Calendar which extends the Gregorian calendar +// backwards to year one. It is encoded assuming all minutes are 60 +// seconds long, i.e. leap seconds are "smeared" so that no leap second +// table is needed for interpretation. Range is from +// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. +// By restricting to that range, we ensure that we can convert to +// and from RFC 3339 date strings. +// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required, though only UTC (as indicated by "Z") is presently supported. +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--) +// to obtain a formatter capable of generating timestamps in this format. +// +// +type Timestamp struct { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Timestamp) Reset() { *m = Timestamp{} } +func (m *Timestamp) String() string { return proto.CompactTextString(m) } +func (*Timestamp) ProtoMessage() {} +func (*Timestamp) Descriptor() ([]byte, []int) { + return fileDescriptor_292007bbfe81227e, []int{0} +} + +func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" } + +func (m *Timestamp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Timestamp.Unmarshal(m, b) +} +func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic) +} +func (m *Timestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_Timestamp.Merge(m, src) +} +func (m *Timestamp) XXX_Size() int { + return xxx_messageInfo_Timestamp.Size(m) +} +func (m *Timestamp) XXX_DiscardUnknown() { + xxx_messageInfo_Timestamp.DiscardUnknown(m) +} + +var xxx_messageInfo_Timestamp proto.InternalMessageInfo + +func (m *Timestamp) GetSeconds() int64 { + if m != nil { + return m.Seconds + } + return 0 +} + +func (m *Timestamp) GetNanos() int32 { + if m != nil { + return m.Nanos + } + return 0 +} + +func init() { + proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp") +} + +func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) } + +var fileDescriptor_292007bbfe81227e = []byte{ + // 191 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, + 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28, + 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5, + 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89, + 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x1d, 0x97, 0x70, + 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0x99, 0x4e, 0x7c, 0x70, 0x13, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51, + 0xda, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0x39, 0x89, + 0x79, 0xe9, 0x08, 0x27, 0x16, 0x94, 0x54, 0x16, 0xa4, 0x16, 0x23, 0x5c, 0xfa, 0x83, 0x91, 0x71, + 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xc9, 0x01, 0x50, 0xb5, 0x7a, + 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x3d, 0x49, 0x6c, 0x60, 0x43, + 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x77, 0x4a, 0x07, 0xf7, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto new file mode 100644 index 0000000..06750ab --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto @@ -0,0 +1,133 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/timestamp"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Timestamp represents a point in time independent of any time zone +// or calendar, represented as seconds and fractions of seconds at +// nanosecond resolution in UTC Epoch time. It is encoded using the +// Proleptic Gregorian Calendar which extends the Gregorian calendar +// backwards to year one. It is encoded assuming all minutes are 60 +// seconds long, i.e. leap seconds are "smeared" so that no leap second +// table is needed for interpretation. Range is from +// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. +// By restricting to that range, we ensure that we can convert to +// and from RFC 3339 date strings. +// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required, though only UTC (as indicated by "Z") is presently supported. +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--) +// to obtain a formatter capable of generating timestamps in this format. +// +// +message Timestamp { + + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/vendor/github.com/hashicorp/hcl/.gitignore b/vendor/github.com/hashicorp/hcl/.gitignore new file mode 100644 index 0000000..15586a2 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/.gitignore @@ -0,0 +1,9 @@ +y.output + +# ignore intellij files +.idea +*.iml +*.ipr +*.iws + +*.test diff --git a/vendor/github.com/hashicorp/hcl/.travis.yml b/vendor/github.com/hashicorp/hcl/.travis.yml new file mode 100644 index 0000000..cb63a32 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/.travis.yml @@ -0,0 +1,13 @@ +sudo: false + +language: go + +go: + - 1.x + - tip + +branches: + only: + - master + +script: make test diff --git a/vendor/github.com/hashicorp/hcl/LICENSE b/vendor/github.com/hashicorp/hcl/LICENSE new file mode 100644 index 0000000..c33dcc7 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/hcl/Makefile b/vendor/github.com/hashicorp/hcl/Makefile new file mode 100644 index 0000000..84fd743 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/Makefile @@ -0,0 +1,18 @@ +TEST?=./... + +default: test + +fmt: generate + go fmt ./... + +test: generate + go get -t ./... + go test $(TEST) $(TESTARGS) + +generate: + go generate ./... + +updatedeps: + go get -u golang.org/x/tools/cmd/stringer + +.PHONY: default generate test updatedeps diff --git a/vendor/github.com/hashicorp/hcl/README.md b/vendor/github.com/hashicorp/hcl/README.md new file mode 100644 index 0000000..c822332 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/README.md @@ -0,0 +1,125 @@ +# HCL + +[![GoDoc](https://godoc.org/github.com/hashicorp/hcl?status.png)](https://godoc.org/github.com/hashicorp/hcl) [![Build Status](https://travis-ci.org/hashicorp/hcl.svg?branch=master)](https://travis-ci.org/hashicorp/hcl) + +HCL (HashiCorp Configuration Language) is a configuration language built +by HashiCorp. The goal of HCL is to build a structured configuration language +that is both human and machine friendly for use with command-line tools, but +specifically targeted towards DevOps tools, servers, etc. + +HCL is also fully JSON compatible. That is, JSON can be used as completely +valid input to a system expecting HCL. This helps makes systems +interoperable with other systems. + +HCL is heavily inspired by +[libucl](https://github.com/vstakhov/libucl), +nginx configuration, and others similar. + +## Why? + +A common question when viewing HCL is to ask the question: why not +JSON, YAML, etc.? + +Prior to HCL, the tools we built at [HashiCorp](http://www.hashicorp.com) +used a variety of configuration languages from full programming languages +such as Ruby to complete data structure languages such as JSON. What we +learned is that some people wanted human-friendly configuration languages +and some people wanted machine-friendly languages. + +JSON fits a nice balance in this, but is fairly verbose and most +importantly doesn't support comments. With YAML, we found that beginners +had a really hard time determining what the actual structure was, and +ended up guessing more often than not whether to use a hyphen, colon, etc. +in order to represent some configuration key. + +Full programming languages such as Ruby enable complex behavior +a configuration language shouldn't usually allow, and also forces +people to learn some set of Ruby. + +Because of this, we decided to create our own configuration language +that is JSON-compatible. Our configuration language (HCL) is designed +to be written and modified by humans. The API for HCL allows JSON +as an input so that it is also machine-friendly (machines can generate +JSON instead of trying to generate HCL). + +Our goal with HCL is not to alienate other configuration languages. +It is instead to provide HCL as a specialized language for our tools, +and JSON as the interoperability layer. + +## Syntax + +For a complete grammar, please see the parser itself. A high-level overview +of the syntax and grammar is listed here. + + * Single line comments start with `#` or `//` + + * Multi-line comments are wrapped in `/*` and `*/`. Nested block comments + are not allowed. A multi-line comment (also known as a block comment) + terminates at the first `*/` found. + + * Values are assigned with the syntax `key = value` (whitespace doesn't + matter). The value can be any primitive: a string, number, boolean, + object, or list. + + * Strings are double-quoted and can contain any UTF-8 characters. + Example: `"Hello, World"` + + * Multi-line strings start with `<- + echo %Path% + + go version + + go env + + go get -t ./... + +build_script: +- cmd: go test -v ./... diff --git a/vendor/github.com/hashicorp/hcl/decoder.go b/vendor/github.com/hashicorp/hcl/decoder.go new file mode 100644 index 0000000..bed9ebb --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/decoder.go @@ -0,0 +1,729 @@ +package hcl + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/parser" + "github.com/hashicorp/hcl/hcl/token" +) + +// This is the tag to use with structures to have settings for HCL +const tagName = "hcl" + +var ( + // nodeType holds a reference to the type of ast.Node + nodeType reflect.Type = findNodeType() +) + +// Unmarshal accepts a byte slice as input and writes the +// data to the value pointed to by v. +func Unmarshal(bs []byte, v interface{}) error { + root, err := parse(bs) + if err != nil { + return err + } + + return DecodeObject(v, root) +} + +// Decode reads the given input and decodes it into the structure +// given by `out`. +func Decode(out interface{}, in string) error { + obj, err := Parse(in) + if err != nil { + return err + } + + return DecodeObject(out, obj) +} + +// DecodeObject is a lower-level version of Decode. It decodes a +// raw Object into the given output. +func DecodeObject(out interface{}, n ast.Node) error { + val := reflect.ValueOf(out) + if val.Kind() != reflect.Ptr { + return errors.New("result must be a pointer") + } + + // If we have the file, we really decode the root node + if f, ok := n.(*ast.File); ok { + n = f.Node + } + + var d decoder + return d.decode("root", n, val.Elem()) +} + +type decoder struct { + stack []reflect.Kind +} + +func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error { + k := result + + // If we have an interface with a valid value, we use that + // for the check. + if result.Kind() == reflect.Interface { + elem := result.Elem() + if elem.IsValid() { + k = elem + } + } + + // Push current onto stack unless it is an interface. + if k.Kind() != reflect.Interface { + d.stack = append(d.stack, k.Kind()) + + // Schedule a pop + defer func() { + d.stack = d.stack[:len(d.stack)-1] + }() + } + + switch k.Kind() { + case reflect.Bool: + return d.decodeBool(name, node, result) + case reflect.Float32, reflect.Float64: + return d.decodeFloat(name, node, result) + case reflect.Int, reflect.Int32, reflect.Int64: + return d.decodeInt(name, node, result) + case reflect.Interface: + // When we see an interface, we make our own thing + return d.decodeInterface(name, node, result) + case reflect.Map: + return d.decodeMap(name, node, result) + case reflect.Ptr: + return d.decodePtr(name, node, result) + case reflect.Slice: + return d.decodeSlice(name, node, result) + case reflect.String: + return d.decodeString(name, node, result) + case reflect.Struct: + return d.decodeStruct(name, node, result) + default: + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unknown kind to decode into: %s", name, k.Kind()), + } + } +} + +func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) error { + switch n := node.(type) { + case *ast.LiteralType: + if n.Token.Type == token.BOOL { + v, err := strconv.ParseBool(n.Token.Text) + if err != nil { + return err + } + + result.Set(reflect.ValueOf(v)) + return nil + } + } + + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unknown type %T", name, node), + } +} + +func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error { + switch n := node.(type) { + case *ast.LiteralType: + if n.Token.Type == token.FLOAT || n.Token.Type == token.NUMBER { + v, err := strconv.ParseFloat(n.Token.Text, 64) + if err != nil { + return err + } + + result.Set(reflect.ValueOf(v).Convert(result.Type())) + return nil + } + } + + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unknown type %T", name, node), + } +} + +func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error { + switch n := node.(type) { + case *ast.LiteralType: + switch n.Token.Type { + case token.NUMBER: + v, err := strconv.ParseInt(n.Token.Text, 0, 0) + if err != nil { + return err + } + + if result.Kind() == reflect.Interface { + result.Set(reflect.ValueOf(int(v))) + } else { + result.SetInt(v) + } + return nil + case token.STRING: + v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0) + if err != nil { + return err + } + + if result.Kind() == reflect.Interface { + result.Set(reflect.ValueOf(int(v))) + } else { + result.SetInt(v) + } + return nil + } + } + + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unknown type %T", name, node), + } +} + +func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error { + // When we see an ast.Node, we retain the value to enable deferred decoding. + // Very useful in situations where we want to preserve ast.Node information + // like Pos + if result.Type() == nodeType && result.CanSet() { + result.Set(reflect.ValueOf(node)) + return nil + } + + var set reflect.Value + redecode := true + + // For testing types, ObjectType should just be treated as a list. We + // set this to a temporary var because we want to pass in the real node. + testNode := node + if ot, ok := node.(*ast.ObjectType); ok { + testNode = ot.List + } + + switch n := testNode.(type) { + case *ast.ObjectList: + // If we're at the root or we're directly within a slice, then we + // decode objects into map[string]interface{}, otherwise we decode + // them into lists. + if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { + var temp map[string]interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeMap( + reflect.MapOf( + reflect.TypeOf(""), + tempVal.Type().Elem())) + + set = result + } else { + var temp []map[string]interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeSlice( + reflect.SliceOf(tempVal.Type().Elem()), 0, len(n.Items)) + set = result + } + case *ast.ObjectType: + // If we're at the root or we're directly within a slice, then we + // decode objects into map[string]interface{}, otherwise we decode + // them into lists. + if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { + var temp map[string]interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeMap( + reflect.MapOf( + reflect.TypeOf(""), + tempVal.Type().Elem())) + + set = result + } else { + var temp []map[string]interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeSlice( + reflect.SliceOf(tempVal.Type().Elem()), 0, 1) + set = result + } + case *ast.ListType: + var temp []interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeSlice( + reflect.SliceOf(tempVal.Type().Elem()), 0, 0) + set = result + case *ast.LiteralType: + switch n.Token.Type { + case token.BOOL: + var result bool + set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) + case token.FLOAT: + var result float64 + set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) + case token.NUMBER: + var result int + set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) + case token.STRING, token.HEREDOC: + set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) + default: + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: cannot decode into interface: %T", name, node), + } + } + default: + return fmt.Errorf( + "%s: cannot decode into interface: %T", + name, node) + } + + // Set the result to what its supposed to be, then reset + // result so we don't reflect into this method anymore. + result.Set(set) + + if redecode { + // Revisit the node so that we can use the newly instantiated + // thing and populate it. + if err := d.decode(name, node, result); err != nil { + return err + } + } + + return nil +} + +func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) error { + if item, ok := node.(*ast.ObjectItem); ok { + node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} + } + + if ot, ok := node.(*ast.ObjectType); ok { + node = ot.List + } + + n, ok := node.(*ast.ObjectList) + if !ok { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: not an object type for map (%T)", name, node), + } + } + + // If we have an interface, then we can address the interface, + // but not the slice itself, so get the element but set the interface + set := result + if result.Kind() == reflect.Interface { + result = result.Elem() + } + + resultType := result.Type() + resultElemType := resultType.Elem() + resultKeyType := resultType.Key() + if resultKeyType.Kind() != reflect.String { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: map must have string keys", name), + } + } + + // Make a map if it is nil + resultMap := result + if result.IsNil() { + resultMap = reflect.MakeMap( + reflect.MapOf(resultKeyType, resultElemType)) + } + + // Go through each element and decode it. + done := make(map[string]struct{}) + for _, item := range n.Items { + if item.Val == nil { + continue + } + + // github.com/hashicorp/terraform/issue/5740 + if len(item.Keys) == 0 { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: map must have string keys", name), + } + } + + // Get the key we're dealing with, which is the first item + keyStr := item.Keys[0].Token.Value().(string) + + // If we've already processed this key, then ignore it + if _, ok := done[keyStr]; ok { + continue + } + + // Determine the value. If we have more than one key, then we + // get the objectlist of only these keys. + itemVal := item.Val + if len(item.Keys) > 1 { + itemVal = n.Filter(keyStr) + done[keyStr] = struct{}{} + } + + // Make the field name + fieldName := fmt.Sprintf("%s.%s", name, keyStr) + + // Get the key/value as reflection values + key := reflect.ValueOf(keyStr) + val := reflect.Indirect(reflect.New(resultElemType)) + + // If we have a pre-existing value in the map, use that + oldVal := resultMap.MapIndex(key) + if oldVal.IsValid() { + val.Set(oldVal) + } + + // Decode! + if err := d.decode(fieldName, itemVal, val); err != nil { + return err + } + + // Set the value on the map + resultMap.SetMapIndex(key, val) + } + + // Set the final map if we can + set.Set(resultMap) + return nil +} + +func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error { + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + resultType := result.Type() + resultElemType := resultType.Elem() + val := reflect.New(resultElemType) + if err := d.decode(name, node, reflect.Indirect(val)); err != nil { + return err + } + + result.Set(val) + return nil +} + +func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value) error { + // If we have an interface, then we can address the interface, + // but not the slice itself, so get the element but set the interface + set := result + if result.Kind() == reflect.Interface { + result = result.Elem() + } + // Create the slice if it isn't nil + resultType := result.Type() + resultElemType := resultType.Elem() + if result.IsNil() { + resultSliceType := reflect.SliceOf(resultElemType) + result = reflect.MakeSlice( + resultSliceType, 0, 0) + } + + // Figure out the items we'll be copying into the slice + var items []ast.Node + switch n := node.(type) { + case *ast.ObjectList: + items = make([]ast.Node, len(n.Items)) + for i, item := range n.Items { + items[i] = item + } + case *ast.ObjectType: + items = []ast.Node{n} + case *ast.ListType: + items = n.List + default: + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("unknown slice type: %T", node), + } + } + + for i, item := range items { + fieldName := fmt.Sprintf("%s[%d]", name, i) + + // Decode + val := reflect.Indirect(reflect.New(resultElemType)) + + // if item is an object that was decoded from ambiguous JSON and + // flattened, make sure it's expanded if it needs to decode into a + // defined structure. + item := expandObject(item, val) + + if err := d.decode(fieldName, item, val); err != nil { + return err + } + + // Append it onto the slice + result = reflect.Append(result, val) + } + + set.Set(result) + return nil +} + +// expandObject detects if an ambiguous JSON object was flattened to a List which +// should be decoded into a struct, and expands the ast to properly deocode. +func expandObject(node ast.Node, result reflect.Value) ast.Node { + item, ok := node.(*ast.ObjectItem) + if !ok { + return node + } + + elemType := result.Type() + + // our target type must be a struct + switch elemType.Kind() { + case reflect.Ptr: + switch elemType.Elem().Kind() { + case reflect.Struct: + //OK + default: + return node + } + case reflect.Struct: + //OK + default: + return node + } + + // A list value will have a key and field name. If it had more fields, + // it wouldn't have been flattened. + if len(item.Keys) != 2 { + return node + } + + keyToken := item.Keys[0].Token + item.Keys = item.Keys[1:] + + // we need to un-flatten the ast enough to decode + newNode := &ast.ObjectItem{ + Keys: []*ast.ObjectKey{ + &ast.ObjectKey{ + Token: keyToken, + }, + }, + Val: &ast.ObjectType{ + List: &ast.ObjectList{ + Items: []*ast.ObjectItem{item}, + }, + }, + } + + return newNode +} + +func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value) error { + switch n := node.(type) { + case *ast.LiteralType: + switch n.Token.Type { + case token.NUMBER: + result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type())) + return nil + case token.STRING, token.HEREDOC: + result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type())) + return nil + } + } + + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unknown type for string %T", name, node), + } +} + +func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error { + var item *ast.ObjectItem + if it, ok := node.(*ast.ObjectItem); ok { + item = it + node = it.Val + } + + if ot, ok := node.(*ast.ObjectType); ok { + node = ot.List + } + + // Handle the special case where the object itself is a literal. Previously + // the yacc parser would always ensure top-level elements were arrays. The new + // parser does not make the same guarantees, thus we need to convert any + // top-level literal elements into a list. + if _, ok := node.(*ast.LiteralType); ok && item != nil { + node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} + } + + list, ok := node.(*ast.ObjectList) + if !ok { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: not an object type for struct (%T)", name, node), + } + } + + // This slice will keep track of all the structs we'll be decoding. + // There can be more than one struct if there are embedded structs + // that are squashed. + structs := make([]reflect.Value, 1, 5) + structs[0] = result + + // Compile the list of all the fields that we're going to be decoding + // from all the structs. + type field struct { + field reflect.StructField + val reflect.Value + } + fields := []field{} + for len(structs) > 0 { + structVal := structs[0] + structs = structs[1:] + + structType := structVal.Type() + for i := 0; i < structType.NumField(); i++ { + fieldType := structType.Field(i) + tagParts := strings.Split(fieldType.Tag.Get(tagName), ",") + + // Ignore fields with tag name "-" + if tagParts[0] == "-" { + continue + } + + if fieldType.Anonymous { + fieldKind := fieldType.Type.Kind() + if fieldKind != reflect.Struct { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: unsupported type to struct: %s", + fieldType.Name, fieldKind), + } + } + + // We have an embedded field. We "squash" the fields down + // if specified in the tag. + squash := false + for _, tag := range tagParts[1:] { + if tag == "squash" { + squash = true + break + } + } + + if squash { + structs = append( + structs, result.FieldByName(fieldType.Name)) + continue + } + } + + // Normal struct field, store it away + fields = append(fields, field{fieldType, structVal.Field(i)}) + } + } + + usedKeys := make(map[string]struct{}) + decodedFields := make([]string, 0, len(fields)) + decodedFieldsVal := make([]reflect.Value, 0) + unusedKeysVal := make([]reflect.Value, 0) + for _, f := range fields { + field, fieldValue := f.field, f.val + if !fieldValue.IsValid() { + // This should never happen + panic("field is not valid") + } + + // If we can't set the field, then it is unexported or something, + // and we just continue onwards. + if !fieldValue.CanSet() { + continue + } + + fieldName := field.Name + + tagValue := field.Tag.Get(tagName) + tagParts := strings.SplitN(tagValue, ",", 2) + if len(tagParts) >= 2 { + switch tagParts[1] { + case "decodedFields": + decodedFieldsVal = append(decodedFieldsVal, fieldValue) + continue + case "key": + if item == nil { + return &parser.PosError{ + Pos: node.Pos(), + Err: fmt.Errorf("%s: %s asked for 'key', impossible", + name, fieldName), + } + } + + fieldValue.SetString(item.Keys[0].Token.Value().(string)) + continue + case "unusedKeys": + unusedKeysVal = append(unusedKeysVal, fieldValue) + continue + } + } + + if tagParts[0] != "" { + fieldName = tagParts[0] + } + + // Determine the element we'll use to decode. If it is a single + // match (only object with the field), then we decode it exactly. + // If it is a prefix match, then we decode the matches. + filter := list.Filter(fieldName) + + prefixMatches := filter.Children() + matches := filter.Elem() + if len(matches.Items) == 0 && len(prefixMatches.Items) == 0 { + continue + } + + // Track the used key + usedKeys[fieldName] = struct{}{} + + // Create the field name and decode. We range over the elements + // because we actually want the value. + fieldName = fmt.Sprintf("%s.%s", name, fieldName) + if len(prefixMatches.Items) > 0 { + if err := d.decode(fieldName, prefixMatches, fieldValue); err != nil { + return err + } + } + for _, match := range matches.Items { + var decodeNode ast.Node = match.Val + if ot, ok := decodeNode.(*ast.ObjectType); ok { + decodeNode = &ast.ObjectList{Items: ot.List.Items} + } + + if err := d.decode(fieldName, decodeNode, fieldValue); err != nil { + return err + } + } + + decodedFields = append(decodedFields, field.Name) + } + + if len(decodedFieldsVal) > 0 { + // Sort it so that it is deterministic + sort.Strings(decodedFields) + + for _, v := range decodedFieldsVal { + v.Set(reflect.ValueOf(decodedFields)) + } + } + + return nil +} + +// findNodeType returns the type of ast.Node +func findNodeType() reflect.Type { + var nodeContainer struct { + Node ast.Node + } + value := reflect.ValueOf(nodeContainer).FieldByName("Node") + return value.Type() +} diff --git a/vendor/github.com/hashicorp/hcl/go.mod b/vendor/github.com/hashicorp/hcl/go.mod new file mode 100644 index 0000000..4debbbe --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/go.mod @@ -0,0 +1,3 @@ +module github.com/hashicorp/hcl + +require github.com/davecgh/go-spew v1.1.1 diff --git a/vendor/github.com/hashicorp/hcl/go.sum b/vendor/github.com/hashicorp/hcl/go.sum new file mode 100644 index 0000000..b5e2922 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/go.sum @@ -0,0 +1,2 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/vendor/github.com/hashicorp/hcl/hcl.go b/vendor/github.com/hashicorp/hcl/hcl.go new file mode 100644 index 0000000..575a20b --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl.go @@ -0,0 +1,11 @@ +// Package hcl decodes HCL into usable Go structures. +// +// hcl input can come in either pure HCL format or JSON format. +// It can be parsed into an AST, and then decoded into a structure, +// or it can be decoded directly from a string into a structure. +// +// If you choose to parse HCL into a raw AST, the benefit is that you +// can write custom visitor implementations to implement custom +// semantic checks. By default, HCL does not perform any semantic +// checks. +package hcl diff --git a/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go b/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go new file mode 100644 index 0000000..6e5ef65 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go @@ -0,0 +1,219 @@ +// Package ast declares the types used to represent syntax trees for HCL +// (HashiCorp Configuration Language) +package ast + +import ( + "fmt" + "strings" + + "github.com/hashicorp/hcl/hcl/token" +) + +// Node is an element in the abstract syntax tree. +type Node interface { + node() + Pos() token.Pos +} + +func (File) node() {} +func (ObjectList) node() {} +func (ObjectKey) node() {} +func (ObjectItem) node() {} +func (Comment) node() {} +func (CommentGroup) node() {} +func (ObjectType) node() {} +func (LiteralType) node() {} +func (ListType) node() {} + +// File represents a single HCL file +type File struct { + Node Node // usually a *ObjectList + Comments []*CommentGroup // list of all comments in the source +} + +func (f *File) Pos() token.Pos { + return f.Node.Pos() +} + +// ObjectList represents a list of ObjectItems. An HCL file itself is an +// ObjectList. +type ObjectList struct { + Items []*ObjectItem +} + +func (o *ObjectList) Add(item *ObjectItem) { + o.Items = append(o.Items, item) +} + +// Filter filters out the objects with the given key list as a prefix. +// +// The returned list of objects contain ObjectItems where the keys have +// this prefix already stripped off. This might result in objects with +// zero-length key lists if they have no children. +// +// If no matches are found, an empty ObjectList (non-nil) is returned. +func (o *ObjectList) Filter(keys ...string) *ObjectList { + var result ObjectList + for _, item := range o.Items { + // If there aren't enough keys, then ignore this + if len(item.Keys) < len(keys) { + continue + } + + match := true + for i, key := range item.Keys[:len(keys)] { + key := key.Token.Value().(string) + if key != keys[i] && !strings.EqualFold(key, keys[i]) { + match = false + break + } + } + if !match { + continue + } + + // Strip off the prefix from the children + newItem := *item + newItem.Keys = newItem.Keys[len(keys):] + result.Add(&newItem) + } + + return &result +} + +// Children returns further nested objects (key length > 0) within this +// ObjectList. This should be used with Filter to get at child items. +func (o *ObjectList) Children() *ObjectList { + var result ObjectList + for _, item := range o.Items { + if len(item.Keys) > 0 { + result.Add(item) + } + } + + return &result +} + +// Elem returns items in the list that are direct element assignments +// (key length == 0). This should be used with Filter to get at elements. +func (o *ObjectList) Elem() *ObjectList { + var result ObjectList + for _, item := range o.Items { + if len(item.Keys) == 0 { + result.Add(item) + } + } + + return &result +} + +func (o *ObjectList) Pos() token.Pos { + // always returns the uninitiliazed position + return o.Items[0].Pos() +} + +// ObjectItem represents a HCL Object Item. An item is represented with a key +// (or keys). It can be an assignment or an object (both normal and nested) +type ObjectItem struct { + // keys is only one length long if it's of type assignment. If it's a + // nested object it can be larger than one. In that case "assign" is + // invalid as there is no assignments for a nested object. + Keys []*ObjectKey + + // assign contains the position of "=", if any + Assign token.Pos + + // val is the item itself. It can be an object,list, number, bool or a + // string. If key length is larger than one, val can be only of type + // Object. + Val Node + + LeadComment *CommentGroup // associated lead comment + LineComment *CommentGroup // associated line comment +} + +func (o *ObjectItem) Pos() token.Pos { + // I'm not entirely sure what causes this, but removing this causes + // a test failure. We should investigate at some point. + if len(o.Keys) == 0 { + return token.Pos{} + } + + return o.Keys[0].Pos() +} + +// ObjectKeys are either an identifier or of type string. +type ObjectKey struct { + Token token.Token +} + +func (o *ObjectKey) Pos() token.Pos { + return o.Token.Pos +} + +// LiteralType represents a literal of basic type. Valid types are: +// token.NUMBER, token.FLOAT, token.BOOL and token.STRING +type LiteralType struct { + Token token.Token + + // comment types, only used when in a list + LeadComment *CommentGroup + LineComment *CommentGroup +} + +func (l *LiteralType) Pos() token.Pos { + return l.Token.Pos +} + +// ListStatement represents a HCL List type +type ListType struct { + Lbrack token.Pos // position of "[" + Rbrack token.Pos // position of "]" + List []Node // the elements in lexical order +} + +func (l *ListType) Pos() token.Pos { + return l.Lbrack +} + +func (l *ListType) Add(node Node) { + l.List = append(l.List, node) +} + +// ObjectType represents a HCL Object Type +type ObjectType struct { + Lbrace token.Pos // position of "{" + Rbrace token.Pos // position of "}" + List *ObjectList // the nodes in lexical order +} + +func (o *ObjectType) Pos() token.Pos { + return o.Lbrace +} + +// Comment node represents a single //, # style or /*- style commment +type Comment struct { + Start token.Pos // position of / or # + Text string +} + +func (c *Comment) Pos() token.Pos { + return c.Start +} + +// CommentGroup node represents a sequence of comments with no other tokens and +// no empty lines between. +type CommentGroup struct { + List []*Comment // len(List) > 0 +} + +func (c *CommentGroup) Pos() token.Pos { + return c.List[0].Pos() +} + +//------------------------------------------------------------------- +// GoStringer +//------------------------------------------------------------------- + +func (o *ObjectKey) GoString() string { return fmt.Sprintf("*%#v", *o) } +func (o *ObjectList) GoString() string { return fmt.Sprintf("*%#v", *o) } diff --git a/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go b/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go new file mode 100644 index 0000000..ba07ad4 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go @@ -0,0 +1,52 @@ +package ast + +import "fmt" + +// WalkFunc describes a function to be called for each node during a Walk. The +// returned node can be used to rewrite the AST. Walking stops the returned +// bool is false. +type WalkFunc func(Node) (Node, bool) + +// Walk traverses an AST in depth-first order: It starts by calling fn(node); +// node must not be nil. If fn returns true, Walk invokes fn recursively for +// each of the non-nil children of node, followed by a call of fn(nil). The +// returned node of fn can be used to rewrite the passed node to fn. +func Walk(node Node, fn WalkFunc) Node { + rewritten, ok := fn(node) + if !ok { + return rewritten + } + + switch n := node.(type) { + case *File: + n.Node = Walk(n.Node, fn) + case *ObjectList: + for i, item := range n.Items { + n.Items[i] = Walk(item, fn).(*ObjectItem) + } + case *ObjectKey: + // nothing to do + case *ObjectItem: + for i, k := range n.Keys { + n.Keys[i] = Walk(k, fn).(*ObjectKey) + } + + if n.Val != nil { + n.Val = Walk(n.Val, fn) + } + case *LiteralType: + // nothing to do + case *ListType: + for i, l := range n.List { + n.List[i] = Walk(l, fn) + } + case *ObjectType: + n.List = Walk(n.List, fn).(*ObjectList) + default: + // should we panic here? + fmt.Printf("unknown type: %T\n", n) + } + + fn(nil) + return rewritten +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/error.go b/vendor/github.com/hashicorp/hcl/hcl/parser/error.go new file mode 100644 index 0000000..5c99381 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/error.go @@ -0,0 +1,17 @@ +package parser + +import ( + "fmt" + + "github.com/hashicorp/hcl/hcl/token" +) + +// PosError is a parse error that contains a position. +type PosError struct { + Pos token.Pos + Err error +} + +func (e *PosError) Error() string { + return fmt.Sprintf("At %s: %s", e.Pos, e.Err) +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go new file mode 100644 index 0000000..64c83bc --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go @@ -0,0 +1,532 @@ +// Package parser implements a parser for HCL (HashiCorp Configuration +// Language) +package parser + +import ( + "bytes" + "errors" + "fmt" + "strings" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/scanner" + "github.com/hashicorp/hcl/hcl/token" +) + +type Parser struct { + sc *scanner.Scanner + + // Last read token + tok token.Token + commaPrev token.Token + + comments []*ast.CommentGroup + leadComment *ast.CommentGroup // last lead comment + lineComment *ast.CommentGroup // last line comment + + enableTrace bool + indent int + n int // buffer size (max = 1) +} + +func newParser(src []byte) *Parser { + return &Parser{ + sc: scanner.New(src), + } +} + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func Parse(src []byte) (*ast.File, error) { + // normalize all line endings + // since the scanner and output only work with "\n" line endings, we may + // end up with dangling "\r" characters in the parsed data. + src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1) + + p := newParser(src) + return p.Parse() +} + +var errEofToken = errors.New("EOF token found") + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func (p *Parser) Parse() (*ast.File, error) { + f := &ast.File{} + var err, scerr error + p.sc.Error = func(pos token.Pos, msg string) { + scerr = &PosError{Pos: pos, Err: errors.New(msg)} + } + + f.Node, err = p.objectList(false) + if scerr != nil { + return nil, scerr + } + if err != nil { + return nil, err + } + + f.Comments = p.comments + return f, nil +} + +// objectList parses a list of items within an object (generally k/v pairs). +// The parameter" obj" tells this whether to we are within an object (braces: +// '{', '}') or just at the top level. If we're within an object, we end +// at an RBRACE. +func (p *Parser) objectList(obj bool) (*ast.ObjectList, error) { + defer un(trace(p, "ParseObjectList")) + node := &ast.ObjectList{} + + for { + if obj { + tok := p.scan() + p.unscan() + if tok.Type == token.RBRACE { + break + } + } + + n, err := p.objectItem() + if err == errEofToken { + break // we are finished + } + + // we don't return a nil node, because might want to use already + // collected items. + if err != nil { + return node, err + } + + node.Add(n) + + // object lists can be optionally comma-delimited e.g. when a list of maps + // is being expressed, so a comma is allowed here - it's simply consumed + tok := p.scan() + if tok.Type != token.COMMA { + p.unscan() + } + } + return node, nil +} + +func (p *Parser) consumeComment() (comment *ast.Comment, endline int) { + endline = p.tok.Pos.Line + + // count the endline if it's multiline comment, ie starting with /* + if len(p.tok.Text) > 1 && p.tok.Text[1] == '*' { + // don't use range here - no need to decode Unicode code points + for i := 0; i < len(p.tok.Text); i++ { + if p.tok.Text[i] == '\n' { + endline++ + } + } + } + + comment = &ast.Comment{Start: p.tok.Pos, Text: p.tok.Text} + p.tok = p.sc.Scan() + return +} + +func (p *Parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) { + var list []*ast.Comment + endline = p.tok.Pos.Line + + for p.tok.Type == token.COMMENT && p.tok.Pos.Line <= endline+n { + var comment *ast.Comment + comment, endline = p.consumeComment() + list = append(list, comment) + } + + // add comment group to the comments list + comments = &ast.CommentGroup{List: list} + p.comments = append(p.comments, comments) + + return +} + +// objectItem parses a single object item +func (p *Parser) objectItem() (*ast.ObjectItem, error) { + defer un(trace(p, "ParseObjectItem")) + + keys, err := p.objectKey() + if len(keys) > 0 && err == errEofToken { + // We ignore eof token here since it is an error if we didn't + // receive a value (but we did receive a key) for the item. + err = nil + } + if len(keys) > 0 && err != nil && p.tok.Type == token.RBRACE { + // This is a strange boolean statement, but what it means is: + // We have keys with no value, and we're likely in an object + // (since RBrace ends an object). For this, we set err to nil so + // we continue and get the error below of having the wrong value + // type. + err = nil + + // Reset the token type so we don't think it completed fine. See + // objectType which uses p.tok.Type to check if we're done with + // the object. + p.tok.Type = token.EOF + } + if err != nil { + return nil, err + } + + o := &ast.ObjectItem{ + Keys: keys, + } + + if p.leadComment != nil { + o.LeadComment = p.leadComment + p.leadComment = nil + } + + switch p.tok.Type { + case token.ASSIGN: + o.Assign = p.tok.Pos + o.Val, err = p.object() + if err != nil { + return nil, err + } + case token.LBRACE: + o.Val, err = p.objectType() + if err != nil { + return nil, err + } + default: + keyStr := make([]string, 0, len(keys)) + for _, k := range keys { + keyStr = append(keyStr, k.Token.Text) + } + + return nil, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf( + "key '%s' expected start of object ('{') or assignment ('=')", + strings.Join(keyStr, " ")), + } + } + + // key=#comment + // val + if p.lineComment != nil { + o.LineComment, p.lineComment = p.lineComment, nil + } + + // do a look-ahead for line comment + p.scan() + if len(keys) > 0 && o.Val.Pos().Line == keys[0].Pos().Line && p.lineComment != nil { + o.LineComment = p.lineComment + p.lineComment = nil + } + p.unscan() + return o, nil +} + +// objectKey parses an object key and returns a ObjectKey AST +func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { + keyCount := 0 + keys := make([]*ast.ObjectKey, 0) + + for { + tok := p.scan() + switch tok.Type { + case token.EOF: + // It is very important to also return the keys here as well as + // the error. This is because we need to be able to tell if we + // did parse keys prior to finding the EOF, or if we just found + // a bare EOF. + return keys, errEofToken + case token.ASSIGN: + // assignment or object only, but not nested objects. this is not + // allowed: `foo bar = {}` + if keyCount > 1 { + return nil, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("nested object expected: LBRACE got: %s", p.tok.Type), + } + } + + if keyCount == 0 { + return nil, &PosError{ + Pos: p.tok.Pos, + Err: errors.New("no object keys found!"), + } + } + + return keys, nil + case token.LBRACE: + var err error + + // If we have no keys, then it is a syntax error. i.e. {{}} is not + // allowed. + if len(keys) == 0 { + err = &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("expected: IDENT | STRING got: %s", p.tok.Type), + } + } + + // object + return keys, err + case token.IDENT, token.STRING: + keyCount++ + keys = append(keys, &ast.ObjectKey{Token: p.tok}) + case token.ILLEGAL: + return keys, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("illegal character"), + } + default: + return keys, &PosError{ + Pos: p.tok.Pos, + Err: fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", p.tok.Type), + } + } + } +} + +// object parses any type of object, such as number, bool, string, object or +// list. +func (p *Parser) object() (ast.Node, error) { + defer un(trace(p, "ParseType")) + tok := p.scan() + + switch tok.Type { + case token.NUMBER, token.FLOAT, token.BOOL, token.STRING, token.HEREDOC: + return p.literalType() + case token.LBRACE: + return p.objectType() + case token.LBRACK: + return p.listType() + case token.COMMENT: + // implement comment + case token.EOF: + return nil, errEofToken + } + + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf("Unknown token: %+v", tok), + } +} + +// objectType parses an object type and returns a ObjectType AST +func (p *Parser) objectType() (*ast.ObjectType, error) { + defer un(trace(p, "ParseObjectType")) + + // we assume that the currently scanned token is a LBRACE + o := &ast.ObjectType{ + Lbrace: p.tok.Pos, + } + + l, err := p.objectList(true) + + // if we hit RBRACE, we are good to go (means we parsed all Items), if it's + // not a RBRACE, it's an syntax error and we just return it. + if err != nil && p.tok.Type != token.RBRACE { + return nil, err + } + + // No error, scan and expect the ending to be a brace + if tok := p.scan(); tok.Type != token.RBRACE { + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf("object expected closing RBRACE got: %s", tok.Type), + } + } + + o.List = l + o.Rbrace = p.tok.Pos // advanced via parseObjectList + return o, nil +} + +// listType parses a list type and returns a ListType AST +func (p *Parser) listType() (*ast.ListType, error) { + defer un(trace(p, "ParseListType")) + + // we assume that the currently scanned token is a LBRACK + l := &ast.ListType{ + Lbrack: p.tok.Pos, + } + + needComma := false + for { + tok := p.scan() + if needComma { + switch tok.Type { + case token.COMMA, token.RBRACK: + default: + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error parsing list, expected comma or list end, got: %s", + tok.Type), + } + } + } + switch tok.Type { + case token.BOOL, token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC: + node, err := p.literalType() + if err != nil { + return nil, err + } + + // If there is a lead comment, apply it + if p.leadComment != nil { + node.LeadComment = p.leadComment + p.leadComment = nil + } + + l.Add(node) + needComma = true + case token.COMMA: + // get next list item or we are at the end + // do a look-ahead for line comment + p.scan() + if p.lineComment != nil && len(l.List) > 0 { + lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) + if ok { + lit.LineComment = p.lineComment + l.List[len(l.List)-1] = lit + p.lineComment = nil + } + } + p.unscan() + + needComma = false + continue + case token.LBRACE: + // Looks like a nested object, so parse it out + node, err := p.objectType() + if err != nil { + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error while trying to parse object within list: %s", err), + } + } + l.Add(node) + needComma = true + case token.LBRACK: + node, err := p.listType() + if err != nil { + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf( + "error while trying to parse list within list: %s", err), + } + } + l.Add(node) + case token.RBRACK: + // finished + l.Rbrack = p.tok.Pos + return l, nil + default: + return nil, &PosError{ + Pos: tok.Pos, + Err: fmt.Errorf("unexpected token while parsing list: %s", tok.Type), + } + } + } +} + +// literalType parses a literal type and returns a LiteralType AST +func (p *Parser) literalType() (*ast.LiteralType, error) { + defer un(trace(p, "ParseLiteral")) + + return &ast.LiteralType{ + Token: p.tok, + }, nil +} + +// scan returns the next token from the underlying scanner. If a token has +// been unscanned then read that instead. In the process, it collects any +// comment groups encountered, and remembers the last lead and line comments. +func (p *Parser) scan() token.Token { + // If we have a token on the buffer, then return it. + if p.n != 0 { + p.n = 0 + return p.tok + } + + // Otherwise read the next token from the scanner and Save it to the buffer + // in case we unscan later. + prev := p.tok + p.tok = p.sc.Scan() + + if p.tok.Type == token.COMMENT { + var comment *ast.CommentGroup + var endline int + + // fmt.Printf("p.tok.Pos.Line = %+v prev: %d endline %d \n", + // p.tok.Pos.Line, prev.Pos.Line, endline) + if p.tok.Pos.Line == prev.Pos.Line { + // The comment is on same line as the previous token; it + // cannot be a lead comment but may be a line comment. + comment, endline = p.consumeCommentGroup(0) + if p.tok.Pos.Line != endline { + // The next token is on a different line, thus + // the last comment group is a line comment. + p.lineComment = comment + } + } + + // consume successor comments, if any + endline = -1 + for p.tok.Type == token.COMMENT { + comment, endline = p.consumeCommentGroup(1) + } + + if endline+1 == p.tok.Pos.Line && p.tok.Type != token.RBRACE { + switch p.tok.Type { + case token.RBRACE, token.RBRACK: + // Do not count for these cases + default: + // The next token is following on the line immediately after the + // comment group, thus the last comment group is a lead comment. + p.leadComment = comment + } + } + + } + + return p.tok +} + +// unscan pushes the previously read token back onto the buffer. +func (p *Parser) unscan() { + p.n = 1 +} + +// ---------------------------------------------------------------------------- +// Parsing support + +func (p *Parser) printTrace(a ...interface{}) { + if !p.enableTrace { + return + } + + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) + fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) + + i := 2 * p.indent + for i > n { + fmt.Print(dots) + i -= n + } + // i <= n + fmt.Print(dots[0:i]) + fmt.Println(a...) +} + +func trace(p *Parser, msg string) *Parser { + p.printTrace(msg, "(") + p.indent++ + return p +} + +// Usage pattern: defer un(trace(p, "...")) +func un(p *Parser) { + p.indent-- + p.printTrace(")") +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go b/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go new file mode 100644 index 0000000..7c038d1 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go @@ -0,0 +1,789 @@ +package printer + +import ( + "bytes" + "fmt" + "sort" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/token" +) + +const ( + blank = byte(' ') + newline = byte('\n') + tab = byte('\t') + infinity = 1 << 30 // offset or line +) + +var ( + unindent = []byte("\uE123") // in the private use space +) + +type printer struct { + cfg Config + prev token.Pos + + comments []*ast.CommentGroup // may be nil, contains all comments + standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node) + + enableTrace bool + indentTrace int +} + +type ByPosition []*ast.CommentGroup + +func (b ByPosition) Len() int { return len(b) } +func (b ByPosition) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b ByPosition) Less(i, j int) bool { return b[i].Pos().Before(b[j].Pos()) } + +// collectComments comments all standalone comments which are not lead or line +// comment +func (p *printer) collectComments(node ast.Node) { + // first collect all comments. This is already stored in + // ast.File.(comments) + ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { + switch t := nn.(type) { + case *ast.File: + p.comments = t.Comments + return nn, false + } + return nn, true + }) + + standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0) + for _, c := range p.comments { + standaloneComments[c.Pos()] = c + } + + // next remove all lead and line comments from the overall comment map. + // This will give us comments which are standalone, comments which are not + // assigned to any kind of node. + ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { + switch t := nn.(type) { + case *ast.LiteralType: + if t.LeadComment != nil { + for _, comment := range t.LeadComment.List { + if _, ok := standaloneComments[comment.Pos()]; ok { + delete(standaloneComments, comment.Pos()) + } + } + } + + if t.LineComment != nil { + for _, comment := range t.LineComment.List { + if _, ok := standaloneComments[comment.Pos()]; ok { + delete(standaloneComments, comment.Pos()) + } + } + } + case *ast.ObjectItem: + if t.LeadComment != nil { + for _, comment := range t.LeadComment.List { + if _, ok := standaloneComments[comment.Pos()]; ok { + delete(standaloneComments, comment.Pos()) + } + } + } + + if t.LineComment != nil { + for _, comment := range t.LineComment.List { + if _, ok := standaloneComments[comment.Pos()]; ok { + delete(standaloneComments, comment.Pos()) + } + } + } + } + + return nn, true + }) + + for _, c := range standaloneComments { + p.standaloneComments = append(p.standaloneComments, c) + } + + sort.Sort(ByPosition(p.standaloneComments)) +} + +// output prints creates b printable HCL output and returns it. +func (p *printer) output(n interface{}) []byte { + var buf bytes.Buffer + + switch t := n.(type) { + case *ast.File: + // File doesn't trace so we add the tracing here + defer un(trace(p, "File")) + return p.output(t.Node) + case *ast.ObjectList: + defer un(trace(p, "ObjectList")) + + var index int + for { + // Determine the location of the next actual non-comment + // item. If we're at the end, the next item is at "infinity" + var nextItem token.Pos + if index != len(t.Items) { + nextItem = t.Items[index].Pos() + } else { + nextItem = token.Pos{Offset: infinity, Line: infinity} + } + + // Go through the standalone comments in the file and print out + // the comments that we should be for this object item. + for _, c := range p.standaloneComments { + // Go through all the comments in the group. The group + // should be printed together, not separated by double newlines. + printed := false + newlinePrinted := false + for _, comment := range c.List { + // We only care about comments after the previous item + // we've printed so that comments are printed in the + // correct locations (between two objects for example). + // And before the next item. + if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { + // if we hit the end add newlines so we can print the comment + // we don't do this if prev is invalid which means the + // beginning of the file since the first comment should + // be at the first line. + if !newlinePrinted && p.prev.IsValid() && index == len(t.Items) { + buf.Write([]byte{newline, newline}) + newlinePrinted = true + } + + // Write the actual comment. + buf.WriteString(comment.Text) + buf.WriteByte(newline) + + // Set printed to true to note that we printed something + printed = true + } + } + + // If we're not at the last item, write a new line so + // that there is a newline separating this comment from + // the next object. + if printed && index != len(t.Items) { + buf.WriteByte(newline) + } + } + + if index == len(t.Items) { + break + } + + buf.Write(p.output(t.Items[index])) + if index != len(t.Items)-1 { + // Always write a newline to separate us from the next item + buf.WriteByte(newline) + + // Need to determine if we're going to separate the next item + // with a blank line. The logic here is simple, though there + // are a few conditions: + // + // 1. The next object is more than one line away anyways, + // so we need an empty line. + // + // 2. The next object is not a "single line" object, so + // we need an empty line. + // + // 3. This current object is not a single line object, + // so we need an empty line. + current := t.Items[index] + next := t.Items[index+1] + if next.Pos().Line != t.Items[index].Pos().Line+1 || + !p.isSingleLineObject(next) || + !p.isSingleLineObject(current) { + buf.WriteByte(newline) + } + } + index++ + } + case *ast.ObjectKey: + buf.WriteString(t.Token.Text) + case *ast.ObjectItem: + p.prev = t.Pos() + buf.Write(p.objectItem(t)) + case *ast.LiteralType: + buf.Write(p.literalType(t)) + case *ast.ListType: + buf.Write(p.list(t)) + case *ast.ObjectType: + buf.Write(p.objectType(t)) + default: + fmt.Printf(" unknown type: %T\n", n) + } + + return buf.Bytes() +} + +func (p *printer) literalType(lit *ast.LiteralType) []byte { + result := []byte(lit.Token.Text) + switch lit.Token.Type { + case token.HEREDOC: + // Clear the trailing newline from heredocs + if result[len(result)-1] == '\n' { + result = result[:len(result)-1] + } + + // Poison lines 2+ so that we don't indent them + result = p.heredocIndent(result) + case token.STRING: + // If this is a multiline string, poison lines 2+ so we don't + // indent them. + if bytes.IndexRune(result, '\n') >= 0 { + result = p.heredocIndent(result) + } + } + + return result +} + +// objectItem returns the printable HCL form of an object item. An object type +// starts with one/multiple keys and has a value. The value might be of any +// type. +func (p *printer) objectItem(o *ast.ObjectItem) []byte { + defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text))) + var buf bytes.Buffer + + if o.LeadComment != nil { + for _, comment := range o.LeadComment.List { + buf.WriteString(comment.Text) + buf.WriteByte(newline) + } + } + + // If key and val are on different lines, treat line comments like lead comments. + if o.LineComment != nil && o.Val.Pos().Line != o.Keys[0].Pos().Line { + for _, comment := range o.LineComment.List { + buf.WriteString(comment.Text) + buf.WriteByte(newline) + } + } + + for i, k := range o.Keys { + buf.WriteString(k.Token.Text) + buf.WriteByte(blank) + + // reach end of key + if o.Assign.IsValid() && i == len(o.Keys)-1 && len(o.Keys) == 1 { + buf.WriteString("=") + buf.WriteByte(blank) + } + } + + buf.Write(p.output(o.Val)) + + if o.LineComment != nil && o.Val.Pos().Line == o.Keys[0].Pos().Line { + buf.WriteByte(blank) + for _, comment := range o.LineComment.List { + buf.WriteString(comment.Text) + } + } + + return buf.Bytes() +} + +// objectType returns the printable HCL form of an object type. An object type +// begins with a brace and ends with a brace. +func (p *printer) objectType(o *ast.ObjectType) []byte { + defer un(trace(p, "ObjectType")) + var buf bytes.Buffer + buf.WriteString("{") + + var index int + var nextItem token.Pos + var commented, newlinePrinted bool + for { + // Determine the location of the next actual non-comment + // item. If we're at the end, the next item is the closing brace + if index != len(o.List.Items) { + nextItem = o.List.Items[index].Pos() + } else { + nextItem = o.Rbrace + } + + // Go through the standalone comments in the file and print out + // the comments that we should be for this object item. + for _, c := range p.standaloneComments { + printed := false + var lastCommentPos token.Pos + for _, comment := range c.List { + // We only care about comments after the previous item + // we've printed so that comments are printed in the + // correct locations (between two objects for example). + // And before the next item. + if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { + // If there are standalone comments and the initial newline has not + // been printed yet, do it now. + if !newlinePrinted { + newlinePrinted = true + buf.WriteByte(newline) + } + + // add newline if it's between other printed nodes + if index > 0 { + commented = true + buf.WriteByte(newline) + } + + // Store this position + lastCommentPos = comment.Pos() + + // output the comment itself + buf.Write(p.indent(p.heredocIndent([]byte(comment.Text)))) + + // Set printed to true to note that we printed something + printed = true + + /* + if index != len(o.List.Items) { + buf.WriteByte(newline) // do not print on the end + } + */ + } + } + + // Stuff to do if we had comments + if printed { + // Always write a newline + buf.WriteByte(newline) + + // If there is another item in the object and our comment + // didn't hug it directly, then make sure there is a blank + // line separating them. + if nextItem != o.Rbrace && nextItem.Line != lastCommentPos.Line+1 { + buf.WriteByte(newline) + } + } + } + + if index == len(o.List.Items) { + p.prev = o.Rbrace + break + } + + // At this point we are sure that it's not a totally empty block: print + // the initial newline if it hasn't been printed yet by the previous + // block about standalone comments. + if !newlinePrinted { + buf.WriteByte(newline) + newlinePrinted = true + } + + // check if we have adjacent one liner items. If yes we'll going to align + // the comments. + var aligned []*ast.ObjectItem + for _, item := range o.List.Items[index:] { + // we don't group one line lists + if len(o.List.Items) == 1 { + break + } + + // one means a oneliner with out any lead comment + // two means a oneliner with lead comment + // anything else might be something else + cur := lines(string(p.objectItem(item))) + if cur > 2 { + break + } + + curPos := item.Pos() + + nextPos := token.Pos{} + if index != len(o.List.Items)-1 { + nextPos = o.List.Items[index+1].Pos() + } + + prevPos := token.Pos{} + if index != 0 { + prevPos = o.List.Items[index-1].Pos() + } + + // fmt.Println("DEBUG ----------------") + // fmt.Printf("prev = %+v prevPos: %s\n", prev, prevPos) + // fmt.Printf("cur = %+v curPos: %s\n", cur, curPos) + // fmt.Printf("next = %+v nextPos: %s\n", next, nextPos) + + if curPos.Line+1 == nextPos.Line { + aligned = append(aligned, item) + index++ + continue + } + + if curPos.Line-1 == prevPos.Line { + aligned = append(aligned, item) + index++ + + // finish if we have a new line or comment next. This happens + // if the next item is not adjacent + if curPos.Line+1 != nextPos.Line { + break + } + continue + } + + break + } + + // put newlines if the items are between other non aligned items. + // newlines are also added if there is a standalone comment already, so + // check it too + if !commented && index != len(aligned) { + buf.WriteByte(newline) + } + + if len(aligned) >= 1 { + p.prev = aligned[len(aligned)-1].Pos() + + items := p.alignedItems(aligned) + buf.Write(p.indent(items)) + } else { + p.prev = o.List.Items[index].Pos() + + buf.Write(p.indent(p.objectItem(o.List.Items[index]))) + index++ + } + + buf.WriteByte(newline) + } + + buf.WriteString("}") + return buf.Bytes() +} + +func (p *printer) alignedItems(items []*ast.ObjectItem) []byte { + var buf bytes.Buffer + + // find the longest key and value length, needed for alignment + var longestKeyLen int // longest key length + var longestValLen int // longest value length + for _, item := range items { + key := len(item.Keys[0].Token.Text) + val := len(p.output(item.Val)) + + if key > longestKeyLen { + longestKeyLen = key + } + + if val > longestValLen { + longestValLen = val + } + } + + for i, item := range items { + if item.LeadComment != nil { + for _, comment := range item.LeadComment.List { + buf.WriteString(comment.Text) + buf.WriteByte(newline) + } + } + + for i, k := range item.Keys { + keyLen := len(k.Token.Text) + buf.WriteString(k.Token.Text) + for i := 0; i < longestKeyLen-keyLen+1; i++ { + buf.WriteByte(blank) + } + + // reach end of key + if i == len(item.Keys)-1 && len(item.Keys) == 1 { + buf.WriteString("=") + buf.WriteByte(blank) + } + } + + val := p.output(item.Val) + valLen := len(val) + buf.Write(val) + + if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil { + for i := 0; i < longestValLen-valLen+1; i++ { + buf.WriteByte(blank) + } + + for _, comment := range item.LineComment.List { + buf.WriteString(comment.Text) + } + } + + // do not print for the last item + if i != len(items)-1 { + buf.WriteByte(newline) + } + } + + return buf.Bytes() +} + +// list returns the printable HCL form of an list type. +func (p *printer) list(l *ast.ListType) []byte { + if p.isSingleLineList(l) { + return p.singleLineList(l) + } + + var buf bytes.Buffer + buf.WriteString("[") + buf.WriteByte(newline) + + var longestLine int + for _, item := range l.List { + // for now we assume that the list only contains literal types + if lit, ok := item.(*ast.LiteralType); ok { + lineLen := len(lit.Token.Text) + if lineLen > longestLine { + longestLine = lineLen + } + } + } + + haveEmptyLine := false + for i, item := range l.List { + // If we have a lead comment, then we want to write that first + leadComment := false + if lit, ok := item.(*ast.LiteralType); ok && lit.LeadComment != nil { + leadComment = true + + // Ensure an empty line before every element with a + // lead comment (except the first item in a list). + if !haveEmptyLine && i != 0 { + buf.WriteByte(newline) + } + + for _, comment := range lit.LeadComment.List { + buf.Write(p.indent([]byte(comment.Text))) + buf.WriteByte(newline) + } + } + + // also indent each line + val := p.output(item) + curLen := len(val) + buf.Write(p.indent(val)) + + // if this item is a heredoc, then we output the comma on + // the next line. This is the only case this happens. + comma := []byte{','} + if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { + buf.WriteByte(newline) + comma = p.indent(comma) + } + + buf.Write(comma) + + if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil { + // if the next item doesn't have any comments, do not align + buf.WriteByte(blank) // align one space + for i := 0; i < longestLine-curLen; i++ { + buf.WriteByte(blank) + } + + for _, comment := range lit.LineComment.List { + buf.WriteString(comment.Text) + } + } + + buf.WriteByte(newline) + + // Ensure an empty line after every element with a + // lead comment (except the first item in a list). + haveEmptyLine = leadComment && i != len(l.List)-1 + if haveEmptyLine { + buf.WriteByte(newline) + } + } + + buf.WriteString("]") + return buf.Bytes() +} + +// isSingleLineList returns true if: +// * they were previously formatted entirely on one line +// * they consist entirely of literals +// * there are either no heredoc strings or the list has exactly one element +// * there are no line comments +func (printer) isSingleLineList(l *ast.ListType) bool { + for _, item := range l.List { + if item.Pos().Line != l.Lbrack.Line { + return false + } + + lit, ok := item.(*ast.LiteralType) + if !ok { + return false + } + + if lit.Token.Type == token.HEREDOC && len(l.List) != 1 { + return false + } + + if lit.LineComment != nil { + return false + } + } + + return true +} + +// singleLineList prints a simple single line list. +// For a definition of "simple", see isSingleLineList above. +func (p *printer) singleLineList(l *ast.ListType) []byte { + buf := &bytes.Buffer{} + + buf.WriteString("[") + for i, item := range l.List { + if i != 0 { + buf.WriteString(", ") + } + + // Output the item itself + buf.Write(p.output(item)) + + // The heredoc marker needs to be at the end of line. + if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { + buf.WriteByte(newline) + } + } + + buf.WriteString("]") + return buf.Bytes() +} + +// indent indents the lines of the given buffer for each non-empty line +func (p *printer) indent(buf []byte) []byte { + var prefix []byte + if p.cfg.SpacesWidth != 0 { + for i := 0; i < p.cfg.SpacesWidth; i++ { + prefix = append(prefix, blank) + } + } else { + prefix = []byte{tab} + } + + var res []byte + bol := true + for _, c := range buf { + if bol && c != '\n' { + res = append(res, prefix...) + } + + res = append(res, c) + bol = c == '\n' + } + return res +} + +// unindent removes all the indentation from the tombstoned lines +func (p *printer) unindent(buf []byte) []byte { + var res []byte + for i := 0; i < len(buf); i++ { + skip := len(buf)-i <= len(unindent) + if !skip { + skip = !bytes.Equal(unindent, buf[i:i+len(unindent)]) + } + if skip { + res = append(res, buf[i]) + continue + } + + // We have a marker. we have to backtrace here and clean out + // any whitespace ahead of our tombstone up to a \n + for j := len(res) - 1; j >= 0; j-- { + if res[j] == '\n' { + break + } + + res = res[:j] + } + + // Skip the entire unindent marker + i += len(unindent) - 1 + } + + return res +} + +// heredocIndent marks all the 2nd and further lines as unindentable +func (p *printer) heredocIndent(buf []byte) []byte { + var res []byte + bol := false + for _, c := range buf { + if bol && c != '\n' { + res = append(res, unindent...) + } + res = append(res, c) + bol = c == '\n' + } + return res +} + +// isSingleLineObject tells whether the given object item is a single +// line object such as "obj {}". +// +// A single line object: +// +// * has no lead comments (hence multi-line) +// * has no assignment +// * has no values in the stanza (within {}) +// +func (p *printer) isSingleLineObject(val *ast.ObjectItem) bool { + // If there is a lead comment, can't be one line + if val.LeadComment != nil { + return false + } + + // If there is assignment, we always break by line + if val.Assign.IsValid() { + return false + } + + // If it isn't an object type, then its not a single line object + ot, ok := val.Val.(*ast.ObjectType) + if !ok { + return false + } + + // If the object has no items, it is single line! + return len(ot.List.Items) == 0 +} + +func lines(txt string) int { + endline := 1 + for i := 0; i < len(txt); i++ { + if txt[i] == '\n' { + endline++ + } + } + return endline +} + +// ---------------------------------------------------------------------------- +// Tracing support + +func (p *printer) printTrace(a ...interface{}) { + if !p.enableTrace { + return + } + + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) + i := 2 * p.indentTrace + for i > n { + fmt.Print(dots) + i -= n + } + // i <= n + fmt.Print(dots[0:i]) + fmt.Println(a...) +} + +func trace(p *printer, msg string) *printer { + p.printTrace(msg, "(") + p.indentTrace++ + return p +} + +// Usage pattern: defer un(trace(p, "...")) +func un(p *printer) { + p.indentTrace-- + p.printTrace(")") +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go b/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go new file mode 100644 index 0000000..6617ab8 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go @@ -0,0 +1,66 @@ +// Package printer implements printing of AST nodes to HCL format. +package printer + +import ( + "bytes" + "io" + "text/tabwriter" + + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/parser" +) + +var DefaultConfig = Config{ + SpacesWidth: 2, +} + +// A Config node controls the output of Fprint. +type Config struct { + SpacesWidth int // if set, it will use spaces instead of tabs for alignment +} + +func (c *Config) Fprint(output io.Writer, node ast.Node) error { + p := &printer{ + cfg: *c, + comments: make([]*ast.CommentGroup, 0), + standaloneComments: make([]*ast.CommentGroup, 0), + // enableTrace: true, + } + + p.collectComments(node) + + if _, err := output.Write(p.unindent(p.output(node))); err != nil { + return err + } + + // flush tabwriter, if any + var err error + if tw, _ := output.(*tabwriter.Writer); tw != nil { + err = tw.Flush() + } + + return err +} + +// Fprint "pretty-prints" an HCL node to output +// It calls Config.Fprint with default settings. +func Fprint(output io.Writer, node ast.Node) error { + return DefaultConfig.Fprint(output, node) +} + +// Format formats src HCL and returns the result. +func Format(src []byte) ([]byte, error) { + node, err := parser.Parse(src) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + if err := DefaultConfig.Fprint(&buf, node); err != nil { + return nil, err + } + + // Add trailing newline to result + buf.WriteString("\n") + return buf.Bytes(), nil +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go new file mode 100644 index 0000000..624a18f --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go @@ -0,0 +1,652 @@ +// Package scanner implements a scanner for HCL (HashiCorp Configuration +// Language) source text. +package scanner + +import ( + "bytes" + "fmt" + "os" + "regexp" + "unicode" + "unicode/utf8" + + "github.com/hashicorp/hcl/hcl/token" +) + +// eof represents a marker rune for the end of the reader. +const eof = rune(0) + +// Scanner defines a lexical scanner +type Scanner struct { + buf *bytes.Buffer // Source buffer for advancing and scanning + src []byte // Source buffer for immutable access + + // Source Position + srcPos token.Pos // current position + prevPos token.Pos // previous position, used for peek() method + + lastCharLen int // length of last character in bytes + lastLineLen int // length of last line in characters (for correct column reporting) + + tokStart int // token text start position + tokEnd int // token text end position + + // Error is called for each error encountered. If no Error + // function is set, the error is reported to os.Stderr. + Error func(pos token.Pos, msg string) + + // ErrorCount is incremented by one for each error encountered. + ErrorCount int + + // tokPos is the start position of most recently scanned token; set by + // Scan. The Filename field is always left untouched by the Scanner. If + // an error is reported (via Error) and Position is invalid, the scanner is + // not inside a token. + tokPos token.Pos +} + +// New creates and initializes a new instance of Scanner using src as +// its source content. +func New(src []byte) *Scanner { + // even though we accept a src, we read from a io.Reader compatible type + // (*bytes.Buffer). So in the future we might easily change it to streaming + // read. + b := bytes.NewBuffer(src) + s := &Scanner{ + buf: b, + src: src, + } + + // srcPosition always starts with 1 + s.srcPos.Line = 1 + return s +} + +// next reads the next rune from the bufferred reader. Returns the rune(0) if +// an error occurs (or io.EOF is returned). +func (s *Scanner) next() rune { + ch, size, err := s.buf.ReadRune() + if err != nil { + // advance for error reporting + s.srcPos.Column++ + s.srcPos.Offset += size + s.lastCharLen = size + return eof + } + + // remember last position + s.prevPos = s.srcPos + + s.srcPos.Column++ + s.lastCharLen = size + s.srcPos.Offset += size + + if ch == utf8.RuneError && size == 1 { + s.err("illegal UTF-8 encoding") + return ch + } + + if ch == '\n' { + s.srcPos.Line++ + s.lastLineLen = s.srcPos.Column + s.srcPos.Column = 0 + } + + if ch == '\x00' { + s.err("unexpected null character (0x00)") + return eof + } + + if ch == '\uE123' { + s.err("unicode code point U+E123 reserved for internal use") + return utf8.RuneError + } + + // debug + // fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) + return ch +} + +// unread unreads the previous read Rune and updates the source position +func (s *Scanner) unread() { + if err := s.buf.UnreadRune(); err != nil { + panic(err) // this is user fault, we should catch it + } + s.srcPos = s.prevPos // put back last position +} + +// peek returns the next rune without advancing the reader. +func (s *Scanner) peek() rune { + peek, _, err := s.buf.ReadRune() + if err != nil { + return eof + } + + s.buf.UnreadRune() + return peek +} + +// Scan scans the next token and returns the token. +func (s *Scanner) Scan() token.Token { + ch := s.next() + + // skip white space + for isWhitespace(ch) { + ch = s.next() + } + + var tok token.Type + + // token text markings + s.tokStart = s.srcPos.Offset - s.lastCharLen + + // token position, initial next() is moving the offset by one(size of rune + // actually), though we are interested with the starting point + s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen + if s.srcPos.Column > 0 { + // common case: last character was not a '\n' + s.tokPos.Line = s.srcPos.Line + s.tokPos.Column = s.srcPos.Column + } else { + // last character was a '\n' + // (we cannot be at the beginning of the source + // since we have called next() at least once) + s.tokPos.Line = s.srcPos.Line - 1 + s.tokPos.Column = s.lastLineLen + } + + switch { + case isLetter(ch): + tok = token.IDENT + lit := s.scanIdentifier() + if lit == "true" || lit == "false" { + tok = token.BOOL + } + case isDecimal(ch): + tok = s.scanNumber(ch) + default: + switch ch { + case eof: + tok = token.EOF + case '"': + tok = token.STRING + s.scanString() + case '#', '/': + tok = token.COMMENT + s.scanComment(ch) + case '.': + tok = token.PERIOD + ch = s.peek() + if isDecimal(ch) { + tok = token.FLOAT + ch = s.scanMantissa(ch) + ch = s.scanExponent(ch) + } + case '<': + tok = token.HEREDOC + s.scanHeredoc() + case '[': + tok = token.LBRACK + case ']': + tok = token.RBRACK + case '{': + tok = token.LBRACE + case '}': + tok = token.RBRACE + case ',': + tok = token.COMMA + case '=': + tok = token.ASSIGN + case '+': + tok = token.ADD + case '-': + if isDecimal(s.peek()) { + ch := s.next() + tok = s.scanNumber(ch) + } else { + tok = token.SUB + } + default: + s.err("illegal char") + } + } + + // finish token ending + s.tokEnd = s.srcPos.Offset + + // create token literal + var tokenText string + if s.tokStart >= 0 { + tokenText = string(s.src[s.tokStart:s.tokEnd]) + } + s.tokStart = s.tokEnd // ensure idempotency of tokenText() call + + return token.Token{ + Type: tok, + Pos: s.tokPos, + Text: tokenText, + } +} + +func (s *Scanner) scanComment(ch rune) { + // single line comments + if ch == '#' || (ch == '/' && s.peek() != '*') { + if ch == '/' && s.peek() != '/' { + s.err("expected '/' for comment") + return + } + + ch = s.next() + for ch != '\n' && ch >= 0 && ch != eof { + ch = s.next() + } + if ch != eof && ch >= 0 { + s.unread() + } + return + } + + // be sure we get the character after /* This allows us to find comment's + // that are not erminated + if ch == '/' { + s.next() + ch = s.next() // read character after "/*" + } + + // look for /* - style comments + for { + if ch < 0 || ch == eof { + s.err("comment not terminated") + break + } + + ch0 := ch + ch = s.next() + if ch0 == '*' && ch == '/' { + break + } + } +} + +// scanNumber scans a HCL number definition starting with the given rune +func (s *Scanner) scanNumber(ch rune) token.Type { + if ch == '0' { + // check for hexadecimal, octal or float + ch = s.next() + if ch == 'x' || ch == 'X' { + // hexadecimal + ch = s.next() + found := false + for isHexadecimal(ch) { + ch = s.next() + found = true + } + + if !found { + s.err("illegal hexadecimal number") + } + + if ch != eof { + s.unread() + } + + return token.NUMBER + } + + // now it's either something like: 0421(octal) or 0.1231(float) + illegalOctal := false + for isDecimal(ch) { + ch = s.next() + if ch == '8' || ch == '9' { + // this is just a possibility. For example 0159 is illegal, but + // 0159.23 is valid. So we mark a possible illegal octal. If + // the next character is not a period, we'll print the error. + illegalOctal = true + } + } + + if ch == 'e' || ch == 'E' { + ch = s.scanExponent(ch) + return token.FLOAT + } + + if ch == '.' { + ch = s.scanFraction(ch) + + if ch == 'e' || ch == 'E' { + ch = s.next() + ch = s.scanExponent(ch) + } + return token.FLOAT + } + + if illegalOctal { + s.err("illegal octal number") + } + + if ch != eof { + s.unread() + } + return token.NUMBER + } + + s.scanMantissa(ch) + ch = s.next() // seek forward + if ch == 'e' || ch == 'E' { + ch = s.scanExponent(ch) + return token.FLOAT + } + + if ch == '.' { + ch = s.scanFraction(ch) + if ch == 'e' || ch == 'E' { + ch = s.next() + ch = s.scanExponent(ch) + } + return token.FLOAT + } + + if ch != eof { + s.unread() + } + return token.NUMBER +} + +// scanMantissa scans the mantissa beginning from the rune. It returns the next +// non decimal rune. It's used to determine wheter it's a fraction or exponent. +func (s *Scanner) scanMantissa(ch rune) rune { + scanned := false + for isDecimal(ch) { + ch = s.next() + scanned = true + } + + if scanned && ch != eof { + s.unread() + } + return ch +} + +// scanFraction scans the fraction after the '.' rune +func (s *Scanner) scanFraction(ch rune) rune { + if ch == '.' { + ch = s.peek() // we peek just to see if we can move forward + ch = s.scanMantissa(ch) + } + return ch +} + +// scanExponent scans the remaining parts of an exponent after the 'e' or 'E' +// rune. +func (s *Scanner) scanExponent(ch rune) rune { + if ch == 'e' || ch == 'E' { + ch = s.next() + if ch == '-' || ch == '+' { + ch = s.next() + } + ch = s.scanMantissa(ch) + } + return ch +} + +// scanHeredoc scans a heredoc string +func (s *Scanner) scanHeredoc() { + // Scan the second '<' in example: '<= len(identBytes) && identRegexp.Match(s.src[lineStart:s.srcPos.Offset-s.lastCharLen]) { + break + } + + // Not an anchor match, record the start of a new line + lineStart = s.srcPos.Offset + } + + if ch == eof { + s.err("heredoc not terminated") + return + } + } + + return +} + +// scanString scans a quoted string +func (s *Scanner) scanString() { + braces := 0 + for { + // '"' opening already consumed + // read character after quote + ch := s.next() + + if (ch == '\n' && braces == 0) || ch < 0 || ch == eof { + s.err("literal not terminated") + return + } + + if ch == '"' && braces == 0 { + break + } + + // If we're going into a ${} then we can ignore quotes for awhile + if braces == 0 && ch == '$' && s.peek() == '{' { + braces++ + s.next() + } else if braces > 0 && ch == '{' { + braces++ + } + if braces > 0 && ch == '}' { + braces-- + } + + if ch == '\\' { + s.scanEscape() + } + } + + return +} + +// scanEscape scans an escape sequence +func (s *Scanner) scanEscape() rune { + // http://en.cppreference.com/w/cpp/language/escape + ch := s.next() // read character after '/' + switch ch { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': + // nothing to do + case '0', '1', '2', '3', '4', '5', '6', '7': + // octal notation + ch = s.scanDigits(ch, 8, 3) + case 'x': + // hexademical notation + ch = s.scanDigits(s.next(), 16, 2) + case 'u': + // universal character name + ch = s.scanDigits(s.next(), 16, 4) + case 'U': + // universal character name + ch = s.scanDigits(s.next(), 16, 8) + default: + s.err("illegal char escape") + } + return ch +} + +// scanDigits scans a rune with the given base for n times. For example an +// octal notation \184 would yield in scanDigits(ch, 8, 3) +func (s *Scanner) scanDigits(ch rune, base, n int) rune { + start := n + for n > 0 && digitVal(ch) < base { + ch = s.next() + if ch == eof { + // If we see an EOF, we halt any more scanning of digits + // immediately. + break + } + + n-- + } + if n > 0 { + s.err("illegal char escape") + } + + if n != start && ch != eof { + // we scanned all digits, put the last non digit char back, + // only if we read anything at all + s.unread() + } + + return ch +} + +// scanIdentifier scans an identifier and returns the literal string +func (s *Scanner) scanIdentifier() string { + offs := s.srcPos.Offset - s.lastCharLen + ch := s.next() + for isLetter(ch) || isDigit(ch) || ch == '-' || ch == '.' { + ch = s.next() + } + + if ch != eof { + s.unread() // we got identifier, put back latest char + } + + return string(s.src[offs:s.srcPos.Offset]) +} + +// recentPosition returns the position of the character immediately after the +// character or token returned by the last call to Scan. +func (s *Scanner) recentPosition() (pos token.Pos) { + pos.Offset = s.srcPos.Offset - s.lastCharLen + switch { + case s.srcPos.Column > 0: + // common case: last character was not a '\n' + pos.Line = s.srcPos.Line + pos.Column = s.srcPos.Column + case s.lastLineLen > 0: + // last character was a '\n' + // (we cannot be at the beginning of the source + // since we have called next() at least once) + pos.Line = s.srcPos.Line - 1 + pos.Column = s.lastLineLen + default: + // at the beginning of the source + pos.Line = 1 + pos.Column = 1 + } + return +} + +// err prints the error of any scanning to s.Error function. If the function is +// not defined, by default it prints them to os.Stderr +func (s *Scanner) err(msg string) { + s.ErrorCount++ + pos := s.recentPosition() + + if s.Error != nil { + s.Error(pos, msg) + return + } + + fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) +} + +// isHexadecimal returns true if the given rune is a letter +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +} + +// isDigit returns true if the given rune is a decimal digit +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) +} + +// isDecimal returns true if the given rune is a decimal number +func isDecimal(ch rune) bool { + return '0' <= ch && ch <= '9' +} + +// isHexadecimal returns true if the given rune is an hexadecimal number +func isHexadecimal(ch rune) bool { + return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' +} + +// isWhitespace returns true if the rune is a space, tab, newline or carriage return +func isWhitespace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' +} + +// digitVal returns the integer value of a given octal,decimal or hexadecimal rune +func digitVal(ch rune) int { + switch { + case '0' <= ch && ch <= '9': + return int(ch - '0') + case 'a' <= ch && ch <= 'f': + return int(ch - 'a' + 10) + case 'A' <= ch && ch <= 'F': + return int(ch - 'A' + 10) + } + return 16 // larger than any legal digit val +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go b/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go new file mode 100644 index 0000000..5f981ea --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go @@ -0,0 +1,241 @@ +package strconv + +import ( + "errors" + "unicode/utf8" +) + +// ErrSyntax indicates that a value does not have the right syntax for the target type. +var ErrSyntax = errors.New("invalid syntax") + +// Unquote interprets s as a single-quoted, double-quoted, +// or backquoted Go string literal, returning the string value +// that s quotes. (If s is single-quoted, it would be a Go +// character literal; Unquote returns the corresponding +// one-character string.) +func Unquote(s string) (t string, err error) { + n := len(s) + if n < 2 { + return "", ErrSyntax + } + quote := s[0] + if quote != s[n-1] { + return "", ErrSyntax + } + s = s[1 : n-1] + + if quote != '"' { + return "", ErrSyntax + } + if !contains(s, '$') && !contains(s, '{') && contains(s, '\n') { + return "", ErrSyntax + } + + // Is it trivial? Avoid allocation. + if !contains(s, '\\') && !contains(s, quote) && !contains(s, '$') { + switch quote { + case '"': + return s, nil + case '\'': + r, size := utf8.DecodeRuneInString(s) + if size == len(s) && (r != utf8.RuneError || size != 1) { + return s, nil + } + } + } + + var runeTmp [utf8.UTFMax]byte + buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. + for len(s) > 0 { + // If we're starting a '${}' then let it through un-unquoted. + // Specifically: we don't unquote any characters within the `${}` + // section. + if s[0] == '$' && len(s) > 1 && s[1] == '{' { + buf = append(buf, '$', '{') + s = s[2:] + + // Continue reading until we find the closing brace, copying as-is + braces := 1 + for len(s) > 0 && braces > 0 { + r, size := utf8.DecodeRuneInString(s) + if r == utf8.RuneError { + return "", ErrSyntax + } + + s = s[size:] + + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + + switch r { + case '{': + braces++ + case '}': + braces-- + } + } + if braces != 0 { + return "", ErrSyntax + } + if len(s) == 0 { + // If there's no string left, we're done! + break + } else { + // If there's more left, we need to pop back up to the top of the loop + // in case there's another interpolation in this string. + continue + } + } + + if s[0] == '\n' { + return "", ErrSyntax + } + + c, multibyte, ss, err := unquoteChar(s, quote) + if err != nil { + return "", err + } + s = ss + if c < utf8.RuneSelf || !multibyte { + buf = append(buf, byte(c)) + } else { + n := utf8.EncodeRune(runeTmp[:], c) + buf = append(buf, runeTmp[:n]...) + } + if quote == '\'' && len(s) != 0 { + // single-quoted must be single character + return "", ErrSyntax + } + } + return string(buf), nil +} + +// contains reports whether the string contains the byte c. +func contains(s string, c byte) bool { + for i := 0; i < len(s); i++ { + if s[i] == c { + return true + } + } + return false +} + +func unhex(b byte) (v rune, ok bool) { + c := rune(b) + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + return +} + +func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { + // easy cases + switch c := s[0]; { + case c == quote && (quote == '\'' || quote == '"'): + err = ErrSyntax + return + case c >= utf8.RuneSelf: + r, size := utf8.DecodeRuneInString(s) + return r, true, s[size:], nil + case c != '\\': + return rune(s[0]), false, s[1:], nil + } + + // hard case: c is backslash + if len(s) <= 1 { + err = ErrSyntax + return + } + c := s[1] + s = s[2:] + + switch c { + case 'a': + value = '\a' + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case 'x', 'u', 'U': + n := 0 + switch c { + case 'x': + n = 2 + case 'u': + n = 4 + case 'U': + n = 8 + } + var v rune + if len(s) < n { + err = ErrSyntax + return + } + for j := 0; j < n; j++ { + x, ok := unhex(s[j]) + if !ok { + err = ErrSyntax + return + } + v = v<<4 | x + } + s = s[n:] + if c == 'x' { + // single-byte string, possibly not UTF-8 + value = v + break + } + if v > utf8.MaxRune { + err = ErrSyntax + return + } + value = v + multibyte = true + case '0', '1', '2', '3', '4', '5', '6', '7': + v := rune(c) - '0' + if len(s) < 2 { + err = ErrSyntax + return + } + for j := 0; j < 2; j++ { // one digit already; two more + x := rune(s[j]) - '0' + if x < 0 || x > 7 { + err = ErrSyntax + return + } + v = (v << 3) | x + } + s = s[2:] + if v > 255 { + err = ErrSyntax + return + } + value = v + case '\\': + value = '\\' + case '\'', '"': + if c != quote { + err = ErrSyntax + return + } + value = rune(c) + default: + err = ErrSyntax + return + } + tail = s + return +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/token/position.go b/vendor/github.com/hashicorp/hcl/hcl/token/position.go new file mode 100644 index 0000000..59c1bb7 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/token/position.go @@ -0,0 +1,46 @@ +package token + +import "fmt" + +// Pos describes an arbitrary source position +// including the file, line, and column location. +// A Position is valid if the line number is > 0. +type Pos struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (character count) +} + +// IsValid returns true if the position is valid. +func (p *Pos) IsValid() bool { return p.Line > 0 } + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// line:column valid position without file name +// file invalid position with file name +// - invalid position without file name +func (p Pos) String() string { + s := p.Filename + if p.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d:%d", p.Line, p.Column) + } + if s == "" { + s = "-" + } + return s +} + +// Before reports whether the position p is before u. +func (p Pos) Before(u Pos) bool { + return u.Offset > p.Offset || u.Line > p.Line +} + +// After reports whether the position p is after u. +func (p Pos) After(u Pos) bool { + return u.Offset < p.Offset || u.Line < p.Line +} diff --git a/vendor/github.com/hashicorp/hcl/hcl/token/token.go b/vendor/github.com/hashicorp/hcl/hcl/token/token.go new file mode 100644 index 0000000..e37c066 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/hcl/token/token.go @@ -0,0 +1,219 @@ +// Package token defines constants representing the lexical tokens for HCL +// (HashiCorp Configuration Language) +package token + +import ( + "fmt" + "strconv" + "strings" + + hclstrconv "github.com/hashicorp/hcl/hcl/strconv" +) + +// Token defines a single HCL token which can be obtained via the Scanner +type Token struct { + Type Type + Pos Pos + Text string + JSON bool +} + +// Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) +type Type int + +const ( + // Special tokens + ILLEGAL Type = iota + EOF + COMMENT + + identifier_beg + IDENT // literals + literal_beg + NUMBER // 12345 + FLOAT // 123.45 + BOOL // true,false + STRING // "abc" + HEREDOC // < 0 { + // Pop the current item + n := len(frontier) + item := frontier[n-1] + frontier = frontier[:n-1] + + switch v := item.Val.(type) { + case *ast.ObjectType: + items, frontier = flattenObjectType(v, item, items, frontier) + case *ast.ListType: + items, frontier = flattenListType(v, item, items, frontier) + default: + items = append(items, item) + } + } + + // Reverse the list since the frontier model runs things backwards + for i := len(items)/2 - 1; i >= 0; i-- { + opp := len(items) - 1 - i + items[i], items[opp] = items[opp], items[i] + } + + // Done! Set the original items + list.Items = items + return n, true + }) +} + +func flattenListType( + ot *ast.ListType, + item *ast.ObjectItem, + items []*ast.ObjectItem, + frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { + // If the list is empty, keep the original list + if len(ot.List) == 0 { + items = append(items, item) + return items, frontier + } + + // All the elements of this object must also be objects! + for _, subitem := range ot.List { + if _, ok := subitem.(*ast.ObjectType); !ok { + items = append(items, item) + return items, frontier + } + } + + // Great! We have a match go through all the items and flatten + for _, elem := range ot.List { + // Add it to the frontier so that we can recurse + frontier = append(frontier, &ast.ObjectItem{ + Keys: item.Keys, + Assign: item.Assign, + Val: elem, + LeadComment: item.LeadComment, + LineComment: item.LineComment, + }) + } + + return items, frontier +} + +func flattenObjectType( + ot *ast.ObjectType, + item *ast.ObjectItem, + items []*ast.ObjectItem, + frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { + // If the list has no items we do not have to flatten anything + if ot.List.Items == nil { + items = append(items, item) + return items, frontier + } + + // All the elements of this object must also be objects! + for _, subitem := range ot.List.Items { + if _, ok := subitem.Val.(*ast.ObjectType); !ok { + items = append(items, item) + return items, frontier + } + } + + // Great! We have a match go through all the items and flatten + for _, subitem := range ot.List.Items { + // Copy the new key + keys := make([]*ast.ObjectKey, len(item.Keys)+len(subitem.Keys)) + copy(keys, item.Keys) + copy(keys[len(item.Keys):], subitem.Keys) + + // Add it to the frontier so that we can recurse + frontier = append(frontier, &ast.ObjectItem{ + Keys: keys, + Assign: item.Assign, + Val: subitem.Val, + LeadComment: item.LeadComment, + LineComment: item.LineComment, + }) + } + + return items, frontier +} diff --git a/vendor/github.com/hashicorp/hcl/json/parser/parser.go b/vendor/github.com/hashicorp/hcl/json/parser/parser.go new file mode 100644 index 0000000..125a5f0 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/json/parser/parser.go @@ -0,0 +1,313 @@ +package parser + +import ( + "errors" + "fmt" + + "github.com/hashicorp/hcl/hcl/ast" + hcltoken "github.com/hashicorp/hcl/hcl/token" + "github.com/hashicorp/hcl/json/scanner" + "github.com/hashicorp/hcl/json/token" +) + +type Parser struct { + sc *scanner.Scanner + + // Last read token + tok token.Token + commaPrev token.Token + + enableTrace bool + indent int + n int // buffer size (max = 1) +} + +func newParser(src []byte) *Parser { + return &Parser{ + sc: scanner.New(src), + } +} + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func Parse(src []byte) (*ast.File, error) { + p := newParser(src) + return p.Parse() +} + +var errEofToken = errors.New("EOF token found") + +// Parse returns the fully parsed source and returns the abstract syntax tree. +func (p *Parser) Parse() (*ast.File, error) { + f := &ast.File{} + var err, scerr error + p.sc.Error = func(pos token.Pos, msg string) { + scerr = fmt.Errorf("%s: %s", pos, msg) + } + + // The root must be an object in JSON + object, err := p.object() + if scerr != nil { + return nil, scerr + } + if err != nil { + return nil, err + } + + // We make our final node an object list so it is more HCL compatible + f.Node = object.List + + // Flatten it, which finds patterns and turns them into more HCL-like + // AST trees. + flattenObjects(f.Node) + + return f, nil +} + +func (p *Parser) objectList() (*ast.ObjectList, error) { + defer un(trace(p, "ParseObjectList")) + node := &ast.ObjectList{} + + for { + n, err := p.objectItem() + if err == errEofToken { + break // we are finished + } + + // we don't return a nil node, because might want to use already + // collected items. + if err != nil { + return node, err + } + + node.Add(n) + + // Check for a followup comma. If it isn't a comma, then we're done + if tok := p.scan(); tok.Type != token.COMMA { + break + } + } + + return node, nil +} + +// objectItem parses a single object item +func (p *Parser) objectItem() (*ast.ObjectItem, error) { + defer un(trace(p, "ParseObjectItem")) + + keys, err := p.objectKey() + if err != nil { + return nil, err + } + + o := &ast.ObjectItem{ + Keys: keys, + } + + switch p.tok.Type { + case token.COLON: + pos := p.tok.Pos + o.Assign = hcltoken.Pos{ + Filename: pos.Filename, + Offset: pos.Offset, + Line: pos.Line, + Column: pos.Column, + } + + o.Val, err = p.objectValue() + if err != nil { + return nil, err + } + } + + return o, nil +} + +// objectKey parses an object key and returns a ObjectKey AST +func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { + keyCount := 0 + keys := make([]*ast.ObjectKey, 0) + + for { + tok := p.scan() + switch tok.Type { + case token.EOF: + return nil, errEofToken + case token.STRING: + keyCount++ + keys = append(keys, &ast.ObjectKey{ + Token: p.tok.HCLToken(), + }) + case token.COLON: + // If we have a zero keycount it means that we never got + // an object key, i.e. `{ :`. This is a syntax error. + if keyCount == 0 { + return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) + } + + // Done + return keys, nil + case token.ILLEGAL: + return nil, errors.New("illegal") + default: + return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) + } + } +} + +// object parses any type of object, such as number, bool, string, object or +// list. +func (p *Parser) objectValue() (ast.Node, error) { + defer un(trace(p, "ParseObjectValue")) + tok := p.scan() + + switch tok.Type { + case token.NUMBER, token.FLOAT, token.BOOL, token.NULL, token.STRING: + return p.literalType() + case token.LBRACE: + return p.objectType() + case token.LBRACK: + return p.listType() + case token.EOF: + return nil, errEofToken + } + + return nil, fmt.Errorf("Expected object value, got unknown token: %+v", tok) +} + +// object parses any type of object, such as number, bool, string, object or +// list. +func (p *Parser) object() (*ast.ObjectType, error) { + defer un(trace(p, "ParseType")) + tok := p.scan() + + switch tok.Type { + case token.LBRACE: + return p.objectType() + case token.EOF: + return nil, errEofToken + } + + return nil, fmt.Errorf("Expected object, got unknown token: %+v", tok) +} + +// objectType parses an object type and returns a ObjectType AST +func (p *Parser) objectType() (*ast.ObjectType, error) { + defer un(trace(p, "ParseObjectType")) + + // we assume that the currently scanned token is a LBRACE + o := &ast.ObjectType{} + + l, err := p.objectList() + + // if we hit RBRACE, we are good to go (means we parsed all Items), if it's + // not a RBRACE, it's an syntax error and we just return it. + if err != nil && p.tok.Type != token.RBRACE { + return nil, err + } + + o.List = l + return o, nil +} + +// listType parses a list type and returns a ListType AST +func (p *Parser) listType() (*ast.ListType, error) { + defer un(trace(p, "ParseListType")) + + // we assume that the currently scanned token is a LBRACK + l := &ast.ListType{} + + for { + tok := p.scan() + switch tok.Type { + case token.NUMBER, token.FLOAT, token.STRING: + node, err := p.literalType() + if err != nil { + return nil, err + } + + l.Add(node) + case token.COMMA: + continue + case token.LBRACE: + node, err := p.objectType() + if err != nil { + return nil, err + } + + l.Add(node) + case token.BOOL: + // TODO(arslan) should we support? not supported by HCL yet + case token.LBRACK: + // TODO(arslan) should we support nested lists? Even though it's + // written in README of HCL, it's not a part of the grammar + // (not defined in parse.y) + case token.RBRACK: + // finished + return l, nil + default: + return nil, fmt.Errorf("unexpected token while parsing list: %s", tok.Type) + } + + } +} + +// literalType parses a literal type and returns a LiteralType AST +func (p *Parser) literalType() (*ast.LiteralType, error) { + defer un(trace(p, "ParseLiteral")) + + return &ast.LiteralType{ + Token: p.tok.HCLToken(), + }, nil +} + +// scan returns the next token from the underlying scanner. If a token has +// been unscanned then read that instead. +func (p *Parser) scan() token.Token { + // If we have a token on the buffer, then return it. + if p.n != 0 { + p.n = 0 + return p.tok + } + + p.tok = p.sc.Scan() + return p.tok +} + +// unscan pushes the previously read token back onto the buffer. +func (p *Parser) unscan() { + p.n = 1 +} + +// ---------------------------------------------------------------------------- +// Parsing support + +func (p *Parser) printTrace(a ...interface{}) { + if !p.enableTrace { + return + } + + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) + fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) + + i := 2 * p.indent + for i > n { + fmt.Print(dots) + i -= n + } + // i <= n + fmt.Print(dots[0:i]) + fmt.Println(a...) +} + +func trace(p *Parser, msg string) *Parser { + p.printTrace(msg, "(") + p.indent++ + return p +} + +// Usage pattern: defer un(trace(p, "...")) +func un(p *Parser) { + p.indent-- + p.printTrace(")") +} diff --git a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go new file mode 100644 index 0000000..fe3f0f0 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go @@ -0,0 +1,451 @@ +package scanner + +import ( + "bytes" + "fmt" + "os" + "unicode" + "unicode/utf8" + + "github.com/hashicorp/hcl/json/token" +) + +// eof represents a marker rune for the end of the reader. +const eof = rune(0) + +// Scanner defines a lexical scanner +type Scanner struct { + buf *bytes.Buffer // Source buffer for advancing and scanning + src []byte // Source buffer for immutable access + + // Source Position + srcPos token.Pos // current position + prevPos token.Pos // previous position, used for peek() method + + lastCharLen int // length of last character in bytes + lastLineLen int // length of last line in characters (for correct column reporting) + + tokStart int // token text start position + tokEnd int // token text end position + + // Error is called for each error encountered. If no Error + // function is set, the error is reported to os.Stderr. + Error func(pos token.Pos, msg string) + + // ErrorCount is incremented by one for each error encountered. + ErrorCount int + + // tokPos is the start position of most recently scanned token; set by + // Scan. The Filename field is always left untouched by the Scanner. If + // an error is reported (via Error) and Position is invalid, the scanner is + // not inside a token. + tokPos token.Pos +} + +// New creates and initializes a new instance of Scanner using src as +// its source content. +func New(src []byte) *Scanner { + // even though we accept a src, we read from a io.Reader compatible type + // (*bytes.Buffer). So in the future we might easily change it to streaming + // read. + b := bytes.NewBuffer(src) + s := &Scanner{ + buf: b, + src: src, + } + + // srcPosition always starts with 1 + s.srcPos.Line = 1 + return s +} + +// next reads the next rune from the bufferred reader. Returns the rune(0) if +// an error occurs (or io.EOF is returned). +func (s *Scanner) next() rune { + ch, size, err := s.buf.ReadRune() + if err != nil { + // advance for error reporting + s.srcPos.Column++ + s.srcPos.Offset += size + s.lastCharLen = size + return eof + } + + if ch == utf8.RuneError && size == 1 { + s.srcPos.Column++ + s.srcPos.Offset += size + s.lastCharLen = size + s.err("illegal UTF-8 encoding") + return ch + } + + // remember last position + s.prevPos = s.srcPos + + s.srcPos.Column++ + s.lastCharLen = size + s.srcPos.Offset += size + + if ch == '\n' { + s.srcPos.Line++ + s.lastLineLen = s.srcPos.Column + s.srcPos.Column = 0 + } + + // debug + // fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) + return ch +} + +// unread unreads the previous read Rune and updates the source position +func (s *Scanner) unread() { + if err := s.buf.UnreadRune(); err != nil { + panic(err) // this is user fault, we should catch it + } + s.srcPos = s.prevPos // put back last position +} + +// peek returns the next rune without advancing the reader. +func (s *Scanner) peek() rune { + peek, _, err := s.buf.ReadRune() + if err != nil { + return eof + } + + s.buf.UnreadRune() + return peek +} + +// Scan scans the next token and returns the token. +func (s *Scanner) Scan() token.Token { + ch := s.next() + + // skip white space + for isWhitespace(ch) { + ch = s.next() + } + + var tok token.Type + + // token text markings + s.tokStart = s.srcPos.Offset - s.lastCharLen + + // token position, initial next() is moving the offset by one(size of rune + // actually), though we are interested with the starting point + s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen + if s.srcPos.Column > 0 { + // common case: last character was not a '\n' + s.tokPos.Line = s.srcPos.Line + s.tokPos.Column = s.srcPos.Column + } else { + // last character was a '\n' + // (we cannot be at the beginning of the source + // since we have called next() at least once) + s.tokPos.Line = s.srcPos.Line - 1 + s.tokPos.Column = s.lastLineLen + } + + switch { + case isLetter(ch): + lit := s.scanIdentifier() + if lit == "true" || lit == "false" { + tok = token.BOOL + } else if lit == "null" { + tok = token.NULL + } else { + s.err("illegal char") + } + case isDecimal(ch): + tok = s.scanNumber(ch) + default: + switch ch { + case eof: + tok = token.EOF + case '"': + tok = token.STRING + s.scanString() + case '.': + tok = token.PERIOD + ch = s.peek() + if isDecimal(ch) { + tok = token.FLOAT + ch = s.scanMantissa(ch) + ch = s.scanExponent(ch) + } + case '[': + tok = token.LBRACK + case ']': + tok = token.RBRACK + case '{': + tok = token.LBRACE + case '}': + tok = token.RBRACE + case ',': + tok = token.COMMA + case ':': + tok = token.COLON + case '-': + if isDecimal(s.peek()) { + ch := s.next() + tok = s.scanNumber(ch) + } else { + s.err("illegal char") + } + default: + s.err("illegal char: " + string(ch)) + } + } + + // finish token ending + s.tokEnd = s.srcPos.Offset + + // create token literal + var tokenText string + if s.tokStart >= 0 { + tokenText = string(s.src[s.tokStart:s.tokEnd]) + } + s.tokStart = s.tokEnd // ensure idempotency of tokenText() call + + return token.Token{ + Type: tok, + Pos: s.tokPos, + Text: tokenText, + } +} + +// scanNumber scans a HCL number definition starting with the given rune +func (s *Scanner) scanNumber(ch rune) token.Type { + zero := ch == '0' + pos := s.srcPos + + s.scanMantissa(ch) + ch = s.next() // seek forward + if ch == 'e' || ch == 'E' { + ch = s.scanExponent(ch) + return token.FLOAT + } + + if ch == '.' { + ch = s.scanFraction(ch) + if ch == 'e' || ch == 'E' { + ch = s.next() + ch = s.scanExponent(ch) + } + return token.FLOAT + } + + if ch != eof { + s.unread() + } + + // If we have a larger number and this is zero, error + if zero && pos != s.srcPos { + s.err("numbers cannot start with 0") + } + + return token.NUMBER +} + +// scanMantissa scans the mantissa beginning from the rune. It returns the next +// non decimal rune. It's used to determine wheter it's a fraction or exponent. +func (s *Scanner) scanMantissa(ch rune) rune { + scanned := false + for isDecimal(ch) { + ch = s.next() + scanned = true + } + + if scanned && ch != eof { + s.unread() + } + return ch +} + +// scanFraction scans the fraction after the '.' rune +func (s *Scanner) scanFraction(ch rune) rune { + if ch == '.' { + ch = s.peek() // we peek just to see if we can move forward + ch = s.scanMantissa(ch) + } + return ch +} + +// scanExponent scans the remaining parts of an exponent after the 'e' or 'E' +// rune. +func (s *Scanner) scanExponent(ch rune) rune { + if ch == 'e' || ch == 'E' { + ch = s.next() + if ch == '-' || ch == '+' { + ch = s.next() + } + ch = s.scanMantissa(ch) + } + return ch +} + +// scanString scans a quoted string +func (s *Scanner) scanString() { + braces := 0 + for { + // '"' opening already consumed + // read character after quote + ch := s.next() + + if ch == '\n' || ch < 0 || ch == eof { + s.err("literal not terminated") + return + } + + if ch == '"' { + break + } + + // If we're going into a ${} then we can ignore quotes for awhile + if braces == 0 && ch == '$' && s.peek() == '{' { + braces++ + s.next() + } else if braces > 0 && ch == '{' { + braces++ + } + if braces > 0 && ch == '}' { + braces-- + } + + if ch == '\\' { + s.scanEscape() + } + } + + return +} + +// scanEscape scans an escape sequence +func (s *Scanner) scanEscape() rune { + // http://en.cppreference.com/w/cpp/language/escape + ch := s.next() // read character after '/' + switch ch { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': + // nothing to do + case '0', '1', '2', '3', '4', '5', '6', '7': + // octal notation + ch = s.scanDigits(ch, 8, 3) + case 'x': + // hexademical notation + ch = s.scanDigits(s.next(), 16, 2) + case 'u': + // universal character name + ch = s.scanDigits(s.next(), 16, 4) + case 'U': + // universal character name + ch = s.scanDigits(s.next(), 16, 8) + default: + s.err("illegal char escape") + } + return ch +} + +// scanDigits scans a rune with the given base for n times. For example an +// octal notation \184 would yield in scanDigits(ch, 8, 3) +func (s *Scanner) scanDigits(ch rune, base, n int) rune { + for n > 0 && digitVal(ch) < base { + ch = s.next() + n-- + } + if n > 0 { + s.err("illegal char escape") + } + + // we scanned all digits, put the last non digit char back + s.unread() + return ch +} + +// scanIdentifier scans an identifier and returns the literal string +func (s *Scanner) scanIdentifier() string { + offs := s.srcPos.Offset - s.lastCharLen + ch := s.next() + for isLetter(ch) || isDigit(ch) || ch == '-' { + ch = s.next() + } + + if ch != eof { + s.unread() // we got identifier, put back latest char + } + + return string(s.src[offs:s.srcPos.Offset]) +} + +// recentPosition returns the position of the character immediately after the +// character or token returned by the last call to Scan. +func (s *Scanner) recentPosition() (pos token.Pos) { + pos.Offset = s.srcPos.Offset - s.lastCharLen + switch { + case s.srcPos.Column > 0: + // common case: last character was not a '\n' + pos.Line = s.srcPos.Line + pos.Column = s.srcPos.Column + case s.lastLineLen > 0: + // last character was a '\n' + // (we cannot be at the beginning of the source + // since we have called next() at least once) + pos.Line = s.srcPos.Line - 1 + pos.Column = s.lastLineLen + default: + // at the beginning of the source + pos.Line = 1 + pos.Column = 1 + } + return +} + +// err prints the error of any scanning to s.Error function. If the function is +// not defined, by default it prints them to os.Stderr +func (s *Scanner) err(msg string) { + s.ErrorCount++ + pos := s.recentPosition() + + if s.Error != nil { + s.Error(pos, msg) + return + } + + fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) +} + +// isHexadecimal returns true if the given rune is a letter +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +} + +// isHexadecimal returns true if the given rune is a decimal digit +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) +} + +// isHexadecimal returns true if the given rune is a decimal number +func isDecimal(ch rune) bool { + return '0' <= ch && ch <= '9' +} + +// isHexadecimal returns true if the given rune is an hexadecimal number +func isHexadecimal(ch rune) bool { + return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' +} + +// isWhitespace returns true if the rune is a space, tab, newline or carriage return +func isWhitespace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' +} + +// digitVal returns the integer value of a given octal,decimal or hexadecimal rune +func digitVal(ch rune) int { + switch { + case '0' <= ch && ch <= '9': + return int(ch - '0') + case 'a' <= ch && ch <= 'f': + return int(ch - 'a' + 10) + case 'A' <= ch && ch <= 'F': + return int(ch - 'A' + 10) + } + return 16 // larger than any legal digit val +} diff --git a/vendor/github.com/hashicorp/hcl/json/token/position.go b/vendor/github.com/hashicorp/hcl/json/token/position.go new file mode 100644 index 0000000..59c1bb7 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/json/token/position.go @@ -0,0 +1,46 @@ +package token + +import "fmt" + +// Pos describes an arbitrary source position +// including the file, line, and column location. +// A Position is valid if the line number is > 0. +type Pos struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (character count) +} + +// IsValid returns true if the position is valid. +func (p *Pos) IsValid() bool { return p.Line > 0 } + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// line:column valid position without file name +// file invalid position with file name +// - invalid position without file name +func (p Pos) String() string { + s := p.Filename + if p.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d:%d", p.Line, p.Column) + } + if s == "" { + s = "-" + } + return s +} + +// Before reports whether the position p is before u. +func (p Pos) Before(u Pos) bool { + return u.Offset > p.Offset || u.Line > p.Line +} + +// After reports whether the position p is after u. +func (p Pos) After(u Pos) bool { + return u.Offset < p.Offset || u.Line < p.Line +} diff --git a/vendor/github.com/hashicorp/hcl/json/token/token.go b/vendor/github.com/hashicorp/hcl/json/token/token.go new file mode 100644 index 0000000..95a0c3e --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/json/token/token.go @@ -0,0 +1,118 @@ +package token + +import ( + "fmt" + "strconv" + + hcltoken "github.com/hashicorp/hcl/hcl/token" +) + +// Token defines a single HCL token which can be obtained via the Scanner +type Token struct { + Type Type + Pos Pos + Text string +} + +// Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) +type Type int + +const ( + // Special tokens + ILLEGAL Type = iota + EOF + + identifier_beg + literal_beg + NUMBER // 12345 + FLOAT // 123.45 + BOOL // true,false + STRING // "abc" + NULL // null + literal_end + identifier_end + + operator_beg + LBRACK // [ + LBRACE // { + COMMA // , + PERIOD // . + COLON // : + + RBRACK // ] + RBRACE // } + + operator_end +) + +var tokens = [...]string{ + ILLEGAL: "ILLEGAL", + + EOF: "EOF", + + NUMBER: "NUMBER", + FLOAT: "FLOAT", + BOOL: "BOOL", + STRING: "STRING", + NULL: "NULL", + + LBRACK: "LBRACK", + LBRACE: "LBRACE", + COMMA: "COMMA", + PERIOD: "PERIOD", + COLON: "COLON", + + RBRACK: "RBRACK", + RBRACE: "RBRACE", +} + +// String returns the string corresponding to the token tok. +func (t Type) String() string { + s := "" + if 0 <= t && t < Type(len(tokens)) { + s = tokens[t] + } + if s == "" { + s = "token(" + strconv.Itoa(int(t)) + ")" + } + return s +} + +// IsIdentifier returns true for tokens corresponding to identifiers and basic +// type literals; it returns false otherwise. +func (t Type) IsIdentifier() bool { return identifier_beg < t && t < identifier_end } + +// IsLiteral returns true for tokens corresponding to basic type literals; it +// returns false otherwise. +func (t Type) IsLiteral() bool { return literal_beg < t && t < literal_end } + +// IsOperator returns true for tokens corresponding to operators and +// delimiters; it returns false otherwise. +func (t Type) IsOperator() bool { return operator_beg < t && t < operator_end } + +// String returns the token's literal text. Note that this is only +// applicable for certain token types, such as token.IDENT, +// token.STRING, etc.. +func (t Token) String() string { + return fmt.Sprintf("%s %s %s", t.Pos.String(), t.Type.String(), t.Text) +} + +// HCLToken converts this token to an HCL token. +// +// The token type must be a literal type or this will panic. +func (t Token) HCLToken() hcltoken.Token { + switch t.Type { + case BOOL: + return hcltoken.Token{Type: hcltoken.BOOL, Text: t.Text} + case FLOAT: + return hcltoken.Token{Type: hcltoken.FLOAT, Text: t.Text} + case NULL: + return hcltoken.Token{Type: hcltoken.STRING, Text: ""} + case NUMBER: + return hcltoken.Token{Type: hcltoken.NUMBER, Text: t.Text} + case STRING: + return hcltoken.Token{Type: hcltoken.STRING, Text: t.Text, JSON: true} + default: + panic(fmt.Sprintf("unimplemented HCLToken for type: %s", t.Type)) + } +} diff --git a/vendor/github.com/hashicorp/hcl/lex.go b/vendor/github.com/hashicorp/hcl/lex.go new file mode 100644 index 0000000..d9993c2 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/lex.go @@ -0,0 +1,38 @@ +package hcl + +import ( + "unicode" + "unicode/utf8" +) + +type lexModeValue byte + +const ( + lexModeUnknown lexModeValue = iota + lexModeHcl + lexModeJson +) + +// lexMode returns whether we're going to be parsing in JSON +// mode or HCL mode. +func lexMode(v []byte) lexModeValue { + var ( + r rune + w int + offset int + ) + + for { + r, w = utf8.DecodeRune(v[offset:]) + offset += w + if unicode.IsSpace(r) { + continue + } + if r == '{' { + return lexModeJson + } + break + } + + return lexModeHcl +} diff --git a/vendor/github.com/hashicorp/hcl/parse.go b/vendor/github.com/hashicorp/hcl/parse.go new file mode 100644 index 0000000..1fca53c --- /dev/null +++ b/vendor/github.com/hashicorp/hcl/parse.go @@ -0,0 +1,39 @@ +package hcl + +import ( + "fmt" + + "github.com/hashicorp/hcl/hcl/ast" + hclParser "github.com/hashicorp/hcl/hcl/parser" + jsonParser "github.com/hashicorp/hcl/json/parser" +) + +// ParseBytes accepts as input byte slice and returns ast tree. +// +// Input can be either JSON or HCL +func ParseBytes(in []byte) (*ast.File, error) { + return parse(in) +} + +// ParseString accepts input as a string and returns ast tree. +func ParseString(input string) (*ast.File, error) { + return parse([]byte(input)) +} + +func parse(in []byte) (*ast.File, error) { + switch lexMode(in) { + case lexModeHcl: + return hclParser.Parse(in) + case lexModeJson: + return jsonParser.Parse(in) + } + + return nil, fmt.Errorf("unknown config format") +} + +// Parse parses the given input and returns the root object. +// +// The input format can be either HCL or JSON. +func Parse(input string) (*ast.File, error) { + return parse([]byte(input)) +} diff --git a/vendor/github.com/influxdata/influxdb/LICENSE b/vendor/github.com/influxdata/influxdb/LICENSE new file mode 100644 index 0000000..63cef79 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 Errplane Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md b/vendor/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md new file mode 100644 index 0000000..b5b0df8 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/LICENSE_OF_DEPENDENCIES.md @@ -0,0 +1,63 @@ +- # List +- bootstrap 3.3.5 [MIT LICENSE](https://github.com/twbs/bootstrap/blob/master/LICENSE) +- collectd.org [ISC LICENSE](https://github.com/collectd/go-collectd/blob/master/LICENSE) +- github.com/BurntSushi/toml [MIT LICENSE](https://github.com/BurntSushi/toml/blob/master/COPYING) +- github.com/RoaringBitmap/roaring [APACHE LICENSE](https://github.com/RoaringBitmap/roaring/blob/master/LICENSE) +- github.com/beorn7/perks [MIT LICENSE](https://github.com/beorn7/perks/blob/master/LICENSE) +- github.com/bmizerany/pat [MIT LICENSE](https://github.com/bmizerany/pat#license) +- github.com/boltdb/bolt [MIT LICENSE](https://github.com/boltdb/bolt/blob/master/LICENSE) +- github.com/cespare/xxhash [MIT LICENSE](https://github.com/cespare/xxhash/blob/master/LICENSE.txt) +- github.com/clarkduvall/hyperloglog [MIT LICENSE](https://github.com/clarkduvall/hyperloglog/blob/master/LICENSE) +- github.com/davecgh/go-spew/spew [ISC LICENSE](https://github.com/davecgh/go-spew/blob/master/LICENSE) +- github.com/dgrijalva/jwt-go [MIT LICENSE](https://github.com/dgrijalva/jwt-go/blob/master/LICENSE) +- github.com/dgryski/go-bits [MIT LICENSE](https://github.com/dgryski/go-bits/blob/master/LICENSE) +- github.com/dgryski/go-bitstream [MIT LICENSE](https://github.com/dgryski/go-bitstream/blob/master/LICENSE) +- github.com/glycerine/go-unsnap-stream [MIT LICENSE](https://github.com/glycerine/go-unsnap-stream/blob/master/LICENSE) +- github.com/gogo/protobuf/proto [BSD LICENSE](https://github.com/gogo/protobuf/blob/master/LICENSE) +- github.com/golang/protobuf [BSD LICENSE](https://github.com/golang/protobuf/blob/master/LICENSE) +- github.com/golang/snappy [BSD LICENSE](https://github.com/golang/snappy/blob/master/LICENSE) +- github.com/google/go-cmp [BSD LICENSE](https://github.com/google/go-cmp/blob/master/LICENSE) +- github.com/influxdata/influxql [MIT LICENSE](https://github.com/influxdata/influxql/blob/master/LICENSE) +- github.com/influxdata/usage-client [MIT LICENSE](https://github.com/influxdata/usage-client/blob/master/LICENSE.txt) +- github.com/influxdata/yamux [MOZILLA PUBLIC LICENSE](https://github.com/influxdata/yamux/blob/master/LICENSE) +- github.com/influxdata/yarpc [MIT LICENSE](https://github.com/influxdata/yarpc/blob/master/LICENSE) +- github.com/jsternberg/zap-logfmt [MIT LICENSE](https://github.com/jsternberg/zap-logfmt/blob/master/LICENSE) +- github.com/jwilder/encoding [MIT LICENSE](https://github.com/jwilder/encoding/blob/master/LICENSE) +- github.com/klauspost/pgzip [MIT LICENSE](https://github.com/klauspost/pgzip/blob/master/LICENSE) +- github.com/mattn/go-isatty [MIT LICENSE](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- github.com/matttproud/golang_protobuf_extensions [APACHE LICENSE](https://github.com/matttproud/golang_protobuf_extensions/blob/master/LICENSE) +- github.com/opentracing/opentracing-go [MIT LICENSE](https://github.com/opentracing/opentracing-go/blob/master/LICENSE) +- github.com/paulbellamy/ratecounter [MIT LICENSE](https://github.com/paulbellamy/ratecounter/blob/master/LICENSE) +- github.com/peterh/liner [MIT LICENSE](https://github.com/peterh/liner/blob/master/COPYING) +- github.com/philhofer/fwd [MIT LICENSE](https://github.com/philhofer/fwd/blob/master/LICENSE.md) +- github.com/prometheus/client_golang [MIT LICENSE](https://github.com/prometheus/client_golang/blob/master/LICENSE) +- github.com/prometheus/client_model [MIT LICENSE](https://github.com/prometheus/client_model/blob/master/LICENSE) +- github.com/prometheus/common [APACHE LICENSE](https://github.com/prometheus/common/blob/master/LICENSE) +- github.com/prometheus/procfs [APACHE LICENSE](https://github.com/prometheus/procfs/blob/master/LICENSE) +- github.com/rakyll/statik [APACHE LICENSE](https://github.com/rakyll/statik/blob/master/LICENSE) +- github.com/retailnext/hllpp [BSD LICENSE](https://github.com/retailnext/hllpp/blob/master/LICENSE) +- github.com/tinylib/msgp [MIT LICENSE](https://github.com/tinylib/msgp/blob/master/LICENSE) +- go.uber.org/atomic [MIT LICENSE](https://github.com/uber-go/atomic/blob/master/LICENSE.txt) +- go.uber.org/multierr [MIT LICENSE](https://github.com/uber-go/multierr/blob/master/LICENSE.txt) +- go.uber.org/zap [MIT LICENSE](https://github.com/uber-go/zap/blob/master/LICENSE.txt) +- golang.org/x/crypto [BSD LICENSE](https://github.com/golang/crypto/blob/master/LICENSE) +- golang.org/x/net [BSD LICENSE](https://github.com/golang/net/blob/master/LICENSE) +- golang.org/x/sys [BSD LICENSE](https://github.com/golang/sys/blob/master/LICENSE) +- golang.org/x/text [BSD LICENSE](https://github.com/golang/text/blob/master/LICENSE) +- golang.org/x/time [BSD LICENSE](https://github.com/golang/time/blob/master/LICENSE) +- jquery 2.1.4 [MIT LICENSE](https://github.com/jquery/jquery/blob/master/LICENSE.txt) +- github.com/xlab/treeprint [MIT LICENSE](https://github.com/xlab/treeprint/blob/master/LICENSE) + + + + + + + + + + + + + + diff --git a/vendor/github.com/influxdata/influxdb/client/v2/client.go b/vendor/github.com/influxdata/influxdb/client/v2/client.go new file mode 100644 index 0000000..6a5c238 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/client/v2/client.go @@ -0,0 +1,662 @@ +// Package client (v2) is the current official Go client for InfluxDB. +package client // import "github.com/influxdata/influxdb/client/v2" + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "net/url" + "path" + "strconv" + "strings" + "time" + + "github.com/influxdata/influxdb/models" +) + +// HTTPConfig is the config data needed to create an HTTP Client. +type HTTPConfig struct { + // Addr should be of the form "http://host:port" + // or "http://[ipv6-host%zone]:port". + Addr string + + // Username is the influxdb username, optional. + Username string + + // Password is the influxdb password, optional. + Password string + + // UserAgent is the http User Agent, defaults to "InfluxDBClient". + UserAgent string + + // Timeout for influxdb writes, defaults to no timeout. + Timeout time.Duration + + // InsecureSkipVerify gets passed to the http client, if true, it will + // skip https certificate verification. Defaults to false. + InsecureSkipVerify bool + + // TLSConfig allows the user to set their own TLS config for the HTTP + // Client. If set, this option overrides InsecureSkipVerify. + TLSConfig *tls.Config + + // Proxy configures the Proxy function on the HTTP client. + Proxy func(req *http.Request) (*url.URL, error) +} + +// BatchPointsConfig is the config data needed to create an instance of the BatchPoints struct. +type BatchPointsConfig struct { + // Precision is the write precision of the points, defaults to "ns". + Precision string + + // Database is the database to write points to. + Database string + + // RetentionPolicy is the retention policy of the points. + RetentionPolicy string + + // Write consistency is the number of servers required to confirm write. + WriteConsistency string +} + +// Client is a client interface for writing & querying the database. +type Client interface { + // Ping checks that status of cluster, and will always return 0 time and no + // error for UDP clients. + Ping(timeout time.Duration) (time.Duration, string, error) + + // Write takes a BatchPoints object and writes all Points to InfluxDB. + Write(bp BatchPoints) error + + // Query makes an InfluxDB Query on the database. This will fail if using + // the UDP client. + Query(q Query) (*Response, error) + + // Close releases any resources a Client may be using. + Close() error +} + +// NewHTTPClient returns a new Client from the provided config. +// Client is safe for concurrent use by multiple goroutines. +func NewHTTPClient(conf HTTPConfig) (Client, error) { + if conf.UserAgent == "" { + conf.UserAgent = "InfluxDBClient" + } + + u, err := url.Parse(conf.Addr) + if err != nil { + return nil, err + } else if u.Scheme != "http" && u.Scheme != "https" { + m := fmt.Sprintf("Unsupported protocol scheme: %s, your address"+ + " must start with http:// or https://", u.Scheme) + return nil, errors.New(m) + } + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: conf.InsecureSkipVerify, + }, + Proxy: conf.Proxy, + } + if conf.TLSConfig != nil { + tr.TLSClientConfig = conf.TLSConfig + } + return &client{ + url: *u, + username: conf.Username, + password: conf.Password, + useragent: conf.UserAgent, + httpClient: &http.Client{ + Timeout: conf.Timeout, + Transport: tr, + }, + transport: tr, + }, nil +} + +// Ping will check to see if the server is up with an optional timeout on waiting for leader. +// Ping returns how long the request took, the version of the server it connected to, and an error if one occurred. +func (c *client) Ping(timeout time.Duration) (time.Duration, string, error) { + now := time.Now() + + u := c.url + u.Path = path.Join(u.Path, "ping") + + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return 0, "", err + } + + req.Header.Set("User-Agent", c.useragent) + + if c.username != "" { + req.SetBasicAuth(c.username, c.password) + } + + if timeout > 0 { + params := req.URL.Query() + params.Set("wait_for_leader", fmt.Sprintf("%.0fs", timeout.Seconds())) + req.URL.RawQuery = params.Encode() + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return 0, "", err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return 0, "", err + } + + if resp.StatusCode != http.StatusNoContent { + var err = fmt.Errorf(string(body)) + return 0, "", err + } + + version := resp.Header.Get("X-Influxdb-Version") + return time.Since(now), version, nil +} + +// Close releases the client's resources. +func (c *client) Close() error { + c.transport.CloseIdleConnections() + return nil +} + +// client is safe for concurrent use as the fields are all read-only +// once the client is instantiated. +type client struct { + // N.B - if url.UserInfo is accessed in future modifications to the + // methods on client, you will need to synchronize access to url. + url url.URL + username string + password string + useragent string + httpClient *http.Client + transport *http.Transport +} + +// BatchPoints is an interface into a batched grouping of points to write into +// InfluxDB together. BatchPoints is NOT thread-safe, you must create a separate +// batch for each goroutine. +type BatchPoints interface { + // AddPoint adds the given point to the Batch of points. + AddPoint(p *Point) + // AddPoints adds the given points to the Batch of points. + AddPoints(ps []*Point) + // Points lists the points in the Batch. + Points() []*Point + + // Precision returns the currently set precision of this Batch. + Precision() string + // SetPrecision sets the precision of this batch. + SetPrecision(s string) error + + // Database returns the currently set database of this Batch. + Database() string + // SetDatabase sets the database of this Batch. + SetDatabase(s string) + + // WriteConsistency returns the currently set write consistency of this Batch. + WriteConsistency() string + // SetWriteConsistency sets the write consistency of this Batch. + SetWriteConsistency(s string) + + // RetentionPolicy returns the currently set retention policy of this Batch. + RetentionPolicy() string + // SetRetentionPolicy sets the retention policy of this Batch. + SetRetentionPolicy(s string) +} + +// NewBatchPoints returns a BatchPoints interface based on the given config. +func NewBatchPoints(conf BatchPointsConfig) (BatchPoints, error) { + if conf.Precision == "" { + conf.Precision = "ns" + } + if _, err := time.ParseDuration("1" + conf.Precision); err != nil { + return nil, err + } + bp := &batchpoints{ + database: conf.Database, + precision: conf.Precision, + retentionPolicy: conf.RetentionPolicy, + writeConsistency: conf.WriteConsistency, + } + return bp, nil +} + +type batchpoints struct { + points []*Point + database string + precision string + retentionPolicy string + writeConsistency string +} + +func (bp *batchpoints) AddPoint(p *Point) { + bp.points = append(bp.points, p) +} + +func (bp *batchpoints) AddPoints(ps []*Point) { + bp.points = append(bp.points, ps...) +} + +func (bp *batchpoints) Points() []*Point { + return bp.points +} + +func (bp *batchpoints) Precision() string { + return bp.precision +} + +func (bp *batchpoints) Database() string { + return bp.database +} + +func (bp *batchpoints) WriteConsistency() string { + return bp.writeConsistency +} + +func (bp *batchpoints) RetentionPolicy() string { + return bp.retentionPolicy +} + +func (bp *batchpoints) SetPrecision(p string) error { + if _, err := time.ParseDuration("1" + p); err != nil { + return err + } + bp.precision = p + return nil +} + +func (bp *batchpoints) SetDatabase(db string) { + bp.database = db +} + +func (bp *batchpoints) SetWriteConsistency(wc string) { + bp.writeConsistency = wc +} + +func (bp *batchpoints) SetRetentionPolicy(rp string) { + bp.retentionPolicy = rp +} + +// Point represents a single data point. +type Point struct { + pt models.Point +} + +// NewPoint returns a point with the given timestamp. If a timestamp is not +// given, then data is sent to the database without a timestamp, in which case +// the server will assign local time upon reception. NOTE: it is recommended to +// send data with a timestamp. +func NewPoint( + name string, + tags map[string]string, + fields map[string]interface{}, + t ...time.Time, +) (*Point, error) { + var T time.Time + if len(t) > 0 { + T = t[0] + } + + pt, err := models.NewPoint(name, models.NewTags(tags), fields, T) + if err != nil { + return nil, err + } + return &Point{ + pt: pt, + }, nil +} + +// String returns a line-protocol string of the Point. +func (p *Point) String() string { + return p.pt.String() +} + +// PrecisionString returns a line-protocol string of the Point, +// with the timestamp formatted for the given precision. +func (p *Point) PrecisionString(precision string) string { + return p.pt.PrecisionString(precision) +} + +// Name returns the measurement name of the point. +func (p *Point) Name() string { + return string(p.pt.Name()) +} + +// Tags returns the tags associated with the point. +func (p *Point) Tags() map[string]string { + return p.pt.Tags().Map() +} + +// Time return the timestamp for the point. +func (p *Point) Time() time.Time { + return p.pt.Time() +} + +// UnixNano returns timestamp of the point in nanoseconds since Unix epoch. +func (p *Point) UnixNano() int64 { + return p.pt.UnixNano() +} + +// Fields returns the fields for the point. +func (p *Point) Fields() (map[string]interface{}, error) { + return p.pt.Fields() +} + +// NewPointFrom returns a point from the provided models.Point. +func NewPointFrom(pt models.Point) *Point { + return &Point{pt: pt} +} + +func (c *client) Write(bp BatchPoints) error { + var b bytes.Buffer + + for _, p := range bp.Points() { + if p == nil { + continue + } + if _, err := b.WriteString(p.pt.PrecisionString(bp.Precision())); err != nil { + return err + } + + if err := b.WriteByte('\n'); err != nil { + return err + } + } + + u := c.url + u.Path = path.Join(u.Path, "write") + + req, err := http.NewRequest("POST", u.String(), &b) + if err != nil { + return err + } + req.Header.Set("Content-Type", "") + req.Header.Set("User-Agent", c.useragent) + if c.username != "" { + req.SetBasicAuth(c.username, c.password) + } + + params := req.URL.Query() + params.Set("db", bp.Database()) + params.Set("rp", bp.RetentionPolicy()) + params.Set("precision", bp.Precision()) + params.Set("consistency", bp.WriteConsistency()) + req.URL.RawQuery = params.Encode() + + resp, err := c.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { + var err = fmt.Errorf(string(body)) + return err + } + + return nil +} + +// Query defines a query to send to the server. +type Query struct { + Command string + Database string + RetentionPolicy string + Precision string + Chunked bool + ChunkSize int + Parameters map[string]interface{} +} + +// NewQuery returns a query object. +// The database and precision arguments can be empty strings if they are not needed for the query. +func NewQuery(command, database, precision string) Query { + return Query{ + Command: command, + Database: database, + Precision: precision, + Parameters: make(map[string]interface{}), + } +} + +// NewQueryWithRP returns a query object. +// The database, retention policy, and precision arguments can be empty strings if they are not needed +// for the query. Setting the retention policy only works on InfluxDB versions 1.6 or greater. +func NewQueryWithRP(command, database, retentionPolicy, precision string) Query { + return Query{ + Command: command, + Database: database, + RetentionPolicy: retentionPolicy, + Precision: precision, + Parameters: make(map[string]interface{}), + } +} + +// NewQueryWithParameters returns a query object. +// The database and precision arguments can be empty strings if they are not needed for the query. +// parameters is a map of the parameter names used in the command to their values. +func NewQueryWithParameters(command, database, precision string, parameters map[string]interface{}) Query { + return Query{ + Command: command, + Database: database, + Precision: precision, + Parameters: parameters, + } +} + +// Response represents a list of statement results. +type Response struct { + Results []Result + Err string `json:"error,omitempty"` +} + +// Error returns the first error from any statement. +// It returns nil if no errors occurred on any statements. +func (r *Response) Error() error { + if r.Err != "" { + return fmt.Errorf(r.Err) + } + for _, result := range r.Results { + if result.Err != "" { + return fmt.Errorf(result.Err) + } + } + return nil +} + +// Message represents a user message. +type Message struct { + Level string + Text string +} + +// Result represents a resultset returned from a single statement. +type Result struct { + Series []models.Row + Messages []*Message + Err string `json:"error,omitempty"` +} + +// Query sends a command to the server and returns the Response. +func (c *client) Query(q Query) (*Response, error) { + u := c.url + u.Path = path.Join(u.Path, "query") + + jsonParameters, err := json.Marshal(q.Parameters) + + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", u.String(), nil) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "") + req.Header.Set("User-Agent", c.useragent) + + if c.username != "" { + req.SetBasicAuth(c.username, c.password) + } + + params := req.URL.Query() + params.Set("q", q.Command) + params.Set("db", q.Database) + if q.RetentionPolicy != "" { + params.Set("rp", q.RetentionPolicy) + } + params.Set("params", string(jsonParameters)) + if q.Chunked { + params.Set("chunked", "true") + if q.ChunkSize > 0 { + params.Set("chunk_size", strconv.Itoa(q.ChunkSize)) + } + } + + if q.Precision != "" { + params.Set("epoch", q.Precision) + } + req.URL.RawQuery = params.Encode() + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // If we lack a X-Influxdb-Version header, then we didn't get a response from influxdb + // but instead some other service. If the error code is also a 500+ code, then some + // downstream loadbalancer/proxy/etc had an issue and we should report that. + if resp.Header.Get("X-Influxdb-Version") == "" && resp.StatusCode >= http.StatusInternalServerError { + body, err := ioutil.ReadAll(resp.Body) + if err != nil || len(body) == 0 { + return nil, fmt.Errorf("received status code %d from downstream server", resp.StatusCode) + } + + return nil, fmt.Errorf("received status code %d from downstream server, with response body: %q", resp.StatusCode, body) + } + + // If we get an unexpected content type, then it is also not from influx direct and therefore + // we want to know what we received and what status code was returned for debugging purposes. + if cType, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type")); cType != "application/json" { + // Read up to 1kb of the body to help identify downstream errors and limit the impact of things + // like downstream serving a large file + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024)) + if err != nil || len(body) == 0 { + return nil, fmt.Errorf("expected json response, got empty body, with status: %v", resp.StatusCode) + } + + return nil, fmt.Errorf("expected json response, got %q, with status: %v and response body: %q", cType, resp.StatusCode, body) + } + + var response Response + if q.Chunked { + cr := NewChunkedResponse(resp.Body) + for { + r, err := cr.NextResponse() + if err != nil { + // If we got an error while decoding the response, send that back. + return nil, err + } + + if r == nil { + break + } + + response.Results = append(response.Results, r.Results...) + if r.Err != "" { + response.Err = r.Err + break + } + } + } else { + dec := json.NewDecoder(resp.Body) + dec.UseNumber() + decErr := dec.Decode(&response) + + // ignore this error if we got an invalid status code + if decErr != nil && decErr.Error() == "EOF" && resp.StatusCode != http.StatusOK { + decErr = nil + } + // If we got a valid decode error, send that back + if decErr != nil { + return nil, fmt.Errorf("unable to decode json: received status code %d err: %s", resp.StatusCode, decErr) + } + } + + // If we don't have an error in our json response, and didn't get statusOK + // then send back an error + if resp.StatusCode != http.StatusOK && response.Error() == nil { + return &response, fmt.Errorf("received status code %d from server", resp.StatusCode) + } + return &response, nil +} + +// duplexReader reads responses and writes it to another writer while +// satisfying the reader interface. +type duplexReader struct { + r io.Reader + w io.Writer +} + +func (r *duplexReader) Read(p []byte) (n int, err error) { + n, err = r.r.Read(p) + if err == nil { + r.w.Write(p[:n]) + } + return n, err +} + +// ChunkedResponse represents a response from the server that +// uses chunking to stream the output. +type ChunkedResponse struct { + dec *json.Decoder + duplex *duplexReader + buf bytes.Buffer +} + +// NewChunkedResponse reads a stream and produces responses from the stream. +func NewChunkedResponse(r io.Reader) *ChunkedResponse { + resp := &ChunkedResponse{} + resp.duplex = &duplexReader{r: r, w: &resp.buf} + resp.dec = json.NewDecoder(resp.duplex) + resp.dec.UseNumber() + return resp +} + +// NextResponse reads the next line of the stream and returns a response. +func (r *ChunkedResponse) NextResponse() (*Response, error) { + var response Response + + if err := r.dec.Decode(&response); err != nil { + if err == io.EOF { + return nil, nil + } + // A decoding error happened. This probably means the server crashed + // and sent a last-ditch error message to us. Ensure we have read the + // entirety of the connection to get any remaining error text. + io.Copy(ioutil.Discard, r.duplex) + return nil, errors.New(strings.TrimSpace(r.buf.String())) + } + + r.buf.Reset() + return &response, nil +} diff --git a/vendor/github.com/influxdata/influxdb/client/v2/udp.go b/vendor/github.com/influxdata/influxdb/client/v2/udp.go new file mode 100644 index 0000000..779a28b --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/client/v2/udp.go @@ -0,0 +1,112 @@ +package client + +import ( + "fmt" + "io" + "net" + "time" +) + +const ( + // UDPPayloadSize is a reasonable default payload size for UDP packets that + // could be travelling over the internet. + UDPPayloadSize = 512 +) + +// UDPConfig is the config data needed to create a UDP Client. +type UDPConfig struct { + // Addr should be of the form "host:port" + // or "[ipv6-host%zone]:port". + Addr string + + // PayloadSize is the maximum size of a UDP client message, optional + // Tune this based on your network. Defaults to UDPPayloadSize. + PayloadSize int +} + +// NewUDPClient returns a client interface for writing to an InfluxDB UDP +// service from the given config. +func NewUDPClient(conf UDPConfig) (Client, error) { + var udpAddr *net.UDPAddr + udpAddr, err := net.ResolveUDPAddr("udp", conf.Addr) + if err != nil { + return nil, err + } + + conn, err := net.DialUDP("udp", nil, udpAddr) + if err != nil { + return nil, err + } + + payloadSize := conf.PayloadSize + if payloadSize == 0 { + payloadSize = UDPPayloadSize + } + + return &udpclient{ + conn: conn, + payloadSize: payloadSize, + }, nil +} + +// Close releases the udpclient's resources. +func (uc *udpclient) Close() error { + return uc.conn.Close() +} + +type udpclient struct { + conn io.WriteCloser + payloadSize int +} + +func (uc *udpclient) Write(bp BatchPoints) error { + var b = make([]byte, 0, uc.payloadSize) // initial buffer size, it will grow as needed + var d, _ = time.ParseDuration("1" + bp.Precision()) + + var delayedError error + + var checkBuffer = func(n int) { + if len(b) > 0 && len(b)+n > uc.payloadSize { + if _, err := uc.conn.Write(b); err != nil { + delayedError = err + } + b = b[:0] + } + } + + for _, p := range bp.Points() { + p.pt.Round(d) + pointSize := p.pt.StringSize() + 1 // include newline in size + //point := p.pt.RoundedString(d) + "\n" + + checkBuffer(pointSize) + + if p.Time().IsZero() || pointSize <= uc.payloadSize { + b = p.pt.AppendString(b) + b = append(b, '\n') + continue + } + + points := p.pt.Split(uc.payloadSize - 1) // account for newline character + for _, sp := range points { + checkBuffer(sp.StringSize() + 1) + b = sp.AppendString(b) + b = append(b, '\n') + } + } + + if len(b) > 0 { + if _, err := uc.conn.Write(b); err != nil { + return err + } + } + return delayedError +} + +func (uc *udpclient) Query(q Query) (*Response, error) { + return nil, fmt.Errorf("Querying via UDP is not supported") +} + +func (uc *udpclient) Ping(timeout time.Duration) (time.Duration, string, error) { + return 0, "", nil +} diff --git a/vendor/github.com/influxdata/influxdb/models/consistency.go b/vendor/github.com/influxdata/influxdb/models/consistency.go new file mode 100644 index 0000000..2a3269b --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/consistency.go @@ -0,0 +1,48 @@ +package models + +import ( + "errors" + "strings" +) + +// ConsistencyLevel represent a required replication criteria before a write can +// be returned as successful. +// +// The consistency level is handled in open-source InfluxDB but only applicable to clusters. +type ConsistencyLevel int + +const ( + // ConsistencyLevelAny allows for hinted handoff, potentially no write happened yet. + ConsistencyLevelAny ConsistencyLevel = iota + + // ConsistencyLevelOne requires at least one data node acknowledged a write. + ConsistencyLevelOne + + // ConsistencyLevelQuorum requires a quorum of data nodes to acknowledge a write. + ConsistencyLevelQuorum + + // ConsistencyLevelAll requires all data nodes to acknowledge a write. + ConsistencyLevelAll +) + +var ( + // ErrInvalidConsistencyLevel is returned when parsing the string version + // of a consistency level. + ErrInvalidConsistencyLevel = errors.New("invalid consistency level") +) + +// ParseConsistencyLevel converts a consistency level string to the corresponding ConsistencyLevel const. +func ParseConsistencyLevel(level string) (ConsistencyLevel, error) { + switch strings.ToLower(level) { + case "any": + return ConsistencyLevelAny, nil + case "one": + return ConsistencyLevelOne, nil + case "quorum": + return ConsistencyLevelQuorum, nil + case "all": + return ConsistencyLevelAll, nil + default: + return 0, ErrInvalidConsistencyLevel + } +} diff --git a/vendor/github.com/influxdata/influxdb/models/inline_fnv.go b/vendor/github.com/influxdata/influxdb/models/inline_fnv.go new file mode 100644 index 0000000..eec1ae8 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/inline_fnv.go @@ -0,0 +1,32 @@ +package models // import "github.com/influxdata/influxdb/models" + +// from stdlib hash/fnv/fnv.go +const ( + prime64 = 1099511628211 + offset64 = 14695981039346656037 +) + +// InlineFNV64a is an alloc-free port of the standard library's fnv64a. +// See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function. +type InlineFNV64a uint64 + +// NewInlineFNV64a returns a new instance of InlineFNV64a. +func NewInlineFNV64a() InlineFNV64a { + return offset64 +} + +// Write adds data to the running hash. +func (s *InlineFNV64a) Write(data []byte) (int, error) { + hash := uint64(*s) + for _, c := range data { + hash ^= uint64(c) + hash *= prime64 + } + *s = InlineFNV64a(hash) + return len(data), nil +} + +// Sum64 returns the uint64 of the current resulting hash. +func (s *InlineFNV64a) Sum64() uint64 { + return uint64(*s) +} diff --git a/vendor/github.com/influxdata/influxdb/models/inline_strconv_parse.go b/vendor/github.com/influxdata/influxdb/models/inline_strconv_parse.go new file mode 100644 index 0000000..8db4837 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/inline_strconv_parse.go @@ -0,0 +1,44 @@ +package models // import "github.com/influxdata/influxdb/models" + +import ( + "reflect" + "strconv" + "unsafe" +) + +// parseIntBytes is a zero-alloc wrapper around strconv.ParseInt. +func parseIntBytes(b []byte, base int, bitSize int) (i int64, err error) { + s := unsafeBytesToString(b) + return strconv.ParseInt(s, base, bitSize) +} + +// parseUintBytes is a zero-alloc wrapper around strconv.ParseUint. +func parseUintBytes(b []byte, base int, bitSize int) (i uint64, err error) { + s := unsafeBytesToString(b) + return strconv.ParseUint(s, base, bitSize) +} + +// parseFloatBytes is a zero-alloc wrapper around strconv.ParseFloat. +func parseFloatBytes(b []byte, bitSize int) (float64, error) { + s := unsafeBytesToString(b) + return strconv.ParseFloat(s, bitSize) +} + +// parseBoolBytes is a zero-alloc wrapper around strconv.ParseBool. +func parseBoolBytes(b []byte) (bool, error) { + return strconv.ParseBool(unsafeBytesToString(b)) +} + +// unsafeBytesToString converts a []byte to a string without a heap allocation. +// +// It is unsafe, and is intended to prepare input to short-lived functions +// that require strings. +func unsafeBytesToString(in []byte) string { + src := *(*reflect.SliceHeader)(unsafe.Pointer(&in)) + dst := reflect.StringHeader{ + Data: src.Data, + Len: src.Len, + } + s := *(*string)(unsafe.Pointer(&dst)) + return s +} diff --git a/vendor/github.com/influxdata/influxdb/models/points.go b/vendor/github.com/influxdata/influxdb/models/points.go new file mode 100644 index 0000000..e919281 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/points.go @@ -0,0 +1,2463 @@ +// Package models implements basic objects used throughout the TICK stack. +package models // import "github.com/influxdata/influxdb/models" + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "sort" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" + + "github.com/influxdata/influxdb/pkg/escape" +) + +type escapeSet struct { + k [1]byte + esc [2]byte +} + +var ( + measurementEscapeCodes = [...]escapeSet{ + {k: [1]byte{','}, esc: [2]byte{'\\', ','}}, + {k: [1]byte{' '}, esc: [2]byte{'\\', ' '}}, + } + + tagEscapeCodes = [...]escapeSet{ + {k: [1]byte{','}, esc: [2]byte{'\\', ','}}, + {k: [1]byte{' '}, esc: [2]byte{'\\', ' '}}, + {k: [1]byte{'='}, esc: [2]byte{'\\', '='}}, + } + + // ErrPointMustHaveAField is returned when operating on a point that does not have any fields. + ErrPointMustHaveAField = errors.New("point without fields is unsupported") + + // ErrInvalidNumber is returned when a number is expected but not provided. + ErrInvalidNumber = errors.New("invalid number") + + // ErrInvalidPoint is returned when a point cannot be parsed correctly. + ErrInvalidPoint = errors.New("point is invalid") +) + +const ( + // MaxKeyLength is the largest allowed size of the combined measurement and tag keys. + MaxKeyLength = 65535 +) + +// enableUint64Support will enable uint64 support if set to true. +var enableUint64Support = false + +// EnableUintSupport manually enables uint support for the point parser. +// This function will be removed in the future and only exists for unit tests during the +// transition. +func EnableUintSupport() { + enableUint64Support = true +} + +// Point defines the values that will be written to the database. +type Point interface { + // Name return the measurement name for the point. + Name() []byte + + // SetName updates the measurement name for the point. + SetName(string) + + // Tags returns the tag set for the point. + Tags() Tags + + // ForEachTag iterates over each tag invoking fn. If fn return false, iteration stops. + ForEachTag(fn func(k, v []byte) bool) + + // AddTag adds or replaces a tag value for a point. + AddTag(key, value string) + + // SetTags replaces the tags for the point. + SetTags(tags Tags) + + // HasTag returns true if the tag exists for the point. + HasTag(tag []byte) bool + + // Fields returns the fields for the point. + Fields() (Fields, error) + + // Time return the timestamp for the point. + Time() time.Time + + // SetTime updates the timestamp for the point. + SetTime(t time.Time) + + // UnixNano returns the timestamp of the point as nanoseconds since Unix epoch. + UnixNano() int64 + + // HashID returns a non-cryptographic checksum of the point's key. + HashID() uint64 + + // Key returns the key (measurement joined with tags) of the point. + Key() []byte + + // String returns a string representation of the point. If there is a + // timestamp associated with the point then it will be specified with the default + // precision of nanoseconds. + String() string + + // MarshalBinary returns a binary representation of the point. + MarshalBinary() ([]byte, error) + + // PrecisionString returns a string representation of the point. If there + // is a timestamp associated with the point then it will be specified in the + // given unit. + PrecisionString(precision string) string + + // RoundedString returns a string representation of the point. If there + // is a timestamp associated with the point, then it will be rounded to the + // given duration. + RoundedString(d time.Duration) string + + // Split will attempt to return multiple points with the same timestamp whose + // string representations are no longer than size. Points with a single field or + // a point without a timestamp may exceed the requested size. + Split(size int) []Point + + // Round will round the timestamp of the point to the given duration. + Round(d time.Duration) + + // StringSize returns the length of the string that would be returned by String(). + StringSize() int + + // AppendString appends the result of String() to the provided buffer and returns + // the result, potentially reducing string allocations. + AppendString(buf []byte) []byte + + // FieldIterator retuns a FieldIterator that can be used to traverse the + // fields of a point without constructing the in-memory map. + FieldIterator() FieldIterator +} + +// FieldType represents the type of a field. +type FieldType int + +const ( + // Integer indicates the field's type is integer. + Integer FieldType = iota + + // Float indicates the field's type is float. + Float + + // Boolean indicates the field's type is boolean. + Boolean + + // String indicates the field's type is string. + String + + // Empty is used to indicate that there is no field. + Empty + + // Unsigned indicates the field's type is an unsigned integer. + Unsigned +) + +// FieldIterator provides a low-allocation interface to iterate through a point's fields. +type FieldIterator interface { + // Next indicates whether there any fields remaining. + Next() bool + + // FieldKey returns the key of the current field. + FieldKey() []byte + + // Type returns the FieldType of the current field. + Type() FieldType + + // StringValue returns the string value of the current field. + StringValue() string + + // IntegerValue returns the integer value of the current field. + IntegerValue() (int64, error) + + // UnsignedValue returns the unsigned value of the current field. + UnsignedValue() (uint64, error) + + // BooleanValue returns the boolean value of the current field. + BooleanValue() (bool, error) + + // FloatValue returns the float value of the current field. + FloatValue() (float64, error) + + // Reset resets the iterator to its initial state. + Reset() +} + +// Points represents a sortable list of points by timestamp. +type Points []Point + +// Len implements sort.Interface. +func (a Points) Len() int { return len(a) } + +// Less implements sort.Interface. +func (a Points) Less(i, j int) bool { return a[i].Time().Before(a[j].Time()) } + +// Swap implements sort.Interface. +func (a Points) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// point is the default implementation of Point. +type point struct { + time time.Time + + // text encoding of measurement and tags + // key must always be stored sorted by tags, if the original line was not sorted, + // we need to resort it + key []byte + + // text encoding of field data + fields []byte + + // text encoding of timestamp + ts []byte + + // cached version of parsed fields from data + cachedFields map[string]interface{} + + // cached version of parsed name from key + cachedName string + + // cached version of parsed tags + cachedTags Tags + + it fieldIterator +} + +// type assertions +var ( + _ Point = (*point)(nil) + _ FieldIterator = (*point)(nil) +) + +const ( + // the number of characters for the largest possible int64 (9223372036854775807) + maxInt64Digits = 19 + + // the number of characters for the smallest possible int64 (-9223372036854775808) + minInt64Digits = 20 + + // the number of characters for the largest possible uint64 (18446744073709551615) + maxUint64Digits = 20 + + // the number of characters required for the largest float64 before a range check + // would occur during parsing + maxFloat64Digits = 25 + + // the number of characters required for smallest float64 before a range check occur + // would occur during parsing + minFloat64Digits = 27 +) + +// ParsePoints returns a slice of Points from a text representation of a point +// with each point separated by newlines. If any points fail to parse, a non-nil error +// will be returned in addition to the points that parsed successfully. +func ParsePoints(buf []byte) ([]Point, error) { + return ParsePointsWithPrecision(buf, time.Now().UTC(), "n") +} + +// ParsePointsString is identical to ParsePoints but accepts a string. +func ParsePointsString(buf string) ([]Point, error) { + return ParsePoints([]byte(buf)) +} + +// ParseKey returns the measurement name and tags from a point. +// +// NOTE: to minimize heap allocations, the returned Tags will refer to subslices of buf. +// This can have the unintended effect preventing buf from being garbage collected. +func ParseKey(buf []byte) (string, Tags) { + name, tags := ParseKeyBytes(buf) + return string(name), tags +} + +func ParseKeyBytes(buf []byte) ([]byte, Tags) { + return ParseKeyBytesWithTags(buf, nil) +} + +func ParseKeyBytesWithTags(buf []byte, tags Tags) ([]byte, Tags) { + // Ignore the error because scanMeasurement returns "missing fields" which we ignore + // when just parsing a key + state, i, _ := scanMeasurement(buf, 0) + + var name []byte + if state == tagKeyState { + tags = parseTags(buf, tags) + // scanMeasurement returns the location of the comma if there are tags, strip that off + name = buf[:i-1] + } else { + name = buf[:i] + } + return unescapeMeasurement(name), tags +} + +func ParseTags(buf []byte) Tags { + return parseTags(buf, nil) +} + +func ParseName(buf []byte) []byte { + // Ignore the error because scanMeasurement returns "missing fields" which we ignore + // when just parsing a key + state, i, _ := scanMeasurement(buf, 0) + var name []byte + if state == tagKeyState { + name = buf[:i-1] + } else { + name = buf[:i] + } + + return unescapeMeasurement(name) +} + +// ParsePointsWithPrecision is similar to ParsePoints, but allows the +// caller to provide a precision for time. +// +// NOTE: to minimize heap allocations, the returned Points will refer to subslices of buf. +// This can have the unintended effect preventing buf from being garbage collected. +func ParsePointsWithPrecision(buf []byte, defaultTime time.Time, precision string) ([]Point, error) { + points := make([]Point, 0, bytes.Count(buf, []byte{'\n'})+1) + var ( + pos int + block []byte + failed []string + ) + for pos < len(buf) { + pos, block = scanLine(buf, pos) + pos++ + + if len(block) == 0 { + continue + } + + // lines which start with '#' are comments + start := skipWhitespace(block, 0) + + // If line is all whitespace, just skip it + if start >= len(block) { + continue + } + + if block[start] == '#' { + continue + } + + // strip the newline if one is present + if block[len(block)-1] == '\n' { + block = block[:len(block)-1] + } + + pt, err := parsePoint(block[start:], defaultTime, precision) + if err != nil { + failed = append(failed, fmt.Sprintf("unable to parse '%s': %v", string(block[start:]), err)) + } else { + points = append(points, pt) + } + + } + if len(failed) > 0 { + return points, fmt.Errorf("%s", strings.Join(failed, "\n")) + } + return points, nil + +} + +func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, error) { + // scan the first block which is measurement[,tag1=value1,tag2=value=2...] + pos, key, err := scanKey(buf, 0) + if err != nil { + return nil, err + } + + // measurement name is required + if len(key) == 0 { + return nil, fmt.Errorf("missing measurement") + } + + if len(key) > MaxKeyLength { + return nil, fmt.Errorf("max key length exceeded: %v > %v", len(key), MaxKeyLength) + } + + // scan the second block is which is field1=value1[,field2=value2,...] + pos, fields, err := scanFields(buf, pos) + if err != nil { + return nil, err + } + + // at least one field is required + if len(fields) == 0 { + return nil, fmt.Errorf("missing fields") + } + + var maxKeyErr error + err = walkFields(fields, func(k, v []byte) bool { + if sz := seriesKeySize(key, k); sz > MaxKeyLength { + maxKeyErr = fmt.Errorf("max key length exceeded: %v > %v", sz, MaxKeyLength) + return false + } + return true + }) + + if err != nil { + return nil, err + } + + if maxKeyErr != nil { + return nil, maxKeyErr + } + + // scan the last block which is an optional integer timestamp + pos, ts, err := scanTime(buf, pos) + if err != nil { + return nil, err + } + + pt := &point{ + key: key, + fields: fields, + ts: ts, + } + + if len(ts) == 0 { + pt.time = defaultTime + pt.SetPrecision(precision) + } else { + ts, err := parseIntBytes(ts, 10, 64) + if err != nil { + return nil, err + } + pt.time, err = SafeCalcTime(ts, precision) + if err != nil { + return nil, err + } + + // Determine if there are illegal non-whitespace characters after the + // timestamp block. + for pos < len(buf) { + if buf[pos] != ' ' { + return nil, ErrInvalidPoint + } + pos++ + } + } + return pt, nil +} + +// GetPrecisionMultiplier will return a multiplier for the precision specified. +func GetPrecisionMultiplier(precision string) int64 { + d := time.Nanosecond + switch precision { + case "u": + d = time.Microsecond + case "ms": + d = time.Millisecond + case "s": + d = time.Second + case "m": + d = time.Minute + case "h": + d = time.Hour + } + return int64(d) +} + +// scanKey scans buf starting at i for the measurement and tag portion of the point. +// It returns the ending position and the byte slice of key within buf. If there +// are tags, they will be sorted if they are not already. +func scanKey(buf []byte, i int) (int, []byte, error) { + start := skipWhitespace(buf, i) + + i = start + + // Determines whether the tags are sort, assume they are + sorted := true + + // indices holds the indexes within buf of the start of each tag. For example, + // a buf of 'cpu,host=a,region=b,zone=c' would have indices slice of [4,11,20] + // which indicates that the first tag starts at buf[4], seconds at buf[11], and + // last at buf[20] + indices := make([]int, 100) + + // tracks how many commas we've seen so we know how many values are indices. + // Since indices is an arbitrarily large slice, + // we need to know how many values in the buffer are in use. + commas := 0 + + // First scan the Point's measurement. + state, i, err := scanMeasurement(buf, i) + if err != nil { + return i, buf[start:i], err + } + + // Optionally scan tags if needed. + if state == tagKeyState { + i, commas, indices, err = scanTags(buf, i, indices) + if err != nil { + return i, buf[start:i], err + } + } + + // Now we know where the key region is within buf, and the location of tags, we + // need to determine if duplicate tags exist and if the tags are sorted. This iterates + // over the list comparing each tag in the sequence with each other. + for j := 0; j < commas-1; j++ { + // get the left and right tags + _, left := scanTo(buf[indices[j]:indices[j+1]-1], 0, '=') + _, right := scanTo(buf[indices[j+1]:indices[j+2]-1], 0, '=') + + // If left is greater than right, the tags are not sorted. We do not have to + // continue because the short path no longer works. + // If the tags are equal, then there are duplicate tags, and we should abort. + // If the tags are not sorted, this pass may not find duplicate tags and we + // need to do a more exhaustive search later. + if cmp := bytes.Compare(left, right); cmp > 0 { + sorted = false + break + } else if cmp == 0 { + return i, buf[start:i], fmt.Errorf("duplicate tags") + } + } + + // If the tags are not sorted, then sort them. This sort is inline and + // uses the tag indices we created earlier. The actual buffer is not sorted, the + // indices are using the buffer for value comparison. After the indices are sorted, + // the buffer is reconstructed from the sorted indices. + if !sorted && commas > 0 { + // Get the measurement name for later + measurement := buf[start : indices[0]-1] + + // Sort the indices + indices := indices[:commas] + insertionSort(0, commas, buf, indices) + + // Create a new key using the measurement and sorted indices + b := make([]byte, len(buf[start:i])) + pos := copy(b, measurement) + for _, i := range indices { + b[pos] = ',' + pos++ + _, v := scanToSpaceOr(buf, i, ',') + pos += copy(b[pos:], v) + } + + // Check again for duplicate tags now that the tags are sorted. + for j := 0; j < commas-1; j++ { + // get the left and right tags + _, left := scanTo(buf[indices[j]:], 0, '=') + _, right := scanTo(buf[indices[j+1]:], 0, '=') + + // If the tags are equal, then there are duplicate tags, and we should abort. + // If the tags are not sorted, this pass may not find duplicate tags and we + // need to do a more exhaustive search later. + if bytes.Equal(left, right) { + return i, b, fmt.Errorf("duplicate tags") + } + } + + return i, b, nil + } + + return i, buf[start:i], nil +} + +// The following constants allow us to specify which state to move to +// next, when scanning sections of a Point. +const ( + tagKeyState = iota + tagValueState + fieldsState +) + +// scanMeasurement examines the measurement part of a Point, returning +// the next state to move to, and the current location in the buffer. +func scanMeasurement(buf []byte, i int) (int, int, error) { + // Check first byte of measurement, anything except a comma is fine. + // It can't be a space, since whitespace is stripped prior to this + // function call. + if i >= len(buf) || buf[i] == ',' { + return -1, i, fmt.Errorf("missing measurement") + } + + for { + i++ + if i >= len(buf) { + // cpu + return -1, i, fmt.Errorf("missing fields") + } + + if buf[i-1] == '\\' { + // Skip character (it's escaped). + continue + } + + // Unescaped comma; move onto scanning the tags. + if buf[i] == ',' { + return tagKeyState, i + 1, nil + } + + // Unescaped space; move onto scanning the fields. + if buf[i] == ' ' { + // cpu value=1.0 + return fieldsState, i, nil + } + } +} + +// scanTags examines all the tags in a Point, keeping track of and +// returning the updated indices slice, number of commas and location +// in buf where to start examining the Point fields. +func scanTags(buf []byte, i int, indices []int) (int, int, []int, error) { + var ( + err error + commas int + state = tagKeyState + ) + + for { + switch state { + case tagKeyState: + // Grow our indices slice if we have too many tags. + if commas >= len(indices) { + newIndics := make([]int, cap(indices)*2) + copy(newIndics, indices) + indices = newIndics + } + indices[commas] = i + commas++ + + i, err = scanTagsKey(buf, i) + state = tagValueState // tag value always follows a tag key + case tagValueState: + state, i, err = scanTagsValue(buf, i) + case fieldsState: + indices[commas] = i + 1 + return i, commas, indices, nil + } + + if err != nil { + return i, commas, indices, err + } + } +} + +// scanTagsKey scans each character in a tag key. +func scanTagsKey(buf []byte, i int) (int, error) { + // First character of the key. + if i >= len(buf) || buf[i] == ' ' || buf[i] == ',' || buf[i] == '=' { + // cpu,{'', ' ', ',', '='} + return i, fmt.Errorf("missing tag key") + } + + // Examine each character in the tag key until we hit an unescaped + // equals (the tag value), or we hit an error (i.e., unescaped + // space or comma). + for { + i++ + + // Either we reached the end of the buffer or we hit an + // unescaped comma or space. + if i >= len(buf) || + ((buf[i] == ' ' || buf[i] == ',') && buf[i-1] != '\\') { + // cpu,tag{'', ' ', ','} + return i, fmt.Errorf("missing tag value") + } + + if buf[i] == '=' && buf[i-1] != '\\' { + // cpu,tag= + return i + 1, nil + } + } +} + +// scanTagsValue scans each character in a tag value. +func scanTagsValue(buf []byte, i int) (int, int, error) { + // Tag value cannot be empty. + if i >= len(buf) || buf[i] == ',' || buf[i] == ' ' { + // cpu,tag={',', ' '} + return -1, i, fmt.Errorf("missing tag value") + } + + // Examine each character in the tag value until we hit an unescaped + // comma (move onto next tag key), an unescaped space (move onto + // fields), or we error out. + for { + i++ + if i >= len(buf) { + // cpu,tag=value + return -1, i, fmt.Errorf("missing fields") + } + + // An unescaped equals sign is an invalid tag value. + if buf[i] == '=' && buf[i-1] != '\\' { + // cpu,tag={'=', 'fo=o'} + return -1, i, fmt.Errorf("invalid tag format") + } + + if buf[i] == ',' && buf[i-1] != '\\' { + // cpu,tag=foo, + return tagKeyState, i + 1, nil + } + + // cpu,tag=foo value=1.0 + // cpu, tag=foo\= value=1.0 + if buf[i] == ' ' && buf[i-1] != '\\' { + return fieldsState, i, nil + } + } +} + +func insertionSort(l, r int, buf []byte, indices []int) { + for i := l + 1; i < r; i++ { + for j := i; j > l && less(buf, indices, j, j-1); j-- { + indices[j], indices[j-1] = indices[j-1], indices[j] + } + } +} + +func less(buf []byte, indices []int, i, j int) bool { + // This grabs the tag names for i & j, it ignores the values + _, a := scanTo(buf, indices[i], '=') + _, b := scanTo(buf, indices[j], '=') + return bytes.Compare(a, b) < 0 +} + +// scanFields scans buf, starting at i for the fields section of a point. It returns +// the ending position and the byte slice of the fields within buf. +func scanFields(buf []byte, i int) (int, []byte, error) { + start := skipWhitespace(buf, i) + i = start + quoted := false + + // tracks how many '=' we've seen + equals := 0 + + // tracks how many commas we've seen + commas := 0 + + for { + // reached the end of buf? + if i >= len(buf) { + break + } + + // escaped characters? + if buf[i] == '\\' && i+1 < len(buf) { + i += 2 + continue + } + + // If the value is quoted, scan until we get to the end quote + // Only quote values in the field value since quotes are not significant + // in the field key + if buf[i] == '"' && equals > commas { + quoted = !quoted + i++ + continue + } + + // If we see an =, ensure that there is at least on char before and after it + if buf[i] == '=' && !quoted { + equals++ + + // check for "... =123" but allow "a\ =123" + if buf[i-1] == ' ' && buf[i-2] != '\\' { + return i, buf[start:i], fmt.Errorf("missing field key") + } + + // check for "...a=123,=456" but allow "a=123,a\,=456" + if buf[i-1] == ',' && buf[i-2] != '\\' { + return i, buf[start:i], fmt.Errorf("missing field key") + } + + // check for "... value=" + if i+1 >= len(buf) { + return i, buf[start:i], fmt.Errorf("missing field value") + } + + // check for "... value=,value2=..." + if buf[i+1] == ',' || buf[i+1] == ' ' { + return i, buf[start:i], fmt.Errorf("missing field value") + } + + if isNumeric(buf[i+1]) || buf[i+1] == '-' || buf[i+1] == 'N' || buf[i+1] == 'n' { + var err error + i, err = scanNumber(buf, i+1) + if err != nil { + return i, buf[start:i], err + } + continue + } + // If next byte is not a double-quote, the value must be a boolean + if buf[i+1] != '"' { + var err error + i, _, err = scanBoolean(buf, i+1) + if err != nil { + return i, buf[start:i], err + } + continue + } + } + + if buf[i] == ',' && !quoted { + commas++ + } + + // reached end of block? + if buf[i] == ' ' && !quoted { + break + } + i++ + } + + if quoted { + return i, buf[start:i], fmt.Errorf("unbalanced quotes") + } + + // check that all field sections had key and values (e.g. prevent "a=1,b" + if equals == 0 || commas != equals-1 { + return i, buf[start:i], fmt.Errorf("invalid field format") + } + + return i, buf[start:i], nil +} + +// scanTime scans buf, starting at i for the time section of a point. It +// returns the ending position and the byte slice of the timestamp within buf +// and and error if the timestamp is not in the correct numeric format. +func scanTime(buf []byte, i int) (int, []byte, error) { + start := skipWhitespace(buf, i) + i = start + + for { + // reached the end of buf? + if i >= len(buf) { + break + } + + // Reached end of block or trailing whitespace? + if buf[i] == '\n' || buf[i] == ' ' { + break + } + + // Handle negative timestamps + if i == start && buf[i] == '-' { + i++ + continue + } + + // Timestamps should be integers, make sure they are so we don't need + // to actually parse the timestamp until needed. + if buf[i] < '0' || buf[i] > '9' { + return i, buf[start:i], fmt.Errorf("bad timestamp") + } + i++ + } + return i, buf[start:i], nil +} + +func isNumeric(b byte) bool { + return (b >= '0' && b <= '9') || b == '.' +} + +// scanNumber returns the end position within buf, start at i after +// scanning over buf for an integer, or float. It returns an +// error if a invalid number is scanned. +func scanNumber(buf []byte, i int) (int, error) { + start := i + var isInt, isUnsigned bool + + // Is negative number? + if i < len(buf) && buf[i] == '-' { + i++ + // There must be more characters now, as just '-' is illegal. + if i == len(buf) { + return i, ErrInvalidNumber + } + } + + // how many decimal points we've see + decimal := false + + // indicates the number is float in scientific notation + scientific := false + + for { + if i >= len(buf) { + break + } + + if buf[i] == ',' || buf[i] == ' ' { + break + } + + if buf[i] == 'i' && i > start && !(isInt || isUnsigned) { + isInt = true + i++ + continue + } else if buf[i] == 'u' && i > start && !(isInt || isUnsigned) { + isUnsigned = true + i++ + continue + } + + if buf[i] == '.' { + // Can't have more than 1 decimal (e.g. 1.1.1 should fail) + if decimal { + return i, ErrInvalidNumber + } + decimal = true + } + + // `e` is valid for floats but not as the first char + if i > start && (buf[i] == 'e' || buf[i] == 'E') { + scientific = true + i++ + continue + } + + // + and - are only valid at this point if they follow an e (scientific notation) + if (buf[i] == '+' || buf[i] == '-') && (buf[i-1] == 'e' || buf[i-1] == 'E') { + i++ + continue + } + + // NaN is an unsupported value + if i+2 < len(buf) && (buf[i] == 'N' || buf[i] == 'n') { + return i, ErrInvalidNumber + } + + if !isNumeric(buf[i]) { + return i, ErrInvalidNumber + } + i++ + } + + if (isInt || isUnsigned) && (decimal || scientific) { + return i, ErrInvalidNumber + } + + numericDigits := i - start + if isInt { + numericDigits-- + } + if decimal { + numericDigits-- + } + if buf[start] == '-' { + numericDigits-- + } + + if numericDigits == 0 { + return i, ErrInvalidNumber + } + + // It's more common that numbers will be within min/max range for their type but we need to prevent + // out or range numbers from being parsed successfully. This uses some simple heuristics to decide + // if we should parse the number to the actual type. It does not do it all the time because it incurs + // extra allocations and we end up converting the type again when writing points to disk. + if isInt { + // Make sure the last char is an 'i' for integers (e.g. 9i10 is not valid) + if buf[i-1] != 'i' { + return i, ErrInvalidNumber + } + // Parse the int to check bounds the number of digits could be larger than the max range + // We subtract 1 from the index to remove the `i` from our tests + if len(buf[start:i-1]) >= maxInt64Digits || len(buf[start:i-1]) >= minInt64Digits { + if _, err := parseIntBytes(buf[start:i-1], 10, 64); err != nil { + return i, fmt.Errorf("unable to parse integer %s: %s", buf[start:i-1], err) + } + } + } else if isUnsigned { + // Return an error if uint64 support has not been enabled. + if !enableUint64Support { + return i, ErrInvalidNumber + } + // Make sure the last char is a 'u' for unsigned + if buf[i-1] != 'u' { + return i, ErrInvalidNumber + } + // Make sure the first char is not a '-' for unsigned + if buf[start] == '-' { + return i, ErrInvalidNumber + } + // Parse the uint to check bounds the number of digits could be larger than the max range + // We subtract 1 from the index to remove the `u` from our tests + if len(buf[start:i-1]) >= maxUint64Digits { + if _, err := parseUintBytes(buf[start:i-1], 10, 64); err != nil { + return i, fmt.Errorf("unable to parse unsigned %s: %s", buf[start:i-1], err) + } + } + } else { + // Parse the float to check bounds if it's scientific or the number of digits could be larger than the max range + if scientific || len(buf[start:i]) >= maxFloat64Digits || len(buf[start:i]) >= minFloat64Digits { + if _, err := parseFloatBytes(buf[start:i], 10); err != nil { + return i, fmt.Errorf("invalid float") + } + } + } + + return i, nil +} + +// scanBoolean returns the end position within buf, start at i after +// scanning over buf for boolean. Valid values for a boolean are +// t, T, true, TRUE, f, F, false, FALSE. It returns an error if a invalid boolean +// is scanned. +func scanBoolean(buf []byte, i int) (int, []byte, error) { + start := i + + if i < len(buf) && (buf[i] != 't' && buf[i] != 'f' && buf[i] != 'T' && buf[i] != 'F') { + return i, buf[start:i], fmt.Errorf("invalid boolean") + } + + i++ + for { + if i >= len(buf) { + break + } + + if buf[i] == ',' || buf[i] == ' ' { + break + } + i++ + } + + // Single char bool (t, T, f, F) is ok + if i-start == 1 { + return i, buf[start:i], nil + } + + // length must be 4 for true or TRUE + if (buf[start] == 't' || buf[start] == 'T') && i-start != 4 { + return i, buf[start:i], fmt.Errorf("invalid boolean") + } + + // length must be 5 for false or FALSE + if (buf[start] == 'f' || buf[start] == 'F') && i-start != 5 { + return i, buf[start:i], fmt.Errorf("invalid boolean") + } + + // Otherwise + valid := false + switch buf[start] { + case 't': + valid = bytes.Equal(buf[start:i], []byte("true")) + case 'f': + valid = bytes.Equal(buf[start:i], []byte("false")) + case 'T': + valid = bytes.Equal(buf[start:i], []byte("TRUE")) || bytes.Equal(buf[start:i], []byte("True")) + case 'F': + valid = bytes.Equal(buf[start:i], []byte("FALSE")) || bytes.Equal(buf[start:i], []byte("False")) + } + + if !valid { + return i, buf[start:i], fmt.Errorf("invalid boolean") + } + + return i, buf[start:i], nil + +} + +// skipWhitespace returns the end position within buf, starting at i after +// scanning over spaces in tags. +func skipWhitespace(buf []byte, i int) int { + for i < len(buf) { + if buf[i] != ' ' && buf[i] != '\t' && buf[i] != 0 { + break + } + i++ + } + return i +} + +// scanLine returns the end position in buf and the next line found within +// buf. +func scanLine(buf []byte, i int) (int, []byte) { + start := i + quoted := false + fields := false + + // tracks how many '=' and commas we've seen + // this duplicates some of the functionality in scanFields + equals := 0 + commas := 0 + for { + // reached the end of buf? + if i >= len(buf) { + break + } + + // skip past escaped characters + if buf[i] == '\\' && i+2 < len(buf) { + i += 2 + continue + } + + if buf[i] == ' ' { + fields = true + } + + // If we see a double quote, makes sure it is not escaped + if fields { + if !quoted && buf[i] == '=' { + i++ + equals++ + continue + } else if !quoted && buf[i] == ',' { + i++ + commas++ + continue + } else if buf[i] == '"' && equals > commas { + i++ + quoted = !quoted + continue + } + } + + if buf[i] == '\n' && !quoted { + break + } + + i++ + } + + return i, buf[start:i] +} + +// scanTo returns the end position in buf and the next consecutive block +// of bytes, starting from i and ending with stop byte, where stop byte +// has not been escaped. +// +// If there are leading spaces, they are skipped. +func scanTo(buf []byte, i int, stop byte) (int, []byte) { + start := i + for { + // reached the end of buf? + if i >= len(buf) { + break + } + + // Reached unescaped stop value? + if buf[i] == stop && (i == 0 || buf[i-1] != '\\') { + break + } + i++ + } + + return i, buf[start:i] +} + +// scanTo returns the end position in buf and the next consecutive block +// of bytes, starting from i and ending with stop byte. If there are leading +// spaces, they are skipped. +func scanToSpaceOr(buf []byte, i int, stop byte) (int, []byte) { + start := i + if buf[i] == stop || buf[i] == ' ' { + return i, buf[start:i] + } + + for { + i++ + if buf[i-1] == '\\' { + continue + } + + // reached the end of buf? + if i >= len(buf) { + return i, buf[start:i] + } + + // reached end of block? + if buf[i] == stop || buf[i] == ' ' { + return i, buf[start:i] + } + } +} + +func scanTagValue(buf []byte, i int) (int, []byte) { + start := i + for { + if i >= len(buf) { + break + } + + if buf[i] == ',' && buf[i-1] != '\\' { + break + } + i++ + } + if i > len(buf) { + return i, nil + } + return i, buf[start:i] +} + +func scanFieldValue(buf []byte, i int) (int, []byte) { + start := i + quoted := false + for i < len(buf) { + // Only escape char for a field value is a double-quote and backslash + if buf[i] == '\\' && i+1 < len(buf) && (buf[i+1] == '"' || buf[i+1] == '\\') { + i += 2 + continue + } + + // Quoted value? (e.g. string) + if buf[i] == '"' { + i++ + quoted = !quoted + continue + } + + if buf[i] == ',' && !quoted { + break + } + i++ + } + return i, buf[start:i] +} + +func EscapeMeasurement(in []byte) []byte { + for _, c := range measurementEscapeCodes { + if bytes.IndexByte(in, c.k[0]) != -1 { + in = bytes.Replace(in, c.k[:], c.esc[:], -1) + } + } + return in +} + +func unescapeMeasurement(in []byte) []byte { + if bytes.IndexByte(in, '\\') == -1 { + return in + } + + for i := range measurementEscapeCodes { + c := &measurementEscapeCodes[i] + if bytes.IndexByte(in, c.k[0]) != -1 { + in = bytes.Replace(in, c.esc[:], c.k[:], -1) + } + } + return in +} + +func escapeTag(in []byte) []byte { + for i := range tagEscapeCodes { + c := &tagEscapeCodes[i] + if bytes.IndexByte(in, c.k[0]) != -1 { + in = bytes.Replace(in, c.k[:], c.esc[:], -1) + } + } + return in +} + +func unescapeTag(in []byte) []byte { + if bytes.IndexByte(in, '\\') == -1 { + return in + } + + for i := range tagEscapeCodes { + c := &tagEscapeCodes[i] + if bytes.IndexByte(in, c.k[0]) != -1 { + in = bytes.Replace(in, c.esc[:], c.k[:], -1) + } + } + return in +} + +// escapeStringFieldReplacer replaces double quotes and backslashes +// with the same character preceded by a backslash. +// As of Go 1.7 this benchmarked better in allocations and CPU time +// compared to iterating through a string byte-by-byte and appending to a new byte slice, +// calling strings.Replace twice, and better than (*Regex).ReplaceAllString. +var escapeStringFieldReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`) + +// EscapeStringField returns a copy of in with any double quotes or +// backslashes with escaped values. +func EscapeStringField(in string) string { + return escapeStringFieldReplacer.Replace(in) +} + +// unescapeStringField returns a copy of in with any escaped double-quotes +// or backslashes unescaped. +func unescapeStringField(in string) string { + if strings.IndexByte(in, '\\') == -1 { + return in + } + + var out []byte + i := 0 + for { + if i >= len(in) { + break + } + // unescape backslashes + if in[i] == '\\' && i+1 < len(in) && in[i+1] == '\\' { + out = append(out, '\\') + i += 2 + continue + } + // unescape double-quotes + if in[i] == '\\' && i+1 < len(in) && in[i+1] == '"' { + out = append(out, '"') + i += 2 + continue + } + out = append(out, in[i]) + i++ + + } + return string(out) +} + +// NewPoint returns a new point with the given measurement name, tags, fields and timestamp. If +// an unsupported field value (NaN, or +/-Inf) or out of range time is passed, this function +// returns an error. +func NewPoint(name string, tags Tags, fields Fields, t time.Time) (Point, error) { + key, err := pointKey(name, tags, fields, t) + if err != nil { + return nil, err + } + + return &point{ + key: key, + time: t, + fields: fields.MarshalBinary(), + }, nil +} + +// pointKey checks some basic requirements for valid points, and returns the +// key, along with an possible error. +func pointKey(measurement string, tags Tags, fields Fields, t time.Time) ([]byte, error) { + if len(fields) == 0 { + return nil, ErrPointMustHaveAField + } + + if !t.IsZero() { + if err := CheckTime(t); err != nil { + return nil, err + } + } + + for key, value := range fields { + switch value := value.(type) { + case float64: + // Ensure the caller validates and handles invalid field values + if math.IsInf(value, 0) { + return nil, fmt.Errorf("+/-Inf is an unsupported value for field %s", key) + } + if math.IsNaN(value) { + return nil, fmt.Errorf("NaN is an unsupported value for field %s", key) + } + case float32: + // Ensure the caller validates and handles invalid field values + if math.IsInf(float64(value), 0) { + return nil, fmt.Errorf("+/-Inf is an unsupported value for field %s", key) + } + if math.IsNaN(float64(value)) { + return nil, fmt.Errorf("NaN is an unsupported value for field %s", key) + } + } + if len(key) == 0 { + return nil, fmt.Errorf("all fields must have non-empty names") + } + } + + key := MakeKey([]byte(measurement), tags) + for field := range fields { + sz := seriesKeySize(key, []byte(field)) + if sz > MaxKeyLength { + return nil, fmt.Errorf("max key length exceeded: %v > %v", sz, MaxKeyLength) + } + } + + return key, nil +} + +func seriesKeySize(key, field []byte) int { + // 4 is the length of the tsm1.fieldKeySeparator constant. It's inlined here to avoid a circular + // dependency. + return len(key) + 4 + len(field) +} + +// NewPointFromBytes returns a new Point from a marshalled Point. +func NewPointFromBytes(b []byte) (Point, error) { + p := &point{} + if err := p.UnmarshalBinary(b); err != nil { + return nil, err + } + + // This does some basic validation to ensure there are fields and they + // can be unmarshalled as well. + iter := p.FieldIterator() + var hasField bool + for iter.Next() { + if len(iter.FieldKey()) == 0 { + continue + } + hasField = true + switch iter.Type() { + case Float: + _, err := iter.FloatValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + case Integer: + _, err := iter.IntegerValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + case Unsigned: + _, err := iter.UnsignedValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + case String: + // Skip since this won't return an error + case Boolean: + _, err := iter.BooleanValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + } + } + + if !hasField { + return nil, ErrPointMustHaveAField + } + + return p, nil +} + +// MustNewPoint returns a new point with the given measurement name, tags, fields and timestamp. If +// an unsupported field value (NaN) is passed, this function panics. +func MustNewPoint(name string, tags Tags, fields Fields, time time.Time) Point { + pt, err := NewPoint(name, tags, fields, time) + if err != nil { + panic(err.Error()) + } + return pt +} + +// Key returns the key (measurement joined with tags) of the point. +func (p *point) Key() []byte { + return p.key +} + +func (p *point) name() []byte { + _, name := scanTo(p.key, 0, ',') + return name +} + +func (p *point) Name() []byte { + return escape.Unescape(p.name()) +} + +// SetName updates the measurement name for the point. +func (p *point) SetName(name string) { + p.cachedName = "" + p.key = MakeKey([]byte(name), p.Tags()) +} + +// Time return the timestamp for the point. +func (p *point) Time() time.Time { + return p.time +} + +// SetTime updates the timestamp for the point. +func (p *point) SetTime(t time.Time) { + p.time = t +} + +// Round will round the timestamp of the point to the given duration. +func (p *point) Round(d time.Duration) { + p.time = p.time.Round(d) +} + +// Tags returns the tag set for the point. +func (p *point) Tags() Tags { + if p.cachedTags != nil { + return p.cachedTags + } + p.cachedTags = parseTags(p.key, nil) + return p.cachedTags +} + +func (p *point) ForEachTag(fn func(k, v []byte) bool) { + walkTags(p.key, fn) +} + +func (p *point) HasTag(tag []byte) bool { + if len(p.key) == 0 { + return false + } + + var exists bool + walkTags(p.key, func(key, value []byte) bool { + if bytes.Equal(tag, key) { + exists = true + return false + } + return true + }) + + return exists +} + +func walkTags(buf []byte, fn func(key, value []byte) bool) { + if len(buf) == 0 { + return + } + + pos, name := scanTo(buf, 0, ',') + + // it's an empty key, so there are no tags + if len(name) == 0 { + return + } + + hasEscape := bytes.IndexByte(buf, '\\') != -1 + i := pos + 1 + var key, value []byte + for { + if i >= len(buf) { + break + } + i, key = scanTo(buf, i, '=') + i, value = scanTagValue(buf, i+1) + + if len(value) == 0 { + continue + } + + if hasEscape { + if !fn(unescapeTag(key), unescapeTag(value)) { + return + } + } else { + if !fn(key, value) { + return + } + } + + i++ + } +} + +// walkFields walks each field key and value via fn. If fn returns false, the iteration +// is stopped. The values are the raw byte slices and not the converted types. +func walkFields(buf []byte, fn func(key, value []byte) bool) error { + var i int + var key, val []byte + for len(buf) > 0 { + i, key = scanTo(buf, 0, '=') + if i > len(buf)-2 { + return fmt.Errorf("invalid value: field-key=%s", key) + } + buf = buf[i+1:] + i, val = scanFieldValue(buf, 0) + buf = buf[i:] + if !fn(key, val) { + break + } + + // slice off comma + if len(buf) > 0 { + buf = buf[1:] + } + } + return nil +} + +// parseTags parses buf into the provided destination tags, returning destination +// Tags, which may have a different length and capacity. +func parseTags(buf []byte, dst Tags) Tags { + if len(buf) == 0 { + return nil + } + + n := bytes.Count(buf, []byte(",")) + if cap(dst) < n { + dst = make(Tags, n) + } else { + dst = dst[:n] + } + + // Ensure existing behaviour when point has no tags and nil slice passed in. + if dst == nil { + dst = Tags{} + } + + // Series keys can contain escaped commas, therefore the number of commas + // in a series key only gives an estimation of the upper bound on the number + // of tags. + var i int + walkTags(buf, func(key, value []byte) bool { + dst[i].Key, dst[i].Value = key, value + i++ + return true + }) + return dst[:i] +} + +// MakeKey creates a key for a set of tags. +func MakeKey(name []byte, tags Tags) []byte { + return AppendMakeKey(nil, name, tags) +} + +// AppendMakeKey appends the key derived from name and tags to dst and returns the extended buffer. +func AppendMakeKey(dst []byte, name []byte, tags Tags) []byte { + // unescape the name and then re-escape it to avoid double escaping. + // The key should always be stored in escaped form. + dst = append(dst, EscapeMeasurement(unescapeMeasurement(name))...) + dst = tags.AppendHashKey(dst) + return dst +} + +// SetTags replaces the tags for the point. +func (p *point) SetTags(tags Tags) { + p.key = MakeKey(p.Name(), tags) + p.cachedTags = tags +} + +// AddTag adds or replaces a tag value for a point. +func (p *point) AddTag(key, value string) { + tags := p.Tags() + tags = append(tags, Tag{Key: []byte(key), Value: []byte(value)}) + sort.Sort(tags) + p.cachedTags = tags + p.key = MakeKey(p.Name(), tags) +} + +// Fields returns the fields for the point. +func (p *point) Fields() (Fields, error) { + if p.cachedFields != nil { + return p.cachedFields, nil + } + cf, err := p.unmarshalBinary() + if err != nil { + return nil, err + } + p.cachedFields = cf + return p.cachedFields, nil +} + +// SetPrecision will round a time to the specified precision. +func (p *point) SetPrecision(precision string) { + switch precision { + case "n": + case "u": + p.SetTime(p.Time().Truncate(time.Microsecond)) + case "ms": + p.SetTime(p.Time().Truncate(time.Millisecond)) + case "s": + p.SetTime(p.Time().Truncate(time.Second)) + case "m": + p.SetTime(p.Time().Truncate(time.Minute)) + case "h": + p.SetTime(p.Time().Truncate(time.Hour)) + } +} + +// String returns the string representation of the point. +func (p *point) String() string { + if p.Time().IsZero() { + return string(p.Key()) + " " + string(p.fields) + } + return string(p.Key()) + " " + string(p.fields) + " " + strconv.FormatInt(p.UnixNano(), 10) +} + +// AppendString appends the string representation of the point to buf. +func (p *point) AppendString(buf []byte) []byte { + buf = append(buf, p.key...) + buf = append(buf, ' ') + buf = append(buf, p.fields...) + + if !p.time.IsZero() { + buf = append(buf, ' ') + buf = strconv.AppendInt(buf, p.UnixNano(), 10) + } + + return buf +} + +// StringSize returns the length of the string that would be returned by String(). +func (p *point) StringSize() int { + size := len(p.key) + len(p.fields) + 1 + + if !p.time.IsZero() { + digits := 1 // even "0" has one digit + t := p.UnixNano() + if t < 0 { + // account for negative sign, then negate + digits++ + t = -t + } + for t > 9 { // already accounted for one digit + digits++ + t /= 10 + } + size += digits + 1 // digits and a space + } + + return size +} + +// MarshalBinary returns a binary representation of the point. +func (p *point) MarshalBinary() ([]byte, error) { + if len(p.fields) == 0 { + return nil, ErrPointMustHaveAField + } + + tb, err := p.time.MarshalBinary() + if err != nil { + return nil, err + } + + b := make([]byte, 8+len(p.key)+len(p.fields)+len(tb)) + i := 0 + + binary.BigEndian.PutUint32(b[i:], uint32(len(p.key))) + i += 4 + + i += copy(b[i:], p.key) + + binary.BigEndian.PutUint32(b[i:i+4], uint32(len(p.fields))) + i += 4 + + i += copy(b[i:], p.fields) + + copy(b[i:], tb) + return b, nil +} + +// UnmarshalBinary decodes a binary representation of the point into a point struct. +func (p *point) UnmarshalBinary(b []byte) error { + var n int + + // Read key length. + if len(b) < 4 { + return io.ErrShortBuffer + } + n, b = int(binary.BigEndian.Uint32(b[:4])), b[4:] + + // Read key. + if len(b) < n { + return io.ErrShortBuffer + } + p.key, b = b[:n], b[n:] + + // Read fields length. + if len(b) < 4 { + return io.ErrShortBuffer + } + n, b = int(binary.BigEndian.Uint32(b[:4])), b[4:] + + // Read fields. + if len(b) < n { + return io.ErrShortBuffer + } + p.fields, b = b[:n], b[n:] + + // Read timestamp. + return p.time.UnmarshalBinary(b) +} + +// PrecisionString returns a string representation of the point. If there +// is a timestamp associated with the point then it will be specified in the +// given unit. +func (p *point) PrecisionString(precision string) string { + if p.Time().IsZero() { + return fmt.Sprintf("%s %s", p.Key(), string(p.fields)) + } + return fmt.Sprintf("%s %s %d", p.Key(), string(p.fields), + p.UnixNano()/GetPrecisionMultiplier(precision)) +} + +// RoundedString returns a string representation of the point. If there +// is a timestamp associated with the point, then it will be rounded to the +// given duration. +func (p *point) RoundedString(d time.Duration) string { + if p.Time().IsZero() { + return fmt.Sprintf("%s %s", p.Key(), string(p.fields)) + } + return fmt.Sprintf("%s %s %d", p.Key(), string(p.fields), + p.time.Round(d).UnixNano()) +} + +func (p *point) unmarshalBinary() (Fields, error) { + iter := p.FieldIterator() + fields := make(Fields, 8) + for iter.Next() { + if len(iter.FieldKey()) == 0 { + continue + } + switch iter.Type() { + case Float: + v, err := iter.FloatValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + fields[string(iter.FieldKey())] = v + case Integer: + v, err := iter.IntegerValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + fields[string(iter.FieldKey())] = v + case Unsigned: + v, err := iter.UnsignedValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + fields[string(iter.FieldKey())] = v + case String: + fields[string(iter.FieldKey())] = iter.StringValue() + case Boolean: + v, err := iter.BooleanValue() + if err != nil { + return nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err) + } + fields[string(iter.FieldKey())] = v + } + } + return fields, nil +} + +// HashID returns a non-cryptographic checksum of the point's key. +func (p *point) HashID() uint64 { + h := NewInlineFNV64a() + h.Write(p.key) + sum := h.Sum64() + return sum +} + +// UnixNano returns the timestamp of the point as nanoseconds since Unix epoch. +func (p *point) UnixNano() int64 { + return p.Time().UnixNano() +} + +// Split will attempt to return multiple points with the same timestamp whose +// string representations are no longer than size. Points with a single field or +// a point without a timestamp may exceed the requested size. +func (p *point) Split(size int) []Point { + if p.time.IsZero() || p.StringSize() <= size { + return []Point{p} + } + + // key string, timestamp string, spaces + size -= len(p.key) + len(strconv.FormatInt(p.time.UnixNano(), 10)) + 2 + + var points []Point + var start, cur int + + for cur < len(p.fields) { + end, _ := scanTo(p.fields, cur, '=') + end, _ = scanFieldValue(p.fields, end+1) + + if cur > start && end-start > size { + points = append(points, &point{ + key: p.key, + time: p.time, + fields: p.fields[start : cur-1], + }) + start = cur + } + + cur = end + 1 + } + + points = append(points, &point{ + key: p.key, + time: p.time, + fields: p.fields[start:], + }) + + return points +} + +// Tag represents a single key/value tag pair. +type Tag struct { + Key []byte + Value []byte +} + +// NewTag returns a new Tag. +func NewTag(key, value []byte) Tag { + return Tag{ + Key: key, + Value: value, + } +} + +// Size returns the size of the key and value. +func (t Tag) Size() int { return len(t.Key) + len(t.Value) } + +// Clone returns a shallow copy of Tag. +// +// Tags associated with a Point created by ParsePointsWithPrecision will hold references to the byte slice that was parsed. +// Use Clone to create a Tag with new byte slices that do not refer to the argument to ParsePointsWithPrecision. +func (t Tag) Clone() Tag { + other := Tag{ + Key: make([]byte, len(t.Key)), + Value: make([]byte, len(t.Value)), + } + + copy(other.Key, t.Key) + copy(other.Value, t.Value) + + return other +} + +// String returns the string reprsentation of the tag. +func (t *Tag) String() string { + var buf bytes.Buffer + buf.WriteByte('{') + buf.WriteString(string(t.Key)) + buf.WriteByte(' ') + buf.WriteString(string(t.Value)) + buf.WriteByte('}') + return buf.String() +} + +// Tags represents a sorted list of tags. +type Tags []Tag + +// NewTags returns a new Tags from a map. +func NewTags(m map[string]string) Tags { + if len(m) == 0 { + return nil + } + a := make(Tags, 0, len(m)) + for k, v := range m { + a = append(a, NewTag([]byte(k), []byte(v))) + } + sort.Sort(a) + return a +} + +// Keys returns the list of keys for a tag set. +func (a Tags) Keys() []string { + if len(a) == 0 { + return nil + } + keys := make([]string, len(a)) + for i, tag := range a { + keys[i] = string(tag.Key) + } + return keys +} + +// Values returns the list of values for a tag set. +func (a Tags) Values() []string { + if len(a) == 0 { + return nil + } + values := make([]string, len(a)) + for i, tag := range a { + values[i] = string(tag.Value) + } + return values +} + +// String returns the string representation of the tags. +func (a Tags) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i := range a { + buf.WriteString(a[i].String()) + if i < len(a)-1 { + buf.WriteByte(' ') + } + } + buf.WriteByte(']') + return buf.String() +} + +// Size returns the number of bytes needed to store all tags. Note, this is +// the number of bytes needed to store all keys and values and does not account +// for data structures or delimiters for example. +func (a Tags) Size() int { + var total int + for i := range a { + total += a[i].Size() + } + return total +} + +// Clone returns a copy of the slice where the elements are a result of calling `Clone` on the original elements +// +// Tags associated with a Point created by ParsePointsWithPrecision will hold references to the byte slice that was parsed. +// Use Clone to create Tags with new byte slices that do not refer to the argument to ParsePointsWithPrecision. +func (a Tags) Clone() Tags { + if len(a) == 0 { + return nil + } + + others := make(Tags, len(a)) + for i := range a { + others[i] = a[i].Clone() + } + + return others +} + +func (a Tags) Len() int { return len(a) } +func (a Tags) Less(i, j int) bool { return bytes.Compare(a[i].Key, a[j].Key) == -1 } +func (a Tags) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// Equal returns true if a equals other. +func (a Tags) Equal(other Tags) bool { + if len(a) != len(other) { + return false + } + for i := range a { + if !bytes.Equal(a[i].Key, other[i].Key) || !bytes.Equal(a[i].Value, other[i].Value) { + return false + } + } + return true +} + +// CompareTags returns -1 if a < b, 1 if a > b, and 0 if a == b. +func CompareTags(a, b Tags) int { + // Compare each key & value until a mismatch. + for i := 0; i < len(a) && i < len(b); i++ { + if cmp := bytes.Compare(a[i].Key, b[i].Key); cmp != 0 { + return cmp + } + if cmp := bytes.Compare(a[i].Value, b[i].Value); cmp != 0 { + return cmp + } + } + + // If all tags are equal up to this point then return shorter tagset. + if len(a) < len(b) { + return -1 + } else if len(a) > len(b) { + return 1 + } + + // All tags are equal. + return 0 +} + +// Get returns the value for a key. +func (a Tags) Get(key []byte) []byte { + // OPTIMIZE: Use sort.Search if tagset is large. + + for _, t := range a { + if bytes.Equal(t.Key, key) { + return t.Value + } + } + return nil +} + +// GetString returns the string value for a string key. +func (a Tags) GetString(key string) string { + return string(a.Get([]byte(key))) +} + +// Set sets the value for a key. +func (a *Tags) Set(key, value []byte) { + for i, t := range *a { + if bytes.Equal(t.Key, key) { + (*a)[i].Value = value + return + } + } + *a = append(*a, Tag{Key: key, Value: value}) + sort.Sort(*a) +} + +// SetString sets the string value for a string key. +func (a *Tags) SetString(key, value string) { + a.Set([]byte(key), []byte(value)) +} + +// Delete removes a tag by key. +func (a *Tags) Delete(key []byte) { + for i, t := range *a { + if bytes.Equal(t.Key, key) { + copy((*a)[i:], (*a)[i+1:]) + (*a)[len(*a)-1] = Tag{} + *a = (*a)[:len(*a)-1] + return + } + } +} + +// Map returns a map representation of the tags. +func (a Tags) Map() map[string]string { + m := make(map[string]string, len(a)) + for _, t := range a { + m[string(t.Key)] = string(t.Value) + } + return m +} + +// Merge merges the tags combining the two. If both define a tag with the +// same key, the merged value overwrites the old value. +// A new map is returned. +func (a Tags) Merge(other map[string]string) Tags { + merged := make(map[string]string, len(a)+len(other)) + for _, t := range a { + merged[string(t.Key)] = string(t.Value) + } + for k, v := range other { + merged[k] = v + } + return NewTags(merged) +} + +// HashKey hashes all of a tag's keys. +func (a Tags) HashKey() []byte { + return a.AppendHashKey(nil) +} + +func (a Tags) needsEscape() bool { + for i := range a { + t := &a[i] + for j := range tagEscapeCodes { + c := &tagEscapeCodes[j] + if bytes.IndexByte(t.Key, c.k[0]) != -1 || bytes.IndexByte(t.Value, c.k[0]) != -1 { + return true + } + } + } + return false +} + +// AppendHashKey appends the result of hashing all of a tag's keys and values to dst and returns the extended buffer. +func (a Tags) AppendHashKey(dst []byte) []byte { + // Empty maps marshal to empty bytes. + if len(a) == 0 { + return dst + } + + // Type invariant: Tags are sorted + + sz := 0 + var escaped Tags + if a.needsEscape() { + var tmp [20]Tag + if len(a) < len(tmp) { + escaped = tmp[:len(a)] + } else { + escaped = make(Tags, len(a)) + } + + for i := range a { + t := &a[i] + nt := &escaped[i] + nt.Key = escapeTag(t.Key) + nt.Value = escapeTag(t.Value) + sz += len(nt.Key) + len(nt.Value) + } + } else { + sz = a.Size() + escaped = a + } + + sz += len(escaped) + (len(escaped) * 2) // separators + + // Generate marshaled bytes. + if cap(dst)-len(dst) < sz { + nd := make([]byte, len(dst), len(dst)+sz) + copy(nd, dst) + dst = nd + } + buf := dst[len(dst) : len(dst)+sz] + idx := 0 + for i := range escaped { + k := &escaped[i] + if len(k.Value) == 0 { + continue + } + buf[idx] = ',' + idx++ + copy(buf[idx:], k.Key) + idx += len(k.Key) + buf[idx] = '=' + idx++ + copy(buf[idx:], k.Value) + idx += len(k.Value) + } + return dst[:len(dst)+idx] +} + +// CopyTags returns a shallow copy of tags. +func CopyTags(a Tags) Tags { + other := make(Tags, len(a)) + copy(other, a) + return other +} + +// DeepCopyTags returns a deep copy of tags. +func DeepCopyTags(a Tags) Tags { + // Calculate size of keys/values in bytes. + var n int + for _, t := range a { + n += len(t.Key) + len(t.Value) + } + + // Build single allocation for all key/values. + buf := make([]byte, n) + + // Copy tags to new set. + other := make(Tags, len(a)) + for i, t := range a { + copy(buf, t.Key) + other[i].Key, buf = buf[:len(t.Key)], buf[len(t.Key):] + + copy(buf, t.Value) + other[i].Value, buf = buf[:len(t.Value)], buf[len(t.Value):] + } + + return other +} + +// Fields represents a mapping between a Point's field names and their +// values. +type Fields map[string]interface{} + +// FieldIterator retuns a FieldIterator that can be used to traverse the +// fields of a point without constructing the in-memory map. +func (p *point) FieldIterator() FieldIterator { + p.Reset() + return p +} + +type fieldIterator struct { + start, end int + key, keybuf []byte + valueBuf []byte + fieldType FieldType +} + +// Next indicates whether there any fields remaining. +func (p *point) Next() bool { + p.it.start = p.it.end + if p.it.start >= len(p.fields) { + return false + } + + p.it.end, p.it.key = scanTo(p.fields, p.it.start, '=') + if escape.IsEscaped(p.it.key) { + p.it.keybuf = escape.AppendUnescaped(p.it.keybuf[:0], p.it.key) + p.it.key = p.it.keybuf + } + + p.it.end, p.it.valueBuf = scanFieldValue(p.fields, p.it.end+1) + p.it.end++ + + if len(p.it.valueBuf) == 0 { + p.it.fieldType = Empty + return true + } + + c := p.it.valueBuf[0] + + if c == '"' { + p.it.fieldType = String + return true + } + + if strings.IndexByte(`0123456789-.nNiIu`, c) >= 0 { + if p.it.valueBuf[len(p.it.valueBuf)-1] == 'i' { + p.it.fieldType = Integer + p.it.valueBuf = p.it.valueBuf[:len(p.it.valueBuf)-1] + } else if p.it.valueBuf[len(p.it.valueBuf)-1] == 'u' { + p.it.fieldType = Unsigned + p.it.valueBuf = p.it.valueBuf[:len(p.it.valueBuf)-1] + } else { + p.it.fieldType = Float + } + return true + } + + // to keep the same behavior that currently exists, default to boolean + p.it.fieldType = Boolean + return true +} + +// FieldKey returns the key of the current field. +func (p *point) FieldKey() []byte { + return p.it.key +} + +// Type returns the FieldType of the current field. +func (p *point) Type() FieldType { + return p.it.fieldType +} + +// StringValue returns the string value of the current field. +func (p *point) StringValue() string { + return unescapeStringField(string(p.it.valueBuf[1 : len(p.it.valueBuf)-1])) +} + +// IntegerValue returns the integer value of the current field. +func (p *point) IntegerValue() (int64, error) { + n, err := parseIntBytes(p.it.valueBuf, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse integer value %q: %v", p.it.valueBuf, err) + } + return n, nil +} + +// UnsignedValue returns the unsigned value of the current field. +func (p *point) UnsignedValue() (uint64, error) { + n, err := parseUintBytes(p.it.valueBuf, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse unsigned value %q: %v", p.it.valueBuf, err) + } + return n, nil +} + +// BooleanValue returns the boolean value of the current field. +func (p *point) BooleanValue() (bool, error) { + b, err := parseBoolBytes(p.it.valueBuf) + if err != nil { + return false, fmt.Errorf("unable to parse bool value %q: %v", p.it.valueBuf, err) + } + return b, nil +} + +// FloatValue returns the float value of the current field. +func (p *point) FloatValue() (float64, error) { + f, err := parseFloatBytes(p.it.valueBuf, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse floating point value %q: %v", p.it.valueBuf, err) + } + return f, nil +} + +// Reset resets the iterator to its initial state. +func (p *point) Reset() { + p.it.fieldType = Empty + p.it.key = nil + p.it.valueBuf = nil + p.it.start = 0 + p.it.end = 0 +} + +// MarshalBinary encodes all the fields to their proper type and returns the binary +// represenation +// NOTE: uint64 is specifically not supported due to potential overflow when we decode +// again later to an int64 +// NOTE2: uint is accepted, and may be 64 bits, and is for some reason accepted... +func (p Fields) MarshalBinary() []byte { + var b []byte + keys := make([]string, 0, len(p)) + + for k := range p { + keys = append(keys, k) + } + + // Not really necessary, can probably be removed. + sort.Strings(keys) + + for i, k := range keys { + if i > 0 { + b = append(b, ',') + } + b = appendField(b, k, p[k]) + } + + return b +} + +func appendField(b []byte, k string, v interface{}) []byte { + b = append(b, []byte(escape.String(k))...) + b = append(b, '=') + + // check popular types first + switch v := v.(type) { + case float64: + b = strconv.AppendFloat(b, v, 'f', -1, 64) + case int64: + b = strconv.AppendInt(b, v, 10) + b = append(b, 'i') + case string: + b = append(b, '"') + b = append(b, []byte(EscapeStringField(v))...) + b = append(b, '"') + case bool: + b = strconv.AppendBool(b, v) + case int32: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case int16: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case int8: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case int: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case uint64: + b = strconv.AppendUint(b, v, 10) + b = append(b, 'u') + case uint32: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case uint16: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case uint8: + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case uint: + // TODO: 'uint' should be converted to writing as an unsigned integer, + // but we cannot since that would break backwards compatibility. + b = strconv.AppendInt(b, int64(v), 10) + b = append(b, 'i') + case float32: + b = strconv.AppendFloat(b, float64(v), 'f', -1, 32) + case []byte: + b = append(b, v...) + case nil: + // skip + default: + // Can't determine the type, so convert to string + b = append(b, '"') + b = append(b, []byte(EscapeStringField(fmt.Sprintf("%v", v)))...) + b = append(b, '"') + + } + + return b +} + +// ValidKeyToken returns true if the token used for measurement, tag key, or tag +// value is a valid unicode string and only contains printable, non-replacement characters. +func ValidKeyToken(s string) bool { + if !utf8.ValidString(s) { + return false + } + for _, r := range s { + if !unicode.IsPrint(r) || r == unicode.ReplacementChar { + return false + } + } + return true +} + +// ValidKeyTokens returns true if the measurement name and all tags are valid. +func ValidKeyTokens(name string, tags Tags) bool { + if !ValidKeyToken(name) { + return false + } + for _, tag := range tags { + if !ValidKeyToken(string(tag.Key)) || !ValidKeyToken(string(tag.Value)) { + return false + } + } + return true +} diff --git a/vendor/github.com/influxdata/influxdb/models/rows.go b/vendor/github.com/influxdata/influxdb/models/rows.go new file mode 100644 index 0000000..c087a48 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/rows.go @@ -0,0 +1,62 @@ +package models + +import ( + "sort" +) + +// Row represents a single row returned from the execution of a statement. +type Row struct { + Name string `json:"name,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + Columns []string `json:"columns,omitempty"` + Values [][]interface{} `json:"values,omitempty"` + Partial bool `json:"partial,omitempty"` +} + +// SameSeries returns true if r contains values for the same series as o. +func (r *Row) SameSeries(o *Row) bool { + return r.tagsHash() == o.tagsHash() && r.Name == o.Name +} + +// tagsHash returns a hash of tag key/value pairs. +func (r *Row) tagsHash() uint64 { + h := NewInlineFNV64a() + keys := r.tagsKeys() + for _, k := range keys { + h.Write([]byte(k)) + h.Write([]byte(r.Tags[k])) + } + return h.Sum64() +} + +// tagKeys returns a sorted list of tag keys. +func (r *Row) tagsKeys() []string { + a := make([]string, 0, len(r.Tags)) + for k := range r.Tags { + a = append(a, k) + } + sort.Strings(a) + return a +} + +// Rows represents a collection of rows. Rows implements sort.Interface. +type Rows []*Row + +// Len implements sort.Interface. +func (p Rows) Len() int { return len(p) } + +// Less implements sort.Interface. +func (p Rows) Less(i, j int) bool { + // Sort by name first. + if p[i].Name != p[j].Name { + return p[i].Name < p[j].Name + } + + // Sort by tag set hash. Tags don't have a meaningful sort order so we + // just compute a hash and sort by that instead. This allows the tests + // to receive rows in a predictable order every time. + return p[i].tagsHash() < p[j].tagsHash() +} + +// Swap implements sort.Interface. +func (p Rows) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/influxdata/influxdb/models/statistic.go b/vendor/github.com/influxdata/influxdb/models/statistic.go new file mode 100644 index 0000000..553e9d0 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/statistic.go @@ -0,0 +1,42 @@ +package models + +// Statistic is the representation of a statistic used by the monitoring service. +type Statistic struct { + Name string `json:"name"` + Tags map[string]string `json:"tags"` + Values map[string]interface{} `json:"values"` +} + +// NewStatistic returns an initialized Statistic. +func NewStatistic(name string) Statistic { + return Statistic{ + Name: name, + Tags: make(map[string]string), + Values: make(map[string]interface{}), + } +} + +// StatisticTags is a map that can be merged with others without causing +// mutations to either map. +type StatisticTags map[string]string + +// Merge creates a new map containing the merged contents of tags and t. +// If both tags and the receiver map contain the same key, the value in tags +// is used in the resulting map. +// +// Merge always returns a usable map. +func (t StatisticTags) Merge(tags map[string]string) map[string]string { + // Add everything in tags to the result. + out := make(map[string]string, len(tags)) + for k, v := range tags { + out[k] = v + } + + // Only add values from t that don't appear in tags. + for k, v := range t { + if _, ok := tags[k]; !ok { + out[k] = v + } + } + return out +} diff --git a/vendor/github.com/influxdata/influxdb/models/time.go b/vendor/github.com/influxdata/influxdb/models/time.go new file mode 100644 index 0000000..e98f2cb --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/time.go @@ -0,0 +1,74 @@ +package models + +// Helper time methods since parsing time can easily overflow and we only support a +// specific time range. + +import ( + "fmt" + "math" + "time" +) + +const ( + // MinNanoTime is the minumum time that can be represented. + // + // 1677-09-21 00:12:43.145224194 +0000 UTC + // + // The two lowest minimum integers are used as sentinel values. The + // minimum value needs to be used as a value lower than any other value for + // comparisons and another separate value is needed to act as a sentinel + // default value that is unusable by the user, but usable internally. + // Because these two values need to be used for a special purpose, we do + // not allow users to write points at these two times. + MinNanoTime = int64(math.MinInt64) + 2 + + // MaxNanoTime is the maximum time that can be represented. + // + // 2262-04-11 23:47:16.854775806 +0000 UTC + // + // The highest time represented by a nanosecond needs to be used for an + // exclusive range in the shard group, so the maximum time needs to be one + // less than the possible maximum number of nanoseconds representable by an + // int64 so that we don't lose a point at that one time. + MaxNanoTime = int64(math.MaxInt64) - 1 +) + +var ( + minNanoTime = time.Unix(0, MinNanoTime).UTC() + maxNanoTime = time.Unix(0, MaxNanoTime).UTC() + + // ErrTimeOutOfRange gets returned when time is out of the representable range using int64 nanoseconds since the epoch. + ErrTimeOutOfRange = fmt.Errorf("time outside range %d - %d", MinNanoTime, MaxNanoTime) +) + +// SafeCalcTime safely calculates the time given. Will return error if the time is outside the +// supported range. +func SafeCalcTime(timestamp int64, precision string) (time.Time, error) { + mult := GetPrecisionMultiplier(precision) + if t, ok := safeSignedMult(timestamp, mult); ok { + tme := time.Unix(0, t).UTC() + return tme, CheckTime(tme) + } + + return time.Time{}, ErrTimeOutOfRange +} + +// CheckTime checks that a time is within the safe range. +func CheckTime(t time.Time) error { + if t.Before(minNanoTime) || t.After(maxNanoTime) { + return ErrTimeOutOfRange + } + return nil +} + +// Perform the multiplication and check to make sure it didn't overflow. +func safeSignedMult(a, b int64) (int64, bool) { + if a == 0 || b == 0 || a == 1 || b == 1 { + return a * b, true + } + if a == MinNanoTime || b == MaxNanoTime { + return 0, false + } + c := a * b + return c, c/b == a +} diff --git a/vendor/github.com/influxdata/influxdb/models/uint_support.go b/vendor/github.com/influxdata/influxdb/models/uint_support.go new file mode 100644 index 0000000..18d1ca0 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/models/uint_support.go @@ -0,0 +1,7 @@ +// +build uint uint64 + +package models + +func init() { + EnableUintSupport() +} diff --git a/vendor/github.com/influxdata/influxdb/pkg/escape/bytes.go b/vendor/github.com/influxdata/influxdb/pkg/escape/bytes.go new file mode 100644 index 0000000..f3b31f4 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/pkg/escape/bytes.go @@ -0,0 +1,115 @@ +// Package escape contains utilities for escaping parts of InfluxQL +// and InfluxDB line protocol. +package escape // import "github.com/influxdata/influxdb/pkg/escape" + +import ( + "bytes" + "strings" +) + +// Codes is a map of bytes to be escaped. +var Codes = map[byte][]byte{ + ',': []byte(`\,`), + '"': []byte(`\"`), + ' ': []byte(`\ `), + '=': []byte(`\=`), +} + +// Bytes escapes characters on the input slice, as defined by Codes. +func Bytes(in []byte) []byte { + for b, esc := range Codes { + in = bytes.Replace(in, []byte{b}, esc, -1) + } + return in +} + +const escapeChars = `," =` + +// IsEscaped returns whether b has any escaped characters, +// i.e. whether b seems to have been processed by Bytes. +func IsEscaped(b []byte) bool { + for len(b) > 0 { + i := bytes.IndexByte(b, '\\') + if i < 0 { + return false + } + + if i+1 < len(b) && strings.IndexByte(escapeChars, b[i+1]) >= 0 { + return true + } + b = b[i+1:] + } + return false +} + +// AppendUnescaped appends the unescaped version of src to dst +// and returns the resulting slice. +func AppendUnescaped(dst, src []byte) []byte { + var pos int + for len(src) > 0 { + next := bytes.IndexByte(src[pos:], '\\') + if next < 0 || pos+next+1 >= len(src) { + return append(dst, src...) + } + + if pos+next+1 < len(src) && strings.IndexByte(escapeChars, src[pos+next+1]) >= 0 { + if pos+next > 0 { + dst = append(dst, src[:pos+next]...) + } + src = src[pos+next+1:] + pos = 0 + } else { + pos += next + 1 + } + } + + return dst +} + +// Unescape returns a new slice containing the unescaped version of in. +func Unescape(in []byte) []byte { + if len(in) == 0 { + return nil + } + + if bytes.IndexByte(in, '\\') == -1 { + return in + } + + i := 0 + inLen := len(in) + + // The output size will be no more than inLen. Preallocating the + // capacity of the output is faster and uses less memory than + // letting append() do its own (over)allocation. + out := make([]byte, 0, inLen) + + for { + if i >= inLen { + break + } + if in[i] == '\\' && i+1 < inLen { + switch in[i+1] { + case ',': + out = append(out, ',') + i += 2 + continue + case '"': + out = append(out, '"') + i += 2 + continue + case ' ': + out = append(out, ' ') + i += 2 + continue + case '=': + out = append(out, '=') + i += 2 + continue + } + } + out = append(out, in[i]) + i += 1 + } + return out +} diff --git a/vendor/github.com/influxdata/influxdb/pkg/escape/strings.go b/vendor/github.com/influxdata/influxdb/pkg/escape/strings.go new file mode 100644 index 0000000..db98033 --- /dev/null +++ b/vendor/github.com/influxdata/influxdb/pkg/escape/strings.go @@ -0,0 +1,21 @@ +package escape + +import "strings" + +var ( + escaper = strings.NewReplacer(`,`, `\,`, `"`, `\"`, ` `, `\ `, `=`, `\=`) + unescaper = strings.NewReplacer(`\,`, `,`, `\"`, `"`, `\ `, ` `, `\=`, `=`) +) + +// UnescapeString returns unescaped version of in. +func UnescapeString(in string) string { + if strings.IndexByte(in, '\\') == -1 { + return in + } + return unescaper.Replace(in) +} + +// String returns the escaped version of in. +func String(in string) string { + return escaper.Replace(in) +} diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE new file mode 100644 index 0000000..14127cd --- /dev/null +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE @@ -0,0 +1,9 @@ +(The MIT License) + +Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md new file mode 100644 index 0000000..949b77e --- /dev/null +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md @@ -0,0 +1,40 @@ +# Windows Terminal Sequences + +This library allow for enabling Windows terminal color support for Go. + +See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details. + +## Usage + +```go +import ( + "syscall" + + sequences "github.com/konsorten/go-windows-terminal-sequences" +) + +func main() { + sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true) +} + +``` + +## Authors + +The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de). + +We thank all the authors who provided code to this library: + +* Felix Kollmann + +## License + +(The MIT License) + +Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod b/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod new file mode 100644 index 0000000..716c613 --- /dev/null +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod @@ -0,0 +1 @@ +module github.com/konsorten/go-windows-terminal-sequences diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go new file mode 100644 index 0000000..ef18d8f --- /dev/null +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go @@ -0,0 +1,36 @@ +// +build windows + +package sequences + +import ( + "syscall" + "unsafe" +) + +var ( + kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll") + setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode") +) + +func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { + const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4 + + var mode uint32 + err := syscall.GetConsoleMode(syscall.Stdout, &mode) + if err != nil { + return err + } + + if enable { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING + } + + ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode)) + if ret == 0 { + return err + } + + return nil +} diff --git a/vendor/github.com/magiconair/properties/.gitignore b/vendor/github.com/magiconair/properties/.gitignore new file mode 100644 index 0000000..e7081ff --- /dev/null +++ b/vendor/github.com/magiconair/properties/.gitignore @@ -0,0 +1,6 @@ +*.sublime-project +*.sublime-workspace +*.un~ +*.swp +.idea/ +*.iml diff --git a/vendor/github.com/magiconair/properties/.travis.yml b/vendor/github.com/magiconair/properties/.travis.yml new file mode 100644 index 0000000..3e7c3d2 --- /dev/null +++ b/vendor/github.com/magiconair/properties/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - "1.10.x" + - tip diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md new file mode 100644 index 0000000..f83adc2 --- /dev/null +++ b/vendor/github.com/magiconair/properties/CHANGELOG.md @@ -0,0 +1,131 @@ +## Changelog + +### [1.8](https://github.com/magiconair/properties/tree/v1.8) - 15 May 2018 + + * [PR #26](https://github.com/magiconair/properties/pull/26): Disable expansion during loading + + This adds the option to disable property expansion during loading. + + Thanks to [@kmala](https://github.com/kmala) for the patch. + +### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018 + + * [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases. + + See PR for an example. + + Thanks to [@yobert](https://github.com/yobert) for the fix. + +### [1.7.5](https://github.com/magiconair/properties/tree/v1.7.5) - 13 Feb 2018 + + * [PR #28](https://github.com/magiconair/properties/pull/28): Support duplicate expansions in the same value + + Values which expand the same key multiple times (e.g. `key=${a} ${a}`) will no longer fail + with a `circular reference error`. + + Thanks to [@yobert](https://github.com/yobert) for the fix. + +### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017 + + * [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces + + * [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled + + Thanks to [@mgurov](https://github.com/mgurov) for the fix. + +### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017 + + * [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically + * [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map + +### [1.7.2](https://github.com/magiconair/properties/tree/v1.7.2) - 20 Mar 2017 + + * [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency + * [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc) + +### [1.7.1](https://github.com/magiconair/properties/tree/v1.7.1) - 13 Jan 2017 + + * [Issue #14](https://github.com/magiconair/properties/issues/14): Decouple TestLoadExpandedFile from `$USER` + * [PR #12](https://github.com/magiconair/properties/pull/12): Load from files and URLs + * [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy + * [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function + +### [1.7.0](https://github.com/magiconair/properties/tree/v1.7.0) - 20 Mar 2016 + + * [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL. + * [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string. + * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe) + +### [1.6.0](https://github.com/magiconair/properties/tree/v1.6.0) - 11 Dec 2015 + + * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags. + +### [1.5.6](https://github.com/magiconair/properties/tree/v1.5.6) - 18 Oct 2015 + + * Vendored in gopkg.in/check.v1 + +### [1.5.5](https://github.com/magiconair/properties/tree/v1.5.5) - 31 Jul 2015 + + * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs) + +### [1.5.4](https://github.com/magiconair/properties/tree/v1.5.4) - 23 Jun 2015 + + * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references. + +### [1.5.3](https://github.com/magiconair/properties/tree/v1.5.3) - 02 Jun 2015 + + * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) + +### [1.5.2](https://github.com/magiconair/properties/tree/v1.5.2) - 10 Apr 2015 + + * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty + * Add clickable links to README + +### [1.5.1](https://github.com/magiconair/properties/tree/v1.5.1) - 08 Dec 2014 + + * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with + [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration). + +### [1.5.0](https://github.com/magiconair/properties/tree/v1.5.0) - 18 Nov 2014 + + * Added support for single and multi-line comments (reading, writing and updating) + * The order of keys is now preserved + * Calling [Set()](http://godoc.org/github.com/magiconair/properties#Properties.Set) with an empty key now silently ignores the call and does not create a new entry + * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method + * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1) + +### [1.4.2](https://github.com/magiconair/properties/tree/v1.4.2) - 15 Nov 2014 + + * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one + +### [1.4.1](https://github.com/magiconair/properties/tree/v1.4.1) - 13 Nov 2014 + + * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string + +### [1.4.0](https://github.com/magiconair/properties/tree/v1.4.0) - 23 Sep 2014 + + * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys + * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties + +### [1.3.0](https://github.com/magiconair/properties/tree/v1.3.0) - 18 Mar 2014 + +* Added support for time.Duration +* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tree/vior configurable (log.Fatal, panic) - custom) +* Changed default of MustXXX() failure from panic to log.Fatal + +### [1.2.0](https://github.com/magiconair/properties/tree/v1.2.0) - 05 Mar 2014 + +* Added MustGet... functions +* Added support for int and uint with range checks on 32 bit platforms + +### [1.1.0](https://github.com/magiconair/properties/tree/v1.1.0) - 20 Jan 2014 + +* Renamed from goproperties to properties +* Added support for expansion of environment vars in + filenames and value expressions +* Fixed bug where value expressions were not at the + start of the string + +### [1.0.0](https://github.com/magiconair/properties/tree/v1.0.0) - 7 Jan 2014 + +* Initial release diff --git a/vendor/github.com/magiconair/properties/LICENSE b/vendor/github.com/magiconair/properties/LICENSE new file mode 100644 index 0000000..b387087 --- /dev/null +++ b/vendor/github.com/magiconair/properties/LICENSE @@ -0,0 +1,25 @@ +goproperties - properties file decoder for Go + +Copyright (c) 2013-2018 - Frank Schroeder + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/magiconair/properties/README.md b/vendor/github.com/magiconair/properties/README.md new file mode 100644 index 0000000..2c05f29 --- /dev/null +++ b/vendor/github.com/magiconair/properties/README.md @@ -0,0 +1,129 @@ +[![](https://img.shields.io/github/tag/magiconair/properties.svg?style=flat-square&label=release)](https://github.com/magiconair/properties/releases) +[![Travis CI Status](https://img.shields.io/travis/magiconair/properties.svg?branch=master&style=flat-square&label=travis)](https://travis-ci.org/magiconair/properties) +[![Codeship CI Status](https://img.shields.io/codeship/16aaf660-f615-0135-b8f0-7e33b70920c0/master.svg?label=codeship&style=flat-square)](https://app.codeship.com/projects/274177") +[![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg?style=flat-square)](https://raw.githubusercontent.com/magiconair/properties/master/LICENSE) +[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/magiconair/properties) + +# Overview + +#### Please run `git pull --tags` to update the tags. See [below](#updated-git-tags) why. + +properties is a Go library for reading and writing properties files. + +It supports reading from multiple files or URLs and Spring style recursive +property expansion of expressions like `${key}` to their corresponding value. +Value expressions can refer to other keys like in `${key}` or to environment +variables like in `${USER}`. Filenames can also contain environment variables +like in `/home/${USER}/myapp.properties`. + +Properties can be decoded into structs, maps, arrays and values through +struct tags. + +Comments and the order of keys are preserved. Comments can be modified +and can be written to the output. + +The properties library supports both ISO-8859-1 and UTF-8 encoded data. + +Starting from version 1.3.0 the behavior of the MustXXX() functions is +configurable by providing a custom `ErrorHandler` function. The default has +changed from `panic` to `log.Fatal` but this is configurable and custom +error handling functions can be provided. See the package documentation for +details. + +Read the full documentation on [GoDoc](https://godoc.org/github.com/magiconair/properties) [![GoDoc](https://godoc.org/github.com/magiconair/properties?status.png)](https://godoc.org/github.com/magiconair/properties) + +## Getting Started + +```go +import ( + "flag" + "github.com/magiconair/properties" +) + +func main() { + // init from a file + p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8) + + // or multiple files + p = properties.MustLoadFiles([]string{ + "${HOME}/config.properties", + "${HOME}/config-${USER}.properties", + }, properties.UTF8, true) + + // or from a map + p = properties.LoadMap(map[string]string{"key": "value", "abc": "def"}) + + // or from a string + p = properties.MustLoadString("key=value\nabc=def") + + // or from a URL + p = properties.MustLoadURL("http://host/path") + + // or from multiple URLs + p = properties.MustLoadURL([]string{ + "http://host/config", + "http://host/config-${USER}", + }, true) + + // or from flags + p.MustFlag(flag.CommandLine) + + // get values through getters + host := p.MustGetString("host") + port := p.GetInt("port", 8080) + + // or through Decode + type Config struct { + Host string `properties:"host"` + Port int `properties:"port,default=9000"` + Accept []string `properties:"accept,default=image/png;image;gif"` + Timeout time.Duration `properties:"timeout,default=5s"` + } + var cfg Config + if err := p.Decode(&cfg); err != nil { + log.Fatal(err) + } +} + +``` + +## Installation and Upgrade + +``` +$ go get -u github.com/magiconair/properties +``` + +## License + +2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details. + +## ToDo + +* Dump contents with passwords and secrets obscured + +## Updated Git tags + +#### 13 Feb 2018 + +I realized that all of the git tags I had pushed before v1.7.5 were lightweight tags +and I've only recently learned that this doesn't play well with `git describe` 😞 + +I have replaced all lightweight tags with signed tags using this script which should +retain the commit date, name and email address. Please run `git pull --tags` to update them. + +Worst case you have to reclone the repo. + +```shell +#!/bin/bash +tag=$1 +echo "Updating $tag" +date=$(git show ${tag}^0 --format=%aD | head -1) +email=$(git show ${tag}^0 --format=%aE | head -1) +name=$(git show ${tag}^0 --format=%aN | head -1) +GIT_COMMITTER_DATE="$date" GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" git tag -s -f ${tag} ${tag}^0 -m ${tag} +``` + +I apologize for the inconvenience. + +Frank + diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go new file mode 100644 index 0000000..3ebf804 --- /dev/null +++ b/vendor/github.com/magiconair/properties/decode.go @@ -0,0 +1,289 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" +) + +// Decode assigns property values to exported fields of a struct. +// +// Decode traverses v recursively and returns an error if a value cannot be +// converted to the field type or a required value is missing for a field. +// +// The following type dependent decodings are used: +// +// String, boolean, numeric fields have the value of the property key assigned. +// The property key name is the name of the field. A different key and a default +// value can be set in the field's tag. Fields without default value are +// required. If the value cannot be converted to the field type an error is +// returned. +// +// time.Duration fields have the result of time.ParseDuration() assigned. +// +// time.Time fields have the vaule of time.Parse() assigned. The default layout +// is time.RFC3339 but can be set in the field's tag. +// +// Arrays and slices of string, boolean, numeric, time.Duration and time.Time +// fields have the value interpreted as a comma separated list of values. The +// individual values are trimmed of whitespace and empty values are ignored. A +// default value can be provided as a semicolon separated list in the field's +// tag. +// +// Struct fields are decoded recursively using the field name plus "." as +// prefix. The prefix (without dot) can be overridden in the field's tag. +// Default values are not supported in the field's tag. Specify them on the +// fields of the inner struct instead. +// +// Map fields must have a key of type string and are decoded recursively by +// using the field's name plus ".' as prefix and the next element of the key +// name as map key. The prefix (without dot) can be overridden in the field's +// tag. Default values are not supported. +// +// Examples: +// +// // Field is ignored. +// Field int `properties:"-"` +// +// // Field is assigned value of 'Field'. +// Field int +// +// // Field is assigned value of 'myName'. +// Field int `properties:"myName"` +// +// // Field is assigned value of key 'myName' and has a default +// // value 15 if the key does not exist. +// Field int `properties:"myName,default=15"` +// +// // Field is assigned value of key 'Field' and has a default +// // value 15 if the key does not exist. +// Field int `properties:",default=15"` +// +// // Field is assigned value of key 'date' and the date +// // is in format 2006-01-02 +// Field time.Time `properties:"date,layout=2006-01-02"` +// +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas. +// Field []string +// +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas and has a default +// // value ["a", "b", "c"] if the key does not exist. +// Field []string `properties:",default=a;b;c"` +// +// // Field is decoded recursively with "Field." as key prefix. +// Field SomeStruct +// +// // Field is decoded recursively with "myName." as key prefix. +// Field SomeStruct `properties:"myName"` +// +// // Field is decoded recursively with "Field." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string +// +// // Field is decoded recursively with "myName." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string `properties:"myName"` +func (p *Properties) Decode(x interface{}) error { + t, v := reflect.TypeOf(x), reflect.ValueOf(x) + if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct { + return fmt.Errorf("not a pointer to struct: %s", t) + } + if err := dec(p, "", nil, nil, v); err != nil { + return err + } + return nil +} + +func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error { + t := v.Type() + + // value returns the property value for key or the default if provided. + value := func() (string, error) { + if val, ok := p.Get(key); ok { + return val, nil + } + if def != nil { + return *def, nil + } + return "", fmt.Errorf("missing required key %s", key) + } + + // conv converts a string to a value of the given type. + conv := func(s string, t reflect.Type) (val reflect.Value, err error) { + var v interface{} + + switch { + case isDuration(t): + v, err = time.ParseDuration(s) + + case isTime(t): + layout := opts["layout"] + if layout == "" { + layout = time.RFC3339 + } + v, err = time.Parse(layout, s) + + case isBool(t): + v, err = boolVal(s), nil + + case isString(t): + v, err = s, nil + + case isFloat(t): + v, err = strconv.ParseFloat(s, 64) + + case isInt(t): + v, err = strconv.ParseInt(s, 10, 64) + + case isUint(t): + v, err = strconv.ParseUint(s, 10, 64) + + default: + return reflect.Zero(t), fmt.Errorf("unsupported type %s", t) + } + if err != nil { + return reflect.Zero(t), err + } + return reflect.ValueOf(v).Convert(t), nil + } + + // keydef returns the property key and the default value based on the + // name of the struct field and the options in the tag. + keydef := func(f reflect.StructField) (string, *string, map[string]string) { + _key, _opts := parseTag(f.Tag.Get("properties")) + + var _def *string + if d, ok := _opts["default"]; ok { + _def = &d + } + if _key != "" { + return _key, _def, _opts + } + return f.Name, _def, _opts + } + + switch { + case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t): + s, err := value() + if err != nil { + return err + } + val, err := conv(s, t) + if err != nil { + return err + } + v.Set(val) + + case isPtr(t): + return dec(p, key, def, opts, v.Elem()) + + case isStruct(t): + for i := 0; i < v.NumField(); i++ { + fv := v.Field(i) + fk, def, opts := keydef(t.Field(i)) + if !fv.CanSet() { + return fmt.Errorf("cannot set %s", t.Field(i).Name) + } + if fk == "-" { + continue + } + if key != "" { + fk = key + "." + fk + } + if err := dec(p, fk, def, opts, fv); err != nil { + return err + } + } + return nil + + case isArray(t): + val, err := value() + if err != nil { + return err + } + vals := split(val, ";") + a := reflect.MakeSlice(t, 0, len(vals)) + for _, s := range vals { + val, err := conv(s, t.Elem()) + if err != nil { + return err + } + a = reflect.Append(a, val) + } + v.Set(a) + + case isMap(t): + valT := t.Elem() + m := reflect.MakeMap(t) + for postfix := range p.FilterStripPrefix(key + ".").m { + pp := strings.SplitN(postfix, ".", 2) + mk, mv := pp[0], reflect.New(valT) + if err := dec(p, key+"."+mk, nil, nil, mv); err != nil { + return err + } + m.SetMapIndex(reflect.ValueOf(mk), mv.Elem()) + } + v.Set(m) + + default: + return fmt.Errorf("unsupported type %s", t) + } + return nil +} + +// split splits a string on sep, trims whitespace of elements +// and omits empty elements +func split(s string, sep string) []string { + var a []string + for _, v := range strings.Split(s, sep) { + if v = strings.TrimSpace(v); v != "" { + a = append(a, v) + } + } + return a +} + +// parseTag parses a "key,k=v,k=v,..." +func parseTag(tag string) (key string, opts map[string]string) { + opts = map[string]string{} + for i, s := range strings.Split(tag, ",") { + if i == 0 { + key = s + continue + } + + pp := strings.SplitN(s, "=", 2) + if len(pp) == 1 { + opts[pp[0]] = "" + } else { + opts[pp[0]] = pp[1] + } + } + return key, opts +} + +func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice } +func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool } +func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) } +func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map } +func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr } +func isString(t reflect.Type) bool { return t.Kind() == reflect.String } +func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct } +func isTime(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) } +func isFloat(t reflect.Type) bool { + return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64 +} +func isInt(t reflect.Type) bool { + return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64 +} +func isUint(t reflect.Type) bool { + return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64 +} diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go new file mode 100644 index 0000000..f8822da --- /dev/null +++ b/vendor/github.com/magiconair/properties/doc.go @@ -0,0 +1,156 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package properties provides functions for reading and writing +// ISO-8859-1 and UTF-8 encoded .properties files and has +// support for recursive property expansion. +// +// Java properties files are ISO-8859-1 encoded and use Unicode +// literals for characters outside the ISO character set. Unicode +// literals can be used in UTF-8 encoded properties files but +// aren't necessary. +// +// To load a single properties file use MustLoadFile(): +// +// p := properties.MustLoadFile(filename, properties.UTF8) +// +// To load multiple properties files use MustLoadFiles() +// which loads the files in the given order and merges the +// result. Missing properties files can be ignored if the +// 'ignoreMissing' flag is set to true. +// +// Filenames can contain environment variables which are expanded +// before loading. +// +// f1 := "/etc/myapp/myapp.conf" +// f2 := "/home/${USER}/myapp.conf" +// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) +// +// All of the different key/value delimiters ' ', ':' and '=' are +// supported as well as the comment characters '!' and '#' and +// multi-line values. +// +// ! this is a comment +// # and so is this +// +// # the following expressions are equal +// key value +// key=value +// key:value +// key = value +// key : value +// key = val\ +// ue +// +// Properties stores all comments preceding a key and provides +// GetComments() and SetComments() methods to retrieve and +// update them. The convenience functions GetComment() and +// SetComment() allow access to the last comment. The +// WriteComment() method writes properties files including +// the comments and with the keys in the original order. +// This can be used for sanitizing properties files. +// +// Property expansion is recursive and circular references +// and malformed expressions are not allowed and cause an +// error. Expansion of environment variables is supported. +// +// # standard property +// key = value +// +// # property expansion: key2 = value +// key2 = ${key} +// +// # recursive expansion: key3 = value +// key3 = ${key2} +// +// # circular reference (error) +// key = ${key} +// +// # malformed expression (error) +// key = ${ke +// +// # refers to the users' home dir +// home = ${HOME} +// +// # local key takes precedence over env var: u = foo +// USER = foo +// u = ${USER} +// +// The default property expansion format is ${key} but can be +// changed by setting different pre- and postfix values on the +// Properties object. +// +// p := properties.NewProperties() +// p.Prefix = "#[" +// p.Postfix = "]#" +// +// Properties provides convenience functions for getting typed +// values with default values if the key does not exist or the +// type conversion failed. +// +// # Returns true if the value is either "1", "on", "yes" or "true" +// # Returns false for every other value and the default value if +// # the key does not exist. +// v = p.GetBool("key", false) +// +// # Returns the value if the key exists and the format conversion +// # was successful. Otherwise, the default value is returned. +// v = p.GetInt64("key", 999) +// v = p.GetUint64("key", 999) +// v = p.GetFloat64("key", 123.0) +// v = p.GetString("key", "def") +// v = p.GetDuration("key", 999) +// +// As an alternative properties may be applied with the standard +// library's flag implementation at any time. +// +// # Standard configuration +// v = flag.Int("key", 999, "help message") +// flag.Parse() +// +// # Merge p into the flag set +// p.MustFlag(flag.CommandLine) +// +// Properties provides several MustXXX() convenience functions +// which will terminate the app if an error occurs. The behavior +// of the failure is configurable and the default is to call +// log.Fatal(err). To have the MustXXX() functions panic instead +// of logging the error set a different ErrorHandler before +// you use the Properties package. +// +// properties.ErrorHandler = properties.PanicHandler +// +// # Will panic instead of logging an error +// p := properties.MustLoadFile("config.properties") +// +// You can also provide your own ErrorHandler function. The only requirement +// is that the error handler function must exit after handling the error. +// +// properties.ErrorHandler = func(err error) { +// fmt.Println(err) +// os.Exit(1) +// } +// +// # Will write to stdout and then exit +// p := properties.MustLoadFile("config.properties") +// +// Properties can also be loaded into a struct via the `Decode` +// method, e.g. +// +// type S struct { +// A string `properties:"a,default=foo"` +// D time.Duration `properties:"timeout,default=5s"` +// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` +// } +// +// See `Decode()` method for the full documentation. +// +// The following documents provide a description of the properties +// file format. +// +// http://en.wikipedia.org/wiki/.properties +// +// http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29 +// +package properties diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go new file mode 100644 index 0000000..74d38dc --- /dev/null +++ b/vendor/github.com/magiconair/properties/integrate.go @@ -0,0 +1,34 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +import "flag" + +// MustFlag sets flags that are skipped by dst.Parse when p contains +// the respective key for flag.Flag.Name. +// +// It's use is recommended with command line arguments as in: +// flag.Parse() +// p.MustFlag(flag.CommandLine) +func (p *Properties) MustFlag(dst *flag.FlagSet) { + m := make(map[string]*flag.Flag) + dst.VisitAll(func(f *flag.Flag) { + m[f.Name] = f + }) + dst.Visit(func(f *flag.Flag) { + delete(m, f.Name) // overridden + }) + + for name, f := range m { + v, ok := p.Get(name) + if !ok { + continue + } + + if err := f.Value.Set(v); err != nil { + ErrorHandler(err) + } + } +} diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go new file mode 100644 index 0000000..367166d --- /dev/null +++ b/vendor/github.com/magiconair/properties/lex.go @@ -0,0 +1,407 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// Parts of the lexer are from the template/text/parser package +// For these parts the following applies: +// +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file of the go 1.2 +// distribution. + +package properties + +import ( + "fmt" + "strconv" + "strings" + "unicode/utf8" +) + +// item represents a token or text string returned from the scanner. +type item struct { + typ itemType // The type of this item. + pos int // The starting position, in bytes, of this item in the input string. + val string // The value of this item. +} + +func (i item) String() string { + switch { + case i.typ == itemEOF: + return "EOF" + case i.typ == itemError: + return i.val + case len(i.val) > 10: + return fmt.Sprintf("%.10q...", i.val) + } + return fmt.Sprintf("%q", i.val) +} + +// itemType identifies the type of lex items. +type itemType int + +const ( + itemError itemType = iota // error occurred; value is text of error + itemEOF + itemKey // a key + itemValue // a value + itemComment // a comment +) + +// defines a constant for EOF +const eof = -1 + +// permitted whitespace characters space, FF and TAB +const whitespace = " \f\t" + +// stateFn represents the state of the scanner as a function that returns the next state. +type stateFn func(*lexer) stateFn + +// lexer holds the state of the scanner. +type lexer struct { + input string // the string being scanned + state stateFn // the next lexing function to enter + pos int // current position in the input + start int // start position of this item + width int // width of last rune read from input + lastPos int // position of most recent item returned by nextItem + runes []rune // scanned runes for this item + items chan item // channel of scanned items +} + +// next returns the next rune in the input. +func (l *lexer) next() rune { + if l.pos >= len(l.input) { + l.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(l.input[l.pos:]) + l.width = w + l.pos += l.width + return r +} + +// peek returns but does not consume the next rune in the input. +func (l *lexer) peek() rune { + r := l.next() + l.backup() + return r +} + +// backup steps back one rune. Can only be called once per call of next. +func (l *lexer) backup() { + l.pos -= l.width +} + +// emit passes an item back to the client. +func (l *lexer) emit(t itemType) { + i := item{t, l.start, string(l.runes)} + l.items <- i + l.start = l.pos + l.runes = l.runes[:0] +} + +// ignore skips over the pending input before this point. +func (l *lexer) ignore() { + l.start = l.pos +} + +// appends the rune to the current value +func (l *lexer) appendRune(r rune) { + l.runes = append(l.runes, r) +} + +// accept consumes the next rune if it's from the valid set. +func (l *lexer) accept(valid string) bool { + if strings.ContainsRune(valid, l.next()) { + return true + } + l.backup() + return false +} + +// acceptRun consumes a run of runes from the valid set. +func (l *lexer) acceptRun(valid string) { + for strings.ContainsRune(valid, l.next()) { + } + l.backup() +} + +// acceptRunUntil consumes a run of runes up to a terminator. +func (l *lexer) acceptRunUntil(term rune) { + for term != l.next() { + } + l.backup() +} + +// hasText returns true if the current parsed text is not empty. +func (l *lexer) isNotEmpty() bool { + return l.pos > l.start +} + +// lineNumber reports which line we're on, based on the position of +// the previous item returned by nextItem. Doing it this way +// means we don't have to worry about peek double counting. +func (l *lexer) lineNumber() int { + return 1 + strings.Count(l.input[:l.lastPos], "\n") +} + +// errorf returns an error token and terminates the scan by passing +// back a nil pointer that will be the next state, terminating l.nextItem. +func (l *lexer) errorf(format string, args ...interface{}) stateFn { + l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} + return nil +} + +// nextItem returns the next item from the input. +func (l *lexer) nextItem() item { + i := <-l.items + l.lastPos = i.pos + return i +} + +// lex creates a new scanner for the input string. +func lex(input string) *lexer { + l := &lexer{ + input: input, + items: make(chan item), + runes: make([]rune, 0, 32), + } + go l.run() + return l +} + +// run runs the state machine for the lexer. +func (l *lexer) run() { + for l.state = lexBeforeKey(l); l.state != nil; { + l.state = l.state(l) + } +} + +// state functions + +// lexBeforeKey scans until a key begins. +func lexBeforeKey(l *lexer) stateFn { + switch r := l.next(); { + case isEOF(r): + l.emit(itemEOF) + return nil + + case isEOL(r): + l.ignore() + return lexBeforeKey + + case isComment(r): + return lexComment + + case isWhitespace(r): + l.ignore() + return lexBeforeKey + + default: + l.backup() + return lexKey + } +} + +// lexComment scans a comment line. The comment character has already been scanned. +func lexComment(l *lexer) stateFn { + l.acceptRun(whitespace) + l.ignore() + for { + switch r := l.next(); { + case isEOF(r): + l.ignore() + l.emit(itemEOF) + return nil + case isEOL(r): + l.emit(itemComment) + return lexBeforeKey + default: + l.appendRune(r) + } + } +} + +// lexKey scans the key up to a delimiter +func lexKey(l *lexer) stateFn { + var r rune + +Loop: + for { + switch r = l.next(); { + + case isEscape(r): + err := l.scanEscapeSequence() + if err != nil { + return l.errorf(err.Error()) + } + + case isEndOfKey(r): + l.backup() + break Loop + + case isEOF(r): + break Loop + + default: + l.appendRune(r) + } + } + + if len(l.runes) > 0 { + l.emit(itemKey) + } + + if isEOF(r) { + l.emit(itemEOF) + return nil + } + + return lexBeforeValue +} + +// lexBeforeValue scans the delimiter between key and value. +// Leading and trailing whitespace is ignored. +// We expect to be just after the key. +func lexBeforeValue(l *lexer) stateFn { + l.acceptRun(whitespace) + l.accept(":=") + l.acceptRun(whitespace) + l.ignore() + return lexValue +} + +// lexValue scans text until the end of the line. We expect to be just after the delimiter. +func lexValue(l *lexer) stateFn { + for { + switch r := l.next(); { + case isEscape(r): + if isEOL(l.peek()) { + l.next() + l.acceptRun(whitespace) + } else { + err := l.scanEscapeSequence() + if err != nil { + return l.errorf(err.Error()) + } + } + + case isEOL(r): + l.emit(itemValue) + l.ignore() + return lexBeforeKey + + case isEOF(r): + l.emit(itemValue) + l.emit(itemEOF) + return nil + + default: + l.appendRune(r) + } + } +} + +// scanEscapeSequence scans either one of the escaped characters +// or a unicode literal. We expect to be after the escape character. +func (l *lexer) scanEscapeSequence() error { + switch r := l.next(); { + + case isEscapedCharacter(r): + l.appendRune(decodeEscapedCharacter(r)) + return nil + + case atUnicodeLiteral(r): + return l.scanUnicodeLiteral() + + case isEOF(r): + return fmt.Errorf("premature EOF") + + // silently drop the escape character and append the rune as is + default: + l.appendRune(r) + return nil + } +} + +// scans a unicode literal in the form \uXXXX. We expect to be after the \u. +func (l *lexer) scanUnicodeLiteral() error { + // scan the digits + d := make([]rune, 4) + for i := 0; i < 4; i++ { + d[i] = l.next() + if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) { + return fmt.Errorf("invalid unicode literal") + } + } + + // decode the digits into a rune + r, err := strconv.ParseInt(string(d), 16, 0) + if err != nil { + return err + } + + l.appendRune(rune(r)) + return nil +} + +// decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character. +func decodeEscapedCharacter(r rune) rune { + switch r { + case 'f': + return '\f' + case 'n': + return '\n' + case 'r': + return '\r' + case 't': + return '\t' + default: + return r + } +} + +// atUnicodeLiteral reports whether we are at a unicode literal. +// The escape character has already been consumed. +func atUnicodeLiteral(r rune) bool { + return r == 'u' +} + +// isComment reports whether we are at the start of a comment. +func isComment(r rune) bool { + return r == '#' || r == '!' +} + +// isEndOfKey reports whether the rune terminates the current key. +func isEndOfKey(r rune) bool { + return strings.ContainsRune(" \f\t\r\n:=", r) +} + +// isEOF reports whether we are at EOF. +func isEOF(r rune) bool { + return r == eof +} + +// isEOL reports whether we are at a new line character. +func isEOL(r rune) bool { + return r == '\n' || r == '\r' +} + +// isEscape reports whether the rune is the escape character which +// prefixes unicode literals and other escaped characters. +func isEscape(r rune) bool { + return r == '\\' +} + +// isEscapedCharacter reports whether we are at one of the characters that need escaping. +// The escape character has already been consumed. +func isEscapedCharacter(r rune) bool { + return strings.ContainsRune(" :=fnrt", r) +} + +// isWhitespace reports whether the rune is a whitespace character. +func isWhitespace(r rune) bool { + return strings.ContainsRune(whitespace, r) +} diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go new file mode 100644 index 0000000..c8e1b58 --- /dev/null +++ b/vendor/github.com/magiconair/properties/load.go @@ -0,0 +1,292 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" +) + +// Encoding specifies encoding of the input data. +type Encoding uint + +const ( + // utf8Default is a private placeholder for the zero value of Encoding to + // ensure that it has the correct meaning. UTF8 is the default encoding but + // was assigned a non-zero value which cannot be changed without breaking + // existing code. Clients should continue to use the public constants. + utf8Default Encoding = iota + + // UTF8 interprets the input data as UTF-8. + UTF8 + + // ISO_8859_1 interprets the input data as ISO-8859-1. + ISO_8859_1 +) + +type Loader struct { + // Encoding determines how the data from files and byte buffers + // is interpreted. For URLs the Content-Type header is used + // to determine the encoding of the data. + Encoding Encoding + + // DisableExpansion configures the property expansion of the + // returned property object. When set to true, the property values + // will not be expanded and the Property object will not be checked + // for invalid expansion expressions. + DisableExpansion bool + + // IgnoreMissing configures whether missing files or URLs which return + // 404 are reported as errors. When set to true, missing files and 404 + // status codes are not reported as errors. + IgnoreMissing bool +} + +// Load reads a buffer into a Properties struct. +func (l *Loader) LoadBytes(buf []byte) (*Properties, error) { + return l.loadBytes(buf, l.Encoding) +} + +// LoadAll reads the content of multiple URLs or files in the given order into +// a Properties struct. If IgnoreMissing is true then a 404 status code or +// missing file will not be reported as error. Encoding sets the encoding for +// files. For the URLs see LoadURL for the Content-Type header and the +// encoding. +func (l *Loader) LoadAll(names []string) (*Properties, error) { + all := NewProperties() + for _, name := range names { + n, err := expandName(name) + if err != nil { + return nil, err + } + + var p *Properties + switch { + case strings.HasPrefix(n, "http://"): + p, err = l.LoadURL(n) + case strings.HasPrefix(n, "https://"): + p, err = l.LoadURL(n) + default: + p, err = l.LoadFile(n) + } + if err != nil { + return nil, err + } + all.Merge(p) + } + + all.DisableExpansion = l.DisableExpansion + if all.DisableExpansion { + return all, nil + } + return all, all.check() +} + +// LoadFile reads a file into a Properties struct. +// If IgnoreMissing is true then a missing file will not be +// reported as error. +func (l *Loader) LoadFile(filename string) (*Properties, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + if l.IgnoreMissing && os.IsNotExist(err) { + LogPrintf("properties: %s not found. skipping", filename) + return NewProperties(), nil + } + return nil, err + } + return l.loadBytes(data, l.Encoding) +} + +// LoadURL reads the content of the URL into a Properties struct. +// +// The encoding is determined via the Content-Type header which +// should be set to 'text/plain'. If the 'charset' parameter is +// missing, 'iso-8859-1' or 'latin1' the encoding is set to +// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the +// encoding is set to UTF-8. A missing content type header is +// interpreted as 'text/plain; charset=utf-8'. +func (l *Loader) LoadURL(url string) (*Properties, error) { + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("properties: error fetching %q. %s", url, err) + } + + if resp.StatusCode == 404 && l.IgnoreMissing { + LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode) + return NewProperties(), nil + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("properties: %s error reading response. %s", url, err) + } + defer resp.Body.Close() + + ct := resp.Header.Get("Content-Type") + var enc Encoding + switch strings.ToLower(ct) { + case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1": + enc = ISO_8859_1 + case "", "text/plain; charset=utf-8": + enc = UTF8 + default: + return nil, fmt.Errorf("properties: invalid content type %s", ct) + } + + return l.loadBytes(body, enc) +} + +func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) { + p, err := parse(convert(buf, enc)) + if err != nil { + return nil, err + } + p.DisableExpansion = l.DisableExpansion + if p.DisableExpansion { + return p, nil + } + return p, p.check() +} + +// Load reads a buffer into a Properties struct. +func Load(buf []byte, enc Encoding) (*Properties, error) { + l := &Loader{Encoding: enc} + return l.LoadBytes(buf) +} + +// LoadString reads an UTF8 string into a properties struct. +func LoadString(s string) (*Properties, error) { + l := &Loader{Encoding: UTF8} + return l.LoadBytes([]byte(s)) +} + +// LoadMap creates a new Properties struct from a string map. +func LoadMap(m map[string]string) *Properties { + p := NewProperties() + for k, v := range m { + p.Set(k, v) + } + return p +} + +// LoadFile reads a file into a Properties struct. +func LoadFile(filename string, enc Encoding) (*Properties, error) { + l := &Loader{Encoding: enc} + return l.LoadAll([]string{filename}) +} + +// LoadFiles reads multiple files in the given order into +// a Properties struct. If 'ignoreMissing' is true then +// non-existent files will not be reported as error. +func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { + l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} + return l.LoadAll(filenames) +} + +// LoadURL reads the content of the URL into a Properties struct. +// See Loader#LoadURL for details. +func LoadURL(url string) (*Properties, error) { + l := &Loader{Encoding: UTF8} + return l.LoadAll([]string{url}) +} + +// LoadURLs reads the content of multiple URLs in the given order into a +// Properties struct. If IgnoreMissing is true then a 404 status code will +// not be reported as error. See Loader#LoadURL for the Content-Type header +// and the encoding. +func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) { + l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing} + return l.LoadAll(urls) +} + +// LoadAll reads the content of multiple URLs or files in the given order into a +// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will +// not be reported as error. Encoding sets the encoding for files. For the URLs please see +// LoadURL for the Content-Type header and the encoding. +func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) { + l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} + return l.LoadAll(names) +} + +// MustLoadString reads an UTF8 string into a Properties struct and +// panics on error. +func MustLoadString(s string) *Properties { + return must(LoadString(s)) +} + +// MustLoadFile reads a file into a Properties struct and +// panics on error. +func MustLoadFile(filename string, enc Encoding) *Properties { + return must(LoadFile(filename, enc)) +} + +// MustLoadFiles reads multiple files in the given order into +// a Properties struct and panics on error. If 'ignoreMissing' +// is true then non-existent files will not be reported as error. +func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties { + return must(LoadFiles(filenames, enc, ignoreMissing)) +} + +// MustLoadURL reads the content of a URL into a Properties struct and +// panics on error. +func MustLoadURL(url string) *Properties { + return must(LoadURL(url)) +} + +// MustLoadURLs reads the content of multiple URLs in the given order into a +// Properties struct and panics on error. If 'ignoreMissing' is true then a 404 +// status code will not be reported as error. +func MustLoadURLs(urls []string, ignoreMissing bool) *Properties { + return must(LoadURLs(urls, ignoreMissing)) +} + +// MustLoadAll reads the content of multiple URLs or files in the given order into a +// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will +// not be reported as error. Encoding sets the encoding for files. For the URLs please see +// LoadURL for the Content-Type header and the encoding. It panics on error. +func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties { + return must(LoadAll(names, enc, ignoreMissing)) +} + +func must(p *Properties, err error) *Properties { + if err != nil { + ErrorHandler(err) + } + return p +} + +// expandName expands ${ENV_VAR} expressions in a name. +// If the environment variable does not exist then it will be replaced +// with an empty string. Malformed expressions like "${ENV_VAR" will +// be reported as error. +func expandName(name string) (string, error) { + return expand(name, []string{}, "${", "}", make(map[string]string)) +} + +// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string. +// For ISO-8859-1 we can convert each byte straight into a rune since the +// first 256 unicode code points cover ISO-8859-1. +func convert(buf []byte, enc Encoding) string { + switch enc { + case utf8Default, UTF8: + return string(buf) + case ISO_8859_1: + runes := make([]rune, len(buf)) + for i, b := range buf { + runes[i] = rune(b) + } + return string(runes) + default: + ErrorHandler(fmt.Errorf("unsupported encoding %v", enc)) + } + panic("ErrorHandler should exit") +} diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go new file mode 100644 index 0000000..cdc4a80 --- /dev/null +++ b/vendor/github.com/magiconair/properties/parser.go @@ -0,0 +1,95 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +import ( + "fmt" + "runtime" +) + +type parser struct { + lex *lexer +} + +func parse(input string) (properties *Properties, err error) { + p := &parser{lex: lex(input)} + defer p.recover(&err) + + properties = NewProperties() + key := "" + comments := []string{} + + for { + token := p.expectOneOf(itemComment, itemKey, itemEOF) + switch token.typ { + case itemEOF: + goto done + case itemComment: + comments = append(comments, token.val) + continue + case itemKey: + key = token.val + if _, ok := properties.m[key]; !ok { + properties.k = append(properties.k, key) + } + } + + token = p.expectOneOf(itemValue, itemEOF) + if len(comments) > 0 { + properties.c[key] = comments + comments = []string{} + } + switch token.typ { + case itemEOF: + properties.m[key] = "" + goto done + case itemValue: + properties.m[key] = token.val + } + } + +done: + return properties, nil +} + +func (p *parser) errorf(format string, args ...interface{}) { + format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format) + panic(fmt.Errorf(format, args...)) +} + +func (p *parser) expect(expected itemType) (token item) { + token = p.lex.nextItem() + if token.typ != expected { + p.unexpected(token) + } + return token +} + +func (p *parser) expectOneOf(expected ...itemType) (token item) { + token = p.lex.nextItem() + for _, v := range expected { + if token.typ == v { + return token + } + } + p.unexpected(token) + panic("unexpected token") +} + +func (p *parser) unexpected(token item) { + p.errorf(token.String()) +} + +// recover is the handler that turns panics into returns from the top level of Parse. +func (p *parser) recover(errp *error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + *errp = e.(error) + } + return +} diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go new file mode 100644 index 0000000..cb3d1a3 --- /dev/null +++ b/vendor/github.com/magiconair/properties/properties.go @@ -0,0 +1,833 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +// BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer. +// BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used. + +import ( + "fmt" + "io" + "log" + "os" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +const maxExpansionDepth = 64 + +// ErrorHandlerFunc defines the type of function which handles failures +// of the MustXXX() functions. An error handler function must exit +// the application after handling the error. +type ErrorHandlerFunc func(error) + +// ErrorHandler is the function which handles failures of the MustXXX() +// functions. The default is LogFatalHandler. +var ErrorHandler ErrorHandlerFunc = LogFatalHandler + +// LogHandlerFunc defines the function prototype for logging errors. +type LogHandlerFunc func(fmt string, args ...interface{}) + +// LogPrintf defines a log handler which uses log.Printf. +var LogPrintf LogHandlerFunc = log.Printf + +// LogFatalHandler handles the error by logging a fatal error and exiting. +func LogFatalHandler(err error) { + log.Fatal(err) +} + +// PanicHandler handles the error by panicking. +func PanicHandler(err error) { + panic(err) +} + +// ----------------------------------------------------------------------------- + +// A Properties contains the key/value pairs from the properties input. +// All values are stored in unexpanded form and are expanded at runtime +type Properties struct { + // Pre-/Postfix for property expansion. + Prefix string + Postfix string + + // DisableExpansion controls the expansion of properties on Get() + // and the check for circular references on Set(). When set to + // true Properties behaves like a simple key/value store and does + // not check for circular references on Get() or on Set(). + DisableExpansion bool + + // Stores the key/value pairs + m map[string]string + + // Stores the comments per key. + c map[string][]string + + // Stores the keys in order of appearance. + k []string +} + +// NewProperties creates a new Properties struct with the default +// configuration for "${key}" expressions. +func NewProperties() *Properties { + return &Properties{ + Prefix: "${", + Postfix: "}", + m: map[string]string{}, + c: map[string][]string{}, + k: []string{}, + } +} + +// Load reads a buffer into the given Properties struct. +func (p *Properties) Load(buf []byte, enc Encoding) error { + l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion} + newProperties, err := l.LoadBytes(buf) + if err != nil { + return err + } + p.Merge(newProperties) + return nil +} + +// Get returns the expanded value for the given key if exists. +// Otherwise, ok is false. +func (p *Properties) Get(key string) (value string, ok bool) { + v, ok := p.m[key] + if p.DisableExpansion { + return v, ok + } + if !ok { + return "", false + } + + expanded, err := p.expand(key, v) + + // we guarantee that the expanded value is free of + // circular references and malformed expressions + // so we panic if we still get an error here. + if err != nil { + ErrorHandler(fmt.Errorf("%s in %q", err, key+" = "+v)) + } + + return expanded, true +} + +// MustGet returns the expanded value for the given key if exists. +// Otherwise, it panics. +func (p *Properties) MustGet(key string) string { + if v, ok := p.Get(key); ok { + return v + } + ErrorHandler(invalidKeyError(key)) + panic("ErrorHandler should exit") +} + +// ---------------------------------------------------------------------------- + +// ClearComments removes the comments for all keys. +func (p *Properties) ClearComments() { + p.c = map[string][]string{} +} + +// ---------------------------------------------------------------------------- + +// GetComment returns the last comment before the given key or an empty string. +func (p *Properties) GetComment(key string) string { + comments, ok := p.c[key] + if !ok || len(comments) == 0 { + return "" + } + return comments[len(comments)-1] +} + +// ---------------------------------------------------------------------------- + +// GetComments returns all comments that appeared before the given key or nil. +func (p *Properties) GetComments(key string) []string { + if comments, ok := p.c[key]; ok { + return comments + } + return nil +} + +// ---------------------------------------------------------------------------- + +// SetComment sets the comment for the key. +func (p *Properties) SetComment(key, comment string) { + p.c[key] = []string{comment} +} + +// ---------------------------------------------------------------------------- + +// SetComments sets the comments for the key. If the comments are nil then +// all comments for this key are deleted. +func (p *Properties) SetComments(key string, comments []string) { + if comments == nil { + delete(p.c, key) + return + } + p.c[key] = comments +} + +// ---------------------------------------------------------------------------- + +// GetBool checks if the expanded value is one of '1', 'yes', +// 'true' or 'on' if the key exists. The comparison is case-insensitive. +// If the key does not exist the default value is returned. +func (p *Properties) GetBool(key string, def bool) bool { + v, err := p.getBool(key) + if err != nil { + return def + } + return v +} + +// MustGetBool checks if the expanded value is one of '1', 'yes', +// 'true' or 'on' if the key exists. The comparison is case-insensitive. +// If the key does not exist the function panics. +func (p *Properties) MustGetBool(key string) bool { + v, err := p.getBool(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getBool(key string) (value bool, err error) { + if v, ok := p.Get(key); ok { + return boolVal(v), nil + } + return false, invalidKeyError(key) +} + +func boolVal(v string) bool { + v = strings.ToLower(v) + return v == "1" || v == "true" || v == "yes" || v == "on" +} + +// ---------------------------------------------------------------------------- + +// GetDuration parses the expanded value as an time.Duration (in ns) if the +// key exists. If key does not exist or the value cannot be parsed the default +// value is returned. In almost all cases you want to use GetParsedDuration(). +func (p *Properties) GetDuration(key string, def time.Duration) time.Duration { + v, err := p.getInt64(key) + if err != nil { + return def + } + return time.Duration(v) +} + +// MustGetDuration parses the expanded value as an time.Duration (in ns) if +// the key exists. If key does not exist or the value cannot be parsed the +// function panics. In almost all cases you want to use MustGetParsedDuration(). +func (p *Properties) MustGetDuration(key string) time.Duration { + v, err := p.getInt64(key) + if err != nil { + ErrorHandler(err) + } + return time.Duration(v) +} + +// ---------------------------------------------------------------------------- + +// GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration { + s, ok := p.Get(key) + if !ok { + return def + } + v, err := time.ParseDuration(s) + if err != nil { + return def + } + return v +} + +// MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetParsedDuration(key string) time.Duration { + s, ok := p.Get(key) + if !ok { + ErrorHandler(invalidKeyError(key)) + } + v, err := time.ParseDuration(s) + if err != nil { + ErrorHandler(err) + } + return v +} + +// ---------------------------------------------------------------------------- + +// GetFloat64 parses the expanded value as a float64 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetFloat64(key string, def float64) float64 { + v, err := p.getFloat64(key) + if err != nil { + return def + } + return v +} + +// MustGetFloat64 parses the expanded value as a float64 if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetFloat64(key string) float64 { + v, err := p.getFloat64(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getFloat64(key string) (value float64, err error) { + if v, ok := p.Get(key); ok { + value, err = strconv.ParseFloat(v, 64) + if err != nil { + return 0, err + } + return value, nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + +// GetInt parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. If the value does not fit into an int the +// function panics with an out of range error. +func (p *Properties) GetInt(key string, def int) int { + v, err := p.getInt64(key) + if err != nil { + return def + } + return intRangeCheck(key, v) +} + +// MustGetInt parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +// If the value does not fit into an int the function panics with +// an out of range error. +func (p *Properties) MustGetInt(key string) int { + v, err := p.getInt64(key) + if err != nil { + ErrorHandler(err) + } + return intRangeCheck(key, v) +} + +// ---------------------------------------------------------------------------- + +// GetInt64 parses the expanded value as an int64 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetInt64(key string, def int64) int64 { + v, err := p.getInt64(key) + if err != nil { + return def + } + return v +} + +// MustGetInt64 parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetInt64(key string) int64 { + v, err := p.getInt64(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getInt64(key string) (value int64, err error) { + if v, ok := p.Get(key); ok { + value, err = strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, err + } + return value, nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + +// GetUint parses the expanded value as an uint if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. If the value does not fit into an int the +// function panics with an out of range error. +func (p *Properties) GetUint(key string, def uint) uint { + v, err := p.getUint64(key) + if err != nil { + return def + } + return uintRangeCheck(key, v) +} + +// MustGetUint parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +// If the value does not fit into an int the function panics with +// an out of range error. +func (p *Properties) MustGetUint(key string) uint { + v, err := p.getUint64(key) + if err != nil { + ErrorHandler(err) + } + return uintRangeCheck(key, v) +} + +// ---------------------------------------------------------------------------- + +// GetUint64 parses the expanded value as an uint64 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetUint64(key string, def uint64) uint64 { + v, err := p.getUint64(key) + if err != nil { + return def + } + return v +} + +// MustGetUint64 parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetUint64(key string) uint64 { + v, err := p.getUint64(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getUint64(key string) (value uint64, err error) { + if v, ok := p.Get(key); ok { + value, err = strconv.ParseUint(v, 10, 64) + if err != nil { + return 0, err + } + return value, nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + +// GetString returns the expanded value for the given key if exists or +// the default value otherwise. +func (p *Properties) GetString(key, def string) string { + if v, ok := p.Get(key); ok { + return v + } + return def +} + +// MustGetString returns the expanded value for the given key if exists or +// panics otherwise. +func (p *Properties) MustGetString(key string) string { + if v, ok := p.Get(key); ok { + return v + } + ErrorHandler(invalidKeyError(key)) + panic("ErrorHandler should exit") +} + +// ---------------------------------------------------------------------------- + +// Filter returns a new properties object which contains all properties +// for which the key matches the pattern. +func (p *Properties) Filter(pattern string) (*Properties, error) { + re, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + + return p.FilterRegexp(re), nil +} + +// FilterRegexp returns a new properties object which contains all properties +// for which the key matches the regular expression. +func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties { + pp := NewProperties() + for _, k := range p.k { + if re.MatchString(k) { + // TODO(fs): we are ignoring the error which flags a circular reference. + // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) + pp.Set(k, p.m[k]) + } + } + return pp +} + +// FilterPrefix returns a new properties object with a subset of all keys +// with the given prefix. +func (p *Properties) FilterPrefix(prefix string) *Properties { + pp := NewProperties() + for _, k := range p.k { + if strings.HasPrefix(k, prefix) { + // TODO(fs): we are ignoring the error which flags a circular reference. + // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) + pp.Set(k, p.m[k]) + } + } + return pp +} + +// FilterStripPrefix returns a new properties object with a subset of all keys +// with the given prefix and the prefix removed from the keys. +func (p *Properties) FilterStripPrefix(prefix string) *Properties { + pp := NewProperties() + n := len(prefix) + for _, k := range p.k { + if len(k) > len(prefix) && strings.HasPrefix(k, prefix) { + // TODO(fs): we are ignoring the error which flags a circular reference. + // TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference + // TODO(fs): this function should probably return an error but the signature is fixed + pp.Set(k[n:], p.m[k]) + } + } + return pp +} + +// Len returns the number of keys. +func (p *Properties) Len() int { + return len(p.m) +} + +// Keys returns all keys in the same order as in the input. +func (p *Properties) Keys() []string { + keys := make([]string, len(p.k)) + copy(keys, p.k) + return keys +} + +// Set sets the property key to the corresponding value. +// If a value for key existed before then ok is true and prev +// contains the previous value. If the value contains a +// circular reference or a malformed expression then +// an error is returned. +// An empty key is silently ignored. +func (p *Properties) Set(key, value string) (prev string, ok bool, err error) { + if key == "" { + return "", false, nil + } + + // if expansion is disabled we allow circular references + if p.DisableExpansion { + prev, ok = p.Get(key) + p.m[key] = value + if !ok { + p.k = append(p.k, key) + } + return prev, ok, nil + } + + // to check for a circular reference we temporarily need + // to set the new value. If there is an error then revert + // to the previous state. Only if all tests are successful + // then we add the key to the p.k list. + prev, ok = p.Get(key) + p.m[key] = value + + // now check for a circular reference + _, err = p.expand(key, value) + if err != nil { + + // revert to the previous state + if ok { + p.m[key] = prev + } else { + delete(p.m, key) + } + + return "", false, err + } + + if !ok { + p.k = append(p.k, key) + } + + return prev, ok, nil +} + +// SetValue sets property key to the default string value +// as defined by fmt.Sprintf("%v"). +func (p *Properties) SetValue(key string, value interface{}) error { + _, _, err := p.Set(key, fmt.Sprintf("%v", value)) + return err +} + +// MustSet sets the property key to the corresponding value. +// If a value for key existed before then ok is true and prev +// contains the previous value. An empty key is silently ignored. +func (p *Properties) MustSet(key, value string) (prev string, ok bool) { + prev, ok, err := p.Set(key, value) + if err != nil { + ErrorHandler(err) + } + return prev, ok +} + +// String returns a string of all expanded 'key = value' pairs. +func (p *Properties) String() string { + var s string + for _, key := range p.k { + value, _ := p.Get(key) + s = fmt.Sprintf("%s%s = %s\n", s, key, value) + } + return s +} + +// Write writes all unexpanded 'key = value' pairs to the given writer. +// Write returns the number of bytes written and any write error encountered. +func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) { + return p.WriteComment(w, "", enc) +} + +// WriteComment writes all unexpanced 'key = value' pairs to the given writer. +// If prefix is not empty then comments are written with a blank line and the +// given prefix. The prefix should be either "# " or "! " to be compatible with +// the properties file format. Otherwise, the properties parser will not be +// able to read the file back in. It returns the number of bytes written and +// any write error encountered. +func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) { + var x int + + for _, key := range p.k { + value := p.m[key] + + if prefix != "" { + if comments, ok := p.c[key]; ok { + // don't print comments if they are all empty + allEmpty := true + for _, c := range comments { + if c != "" { + allEmpty = false + break + } + } + + if !allEmpty { + // add a blank line between entries but not at the top + if len(comments) > 0 && n > 0 { + x, err = fmt.Fprintln(w) + if err != nil { + return + } + n += x + } + + for _, c := range comments { + x, err = fmt.Fprintf(w, "%s%s\n", prefix, encode(c, "", enc)) + if err != nil { + return + } + n += x + } + } + } + } + + x, err = fmt.Fprintf(w, "%s = %s\n", encode(key, " :", enc), encode(value, "", enc)) + if err != nil { + return + } + n += x + } + return +} + +// Map returns a copy of the properties as a map. +func (p *Properties) Map() map[string]string { + m := make(map[string]string) + for k, v := range p.m { + m[k] = v + } + return m +} + +// FilterFunc returns a copy of the properties which includes the values which passed all filters. +func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties { + pp := NewProperties() +outer: + for k, v := range p.m { + for _, f := range filters { + if !f(k, v) { + continue outer + } + pp.Set(k, v) + } + } + return pp +} + +// ---------------------------------------------------------------------------- + +// Delete removes the key and its comments. +func (p *Properties) Delete(key string) { + delete(p.m, key) + delete(p.c, key) + newKeys := []string{} + for _, k := range p.k { + if k != key { + newKeys = append(newKeys, k) + } + } + p.k = newKeys +} + +// Merge merges properties, comments and keys from other *Properties into p +func (p *Properties) Merge(other *Properties) { + for k, v := range other.m { + p.m[k] = v + } + for k, v := range other.c { + p.c[k] = v + } + +outer: + for _, otherKey := range other.k { + for _, key := range p.k { + if otherKey == key { + continue outer + } + } + p.k = append(p.k, otherKey) + } +} + +// ---------------------------------------------------------------------------- + +// check expands all values and returns an error if a circular reference or +// a malformed expression was found. +func (p *Properties) check() error { + for key, value := range p.m { + if _, err := p.expand(key, value); err != nil { + return err + } + } + return nil +} + +func (p *Properties) expand(key, input string) (string, error) { + // no pre/postfix -> nothing to expand + if p.Prefix == "" && p.Postfix == "" { + return input, nil + } + + return expand(input, []string{key}, p.Prefix, p.Postfix, p.m) +} + +// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values. +// The function keeps track of the keys that were already expanded and stops if it +// detects a circular reference or a malformed expression of the form '(prefix)key'. +func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) { + if len(keys) > maxExpansionDepth { + return "", fmt.Errorf("expansion too deep") + } + + for { + start := strings.Index(s, prefix) + if start == -1 { + return s, nil + } + + keyStart := start + len(prefix) + keyLen := strings.Index(s[keyStart:], postfix) + if keyLen == -1 { + return "", fmt.Errorf("malformed expression") + } + + end := keyStart + keyLen + len(postfix) - 1 + key := s[keyStart : keyStart+keyLen] + + // fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key) + + for _, k := range keys { + if key == k { + return "", fmt.Errorf("circular reference") + } + } + + val, ok := values[key] + if !ok { + val = os.Getenv(key) + } + new_val, err := expand(val, append(keys, key), prefix, postfix, values) + if err != nil { + return "", err + } + s = s[:start] + new_val + s[end+1:] + } + return s, nil +} + +// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters. +func encode(s string, special string, enc Encoding) string { + switch enc { + case UTF8: + return encodeUtf8(s, special) + case ISO_8859_1: + return encodeIso(s, special) + default: + panic(fmt.Sprintf("unsupported encoding %v", enc)) + } +} + +func encodeUtf8(s string, special string) string { + v := "" + for pos := 0; pos < len(s); { + r, w := utf8.DecodeRuneInString(s[pos:]) + pos += w + v += escape(r, special) + } + return v +} + +func encodeIso(s string, special string) string { + var r rune + var w int + var v string + for pos := 0; pos < len(s); { + switch r, w = utf8.DecodeRuneInString(s[pos:]); { + case r < 1<<8: // single byte rune -> escape special chars only + v += escape(r, special) + case r < 1<<16: // two byte rune -> unicode literal + v += fmt.Sprintf("\\u%04x", r) + default: // more than two bytes per rune -> can't encode + v += "?" + } + pos += w + } + return v +} + +func escape(r rune, special string) string { + switch r { + case '\f': + return "\\f" + case '\n': + return "\\n" + case '\r': + return "\\r" + case '\t': + return "\\t" + default: + if strings.ContainsRune(special, r) { + return "\\" + string(r) + } + return string(r) + } +} + +func invalidKeyError(key string) error { + return fmt.Errorf("unknown property: %s", key) +} diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go new file mode 100644 index 0000000..b013a2e --- /dev/null +++ b/vendor/github.com/magiconair/properties/rangecheck.go @@ -0,0 +1,31 @@ +// Copyright 2018 Frank Schroeder. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package properties + +import ( + "fmt" + "math" +) + +// make this a var to overwrite it in a test +var is32Bit = ^uint(0) == math.MaxUint32 + +// intRangeCheck checks if the value fits into the int type and +// panics if it does not. +func intRangeCheck(key string, v int64) int { + if is32Bit && (v < math.MinInt32 || v > math.MaxInt32) { + panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) + } + return int(v) +} + +// uintRangeCheck checks if the value fits into the uint type and +// panics if it does not. +func uintRangeCheck(key string, v uint64) uint { + if is32Bit && v > math.MaxUint32 { + panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) + } + return uint(v) +} diff --git a/vendor/github.com/mitchellh/mapstructure/.travis.yml b/vendor/github.com/mitchellh/mapstructure/.travis.yml new file mode 100644 index 0000000..1689c7d --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/.travis.yml @@ -0,0 +1,8 @@ +language: go + +go: + - "1.11.x" + - tip + +script: + - go test diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md new file mode 100644 index 0000000..3b3cb72 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md @@ -0,0 +1,21 @@ +## 1.1.2 + +* Fix error when decode hook decodes interface implementation into interface + type. [GH-140] + +## 1.1.1 + +* Fix panic that can happen in `decodePtr` + +## 1.1.0 + +* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] +* Support struct to struct decoding [GH-137] +* If source map value is nil, then destination map value is nil (instead of empty) +* If source slice value is nil, then destination slice value is nil (instead of empty) +* If source pointer is nil, then destination pointer is set to nil (instead of + allocated zero value of type) + +## 1.0.0 + +* Initial tagged stable release. diff --git a/vendor/github.com/mitchellh/mapstructure/LICENSE b/vendor/github.com/mitchellh/mapstructure/LICENSE new file mode 100644 index 0000000..f9c841a --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/mapstructure/README.md b/vendor/github.com/mitchellh/mapstructure/README.md new file mode 100644 index 0000000..0018dc7 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/README.md @@ -0,0 +1,46 @@ +# mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure) + +mapstructure is a Go library for decoding generic map values to structures +and vice versa, while providing helpful error handling. + +This library is most useful when decoding values from some data stream (JSON, +Gob, etc.) where you don't _quite_ know the structure of the underlying data +until you read a part of it. You can therefore read a `map[string]interface{}` +and use this library to decode it into the proper underlying native Go +structure. + +## Installation + +Standard `go get`: + +``` +$ go get github.com/mitchellh/mapstructure +``` + +## Usage & Example + +For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure). + +The `Decode` function has examples associated with it there. + +## But Why?! + +Go offers fantastic standard libraries for decoding formats such as JSON. +The standard method is to have a struct pre-created, and populate that struct +from the bytes of the encoded format. This is great, but the problem is if +you have configuration or an encoding that changes slightly depending on +specific fields. For example, consider this JSON: + +```json +{ + "type": "person", + "name": "Mitchell" +} +``` + +Perhaps we can't populate a specific structure without first reading +the "type" field from the JSON. We could always do two passes over the +decoding of the JSON (reading the "type" first, and the rest later). +However, it is much simpler to just decode this into a `map[string]interface{}` +structure, read the "type" key, then use something like this library +to decode it into the proper structure. diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go new file mode 100644 index 0000000..1f0abc6 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go @@ -0,0 +1,217 @@ +package mapstructure + +import ( + "errors" + "fmt" + "net" + "reflect" + "strconv" + "strings" + "time" +) + +// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns +// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. +func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { + // Create variables here so we can reference them with the reflect pkg + var f1 DecodeHookFuncType + var f2 DecodeHookFuncKind + + // Fill in the variables into this interface and the rest is done + // automatically using the reflect package. + potential := []interface{}{f1, f2} + + v := reflect.ValueOf(h) + vt := v.Type() + for _, raw := range potential { + pt := reflect.ValueOf(raw).Type() + if vt.ConvertibleTo(pt) { + return v.Convert(pt).Interface() + } + } + + return nil +} + +// DecodeHookExec executes the given decode hook. This should be used +// since it'll naturally degrade to the older backwards compatible DecodeHookFunc +// that took reflect.Kind instead of reflect.Type. +func DecodeHookExec( + raw DecodeHookFunc, + from reflect.Type, to reflect.Type, + data interface{}) (interface{}, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return f(from, to, data) + case DecodeHookFuncKind: + return f(from.Kind(), to.Kind(), data) + default: + return nil, errors.New("invalid decode hook signature") + } +} + +// ComposeDecodeHookFunc creates a single DecodeHookFunc that +// automatically composes multiple DecodeHookFuncs. +// +// The composed funcs are called in order, with the result of the +// previous transformation. +func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + var err error + for _, f1 := range fs { + data, err = DecodeHookExec(f1, f, t, data) + if err != nil { + return nil, err + } + + // Modify the from kind to be correct with the new data + f = nil + if val := reflect.ValueOf(data); val.IsValid() { + f = val.Type() + } + } + + return data, nil + } +} + +// StringToSliceHookFunc returns a DecodeHookFunc that converts +// string to []string by splitting on the given sep. +func StringToSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Kind, + t reflect.Kind, + data interface{}) (interface{}, error) { + if f != reflect.String || t != reflect.Slice { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts +// strings to time.Duration. +func StringToTimeDurationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Duration(5)) { + return data, nil + } + + // Convert it by parsing + return time.ParseDuration(data.(string)) + } +} + +// StringToIPHookFunc returns a DecodeHookFunc that converts +// strings to net.IP +func StringToIPHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IP{}) { + return data, nil + } + + // Convert it by parsing + ip := net.ParseIP(data.(string)) + if ip == nil { + return net.IP{}, fmt.Errorf("failed parsing ip %v", data) + } + + return ip, nil + } +} + +// StringToIPNetHookFunc returns a DecodeHookFunc that converts +// strings to net.IPNet +func StringToIPNetHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IPNet{}) { + return data, nil + } + + // Convert it by parsing + _, net, err := net.ParseCIDR(data.(string)) + return net, err + } +} + +// StringToTimeHookFunc returns a DecodeHookFunc that converts +// strings to time.Time. +func StringToTimeHookFunc(layout string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Time{}) { + return data, nil + } + + // Convert it by parsing + return time.Parse(layout, data.(string)) + } +} + +// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to +// the decoder. +// +// Note that this is significantly different from the WeaklyTypedInput option +// of the DecoderConfig. +func WeaklyTypedHook( + f reflect.Kind, + t reflect.Kind, + data interface{}) (interface{}, error) { + dataVal := reflect.ValueOf(data) + switch t { + case reflect.String: + switch f { + case reflect.Bool: + if dataVal.Bool() { + return "1", nil + } + return "0", nil + case reflect.Float32: + return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil + case reflect.Int: + return strconv.FormatInt(dataVal.Int(), 10), nil + case reflect.Slice: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + if elemKind == reflect.Uint8 { + return string(dataVal.Interface().([]uint8)), nil + } + case reflect.Uint: + return strconv.FormatUint(dataVal.Uint(), 10), nil + } + } + + return data, nil +} diff --git a/vendor/github.com/mitchellh/mapstructure/error.go b/vendor/github.com/mitchellh/mapstructure/error.go new file mode 100644 index 0000000..47a99e5 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/error.go @@ -0,0 +1,50 @@ +package mapstructure + +import ( + "errors" + "fmt" + "sort" + "strings" +) + +// Error implements the error interface and can represents multiple +// errors that occur in the course of a single decode. +type Error struct { + Errors []string +} + +func (e *Error) Error() string { + points := make([]string, len(e.Errors)) + for i, err := range e.Errors { + points[i] = fmt.Sprintf("* %s", err) + } + + sort.Strings(points) + return fmt.Sprintf( + "%d error(s) decoding:\n\n%s", + len(e.Errors), strings.Join(points, "\n")) +} + +// WrappedErrors implements the errwrap.Wrapper interface to make this +// return value more useful with the errwrap and go-multierror libraries. +func (e *Error) WrappedErrors() []error { + if e == nil { + return nil + } + + result := make([]error, len(e.Errors)) + for i, e := range e.Errors { + result[i] = errors.New(e) + } + + return result +} + +func appendErrors(errors []string, err error) []string { + switch e := err.(type) { + case *Error: + return append(errors, e.Errors...) + default: + return append(errors, e.Error()) + } +} diff --git a/vendor/github.com/mitchellh/mapstructure/go.mod b/vendor/github.com/mitchellh/mapstructure/go.mod new file mode 100644 index 0000000..d2a7125 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/go.mod @@ -0,0 +1 @@ +module github.com/mitchellh/mapstructure diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go new file mode 100644 index 0000000..256ee63 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -0,0 +1,1149 @@ +// Package mapstructure exposes functionality to convert an arbitrary +// map[string]interface{} into a native Go structure. +// +// The Go structure can be arbitrarily complex, containing slices, +// other structs, etc. and the decoder will properly decode nested +// maps and so on into the proper structures in the native Go struct. +// See the examples to see what the decoder is capable of. +package mapstructure + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "sort" + "strconv" + "strings" +) + +// DecodeHookFunc is the callback function that can be used for +// data transformations. See "DecodeHook" in the DecoderConfig +// struct. +// +// The type should be DecodeHookFuncType or DecodeHookFuncKind. +// Either is accepted. Types are a superset of Kinds (Types can return +// Kinds) and are generally a richer thing to use, but Kinds are simpler +// if you only need those. +// +// The reason DecodeHookFunc is multi-typed is for backwards compatibility: +// we started with Kinds and then realized Types were the better solution, +// but have a promise to not break backwards compat so we now support +// both. +type DecodeHookFunc interface{} + +// DecodeHookFuncType is a DecodeHookFunc which has complete information about +// the source and target types. +type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) + +// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the +// source and target types. +type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) + +// DecoderConfig is the configuration that is used to create a new decoder +// and allows customization of various aspects of decoding. +type DecoderConfig struct { + // DecodeHook, if set, will be called before any decoding and any + // type conversion (if WeaklyTypedInput is on). This lets you modify + // the values before they're set down onto the resulting struct. + // + // If an error is returned, the entire decode will fail with that + // error. + DecodeHook DecodeHookFunc + + // If ErrorUnused is true, then it is an error for there to exist + // keys in the original map that were unused in the decoding process + // (extra keys). + ErrorUnused bool + + // ZeroFields, if set to true, will zero fields before writing them. + // For example, a map will be emptied before decoded values are put in + // it. If this is false, a map will be merged. + ZeroFields bool + + // If WeaklyTypedInput is true, the decoder will make the following + // "weak" conversions: + // + // - bools to string (true = "1", false = "0") + // - numbers to string (base 10) + // - bools to int/uint (true = 1, false = 0) + // - strings to int/uint (base implied by prefix) + // - int to bool (true if value != 0) + // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, + // FALSE, false, False. Anything else is an error) + // - empty array = empty map and vice versa + // - negative numbers to overflowed uint values (base 10) + // - slice of maps to a merged map + // - single values are converted to slices if required. Each + // element is weakly decoded. For example: "4" can become []int{4} + // if the target type is an int slice. + // + WeaklyTypedInput bool + + // Metadata is the struct that will contain extra metadata about + // the decoding. If this is nil, then no metadata will be tracked. + Metadata *Metadata + + // Result is a pointer to the struct that will contain the decoded + // value. + Result interface{} + + // The tag name that mapstructure reads for field names. This + // defaults to "mapstructure" + TagName string +} + +// A Decoder takes a raw interface value and turns it into structured +// data, keeping track of rich error information along the way in case +// anything goes wrong. Unlike the basic top-level Decode method, you can +// more finely control how the Decoder behaves using the DecoderConfig +// structure. The top-level Decode method is just a convenience that sets +// up the most basic Decoder. +type Decoder struct { + config *DecoderConfig +} + +// Metadata contains information about decoding a structure that +// is tedious or difficult to get otherwise. +type Metadata struct { + // Keys are the keys of the structure which were successfully decoded + Keys []string + + // Unused is a slice of keys that were found in the raw value but + // weren't decoded since there was no matching field in the result interface + Unused []string +} + +// Decode takes an input structure and uses reflection to translate it to +// the output structure. output must be a pointer to a map or struct. +func Decode(input interface{}, output interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecode is the same as Decode but is shorthand to enable +// WeaklyTypedInput. See DecoderConfig for more info. +func WeakDecode(input, output interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// DecodeMetadata is the same as Decode, but is shorthand to +// enable metadata collection. See DecoderConfig for more info. +func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecodeMetadata is the same as Decode, but is shorthand to +// enable both WeaklyTypedInput and metadata collection. See +// DecoderConfig for more info. +func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// NewDecoder returns a new decoder for the given configuration. Once +// a decoder has been returned, the same configuration must not be used +// again. +func NewDecoder(config *DecoderConfig) (*Decoder, error) { + val := reflect.ValueOf(config.Result) + if val.Kind() != reflect.Ptr { + return nil, errors.New("result must be a pointer") + } + + val = val.Elem() + if !val.CanAddr() { + return nil, errors.New("result must be addressable (a pointer)") + } + + if config.Metadata != nil { + if config.Metadata.Keys == nil { + config.Metadata.Keys = make([]string, 0) + } + + if config.Metadata.Unused == nil { + config.Metadata.Unused = make([]string, 0) + } + } + + if config.TagName == "" { + config.TagName = "mapstructure" + } + + result := &Decoder{ + config: config, + } + + return result, nil +} + +// Decode decodes the given raw interface to the target pointer specified +// by the configuration. +func (d *Decoder) Decode(input interface{}) error { + return d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) +} + +// Decodes an unknown data type into a specific reflection value. +func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { + var inputVal reflect.Value + if input != nil { + inputVal = reflect.ValueOf(input) + + // We need to check here if input is a typed nil. Typed nils won't + // match the "input == nil" below so we check that here. + if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { + input = nil + } + } + + if input == nil { + // If the data is nil, then we don't set anything, unless ZeroFields is set + // to true. + if d.config.ZeroFields { + outVal.Set(reflect.Zero(outVal.Type())) + + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + } + return nil + } + + if !inputVal.IsValid() { + // If the input value is invalid, then we just set the value + // to be the zero value. + outVal.Set(reflect.Zero(outVal.Type())) + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + return nil + } + + if d.config.DecodeHook != nil { + // We have a DecodeHook, so let's pre-process the input. + var err error + input, err = DecodeHookExec( + d.config.DecodeHook, + inputVal.Type(), outVal.Type(), input) + if err != nil { + return fmt.Errorf("error decoding '%s': %s", name, err) + } + } + + var err error + outputKind := getKind(outVal) + switch outputKind { + case reflect.Bool: + err = d.decodeBool(name, input, outVal) + case reflect.Interface: + err = d.decodeBasic(name, input, outVal) + case reflect.String: + err = d.decodeString(name, input, outVal) + case reflect.Int: + err = d.decodeInt(name, input, outVal) + case reflect.Uint: + err = d.decodeUint(name, input, outVal) + case reflect.Float32: + err = d.decodeFloat(name, input, outVal) + case reflect.Struct: + err = d.decodeStruct(name, input, outVal) + case reflect.Map: + err = d.decodeMap(name, input, outVal) + case reflect.Ptr: + err = d.decodePtr(name, input, outVal) + case reflect.Slice: + err = d.decodeSlice(name, input, outVal) + case reflect.Array: + err = d.decodeArray(name, input, outVal) + case reflect.Func: + err = d.decodeFunc(name, input, outVal) + default: + // If we reached this point then we weren't able to decode it + return fmt.Errorf("%s: unsupported type: %s", name, outputKind) + } + + // If we reached here, then we successfully decoded SOMETHING, so + // mark the key as used if we're tracking metainput. + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + + return err +} + +// This decodes a basic type (bool, int, string, etc.) and sets the +// value to "data" of that type. +func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { + if val.IsValid() && val.Elem().IsValid() { + return d.decode(name, data, val.Elem()) + } + + dataVal := reflect.ValueOf(data) + + // If the input data is a pointer, and the assigned type is the dereference + // of that exact pointer, then indirect it so that we can assign it. + // Example: *string to string + if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { + dataVal = reflect.Indirect(dataVal) + } + + if !dataVal.IsValid() { + dataVal = reflect.Zero(val.Type()) + } + + dataValType := dataVal.Type() + if !dataValType.AssignableTo(val.Type()) { + return fmt.Errorf( + "'%s' expected type '%s', got '%s'", + name, val.Type(), dataValType) + } + + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + converted := true + switch { + case dataKind == reflect.String: + val.SetString(dataVal.String()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetString("1") + } else { + val.SetString("0") + } + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) + case dataKind == reflect.Slice && d.config.WeaklyTypedInput, + dataKind == reflect.Array && d.config.WeaklyTypedInput: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + switch elemKind { + case reflect.Uint8: + var uints []uint8 + if dataKind == reflect.Array { + uints = make([]uint8, dataVal.Len(), dataVal.Len()) + for i := range uints { + uints[i] = dataVal.Index(i).Interface().(uint8) + } + } else { + uints = dataVal.Interface().([]uint8) + } + val.SetString(string(uints)) + default: + converted = false + } + default: + converted = false + } + + if !converted { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetInt(dataVal.Int()) + case dataKind == reflect.Uint: + val.SetInt(int64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetInt(int64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetInt(1) + } else { + val.SetInt(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits()) + if err == nil { + val.SetInt(i) + } else { + return fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Int64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetInt(i) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Int: + i := dataVal.Int() + if i < 0 && !d.config.WeaklyTypedInput { + return fmt.Errorf("cannot parse '%s', %d overflows uint", + name, i) + } + val.SetUint(uint64(i)) + case dataKind == reflect.Uint: + val.SetUint(dataVal.Uint()) + case dataKind == reflect.Float32: + f := dataVal.Float() + if f < 0 && !d.config.WeaklyTypedInput { + return fmt.Errorf("cannot parse '%s', %f overflows uint", + name, f) + } + val.SetUint(uint64(f)) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetUint(1) + } else { + val.SetUint(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) + if err == nil { + val.SetUint(i) + } else { + return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Bool: + val.SetBool(dataVal.Bool()) + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Int() != 0) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Uint() != 0) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Float() != 0) + case dataKind == reflect.String && d.config.WeaklyTypedInput: + b, err := strconv.ParseBool(dataVal.String()) + if err == nil { + val.SetBool(b) + } else if dataVal.String() == "" { + val.SetBool(false) + } else { + return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetFloat(float64(dataVal.Int())) + case dataKind == reflect.Uint: + val.SetFloat(float64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetFloat(dataVal.Float()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetFloat(1) + } else { + val.SetFloat(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits()) + if err == nil { + val.SetFloat(f) + } else { + return fmt.Errorf("cannot parse '%s' as float: %s", name, err) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Float64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetFloat(i) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // By default we overwrite keys in the current map + valMap := val + + // If the map is nil or we're purposely zeroing fields, make a new map + if valMap.IsNil() || d.config.ZeroFields { + // Make a new map to hold our result + mapType := reflect.MapOf(valKeyType, valElemType) + valMap = reflect.MakeMap(mapType) + } + + // Check input type and based on the input type jump to the proper func + dataVal := reflect.Indirect(reflect.ValueOf(data)) + switch dataVal.Kind() { + case reflect.Map: + return d.decodeMapFromMap(name, dataVal, val, valMap) + + case reflect.Struct: + return d.decodeMapFromStruct(name, dataVal, val, valMap) + + case reflect.Array, reflect.Slice: + if d.config.WeaklyTypedInput { + return d.decodeMapFromSlice(name, dataVal, val, valMap) + } + + fallthrough + + default: + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + } +} + +func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + // Special case for BC reasons (covered by tests) + if dataVal.Len() == 0 { + val.Set(valMap) + return nil + } + + for i := 0; i < dataVal.Len(); i++ { + err := d.decode( + fmt.Sprintf("%s[%d]", name, i), + dataVal.Index(i).Interface(), val) + if err != nil { + return err + } + } + + return nil +} + +func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // Accumulate errors + errors := make([]string, 0) + + // If the input data is empty, then we just match what the input data is. + if dataVal.Len() == 0 { + if dataVal.IsNil() { + if !val.IsNil() { + val.Set(dataVal) + } + } else { + // Set to empty allocated value + val.Set(valMap) + } + + return nil + } + + for _, k := range dataVal.MapKeys() { + fieldName := fmt.Sprintf("%s[%s]", name, k) + + // First decode the key into the proper type + currentKey := reflect.Indirect(reflect.New(valKeyType)) + if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { + errors = appendErrors(errors, err) + continue + } + + // Next decode the data into the proper type + v := dataVal.MapIndex(k).Interface() + currentVal := reflect.Indirect(reflect.New(valElemType)) + if err := d.decode(fieldName, v, currentVal); err != nil { + errors = appendErrors(errors, err) + continue + } + + valMap.SetMapIndex(currentKey, currentVal) + } + + // Set the built up map to the value + val.Set(valMap) + + // If we had errors, return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + +func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + typ := dataVal.Type() + for i := 0; i < typ.NumField(); i++ { + // Get the StructField first since this is a cheap operation. If the + // field is unexported, then ignore it. + f := typ.Field(i) + if f.PkgPath != "" { + continue + } + + // Next get the actual value of this field and verify it is assignable + // to the map value. + v := dataVal.Field(i) + if !v.Type().AssignableTo(valMap.Type().Elem()) { + return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) + } + + tagValue := f.Tag.Get(d.config.TagName) + tagParts := strings.Split(tagValue, ",") + + // Determine the name of the key in the map + keyName := f.Name + if tagParts[0] != "" { + if tagParts[0] == "-" { + continue + } + keyName = tagParts[0] + } + + // If "squash" is specified in the tag, we squash the field down. + squash := false + for _, tag := range tagParts[1:] { + if tag == "squash" { + squash = true + break + } + } + if squash && v.Kind() != reflect.Struct { + return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) + } + + switch v.Kind() { + // this is an embedded struct, so handle it differently + case reflect.Struct: + x := reflect.New(v.Type()) + x.Elem().Set(v) + + vType := valMap.Type() + vKeyType := vType.Key() + vElemType := vType.Elem() + mType := reflect.MapOf(vKeyType, vElemType) + vMap := reflect.MakeMap(mType) + + err := d.decode(keyName, x.Interface(), vMap) + if err != nil { + return err + } + + if squash { + for _, k := range vMap.MapKeys() { + valMap.SetMapIndex(k, vMap.MapIndex(k)) + } + } else { + valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) + } + + default: + valMap.SetMapIndex(reflect.ValueOf(keyName), v) + } + } + + if val.CanAddr() { + val.Set(valMap) + } + + return nil +} + +func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error { + // If the input data is nil, then we want to just set the output + // pointer to be nil as well. + isNil := data == nil + if !isNil { + switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { + case reflect.Chan, + reflect.Func, + reflect.Interface, + reflect.Map, + reflect.Ptr, + reflect.Slice: + isNil = v.IsNil() + } + } + if isNil { + if !val.IsNil() && val.CanSet() { + nilValue := reflect.New(val.Type()).Elem() + val.Set(nilValue) + } + + return nil + } + + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + valType := val.Type() + valElemType := valType.Elem() + if val.CanSet() { + realVal := val + if realVal.IsNil() || d.config.ZeroFields { + realVal = reflect.New(valElemType) + } + + if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { + return err + } + + val.Set(realVal) + } else { + if err := d.decode(name, data, reflect.Indirect(val)); err != nil { + return err + } + } + return nil +} + +func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + dataVal := reflect.Indirect(reflect.ValueOf(data)) + if val.Type() != dataVal.Type() { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + sliceType := reflect.SliceOf(valElemType) + + valSlice := val + if valSlice.IsNil() || d.config.ZeroFields { + if d.config.WeaklyTypedInput { + switch { + // Slice and array we use the normal logic + case dataValKind == reflect.Slice, dataValKind == reflect.Array: + break + + // Empty maps turn into empty slices + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.MakeSlice(sliceType, 0, 0)) + return nil + } + // Create slice of maps of other sizes + return d.decodeSlice(name, []interface{}{data}, val) + + case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: + return d.decodeSlice(name, []byte(dataVal.String()), val) + + // All other types we try to convert to the slice type + // and "lift" it into it. i.e. a string becomes a string slice. + default: + // Just re-try this function with data as a slice. + return d.decodeSlice(name, []interface{}{data}, val) + } + } + + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + + } + + // If the input value is empty, then don't allocate since non-nil != nil + if dataVal.Len() == 0 { + return nil + } + + // Make a new slice to hold our result, same size as the original data. + valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } + + // Accumulate any errors + errors := make([]string, 0) + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + for valSlice.Len() <= i { + valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) + } + currentField := valSlice.Index(i) + + fieldName := fmt.Sprintf("%s[%d]", name, i) + if err := d.decode(fieldName, currentData, currentField); err != nil { + errors = appendErrors(errors, err) + } + } + + // Finally, set the value to the slice we built up + val.Set(valSlice) + + // If there were errors, we return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + +func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + arrayType := reflect.ArrayOf(valType.Len(), valElemType) + + valArray := val + + if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Empty maps turn into empty arrays + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.Zero(arrayType)) + return nil + } + + // All other types we try to convert to the array type + // and "lift" it into it. i.e. a string becomes a string array. + default: + // Just re-try this function with data as a slice. + return d.decodeArray(name, []interface{}{data}, val) + } + } + + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + + } + if dataVal.Len() > arrayType.Len() { + return fmt.Errorf( + "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) + + } + + // Make a new array to hold our result, same size as the original data. + valArray = reflect.New(arrayType).Elem() + } + + // Accumulate any errors + errors := make([]string, 0) + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + currentField := valArray.Index(i) + + fieldName := fmt.Sprintf("%s[%d]", name, i) + if err := d.decode(fieldName, currentData, currentField); err != nil { + errors = appendErrors(errors, err) + } + } + + // Finally, set the value to the array we built up + val.Set(valArray) + + // If there were errors, we return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + +func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + + // If the type of the value to write to and the data match directly, + // then we just set it directly instead of recursing into the structure. + if dataVal.Type() == val.Type() { + val.Set(dataVal) + return nil + } + + dataValKind := dataVal.Kind() + switch dataValKind { + case reflect.Map: + return d.decodeStructFromMap(name, dataVal, val) + + case reflect.Struct: + // Not the most efficient way to do this but we can optimize later if + // we want to. To convert from struct to struct we go to map first + // as an intermediary. + m := make(map[string]interface{}) + mval := reflect.Indirect(reflect.ValueOf(&m)) + if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil { + return err + } + + result := d.decodeStructFromMap(name, mval, val) + return result + + default: + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + } +} + +func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { + dataValType := dataVal.Type() + if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { + return fmt.Errorf( + "'%s' needs a map with string keys, has '%s' keys", + name, dataValType.Key().Kind()) + } + + dataValKeys := make(map[reflect.Value]struct{}) + dataValKeysUnused := make(map[interface{}]struct{}) + for _, dataValKey := range dataVal.MapKeys() { + dataValKeys[dataValKey] = struct{}{} + dataValKeysUnused[dataValKey.Interface()] = struct{}{} + } + + errors := make([]string, 0) + + // This slice will keep track of all the structs we'll be decoding. + // There can be more than one struct if there are embedded structs + // that are squashed. + structs := make([]reflect.Value, 1, 5) + structs[0] = val + + // Compile the list of all the fields that we're going to be decoding + // from all the structs. + type field struct { + field reflect.StructField + val reflect.Value + } + fields := []field{} + for len(structs) > 0 { + structVal := structs[0] + structs = structs[1:] + + structType := structVal.Type() + + for i := 0; i < structType.NumField(); i++ { + fieldType := structType.Field(i) + fieldKind := fieldType.Type.Kind() + + // If "squash" is specified in the tag, we squash the field down. + squash := false + tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + for _, tag := range tagParts[1:] { + if tag == "squash" { + squash = true + break + } + } + + if squash { + if fieldKind != reflect.Struct { + errors = appendErrors(errors, + fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind)) + } else { + structs = append(structs, structVal.FieldByName(fieldType.Name)) + } + continue + } + + // Normal struct field, store it away + fields = append(fields, field{fieldType, structVal.Field(i)}) + } + } + + // for fieldType, field := range fields { + for _, f := range fields { + field, fieldValue := f.field, f.val + fieldName := field.Name + + tagValue := field.Tag.Get(d.config.TagName) + tagValue = strings.SplitN(tagValue, ",", 2)[0] + if tagValue != "" { + fieldName = tagValue + } + + rawMapKey := reflect.ValueOf(fieldName) + rawMapVal := dataVal.MapIndex(rawMapKey) + if !rawMapVal.IsValid() { + // Do a slower search by iterating over each key and + // doing case-insensitive search. + for dataValKey := range dataValKeys { + mK, ok := dataValKey.Interface().(string) + if !ok { + // Not a string key + continue + } + + if strings.EqualFold(mK, fieldName) { + rawMapKey = dataValKey + rawMapVal = dataVal.MapIndex(dataValKey) + break + } + } + + if !rawMapVal.IsValid() { + // There was no matching key in the map for the value in + // the struct. Just ignore. + continue + } + } + + // Delete the key we're using from the unused map so we stop tracking + delete(dataValKeysUnused, rawMapKey.Interface()) + + if !fieldValue.IsValid() { + // This should never happen + panic("field is not valid") + } + + // If we can't set the field, then it is unexported or something, + // and we just continue onwards. + if !fieldValue.CanSet() { + continue + } + + // If the name is empty string, then we're at the root, and we + // don't dot-join the fields. + if name != "" { + fieldName = fmt.Sprintf("%s.%s", name, fieldName) + } + + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { + errors = appendErrors(errors, err) + } + } + + if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { + keys := make([]string, 0, len(dataValKeysUnused)) + for rawKey := range dataValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) + errors = appendErrors(errors, err) + } + + if len(errors) > 0 { + return &Error{errors} + } + + // Add the unused keys to the list of unused keys if we're tracking metadata + if d.config.Metadata != nil { + for rawKey := range dataValKeysUnused { + key := rawKey.(string) + if name != "" { + key = fmt.Sprintf("%s.%s", name, key) + } + + d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) + } + } + + return nil +} + +func getKind(val reflect.Value) reflect.Kind { + kind := val.Kind() + + switch { + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + default: + return kind + } +} diff --git a/vendor/github.com/osrg/gobgp/LICENSE b/vendor/github.com/osrg/gobgp/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/github.com/osrg/gobgp/api/attribute.pb.go b/vendor/github.com/osrg/gobgp/api/attribute.pb.go new file mode 100644 index 0000000..48d820e --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/attribute.pb.go @@ -0,0 +1,2088 @@ +// Code generated by protoc-gen-go. +// source: attribute.proto +// DO NOT EDIT! + +package gobgpapi + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/any" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type OriginAttribute struct { + Origin uint32 `protobuf:"varint,1,opt,name=origin" json:"origin,omitempty"` +} + +func (m *OriginAttribute) Reset() { *m = OriginAttribute{} } +func (m *OriginAttribute) String() string { return proto.CompactTextString(m) } +func (*OriginAttribute) ProtoMessage() {} +func (*OriginAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } + +func (m *OriginAttribute) GetOrigin() uint32 { + if m != nil { + return m.Origin + } + return 0 +} + +type AsSegment struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Numbers []uint32 `protobuf:"varint,2,rep,packed,name=numbers" json:"numbers,omitempty"` +} + +func (m *AsSegment) Reset() { *m = AsSegment{} } +func (m *AsSegment) String() string { return proto.CompactTextString(m) } +func (*AsSegment) ProtoMessage() {} +func (*AsSegment) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } + +func (m *AsSegment) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *AsSegment) GetNumbers() []uint32 { + if m != nil { + return m.Numbers + } + return nil +} + +type AsPathAttribute struct { + Segments []*AsSegment `protobuf:"bytes,1,rep,name=segments" json:"segments,omitempty"` +} + +func (m *AsPathAttribute) Reset() { *m = AsPathAttribute{} } +func (m *AsPathAttribute) String() string { return proto.CompactTextString(m) } +func (*AsPathAttribute) ProtoMessage() {} +func (*AsPathAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } + +func (m *AsPathAttribute) GetSegments() []*AsSegment { + if m != nil { + return m.Segments + } + return nil +} + +type NextHopAttribute struct { + NextHop string `protobuf:"bytes,1,opt,name=next_hop,json=nextHop" json:"next_hop,omitempty"` +} + +func (m *NextHopAttribute) Reset() { *m = NextHopAttribute{} } +func (m *NextHopAttribute) String() string { return proto.CompactTextString(m) } +func (*NextHopAttribute) ProtoMessage() {} +func (*NextHopAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} } + +func (m *NextHopAttribute) GetNextHop() string { + if m != nil { + return m.NextHop + } + return "" +} + +type MultiExitDiscAttribute struct { + Med uint32 `protobuf:"varint,1,opt,name=med" json:"med,omitempty"` +} + +func (m *MultiExitDiscAttribute) Reset() { *m = MultiExitDiscAttribute{} } +func (m *MultiExitDiscAttribute) String() string { return proto.CompactTextString(m) } +func (*MultiExitDiscAttribute) ProtoMessage() {} +func (*MultiExitDiscAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} } + +func (m *MultiExitDiscAttribute) GetMed() uint32 { + if m != nil { + return m.Med + } + return 0 +} + +type LocalPrefAttribute struct { + LocalPref uint32 `protobuf:"varint,1,opt,name=local_pref,json=localPref" json:"local_pref,omitempty"` +} + +func (m *LocalPrefAttribute) Reset() { *m = LocalPrefAttribute{} } +func (m *LocalPrefAttribute) String() string { return proto.CompactTextString(m) } +func (*LocalPrefAttribute) ProtoMessage() {} +func (*LocalPrefAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } + +func (m *LocalPrefAttribute) GetLocalPref() uint32 { + if m != nil { + return m.LocalPref + } + return 0 +} + +type AtomicAggregateAttribute struct { +} + +func (m *AtomicAggregateAttribute) Reset() { *m = AtomicAggregateAttribute{} } +func (m *AtomicAggregateAttribute) String() string { return proto.CompactTextString(m) } +func (*AtomicAggregateAttribute) ProtoMessage() {} +func (*AtomicAggregateAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } + +type AggregatorAttribute struct { + As uint32 `protobuf:"varint,2,opt,name=as" json:"as,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` +} + +func (m *AggregatorAttribute) Reset() { *m = AggregatorAttribute{} } +func (m *AggregatorAttribute) String() string { return proto.CompactTextString(m) } +func (*AggregatorAttribute) ProtoMessage() {} +func (*AggregatorAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } + +func (m *AggregatorAttribute) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *AggregatorAttribute) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type CommunitiesAttribute struct { + Communities []uint32 `protobuf:"varint,1,rep,packed,name=communities" json:"communities,omitempty"` +} + +func (m *CommunitiesAttribute) Reset() { *m = CommunitiesAttribute{} } +func (m *CommunitiesAttribute) String() string { return proto.CompactTextString(m) } +func (*CommunitiesAttribute) ProtoMessage() {} +func (*CommunitiesAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } + +func (m *CommunitiesAttribute) GetCommunities() []uint32 { + if m != nil { + return m.Communities + } + return nil +} + +type OriginatorIdAttribute struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` +} + +func (m *OriginatorIdAttribute) Reset() { *m = OriginatorIdAttribute{} } +func (m *OriginatorIdAttribute) String() string { return proto.CompactTextString(m) } +func (*OriginatorIdAttribute) ProtoMessage() {} +func (*OriginatorIdAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } + +func (m *OriginatorIdAttribute) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +type ClusterListAttribute struct { + Ids []string `protobuf:"bytes,1,rep,name=ids" json:"ids,omitempty"` +} + +func (m *ClusterListAttribute) Reset() { *m = ClusterListAttribute{} } +func (m *ClusterListAttribute) String() string { return proto.CompactTextString(m) } +func (*ClusterListAttribute) ProtoMessage() {} +func (*ClusterListAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{10} } + +func (m *ClusterListAttribute) GetIds() []string { + if m != nil { + return m.Ids + } + return nil +} + +// IPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=1 +// - AFI=2, SAFI=1 +type IPAddressPrefix struct { + PrefixLen uint32 `protobuf:"varint,1,opt,name=prefix_len,json=prefixLen" json:"prefix_len,omitempty"` + Prefix string `protobuf:"bytes,2,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *IPAddressPrefix) Reset() { *m = IPAddressPrefix{} } +func (m *IPAddressPrefix) String() string { return proto.CompactTextString(m) } +func (*IPAddressPrefix) ProtoMessage() {} +func (*IPAddressPrefix) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{11} } + +func (m *IPAddressPrefix) GetPrefixLen() uint32 { + if m != nil { + return m.PrefixLen + } + return 0 +} + +func (m *IPAddressPrefix) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +// LabeledIPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=4 +// - AFI=2, SAFI=4 +type LabeledIPAddressPrefix struct { + Labels []uint32 `protobuf:"varint,1,rep,packed,name=labels" json:"labels,omitempty"` + PrefixLen uint32 `protobuf:"varint,2,opt,name=prefix_len,json=prefixLen" json:"prefix_len,omitempty"` + Prefix string `protobuf:"bytes,3,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *LabeledIPAddressPrefix) Reset() { *m = LabeledIPAddressPrefix{} } +func (m *LabeledIPAddressPrefix) String() string { return proto.CompactTextString(m) } +func (*LabeledIPAddressPrefix) ProtoMessage() {} +func (*LabeledIPAddressPrefix) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{12} } + +func (m *LabeledIPAddressPrefix) GetLabels() []uint32 { + if m != nil { + return m.Labels + } + return nil +} + +func (m *LabeledIPAddressPrefix) GetPrefixLen() uint32 { + if m != nil { + return m.PrefixLen + } + return 0 +} + +func (m *LabeledIPAddressPrefix) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +// EncapsulationNLRI represents the NLRI for: +// - AFI=1, SAFI=7 +// - AFI=2, SAFI=7 +type EncapsulationNLRI struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` +} + +func (m *EncapsulationNLRI) Reset() { *m = EncapsulationNLRI{} } +func (m *EncapsulationNLRI) String() string { return proto.CompactTextString(m) } +func (*EncapsulationNLRI) ProtoMessage() {} +func (*EncapsulationNLRI) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{13} } + +func (m *EncapsulationNLRI) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type RouteDistinguisherTwoOctetAS struct { + Admin uint32 `protobuf:"varint,1,opt,name=admin" json:"admin,omitempty"` + Assigned uint32 `protobuf:"varint,2,opt,name=assigned" json:"assigned,omitempty"` +} + +func (m *RouteDistinguisherTwoOctetAS) Reset() { *m = RouteDistinguisherTwoOctetAS{} } +func (m *RouteDistinguisherTwoOctetAS) String() string { return proto.CompactTextString(m) } +func (*RouteDistinguisherTwoOctetAS) ProtoMessage() {} +func (*RouteDistinguisherTwoOctetAS) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{14} } + +func (m *RouteDistinguisherTwoOctetAS) GetAdmin() uint32 { + if m != nil { + return m.Admin + } + return 0 +} + +func (m *RouteDistinguisherTwoOctetAS) GetAssigned() uint32 { + if m != nil { + return m.Assigned + } + return 0 +} + +type RouteDistinguisherIPAddress struct { + Admin string `protobuf:"bytes,1,opt,name=admin" json:"admin,omitempty"` + Assigned uint32 `protobuf:"varint,2,opt,name=assigned" json:"assigned,omitempty"` +} + +func (m *RouteDistinguisherIPAddress) Reset() { *m = RouteDistinguisherIPAddress{} } +func (m *RouteDistinguisherIPAddress) String() string { return proto.CompactTextString(m) } +func (*RouteDistinguisherIPAddress) ProtoMessage() {} +func (*RouteDistinguisherIPAddress) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{15} } + +func (m *RouteDistinguisherIPAddress) GetAdmin() string { + if m != nil { + return m.Admin + } + return "" +} + +func (m *RouteDistinguisherIPAddress) GetAssigned() uint32 { + if m != nil { + return m.Assigned + } + return 0 +} + +type RouteDistinguisherFourOctetAS struct { + Admin uint32 `protobuf:"varint,1,opt,name=admin" json:"admin,omitempty"` + Assigned uint32 `protobuf:"varint,2,opt,name=assigned" json:"assigned,omitempty"` +} + +func (m *RouteDistinguisherFourOctetAS) Reset() { *m = RouteDistinguisherFourOctetAS{} } +func (m *RouteDistinguisherFourOctetAS) String() string { return proto.CompactTextString(m) } +func (*RouteDistinguisherFourOctetAS) ProtoMessage() {} +func (*RouteDistinguisherFourOctetAS) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{16} } + +func (m *RouteDistinguisherFourOctetAS) GetAdmin() uint32 { + if m != nil { + return m.Admin + } + return 0 +} + +func (m *RouteDistinguisherFourOctetAS) GetAssigned() uint32 { + if m != nil { + return m.Assigned + } + return 0 +} + +type EthernetSegmentIdentifier struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *EthernetSegmentIdentifier) Reset() { *m = EthernetSegmentIdentifier{} } +func (m *EthernetSegmentIdentifier) String() string { return proto.CompactTextString(m) } +func (*EthernetSegmentIdentifier) ProtoMessage() {} +func (*EthernetSegmentIdentifier) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{17} } + +func (m *EthernetSegmentIdentifier) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *EthernetSegmentIdentifier) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +// EVPNEthernetAutoDiscoveryRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=1 +type EVPNEthernetAutoDiscoveryRoute struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + Esi *EthernetSegmentIdentifier `protobuf:"bytes,2,opt,name=esi" json:"esi,omitempty"` + EthernetTag uint32 `protobuf:"varint,3,opt,name=ethernet_tag,json=ethernetTag" json:"ethernet_tag,omitempty"` + Label uint32 `protobuf:"varint,4,opt,name=label" json:"label,omitempty"` +} + +func (m *EVPNEthernetAutoDiscoveryRoute) Reset() { *m = EVPNEthernetAutoDiscoveryRoute{} } +func (m *EVPNEthernetAutoDiscoveryRoute) String() string { return proto.CompactTextString(m) } +func (*EVPNEthernetAutoDiscoveryRoute) ProtoMessage() {} +func (*EVPNEthernetAutoDiscoveryRoute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{18} } + +func (m *EVPNEthernetAutoDiscoveryRoute) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *EVPNEthernetAutoDiscoveryRoute) GetEsi() *EthernetSegmentIdentifier { + if m != nil { + return m.Esi + } + return nil +} + +func (m *EVPNEthernetAutoDiscoveryRoute) GetEthernetTag() uint32 { + if m != nil { + return m.EthernetTag + } + return 0 +} + +func (m *EVPNEthernetAutoDiscoveryRoute) GetLabel() uint32 { + if m != nil { + return m.Label + } + return 0 +} + +// EVPNMACIPAdvertisementRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=2 +type EVPNMACIPAdvertisementRoute struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + Esi *EthernetSegmentIdentifier `protobuf:"bytes,2,opt,name=esi" json:"esi,omitempty"` + EthernetTag uint32 `protobuf:"varint,3,opt,name=ethernet_tag,json=ethernetTag" json:"ethernet_tag,omitempty"` + MacAddress string `protobuf:"bytes,4,opt,name=mac_address,json=macAddress" json:"mac_address,omitempty"` + IpAddress string `protobuf:"bytes,5,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` + Labels []uint32 `protobuf:"varint,6,rep,packed,name=labels" json:"labels,omitempty"` +} + +func (m *EVPNMACIPAdvertisementRoute) Reset() { *m = EVPNMACIPAdvertisementRoute{} } +func (m *EVPNMACIPAdvertisementRoute) String() string { return proto.CompactTextString(m) } +func (*EVPNMACIPAdvertisementRoute) ProtoMessage() {} +func (*EVPNMACIPAdvertisementRoute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{19} } + +func (m *EVPNMACIPAdvertisementRoute) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *EVPNMACIPAdvertisementRoute) GetEsi() *EthernetSegmentIdentifier { + if m != nil { + return m.Esi + } + return nil +} + +func (m *EVPNMACIPAdvertisementRoute) GetEthernetTag() uint32 { + if m != nil { + return m.EthernetTag + } + return 0 +} + +func (m *EVPNMACIPAdvertisementRoute) GetMacAddress() string { + if m != nil { + return m.MacAddress + } + return "" +} + +func (m *EVPNMACIPAdvertisementRoute) GetIpAddress() string { + if m != nil { + return m.IpAddress + } + return "" +} + +func (m *EVPNMACIPAdvertisementRoute) GetLabels() []uint32 { + if m != nil { + return m.Labels + } + return nil +} + +// EVPNInclusiveMulticastEthernetTagRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=3 +type EVPNInclusiveMulticastEthernetTagRoute struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + EthernetTag uint32 `protobuf:"varint,2,opt,name=ethernet_tag,json=ethernetTag" json:"ethernet_tag,omitempty"` + IpAddress string `protobuf:"bytes,3,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` +} + +func (m *EVPNInclusiveMulticastEthernetTagRoute) Reset() { + *m = EVPNInclusiveMulticastEthernetTagRoute{} +} +func (m *EVPNInclusiveMulticastEthernetTagRoute) String() string { return proto.CompactTextString(m) } +func (*EVPNInclusiveMulticastEthernetTagRoute) ProtoMessage() {} +func (*EVPNInclusiveMulticastEthernetTagRoute) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{20} +} + +func (m *EVPNInclusiveMulticastEthernetTagRoute) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *EVPNInclusiveMulticastEthernetTagRoute) GetEthernetTag() uint32 { + if m != nil { + return m.EthernetTag + } + return 0 +} + +func (m *EVPNInclusiveMulticastEthernetTagRoute) GetIpAddress() string { + if m != nil { + return m.IpAddress + } + return "" +} + +// EVPNEthernetSegmentRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=4 +type EVPNEthernetSegmentRoute struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + Esi *EthernetSegmentIdentifier `protobuf:"bytes,2,opt,name=esi" json:"esi,omitempty"` + IpAddress string `protobuf:"bytes,3,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` +} + +func (m *EVPNEthernetSegmentRoute) Reset() { *m = EVPNEthernetSegmentRoute{} } +func (m *EVPNEthernetSegmentRoute) String() string { return proto.CompactTextString(m) } +func (*EVPNEthernetSegmentRoute) ProtoMessage() {} +func (*EVPNEthernetSegmentRoute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{21} } + +func (m *EVPNEthernetSegmentRoute) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *EVPNEthernetSegmentRoute) GetEsi() *EthernetSegmentIdentifier { + if m != nil { + return m.Esi + } + return nil +} + +func (m *EVPNEthernetSegmentRoute) GetIpAddress() string { + if m != nil { + return m.IpAddress + } + return "" +} + +// EVPNIPPrefixRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=5 +type EVPNIPPrefixRoute struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + Esi *EthernetSegmentIdentifier `protobuf:"bytes,2,opt,name=esi" json:"esi,omitempty"` + EthernetTag uint32 `protobuf:"varint,3,opt,name=ethernet_tag,json=ethernetTag" json:"ethernet_tag,omitempty"` + IpPrefix string `protobuf:"bytes,4,opt,name=ip_prefix,json=ipPrefix" json:"ip_prefix,omitempty"` + IpPrefixLen uint32 `protobuf:"varint,5,opt,name=ip_prefix_len,json=ipPrefixLen" json:"ip_prefix_len,omitempty"` + GwAddress string `protobuf:"bytes,6,opt,name=gw_address,json=gwAddress" json:"gw_address,omitempty"` + Label uint32 `protobuf:"varint,7,opt,name=label" json:"label,omitempty"` +} + +func (m *EVPNIPPrefixRoute) Reset() { *m = EVPNIPPrefixRoute{} } +func (m *EVPNIPPrefixRoute) String() string { return proto.CompactTextString(m) } +func (*EVPNIPPrefixRoute) ProtoMessage() {} +func (*EVPNIPPrefixRoute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{22} } + +func (m *EVPNIPPrefixRoute) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *EVPNIPPrefixRoute) GetEsi() *EthernetSegmentIdentifier { + if m != nil { + return m.Esi + } + return nil +} + +func (m *EVPNIPPrefixRoute) GetEthernetTag() uint32 { + if m != nil { + return m.EthernetTag + } + return 0 +} + +func (m *EVPNIPPrefixRoute) GetIpPrefix() string { + if m != nil { + return m.IpPrefix + } + return "" +} + +func (m *EVPNIPPrefixRoute) GetIpPrefixLen() uint32 { + if m != nil { + return m.IpPrefixLen + } + return 0 +} + +func (m *EVPNIPPrefixRoute) GetGwAddress() string { + if m != nil { + return m.GwAddress + } + return "" +} + +func (m *EVPNIPPrefixRoute) GetLabel() uint32 { + if m != nil { + return m.Label + } + return 0 +} + +// LabeledVPNIPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=128 +// - AFI=2, SAFI=128 +type LabeledVPNIPAddressPrefix struct { + Labels []uint32 `protobuf:"varint,1,rep,packed,name=labels" json:"labels,omitempty"` + // One of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + Rd *google_protobuf.Any `protobuf:"bytes,2,opt,name=rd" json:"rd,omitempty"` + PrefixLen uint32 `protobuf:"varint,3,opt,name=prefix_len,json=prefixLen" json:"prefix_len,omitempty"` + Prefix string `protobuf:"bytes,4,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *LabeledVPNIPAddressPrefix) Reset() { *m = LabeledVPNIPAddressPrefix{} } +func (m *LabeledVPNIPAddressPrefix) String() string { return proto.CompactTextString(m) } +func (*LabeledVPNIPAddressPrefix) ProtoMessage() {} +func (*LabeledVPNIPAddressPrefix) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{23} } + +func (m *LabeledVPNIPAddressPrefix) GetLabels() []uint32 { + if m != nil { + return m.Labels + } + return nil +} + +func (m *LabeledVPNIPAddressPrefix) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *LabeledVPNIPAddressPrefix) GetPrefixLen() uint32 { + if m != nil { + return m.PrefixLen + } + return 0 +} + +func (m *LabeledVPNIPAddressPrefix) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +// RouteTargetMembershipNLRI represents the NLRI for: +// - AFI=1, SAFI=132 +type RouteTargetMembershipNLRI struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + // One of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + Rt *google_protobuf.Any `protobuf:"bytes,2,opt,name=rt" json:"rt,omitempty"` +} + +func (m *RouteTargetMembershipNLRI) Reset() { *m = RouteTargetMembershipNLRI{} } +func (m *RouteTargetMembershipNLRI) String() string { return proto.CompactTextString(m) } +func (*RouteTargetMembershipNLRI) ProtoMessage() {} +func (*RouteTargetMembershipNLRI) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{24} } + +func (m *RouteTargetMembershipNLRI) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *RouteTargetMembershipNLRI) GetRt() *google_protobuf.Any { + if m != nil { + return m.Rt + } + return nil +} + +type FlowSpecIPPrefix struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + PrefixLen uint32 `protobuf:"varint,2,opt,name=prefix_len,json=prefixLen" json:"prefix_len,omitempty"` + Prefix string `protobuf:"bytes,3,opt,name=prefix" json:"prefix,omitempty"` + // IPv6 only + Offset uint32 `protobuf:"varint,4,opt,name=offset" json:"offset,omitempty"` +} + +func (m *FlowSpecIPPrefix) Reset() { *m = FlowSpecIPPrefix{} } +func (m *FlowSpecIPPrefix) String() string { return proto.CompactTextString(m) } +func (*FlowSpecIPPrefix) ProtoMessage() {} +func (*FlowSpecIPPrefix) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{25} } + +func (m *FlowSpecIPPrefix) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *FlowSpecIPPrefix) GetPrefixLen() uint32 { + if m != nil { + return m.PrefixLen + } + return 0 +} + +func (m *FlowSpecIPPrefix) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *FlowSpecIPPrefix) GetOffset() uint32 { + if m != nil { + return m.Offset + } + return 0 +} + +type FlowSpecMAC struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address" json:"address,omitempty"` +} + +func (m *FlowSpecMAC) Reset() { *m = FlowSpecMAC{} } +func (m *FlowSpecMAC) String() string { return proto.CompactTextString(m) } +func (*FlowSpecMAC) ProtoMessage() {} +func (*FlowSpecMAC) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{26} } + +func (m *FlowSpecMAC) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *FlowSpecMAC) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type FlowSpecComponentItem struct { + // Operator for Numeric type, Operand for Bitmask type + Op uint32 `protobuf:"varint,1,opt,name=op" json:"op,omitempty"` + Value uint64 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` +} + +func (m *FlowSpecComponentItem) Reset() { *m = FlowSpecComponentItem{} } +func (m *FlowSpecComponentItem) String() string { return proto.CompactTextString(m) } +func (*FlowSpecComponentItem) ProtoMessage() {} +func (*FlowSpecComponentItem) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{27} } + +func (m *FlowSpecComponentItem) GetOp() uint32 { + if m != nil { + return m.Op + } + return 0 +} + +func (m *FlowSpecComponentItem) GetValue() uint64 { + if m != nil { + return m.Value + } + return 0 +} + +type FlowSpecComponent struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Items []*FlowSpecComponentItem `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` +} + +func (m *FlowSpecComponent) Reset() { *m = FlowSpecComponent{} } +func (m *FlowSpecComponent) String() string { return proto.CompactTextString(m) } +func (*FlowSpecComponent) ProtoMessage() {} +func (*FlowSpecComponent) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{28} } + +func (m *FlowSpecComponent) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *FlowSpecComponent) GetItems() []*FlowSpecComponentItem { + if m != nil { + return m.Items + } + return nil +} + +// FlowSpecNLRI represents the NLRI for: +// - AFI=1, SAFI=133 +// - AFI=2, SAFI=133 +type FlowSpecNLRI struct { + // One of: + // - FlowSpecIPPrefix + // - FlowSpecMAC + // - FlowSpecComponent + Rules []*google_protobuf.Any `protobuf:"bytes,1,rep,name=rules" json:"rules,omitempty"` +} + +func (m *FlowSpecNLRI) Reset() { *m = FlowSpecNLRI{} } +func (m *FlowSpecNLRI) String() string { return proto.CompactTextString(m) } +func (*FlowSpecNLRI) ProtoMessage() {} +func (*FlowSpecNLRI) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{29} } + +func (m *FlowSpecNLRI) GetRules() []*google_protobuf.Any { + if m != nil { + return m.Rules + } + return nil +} + +// VPNFlowSpecNLRI represents the NLRI for: +// - AFI=1, SAFI=134 +// - AFI=2, SAFI=134 +// - AFI=25, SAFI=134 +type VPNFlowSpecNLRI struct { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + Rd *google_protobuf.Any `protobuf:"bytes,1,opt,name=rd" json:"rd,omitempty"` + // One of: + // - FlowSpecIPPrefix + // - FlowSpecMAC + // - FlowSpecComponent + Rules []*google_protobuf.Any `protobuf:"bytes,2,rep,name=rules" json:"rules,omitempty"` +} + +func (m *VPNFlowSpecNLRI) Reset() { *m = VPNFlowSpecNLRI{} } +func (m *VPNFlowSpecNLRI) String() string { return proto.CompactTextString(m) } +func (*VPNFlowSpecNLRI) ProtoMessage() {} +func (*VPNFlowSpecNLRI) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{30} } + +func (m *VPNFlowSpecNLRI) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *VPNFlowSpecNLRI) GetRules() []*google_protobuf.Any { + if m != nil { + return m.Rules + } + return nil +} + +// OpaqueNLRI represents the NLRI for: +// - AFI=16397, SAFI=241 +type OpaqueNLRI struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *OpaqueNLRI) Reset() { *m = OpaqueNLRI{} } +func (m *OpaqueNLRI) String() string { return proto.CompactTextString(m) } +func (*OpaqueNLRI) ProtoMessage() {} +func (*OpaqueNLRI) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{31} } + +func (m *OpaqueNLRI) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *OpaqueNLRI) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type MpReachNLRIAttribute struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + NextHops []string `protobuf:"bytes,2,rep,name=next_hops,json=nextHops" json:"next_hops,omitempty"` + // Each NLRI must be one of: + // - IPAddressPrefix + // - LabeledIPAddressPrefix + // - EncapsulationNLRI + // - EVPNEthernetAutoDiscoveryRoute + // - EVPNMACIPAdvertisementRoute + // - EVPNInclusiveMulticastEthernetTagRoute + // - EVPNEthernetSegmentRoute + // - EVPNIPPrefixRoute + // - LabeledVPNIPAddressPrefix + // - RouteTargetMembershipNLRI + // - FlowSpecNLRI + // - VPNFlowSpecNLRI + // - OpaqueNLRI + Nlris []*google_protobuf.Any `protobuf:"bytes,3,rep,name=nlris" json:"nlris,omitempty"` +} + +func (m *MpReachNLRIAttribute) Reset() { *m = MpReachNLRIAttribute{} } +func (m *MpReachNLRIAttribute) String() string { return proto.CompactTextString(m) } +func (*MpReachNLRIAttribute) ProtoMessage() {} +func (*MpReachNLRIAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{32} } + +func (m *MpReachNLRIAttribute) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *MpReachNLRIAttribute) GetNextHops() []string { + if m != nil { + return m.NextHops + } + return nil +} + +func (m *MpReachNLRIAttribute) GetNlris() []*google_protobuf.Any { + if m != nil { + return m.Nlris + } + return nil +} + +type MpUnreachNLRIAttribute struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + // The same as NLRI field of MpReachNLRIAttribute + Nlris []*google_protobuf.Any `protobuf:"bytes,3,rep,name=nlris" json:"nlris,omitempty"` +} + +func (m *MpUnreachNLRIAttribute) Reset() { *m = MpUnreachNLRIAttribute{} } +func (m *MpUnreachNLRIAttribute) String() string { return proto.CompactTextString(m) } +func (*MpUnreachNLRIAttribute) ProtoMessage() {} +func (*MpUnreachNLRIAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{33} } + +func (m *MpUnreachNLRIAttribute) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *MpUnreachNLRIAttribute) GetNlris() []*google_protobuf.Any { + if m != nil { + return m.Nlris + } + return nil +} + +type TwoOctetAsSpecificExtended struct { + IsTransitive bool `protobuf:"varint,1,opt,name=is_transitive,json=isTransitive" json:"is_transitive,omitempty"` + SubType uint32 `protobuf:"varint,2,opt,name=sub_type,json=subType" json:"sub_type,omitempty"` + As uint32 `protobuf:"varint,3,opt,name=as" json:"as,omitempty"` + LocalAdmin uint32 `protobuf:"varint,4,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *TwoOctetAsSpecificExtended) Reset() { *m = TwoOctetAsSpecificExtended{} } +func (m *TwoOctetAsSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*TwoOctetAsSpecificExtended) ProtoMessage() {} +func (*TwoOctetAsSpecificExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{34} } + +func (m *TwoOctetAsSpecificExtended) GetIsTransitive() bool { + if m != nil { + return m.IsTransitive + } + return false +} + +func (m *TwoOctetAsSpecificExtended) GetSubType() uint32 { + if m != nil { + return m.SubType + } + return 0 +} + +func (m *TwoOctetAsSpecificExtended) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *TwoOctetAsSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type IPv4AddressSpecificExtended struct { + IsTransitive bool `protobuf:"varint,1,opt,name=is_transitive,json=isTransitive" json:"is_transitive,omitempty"` + SubType uint32 `protobuf:"varint,2,opt,name=sub_type,json=subType" json:"sub_type,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` + LocalAdmin uint32 `protobuf:"varint,4,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *IPv4AddressSpecificExtended) Reset() { *m = IPv4AddressSpecificExtended{} } +func (m *IPv4AddressSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*IPv4AddressSpecificExtended) ProtoMessage() {} +func (*IPv4AddressSpecificExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{35} } + +func (m *IPv4AddressSpecificExtended) GetIsTransitive() bool { + if m != nil { + return m.IsTransitive + } + return false +} + +func (m *IPv4AddressSpecificExtended) GetSubType() uint32 { + if m != nil { + return m.SubType + } + return 0 +} + +func (m *IPv4AddressSpecificExtended) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *IPv4AddressSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type FourOctetAsSpecificExtended struct { + IsTransitive bool `protobuf:"varint,1,opt,name=is_transitive,json=isTransitive" json:"is_transitive,omitempty"` + SubType uint32 `protobuf:"varint,2,opt,name=sub_type,json=subType" json:"sub_type,omitempty"` + As uint32 `protobuf:"varint,3,opt,name=as" json:"as,omitempty"` + LocalAdmin uint32 `protobuf:"varint,4,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *FourOctetAsSpecificExtended) Reset() { *m = FourOctetAsSpecificExtended{} } +func (m *FourOctetAsSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*FourOctetAsSpecificExtended) ProtoMessage() {} +func (*FourOctetAsSpecificExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{36} } + +func (m *FourOctetAsSpecificExtended) GetIsTransitive() bool { + if m != nil { + return m.IsTransitive + } + return false +} + +func (m *FourOctetAsSpecificExtended) GetSubType() uint32 { + if m != nil { + return m.SubType + } + return 0 +} + +func (m *FourOctetAsSpecificExtended) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *FourOctetAsSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type ValidationExtended struct { + State uint32 `protobuf:"varint,1,opt,name=state" json:"state,omitempty"` +} + +func (m *ValidationExtended) Reset() { *m = ValidationExtended{} } +func (m *ValidationExtended) String() string { return proto.CompactTextString(m) } +func (*ValidationExtended) ProtoMessage() {} +func (*ValidationExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{37} } + +func (m *ValidationExtended) GetState() uint32 { + if m != nil { + return m.State + } + return 0 +} + +type ColorExtended struct { + Color uint32 `protobuf:"varint,1,opt,name=color" json:"color,omitempty"` +} + +func (m *ColorExtended) Reset() { *m = ColorExtended{} } +func (m *ColorExtended) String() string { return proto.CompactTextString(m) } +func (*ColorExtended) ProtoMessage() {} +func (*ColorExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{38} } + +func (m *ColorExtended) GetColor() uint32 { + if m != nil { + return m.Color + } + return 0 +} + +type EncapExtended struct { + TunnelType uint32 `protobuf:"varint,1,opt,name=tunnel_type,json=tunnelType" json:"tunnel_type,omitempty"` +} + +func (m *EncapExtended) Reset() { *m = EncapExtended{} } +func (m *EncapExtended) String() string { return proto.CompactTextString(m) } +func (*EncapExtended) ProtoMessage() {} +func (*EncapExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{39} } + +func (m *EncapExtended) GetTunnelType() uint32 { + if m != nil { + return m.TunnelType + } + return 0 +} + +type DefaultGatewayExtended struct { +} + +func (m *DefaultGatewayExtended) Reset() { *m = DefaultGatewayExtended{} } +func (m *DefaultGatewayExtended) String() string { return proto.CompactTextString(m) } +func (*DefaultGatewayExtended) ProtoMessage() {} +func (*DefaultGatewayExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{40} } + +type OpaqueExtended struct { + IsTransitive bool `protobuf:"varint,1,opt,name=is_transitive,json=isTransitive" json:"is_transitive,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *OpaqueExtended) Reset() { *m = OpaqueExtended{} } +func (m *OpaqueExtended) String() string { return proto.CompactTextString(m) } +func (*OpaqueExtended) ProtoMessage() {} +func (*OpaqueExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{41} } + +func (m *OpaqueExtended) GetIsTransitive() bool { + if m != nil { + return m.IsTransitive + } + return false +} + +func (m *OpaqueExtended) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type ESILabelExtended struct { + IsSingleActive bool `protobuf:"varint,1,opt,name=is_single_active,json=isSingleActive" json:"is_single_active,omitempty"` + Label uint32 `protobuf:"varint,2,opt,name=label" json:"label,omitempty"` +} + +func (m *ESILabelExtended) Reset() { *m = ESILabelExtended{} } +func (m *ESILabelExtended) String() string { return proto.CompactTextString(m) } +func (*ESILabelExtended) ProtoMessage() {} +func (*ESILabelExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{42} } + +func (m *ESILabelExtended) GetIsSingleActive() bool { + if m != nil { + return m.IsSingleActive + } + return false +} + +func (m *ESILabelExtended) GetLabel() uint32 { + if m != nil { + return m.Label + } + return 0 +} + +type ESImportRouteTarget struct { + EsImport string `protobuf:"bytes,1,opt,name=es_import,json=esImport" json:"es_import,omitempty"` +} + +func (m *ESImportRouteTarget) Reset() { *m = ESImportRouteTarget{} } +func (m *ESImportRouteTarget) String() string { return proto.CompactTextString(m) } +func (*ESImportRouteTarget) ProtoMessage() {} +func (*ESImportRouteTarget) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{43} } + +func (m *ESImportRouteTarget) GetEsImport() string { + if m != nil { + return m.EsImport + } + return "" +} + +type MacMobilityExtended struct { + IsSticky bool `protobuf:"varint,1,opt,name=is_sticky,json=isSticky" json:"is_sticky,omitempty"` + SequenceNum uint32 `protobuf:"varint,2,opt,name=sequence_num,json=sequenceNum" json:"sequence_num,omitempty"` +} + +func (m *MacMobilityExtended) Reset() { *m = MacMobilityExtended{} } +func (m *MacMobilityExtended) String() string { return proto.CompactTextString(m) } +func (*MacMobilityExtended) ProtoMessage() {} +func (*MacMobilityExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{44} } + +func (m *MacMobilityExtended) GetIsSticky() bool { + if m != nil { + return m.IsSticky + } + return false +} + +func (m *MacMobilityExtended) GetSequenceNum() uint32 { + if m != nil { + return m.SequenceNum + } + return 0 +} + +type RouterMacExtended struct { + Mac string `protobuf:"bytes,1,opt,name=mac" json:"mac,omitempty"` +} + +func (m *RouterMacExtended) Reset() { *m = RouterMacExtended{} } +func (m *RouterMacExtended) String() string { return proto.CompactTextString(m) } +func (*RouterMacExtended) ProtoMessage() {} +func (*RouterMacExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{45} } + +func (m *RouterMacExtended) GetMac() string { + if m != nil { + return m.Mac + } + return "" +} + +type TrafficRateExtended struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + Rate float32 `protobuf:"fixed32,2,opt,name=rate" json:"rate,omitempty"` +} + +func (m *TrafficRateExtended) Reset() { *m = TrafficRateExtended{} } +func (m *TrafficRateExtended) String() string { return proto.CompactTextString(m) } +func (*TrafficRateExtended) ProtoMessage() {} +func (*TrafficRateExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{46} } + +func (m *TrafficRateExtended) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *TrafficRateExtended) GetRate() float32 { + if m != nil { + return m.Rate + } + return 0 +} + +type TrafficActionExtended struct { + Terminal bool `protobuf:"varint,1,opt,name=terminal" json:"terminal,omitempty"` + Sample bool `protobuf:"varint,2,opt,name=sample" json:"sample,omitempty"` +} + +func (m *TrafficActionExtended) Reset() { *m = TrafficActionExtended{} } +func (m *TrafficActionExtended) String() string { return proto.CompactTextString(m) } +func (*TrafficActionExtended) ProtoMessage() {} +func (*TrafficActionExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{47} } + +func (m *TrafficActionExtended) GetTerminal() bool { + if m != nil { + return m.Terminal + } + return false +} + +func (m *TrafficActionExtended) GetSample() bool { + if m != nil { + return m.Sample + } + return false +} + +type RedirectTwoOctetAsSpecificExtended struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + LocalAdmin uint32 `protobuf:"varint,2,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *RedirectTwoOctetAsSpecificExtended) Reset() { *m = RedirectTwoOctetAsSpecificExtended{} } +func (m *RedirectTwoOctetAsSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*RedirectTwoOctetAsSpecificExtended) ProtoMessage() {} +func (*RedirectTwoOctetAsSpecificExtended) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{48} +} + +func (m *RedirectTwoOctetAsSpecificExtended) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *RedirectTwoOctetAsSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type RedirectIPv4AddressSpecificExtended struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + LocalAdmin uint32 `protobuf:"varint,2,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *RedirectIPv4AddressSpecificExtended) Reset() { *m = RedirectIPv4AddressSpecificExtended{} } +func (m *RedirectIPv4AddressSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*RedirectIPv4AddressSpecificExtended) ProtoMessage() {} +func (*RedirectIPv4AddressSpecificExtended) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{49} +} + +func (m *RedirectIPv4AddressSpecificExtended) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *RedirectIPv4AddressSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type RedirectFourOctetAsSpecificExtended struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + LocalAdmin uint32 `protobuf:"varint,2,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *RedirectFourOctetAsSpecificExtended) Reset() { *m = RedirectFourOctetAsSpecificExtended{} } +func (m *RedirectFourOctetAsSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*RedirectFourOctetAsSpecificExtended) ProtoMessage() {} +func (*RedirectFourOctetAsSpecificExtended) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{50} +} + +func (m *RedirectFourOctetAsSpecificExtended) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *RedirectFourOctetAsSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type TrafficRemarkExtended struct { + Dscp uint32 `protobuf:"varint,1,opt,name=dscp" json:"dscp,omitempty"` +} + +func (m *TrafficRemarkExtended) Reset() { *m = TrafficRemarkExtended{} } +func (m *TrafficRemarkExtended) String() string { return proto.CompactTextString(m) } +func (*TrafficRemarkExtended) ProtoMessage() {} +func (*TrafficRemarkExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{51} } + +func (m *TrafficRemarkExtended) GetDscp() uint32 { + if m != nil { + return m.Dscp + } + return 0 +} + +type UnknownExtended struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *UnknownExtended) Reset() { *m = UnknownExtended{} } +func (m *UnknownExtended) String() string { return proto.CompactTextString(m) } +func (*UnknownExtended) ProtoMessage() {} +func (*UnknownExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{52} } + +func (m *UnknownExtended) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *UnknownExtended) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type ExtendedCommunitiesAttribute struct { + // Each Community must be one of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + // - OpaqueExtended + // - ESILabelExtended + // - MacMobilityExtended + // - RouterMacExtended + // - TrafficRateExtended + // - TrafficActionExtended + // - RedirectTwoOctetAsSpecificExtended + // - RedirectIPv4AddressSpecificExtended + // - RedirectFourOctetAsSpecificExtended + // - TrafficRemarkExtended + // - UnknownExtended + Communities []*google_protobuf.Any `protobuf:"bytes,1,rep,name=communities" json:"communities,omitempty"` +} + +func (m *ExtendedCommunitiesAttribute) Reset() { *m = ExtendedCommunitiesAttribute{} } +func (m *ExtendedCommunitiesAttribute) String() string { return proto.CompactTextString(m) } +func (*ExtendedCommunitiesAttribute) ProtoMessage() {} +func (*ExtendedCommunitiesAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{53} } + +func (m *ExtendedCommunitiesAttribute) GetCommunities() []*google_protobuf.Any { + if m != nil { + return m.Communities + } + return nil +} + +type As4PathAttribute struct { + Segments []*AsSegment `protobuf:"bytes,1,rep,name=segments" json:"segments,omitempty"` +} + +func (m *As4PathAttribute) Reset() { *m = As4PathAttribute{} } +func (m *As4PathAttribute) String() string { return proto.CompactTextString(m) } +func (*As4PathAttribute) ProtoMessage() {} +func (*As4PathAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{54} } + +func (m *As4PathAttribute) GetSegments() []*AsSegment { + if m != nil { + return m.Segments + } + return nil +} + +type As4AggregatorAttribute struct { + As uint32 `protobuf:"varint,2,opt,name=as" json:"as,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` +} + +func (m *As4AggregatorAttribute) Reset() { *m = As4AggregatorAttribute{} } +func (m *As4AggregatorAttribute) String() string { return proto.CompactTextString(m) } +func (*As4AggregatorAttribute) ProtoMessage() {} +func (*As4AggregatorAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{55} } + +func (m *As4AggregatorAttribute) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *As4AggregatorAttribute) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type PmsiTunnelAttribute struct { + Flags uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"` + Type uint32 `protobuf:"varint,2,opt,name=type" json:"type,omitempty"` + Label uint32 `protobuf:"varint,3,opt,name=label" json:"label,omitempty"` + Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *PmsiTunnelAttribute) Reset() { *m = PmsiTunnelAttribute{} } +func (m *PmsiTunnelAttribute) String() string { return proto.CompactTextString(m) } +func (*PmsiTunnelAttribute) ProtoMessage() {} +func (*PmsiTunnelAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{56} } + +func (m *PmsiTunnelAttribute) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +func (m *PmsiTunnelAttribute) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *PmsiTunnelAttribute) GetLabel() uint32 { + if m != nil { + return m.Label + } + return 0 +} + +func (m *PmsiTunnelAttribute) GetId() []byte { + if m != nil { + return m.Id + } + return nil +} + +type TunnelEncapSubTLVEncapsulation struct { + Key uint32 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"` + Cookie []byte `protobuf:"bytes,2,opt,name=cookie,proto3" json:"cookie,omitempty"` +} + +func (m *TunnelEncapSubTLVEncapsulation) Reset() { *m = TunnelEncapSubTLVEncapsulation{} } +func (m *TunnelEncapSubTLVEncapsulation) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapSubTLVEncapsulation) ProtoMessage() {} +func (*TunnelEncapSubTLVEncapsulation) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{57} } + +func (m *TunnelEncapSubTLVEncapsulation) GetKey() uint32 { + if m != nil { + return m.Key + } + return 0 +} + +func (m *TunnelEncapSubTLVEncapsulation) GetCookie() []byte { + if m != nil { + return m.Cookie + } + return nil +} + +type TunnelEncapSubTLVProtocol struct { + Protocol uint32 `protobuf:"varint,1,opt,name=protocol" json:"protocol,omitempty"` +} + +func (m *TunnelEncapSubTLVProtocol) Reset() { *m = TunnelEncapSubTLVProtocol{} } +func (m *TunnelEncapSubTLVProtocol) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapSubTLVProtocol) ProtoMessage() {} +func (*TunnelEncapSubTLVProtocol) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{58} } + +func (m *TunnelEncapSubTLVProtocol) GetProtocol() uint32 { + if m != nil { + return m.Protocol + } + return 0 +} + +type TunnelEncapSubTLVColor struct { + Color uint32 `protobuf:"varint,1,opt,name=color" json:"color,omitempty"` +} + +func (m *TunnelEncapSubTLVColor) Reset() { *m = TunnelEncapSubTLVColor{} } +func (m *TunnelEncapSubTLVColor) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapSubTLVColor) ProtoMessage() {} +func (*TunnelEncapSubTLVColor) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{59} } + +func (m *TunnelEncapSubTLVColor) GetColor() uint32 { + if m != nil { + return m.Color + } + return 0 +} + +type TunnelEncapSubTLVUnknown struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *TunnelEncapSubTLVUnknown) Reset() { *m = TunnelEncapSubTLVUnknown{} } +func (m *TunnelEncapSubTLVUnknown) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapSubTLVUnknown) ProtoMessage() {} +func (*TunnelEncapSubTLVUnknown) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{60} } + +func (m *TunnelEncapSubTLVUnknown) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *TunnelEncapSubTLVUnknown) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type TunnelEncapTLV struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + // Each TLV must be one of: + // - TunnelEncapSubTLVEncapsulation + // - TunnelEncapSubTLVProtocol + // - TunnelEncapSubTLVColor + // - TunnelEncapSubTLVUnknown + Tlvs []*google_protobuf.Any `protobuf:"bytes,2,rep,name=tlvs" json:"tlvs,omitempty"` +} + +func (m *TunnelEncapTLV) Reset() { *m = TunnelEncapTLV{} } +func (m *TunnelEncapTLV) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapTLV) ProtoMessage() {} +func (*TunnelEncapTLV) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{61} } + +func (m *TunnelEncapTLV) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *TunnelEncapTLV) GetTlvs() []*google_protobuf.Any { + if m != nil { + return m.Tlvs + } + return nil +} + +type TunnelEncapAttribute struct { + Tlvs []*TunnelEncapTLV `protobuf:"bytes,1,rep,name=tlvs" json:"tlvs,omitempty"` +} + +func (m *TunnelEncapAttribute) Reset() { *m = TunnelEncapAttribute{} } +func (m *TunnelEncapAttribute) String() string { return proto.CompactTextString(m) } +func (*TunnelEncapAttribute) ProtoMessage() {} +func (*TunnelEncapAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{62} } + +func (m *TunnelEncapAttribute) GetTlvs() []*TunnelEncapTLV { + if m != nil { + return m.Tlvs + } + return nil +} + +type IPv6AddressSpecificExtended struct { + IsTransitive bool `protobuf:"varint,1,opt,name=is_transitive,json=isTransitive" json:"is_transitive,omitempty"` + SubType uint32 `protobuf:"varint,2,opt,name=sub_type,json=subType" json:"sub_type,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` + LocalAdmin uint32 `protobuf:"varint,4,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *IPv6AddressSpecificExtended) Reset() { *m = IPv6AddressSpecificExtended{} } +func (m *IPv6AddressSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*IPv6AddressSpecificExtended) ProtoMessage() {} +func (*IPv6AddressSpecificExtended) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{63} } + +func (m *IPv6AddressSpecificExtended) GetIsTransitive() bool { + if m != nil { + return m.IsTransitive + } + return false +} + +func (m *IPv6AddressSpecificExtended) GetSubType() uint32 { + if m != nil { + return m.SubType + } + return 0 +} + +func (m *IPv6AddressSpecificExtended) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *IPv6AddressSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type RedirectIPv6AddressSpecificExtended struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + LocalAdmin uint32 `protobuf:"varint,2,opt,name=local_admin,json=localAdmin" json:"local_admin,omitempty"` +} + +func (m *RedirectIPv6AddressSpecificExtended) Reset() { *m = RedirectIPv6AddressSpecificExtended{} } +func (m *RedirectIPv6AddressSpecificExtended) String() string { return proto.CompactTextString(m) } +func (*RedirectIPv6AddressSpecificExtended) ProtoMessage() {} +func (*RedirectIPv6AddressSpecificExtended) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{64} +} + +func (m *RedirectIPv6AddressSpecificExtended) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *RedirectIPv6AddressSpecificExtended) GetLocalAdmin() uint32 { + if m != nil { + return m.LocalAdmin + } + return 0 +} + +type IP6ExtendedCommunitiesAttribute struct { + // Each Community must be one of: + // - IPv6AddressSpecificExtended + // - RedirectIPv6AddressSpecificExtended + Communities []*google_protobuf.Any `protobuf:"bytes,1,rep,name=communities" json:"communities,omitempty"` +} + +func (m *IP6ExtendedCommunitiesAttribute) Reset() { *m = IP6ExtendedCommunitiesAttribute{} } +func (m *IP6ExtendedCommunitiesAttribute) String() string { return proto.CompactTextString(m) } +func (*IP6ExtendedCommunitiesAttribute) ProtoMessage() {} +func (*IP6ExtendedCommunitiesAttribute) Descriptor() ([]byte, []int) { + return fileDescriptor1, []int{65} +} + +func (m *IP6ExtendedCommunitiesAttribute) GetCommunities() []*google_protobuf.Any { + if m != nil { + return m.Communities + } + return nil +} + +type AigpTLVIGPMetric struct { + Metric uint64 `protobuf:"varint,1,opt,name=metric" json:"metric,omitempty"` +} + +func (m *AigpTLVIGPMetric) Reset() { *m = AigpTLVIGPMetric{} } +func (m *AigpTLVIGPMetric) String() string { return proto.CompactTextString(m) } +func (*AigpTLVIGPMetric) ProtoMessage() {} +func (*AigpTLVIGPMetric) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{66} } + +func (m *AigpTLVIGPMetric) GetMetric() uint64 { + if m != nil { + return m.Metric + } + return 0 +} + +type AigpTLVUnknown struct { + Type uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *AigpTLVUnknown) Reset() { *m = AigpTLVUnknown{} } +func (m *AigpTLVUnknown) String() string { return proto.CompactTextString(m) } +func (*AigpTLVUnknown) ProtoMessage() {} +func (*AigpTLVUnknown) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{67} } + +func (m *AigpTLVUnknown) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *AigpTLVUnknown) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +type AigpAttribute struct { + // Each TLV must be one of: + // - AigpTLVIGPMetric + // - AigpTLVUnknown + Tlvs []*google_protobuf.Any `protobuf:"bytes,1,rep,name=tlvs" json:"tlvs,omitempty"` +} + +func (m *AigpAttribute) Reset() { *m = AigpAttribute{} } +func (m *AigpAttribute) String() string { return proto.CompactTextString(m) } +func (*AigpAttribute) ProtoMessage() {} +func (*AigpAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{68} } + +func (m *AigpAttribute) GetTlvs() []*google_protobuf.Any { + if m != nil { + return m.Tlvs + } + return nil +} + +type LargeCommunity struct { + GlobalAdmin uint32 `protobuf:"varint,1,opt,name=global_admin,json=globalAdmin" json:"global_admin,omitempty"` + LocalData1 uint32 `protobuf:"varint,2,opt,name=local_data1,json=localData1" json:"local_data1,omitempty"` + LocalData2 uint32 `protobuf:"varint,3,opt,name=local_data2,json=localData2" json:"local_data2,omitempty"` +} + +func (m *LargeCommunity) Reset() { *m = LargeCommunity{} } +func (m *LargeCommunity) String() string { return proto.CompactTextString(m) } +func (*LargeCommunity) ProtoMessage() {} +func (*LargeCommunity) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{69} } + +func (m *LargeCommunity) GetGlobalAdmin() uint32 { + if m != nil { + return m.GlobalAdmin + } + return 0 +} + +func (m *LargeCommunity) GetLocalData1() uint32 { + if m != nil { + return m.LocalData1 + } + return 0 +} + +func (m *LargeCommunity) GetLocalData2() uint32 { + if m != nil { + return m.LocalData2 + } + return 0 +} + +type LargeCommunitiesAttribute struct { + Communities []*LargeCommunity `protobuf:"bytes,1,rep,name=communities" json:"communities,omitempty"` +} + +func (m *LargeCommunitiesAttribute) Reset() { *m = LargeCommunitiesAttribute{} } +func (m *LargeCommunitiesAttribute) String() string { return proto.CompactTextString(m) } +func (*LargeCommunitiesAttribute) ProtoMessage() {} +func (*LargeCommunitiesAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{70} } + +func (m *LargeCommunitiesAttribute) GetCommunities() []*LargeCommunity { + if m != nil { + return m.Communities + } + return nil +} + +type UnknownAttribute struct { + Flags uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"` + Type uint32 `protobuf:"varint,2,opt,name=type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *UnknownAttribute) Reset() { *m = UnknownAttribute{} } +func (m *UnknownAttribute) String() string { return proto.CompactTextString(m) } +func (*UnknownAttribute) ProtoMessage() {} +func (*UnknownAttribute) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{71} } + +func (m *UnknownAttribute) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +func (m *UnknownAttribute) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *UnknownAttribute) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*OriginAttribute)(nil), "gobgpapi.OriginAttribute") + proto.RegisterType((*AsSegment)(nil), "gobgpapi.AsSegment") + proto.RegisterType((*AsPathAttribute)(nil), "gobgpapi.AsPathAttribute") + proto.RegisterType((*NextHopAttribute)(nil), "gobgpapi.NextHopAttribute") + proto.RegisterType((*MultiExitDiscAttribute)(nil), "gobgpapi.MultiExitDiscAttribute") + proto.RegisterType((*LocalPrefAttribute)(nil), "gobgpapi.LocalPrefAttribute") + proto.RegisterType((*AtomicAggregateAttribute)(nil), "gobgpapi.AtomicAggregateAttribute") + proto.RegisterType((*AggregatorAttribute)(nil), "gobgpapi.AggregatorAttribute") + proto.RegisterType((*CommunitiesAttribute)(nil), "gobgpapi.CommunitiesAttribute") + proto.RegisterType((*OriginatorIdAttribute)(nil), "gobgpapi.OriginatorIdAttribute") + proto.RegisterType((*ClusterListAttribute)(nil), "gobgpapi.ClusterListAttribute") + proto.RegisterType((*IPAddressPrefix)(nil), "gobgpapi.IPAddressPrefix") + proto.RegisterType((*LabeledIPAddressPrefix)(nil), "gobgpapi.LabeledIPAddressPrefix") + proto.RegisterType((*EncapsulationNLRI)(nil), "gobgpapi.EncapsulationNLRI") + proto.RegisterType((*RouteDistinguisherTwoOctetAS)(nil), "gobgpapi.RouteDistinguisherTwoOctetAS") + proto.RegisterType((*RouteDistinguisherIPAddress)(nil), "gobgpapi.RouteDistinguisherIPAddress") + proto.RegisterType((*RouteDistinguisherFourOctetAS)(nil), "gobgpapi.RouteDistinguisherFourOctetAS") + proto.RegisterType((*EthernetSegmentIdentifier)(nil), "gobgpapi.EthernetSegmentIdentifier") + proto.RegisterType((*EVPNEthernetAutoDiscoveryRoute)(nil), "gobgpapi.EVPNEthernetAutoDiscoveryRoute") + proto.RegisterType((*EVPNMACIPAdvertisementRoute)(nil), "gobgpapi.EVPNMACIPAdvertisementRoute") + proto.RegisterType((*EVPNInclusiveMulticastEthernetTagRoute)(nil), "gobgpapi.EVPNInclusiveMulticastEthernetTagRoute") + proto.RegisterType((*EVPNEthernetSegmentRoute)(nil), "gobgpapi.EVPNEthernetSegmentRoute") + proto.RegisterType((*EVPNIPPrefixRoute)(nil), "gobgpapi.EVPNIPPrefixRoute") + proto.RegisterType((*LabeledVPNIPAddressPrefix)(nil), "gobgpapi.LabeledVPNIPAddressPrefix") + proto.RegisterType((*RouteTargetMembershipNLRI)(nil), "gobgpapi.RouteTargetMembershipNLRI") + proto.RegisterType((*FlowSpecIPPrefix)(nil), "gobgpapi.FlowSpecIPPrefix") + proto.RegisterType((*FlowSpecMAC)(nil), "gobgpapi.FlowSpecMAC") + proto.RegisterType((*FlowSpecComponentItem)(nil), "gobgpapi.FlowSpecComponentItem") + proto.RegisterType((*FlowSpecComponent)(nil), "gobgpapi.FlowSpecComponent") + proto.RegisterType((*FlowSpecNLRI)(nil), "gobgpapi.FlowSpecNLRI") + proto.RegisterType((*VPNFlowSpecNLRI)(nil), "gobgpapi.VPNFlowSpecNLRI") + proto.RegisterType((*OpaqueNLRI)(nil), "gobgpapi.OpaqueNLRI") + proto.RegisterType((*MpReachNLRIAttribute)(nil), "gobgpapi.MpReachNLRIAttribute") + proto.RegisterType((*MpUnreachNLRIAttribute)(nil), "gobgpapi.MpUnreachNLRIAttribute") + proto.RegisterType((*TwoOctetAsSpecificExtended)(nil), "gobgpapi.TwoOctetAsSpecificExtended") + proto.RegisterType((*IPv4AddressSpecificExtended)(nil), "gobgpapi.IPv4AddressSpecificExtended") + proto.RegisterType((*FourOctetAsSpecificExtended)(nil), "gobgpapi.FourOctetAsSpecificExtended") + proto.RegisterType((*ValidationExtended)(nil), "gobgpapi.ValidationExtended") + proto.RegisterType((*ColorExtended)(nil), "gobgpapi.ColorExtended") + proto.RegisterType((*EncapExtended)(nil), "gobgpapi.EncapExtended") + proto.RegisterType((*DefaultGatewayExtended)(nil), "gobgpapi.DefaultGatewayExtended") + proto.RegisterType((*OpaqueExtended)(nil), "gobgpapi.OpaqueExtended") + proto.RegisterType((*ESILabelExtended)(nil), "gobgpapi.ESILabelExtended") + proto.RegisterType((*ESImportRouteTarget)(nil), "gobgpapi.ESImportRouteTarget") + proto.RegisterType((*MacMobilityExtended)(nil), "gobgpapi.MacMobilityExtended") + proto.RegisterType((*RouterMacExtended)(nil), "gobgpapi.RouterMacExtended") + proto.RegisterType((*TrafficRateExtended)(nil), "gobgpapi.TrafficRateExtended") + proto.RegisterType((*TrafficActionExtended)(nil), "gobgpapi.TrafficActionExtended") + proto.RegisterType((*RedirectTwoOctetAsSpecificExtended)(nil), "gobgpapi.RedirectTwoOctetAsSpecificExtended") + proto.RegisterType((*RedirectIPv4AddressSpecificExtended)(nil), "gobgpapi.RedirectIPv4AddressSpecificExtended") + proto.RegisterType((*RedirectFourOctetAsSpecificExtended)(nil), "gobgpapi.RedirectFourOctetAsSpecificExtended") + proto.RegisterType((*TrafficRemarkExtended)(nil), "gobgpapi.TrafficRemarkExtended") + proto.RegisterType((*UnknownExtended)(nil), "gobgpapi.UnknownExtended") + proto.RegisterType((*ExtendedCommunitiesAttribute)(nil), "gobgpapi.ExtendedCommunitiesAttribute") + proto.RegisterType((*As4PathAttribute)(nil), "gobgpapi.As4PathAttribute") + proto.RegisterType((*As4AggregatorAttribute)(nil), "gobgpapi.As4AggregatorAttribute") + proto.RegisterType((*PmsiTunnelAttribute)(nil), "gobgpapi.PmsiTunnelAttribute") + proto.RegisterType((*TunnelEncapSubTLVEncapsulation)(nil), "gobgpapi.TunnelEncapSubTLVEncapsulation") + proto.RegisterType((*TunnelEncapSubTLVProtocol)(nil), "gobgpapi.TunnelEncapSubTLVProtocol") + proto.RegisterType((*TunnelEncapSubTLVColor)(nil), "gobgpapi.TunnelEncapSubTLVColor") + proto.RegisterType((*TunnelEncapSubTLVUnknown)(nil), "gobgpapi.TunnelEncapSubTLVUnknown") + proto.RegisterType((*TunnelEncapTLV)(nil), "gobgpapi.TunnelEncapTLV") + proto.RegisterType((*TunnelEncapAttribute)(nil), "gobgpapi.TunnelEncapAttribute") + proto.RegisterType((*IPv6AddressSpecificExtended)(nil), "gobgpapi.IPv6AddressSpecificExtended") + proto.RegisterType((*RedirectIPv6AddressSpecificExtended)(nil), "gobgpapi.RedirectIPv6AddressSpecificExtended") + proto.RegisterType((*IP6ExtendedCommunitiesAttribute)(nil), "gobgpapi.IP6ExtendedCommunitiesAttribute") + proto.RegisterType((*AigpTLVIGPMetric)(nil), "gobgpapi.AigpTLVIGPMetric") + proto.RegisterType((*AigpTLVUnknown)(nil), "gobgpapi.AigpTLVUnknown") + proto.RegisterType((*AigpAttribute)(nil), "gobgpapi.AigpAttribute") + proto.RegisterType((*LargeCommunity)(nil), "gobgpapi.LargeCommunity") + proto.RegisterType((*LargeCommunitiesAttribute)(nil), "gobgpapi.LargeCommunitiesAttribute") + proto.RegisterType((*UnknownAttribute)(nil), "gobgpapi.UnknownAttribute") +} + +func init() { proto.RegisterFile("attribute.proto", fileDescriptor1) } + +var fileDescriptor1 = []byte{ + // 1881 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4b, 0x73, 0x1b, 0xc7, + 0x11, 0x2e, 0x2c, 0x40, 0x0a, 0x68, 0x10, 0x24, 0x04, 0x52, 0x2c, 0x90, 0xb4, 0x4c, 0x65, 0x14, + 0x27, 0x8c, 0x12, 0x53, 0x09, 0x2d, 0x2b, 0x91, 0x5c, 0xa9, 0x14, 0x4c, 0xd2, 0x36, 0x62, 0x82, + 0x82, 0x97, 0x14, 0x53, 0xb9, 0x04, 0x19, 0xec, 0x0e, 0x96, 0x53, 0xdc, 0x97, 0x77, 0x66, 0xf9, + 0xf8, 0x09, 0x3e, 0x24, 0xa5, 0x53, 0x2a, 0xbf, 0x24, 0xff, 0x2c, 0xf7, 0xd4, 0xbc, 0xf6, 0x41, + 0xf0, 0x21, 0x4a, 0x51, 0x95, 0x6e, 0xdb, 0xb3, 0x3d, 0xdd, 0x3d, 0xdf, 0x74, 0x7f, 0xdd, 0xbb, + 0xb0, 0x80, 0x39, 0x4f, 0xe8, 0x38, 0xe5, 0x64, 0x33, 0x4e, 0x22, 0x1e, 0x75, 0xea, 0x5e, 0x34, + 0xf6, 0x62, 0x1c, 0xd3, 0xd5, 0x15, 0x2f, 0x8a, 0x3c, 0x9f, 0x3c, 0x95, 0xeb, 0xe3, 0x74, 0xf2, + 0x14, 0x87, 0x17, 0x4a, 0x69, 0xb5, 0x29, 0x95, 0x94, 0x80, 0x7e, 0x05, 0x0b, 0xaf, 0x12, 0xea, + 0xd1, 0xb0, 0x67, 0x4c, 0x75, 0x96, 0x61, 0x36, 0x92, 0x4b, 0xdd, 0xca, 0xa3, 0xca, 0x46, 0xcb, + 0xd6, 0x12, 0x7a, 0x01, 0x8d, 0x1e, 0x3b, 0x20, 0x5e, 0x40, 0x42, 0xde, 0xe9, 0x40, 0x8d, 0x5f, + 0xc4, 0x44, 0xab, 0xc8, 0xe7, 0x4e, 0x17, 0xee, 0x85, 0x69, 0x30, 0x26, 0x09, 0xeb, 0x5a, 0x8f, + 0xaa, 0x1b, 0x2d, 0xdb, 0x88, 0xe8, 0x6b, 0x58, 0xe8, 0xb1, 0x21, 0xe6, 0xc7, 0xb9, 0x97, 0xa7, + 0x50, 0x67, 0xca, 0x16, 0xeb, 0x56, 0x1e, 0x55, 0x37, 0x9a, 0x5b, 0x8b, 0x9b, 0x26, 0xfa, 0xcd, + 0xcc, 0x8f, 0x9d, 0x29, 0xa1, 0xcf, 0xa1, 0xbd, 0x4f, 0xce, 0xf9, 0x77, 0x51, 0x9c, 0x1b, 0x59, + 0x81, 0x7a, 0x48, 0xce, 0xf9, 0xe8, 0x38, 0x8a, 0x65, 0x24, 0x0d, 0xfb, 0x5e, 0xa8, 0x74, 0xd0, + 0x13, 0x58, 0x1e, 0xa4, 0x3e, 0xa7, 0xbb, 0xe7, 0x94, 0xef, 0x50, 0xe6, 0xe4, 0x9b, 0xda, 0x50, + 0x0d, 0x88, 0xab, 0x23, 0x17, 0x8f, 0xe8, 0x0b, 0xe8, 0xec, 0x45, 0x0e, 0xf6, 0x87, 0x09, 0x99, + 0xe4, 0x7a, 0x0f, 0x01, 0x7c, 0xb1, 0x3a, 0x8a, 0x13, 0x32, 0xd1, 0xea, 0x0d, 0xdf, 0xe8, 0xa1, + 0x55, 0xe8, 0xf6, 0x78, 0x14, 0x50, 0xa7, 0xe7, 0x79, 0x09, 0xf1, 0x30, 0x27, 0xd9, 0x56, 0xf4, + 0x27, 0x58, 0x34, 0xab, 0x51, 0x92, 0x5b, 0x9c, 0x07, 0x0b, 0x0b, 0x6c, 0x84, 0x25, 0x0b, 0x33, + 0x01, 0x18, 0x76, 0xdd, 0x84, 0x30, 0xd6, 0xad, 0xaa, 0xe8, 0xb5, 0x88, 0xfe, 0x00, 0x4b, 0xdb, + 0x51, 0x10, 0xa4, 0x21, 0xe5, 0x94, 0xb0, 0xdc, 0xc2, 0x23, 0x68, 0x3a, 0xf9, 0xba, 0x04, 0xae, + 0x65, 0x17, 0x97, 0xd0, 0x2f, 0xe1, 0x81, 0xba, 0x50, 0xe1, 0xba, 0xef, 0x96, 0x9c, 0x53, 0x57, + 0xa3, 0x64, 0x51, 0x17, 0x6d, 0xc0, 0xd2, 0xb6, 0x9f, 0x32, 0x4e, 0x92, 0x3d, 0xca, 0x78, 0x09, + 0x1e, 0xea, 0x2a, 0xd3, 0x0d, 0x5b, 0x3c, 0xa2, 0xef, 0x60, 0xa1, 0x3f, 0xec, 0xa9, 0xc8, 0xc4, + 0xd1, 0xe9, 0xb9, 0xc0, 0x26, 0x96, 0x4f, 0x23, 0x9f, 0x98, 0x3c, 0x69, 0xa8, 0x95, 0x3d, 0x12, + 0x8a, 0x14, 0x52, 0x82, 0x3c, 0x6c, 0xc3, 0xd6, 0x12, 0xf2, 0x60, 0x79, 0x0f, 0x8f, 0x89, 0x4f, + 0xdc, 0xcb, 0x06, 0x97, 0x61, 0xd6, 0x17, 0x6f, 0xcc, 0x99, 0xb4, 0x74, 0xc9, 0x91, 0x75, 0xbd, + 0xa3, 0x6a, 0xc9, 0xd1, 0xe7, 0x70, 0x7f, 0x37, 0x74, 0x70, 0xcc, 0x52, 0x1f, 0x73, 0x1a, 0x85, + 0xfb, 0x7b, 0x76, 0xbf, 0x08, 0x77, 0xa5, 0x0c, 0xf7, 0x10, 0x3e, 0xb1, 0xa3, 0x94, 0x93, 0x1d, + 0xca, 0x38, 0x0d, 0xbd, 0x94, 0xb2, 0x63, 0x92, 0x1c, 0x9e, 0x45, 0xaf, 0x1c, 0x4e, 0x78, 0xef, + 0xa0, 0xb3, 0x04, 0x33, 0xd8, 0x0d, 0xb2, 0x8a, 0x50, 0x42, 0x67, 0x15, 0xea, 0x98, 0x31, 0xea, + 0x85, 0xc4, 0xd5, 0x91, 0x65, 0x32, 0x7a, 0x05, 0x6b, 0xd3, 0x16, 0xb3, 0x43, 0x97, 0x0d, 0x36, + 0xde, 0xc6, 0xe0, 0x0f, 0xf0, 0x70, 0xda, 0xe0, 0x37, 0x51, 0x9a, 0xbc, 0x7b, 0x8c, 0xbb, 0xb0, + 0xb2, 0xcb, 0x8f, 0x49, 0x12, 0x12, 0xae, 0xcb, 0xad, 0xef, 0x92, 0x90, 0xd3, 0x09, 0x25, 0xc9, + 0x95, 0x05, 0xbe, 0x04, 0x33, 0xa7, 0xd8, 0x4f, 0x89, 0xb4, 0x34, 0x67, 0x2b, 0x01, 0xfd, 0xa7, + 0x02, 0x9f, 0xee, 0x1e, 0x0d, 0xf7, 0x8d, 0xad, 0x5e, 0xca, 0x23, 0x51, 0x71, 0xd1, 0x29, 0x49, + 0x2e, 0x64, 0xcc, 0x9d, 0x9f, 0x83, 0x95, 0xa8, 0xdc, 0x6b, 0x6e, 0x2d, 0x6d, 0x2a, 0x6a, 0xda, + 0x34, 0xd4, 0xb4, 0xd9, 0x0b, 0x2f, 0x6c, 0x2b, 0x71, 0x3b, 0x5f, 0x42, 0x95, 0x30, 0x2a, 0x8d, + 0x37, 0xb7, 0x1e, 0xe7, 0x6c, 0x70, 0x6d, 0x90, 0xb6, 0xd0, 0xef, 0xfc, 0x0c, 0xe6, 0x88, 0xd6, + 0x18, 0x71, 0xec, 0xc9, 0x4c, 0x68, 0xd9, 0x4d, 0xb3, 0x76, 0x88, 0x3d, 0x11, 0xb8, 0xcc, 0xa7, + 0x6e, 0x4d, 0x61, 0x23, 0x05, 0xf4, 0xdf, 0x0a, 0xac, 0x89, 0xc0, 0x07, 0xbd, 0x6d, 0x71, 0x33, + 0xa7, 0x24, 0xe1, 0x94, 0x11, 0x49, 0x3b, 0x1f, 0x45, 0xd4, 0xeb, 0xd0, 0x0c, 0xb0, 0x33, 0x32, + 0x39, 0x5b, 0x93, 0xa9, 0x02, 0x01, 0x76, 0x4c, 0x16, 0x3d, 0x04, 0xa0, 0x71, 0xf6, 0x7e, 0x46, + 0xbe, 0x6f, 0xd0, 0xd8, 0xbc, 0xce, 0x6b, 0x6a, 0xb6, 0x58, 0x53, 0xe8, 0x4d, 0x05, 0x7e, 0x21, + 0xce, 0xdd, 0x0f, 0x1d, 0x3f, 0x65, 0xf4, 0x94, 0x48, 0xa2, 0x74, 0x30, 0xe3, 0xbb, 0xb9, 0xf7, + 0xbb, 0x40, 0x70, 0xf9, 0x2c, 0xd6, 0xf4, 0x59, 0xca, 0xa1, 0x56, 0x2f, 0x85, 0x8a, 0xfe, 0x55, + 0x81, 0x6e, 0x31, 0x87, 0x0c, 0xfd, 0x7f, 0xf8, 0x7b, 0xb8, 0x25, 0xb0, 0x9f, 0x2c, 0xb8, 0x2f, + 0xb1, 0x1a, 0x2a, 0xa2, 0xfa, 0x38, 0x32, 0x63, 0x0d, 0x1a, 0x34, 0x1e, 0x69, 0xe6, 0x53, 0x79, + 0x51, 0xa7, 0xb1, 0xa6, 0x52, 0x04, 0xad, 0xec, 0xa5, 0x64, 0xcd, 0x19, 0x65, 0xc0, 0x28, 0x08, + 0xde, 0x7c, 0x08, 0xe0, 0x9d, 0x65, 0xa7, 0x9e, 0x55, 0xa7, 0xf6, 0xce, 0x0a, 0xf4, 0xa4, 0xea, + 0xe5, 0x5e, 0xb1, 0x5e, 0xde, 0x54, 0x60, 0x45, 0xd3, 0xb7, 0x44, 0xe4, 0xed, 0x18, 0x5c, 0x61, + 0x65, 0xdd, 0x82, 0x55, 0x99, 0xe7, 0xab, 0xd7, 0xf3, 0x7c, 0xad, 0xc4, 0xf3, 0x3f, 0xc0, 0x8a, + 0xbc, 0x91, 0x43, 0x9c, 0x78, 0x84, 0x0f, 0x88, 0x1c, 0x37, 0x8e, 0x69, 0x2c, 0xf9, 0x5e, 0xb5, + 0xdb, 0x4a, 0xd6, 0x6e, 0x45, 0x24, 0xfc, 0x96, 0x48, 0x38, 0x4a, 0xa1, 0xfd, 0x8d, 0x1f, 0x9d, + 0x1d, 0xc4, 0xc4, 0x31, 0x97, 0x7e, 0x25, 0x19, 0xbe, 0x5b, 0x67, 0x92, 0xd3, 0xd5, 0x64, 0xc2, + 0x08, 0xd7, 0x5c, 0xa4, 0x25, 0xf4, 0x15, 0x34, 0x8d, 0xdb, 0x41, 0x6f, 0xfb, 0xba, 0xf9, 0xca, + 0xdc, 0x98, 0x55, 0xee, 0x5f, 0x7f, 0x84, 0x07, 0x66, 0xf3, 0x76, 0x14, 0xc4, 0x51, 0x28, 0xd2, + 0x8a, 0x93, 0x40, 0x40, 0xa0, 0x47, 0xa3, 0x96, 0x6d, 0x45, 0x71, 0x99, 0xc1, 0x6b, 0x86, 0xc1, + 0xff, 0x06, 0xf7, 0xa7, 0xb6, 0x5f, 0x19, 0xc1, 0x97, 0x30, 0x43, 0x39, 0x09, 0xd4, 0x7c, 0xd7, + 0xdc, 0x5a, 0xcf, 0x73, 0xfa, 0x4a, 0xf7, 0xb6, 0xd2, 0x46, 0x2f, 0x61, 0xce, 0xbc, 0x97, 0x17, + 0xf3, 0x04, 0x66, 0x92, 0xd4, 0x27, 0x66, 0xf0, 0xbb, 0xfa, 0x2e, 0x94, 0x0a, 0x72, 0x60, 0xe1, + 0x68, 0xb8, 0x5f, 0xda, 0xfe, 0x76, 0xd5, 0x97, 0x39, 0xb1, 0x6e, 0x77, 0xf2, 0x0c, 0xe0, 0x55, + 0x8c, 0x7f, 0x4c, 0x89, 0xb4, 0xdf, 0x86, 0xea, 0x09, 0xb9, 0x90, 0x0e, 0xe6, 0x6c, 0xf1, 0x78, + 0x4d, 0xe3, 0xfb, 0xa9, 0x02, 0x4b, 0x83, 0xd8, 0x26, 0xd8, 0x39, 0x16, 0xfb, 0xf2, 0x11, 0x6a, + 0x03, 0x66, 0x27, 0x38, 0xa0, 0xfe, 0x85, 0x0e, 0xb2, 0x5d, 0xc0, 0x49, 0xae, 0xdb, 0xfa, 0xbd, + 0x28, 0x64, 0x33, 0xc0, 0xaa, 0x40, 0x1b, 0x76, 0x5d, 0x4f, 0xb0, 0x4c, 0x9c, 0x20, 0xf4, 0x13, + 0x2a, 0x58, 0xe9, 0x86, 0x13, 0x48, 0x15, 0x14, 0xc2, 0xf2, 0x20, 0x7e, 0x1d, 0x26, 0xef, 0x13, + 0xcc, 0x5d, 0xfc, 0xfd, 0xa3, 0x02, 0xab, 0xd9, 0x80, 0xc4, 0xc4, 0xd5, 0xd0, 0x09, 0x75, 0x76, + 0xcf, 0x39, 0x09, 0x5d, 0xe2, 0x76, 0x1e, 0x43, 0x8b, 0xb2, 0x11, 0x4f, 0x70, 0xc8, 0x28, 0xa7, + 0xa7, 0x2a, 0x8b, 0xea, 0xf6, 0x1c, 0x65, 0x87, 0xd9, 0x9a, 0x98, 0xde, 0x59, 0x3a, 0x1e, 0xc9, + 0x2c, 0x53, 0xf5, 0x73, 0x8f, 0xa5, 0xe3, 0x43, 0x91, 0x68, 0xaa, 0x74, 0xab, 0x59, 0xe9, 0xae, + 0x43, 0x53, 0xcd, 0xe2, 0x6a, 0xc4, 0x51, 0xa5, 0xa3, 0xc6, 0xf3, 0x9e, 0x58, 0x41, 0xff, 0xae, + 0xc0, 0x5a, 0x7f, 0x78, 0xfa, 0x4c, 0x73, 0xd2, 0xff, 0x3d, 0xa0, 0x6b, 0x47, 0xf5, 0xdb, 0x43, + 0xfb, 0x67, 0x05, 0xd6, 0xf2, 0x41, 0xed, 0x23, 0xc0, 0xea, 0x09, 0x74, 0x8e, 0xb0, 0x4f, 0x5d, + 0x39, 0x19, 0x67, 0x61, 0x2c, 0xc1, 0x0c, 0xe3, 0x98, 0x9b, 0x82, 0x57, 0x02, 0xfa, 0x0c, 0x5a, + 0xdb, 0x91, 0x1f, 0x25, 0x45, 0x35, 0x47, 0x2c, 0x18, 0x35, 0x29, 0xa0, 0xdf, 0x42, 0x4b, 0xce, + 0xdb, 0x99, 0xda, 0x3a, 0x34, 0x79, 0x1a, 0x86, 0xc4, 0x1f, 0x15, 0x48, 0x04, 0xd4, 0x92, 0x88, + 0x1a, 0x75, 0x61, 0x79, 0x87, 0x4c, 0x70, 0xea, 0xf3, 0x6f, 0x31, 0x27, 0x67, 0xf8, 0xc2, 0x6c, + 0x45, 0xdf, 0xc3, 0xbc, 0x2a, 0xc6, 0xbb, 0x21, 0x94, 0xd5, 0x68, 0xb5, 0x58, 0xa3, 0x36, 0xb4, + 0x77, 0x0f, 0xfa, 0xb2, 0x6b, 0x65, 0xe6, 0x36, 0xa0, 0x4d, 0xd9, 0x88, 0xd1, 0xd0, 0xf3, 0xc9, + 0x08, 0x3b, 0x05, 0x8b, 0xf3, 0x94, 0x1d, 0xc8, 0xe5, 0x9e, 0x63, 0x6c, 0xaa, 0x3e, 0x68, 0x15, + 0xfb, 0xe0, 0x16, 0x2c, 0xee, 0x1e, 0xf4, 0x83, 0x38, 0x4a, 0x78, 0xa1, 0xf9, 0x88, 0x5a, 0x26, + 0x6c, 0x44, 0xe5, 0xba, 0x9e, 0xeb, 0xeb, 0x84, 0x29, 0x3d, 0xf4, 0x1a, 0x16, 0x07, 0xd8, 0x19, + 0x44, 0x63, 0xea, 0x53, 0x9e, 0x9d, 0x55, 0x36, 0x72, 0x36, 0x62, 0x9c, 0x3a, 0x27, 0x17, 0x3a, + 0x86, 0x3a, 0x65, 0x07, 0x52, 0x16, 0x83, 0x00, 0x23, 0x3f, 0xa6, 0x24, 0x74, 0xc8, 0x28, 0x4c, + 0x03, 0x33, 0x56, 0x99, 0xb5, 0xfd, 0x34, 0x40, 0x9f, 0xc1, 0x7d, 0x19, 0x42, 0x32, 0xc0, 0x79, + 0x42, 0x89, 0x0f, 0x5c, 0xec, 0xe8, 0x10, 0xc4, 0x23, 0x7a, 0x01, 0x8b, 0x87, 0x09, 0x9e, 0x4c, + 0xa8, 0x63, 0x63, 0x9e, 0xe3, 0x7a, 0xb9, 0x41, 0x76, 0xa0, 0x96, 0x88, 0x0c, 0x10, 0x8e, 0x2c, + 0x5b, 0x3e, 0xa3, 0xef, 0xe1, 0x81, 0xde, 0x2a, 0x30, 0x29, 0xe4, 0xcb, 0x2a, 0xd4, 0x39, 0x49, + 0x02, 0x1a, 0x62, 0xdf, 0x44, 0x6e, 0x64, 0xd1, 0xe4, 0x18, 0x0e, 0x62, 0x5f, 0x99, 0xaa, 0xdb, + 0x5a, 0x42, 0xaf, 0x01, 0xd9, 0xc4, 0xa5, 0x09, 0x71, 0xf8, 0x0d, 0xe4, 0x71, 0x39, 0xac, 0x4b, + 0x09, 0x6d, 0x4d, 0x25, 0xf4, 0xdf, 0xe1, 0xb1, 0x31, 0x7b, 0x13, 0x07, 0x5c, 0xfb, 0xfd, 0x77, + 0xbb, 0x87, 0xa3, 0xdc, 0xc3, 0x4d, 0xa5, 0x7c, 0xe7, 0xc8, 0x7f, 0x9d, 0xa1, 0x6b, 0x93, 0x00, + 0x27, 0x27, 0x99, 0xa5, 0x0e, 0xd4, 0x5c, 0xe6, 0x98, 0xd6, 0x2d, 0x9f, 0xd1, 0x57, 0xb0, 0xf0, + 0x3a, 0x3c, 0x09, 0xa3, 0xb3, 0xb0, 0xa8, 0xf6, 0x96, 0x5f, 0x69, 0x47, 0xf0, 0x89, 0xd9, 0x75, + 0xe5, 0x9f, 0x85, 0xe7, 0xd3, 0x7f, 0x16, 0xae, 0x6b, 0x01, 0xa5, 0xff, 0x0d, 0xdb, 0xd0, 0xee, + 0xb1, 0x67, 0xef, 0xf9, 0x6f, 0xe7, 0x6b, 0x58, 0xee, 0xb1, 0x67, 0xef, 0xf7, 0xcb, 0x84, 0xc0, + 0xe2, 0x30, 0x60, 0xf4, 0x50, 0x52, 0x4c, 0x6e, 0x60, 0x09, 0x66, 0x26, 0x3e, 0xf6, 0xcc, 0xad, + 0x28, 0x21, 0xc3, 0xcd, 0x2a, 0xe3, 0xa6, 0x8a, 0xbd, 0x5a, 0x28, 0x76, 0xfd, 0xdb, 0xa4, 0x26, + 0xa1, 0xb4, 0xa8, 0x8b, 0xfe, 0x0c, 0x9f, 0x2a, 0x17, 0x92, 0xef, 0x0e, 0xd2, 0xf1, 0xe1, 0xde, + 0x51, 0xe9, 0x57, 0x43, 0x71, 0x7c, 0x68, 0xa9, 0xf1, 0x61, 0x19, 0x66, 0x9d, 0x28, 0x3a, 0xa1, + 0xe6, 0x4a, 0xb4, 0x84, 0x7e, 0x0f, 0x2b, 0x53, 0xb6, 0x86, 0x02, 0x69, 0x27, 0xf2, 0x45, 0x7d, + 0xc5, 0xfa, 0x59, 0xdb, 0xca, 0x64, 0xb4, 0x09, 0xcb, 0x53, 0x1b, 0x25, 0x4d, 0x5f, 0x43, 0xcf, + 0x3b, 0xd0, 0x9d, 0xd2, 0xd7, 0xa9, 0x74, 0x87, 0x14, 0xda, 0x87, 0xf9, 0x82, 0x95, 0xc3, 0xbd, + 0xa3, 0x2b, 0xf7, 0x6e, 0x40, 0x8d, 0xfb, 0xa7, 0x37, 0x8f, 0x5d, 0x52, 0x03, 0xed, 0xc0, 0x52, + 0xc1, 0x5e, 0x7e, 0x65, 0xbf, 0xd1, 0x16, 0x54, 0xea, 0x74, 0xf3, 0xd4, 0x29, 0x7b, 0xd7, 0x56, + 0x74, 0xe7, 0x7f, 0xfe, 0x11, 0x76, 0xfe, 0x32, 0x2f, 0x3d, 0xff, 0x00, 0xbc, 0xf4, 0x57, 0x58, + 0xef, 0x0f, 0x9f, 0x7f, 0x90, 0xc2, 0x7e, 0x02, 0xed, 0x1e, 0xf5, 0x04, 0xd0, 0xfd, 0x6f, 0x87, + 0x03, 0xc2, 0x13, 0xea, 0x88, 0x44, 0x0e, 0xe4, 0x93, 0x0c, 0xb4, 0x66, 0x6b, 0x09, 0xbd, 0x84, + 0x79, 0xad, 0x7b, 0xf7, 0xac, 0x7a, 0x01, 0x2d, 0xb1, 0xb7, 0x38, 0xb0, 0x16, 0xaf, 0xff, 0xa6, + 0x04, 0x4a, 0x61, 0x7e, 0x4f, 0xf4, 0x5e, 0x73, 0x6e, 0xd9, 0x32, 0x3d, 0x3f, 0x1a, 0x67, 0x88, + 0x29, 0xf7, 0x4d, 0xb5, 0x26, 0x21, 0xcb, 0x31, 0x75, 0x31, 0xc7, 0xbf, 0x2b, 0x61, 0xba, 0x23, + 0x56, 0xca, 0x0a, 0x5b, 0x9a, 0x0d, 0x72, 0x85, 0x2d, 0xf4, 0x17, 0xf1, 0x19, 0x5c, 0x70, 0x5b, + 0x82, 0xfb, 0xe5, 0x55, 0x70, 0x17, 0x72, 0xb8, 0x1c, 0x70, 0x19, 0x72, 0x1b, 0xda, 0x1a, 0xbf, + 0x77, 0xe4, 0xaf, 0xe9, 0x01, 0x68, 0x3c, 0x2b, 0x71, 0xfb, 0xe2, 0x7f, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xc5, 0x72, 0x43, 0xec, 0x2c, 0x18, 0x00, 0x00, +} diff --git a/vendor/github.com/osrg/gobgp/api/attribute.proto b/vendor/github.com/osrg/gobgp/api/attribute.proto new file mode 100644 index 0000000..c07ad05 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/attribute.proto @@ -0,0 +1,505 @@ +// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "gobgp.proto"; + +package gobgpapi; + +message OriginAttribute { + uint32 origin = 1; +} + +message AsSegment { + uint32 type = 1; + repeated uint32 numbers = 2; +} + +message AsPathAttribute { + repeated AsSegment segments = 1; +} + +message NextHopAttribute { + string next_hop = 1; +} + +message MultiExitDiscAttribute { + uint32 med = 1; +} + +message LocalPrefAttribute { + uint32 local_pref = 1; +} + +message AtomicAggregateAttribute { +} + +message AggregatorAttribute { + uint32 as = 2; + string address = 3; +} + +message CommunitiesAttribute { + repeated uint32 communities = 1; +} + +message OriginatorIdAttribute { + string id = 1; +} + +message ClusterListAttribute { + repeated string ids = 1; +} + +// IPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=1 +// - AFI=2, SAFI=1 +message IPAddressPrefix { + uint32 prefix_len = 1; + string prefix = 2; +} + +// LabeledIPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=4 +// - AFI=2, SAFI=4 +message LabeledIPAddressPrefix { + repeated uint32 labels = 1; + uint32 prefix_len = 2; + string prefix = 3; +} + +// EncapsulationNLRI represents the NLRI for: +// - AFI=1, SAFI=7 +// - AFI=2, SAFI=7 +message EncapsulationNLRI { + string address = 1; +} + +message RouteDistinguisherTwoOctetAS { + uint32 admin = 1; + uint32 assigned = 2; +} + +message RouteDistinguisherIPAddress { + string admin = 1; + uint32 assigned = 2; +} + +message RouteDistinguisherFourOctetAS { + uint32 admin = 1; + uint32 assigned = 2; +} + +message EthernetSegmentIdentifier { + uint32 type = 1; + bytes value = 2; +} + +// EVPNEthernetAutoDiscoveryRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=1 +message EVPNEthernetAutoDiscoveryRoute { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + EthernetSegmentIdentifier esi = 2; + uint32 ethernet_tag = 3; + uint32 label = 4; +} + +// EVPNMACIPAdvertisementRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=2 +message EVPNMACIPAdvertisementRoute { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + EthernetSegmentIdentifier esi = 2; + uint32 ethernet_tag = 3; + string mac_address = 4; + string ip_address = 5; + repeated uint32 labels = 6; +} + +// EVPNInclusiveMulticastEthernetTagRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=3 +message EVPNInclusiveMulticastEthernetTagRoute { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + uint32 ethernet_tag = 2; + string ip_address = 3; +} + +// EVPNEthernetSegmentRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=4 +message EVPNEthernetSegmentRoute { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + EthernetSegmentIdentifier esi = 2; + string ip_address = 3; +} + +// EVPNIPPrefixRoute represents the NLRI for: +// - AFI=25, SAFI=70, RouteType=5 +message EVPNIPPrefixRoute { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + EthernetSegmentIdentifier esi = 2; + uint32 ethernet_tag = 3; + string ip_prefix = 4; + uint32 ip_prefix_len = 5; + string gw_address = 6; + uint32 label = 7; +} + +// LabeledVPNIPAddressPrefix represents the NLRI for: +// - AFI=1, SAFI=128 +// - AFI=2, SAFI=128 +message LabeledVPNIPAddressPrefix { + repeated uint32 labels = 1; + // One of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + google.protobuf.Any rd = 2; + uint32 prefix_len = 3; + string prefix = 4; +} + +// RouteTargetMembershipNLRI represents the NLRI for: +// - AFI=1, SAFI=132 +message RouteTargetMembershipNLRI { + uint32 as = 1; + // One of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + google.protobuf.Any rt = 2; +} + +message FlowSpecIPPrefix { + uint32 type = 1; + uint32 prefix_len = 2; + string prefix = 3; + // IPv6 only + uint32 offset = 4; +} + +message FlowSpecMAC { + uint32 type = 1; + string address = 2; +} + +message FlowSpecComponentItem { + // Operator for Numeric type, Operand for Bitmask type + uint32 op = 1; + uint64 value = 2; +} + +message FlowSpecComponent { + uint32 type = 1; + repeated FlowSpecComponentItem items = 2; +} + +// FlowSpecNLRI represents the NLRI for: +// - AFI=1, SAFI=133 +// - AFI=2, SAFI=133 +message FlowSpecNLRI { + // One of: + // - FlowSpecIPPrefix + // - FlowSpecMAC + // - FlowSpecComponent + repeated google.protobuf.Any rules = 1; +} + +// VPNFlowSpecNLRI represents the NLRI for: +// - AFI=1, SAFI=134 +// - AFI=2, SAFI=134 +// - AFI=25, SAFI=134 +message VPNFlowSpecNLRI { + // One of: + // - RouteDistinguisherTwoOctetAS + // - RouteDistinguisherIPAddressAS + // - RouteDistinguisherFourOctetAS + google.protobuf.Any rd = 1; + // One of: + // - FlowSpecIPPrefix + // - FlowSpecMAC + // - FlowSpecComponent + repeated google.protobuf.Any rules = 2; +} + +// OpaqueNLRI represents the NLRI for: +// - AFI=16397, SAFI=241 +message OpaqueNLRI { + bytes key = 1; + bytes value = 2; +} + +message MpReachNLRIAttribute { + gobgpapi.Family family = 1; + repeated string next_hops = 2; + // Each NLRI must be one of: + // - IPAddressPrefix + // - LabeledIPAddressPrefix + // - EncapsulationNLRI + // - EVPNEthernetAutoDiscoveryRoute + // - EVPNMACIPAdvertisementRoute + // - EVPNInclusiveMulticastEthernetTagRoute + // - EVPNEthernetSegmentRoute + // - EVPNIPPrefixRoute + // - LabeledVPNIPAddressPrefix + // - RouteTargetMembershipNLRI + // - FlowSpecNLRI + // - VPNFlowSpecNLRI + // - OpaqueNLRI + repeated google.protobuf.Any nlris = 3; +} + +message MpUnreachNLRIAttribute { + gobgpapi.Family family = 1; + // The same as NLRI field of MpReachNLRIAttribute + repeated google.protobuf.Any nlris = 3; +} + +message TwoOctetAsSpecificExtended { + bool is_transitive = 1; + uint32 sub_type = 2; + uint32 as = 3; + uint32 local_admin = 4; +} + +message IPv4AddressSpecificExtended { + bool is_transitive = 1; + uint32 sub_type = 2; + string address = 3; + uint32 local_admin = 4; +} + +message FourOctetAsSpecificExtended { + bool is_transitive = 1; + uint32 sub_type = 2; + uint32 as = 3; + uint32 local_admin = 4; +} + +message ValidationExtended { + uint32 state = 1; +} + +message ColorExtended { + uint32 color = 1; +} + +message EncapExtended { + uint32 tunnel_type = 1; +} + +message DefaultGatewayExtended { +} + +message OpaqueExtended { + bool is_transitive = 1; + bytes value = 3; +} + +message ESILabelExtended { + bool is_single_active = 1; + uint32 label = 2; +} + +message ESImportRouteTarget { + string es_import = 1; +} + +message MacMobilityExtended { + bool is_sticky = 1; + uint32 sequence_num = 2; +} + +message RouterMacExtended { + string mac = 1; +} + +message TrafficRateExtended { + uint32 as = 1; + float rate = 2; +} + +message TrafficActionExtended { + bool terminal = 1; + bool sample = 2; +} + +message RedirectTwoOctetAsSpecificExtended { + uint32 as = 1; + uint32 local_admin = 2; +} + +message RedirectIPv4AddressSpecificExtended { + string address = 1; + uint32 local_admin = 2; +} + +message RedirectFourOctetAsSpecificExtended { + uint32 as = 1; + uint32 local_admin = 2; +} + +message TrafficRemarkExtended { + uint32 dscp = 1; +} + +message UnknownExtended { + uint32 type = 1; + bytes value = 2; +} + +message ExtendedCommunitiesAttribute { + // Each Community must be one of: + // - TwoOctetAsSpecificExtended + // - IPv4AddressSpecificExtended + // - FourOctetAsSpecificExtended + // - OpaqueExtended + // - ESILabelExtended + // - MacMobilityExtended + // - RouterMacExtended + // - TrafficRateExtended + // - TrafficActionExtended + // - RedirectTwoOctetAsSpecificExtended + // - RedirectIPv4AddressSpecificExtended + // - RedirectFourOctetAsSpecificExtended + // - TrafficRemarkExtended + // - UnknownExtended + repeated google.protobuf.Any communities = 1; +} + +message As4PathAttribute { + repeated AsSegment segments = 1; +} + +message As4AggregatorAttribute { + uint32 as = 2; + string address = 3; +} + +message PmsiTunnelAttribute { + uint32 flags = 1; + uint32 type = 2; + uint32 label = 3; + bytes id = 4; +} + +message TunnelEncapSubTLVEncapsulation { + uint32 key = 1; + bytes cookie = 2; +} + +message TunnelEncapSubTLVProtocol { + uint32 protocol = 1; +} + +message TunnelEncapSubTLVColor { + uint32 color = 1; +} + +message TunnelEncapSubTLVUnknown { + uint32 type = 1; + bytes value = 2; +} + +message TunnelEncapTLV { + uint32 type = 1; + // Each TLV must be one of: + // - TunnelEncapSubTLVEncapsulation + // - TunnelEncapSubTLVProtocol + // - TunnelEncapSubTLVColor + // - TunnelEncapSubTLVUnknown + repeated google.protobuf.Any tlvs = 2; +} + +message TunnelEncapAttribute { + repeated TunnelEncapTLV tlvs = 1; +} + +message IPv6AddressSpecificExtended { + bool is_transitive = 1; + uint32 sub_type = 2; + string address = 3; + uint32 local_admin = 4; +} + +message RedirectIPv6AddressSpecificExtended { + string address = 1; + uint32 local_admin = 2; +} + +message IP6ExtendedCommunitiesAttribute { + // Each Community must be one of: + // - IPv6AddressSpecificExtended + // - RedirectIPv6AddressSpecificExtended + repeated google.protobuf.Any communities = 1; +} + +message AigpTLVIGPMetric { + uint64 metric = 1; +} + +message AigpTLVUnknown { + uint32 type = 1; + bytes value = 2; +} + +message AigpAttribute { + // Each TLV must be one of: + // - AigpTLVIGPMetric + // - AigpTLVUnknown + repeated google.protobuf.Any tlvs = 1; +} + +message LargeCommunity { + uint32 global_admin = 1; + uint32 local_data1 = 2; + uint32 local_data2 = 3; +} + +message LargeCommunitiesAttribute { + repeated LargeCommunity communities = 1; +} + +message UnknownAttribute { + uint32 flags = 1; + uint32 type = 2; + bytes value = 3; +} diff --git a/vendor/github.com/osrg/gobgp/api/capability.pb.go b/vendor/github.com/osrg/gobgp/api/capability.pb.go new file mode 100644 index 0000000..ddacc16 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/capability.pb.go @@ -0,0 +1,380 @@ +// Code generated by protoc-gen-go. +// source: capability.proto +// DO NOT EDIT! + +package gobgpapi + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type AddPathMode int32 + +const ( + AddPathMode_MODE_NONE AddPathMode = 0 + AddPathMode_MODE_RECEIVE AddPathMode = 1 + AddPathMode_MODE_SEND AddPathMode = 2 + AddPathMode_MODE_BOTH AddPathMode = 3 +) + +var AddPathMode_name = map[int32]string{ + 0: "MODE_NONE", + 1: "MODE_RECEIVE", + 2: "MODE_SEND", + 3: "MODE_BOTH", +} +var AddPathMode_value = map[string]int32{ + "MODE_NONE": 0, + "MODE_RECEIVE": 1, + "MODE_SEND": 2, + "MODE_BOTH": 3, +} + +func (x AddPathMode) String() string { + return proto.EnumName(AddPathMode_name, int32(x)) +} +func (AddPathMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } + +type MultiProtocolCapability struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` +} + +func (m *MultiProtocolCapability) Reset() { *m = MultiProtocolCapability{} } +func (m *MultiProtocolCapability) String() string { return proto.CompactTextString(m) } +func (*MultiProtocolCapability) ProtoMessage() {} +func (*MultiProtocolCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } + +func (m *MultiProtocolCapability) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +type RouteRefreshCapability struct { +} + +func (m *RouteRefreshCapability) Reset() { *m = RouteRefreshCapability{} } +func (m *RouteRefreshCapability) String() string { return proto.CompactTextString(m) } +func (*RouteRefreshCapability) ProtoMessage() {} +func (*RouteRefreshCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{1} } + +type CarryingLabelInfoCapability struct { +} + +func (m *CarryingLabelInfoCapability) Reset() { *m = CarryingLabelInfoCapability{} } +func (m *CarryingLabelInfoCapability) String() string { return proto.CompactTextString(m) } +func (*CarryingLabelInfoCapability) ProtoMessage() {} +func (*CarryingLabelInfoCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{2} } + +type ExtendedNexthopCapabilityTuple struct { + NlriFamily *Family `protobuf:"bytes,1,opt,name=nlri_family,json=nlriFamily" json:"nlri_family,omitempty"` + // Nexthop AFI must be either + // gobgp.IPv4 or + // gobgp.IPv6. + NexthopFamily *Family `protobuf:"bytes,2,opt,name=nexthop_family,json=nexthopFamily" json:"nexthop_family,omitempty"` +} + +func (m *ExtendedNexthopCapabilityTuple) Reset() { *m = ExtendedNexthopCapabilityTuple{} } +func (m *ExtendedNexthopCapabilityTuple) String() string { return proto.CompactTextString(m) } +func (*ExtendedNexthopCapabilityTuple) ProtoMessage() {} +func (*ExtendedNexthopCapabilityTuple) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} } + +func (m *ExtendedNexthopCapabilityTuple) GetNlriFamily() *Family { + if m != nil { + return m.NlriFamily + } + return nil +} + +func (m *ExtendedNexthopCapabilityTuple) GetNexthopFamily() *Family { + if m != nil { + return m.NexthopFamily + } + return nil +} + +type ExtendedNexthopCapability struct { + Tuples []*ExtendedNexthopCapabilityTuple `protobuf:"bytes,1,rep,name=tuples" json:"tuples,omitempty"` +} + +func (m *ExtendedNexthopCapability) Reset() { *m = ExtendedNexthopCapability{} } +func (m *ExtendedNexthopCapability) String() string { return proto.CompactTextString(m) } +func (*ExtendedNexthopCapability) ProtoMessage() {} +func (*ExtendedNexthopCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} } + +func (m *ExtendedNexthopCapability) GetTuples() []*ExtendedNexthopCapabilityTuple { + if m != nil { + return m.Tuples + } + return nil +} + +type GracefulRestartCapabilityTuple struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags" json:"flags,omitempty"` +} + +func (m *GracefulRestartCapabilityTuple) Reset() { *m = GracefulRestartCapabilityTuple{} } +func (m *GracefulRestartCapabilityTuple) String() string { return proto.CompactTextString(m) } +func (*GracefulRestartCapabilityTuple) ProtoMessage() {} +func (*GracefulRestartCapabilityTuple) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} } + +func (m *GracefulRestartCapabilityTuple) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *GracefulRestartCapabilityTuple) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +type GracefulRestartCapability struct { + Flags uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"` + Time uint32 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"` + Tuples []*GracefulRestartCapabilityTuple `protobuf:"bytes,3,rep,name=tuples" json:"tuples,omitempty"` +} + +func (m *GracefulRestartCapability) Reset() { *m = GracefulRestartCapability{} } +func (m *GracefulRestartCapability) String() string { return proto.CompactTextString(m) } +func (*GracefulRestartCapability) ProtoMessage() {} +func (*GracefulRestartCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} } + +func (m *GracefulRestartCapability) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +func (m *GracefulRestartCapability) GetTime() uint32 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *GracefulRestartCapability) GetTuples() []*GracefulRestartCapabilityTuple { + if m != nil { + return m.Tuples + } + return nil +} + +type FourOctetASNumberCapability struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` +} + +func (m *FourOctetASNumberCapability) Reset() { *m = FourOctetASNumberCapability{} } +func (m *FourOctetASNumberCapability) String() string { return proto.CompactTextString(m) } +func (*FourOctetASNumberCapability) ProtoMessage() {} +func (*FourOctetASNumberCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{7} } + +func (m *FourOctetASNumberCapability) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +type AddPathCapabilityTuple struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + Mode AddPathMode `protobuf:"varint,2,opt,name=mode,enum=gobgpapi.AddPathMode" json:"mode,omitempty"` +} + +func (m *AddPathCapabilityTuple) Reset() { *m = AddPathCapabilityTuple{} } +func (m *AddPathCapabilityTuple) String() string { return proto.CompactTextString(m) } +func (*AddPathCapabilityTuple) ProtoMessage() {} +func (*AddPathCapabilityTuple) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{8} } + +func (m *AddPathCapabilityTuple) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *AddPathCapabilityTuple) GetMode() AddPathMode { + if m != nil { + return m.Mode + } + return AddPathMode_MODE_NONE +} + +type AddPathCapability struct { + Tuples []*AddPathCapabilityTuple `protobuf:"bytes,1,rep,name=tuples" json:"tuples,omitempty"` +} + +func (m *AddPathCapability) Reset() { *m = AddPathCapability{} } +func (m *AddPathCapability) String() string { return proto.CompactTextString(m) } +func (*AddPathCapability) ProtoMessage() {} +func (*AddPathCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{9} } + +func (m *AddPathCapability) GetTuples() []*AddPathCapabilityTuple { + if m != nil { + return m.Tuples + } + return nil +} + +type EnhancedRouteRefreshCapability struct { +} + +func (m *EnhancedRouteRefreshCapability) Reset() { *m = EnhancedRouteRefreshCapability{} } +func (m *EnhancedRouteRefreshCapability) String() string { return proto.CompactTextString(m) } +func (*EnhancedRouteRefreshCapability) ProtoMessage() {} +func (*EnhancedRouteRefreshCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{10} } + +type LongLivedGracefulRestartCapabilityTuple struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags" json:"flags,omitempty"` + Time uint32 `protobuf:"varint,3,opt,name=time" json:"time,omitempty"` +} + +func (m *LongLivedGracefulRestartCapabilityTuple) Reset() { + *m = LongLivedGracefulRestartCapabilityTuple{} +} +func (m *LongLivedGracefulRestartCapabilityTuple) String() string { return proto.CompactTextString(m) } +func (*LongLivedGracefulRestartCapabilityTuple) ProtoMessage() {} +func (*LongLivedGracefulRestartCapabilityTuple) Descriptor() ([]byte, []int) { + return fileDescriptor2, []int{11} +} + +func (m *LongLivedGracefulRestartCapabilityTuple) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *LongLivedGracefulRestartCapabilityTuple) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +func (m *LongLivedGracefulRestartCapabilityTuple) GetTime() uint32 { + if m != nil { + return m.Time + } + return 0 +} + +type LongLivedGracefulRestartCapability struct { + Tuples []*LongLivedGracefulRestartCapabilityTuple `protobuf:"bytes,1,rep,name=tuples" json:"tuples,omitempty"` +} + +func (m *LongLivedGracefulRestartCapability) Reset() { *m = LongLivedGracefulRestartCapability{} } +func (m *LongLivedGracefulRestartCapability) String() string { return proto.CompactTextString(m) } +func (*LongLivedGracefulRestartCapability) ProtoMessage() {} +func (*LongLivedGracefulRestartCapability) Descriptor() ([]byte, []int) { + return fileDescriptor2, []int{12} +} + +func (m *LongLivedGracefulRestartCapability) GetTuples() []*LongLivedGracefulRestartCapabilityTuple { + if m != nil { + return m.Tuples + } + return nil +} + +type RouteRefreshCiscoCapability struct { +} + +func (m *RouteRefreshCiscoCapability) Reset() { *m = RouteRefreshCiscoCapability{} } +func (m *RouteRefreshCiscoCapability) String() string { return proto.CompactTextString(m) } +func (*RouteRefreshCiscoCapability) ProtoMessage() {} +func (*RouteRefreshCiscoCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{13} } + +type UnknownCapability struct { + Code uint32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *UnknownCapability) Reset() { *m = UnknownCapability{} } +func (m *UnknownCapability) String() string { return proto.CompactTextString(m) } +func (*UnknownCapability) ProtoMessage() {} +func (*UnknownCapability) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{14} } + +func (m *UnknownCapability) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *UnknownCapability) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*MultiProtocolCapability)(nil), "gobgpapi.MultiProtocolCapability") + proto.RegisterType((*RouteRefreshCapability)(nil), "gobgpapi.RouteRefreshCapability") + proto.RegisterType((*CarryingLabelInfoCapability)(nil), "gobgpapi.CarryingLabelInfoCapability") + proto.RegisterType((*ExtendedNexthopCapabilityTuple)(nil), "gobgpapi.ExtendedNexthopCapabilityTuple") + proto.RegisterType((*ExtendedNexthopCapability)(nil), "gobgpapi.ExtendedNexthopCapability") + proto.RegisterType((*GracefulRestartCapabilityTuple)(nil), "gobgpapi.GracefulRestartCapabilityTuple") + proto.RegisterType((*GracefulRestartCapability)(nil), "gobgpapi.GracefulRestartCapability") + proto.RegisterType((*FourOctetASNumberCapability)(nil), "gobgpapi.FourOctetASNumberCapability") + proto.RegisterType((*AddPathCapabilityTuple)(nil), "gobgpapi.AddPathCapabilityTuple") + proto.RegisterType((*AddPathCapability)(nil), "gobgpapi.AddPathCapability") + proto.RegisterType((*EnhancedRouteRefreshCapability)(nil), "gobgpapi.EnhancedRouteRefreshCapability") + proto.RegisterType((*LongLivedGracefulRestartCapabilityTuple)(nil), "gobgpapi.LongLivedGracefulRestartCapabilityTuple") + proto.RegisterType((*LongLivedGracefulRestartCapability)(nil), "gobgpapi.LongLivedGracefulRestartCapability") + proto.RegisterType((*RouteRefreshCiscoCapability)(nil), "gobgpapi.RouteRefreshCiscoCapability") + proto.RegisterType((*UnknownCapability)(nil), "gobgpapi.UnknownCapability") + proto.RegisterEnum("gobgpapi.AddPathMode", AddPathMode_name, AddPathMode_value) +} + +func init() { proto.RegisterFile("capability.proto", fileDescriptor2) } + +var fileDescriptor2 = []byte{ + // 520 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0x4d, 0x6f, 0xd3, 0x40, + 0x10, 0xc5, 0x49, 0x88, 0x60, 0xd2, 0x44, 0xee, 0x0a, 0x4a, 0x4a, 0xd4, 0x28, 0xda, 0x0b, 0x01, + 0x89, 0x48, 0x0d, 0x07, 0xb8, 0x20, 0x51, 0x52, 0x17, 0x22, 0xe5, 0xa3, 0x72, 0x0b, 0x37, 0x54, + 0x36, 0xf6, 0xc6, 0x59, 0xb1, 0xde, 0xb5, 0xec, 0x75, 0x69, 0x0e, 0x9c, 0xb9, 0xf0, 0xa3, 0x91, + 0x3f, 0x62, 0x9b, 0x54, 0x6e, 0x2b, 0xa4, 0xde, 0x66, 0xbc, 0x33, 0x6f, 0xde, 0x9b, 0xb7, 0x6b, + 0xd0, 0x2d, 0xe2, 0x91, 0x05, 0xe3, 0x4c, 0xad, 0x07, 0x9e, 0x2f, 0x95, 0x44, 0x8f, 0x1c, 0xb9, + 0x70, 0x3c, 0xe2, 0xb1, 0xe7, 0x8d, 0x38, 0x4a, 0x3e, 0xe3, 0x11, 0x3c, 0x9b, 0x86, 0x5c, 0xb1, + 0xd3, 0x28, 0xb3, 0x24, 0x1f, 0x65, 0x7d, 0xa8, 0x0f, 0xf5, 0x25, 0x71, 0x19, 0x5f, 0xb7, 0xb5, + 0x9e, 0xd6, 0x6f, 0x0c, 0xf5, 0xc1, 0x06, 0x62, 0x70, 0x12, 0x7f, 0x37, 0xd3, 0x73, 0xdc, 0x86, + 0x3d, 0x53, 0x86, 0x8a, 0x9a, 0x74, 0xe9, 0xd3, 0x60, 0x95, 0x63, 0xe0, 0x03, 0xe8, 0x8c, 0x88, + 0xef, 0xaf, 0x99, 0x70, 0x26, 0x64, 0x41, 0xf9, 0x58, 0x2c, 0x65, 0xe1, 0xf8, 0x8f, 0x06, 0x5d, + 0xe3, 0x4a, 0x51, 0x61, 0x53, 0x7b, 0x46, 0xaf, 0xd4, 0x4a, 0x7a, 0xf9, 0xe9, 0x79, 0xe8, 0x71, + 0x8a, 0x0e, 0xa1, 0x21, 0xb8, 0xcf, 0x2e, 0x6e, 0xa1, 0x02, 0x51, 0x51, 0x12, 0xa3, 0xb7, 0xd0, + 0x12, 0x09, 0xd8, 0xa6, 0xab, 0x52, 0xd2, 0xd5, 0x4c, 0xeb, 0x92, 0x14, 0x7f, 0x83, 0xfd, 0x52, + 0x36, 0xe8, 0x03, 0xd4, 0x55, 0xc4, 0x28, 0x68, 0x6b, 0xbd, 0x6a, 0xbf, 0x31, 0xec, 0xe7, 0x68, + 0x37, 0x4b, 0x30, 0xd3, 0x3e, 0xfc, 0x1d, 0xba, 0x9f, 0x7c, 0x62, 0xd1, 0x65, 0xc8, 0x4d, 0x1a, + 0x28, 0xe2, 0xab, 0x6d, 0xb1, 0x77, 0x5e, 0x39, 0x7a, 0x02, 0x0f, 0x97, 0x9c, 0x38, 0x41, 0x2c, + 0xad, 0x69, 0x26, 0x09, 0xfe, 0xad, 0xc1, 0x7e, 0xe9, 0x88, 0xbc, 0x47, 0x2b, 0xf4, 0x20, 0x04, + 0x35, 0xc5, 0x5c, 0x9a, 0x02, 0xc5, 0x71, 0x41, 0x6b, 0x75, 0x5b, 0xeb, 0xcd, 0x0a, 0x32, 0xad, + 0xaf, 0xa1, 0x73, 0x22, 0x43, 0x7f, 0x6e, 0x29, 0xaa, 0x8e, 0xce, 0x66, 0xa1, 0xbb, 0xa0, 0x7e, + 0x81, 0x4a, 0x0b, 0x2a, 0x64, 0xc3, 0xa3, 0x42, 0x02, 0xec, 0xc2, 0xde, 0x91, 0x6d, 0x9f, 0x12, + 0xb5, 0xfa, 0xff, 0x95, 0xbc, 0x84, 0x9a, 0x2b, 0xed, 0x44, 0x48, 0x6b, 0xf8, 0x34, 0xaf, 0x4b, + 0x91, 0xa7, 0xd2, 0xa6, 0x66, 0x5c, 0x82, 0xa7, 0xb0, 0x7b, 0x6d, 0x1c, 0x7a, 0xb7, 0x65, 0x70, + 0xef, 0x1a, 0x42, 0x99, 0xd8, 0x1e, 0x74, 0x0d, 0xb1, 0x22, 0xc2, 0xa2, 0x76, 0xc9, 0x3b, 0xf8, + 0x05, 0x2f, 0x26, 0x52, 0x38, 0x13, 0x76, 0x49, 0xed, 0xfb, 0xbd, 0x03, 0x99, 0x9f, 0xd5, 0xdc, + 0x4f, 0x2c, 0x01, 0xdf, 0x3e, 0x1e, 0x8d, 0xb7, 0x16, 0x70, 0x98, 0x4f, 0xbe, 0x23, 0xf9, 0x6c, + 0x23, 0x07, 0xd0, 0xf9, 0x67, 0x13, 0x2c, 0xb0, 0x8a, 0xef, 0xfe, 0x3d, 0xec, 0x7e, 0x11, 0x3f, + 0x84, 0xfc, 0x29, 0x0a, 0xe3, 0x11, 0xd4, 0xac, 0xc8, 0xbf, 0xe4, 0x56, 0xc4, 0x71, 0x24, 0xf1, + 0x92, 0xf0, 0x30, 0x31, 0x75, 0xc7, 0x4c, 0x92, 0x57, 0x13, 0x68, 0x14, 0x3c, 0x45, 0x4d, 0x78, + 0x3c, 0x9d, 0x1f, 0x1b, 0x17, 0xb3, 0xf9, 0xcc, 0xd0, 0x1f, 0x20, 0x1d, 0x76, 0xe2, 0xd4, 0x34, + 0x46, 0xc6, 0xf8, 0xab, 0xa1, 0x6b, 0x59, 0xc1, 0x99, 0x31, 0x3b, 0xd6, 0x2b, 0x59, 0xfa, 0x71, + 0x7e, 0xfe, 0x59, 0xaf, 0x2e, 0xea, 0xf1, 0x9f, 0xf0, 0xcd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x3b, 0xec, 0xd3, 0x4d, 0x34, 0x05, 0x00, 0x00, +} diff --git a/vendor/github.com/osrg/gobgp/api/capability.proto b/vendor/github.com/osrg/gobgp/api/capability.proto new file mode 100644 index 0000000..78504b5 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/capability.proto @@ -0,0 +1,100 @@ +// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +syntax = "proto3"; + +import "gobgp.proto"; + +package gobgpapi; + +enum AddPathMode { + MODE_NONE = 0; + MODE_RECEIVE = 1; + MODE_SEND = 2; + MODE_BOTH = 3; +} + +message MultiProtocolCapability { + gobgpapi.Family family = 1; +} + +message RouteRefreshCapability { +} + +message CarryingLabelInfoCapability { +} + +message ExtendedNexthopCapabilityTuple { + gobgpapi.Family nlri_family = 1; + // Nexthop AFI must be either + // gobgp.IPv4 or + // gobgp.IPv6. + gobgpapi.Family nexthop_family = 2; +} + +message ExtendedNexthopCapability { + repeated ExtendedNexthopCapabilityTuple tuples = 1; +} + +message GracefulRestartCapabilityTuple { + gobgpapi.Family family = 1; + uint32 flags = 2; +} + +message GracefulRestartCapability { + uint32 flags = 1; + uint32 time = 2; + repeated GracefulRestartCapabilityTuple tuples = 3; +} + +message FourOctetASNumberCapability { + uint32 as = 1; +} + +message AddPathCapabilityTuple { + gobgpapi.Family family = 1; + AddPathMode mode = 2; +} + +message AddPathCapability { + repeated AddPathCapabilityTuple tuples = 1; +} + +message EnhancedRouteRefreshCapability { +} + +message LongLivedGracefulRestartCapabilityTuple { + gobgpapi.Family family = 1; + uint32 flags = 2; + uint32 time = 3; +} + +message LongLivedGracefulRestartCapability { + repeated LongLivedGracefulRestartCapabilityTuple tuples = 1; +} + +message RouteRefreshCiscoCapability { +} + +message UnknownCapability { + uint32 code = 1; + bytes value = 2; +} \ No newline at end of file diff --git a/vendor/github.com/osrg/gobgp/api/gobgp.pb.go b/vendor/github.com/osrg/gobgp/api/gobgp.pb.go new file mode 100644 index 0000000..e2caba9 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/gobgp.pb.go @@ -0,0 +1,8400 @@ +// Code generated by protoc-gen-go. +// source: gobgp.proto +// DO NOT EDIT! + +/* +Package gobgpapi is a generated protocol buffer package. + +It is generated from these files: + gobgp.proto + attribute.proto + capability.proto + +It has these top-level messages: + StartBgpRequest + StopBgpRequest + GetBgpRequest + GetBgpResponse + AddPeerRequest + DeletePeerRequest + ListPeerRequest + ListPeerResponse + UpdatePeerRequest + UpdatePeerResponse + ResetPeerRequest + ShutdownPeerRequest + EnablePeerRequest + DisablePeerRequest + MonitorPeerRequest + MonitorPeerResponse + AddPeerGroupRequest + DeletePeerGroupRequest + UpdatePeerGroupRequest + UpdatePeerGroupResponse + AddDynamicNeighborRequest + AddPathRequest + AddPathResponse + DeletePathRequest + ListPathRequest + ListPathResponse + AddPathStreamRequest + GetTableRequest + GetTableResponse + MonitorTableRequest + MonitorTableResponse + AddVrfRequest + DeleteVrfRequest + ListVrfRequest + ListVrfResponse + AddPolicyRequest + DeletePolicyRequest + ListPolicyRequest + ListPolicyResponse + SetPoliciesRequest + AddDefinedSetRequest + DeleteDefinedSetRequest + ListDefinedSetRequest + ListDefinedSetResponse + AddStatementRequest + DeleteStatementRequest + ListStatementRequest + ListStatementResponse + AddPolicyAssignmentRequest + DeletePolicyAssignmentRequest + ListPolicyAssignmentRequest + ListPolicyAssignmentResponse + SetPolicyAssignmentRequest + AddRpkiRequest + DeleteRpkiRequest + ListRpkiRequest + ListRpkiResponse + EnableRpkiRequest + DisableRpkiRequest + ResetRpkiRequest + ListRpkiTableRequest + ListRpkiTableResponse + EnableZebraRequest + EnableMrtRequest + DisableMrtRequest + AddBmpRequest + DeleteBmpRequest + Family + RPKIValidation + Path + Destination + TableLookupPrefix + Peer + PeerGroup + DynamicNeighbor + ApplyPolicy + PrefixLimit + PeerConf + PeerGroupConf + PeerGroupState + EbgpMultihop + RouteReflector + PeerState + Messages + Message + Queues + Timers + TimersConfig + TimersState + Transport + RouteServer + GracefulRestart + MpGracefulRestartConfig + MpGracefulRestartState + MpGracefulRestart + AfiSafiConfig + AfiSafiState + RouteSelectionOptionsConfig + RouteSelectionOptionsState + RouteSelectionOptions + UseMultiplePathsConfig + UseMultiplePathsState + EbgpConfig + EbgpState + Ebgp + IbgpConfig + IbgpState + Ibgp + UseMultiplePaths + RouteTargetMembershipConfig + RouteTargetMembershipState + RouteTargetMembership + LongLivedGracefulRestartConfig + LongLivedGracefulRestartState + LongLivedGracefulRestart + AfiSafi + AddPathsConfig + AddPathsState + AddPaths + Prefix + DefinedSet + MatchSet + AsPathLength + Conditions + CommunityAction + MedAction + AsPrependAction + NexthopAction + LocalPrefAction + Actions + Statement + Policy + PolicyAssignment + RoutingPolicy + Roa + Vrf + DefaultRouteDistance + Global + Confederation + RPKIConf + RPKIState + Rpki + OriginAttribute + AsSegment + AsPathAttribute + NextHopAttribute + MultiExitDiscAttribute + LocalPrefAttribute + AtomicAggregateAttribute + AggregatorAttribute + CommunitiesAttribute + OriginatorIdAttribute + ClusterListAttribute + IPAddressPrefix + LabeledIPAddressPrefix + EncapsulationNLRI + RouteDistinguisherTwoOctetAS + RouteDistinguisherIPAddress + RouteDistinguisherFourOctetAS + EthernetSegmentIdentifier + EVPNEthernetAutoDiscoveryRoute + EVPNMACIPAdvertisementRoute + EVPNInclusiveMulticastEthernetTagRoute + EVPNEthernetSegmentRoute + EVPNIPPrefixRoute + LabeledVPNIPAddressPrefix + RouteTargetMembershipNLRI + FlowSpecIPPrefix + FlowSpecMAC + FlowSpecComponentItem + FlowSpecComponent + FlowSpecNLRI + VPNFlowSpecNLRI + OpaqueNLRI + MpReachNLRIAttribute + MpUnreachNLRIAttribute + TwoOctetAsSpecificExtended + IPv4AddressSpecificExtended + FourOctetAsSpecificExtended + ValidationExtended + ColorExtended + EncapExtended + DefaultGatewayExtended + OpaqueExtended + ESILabelExtended + ESImportRouteTarget + MacMobilityExtended + RouterMacExtended + TrafficRateExtended + TrafficActionExtended + RedirectTwoOctetAsSpecificExtended + RedirectIPv4AddressSpecificExtended + RedirectFourOctetAsSpecificExtended + TrafficRemarkExtended + UnknownExtended + ExtendedCommunitiesAttribute + As4PathAttribute + As4AggregatorAttribute + PmsiTunnelAttribute + TunnelEncapSubTLVEncapsulation + TunnelEncapSubTLVProtocol + TunnelEncapSubTLVColor + TunnelEncapSubTLVUnknown + TunnelEncapTLV + TunnelEncapAttribute + IPv6AddressSpecificExtended + RedirectIPv6AddressSpecificExtended + IP6ExtendedCommunitiesAttribute + AigpTLVIGPMetric + AigpTLVUnknown + AigpAttribute + LargeCommunity + LargeCommunitiesAttribute + UnknownAttribute + MultiProtocolCapability + RouteRefreshCapability + CarryingLabelInfoCapability + ExtendedNexthopCapabilityTuple + ExtendedNexthopCapability + GracefulRestartCapabilityTuple + GracefulRestartCapability + FourOctetASNumberCapability + AddPathCapabilityTuple + AddPathCapability + EnhancedRouteRefreshCapability + LongLivedGracefulRestartCapabilityTuple + LongLivedGracefulRestartCapability + RouteRefreshCiscoCapability + UnknownCapability +*/ +package gobgpapi + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/any" +import google_protobuf1 "github.com/golang/protobuf/ptypes/empty" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Resource int32 + +const ( + Resource_GLOBAL Resource = 0 + Resource_LOCAL Resource = 1 + Resource_ADJ_IN Resource = 2 + Resource_ADJ_OUT Resource = 3 + Resource_VRF Resource = 4 +) + +var Resource_name = map[int32]string{ + 0: "GLOBAL", + 1: "LOCAL", + 2: "ADJ_IN", + 3: "ADJ_OUT", + 4: "VRF", +} +var Resource_value = map[string]int32{ + "GLOBAL": 0, + "LOCAL": 1, + "ADJ_IN": 2, + "ADJ_OUT": 3, + "VRF": 4, +} + +func (x Resource) String() string { + return proto.EnumName(Resource_name, int32(x)) +} +func (Resource) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +// API representation of table.LookupOption +type TableLookupOption int32 + +const ( + TableLookupOption_LOOKUP_EXACT TableLookupOption = 0 + TableLookupOption_LOOKUP_LONGER TableLookupOption = 1 + TableLookupOption_LOOKUP_SHORTER TableLookupOption = 2 +) + +var TableLookupOption_name = map[int32]string{ + 0: "LOOKUP_EXACT", + 1: "LOOKUP_LONGER", + 2: "LOOKUP_SHORTER", +} +var TableLookupOption_value = map[string]int32{ + "LOOKUP_EXACT": 0, + "LOOKUP_LONGER": 1, + "LOOKUP_SHORTER": 2, +} + +func (x TableLookupOption) String() string { + return proto.EnumName(TableLookupOption_name, int32(x)) +} +func (TableLookupOption) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type DefinedType int32 + +const ( + DefinedType_PREFIX DefinedType = 0 + DefinedType_NEIGHBOR DefinedType = 1 + DefinedType_TAG DefinedType = 2 + DefinedType_AS_PATH DefinedType = 3 + DefinedType_COMMUNITY DefinedType = 4 + DefinedType_EXT_COMMUNITY DefinedType = 5 + DefinedType_LARGE_COMMUNITY DefinedType = 6 + DefinedType_NEXT_HOP DefinedType = 7 +) + +var DefinedType_name = map[int32]string{ + 0: "PREFIX", + 1: "NEIGHBOR", + 2: "TAG", + 3: "AS_PATH", + 4: "COMMUNITY", + 5: "EXT_COMMUNITY", + 6: "LARGE_COMMUNITY", + 7: "NEXT_HOP", +} +var DefinedType_value = map[string]int32{ + "PREFIX": 0, + "NEIGHBOR": 1, + "TAG": 2, + "AS_PATH": 3, + "COMMUNITY": 4, + "EXT_COMMUNITY": 5, + "LARGE_COMMUNITY": 6, + "NEXT_HOP": 7, +} + +func (x DefinedType) String() string { + return proto.EnumName(DefinedType_name, int32(x)) +} +func (DefinedType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +type MatchType int32 + +const ( + MatchType_ANY MatchType = 0 + MatchType_ALL MatchType = 1 + MatchType_INVERT MatchType = 2 +) + +var MatchType_name = map[int32]string{ + 0: "ANY", + 1: "ALL", + 2: "INVERT", +} +var MatchType_value = map[string]int32{ + "ANY": 0, + "ALL": 1, + "INVERT": 2, +} + +func (x MatchType) String() string { + return proto.EnumName(MatchType_name, int32(x)) +} +func (MatchType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +type AsPathLengthType int32 + +const ( + AsPathLengthType_EQ AsPathLengthType = 0 + AsPathLengthType_GE AsPathLengthType = 1 + AsPathLengthType_LE AsPathLengthType = 2 +) + +var AsPathLengthType_name = map[int32]string{ + 0: "EQ", + 1: "GE", + 2: "LE", +} +var AsPathLengthType_value = map[string]int32{ + "EQ": 0, + "GE": 1, + "LE": 2, +} + +func (x AsPathLengthType) String() string { + return proto.EnumName(AsPathLengthType_name, int32(x)) +} +func (AsPathLengthType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +type RouteAction int32 + +const ( + RouteAction_NONE RouteAction = 0 + RouteAction_ACCEPT RouteAction = 1 + RouteAction_REJECT RouteAction = 2 +) + +var RouteAction_name = map[int32]string{ + 0: "NONE", + 1: "ACCEPT", + 2: "REJECT", +} +var RouteAction_value = map[string]int32{ + "NONE": 0, + "ACCEPT": 1, + "REJECT": 2, +} + +func (x RouteAction) String() string { + return proto.EnumName(RouteAction_name, int32(x)) +} +func (RouteAction) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +type CommunityActionType int32 + +const ( + CommunityActionType_COMMUNITY_ADD CommunityActionType = 0 + CommunityActionType_COMMUNITY_REMOVE CommunityActionType = 1 + CommunityActionType_COMMUNITY_REPLACE CommunityActionType = 2 +) + +var CommunityActionType_name = map[int32]string{ + 0: "COMMUNITY_ADD", + 1: "COMMUNITY_REMOVE", + 2: "COMMUNITY_REPLACE", +} +var CommunityActionType_value = map[string]int32{ + "COMMUNITY_ADD": 0, + "COMMUNITY_REMOVE": 1, + "COMMUNITY_REPLACE": 2, +} + +func (x CommunityActionType) String() string { + return proto.EnumName(CommunityActionType_name, int32(x)) +} +func (CommunityActionType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +type MedActionType int32 + +const ( + MedActionType_MED_MOD MedActionType = 0 + MedActionType_MED_REPLACE MedActionType = 1 +) + +var MedActionType_name = map[int32]string{ + 0: "MED_MOD", + 1: "MED_REPLACE", +} +var MedActionType_value = map[string]int32{ + "MED_MOD": 0, + "MED_REPLACE": 1, +} + +func (x MedActionType) String() string { + return proto.EnumName(MedActionType_name, int32(x)) +} +func (MedActionType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +type PolicyDirection int32 + +const ( + PolicyDirection_UNKNOWN PolicyDirection = 0 + PolicyDirection_IMPORT PolicyDirection = 1 + PolicyDirection_EXPORT PolicyDirection = 2 +) + +var PolicyDirection_name = map[int32]string{ + 0: "UNKNOWN", + 1: "IMPORT", + 2: "EXPORT", +} +var PolicyDirection_value = map[string]int32{ + "UNKNOWN": 0, + "IMPORT": 1, + "EXPORT": 2, +} + +func (x PolicyDirection) String() string { + return proto.EnumName(PolicyDirection_name, int32(x)) +} +func (PolicyDirection) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +type ResetPeerRequest_SoftResetDirection int32 + +const ( + ResetPeerRequest_IN ResetPeerRequest_SoftResetDirection = 0 + ResetPeerRequest_OUT ResetPeerRequest_SoftResetDirection = 1 + ResetPeerRequest_BOTH ResetPeerRequest_SoftResetDirection = 2 +) + +var ResetPeerRequest_SoftResetDirection_name = map[int32]string{ + 0: "IN", + 1: "OUT", + 2: "BOTH", +} +var ResetPeerRequest_SoftResetDirection_value = map[string]int32{ + "IN": 0, + "OUT": 1, + "BOTH": 2, +} + +func (x ResetPeerRequest_SoftResetDirection) String() string { + return proto.EnumName(ResetPeerRequest_SoftResetDirection_name, int32(x)) +} +func (ResetPeerRequest_SoftResetDirection) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{10, 0} +} + +type AddBmpRequest_MonitoringPolicy int32 + +const ( + AddBmpRequest_PRE AddBmpRequest_MonitoringPolicy = 0 + AddBmpRequest_POST AddBmpRequest_MonitoringPolicy = 1 + AddBmpRequest_BOTH AddBmpRequest_MonitoringPolicy = 2 + AddBmpRequest_LOCAL AddBmpRequest_MonitoringPolicy = 3 + AddBmpRequest_ALL AddBmpRequest_MonitoringPolicy = 4 +) + +var AddBmpRequest_MonitoringPolicy_name = map[int32]string{ + 0: "PRE", + 1: "POST", + 2: "BOTH", + 3: "LOCAL", + 4: "ALL", +} +var AddBmpRequest_MonitoringPolicy_value = map[string]int32{ + "PRE": 0, + "POST": 1, + "BOTH": 2, + "LOCAL": 3, + "ALL": 4, +} + +func (x AddBmpRequest_MonitoringPolicy) String() string { + return proto.EnumName(AddBmpRequest_MonitoringPolicy_name, int32(x)) +} +func (AddBmpRequest_MonitoringPolicy) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{65, 0} +} + +type Family_Afi int32 + +const ( + Family_AFI_UNKNOWN Family_Afi = 0 + Family_AFI_IP Family_Afi = 1 + Family_AFI_IP6 Family_Afi = 2 + Family_AFI_L2VPN Family_Afi = 25 + Family_AFI_OPAQUE Family_Afi = 16397 +) + +var Family_Afi_name = map[int32]string{ + 0: "AFI_UNKNOWN", + 1: "AFI_IP", + 2: "AFI_IP6", + 25: "AFI_L2VPN", + 16397: "AFI_OPAQUE", +} +var Family_Afi_value = map[string]int32{ + "AFI_UNKNOWN": 0, + "AFI_IP": 1, + "AFI_IP6": 2, + "AFI_L2VPN": 25, + "AFI_OPAQUE": 16397, +} + +func (x Family_Afi) String() string { + return proto.EnumName(Family_Afi_name, int32(x)) +} +func (Family_Afi) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{67, 0} } + +type Family_Safi int32 + +const ( + Family_SAFI_UNKNOWN Family_Safi = 0 + Family_SAFI_UNICAST Family_Safi = 1 + Family_SAFI_MULTICAST Family_Safi = 2 + Family_SAFI_MPLS_LABEL Family_Safi = 4 + Family_SAFI_ENCAPSULATION Family_Safi = 7 + Family_SAFI_VPLS Family_Safi = 65 + Family_SAFI_EVPN Family_Safi = 70 + Family_SAFI_MPLS_VPN Family_Safi = 128 + Family_SAFI_MPLS_VPN_MULTICAST Family_Safi = 129 + Family_SAFI_ROUTE_TARGET_CONSTRAINTS Family_Safi = 132 + Family_SAFI_FLOW_SPEC_UNICAST Family_Safi = 133 + Family_SAFI_FLOW_SPEC_VPN Family_Safi = 134 + Family_SAFI_KEY_VALUE Family_Safi = 241 +) + +var Family_Safi_name = map[int32]string{ + 0: "SAFI_UNKNOWN", + 1: "SAFI_UNICAST", + 2: "SAFI_MULTICAST", + 4: "SAFI_MPLS_LABEL", + 7: "SAFI_ENCAPSULATION", + 65: "SAFI_VPLS", + 70: "SAFI_EVPN", + 128: "SAFI_MPLS_VPN", + 129: "SAFI_MPLS_VPN_MULTICAST", + 132: "SAFI_ROUTE_TARGET_CONSTRAINTS", + 133: "SAFI_FLOW_SPEC_UNICAST", + 134: "SAFI_FLOW_SPEC_VPN", + 241: "SAFI_KEY_VALUE", +} +var Family_Safi_value = map[string]int32{ + "SAFI_UNKNOWN": 0, + "SAFI_UNICAST": 1, + "SAFI_MULTICAST": 2, + "SAFI_MPLS_LABEL": 4, + "SAFI_ENCAPSULATION": 7, + "SAFI_VPLS": 65, + "SAFI_EVPN": 70, + "SAFI_MPLS_VPN": 128, + "SAFI_MPLS_VPN_MULTICAST": 129, + "SAFI_ROUTE_TARGET_CONSTRAINTS": 132, + "SAFI_FLOW_SPEC_UNICAST": 133, + "SAFI_FLOW_SPEC_VPN": 134, + "SAFI_KEY_VALUE": 241, +} + +func (x Family_Safi) String() string { + return proto.EnumName(Family_Safi_name, int32(x)) +} +func (Family_Safi) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{67, 1} } + +type RPKIValidation_State int32 + +const ( + RPKIValidation_STATE_NONE RPKIValidation_State = 0 + RPKIValidation_STATE_NOT_FOUND RPKIValidation_State = 1 + RPKIValidation_STATE_VALID RPKIValidation_State = 2 + RPKIValidation_STATE_INVALID RPKIValidation_State = 3 +) + +var RPKIValidation_State_name = map[int32]string{ + 0: "STATE_NONE", + 1: "STATE_NOT_FOUND", + 2: "STATE_VALID", + 3: "STATE_INVALID", +} +var RPKIValidation_State_value = map[string]int32{ + "STATE_NONE": 0, + "STATE_NOT_FOUND": 1, + "STATE_VALID": 2, + "STATE_INVALID": 3, +} + +func (x RPKIValidation_State) String() string { + return proto.EnumName(RPKIValidation_State_name, int32(x)) +} +func (RPKIValidation_State) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{68, 0} } + +type RPKIValidation_Reason int32 + +const ( + RPKIValidation_REASOT_NONE RPKIValidation_Reason = 0 + RPKIValidation_REASON_AS RPKIValidation_Reason = 1 + RPKIValidation_REASON_LENGTH RPKIValidation_Reason = 2 +) + +var RPKIValidation_Reason_name = map[int32]string{ + 0: "REASOT_NONE", + 1: "REASON_AS", + 2: "REASON_LENGTH", +} +var RPKIValidation_Reason_value = map[string]int32{ + "REASOT_NONE": 0, + "REASON_AS": 1, + "REASON_LENGTH": 2, +} + +func (x RPKIValidation_Reason) String() string { + return proto.EnumName(RPKIValidation_Reason_name, int32(x)) +} +func (RPKIValidation_Reason) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{68, 1} } + +type PeerConf_RemovePrivateAs int32 + +const ( + PeerConf_NONE PeerConf_RemovePrivateAs = 0 + PeerConf_ALL PeerConf_RemovePrivateAs = 1 + PeerConf_REPLACE PeerConf_RemovePrivateAs = 2 +) + +var PeerConf_RemovePrivateAs_name = map[int32]string{ + 0: "NONE", + 1: "ALL", + 2: "REPLACE", +} +var PeerConf_RemovePrivateAs_value = map[string]int32{ + "NONE": 0, + "ALL": 1, + "REPLACE": 2, +} + +func (x PeerConf_RemovePrivateAs) String() string { + return proto.EnumName(PeerConf_RemovePrivateAs_name, int32(x)) +} +func (PeerConf_RemovePrivateAs) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{77, 0} } + +type PeerGroupConf_RemovePrivateAs int32 + +const ( + PeerGroupConf_NONE PeerGroupConf_RemovePrivateAs = 0 + PeerGroupConf_ALL PeerGroupConf_RemovePrivateAs = 1 + PeerGroupConf_REPLACE PeerGroupConf_RemovePrivateAs = 2 +) + +var PeerGroupConf_RemovePrivateAs_name = map[int32]string{ + 0: "NONE", + 1: "ALL", + 2: "REPLACE", +} +var PeerGroupConf_RemovePrivateAs_value = map[string]int32{ + "NONE": 0, + "ALL": 1, + "REPLACE": 2, +} + +func (x PeerGroupConf_RemovePrivateAs) String() string { + return proto.EnumName(PeerGroupConf_RemovePrivateAs_name, int32(x)) +} +func (PeerGroupConf_RemovePrivateAs) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{78, 0} +} + +type PeerGroupState_RemovePrivateAs int32 + +const ( + PeerGroupState_NONE PeerGroupState_RemovePrivateAs = 0 + PeerGroupState_ALL PeerGroupState_RemovePrivateAs = 1 + PeerGroupState_REPLACE PeerGroupState_RemovePrivateAs = 2 +) + +var PeerGroupState_RemovePrivateAs_name = map[int32]string{ + 0: "NONE", + 1: "ALL", + 2: "REPLACE", +} +var PeerGroupState_RemovePrivateAs_value = map[string]int32{ + "NONE": 0, + "ALL": 1, + "REPLACE": 2, +} + +func (x PeerGroupState_RemovePrivateAs) String() string { + return proto.EnumName(PeerGroupState_RemovePrivateAs_name, int32(x)) +} +func (PeerGroupState_RemovePrivateAs) EnumDescriptor() ([]byte, []int) { + return fileDescriptor0, []int{79, 0} +} + +type PeerState_SessionState int32 + +const ( + PeerState_UNKNOWN PeerState_SessionState = 0 + PeerState_IDLE PeerState_SessionState = 1 + PeerState_CONNECT PeerState_SessionState = 2 + PeerState_ACTIVE PeerState_SessionState = 3 + PeerState_OPENSENT PeerState_SessionState = 4 + PeerState_OPENCONFIRM PeerState_SessionState = 5 + PeerState_ESTABLISHED PeerState_SessionState = 6 +) + +var PeerState_SessionState_name = map[int32]string{ + 0: "UNKNOWN", + 1: "IDLE", + 2: "CONNECT", + 3: "ACTIVE", + 4: "OPENSENT", + 5: "OPENCONFIRM", + 6: "ESTABLISHED", +} +var PeerState_SessionState_value = map[string]int32{ + "UNKNOWN": 0, + "IDLE": 1, + "CONNECT": 2, + "ACTIVE": 3, + "OPENSENT": 4, + "OPENCONFIRM": 5, + "ESTABLISHED": 6, +} + +func (x PeerState_SessionState) String() string { + return proto.EnumName(PeerState_SessionState_name, int32(x)) +} +func (PeerState_SessionState) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{82, 0} } + +type PeerState_AdminState int32 + +const ( + PeerState_UP PeerState_AdminState = 0 + PeerState_DOWN PeerState_AdminState = 1 + PeerState_PFX_CT PeerState_AdminState = 2 +) + +var PeerState_AdminState_name = map[int32]string{ + 0: "UP", + 1: "DOWN", + 2: "PFX_CT", +} +var PeerState_AdminState_value = map[string]int32{ + "UP": 0, + "DOWN": 1, + "PFX_CT": 2, +} + +func (x PeerState_AdminState) String() string { + return proto.EnumName(PeerState_AdminState_name, int32(x)) +} +func (PeerState_AdminState) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{82, 1} } + +type Conditions_RouteType int32 + +const ( + Conditions_ROUTE_TYPE_NONE Conditions_RouteType = 0 + Conditions_ROUTE_TYPE_INTERNAL Conditions_RouteType = 1 + Conditions_ROUTE_TYPE_EXTERNAL Conditions_RouteType = 2 + Conditions_ROUTE_TYPE_LOCAL Conditions_RouteType = 3 +) + +var Conditions_RouteType_name = map[int32]string{ + 0: "ROUTE_TYPE_NONE", + 1: "ROUTE_TYPE_INTERNAL", + 2: "ROUTE_TYPE_EXTERNAL", + 3: "ROUTE_TYPE_LOCAL", +} +var Conditions_RouteType_value = map[string]int32{ + "ROUTE_TYPE_NONE": 0, + "ROUTE_TYPE_INTERNAL": 1, + "ROUTE_TYPE_EXTERNAL": 2, + "ROUTE_TYPE_LOCAL": 3, +} + +func (x Conditions_RouteType) String() string { + return proto.EnumName(Conditions_RouteType_name, int32(x)) +} +func (Conditions_RouteType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{123, 0} } + +type StartBgpRequest struct { + Global *Global `protobuf:"bytes,1,opt,name=global" json:"global,omitempty"` +} + +func (m *StartBgpRequest) Reset() { *m = StartBgpRequest{} } +func (m *StartBgpRequest) String() string { return proto.CompactTextString(m) } +func (*StartBgpRequest) ProtoMessage() {} +func (*StartBgpRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *StartBgpRequest) GetGlobal() *Global { + if m != nil { + return m.Global + } + return nil +} + +type StopBgpRequest struct { +} + +func (m *StopBgpRequest) Reset() { *m = StopBgpRequest{} } +func (m *StopBgpRequest) String() string { return proto.CompactTextString(m) } +func (*StopBgpRequest) ProtoMessage() {} +func (*StopBgpRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type GetBgpRequest struct { +} + +func (m *GetBgpRequest) Reset() { *m = GetBgpRequest{} } +func (m *GetBgpRequest) String() string { return proto.CompactTextString(m) } +func (*GetBgpRequest) ProtoMessage() {} +func (*GetBgpRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +type GetBgpResponse struct { + Global *Global `protobuf:"bytes,1,opt,name=global" json:"global,omitempty"` +} + +func (m *GetBgpResponse) Reset() { *m = GetBgpResponse{} } +func (m *GetBgpResponse) String() string { return proto.CompactTextString(m) } +func (*GetBgpResponse) ProtoMessage() {} +func (*GetBgpResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *GetBgpResponse) GetGlobal() *Global { + if m != nil { + return m.Global + } + return nil +} + +type AddPeerRequest struct { + Peer *Peer `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` +} + +func (m *AddPeerRequest) Reset() { *m = AddPeerRequest{} } +func (m *AddPeerRequest) String() string { return proto.CompactTextString(m) } +func (*AddPeerRequest) ProtoMessage() {} +func (*AddPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *AddPeerRequest) GetPeer() *Peer { + if m != nil { + return m.Peer + } + return nil +} + +type DeletePeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Interface string `protobuf:"bytes,2,opt,name=interface" json:"interface,omitempty"` +} + +func (m *DeletePeerRequest) Reset() { *m = DeletePeerRequest{} } +func (m *DeletePeerRequest) String() string { return proto.CompactTextString(m) } +func (*DeletePeerRequest) ProtoMessage() {} +func (*DeletePeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *DeletePeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *DeletePeerRequest) GetInterface() string { + if m != nil { + return m.Interface + } + return "" +} + +type ListPeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + EnableAdvertised bool `protobuf:"varint,2,opt,name=enableAdvertised" json:"enableAdvertised,omitempty"` +} + +func (m *ListPeerRequest) Reset() { *m = ListPeerRequest{} } +func (m *ListPeerRequest) String() string { return proto.CompactTextString(m) } +func (*ListPeerRequest) ProtoMessage() {} +func (*ListPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *ListPeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ListPeerRequest) GetEnableAdvertised() bool { + if m != nil { + return m.EnableAdvertised + } + return false +} + +type ListPeerResponse struct { + Peer *Peer `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` +} + +func (m *ListPeerResponse) Reset() { *m = ListPeerResponse{} } +func (m *ListPeerResponse) String() string { return proto.CompactTextString(m) } +func (*ListPeerResponse) ProtoMessage() {} +func (*ListPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *ListPeerResponse) GetPeer() *Peer { + if m != nil { + return m.Peer + } + return nil +} + +type UpdatePeerRequest struct { + Peer *Peer `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` + // Calls SoftResetIn after updating the peer configuration if needed. + DoSoftResetIn bool `protobuf:"varint,2,opt,name=do_soft_reset_in,json=doSoftResetIn" json:"do_soft_reset_in,omitempty"` +} + +func (m *UpdatePeerRequest) Reset() { *m = UpdatePeerRequest{} } +func (m *UpdatePeerRequest) String() string { return proto.CompactTextString(m) } +func (*UpdatePeerRequest) ProtoMessage() {} +func (*UpdatePeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *UpdatePeerRequest) GetPeer() *Peer { + if m != nil { + return m.Peer + } + return nil +} + +func (m *UpdatePeerRequest) GetDoSoftResetIn() bool { + if m != nil { + return m.DoSoftResetIn + } + return false +} + +type UpdatePeerResponse struct { + // Indicates whether calling SoftResetIn is required due to this update. If + // "true" is set, the client should call SoftResetIn manually. If + // "do_soft_reset_in = true" is set in the request, always returned with + // "false". + NeedsSoftResetIn bool `protobuf:"varint,1,opt,name=needs_soft_reset_in,json=needsSoftResetIn" json:"needs_soft_reset_in,omitempty"` +} + +func (m *UpdatePeerResponse) Reset() { *m = UpdatePeerResponse{} } +func (m *UpdatePeerResponse) String() string { return proto.CompactTextString(m) } +func (*UpdatePeerResponse) ProtoMessage() {} +func (*UpdatePeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *UpdatePeerResponse) GetNeedsSoftResetIn() bool { + if m != nil { + return m.NeedsSoftResetIn + } + return false +} + +type ResetPeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Communication string `protobuf:"bytes,2,opt,name=communication" json:"communication,omitempty"` + Soft bool `protobuf:"varint,3,opt,name=soft" json:"soft,omitempty"` + Direction ResetPeerRequest_SoftResetDirection `protobuf:"varint,4,opt,name=direction,enum=gobgpapi.ResetPeerRequest_SoftResetDirection" json:"direction,omitempty"` +} + +func (m *ResetPeerRequest) Reset() { *m = ResetPeerRequest{} } +func (m *ResetPeerRequest) String() string { return proto.CompactTextString(m) } +func (*ResetPeerRequest) ProtoMessage() {} +func (*ResetPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *ResetPeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ResetPeerRequest) GetCommunication() string { + if m != nil { + return m.Communication + } + return "" +} + +func (m *ResetPeerRequest) GetSoft() bool { + if m != nil { + return m.Soft + } + return false +} + +func (m *ResetPeerRequest) GetDirection() ResetPeerRequest_SoftResetDirection { + if m != nil { + return m.Direction + } + return ResetPeerRequest_IN +} + +type ShutdownPeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Communication string `protobuf:"bytes,2,opt,name=communication" json:"communication,omitempty"` +} + +func (m *ShutdownPeerRequest) Reset() { *m = ShutdownPeerRequest{} } +func (m *ShutdownPeerRequest) String() string { return proto.CompactTextString(m) } +func (*ShutdownPeerRequest) ProtoMessage() {} +func (*ShutdownPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *ShutdownPeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ShutdownPeerRequest) GetCommunication() string { + if m != nil { + return m.Communication + } + return "" +} + +type EnablePeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` +} + +func (m *EnablePeerRequest) Reset() { *m = EnablePeerRequest{} } +func (m *EnablePeerRequest) String() string { return proto.CompactTextString(m) } +func (*EnablePeerRequest) ProtoMessage() {} +func (*EnablePeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *EnablePeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +type DisablePeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Communication string `protobuf:"bytes,2,opt,name=communication" json:"communication,omitempty"` +} + +func (m *DisablePeerRequest) Reset() { *m = DisablePeerRequest{} } +func (m *DisablePeerRequest) String() string { return proto.CompactTextString(m) } +func (*DisablePeerRequest) ProtoMessage() {} +func (*DisablePeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *DisablePeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *DisablePeerRequest) GetCommunication() string { + if m != nil { + return m.Communication + } + return "" +} + +type MonitorPeerRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Current bool `protobuf:"varint,2,opt,name=current" json:"current,omitempty"` +} + +func (m *MonitorPeerRequest) Reset() { *m = MonitorPeerRequest{} } +func (m *MonitorPeerRequest) String() string { return proto.CompactTextString(m) } +func (*MonitorPeerRequest) ProtoMessage() {} +func (*MonitorPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *MonitorPeerRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *MonitorPeerRequest) GetCurrent() bool { + if m != nil { + return m.Current + } + return false +} + +type MonitorPeerResponse struct { + Peer *Peer `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` +} + +func (m *MonitorPeerResponse) Reset() { *m = MonitorPeerResponse{} } +func (m *MonitorPeerResponse) String() string { return proto.CompactTextString(m) } +func (*MonitorPeerResponse) ProtoMessage() {} +func (*MonitorPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *MonitorPeerResponse) GetPeer() *Peer { + if m != nil { + return m.Peer + } + return nil +} + +type AddPeerGroupRequest struct { + PeerGroup *PeerGroup `protobuf:"bytes,1,opt,name=peer_group,json=peerGroup" json:"peer_group,omitempty"` +} + +func (m *AddPeerGroupRequest) Reset() { *m = AddPeerGroupRequest{} } +func (m *AddPeerGroupRequest) String() string { return proto.CompactTextString(m) } +func (*AddPeerGroupRequest) ProtoMessage() {} +func (*AddPeerGroupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *AddPeerGroupRequest) GetPeerGroup() *PeerGroup { + if m != nil { + return m.PeerGroup + } + return nil +} + +type DeletePeerGroupRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *DeletePeerGroupRequest) Reset() { *m = DeletePeerGroupRequest{} } +func (m *DeletePeerGroupRequest) String() string { return proto.CompactTextString(m) } +func (*DeletePeerGroupRequest) ProtoMessage() {} +func (*DeletePeerGroupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *DeletePeerGroupRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type UpdatePeerGroupRequest struct { + PeerGroup *PeerGroup `protobuf:"bytes,1,opt,name=peer_group,json=peerGroup" json:"peer_group,omitempty"` + DoSoftResetIn bool `protobuf:"varint,2,opt,name=do_soft_reset_in,json=doSoftResetIn" json:"do_soft_reset_in,omitempty"` +} + +func (m *UpdatePeerGroupRequest) Reset() { *m = UpdatePeerGroupRequest{} } +func (m *UpdatePeerGroupRequest) String() string { return proto.CompactTextString(m) } +func (*UpdatePeerGroupRequest) ProtoMessage() {} +func (*UpdatePeerGroupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *UpdatePeerGroupRequest) GetPeerGroup() *PeerGroup { + if m != nil { + return m.PeerGroup + } + return nil +} + +func (m *UpdatePeerGroupRequest) GetDoSoftResetIn() bool { + if m != nil { + return m.DoSoftResetIn + } + return false +} + +type UpdatePeerGroupResponse struct { + NeedsSoftResetIn bool `protobuf:"varint,1,opt,name=needs_soft_reset_in,json=needsSoftResetIn" json:"needs_soft_reset_in,omitempty"` +} + +func (m *UpdatePeerGroupResponse) Reset() { *m = UpdatePeerGroupResponse{} } +func (m *UpdatePeerGroupResponse) String() string { return proto.CompactTextString(m) } +func (*UpdatePeerGroupResponse) ProtoMessage() {} +func (*UpdatePeerGroupResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *UpdatePeerGroupResponse) GetNeedsSoftResetIn() bool { + if m != nil { + return m.NeedsSoftResetIn + } + return false +} + +type AddDynamicNeighborRequest struct { + DynamicNeighbor *DynamicNeighbor `protobuf:"bytes,1,opt,name=dynamic_neighbor,json=dynamicNeighbor" json:"dynamic_neighbor,omitempty"` +} + +func (m *AddDynamicNeighborRequest) Reset() { *m = AddDynamicNeighborRequest{} } +func (m *AddDynamicNeighborRequest) String() string { return proto.CompactTextString(m) } +func (*AddDynamicNeighborRequest) ProtoMessage() {} +func (*AddDynamicNeighborRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *AddDynamicNeighborRequest) GetDynamicNeighbor() *DynamicNeighbor { + if m != nil { + return m.DynamicNeighbor + } + return nil +} + +type AddPathRequest struct { + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=gobgpapi.Resource" json:"resource,omitempty"` + VrfId string `protobuf:"bytes,2,opt,name=vrf_id,json=vrfId" json:"vrf_id,omitempty"` + Path *Path `protobuf:"bytes,3,opt,name=path" json:"path,omitempty"` +} + +func (m *AddPathRequest) Reset() { *m = AddPathRequest{} } +func (m *AddPathRequest) String() string { return proto.CompactTextString(m) } +func (*AddPathRequest) ProtoMessage() {} +func (*AddPathRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *AddPathRequest) GetResource() Resource { + if m != nil { + return m.Resource + } + return Resource_GLOBAL +} + +func (m *AddPathRequest) GetVrfId() string { + if m != nil { + return m.VrfId + } + return "" +} + +func (m *AddPathRequest) GetPath() *Path { + if m != nil { + return m.Path + } + return nil +} + +type AddPathResponse struct { + Uuid []byte `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` +} + +func (m *AddPathResponse) Reset() { *m = AddPathResponse{} } +func (m *AddPathResponse) String() string { return proto.CompactTextString(m) } +func (*AddPathResponse) ProtoMessage() {} +func (*AddPathResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +func (m *AddPathResponse) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +type DeletePathRequest struct { + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=gobgpapi.Resource" json:"resource,omitempty"` + VrfId string `protobuf:"bytes,2,opt,name=vrf_id,json=vrfId" json:"vrf_id,omitempty"` + Family *Family `protobuf:"bytes,3,opt,name=family" json:"family,omitempty"` + Path *Path `protobuf:"bytes,4,opt,name=path" json:"path,omitempty"` + Uuid []byte `protobuf:"bytes,5,opt,name=uuid,proto3" json:"uuid,omitempty"` +} + +func (m *DeletePathRequest) Reset() { *m = DeletePathRequest{} } +func (m *DeletePathRequest) String() string { return proto.CompactTextString(m) } +func (*DeletePathRequest) ProtoMessage() {} +func (*DeletePathRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *DeletePathRequest) GetResource() Resource { + if m != nil { + return m.Resource + } + return Resource_GLOBAL +} + +func (m *DeletePathRequest) GetVrfId() string { + if m != nil { + return m.VrfId + } + return "" +} + +func (m *DeletePathRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *DeletePathRequest) GetPath() *Path { + if m != nil { + return m.Path + } + return nil +} + +func (m *DeletePathRequest) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +type ListPathRequest struct { + Type Resource `protobuf:"varint,1,opt,name=type,enum=gobgpapi.Resource" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Family *Family `protobuf:"bytes,3,opt,name=family" json:"family,omitempty"` + Prefixes []*TableLookupPrefix `protobuf:"bytes,4,rep,name=prefixes" json:"prefixes,omitempty"` +} + +func (m *ListPathRequest) Reset() { *m = ListPathRequest{} } +func (m *ListPathRequest) String() string { return proto.CompactTextString(m) } +func (*ListPathRequest) ProtoMessage() {} +func (*ListPathRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *ListPathRequest) GetType() Resource { + if m != nil { + return m.Type + } + return Resource_GLOBAL +} + +func (m *ListPathRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *ListPathRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *ListPathRequest) GetPrefixes() []*TableLookupPrefix { + if m != nil { + return m.Prefixes + } + return nil +} + +type ListPathResponse struct { + Destination *Destination `protobuf:"bytes,1,opt,name=destination" json:"destination,omitempty"` +} + +func (m *ListPathResponse) Reset() { *m = ListPathResponse{} } +func (m *ListPathResponse) String() string { return proto.CompactTextString(m) } +func (*ListPathResponse) ProtoMessage() {} +func (*ListPathResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +func (m *ListPathResponse) GetDestination() *Destination { + if m != nil { + return m.Destination + } + return nil +} + +type AddPathStreamRequest struct { + Resource Resource `protobuf:"varint,1,opt,name=resource,enum=gobgpapi.Resource" json:"resource,omitempty"` + VrfId string `protobuf:"bytes,2,opt,name=vrf_id,json=vrfId" json:"vrf_id,omitempty"` + Paths []*Path `protobuf:"bytes,3,rep,name=paths" json:"paths,omitempty"` +} + +func (m *AddPathStreamRequest) Reset() { *m = AddPathStreamRequest{} } +func (m *AddPathStreamRequest) String() string { return proto.CompactTextString(m) } +func (*AddPathStreamRequest) ProtoMessage() {} +func (*AddPathStreamRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +func (m *AddPathStreamRequest) GetResource() Resource { + if m != nil { + return m.Resource + } + return Resource_GLOBAL +} + +func (m *AddPathStreamRequest) GetVrfId() string { + if m != nil { + return m.VrfId + } + return "" +} + +func (m *AddPathStreamRequest) GetPaths() []*Path { + if m != nil { + return m.Paths + } + return nil +} + +type GetTableRequest struct { + Type Resource `protobuf:"varint,1,opt,name=type,enum=gobgpapi.Resource" json:"type,omitempty"` + Family *Family `protobuf:"bytes,2,opt,name=family" json:"family,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` +} + +func (m *GetTableRequest) Reset() { *m = GetTableRequest{} } +func (m *GetTableRequest) String() string { return proto.CompactTextString(m) } +func (*GetTableRequest) ProtoMessage() {} +func (*GetTableRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +func (m *GetTableRequest) GetType() Resource { + if m != nil { + return m.Type + } + return Resource_GLOBAL +} + +func (m *GetTableRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *GetTableRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type GetTableResponse struct { + NumDestination uint64 `protobuf:"varint,1,opt,name=num_destination,json=numDestination" json:"num_destination,omitempty"` + NumPath uint64 `protobuf:"varint,2,opt,name=num_path,json=numPath" json:"num_path,omitempty"` + NumAccepted uint64 `protobuf:"varint,3,opt,name=num_accepted,json=numAccepted" json:"num_accepted,omitempty"` +} + +func (m *GetTableResponse) Reset() { *m = GetTableResponse{} } +func (m *GetTableResponse) String() string { return proto.CompactTextString(m) } +func (*GetTableResponse) ProtoMessage() {} +func (*GetTableResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *GetTableResponse) GetNumDestination() uint64 { + if m != nil { + return m.NumDestination + } + return 0 +} + +func (m *GetTableResponse) GetNumPath() uint64 { + if m != nil { + return m.NumPath + } + return 0 +} + +func (m *GetTableResponse) GetNumAccepted() uint64 { + if m != nil { + return m.NumAccepted + } + return 0 +} + +type MonitorTableRequest struct { + Type Resource `protobuf:"varint,1,opt,name=type,enum=gobgpapi.Resource" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Family *Family `protobuf:"bytes,3,opt,name=family" json:"family,omitempty"` + Current bool `protobuf:"varint,4,opt,name=current" json:"current,omitempty"` + PostPolicy bool `protobuf:"varint,5,opt,name=post_policy,json=postPolicy" json:"post_policy,omitempty"` +} + +func (m *MonitorTableRequest) Reset() { *m = MonitorTableRequest{} } +func (m *MonitorTableRequest) String() string { return proto.CompactTextString(m) } +func (*MonitorTableRequest) ProtoMessage() {} +func (*MonitorTableRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +func (m *MonitorTableRequest) GetType() Resource { + if m != nil { + return m.Type + } + return Resource_GLOBAL +} + +func (m *MonitorTableRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MonitorTableRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *MonitorTableRequest) GetCurrent() bool { + if m != nil { + return m.Current + } + return false +} + +func (m *MonitorTableRequest) GetPostPolicy() bool { + if m != nil { + return m.PostPolicy + } + return false +} + +type MonitorTableResponse struct { + Path *Path `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` +} + +func (m *MonitorTableResponse) Reset() { *m = MonitorTableResponse{} } +func (m *MonitorTableResponse) String() string { return proto.CompactTextString(m) } +func (*MonitorTableResponse) ProtoMessage() {} +func (*MonitorTableResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +func (m *MonitorTableResponse) GetPath() *Path { + if m != nil { + return m.Path + } + return nil +} + +type AddVrfRequest struct { + Vrf *Vrf `protobuf:"bytes,1,opt,name=vrf" json:"vrf,omitempty"` +} + +func (m *AddVrfRequest) Reset() { *m = AddVrfRequest{} } +func (m *AddVrfRequest) String() string { return proto.CompactTextString(m) } +func (*AddVrfRequest) ProtoMessage() {} +func (*AddVrfRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *AddVrfRequest) GetVrf() *Vrf { + if m != nil { + return m.Vrf + } + return nil +} + +type DeleteVrfRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *DeleteVrfRequest) Reset() { *m = DeleteVrfRequest{} } +func (m *DeleteVrfRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteVrfRequest) ProtoMessage() {} +func (*DeleteVrfRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *DeleteVrfRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type ListVrfRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *ListVrfRequest) Reset() { *m = ListVrfRequest{} } +func (m *ListVrfRequest) String() string { return proto.CompactTextString(m) } +func (*ListVrfRequest) ProtoMessage() {} +func (*ListVrfRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *ListVrfRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type ListVrfResponse struct { + Vrf *Vrf `protobuf:"bytes,1,opt,name=vrf" json:"vrf,omitempty"` +} + +func (m *ListVrfResponse) Reset() { *m = ListVrfResponse{} } +func (m *ListVrfResponse) String() string { return proto.CompactTextString(m) } +func (*ListVrfResponse) ProtoMessage() {} +func (*ListVrfResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func (m *ListVrfResponse) GetVrf() *Vrf { + if m != nil { + return m.Vrf + } + return nil +} + +type AddPolicyRequest struct { + Policy *Policy `protobuf:"bytes,1,opt,name=policy" json:"policy,omitempty"` + // if this flag is set, gobgpd won't define new statements + // but refer existing statements using statement's names in this arguments. + ReferExistingStatements bool `protobuf:"varint,2,opt,name=refer_existing_statements,json=referExistingStatements" json:"refer_existing_statements,omitempty"` +} + +func (m *AddPolicyRequest) Reset() { *m = AddPolicyRequest{} } +func (m *AddPolicyRequest) String() string { return proto.CompactTextString(m) } +func (*AddPolicyRequest) ProtoMessage() {} +func (*AddPolicyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } + +func (m *AddPolicyRequest) GetPolicy() *Policy { + if m != nil { + return m.Policy + } + return nil +} + +func (m *AddPolicyRequest) GetReferExistingStatements() bool { + if m != nil { + return m.ReferExistingStatements + } + return false +} + +type DeletePolicyRequest struct { + Policy *Policy `protobuf:"bytes,1,opt,name=policy" json:"policy,omitempty"` + // if this flag is set, gobgpd won't delete any statements + // even if some statements get not used by any policy by this operation. + PreserveStatements bool `protobuf:"varint,2,opt,name=preserve_statements,json=preserveStatements" json:"preserve_statements,omitempty"` + All bool `protobuf:"varint,3,opt,name=all" json:"all,omitempty"` +} + +func (m *DeletePolicyRequest) Reset() { *m = DeletePolicyRequest{} } +func (m *DeletePolicyRequest) String() string { return proto.CompactTextString(m) } +func (*DeletePolicyRequest) ProtoMessage() {} +func (*DeletePolicyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } + +func (m *DeletePolicyRequest) GetPolicy() *Policy { + if m != nil { + return m.Policy + } + return nil +} + +func (m *DeletePolicyRequest) GetPreserveStatements() bool { + if m != nil { + return m.PreserveStatements + } + return false +} + +func (m *DeletePolicyRequest) GetAll() bool { + if m != nil { + return m.All + } + return false +} + +type ListPolicyRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *ListPolicyRequest) Reset() { *m = ListPolicyRequest{} } +func (m *ListPolicyRequest) String() string { return proto.CompactTextString(m) } +func (*ListPolicyRequest) ProtoMessage() {} +func (*ListPolicyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } + +func (m *ListPolicyRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type ListPolicyResponse struct { + Policy *Policy `protobuf:"bytes,1,opt,name=policy" json:"policy,omitempty"` +} + +func (m *ListPolicyResponse) Reset() { *m = ListPolicyResponse{} } +func (m *ListPolicyResponse) String() string { return proto.CompactTextString(m) } +func (*ListPolicyResponse) ProtoMessage() {} +func (*ListPolicyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} } + +func (m *ListPolicyResponse) GetPolicy() *Policy { + if m != nil { + return m.Policy + } + return nil +} + +type SetPoliciesRequest struct { + DefinedSets []*DefinedSet `protobuf:"bytes,1,rep,name=defined_sets,json=definedSets" json:"defined_sets,omitempty"` + Policies []*Policy `protobuf:"bytes,2,rep,name=policies" json:"policies,omitempty"` + Assignments []*PolicyAssignment `protobuf:"bytes,3,rep,name=assignments" json:"assignments,omitempty"` +} + +func (m *SetPoliciesRequest) Reset() { *m = SetPoliciesRequest{} } +func (m *SetPoliciesRequest) String() string { return proto.CompactTextString(m) } +func (*SetPoliciesRequest) ProtoMessage() {} +func (*SetPoliciesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} } + +func (m *SetPoliciesRequest) GetDefinedSets() []*DefinedSet { + if m != nil { + return m.DefinedSets + } + return nil +} + +func (m *SetPoliciesRequest) GetPolicies() []*Policy { + if m != nil { + return m.Policies + } + return nil +} + +func (m *SetPoliciesRequest) GetAssignments() []*PolicyAssignment { + if m != nil { + return m.Assignments + } + return nil +} + +type AddDefinedSetRequest struct { + DefinedSet *DefinedSet `protobuf:"bytes,1,opt,name=defined_set,json=definedSet" json:"defined_set,omitempty"` +} + +func (m *AddDefinedSetRequest) Reset() { *m = AddDefinedSetRequest{} } +func (m *AddDefinedSetRequest) String() string { return proto.CompactTextString(m) } +func (*AddDefinedSetRequest) ProtoMessage() {} +func (*AddDefinedSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} } + +func (m *AddDefinedSetRequest) GetDefinedSet() *DefinedSet { + if m != nil { + return m.DefinedSet + } + return nil +} + +type DeleteDefinedSetRequest struct { + DefinedSet *DefinedSet `protobuf:"bytes,1,opt,name=defined_set,json=definedSet" json:"defined_set,omitempty"` + All bool `protobuf:"varint,2,opt,name=all" json:"all,omitempty"` +} + +func (m *DeleteDefinedSetRequest) Reset() { *m = DeleteDefinedSetRequest{} } +func (m *DeleteDefinedSetRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteDefinedSetRequest) ProtoMessage() {} +func (*DeleteDefinedSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } + +func (m *DeleteDefinedSetRequest) GetDefinedSet() *DefinedSet { + if m != nil { + return m.DefinedSet + } + return nil +} + +func (m *DeleteDefinedSetRequest) GetAll() bool { + if m != nil { + return m.All + } + return false +} + +type ListDefinedSetRequest struct { + Type DefinedType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.DefinedType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` +} + +func (m *ListDefinedSetRequest) Reset() { *m = ListDefinedSetRequest{} } +func (m *ListDefinedSetRequest) String() string { return proto.CompactTextString(m) } +func (*ListDefinedSetRequest) ProtoMessage() {} +func (*ListDefinedSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} } + +func (m *ListDefinedSetRequest) GetType() DefinedType { + if m != nil { + return m.Type + } + return DefinedType_PREFIX +} + +func (m *ListDefinedSetRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type ListDefinedSetResponse struct { + DefinedSet *DefinedSet `protobuf:"bytes,1,opt,name=defined_set,json=definedSet" json:"defined_set,omitempty"` +} + +func (m *ListDefinedSetResponse) Reset() { *m = ListDefinedSetResponse{} } +func (m *ListDefinedSetResponse) String() string { return proto.CompactTextString(m) } +func (*ListDefinedSetResponse) ProtoMessage() {} +func (*ListDefinedSetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} } + +func (m *ListDefinedSetResponse) GetDefinedSet() *DefinedSet { + if m != nil { + return m.DefinedSet + } + return nil +} + +type AddStatementRequest struct { + Statement *Statement `protobuf:"bytes,1,opt,name=statement" json:"statement,omitempty"` +} + +func (m *AddStatementRequest) Reset() { *m = AddStatementRequest{} } +func (m *AddStatementRequest) String() string { return proto.CompactTextString(m) } +func (*AddStatementRequest) ProtoMessage() {} +func (*AddStatementRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} } + +func (m *AddStatementRequest) GetStatement() *Statement { + if m != nil { + return m.Statement + } + return nil +} + +type DeleteStatementRequest struct { + Statement *Statement `protobuf:"bytes,1,opt,name=statement" json:"statement,omitempty"` + All bool `protobuf:"varint,2,opt,name=all" json:"all,omitempty"` +} + +func (m *DeleteStatementRequest) Reset() { *m = DeleteStatementRequest{} } +func (m *DeleteStatementRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteStatementRequest) ProtoMessage() {} +func (*DeleteStatementRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{45} } + +func (m *DeleteStatementRequest) GetStatement() *Statement { + if m != nil { + return m.Statement + } + return nil +} + +func (m *DeleteStatementRequest) GetAll() bool { + if m != nil { + return m.All + } + return false +} + +type ListStatementRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *ListStatementRequest) Reset() { *m = ListStatementRequest{} } +func (m *ListStatementRequest) String() string { return proto.CompactTextString(m) } +func (*ListStatementRequest) ProtoMessage() {} +func (*ListStatementRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{46} } + +func (m *ListStatementRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type ListStatementResponse struct { + Statement *Statement `protobuf:"bytes,1,opt,name=statement" json:"statement,omitempty"` +} + +func (m *ListStatementResponse) Reset() { *m = ListStatementResponse{} } +func (m *ListStatementResponse) String() string { return proto.CompactTextString(m) } +func (*ListStatementResponse) ProtoMessage() {} +func (*ListStatementResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} } + +func (m *ListStatementResponse) GetStatement() *Statement { + if m != nil { + return m.Statement + } + return nil +} + +type AddPolicyAssignmentRequest struct { + Assignment *PolicyAssignment `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"` +} + +func (m *AddPolicyAssignmentRequest) Reset() { *m = AddPolicyAssignmentRequest{} } +func (m *AddPolicyAssignmentRequest) String() string { return proto.CompactTextString(m) } +func (*AddPolicyAssignmentRequest) ProtoMessage() {} +func (*AddPolicyAssignmentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } + +func (m *AddPolicyAssignmentRequest) GetAssignment() *PolicyAssignment { + if m != nil { + return m.Assignment + } + return nil +} + +type DeletePolicyAssignmentRequest struct { + Assignment *PolicyAssignment `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"` + All bool `protobuf:"varint,2,opt,name=all" json:"all,omitempty"` +} + +func (m *DeletePolicyAssignmentRequest) Reset() { *m = DeletePolicyAssignmentRequest{} } +func (m *DeletePolicyAssignmentRequest) String() string { return proto.CompactTextString(m) } +func (*DeletePolicyAssignmentRequest) ProtoMessage() {} +func (*DeletePolicyAssignmentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{49} } + +func (m *DeletePolicyAssignmentRequest) GetAssignment() *PolicyAssignment { + if m != nil { + return m.Assignment + } + return nil +} + +func (m *DeletePolicyAssignmentRequest) GetAll() bool { + if m != nil { + return m.All + } + return false +} + +type ListPolicyAssignmentRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Direction PolicyDirection `protobuf:"varint,2,opt,name=direction,enum=gobgpapi.PolicyDirection" json:"direction,omitempty"` +} + +func (m *ListPolicyAssignmentRequest) Reset() { *m = ListPolicyAssignmentRequest{} } +func (m *ListPolicyAssignmentRequest) String() string { return proto.CompactTextString(m) } +func (*ListPolicyAssignmentRequest) ProtoMessage() {} +func (*ListPolicyAssignmentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} } + +func (m *ListPolicyAssignmentRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *ListPolicyAssignmentRequest) GetDirection() PolicyDirection { + if m != nil { + return m.Direction + } + return PolicyDirection_UNKNOWN +} + +type ListPolicyAssignmentResponse struct { + Assignment *PolicyAssignment `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"` +} + +func (m *ListPolicyAssignmentResponse) Reset() { *m = ListPolicyAssignmentResponse{} } +func (m *ListPolicyAssignmentResponse) String() string { return proto.CompactTextString(m) } +func (*ListPolicyAssignmentResponse) ProtoMessage() {} +func (*ListPolicyAssignmentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} } + +func (m *ListPolicyAssignmentResponse) GetAssignment() *PolicyAssignment { + if m != nil { + return m.Assignment + } + return nil +} + +type SetPolicyAssignmentRequest struct { + Assignment *PolicyAssignment `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"` +} + +func (m *SetPolicyAssignmentRequest) Reset() { *m = SetPolicyAssignmentRequest{} } +func (m *SetPolicyAssignmentRequest) String() string { return proto.CompactTextString(m) } +func (*SetPolicyAssignmentRequest) ProtoMessage() {} +func (*SetPolicyAssignmentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{52} } + +func (m *SetPolicyAssignmentRequest) GetAssignment() *PolicyAssignment { + if m != nil { + return m.Assignment + } + return nil +} + +type AddRpkiRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` + Lifetime int64 `protobuf:"varint,3,opt,name=lifetime" json:"lifetime,omitempty"` +} + +func (m *AddRpkiRequest) Reset() { *m = AddRpkiRequest{} } +func (m *AddRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*AddRpkiRequest) ProtoMessage() {} +func (*AddRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} } + +func (m *AddRpkiRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *AddRpkiRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *AddRpkiRequest) GetLifetime() int64 { + if m != nil { + return m.Lifetime + } + return 0 +} + +type DeleteRpkiRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` +} + +func (m *DeleteRpkiRequest) Reset() { *m = DeleteRpkiRequest{} } +func (m *DeleteRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteRpkiRequest) ProtoMessage() {} +func (*DeleteRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{54} } + +func (m *DeleteRpkiRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *DeleteRpkiRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +type ListRpkiRequest struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` +} + +func (m *ListRpkiRequest) Reset() { *m = ListRpkiRequest{} } +func (m *ListRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*ListRpkiRequest) ProtoMessage() {} +func (*ListRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{55} } + +func (m *ListRpkiRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +type ListRpkiResponse struct { + Server *Rpki `protobuf:"bytes,1,opt,name=server" json:"server,omitempty"` +} + +func (m *ListRpkiResponse) Reset() { *m = ListRpkiResponse{} } +func (m *ListRpkiResponse) String() string { return proto.CompactTextString(m) } +func (*ListRpkiResponse) ProtoMessage() {} +func (*ListRpkiResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} } + +func (m *ListRpkiResponse) GetServer() *Rpki { + if m != nil { + return m.Server + } + return nil +} + +type EnableRpkiRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` +} + +func (m *EnableRpkiRequest) Reset() { *m = EnableRpkiRequest{} } +func (m *EnableRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*EnableRpkiRequest) ProtoMessage() {} +func (*EnableRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} } + +func (m *EnableRpkiRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *EnableRpkiRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +type DisableRpkiRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` +} + +func (m *DisableRpkiRequest) Reset() { *m = DisableRpkiRequest{} } +func (m *DisableRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*DisableRpkiRequest) ProtoMessage() {} +func (*DisableRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{58} } + +func (m *DisableRpkiRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *DisableRpkiRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +type ResetRpkiRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` + Soft bool `protobuf:"varint,3,opt,name=soft" json:"soft,omitempty"` +} + +func (m *ResetRpkiRequest) Reset() { *m = ResetRpkiRequest{} } +func (m *ResetRpkiRequest) String() string { return proto.CompactTextString(m) } +func (*ResetRpkiRequest) ProtoMessage() {} +func (*ResetRpkiRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{59} } + +func (m *ResetRpkiRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ResetRpkiRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *ResetRpkiRequest) GetSoft() bool { + if m != nil { + return m.Soft + } + return false +} + +type ListRpkiTableRequest struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` +} + +func (m *ListRpkiTableRequest) Reset() { *m = ListRpkiTableRequest{} } +func (m *ListRpkiTableRequest) String() string { return proto.CompactTextString(m) } +func (*ListRpkiTableRequest) ProtoMessage() {} +func (*ListRpkiTableRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{60} } + +func (m *ListRpkiTableRequest) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +type ListRpkiTableResponse struct { + Roa *Roa `protobuf:"bytes,1,opt,name=roa" json:"roa,omitempty"` +} + +func (m *ListRpkiTableResponse) Reset() { *m = ListRpkiTableResponse{} } +func (m *ListRpkiTableResponse) String() string { return proto.CompactTextString(m) } +func (*ListRpkiTableResponse) ProtoMessage() {} +func (*ListRpkiTableResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{61} } + +func (m *ListRpkiTableResponse) GetRoa() *Roa { + if m != nil { + return m.Roa + } + return nil +} + +type EnableZebraRequest struct { + Url string `protobuf:"bytes,1,opt,name=url" json:"url,omitempty"` + RouteTypes []string `protobuf:"bytes,2,rep,name=route_types,json=routeTypes" json:"route_types,omitempty"` + Version uint32 `protobuf:"varint,3,opt,name=version" json:"version,omitempty"` + NexthopTriggerEnable bool `protobuf:"varint,4,opt,name=nexthop_trigger_enable,json=nexthopTriggerEnable" json:"nexthop_trigger_enable,omitempty"` + NexthopTriggerDelay uint32 `protobuf:"varint,5,opt,name=nexthop_trigger_delay,json=nexthopTriggerDelay" json:"nexthop_trigger_delay,omitempty"` +} + +func (m *EnableZebraRequest) Reset() { *m = EnableZebraRequest{} } +func (m *EnableZebraRequest) String() string { return proto.CompactTextString(m) } +func (*EnableZebraRequest) ProtoMessage() {} +func (*EnableZebraRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{62} } + +func (m *EnableZebraRequest) GetUrl() string { + if m != nil { + return m.Url + } + return "" +} + +func (m *EnableZebraRequest) GetRouteTypes() []string { + if m != nil { + return m.RouteTypes + } + return nil +} + +func (m *EnableZebraRequest) GetVersion() uint32 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *EnableZebraRequest) GetNexthopTriggerEnable() bool { + if m != nil { + return m.NexthopTriggerEnable + } + return false +} + +func (m *EnableZebraRequest) GetNexthopTriggerDelay() uint32 { + if m != nil { + return m.NexthopTriggerDelay + } + return 0 +} + +type EnableMrtRequest struct { + DumpType int32 `protobuf:"varint,1,opt,name=dump_type,json=dumpType" json:"dump_type,omitempty"` + Filename string `protobuf:"bytes,2,opt,name=filename" json:"filename,omitempty"` + Interval uint64 `protobuf:"varint,3,opt,name=interval" json:"interval,omitempty"` +} + +func (m *EnableMrtRequest) Reset() { *m = EnableMrtRequest{} } +func (m *EnableMrtRequest) String() string { return proto.CompactTextString(m) } +func (*EnableMrtRequest) ProtoMessage() {} +func (*EnableMrtRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{63} } + +func (m *EnableMrtRequest) GetDumpType() int32 { + if m != nil { + return m.DumpType + } + return 0 +} + +func (m *EnableMrtRequest) GetFilename() string { + if m != nil { + return m.Filename + } + return "" +} + +func (m *EnableMrtRequest) GetInterval() uint64 { + if m != nil { + return m.Interval + } + return 0 +} + +type DisableMrtRequest struct { +} + +func (m *DisableMrtRequest) Reset() { *m = DisableMrtRequest{} } +func (m *DisableMrtRequest) String() string { return proto.CompactTextString(m) } +func (*DisableMrtRequest) ProtoMessage() {} +func (*DisableMrtRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} } + +type AddBmpRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` + Type AddBmpRequest_MonitoringPolicy `protobuf:"varint,3,opt,name=type,enum=gobgpapi.AddBmpRequest_MonitoringPolicy" json:"type,omitempty"` +} + +func (m *AddBmpRequest) Reset() { *m = AddBmpRequest{} } +func (m *AddBmpRequest) String() string { return proto.CompactTextString(m) } +func (*AddBmpRequest) ProtoMessage() {} +func (*AddBmpRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{65} } + +func (m *AddBmpRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *AddBmpRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *AddBmpRequest) GetType() AddBmpRequest_MonitoringPolicy { + if m != nil { + return m.Type + } + return AddBmpRequest_PRE +} + +type DeleteBmpRequest struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` +} + +func (m *DeleteBmpRequest) Reset() { *m = DeleteBmpRequest{} } +func (m *DeleteBmpRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteBmpRequest) ProtoMessage() {} +func (*DeleteBmpRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{66} } + +func (m *DeleteBmpRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *DeleteBmpRequest) GetPort() uint32 { + if m != nil { + return m.Port + } + return 0 +} + +type Family struct { + Afi Family_Afi `protobuf:"varint,1,opt,name=afi,enum=gobgpapi.Family_Afi" json:"afi,omitempty"` + Safi Family_Safi `protobuf:"varint,2,opt,name=safi,enum=gobgpapi.Family_Safi" json:"safi,omitempty"` +} + +func (m *Family) Reset() { *m = Family{} } +func (m *Family) String() string { return proto.CompactTextString(m) } +func (*Family) ProtoMessage() {} +func (*Family) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{67} } + +func (m *Family) GetAfi() Family_Afi { + if m != nil { + return m.Afi + } + return Family_AFI_UNKNOWN +} + +func (m *Family) GetSafi() Family_Safi { + if m != nil { + return m.Safi + } + return Family_SAFI_UNKNOWN +} + +type RPKIValidation struct { + State RPKIValidation_State `protobuf:"varint,1,opt,name=state,enum=gobgpapi.RPKIValidation_State" json:"state,omitempty"` + Reason RPKIValidation_Reason `protobuf:"varint,2,opt,name=reason,enum=gobgpapi.RPKIValidation_Reason" json:"reason,omitempty"` + Matched []*Roa `protobuf:"bytes,3,rep,name=matched" json:"matched,omitempty"` + UnmatchedAs []*Roa `protobuf:"bytes,4,rep,name=unmatched_as,json=unmatchedAs" json:"unmatched_as,omitempty"` + UnmatchedLength []*Roa `protobuf:"bytes,5,rep,name=unmatched_length,json=unmatchedLength" json:"unmatched_length,omitempty"` +} + +func (m *RPKIValidation) Reset() { *m = RPKIValidation{} } +func (m *RPKIValidation) String() string { return proto.CompactTextString(m) } +func (*RPKIValidation) ProtoMessage() {} +func (*RPKIValidation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} } + +func (m *RPKIValidation) GetState() RPKIValidation_State { + if m != nil { + return m.State + } + return RPKIValidation_STATE_NONE +} + +func (m *RPKIValidation) GetReason() RPKIValidation_Reason { + if m != nil { + return m.Reason + } + return RPKIValidation_REASOT_NONE +} + +func (m *RPKIValidation) GetMatched() []*Roa { + if m != nil { + return m.Matched + } + return nil +} + +func (m *RPKIValidation) GetUnmatchedAs() []*Roa { + if m != nil { + return m.UnmatchedAs + } + return nil +} + +func (m *RPKIValidation) GetUnmatchedLength() []*Roa { + if m != nil { + return m.UnmatchedLength + } + return nil +} + +type Path struct { + Nlri []byte `protobuf:"bytes,1,opt,name=nlri,proto3" json:"nlri,omitempty"` + Pattrs [][]byte `protobuf:"bytes,2,rep,name=pattrs,proto3" json:"pattrs,omitempty"` + Age int64 `protobuf:"varint,3,opt,name=age" json:"age,omitempty"` + Best bool `protobuf:"varint,4,opt,name=best" json:"best,omitempty"` + IsWithdraw bool `protobuf:"varint,5,opt,name=is_withdraw,json=isWithdraw" json:"is_withdraw,omitempty"` + ValidationDetail *RPKIValidation `protobuf:"bytes,7,opt,name=validation_detail,json=validationDetail" json:"validation_detail,omitempty"` + NoImplicitWithdraw bool `protobuf:"varint,8,opt,name=no_implicit_withdraw,json=noImplicitWithdraw" json:"no_implicit_withdraw,omitempty"` + Family *Family `protobuf:"bytes,9,opt,name=family" json:"family,omitempty"` + SourceAsn uint32 `protobuf:"varint,10,opt,name=source_asn,json=sourceAsn" json:"source_asn,omitempty"` + SourceId string `protobuf:"bytes,11,opt,name=source_id,json=sourceId" json:"source_id,omitempty"` + Filtered bool `protobuf:"varint,12,opt,name=filtered" json:"filtered,omitempty"` + Stale bool `protobuf:"varint,13,opt,name=stale" json:"stale,omitempty"` + IsFromExternal bool `protobuf:"varint,14,opt,name=is_from_external,json=isFromExternal" json:"is_from_external,omitempty"` + NeighborIp string `protobuf:"bytes,15,opt,name=neighbor_ip,json=neighborIp" json:"neighbor_ip,omitempty"` + Uuid []byte `protobuf:"bytes,16,opt,name=uuid,proto3" json:"uuid,omitempty"` + IsNexthopInvalid bool `protobuf:"varint,17,opt,name=is_nexthop_invalid,json=isNexthopInvalid" json:"is_nexthop_invalid,omitempty"` + Identifier uint32 `protobuf:"varint,18,opt,name=identifier" json:"identifier,omitempty"` + LocalIdentifier uint32 `protobuf:"varint,19,opt,name=local_identifier,json=localIdentifier" json:"local_identifier,omitempty"` + // One of the following defined in "api/attribute.proto": + // - IPAddressPrefix + // - LabeledIPAddressPrefix + // - EncapsulationNLRI + // - EVPNEthernetAutoDiscoveryRoute + // - EVPNMACIPAdvertisementRoute + // - EVPNInclusiveMulticastEthernetTagRoute + // - EVPNEthernetSegmentRoute + // - EVPNIPPrefixRoute + // - LabeledVPNIPAddressPrefix + // - RouteTargetMembershipNLRI + // - FlowSpecNLRI + // - VPNFlowSpecNLRI + // - OpaqueNLRI + AnyNlri *google_protobuf.Any `protobuf:"bytes,20,opt,name=any_nlri,json=anyNlri" json:"any_nlri,omitempty"` + // Each attribute must be one of *Attribute defined in + // "api/attribute.proto". + AnyPattrs []*google_protobuf.Any `protobuf:"bytes,21,rep,name=any_pattrs,json=anyPattrs" json:"any_pattrs,omitempty"` +} + +func (m *Path) Reset() { *m = Path{} } +func (m *Path) String() string { return proto.CompactTextString(m) } +func (*Path) ProtoMessage() {} +func (*Path) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} } + +func (m *Path) GetNlri() []byte { + if m != nil { + return m.Nlri + } + return nil +} + +func (m *Path) GetPattrs() [][]byte { + if m != nil { + return m.Pattrs + } + return nil +} + +func (m *Path) GetAge() int64 { + if m != nil { + return m.Age + } + return 0 +} + +func (m *Path) GetBest() bool { + if m != nil { + return m.Best + } + return false +} + +func (m *Path) GetIsWithdraw() bool { + if m != nil { + return m.IsWithdraw + } + return false +} + +func (m *Path) GetValidationDetail() *RPKIValidation { + if m != nil { + return m.ValidationDetail + } + return nil +} + +func (m *Path) GetNoImplicitWithdraw() bool { + if m != nil { + return m.NoImplicitWithdraw + } + return false +} + +func (m *Path) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *Path) GetSourceAsn() uint32 { + if m != nil { + return m.SourceAsn + } + return 0 +} + +func (m *Path) GetSourceId() string { + if m != nil { + return m.SourceId + } + return "" +} + +func (m *Path) GetFiltered() bool { + if m != nil { + return m.Filtered + } + return false +} + +func (m *Path) GetStale() bool { + if m != nil { + return m.Stale + } + return false +} + +func (m *Path) GetIsFromExternal() bool { + if m != nil { + return m.IsFromExternal + } + return false +} + +func (m *Path) GetNeighborIp() string { + if m != nil { + return m.NeighborIp + } + return "" +} + +func (m *Path) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +func (m *Path) GetIsNexthopInvalid() bool { + if m != nil { + return m.IsNexthopInvalid + } + return false +} + +func (m *Path) GetIdentifier() uint32 { + if m != nil { + return m.Identifier + } + return 0 +} + +func (m *Path) GetLocalIdentifier() uint32 { + if m != nil { + return m.LocalIdentifier + } + return 0 +} + +func (m *Path) GetAnyNlri() *google_protobuf.Any { + if m != nil { + return m.AnyNlri + } + return nil +} + +func (m *Path) GetAnyPattrs() []*google_protobuf.Any { + if m != nil { + return m.AnyPattrs + } + return nil +} + +type Destination struct { + Prefix string `protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` + Paths []*Path `protobuf:"bytes,2,rep,name=paths" json:"paths,omitempty"` +} + +func (m *Destination) Reset() { *m = Destination{} } +func (m *Destination) String() string { return proto.CompactTextString(m) } +func (*Destination) ProtoMessage() {} +func (*Destination) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{70} } + +func (m *Destination) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *Destination) GetPaths() []*Path { + if m != nil { + return m.Paths + } + return nil +} + +// API representation of table.LookupPrefix +type TableLookupPrefix struct { + Prefix string `protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` + LookupOption TableLookupOption `protobuf:"varint,2,opt,name=lookup_option,json=lookupOption,enum=gobgpapi.TableLookupOption" json:"lookup_option,omitempty"` +} + +func (m *TableLookupPrefix) Reset() { *m = TableLookupPrefix{} } +func (m *TableLookupPrefix) String() string { return proto.CompactTextString(m) } +func (*TableLookupPrefix) ProtoMessage() {} +func (*TableLookupPrefix) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{71} } + +func (m *TableLookupPrefix) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *TableLookupPrefix) GetLookupOption() TableLookupOption { + if m != nil { + return m.LookupOption + } + return TableLookupOption_LOOKUP_EXACT +} + +type Peer struct { + ApplyPolicy *ApplyPolicy `protobuf:"bytes,1,opt,name=apply_policy,json=applyPolicy" json:"apply_policy,omitempty"` + Conf *PeerConf `protobuf:"bytes,2,opt,name=conf" json:"conf,omitempty"` + EbgpMultihop *EbgpMultihop `protobuf:"bytes,3,opt,name=ebgp_multihop,json=ebgpMultihop" json:"ebgp_multihop,omitempty"` + RouteReflector *RouteReflector `protobuf:"bytes,4,opt,name=route_reflector,json=routeReflector" json:"route_reflector,omitempty"` + State *PeerState `protobuf:"bytes,5,opt,name=state" json:"state,omitempty"` + Timers *Timers `protobuf:"bytes,6,opt,name=timers" json:"timers,omitempty"` + Transport *Transport `protobuf:"bytes,7,opt,name=transport" json:"transport,omitempty"` + RouteServer *RouteServer `protobuf:"bytes,8,opt,name=route_server,json=routeServer" json:"route_server,omitempty"` + GracefulRestart *GracefulRestart `protobuf:"bytes,9,opt,name=graceful_restart,json=gracefulRestart" json:"graceful_restart,omitempty"` + AfiSafis []*AfiSafi `protobuf:"bytes,10,rep,name=afi_safis,json=afiSafis" json:"afi_safis,omitempty"` + AddPaths *AddPaths `protobuf:"bytes,11,opt,name=add_paths,json=addPaths" json:"add_paths,omitempty"` +} + +func (m *Peer) Reset() { *m = Peer{} } +func (m *Peer) String() string { return proto.CompactTextString(m) } +func (*Peer) ProtoMessage() {} +func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{72} } + +func (m *Peer) GetApplyPolicy() *ApplyPolicy { + if m != nil { + return m.ApplyPolicy + } + return nil +} + +func (m *Peer) GetConf() *PeerConf { + if m != nil { + return m.Conf + } + return nil +} + +func (m *Peer) GetEbgpMultihop() *EbgpMultihop { + if m != nil { + return m.EbgpMultihop + } + return nil +} + +func (m *Peer) GetRouteReflector() *RouteReflector { + if m != nil { + return m.RouteReflector + } + return nil +} + +func (m *Peer) GetState() *PeerState { + if m != nil { + return m.State + } + return nil +} + +func (m *Peer) GetTimers() *Timers { + if m != nil { + return m.Timers + } + return nil +} + +func (m *Peer) GetTransport() *Transport { + if m != nil { + return m.Transport + } + return nil +} + +func (m *Peer) GetRouteServer() *RouteServer { + if m != nil { + return m.RouteServer + } + return nil +} + +func (m *Peer) GetGracefulRestart() *GracefulRestart { + if m != nil { + return m.GracefulRestart + } + return nil +} + +func (m *Peer) GetAfiSafis() []*AfiSafi { + if m != nil { + return m.AfiSafis + } + return nil +} + +func (m *Peer) GetAddPaths() *AddPaths { + if m != nil { + return m.AddPaths + } + return nil +} + +type PeerGroup struct { + ApplyPolicy *ApplyPolicy `protobuf:"bytes,1,opt,name=apply_policy,json=applyPolicy" json:"apply_policy,omitempty"` + Conf *PeerGroupConf `protobuf:"bytes,2,opt,name=conf" json:"conf,omitempty"` + EbgpMultihop *EbgpMultihop `protobuf:"bytes,3,opt,name=ebgp_multihop,json=ebgpMultihop" json:"ebgp_multihop,omitempty"` + RouteReflector *RouteReflector `protobuf:"bytes,4,opt,name=route_reflector,json=routeReflector" json:"route_reflector,omitempty"` + Info *PeerGroupState `protobuf:"bytes,5,opt,name=info" json:"info,omitempty"` + Timers *Timers `protobuf:"bytes,6,opt,name=timers" json:"timers,omitempty"` + Transport *Transport `protobuf:"bytes,7,opt,name=transport" json:"transport,omitempty"` + RouteServer *RouteServer `protobuf:"bytes,8,opt,name=route_server,json=routeServer" json:"route_server,omitempty"` + GracefulRestart *GracefulRestart `protobuf:"bytes,9,opt,name=graceful_restart,json=gracefulRestart" json:"graceful_restart,omitempty"` + AfiSafis []*AfiSafi `protobuf:"bytes,10,rep,name=afi_safis,json=afiSafis" json:"afi_safis,omitempty"` + AddPaths *AddPaths `protobuf:"bytes,11,opt,name=add_paths,json=addPaths" json:"add_paths,omitempty"` +} + +func (m *PeerGroup) Reset() { *m = PeerGroup{} } +func (m *PeerGroup) String() string { return proto.CompactTextString(m) } +func (*PeerGroup) ProtoMessage() {} +func (*PeerGroup) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{73} } + +func (m *PeerGroup) GetApplyPolicy() *ApplyPolicy { + if m != nil { + return m.ApplyPolicy + } + return nil +} + +func (m *PeerGroup) GetConf() *PeerGroupConf { + if m != nil { + return m.Conf + } + return nil +} + +func (m *PeerGroup) GetEbgpMultihop() *EbgpMultihop { + if m != nil { + return m.EbgpMultihop + } + return nil +} + +func (m *PeerGroup) GetRouteReflector() *RouteReflector { + if m != nil { + return m.RouteReflector + } + return nil +} + +func (m *PeerGroup) GetInfo() *PeerGroupState { + if m != nil { + return m.Info + } + return nil +} + +func (m *PeerGroup) GetTimers() *Timers { + if m != nil { + return m.Timers + } + return nil +} + +func (m *PeerGroup) GetTransport() *Transport { + if m != nil { + return m.Transport + } + return nil +} + +func (m *PeerGroup) GetRouteServer() *RouteServer { + if m != nil { + return m.RouteServer + } + return nil +} + +func (m *PeerGroup) GetGracefulRestart() *GracefulRestart { + if m != nil { + return m.GracefulRestart + } + return nil +} + +func (m *PeerGroup) GetAfiSafis() []*AfiSafi { + if m != nil { + return m.AfiSafis + } + return nil +} + +func (m *PeerGroup) GetAddPaths() *AddPaths { + if m != nil { + return m.AddPaths + } + return nil +} + +type DynamicNeighbor struct { + Prefix string `protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` + PeerGroup string `protobuf:"bytes,2,opt,name=peer_group,json=peerGroup" json:"peer_group,omitempty"` +} + +func (m *DynamicNeighbor) Reset() { *m = DynamicNeighbor{} } +func (m *DynamicNeighbor) String() string { return proto.CompactTextString(m) } +func (*DynamicNeighbor) ProtoMessage() {} +func (*DynamicNeighbor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} } + +func (m *DynamicNeighbor) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *DynamicNeighbor) GetPeerGroup() string { + if m != nil { + return m.PeerGroup + } + return "" +} + +type ApplyPolicy struct { + InPolicy *PolicyAssignment `protobuf:"bytes,1,opt,name=in_policy,json=inPolicy" json:"in_policy,omitempty"` + ExportPolicy *PolicyAssignment `protobuf:"bytes,2,opt,name=export_policy,json=exportPolicy" json:"export_policy,omitempty"` + ImportPolicy *PolicyAssignment `protobuf:"bytes,3,opt,name=import_policy,json=importPolicy" json:"import_policy,omitempty"` +} + +func (m *ApplyPolicy) Reset() { *m = ApplyPolicy{} } +func (m *ApplyPolicy) String() string { return proto.CompactTextString(m) } +func (*ApplyPolicy) ProtoMessage() {} +func (*ApplyPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} } + +func (m *ApplyPolicy) GetInPolicy() *PolicyAssignment { + if m != nil { + return m.InPolicy + } + return nil +} + +func (m *ApplyPolicy) GetExportPolicy() *PolicyAssignment { + if m != nil { + return m.ExportPolicy + } + return nil +} + +func (m *ApplyPolicy) GetImportPolicy() *PolicyAssignment { + if m != nil { + return m.ImportPolicy + } + return nil +} + +type PrefixLimit struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + MaxPrefixes uint32 `protobuf:"varint,2,opt,name=max_prefixes,json=maxPrefixes" json:"max_prefixes,omitempty"` + ShutdownThresholdPct uint32 `protobuf:"varint,3,opt,name=shutdown_threshold_pct,json=shutdownThresholdPct" json:"shutdown_threshold_pct,omitempty"` +} + +func (m *PrefixLimit) Reset() { *m = PrefixLimit{} } +func (m *PrefixLimit) String() string { return proto.CompactTextString(m) } +func (*PrefixLimit) ProtoMessage() {} +func (*PrefixLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} } + +func (m *PrefixLimit) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *PrefixLimit) GetMaxPrefixes() uint32 { + if m != nil { + return m.MaxPrefixes + } + return 0 +} + +func (m *PrefixLimit) GetShutdownThresholdPct() uint32 { + if m != nil { + return m.ShutdownThresholdPct + } + return 0 +} + +type PeerConf struct { + AuthPassword string `protobuf:"bytes,1,opt,name=auth_password,json=authPassword" json:"auth_password,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` + LocalAs uint32 `protobuf:"varint,3,opt,name=local_as,json=localAs" json:"local_as,omitempty"` + NeighborAddress string `protobuf:"bytes,4,opt,name=neighbor_address,json=neighborAddress" json:"neighbor_address,omitempty"` + PeerAs uint32 `protobuf:"varint,5,opt,name=peer_as,json=peerAs" json:"peer_as,omitempty"` + PeerGroup string `protobuf:"bytes,6,opt,name=peer_group,json=peerGroup" json:"peer_group,omitempty"` + PeerType uint32 `protobuf:"varint,7,opt,name=peer_type,json=peerType" json:"peer_type,omitempty"` + RemovePrivateAs PeerConf_RemovePrivateAs `protobuf:"varint,8,opt,name=remove_private_as,json=removePrivateAs,enum=gobgpapi.PeerConf_RemovePrivateAs" json:"remove_private_as,omitempty"` + RouteFlapDamping bool `protobuf:"varint,9,opt,name=route_flap_damping,json=routeFlapDamping" json:"route_flap_damping,omitempty"` + SendCommunity uint32 `protobuf:"varint,10,opt,name=send_community,json=sendCommunity" json:"send_community,omitempty"` + // Each attribute must be one of *Capability defined in + // "api/capability.proto". + RemoteCap []*google_protobuf.Any `protobuf:"bytes,11,rep,name=remote_cap,json=remoteCap" json:"remote_cap,omitempty"` + LocalCap []*google_protobuf.Any `protobuf:"bytes,12,rep,name=local_cap,json=localCap" json:"local_cap,omitempty"` + Id string `protobuf:"bytes,13,opt,name=id" json:"id,omitempty"` + NeighborInterface string `protobuf:"bytes,14,opt,name=neighbor_interface,json=neighborInterface" json:"neighbor_interface,omitempty"` + Vrf string `protobuf:"bytes,15,opt,name=vrf" json:"vrf,omitempty"` + AllowOwnAs uint32 `protobuf:"varint,16,opt,name=allow_own_as,json=allowOwnAs" json:"allow_own_as,omitempty"` + ReplacePeerAs bool `protobuf:"varint,17,opt,name=replace_peer_as,json=replacePeerAs" json:"replace_peer_as,omitempty"` +} + +func (m *PeerConf) Reset() { *m = PeerConf{} } +func (m *PeerConf) String() string { return proto.CompactTextString(m) } +func (*PeerConf) ProtoMessage() {} +func (*PeerConf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} } + +func (m *PeerConf) GetAuthPassword() string { + if m != nil { + return m.AuthPassword + } + return "" +} + +func (m *PeerConf) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *PeerConf) GetLocalAs() uint32 { + if m != nil { + return m.LocalAs + } + return 0 +} + +func (m *PeerConf) GetNeighborAddress() string { + if m != nil { + return m.NeighborAddress + } + return "" +} + +func (m *PeerConf) GetPeerAs() uint32 { + if m != nil { + return m.PeerAs + } + return 0 +} + +func (m *PeerConf) GetPeerGroup() string { + if m != nil { + return m.PeerGroup + } + return "" +} + +func (m *PeerConf) GetPeerType() uint32 { + if m != nil { + return m.PeerType + } + return 0 +} + +func (m *PeerConf) GetRemovePrivateAs() PeerConf_RemovePrivateAs { + if m != nil { + return m.RemovePrivateAs + } + return PeerConf_NONE +} + +func (m *PeerConf) GetRouteFlapDamping() bool { + if m != nil { + return m.RouteFlapDamping + } + return false +} + +func (m *PeerConf) GetSendCommunity() uint32 { + if m != nil { + return m.SendCommunity + } + return 0 +} + +func (m *PeerConf) GetRemoteCap() []*google_protobuf.Any { + if m != nil { + return m.RemoteCap + } + return nil +} + +func (m *PeerConf) GetLocalCap() []*google_protobuf.Any { + if m != nil { + return m.LocalCap + } + return nil +} + +func (m *PeerConf) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *PeerConf) GetNeighborInterface() string { + if m != nil { + return m.NeighborInterface + } + return "" +} + +func (m *PeerConf) GetVrf() string { + if m != nil { + return m.Vrf + } + return "" +} + +func (m *PeerConf) GetAllowOwnAs() uint32 { + if m != nil { + return m.AllowOwnAs + } + return 0 +} + +func (m *PeerConf) GetReplacePeerAs() bool { + if m != nil { + return m.ReplacePeerAs + } + return false +} + +type PeerGroupConf struct { + AuthPassword string `protobuf:"bytes,1,opt,name=auth_password,json=authPassword" json:"auth_password,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` + LocalAs uint32 `protobuf:"varint,3,opt,name=local_as,json=localAs" json:"local_as,omitempty"` + PeerAs uint32 `protobuf:"varint,4,opt,name=peer_as,json=peerAs" json:"peer_as,omitempty"` + PeerGroupName string `protobuf:"bytes,5,opt,name=peer_group_name,json=peerGroupName" json:"peer_group_name,omitempty"` + PeerType uint32 `protobuf:"varint,6,opt,name=peer_type,json=peerType" json:"peer_type,omitempty"` + RemovePrivateAs PeerGroupConf_RemovePrivateAs `protobuf:"varint,7,opt,name=remove_private_as,json=removePrivateAs,enum=gobgpapi.PeerGroupConf_RemovePrivateAs" json:"remove_private_as,omitempty"` + RouteFlapDamping bool `protobuf:"varint,8,opt,name=route_flap_damping,json=routeFlapDamping" json:"route_flap_damping,omitempty"` + SendCommunity uint32 `protobuf:"varint,9,opt,name=send_community,json=sendCommunity" json:"send_community,omitempty"` +} + +func (m *PeerGroupConf) Reset() { *m = PeerGroupConf{} } +func (m *PeerGroupConf) String() string { return proto.CompactTextString(m) } +func (*PeerGroupConf) ProtoMessage() {} +func (*PeerGroupConf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} } + +func (m *PeerGroupConf) GetAuthPassword() string { + if m != nil { + return m.AuthPassword + } + return "" +} + +func (m *PeerGroupConf) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *PeerGroupConf) GetLocalAs() uint32 { + if m != nil { + return m.LocalAs + } + return 0 +} + +func (m *PeerGroupConf) GetPeerAs() uint32 { + if m != nil { + return m.PeerAs + } + return 0 +} + +func (m *PeerGroupConf) GetPeerGroupName() string { + if m != nil { + return m.PeerGroupName + } + return "" +} + +func (m *PeerGroupConf) GetPeerType() uint32 { + if m != nil { + return m.PeerType + } + return 0 +} + +func (m *PeerGroupConf) GetRemovePrivateAs() PeerGroupConf_RemovePrivateAs { + if m != nil { + return m.RemovePrivateAs + } + return PeerGroupConf_NONE +} + +func (m *PeerGroupConf) GetRouteFlapDamping() bool { + if m != nil { + return m.RouteFlapDamping + } + return false +} + +func (m *PeerGroupConf) GetSendCommunity() uint32 { + if m != nil { + return m.SendCommunity + } + return 0 +} + +type PeerGroupState struct { + AuthPassword string `protobuf:"bytes,1,opt,name=auth_password,json=authPassword" json:"auth_password,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` + LocalAs uint32 `protobuf:"varint,3,opt,name=local_as,json=localAs" json:"local_as,omitempty"` + PeerAs uint32 `protobuf:"varint,4,opt,name=peer_as,json=peerAs" json:"peer_as,omitempty"` + PeerGroupName string `protobuf:"bytes,5,opt,name=peer_group_name,json=peerGroupName" json:"peer_group_name,omitempty"` + PeerType uint32 `protobuf:"varint,6,opt,name=peer_type,json=peerType" json:"peer_type,omitempty"` + RemovePrivateAs PeerGroupState_RemovePrivateAs `protobuf:"varint,7,opt,name=remove_private_as,json=removePrivateAs,enum=gobgpapi.PeerGroupState_RemovePrivateAs" json:"remove_private_as,omitempty"` + RouteFlapDamping bool `protobuf:"varint,8,opt,name=route_flap_damping,json=routeFlapDamping" json:"route_flap_damping,omitempty"` + SendCommunity uint32 `protobuf:"varint,9,opt,name=send_community,json=sendCommunity" json:"send_community,omitempty"` + TotalPaths uint32 `protobuf:"varint,10,opt,name=total_paths,json=totalPaths" json:"total_paths,omitempty"` + TotalPrefixes uint32 `protobuf:"varint,11,opt,name=total_prefixes,json=totalPrefixes" json:"total_prefixes,omitempty"` +} + +func (m *PeerGroupState) Reset() { *m = PeerGroupState{} } +func (m *PeerGroupState) String() string { return proto.CompactTextString(m) } +func (*PeerGroupState) ProtoMessage() {} +func (*PeerGroupState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} } + +func (m *PeerGroupState) GetAuthPassword() string { + if m != nil { + return m.AuthPassword + } + return "" +} + +func (m *PeerGroupState) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *PeerGroupState) GetLocalAs() uint32 { + if m != nil { + return m.LocalAs + } + return 0 +} + +func (m *PeerGroupState) GetPeerAs() uint32 { + if m != nil { + return m.PeerAs + } + return 0 +} + +func (m *PeerGroupState) GetPeerGroupName() string { + if m != nil { + return m.PeerGroupName + } + return "" +} + +func (m *PeerGroupState) GetPeerType() uint32 { + if m != nil { + return m.PeerType + } + return 0 +} + +func (m *PeerGroupState) GetRemovePrivateAs() PeerGroupState_RemovePrivateAs { + if m != nil { + return m.RemovePrivateAs + } + return PeerGroupState_NONE +} + +func (m *PeerGroupState) GetRouteFlapDamping() bool { + if m != nil { + return m.RouteFlapDamping + } + return false +} + +func (m *PeerGroupState) GetSendCommunity() uint32 { + if m != nil { + return m.SendCommunity + } + return 0 +} + +func (m *PeerGroupState) GetTotalPaths() uint32 { + if m != nil { + return m.TotalPaths + } + return 0 +} + +func (m *PeerGroupState) GetTotalPrefixes() uint32 { + if m != nil { + return m.TotalPrefixes + } + return 0 +} + +type EbgpMultihop struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + MultihopTtl uint32 `protobuf:"varint,2,opt,name=multihop_ttl,json=multihopTtl" json:"multihop_ttl,omitempty"` +} + +func (m *EbgpMultihop) Reset() { *m = EbgpMultihop{} } +func (m *EbgpMultihop) String() string { return proto.CompactTextString(m) } +func (*EbgpMultihop) ProtoMessage() {} +func (*EbgpMultihop) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} } + +func (m *EbgpMultihop) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *EbgpMultihop) GetMultihopTtl() uint32 { + if m != nil { + return m.MultihopTtl + } + return 0 +} + +type RouteReflector struct { + RouteReflectorClient bool `protobuf:"varint,1,opt,name=route_reflector_client,json=routeReflectorClient" json:"route_reflector_client,omitempty"` + RouteReflectorClusterId string `protobuf:"bytes,2,opt,name=route_reflector_cluster_id,json=routeReflectorClusterId" json:"route_reflector_cluster_id,omitempty"` +} + +func (m *RouteReflector) Reset() { *m = RouteReflector{} } +func (m *RouteReflector) String() string { return proto.CompactTextString(m) } +func (*RouteReflector) ProtoMessage() {} +func (*RouteReflector) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} } + +func (m *RouteReflector) GetRouteReflectorClient() bool { + if m != nil { + return m.RouteReflectorClient + } + return false +} + +func (m *RouteReflector) GetRouteReflectorClusterId() string { + if m != nil { + return m.RouteReflectorClusterId + } + return "" +} + +type PeerState struct { + AuthPassword string `protobuf:"bytes,1,opt,name=auth_password,json=authPassword" json:"auth_password,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` + LocalAs uint32 `protobuf:"varint,3,opt,name=local_as,json=localAs" json:"local_as,omitempty"` + Messages *Messages `protobuf:"bytes,4,opt,name=messages" json:"messages,omitempty"` + NeighborAddress string `protobuf:"bytes,5,opt,name=neighbor_address,json=neighborAddress" json:"neighbor_address,omitempty"` + PeerAs uint32 `protobuf:"varint,6,opt,name=peer_as,json=peerAs" json:"peer_as,omitempty"` + PeerGroup string `protobuf:"bytes,7,opt,name=peer_group,json=peerGroup" json:"peer_group,omitempty"` + PeerType uint32 `protobuf:"varint,8,opt,name=peer_type,json=peerType" json:"peer_type,omitempty"` + Queues *Queues `protobuf:"bytes,9,opt,name=queues" json:"queues,omitempty"` + RemovePrivateAs uint32 `protobuf:"varint,10,opt,name=remove_private_as,json=removePrivateAs" json:"remove_private_as,omitempty"` + RouteFlapDamping bool `protobuf:"varint,11,opt,name=route_flap_damping,json=routeFlapDamping" json:"route_flap_damping,omitempty"` + SendCommunity uint32 `protobuf:"varint,12,opt,name=send_community,json=sendCommunity" json:"send_community,omitempty"` + SessionState PeerState_SessionState `protobuf:"varint,13,opt,name=session_state,json=sessionState,enum=gobgpapi.PeerState_SessionState" json:"session_state,omitempty"` + SupportedCapabilities []string `protobuf:"bytes,14,rep,name=supported_capabilities,json=supportedCapabilities" json:"supported_capabilities,omitempty"` + AdminState PeerState_AdminState `protobuf:"varint,15,opt,name=admin_state,json=adminState,enum=gobgpapi.PeerState_AdminState" json:"admin_state,omitempty"` + Received uint32 `protobuf:"varint,16,opt,name=received" json:"received,omitempty"` + Accepted uint32 `protobuf:"varint,17,opt,name=accepted" json:"accepted,omitempty"` + Advertised uint32 `protobuf:"varint,18,opt,name=advertised" json:"advertised,omitempty"` + OutQ uint32 `protobuf:"varint,19,opt,name=out_q,json=outQ" json:"out_q,omitempty"` + Flops uint32 `protobuf:"varint,20,opt,name=flops" json:"flops,omitempty"` +} + +func (m *PeerState) Reset() { *m = PeerState{} } +func (m *PeerState) String() string { return proto.CompactTextString(m) } +func (*PeerState) ProtoMessage() {} +func (*PeerState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} } + +func (m *PeerState) GetAuthPassword() string { + if m != nil { + return m.AuthPassword + } + return "" +} + +func (m *PeerState) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *PeerState) GetLocalAs() uint32 { + if m != nil { + return m.LocalAs + } + return 0 +} + +func (m *PeerState) GetMessages() *Messages { + if m != nil { + return m.Messages + } + return nil +} + +func (m *PeerState) GetNeighborAddress() string { + if m != nil { + return m.NeighborAddress + } + return "" +} + +func (m *PeerState) GetPeerAs() uint32 { + if m != nil { + return m.PeerAs + } + return 0 +} + +func (m *PeerState) GetPeerGroup() string { + if m != nil { + return m.PeerGroup + } + return "" +} + +func (m *PeerState) GetPeerType() uint32 { + if m != nil { + return m.PeerType + } + return 0 +} + +func (m *PeerState) GetQueues() *Queues { + if m != nil { + return m.Queues + } + return nil +} + +func (m *PeerState) GetRemovePrivateAs() uint32 { + if m != nil { + return m.RemovePrivateAs + } + return 0 +} + +func (m *PeerState) GetRouteFlapDamping() bool { + if m != nil { + return m.RouteFlapDamping + } + return false +} + +func (m *PeerState) GetSendCommunity() uint32 { + if m != nil { + return m.SendCommunity + } + return 0 +} + +func (m *PeerState) GetSessionState() PeerState_SessionState { + if m != nil { + return m.SessionState + } + return PeerState_UNKNOWN +} + +func (m *PeerState) GetSupportedCapabilities() []string { + if m != nil { + return m.SupportedCapabilities + } + return nil +} + +func (m *PeerState) GetAdminState() PeerState_AdminState { + if m != nil { + return m.AdminState + } + return PeerState_UP +} + +func (m *PeerState) GetReceived() uint32 { + if m != nil { + return m.Received + } + return 0 +} + +func (m *PeerState) GetAccepted() uint32 { + if m != nil { + return m.Accepted + } + return 0 +} + +func (m *PeerState) GetAdvertised() uint32 { + if m != nil { + return m.Advertised + } + return 0 +} + +func (m *PeerState) GetOutQ() uint32 { + if m != nil { + return m.OutQ + } + return 0 +} + +func (m *PeerState) GetFlops() uint32 { + if m != nil { + return m.Flops + } + return 0 +} + +type Messages struct { + Received *Message `protobuf:"bytes,1,opt,name=received" json:"received,omitempty"` + Sent *Message `protobuf:"bytes,2,opt,name=sent" json:"sent,omitempty"` +} + +func (m *Messages) Reset() { *m = Messages{} } +func (m *Messages) String() string { return proto.CompactTextString(m) } +func (*Messages) ProtoMessage() {} +func (*Messages) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} } + +func (m *Messages) GetReceived() *Message { + if m != nil { + return m.Received + } + return nil +} + +func (m *Messages) GetSent() *Message { + if m != nil { + return m.Sent + } + return nil +} + +type Message struct { + Notification uint64 `protobuf:"varint,1,opt,name=notification" json:"notification,omitempty"` + Update uint64 `protobuf:"varint,2,opt,name=update" json:"update,omitempty"` + Open uint64 `protobuf:"varint,3,opt,name=open" json:"open,omitempty"` + Keepalive uint64 `protobuf:"varint,4,opt,name=keepalive" json:"keepalive,omitempty"` + Refresh uint64 `protobuf:"varint,5,opt,name=refresh" json:"refresh,omitempty"` + Discarded uint64 `protobuf:"varint,6,opt,name=discarded" json:"discarded,omitempty"` + Total uint64 `protobuf:"varint,7,opt,name=total" json:"total,omitempty"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} } + +func (m *Message) GetNotification() uint64 { + if m != nil { + return m.Notification + } + return 0 +} + +func (m *Message) GetUpdate() uint64 { + if m != nil { + return m.Update + } + return 0 +} + +func (m *Message) GetOpen() uint64 { + if m != nil { + return m.Open + } + return 0 +} + +func (m *Message) GetKeepalive() uint64 { + if m != nil { + return m.Keepalive + } + return 0 +} + +func (m *Message) GetRefresh() uint64 { + if m != nil { + return m.Refresh + } + return 0 +} + +func (m *Message) GetDiscarded() uint64 { + if m != nil { + return m.Discarded + } + return 0 +} + +func (m *Message) GetTotal() uint64 { + if m != nil { + return m.Total + } + return 0 +} + +type Queues struct { + Input uint32 `protobuf:"varint,1,opt,name=input" json:"input,omitempty"` + Output uint32 `protobuf:"varint,2,opt,name=output" json:"output,omitempty"` +} + +func (m *Queues) Reset() { *m = Queues{} } +func (m *Queues) String() string { return proto.CompactTextString(m) } +func (*Queues) ProtoMessage() {} +func (*Queues) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} } + +func (m *Queues) GetInput() uint32 { + if m != nil { + return m.Input + } + return 0 +} + +func (m *Queues) GetOutput() uint32 { + if m != nil { + return m.Output + } + return 0 +} + +type Timers struct { + Config *TimersConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *TimersState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *Timers) Reset() { *m = Timers{} } +func (m *Timers) String() string { return proto.CompactTextString(m) } +func (*Timers) ProtoMessage() {} +func (*Timers) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} } + +func (m *Timers) GetConfig() *TimersConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *Timers) GetState() *TimersState { + if m != nil { + return m.State + } + return nil +} + +type TimersConfig struct { + ConnectRetry uint64 `protobuf:"varint,1,opt,name=connect_retry,json=connectRetry" json:"connect_retry,omitempty"` + HoldTime uint64 `protobuf:"varint,2,opt,name=hold_time,json=holdTime" json:"hold_time,omitempty"` + KeepaliveInterval uint64 `protobuf:"varint,3,opt,name=keepalive_interval,json=keepaliveInterval" json:"keepalive_interval,omitempty"` + MinimumAdvertisementInterval uint64 `protobuf:"varint,4,opt,name=minimum_advertisement_interval,json=minimumAdvertisementInterval" json:"minimum_advertisement_interval,omitempty"` +} + +func (m *TimersConfig) Reset() { *m = TimersConfig{} } +func (m *TimersConfig) String() string { return proto.CompactTextString(m) } +func (*TimersConfig) ProtoMessage() {} +func (*TimersConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} } + +func (m *TimersConfig) GetConnectRetry() uint64 { + if m != nil { + return m.ConnectRetry + } + return 0 +} + +func (m *TimersConfig) GetHoldTime() uint64 { + if m != nil { + return m.HoldTime + } + return 0 +} + +func (m *TimersConfig) GetKeepaliveInterval() uint64 { + if m != nil { + return m.KeepaliveInterval + } + return 0 +} + +func (m *TimersConfig) GetMinimumAdvertisementInterval() uint64 { + if m != nil { + return m.MinimumAdvertisementInterval + } + return 0 +} + +type TimersState struct { + ConnectRetry uint64 `protobuf:"varint,1,opt,name=connect_retry,json=connectRetry" json:"connect_retry,omitempty"` + HoldTime uint64 `protobuf:"varint,2,opt,name=hold_time,json=holdTime" json:"hold_time,omitempty"` + KeepaliveInterval uint64 `protobuf:"varint,3,opt,name=keepalive_interval,json=keepaliveInterval" json:"keepalive_interval,omitempty"` + MinimumAdvertisementInterval uint64 `protobuf:"varint,4,opt,name=minimum_advertisement_interval,json=minimumAdvertisementInterval" json:"minimum_advertisement_interval,omitempty"` + NegotiatedHoldTime uint64 `protobuf:"varint,5,opt,name=negotiated_hold_time,json=negotiatedHoldTime" json:"negotiated_hold_time,omitempty"` + Uptime uint64 `protobuf:"varint,6,opt,name=uptime" json:"uptime,omitempty"` + Downtime uint64 `protobuf:"varint,7,opt,name=downtime" json:"downtime,omitempty"` +} + +func (m *TimersState) Reset() { *m = TimersState{} } +func (m *TimersState) String() string { return proto.CompactTextString(m) } +func (*TimersState) ProtoMessage() {} +func (*TimersState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} } + +func (m *TimersState) GetConnectRetry() uint64 { + if m != nil { + return m.ConnectRetry + } + return 0 +} + +func (m *TimersState) GetHoldTime() uint64 { + if m != nil { + return m.HoldTime + } + return 0 +} + +func (m *TimersState) GetKeepaliveInterval() uint64 { + if m != nil { + return m.KeepaliveInterval + } + return 0 +} + +func (m *TimersState) GetMinimumAdvertisementInterval() uint64 { + if m != nil { + return m.MinimumAdvertisementInterval + } + return 0 +} + +func (m *TimersState) GetNegotiatedHoldTime() uint64 { + if m != nil { + return m.NegotiatedHoldTime + } + return 0 +} + +func (m *TimersState) GetUptime() uint64 { + if m != nil { + return m.Uptime + } + return 0 +} + +func (m *TimersState) GetDowntime() uint64 { + if m != nil { + return m.Downtime + } + return 0 +} + +type Transport struct { + LocalAddress string `protobuf:"bytes,1,opt,name=local_address,json=localAddress" json:"local_address,omitempty"` + LocalPort uint32 `protobuf:"varint,2,opt,name=local_port,json=localPort" json:"local_port,omitempty"` + MtuDiscovery bool `protobuf:"varint,3,opt,name=mtu_discovery,json=mtuDiscovery" json:"mtu_discovery,omitempty"` + PassiveMode bool `protobuf:"varint,4,opt,name=passive_mode,json=passiveMode" json:"passive_mode,omitempty"` + RemoteAddress string `protobuf:"bytes,5,opt,name=remote_address,json=remoteAddress" json:"remote_address,omitempty"` + RemotePort uint32 `protobuf:"varint,6,opt,name=remote_port,json=remotePort" json:"remote_port,omitempty"` + TcpMss uint32 `protobuf:"varint,7,opt,name=tcp_mss,json=tcpMss" json:"tcp_mss,omitempty"` +} + +func (m *Transport) Reset() { *m = Transport{} } +func (m *Transport) String() string { return proto.CompactTextString(m) } +func (*Transport) ProtoMessage() {} +func (*Transport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} } + +func (m *Transport) GetLocalAddress() string { + if m != nil { + return m.LocalAddress + } + return "" +} + +func (m *Transport) GetLocalPort() uint32 { + if m != nil { + return m.LocalPort + } + return 0 +} + +func (m *Transport) GetMtuDiscovery() bool { + if m != nil { + return m.MtuDiscovery + } + return false +} + +func (m *Transport) GetPassiveMode() bool { + if m != nil { + return m.PassiveMode + } + return false +} + +func (m *Transport) GetRemoteAddress() string { + if m != nil { + return m.RemoteAddress + } + return "" +} + +func (m *Transport) GetRemotePort() uint32 { + if m != nil { + return m.RemotePort + } + return 0 +} + +func (m *Transport) GetTcpMss() uint32 { + if m != nil { + return m.TcpMss + } + return 0 +} + +type RouteServer struct { + RouteServerClient bool `protobuf:"varint,1,opt,name=route_server_client,json=routeServerClient" json:"route_server_client,omitempty"` +} + +func (m *RouteServer) Reset() { *m = RouteServer{} } +func (m *RouteServer) String() string { return proto.CompactTextString(m) } +func (*RouteServer) ProtoMessage() {} +func (*RouteServer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} } + +func (m *RouteServer) GetRouteServerClient() bool { + if m != nil { + return m.RouteServerClient + } + return false +} + +type GracefulRestart struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + RestartTime uint32 `protobuf:"varint,2,opt,name=restart_time,json=restartTime" json:"restart_time,omitempty"` + HelperOnly bool `protobuf:"varint,3,opt,name=helper_only,json=helperOnly" json:"helper_only,omitempty"` + DeferralTime uint32 `protobuf:"varint,4,opt,name=deferral_time,json=deferralTime" json:"deferral_time,omitempty"` + NotificationEnabled bool `protobuf:"varint,5,opt,name=notification_enabled,json=notificationEnabled" json:"notification_enabled,omitempty"` + LonglivedEnabled bool `protobuf:"varint,6,opt,name=longlived_enabled,json=longlivedEnabled" json:"longlived_enabled,omitempty"` + StaleRoutesTime uint32 `protobuf:"varint,7,opt,name=stale_routes_time,json=staleRoutesTime" json:"stale_routes_time,omitempty"` + PeerRestartTime uint32 `protobuf:"varint,8,opt,name=peer_restart_time,json=peerRestartTime" json:"peer_restart_time,omitempty"` + PeerRestarting bool `protobuf:"varint,9,opt,name=peer_restarting,json=peerRestarting" json:"peer_restarting,omitempty"` + LocalRestarting bool `protobuf:"varint,10,opt,name=local_restarting,json=localRestarting" json:"local_restarting,omitempty"` + Mode string `protobuf:"bytes,11,opt,name=mode" json:"mode,omitempty"` +} + +func (m *GracefulRestart) Reset() { *m = GracefulRestart{} } +func (m *GracefulRestart) String() string { return proto.CompactTextString(m) } +func (*GracefulRestart) ProtoMessage() {} +func (*GracefulRestart) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} } + +func (m *GracefulRestart) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *GracefulRestart) GetRestartTime() uint32 { + if m != nil { + return m.RestartTime + } + return 0 +} + +func (m *GracefulRestart) GetHelperOnly() bool { + if m != nil { + return m.HelperOnly + } + return false +} + +func (m *GracefulRestart) GetDeferralTime() uint32 { + if m != nil { + return m.DeferralTime + } + return 0 +} + +func (m *GracefulRestart) GetNotificationEnabled() bool { + if m != nil { + return m.NotificationEnabled + } + return false +} + +func (m *GracefulRestart) GetLonglivedEnabled() bool { + if m != nil { + return m.LonglivedEnabled + } + return false +} + +func (m *GracefulRestart) GetStaleRoutesTime() uint32 { + if m != nil { + return m.StaleRoutesTime + } + return 0 +} + +func (m *GracefulRestart) GetPeerRestartTime() uint32 { + if m != nil { + return m.PeerRestartTime + } + return 0 +} + +func (m *GracefulRestart) GetPeerRestarting() bool { + if m != nil { + return m.PeerRestarting + } + return false +} + +func (m *GracefulRestart) GetLocalRestarting() bool { + if m != nil { + return m.LocalRestarting + } + return false +} + +func (m *GracefulRestart) GetMode() string { + if m != nil { + return m.Mode + } + return "" +} + +type MpGracefulRestartConfig struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` +} + +func (m *MpGracefulRestartConfig) Reset() { *m = MpGracefulRestartConfig{} } +func (m *MpGracefulRestartConfig) String() string { return proto.CompactTextString(m) } +func (*MpGracefulRestartConfig) ProtoMessage() {} +func (*MpGracefulRestartConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{92} } + +func (m *MpGracefulRestartConfig) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +type MpGracefulRestartState struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + Received bool `protobuf:"varint,2,opt,name=received" json:"received,omitempty"` + Advertised bool `protobuf:"varint,3,opt,name=advertised" json:"advertised,omitempty"` + EndOfRibReceived bool `protobuf:"varint,4,opt,name=end_of_rib_received,json=endOfRibReceived" json:"end_of_rib_received,omitempty"` + EndOfRibSent bool `protobuf:"varint,5,opt,name=end_of_rib_sent,json=endOfRibSent" json:"end_of_rib_sent,omitempty"` +} + +func (m *MpGracefulRestartState) Reset() { *m = MpGracefulRestartState{} } +func (m *MpGracefulRestartState) String() string { return proto.CompactTextString(m) } +func (*MpGracefulRestartState) ProtoMessage() {} +func (*MpGracefulRestartState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} } + +func (m *MpGracefulRestartState) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *MpGracefulRestartState) GetReceived() bool { + if m != nil { + return m.Received + } + return false +} + +func (m *MpGracefulRestartState) GetAdvertised() bool { + if m != nil { + return m.Advertised + } + return false +} + +func (m *MpGracefulRestartState) GetEndOfRibReceived() bool { + if m != nil { + return m.EndOfRibReceived + } + return false +} + +func (m *MpGracefulRestartState) GetEndOfRibSent() bool { + if m != nil { + return m.EndOfRibSent + } + return false +} + +type MpGracefulRestart struct { + Config *MpGracefulRestartConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *MpGracefulRestartState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *MpGracefulRestart) Reset() { *m = MpGracefulRestart{} } +func (m *MpGracefulRestart) String() string { return proto.CompactTextString(m) } +func (*MpGracefulRestart) ProtoMessage() {} +func (*MpGracefulRestart) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} } + +func (m *MpGracefulRestart) GetConfig() *MpGracefulRestartConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *MpGracefulRestart) GetState() *MpGracefulRestartState { + if m != nil { + return m.State + } + return nil +} + +type AfiSafiConfig struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled" json:"enabled,omitempty"` +} + +func (m *AfiSafiConfig) Reset() { *m = AfiSafiConfig{} } +func (m *AfiSafiConfig) String() string { return proto.CompactTextString(m) } +func (*AfiSafiConfig) ProtoMessage() {} +func (*AfiSafiConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{95} } + +func (m *AfiSafiConfig) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *AfiSafiConfig) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +type AfiSafiState struct { + Family *Family `protobuf:"bytes,1,opt,name=family" json:"family,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled" json:"enabled,omitempty"` + TotalPaths uint32 `protobuf:"varint,3,opt,name=total_paths,json=totalPaths" json:"total_paths,omitempty"` + TotalPrefixes uint32 `protobuf:"varint,4,opt,name=total_prefixes,json=totalPrefixes" json:"total_prefixes,omitempty"` +} + +func (m *AfiSafiState) Reset() { *m = AfiSafiState{} } +func (m *AfiSafiState) String() string { return proto.CompactTextString(m) } +func (*AfiSafiState) ProtoMessage() {} +func (*AfiSafiState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{96} } + +func (m *AfiSafiState) GetFamily() *Family { + if m != nil { + return m.Family + } + return nil +} + +func (m *AfiSafiState) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *AfiSafiState) GetTotalPaths() uint32 { + if m != nil { + return m.TotalPaths + } + return 0 +} + +func (m *AfiSafiState) GetTotalPrefixes() uint32 { + if m != nil { + return m.TotalPrefixes + } + return 0 +} + +type RouteSelectionOptionsConfig struct { + AlwaysCompareMed bool `protobuf:"varint,1,opt,name=always_compare_med,json=alwaysCompareMed" json:"always_compare_med,omitempty"` + IgnoreAsPathLength bool `protobuf:"varint,2,opt,name=ignore_as_path_length,json=ignoreAsPathLength" json:"ignore_as_path_length,omitempty"` + ExternalCompareRouterId bool `protobuf:"varint,3,opt,name=external_compare_router_id,json=externalCompareRouterId" json:"external_compare_router_id,omitempty"` + AdvertiseInactiveRoutes bool `protobuf:"varint,4,opt,name=advertise_inactive_routes,json=advertiseInactiveRoutes" json:"advertise_inactive_routes,omitempty"` + EnableAigp bool `protobuf:"varint,5,opt,name=enable_aigp,json=enableAigp" json:"enable_aigp,omitempty"` + IgnoreNextHopIgpMetric bool `protobuf:"varint,6,opt,name=ignore_next_hop_igp_metric,json=ignoreNextHopIgpMetric" json:"ignore_next_hop_igp_metric,omitempty"` + DisableBestPathSelection bool `protobuf:"varint,7,opt,name=disable_best_path_selection,json=disableBestPathSelection" json:"disable_best_path_selection,omitempty"` +} + +func (m *RouteSelectionOptionsConfig) Reset() { *m = RouteSelectionOptionsConfig{} } +func (m *RouteSelectionOptionsConfig) String() string { return proto.CompactTextString(m) } +func (*RouteSelectionOptionsConfig) ProtoMessage() {} +func (*RouteSelectionOptionsConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{97} } + +func (m *RouteSelectionOptionsConfig) GetAlwaysCompareMed() bool { + if m != nil { + return m.AlwaysCompareMed + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetIgnoreAsPathLength() bool { + if m != nil { + return m.IgnoreAsPathLength + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetExternalCompareRouterId() bool { + if m != nil { + return m.ExternalCompareRouterId + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetAdvertiseInactiveRoutes() bool { + if m != nil { + return m.AdvertiseInactiveRoutes + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetEnableAigp() bool { + if m != nil { + return m.EnableAigp + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetIgnoreNextHopIgpMetric() bool { + if m != nil { + return m.IgnoreNextHopIgpMetric + } + return false +} + +func (m *RouteSelectionOptionsConfig) GetDisableBestPathSelection() bool { + if m != nil { + return m.DisableBestPathSelection + } + return false +} + +type RouteSelectionOptionsState struct { + AlwaysCompareMed bool `protobuf:"varint,1,opt,name=always_compare_med,json=alwaysCompareMed" json:"always_compare_med,omitempty"` + IgnoreAsPathLength bool `protobuf:"varint,2,opt,name=ignore_as_path_length,json=ignoreAsPathLength" json:"ignore_as_path_length,omitempty"` + ExternalCompareRouterId bool `protobuf:"varint,3,opt,name=external_compare_router_id,json=externalCompareRouterId" json:"external_compare_router_id,omitempty"` + AdvertiseInactiveRoutes bool `protobuf:"varint,4,opt,name=advertise_inactive_routes,json=advertiseInactiveRoutes" json:"advertise_inactive_routes,omitempty"` + EnableAigp bool `protobuf:"varint,5,opt,name=enable_aigp,json=enableAigp" json:"enable_aigp,omitempty"` + IgnoreNextHopIgpMetric bool `protobuf:"varint,6,opt,name=ignore_next_hop_igp_metric,json=ignoreNextHopIgpMetric" json:"ignore_next_hop_igp_metric,omitempty"` + DisableBestPathSelection bool `protobuf:"varint,7,opt,name=disable_best_path_selection,json=disableBestPathSelection" json:"disable_best_path_selection,omitempty"` +} + +func (m *RouteSelectionOptionsState) Reset() { *m = RouteSelectionOptionsState{} } +func (m *RouteSelectionOptionsState) String() string { return proto.CompactTextString(m) } +func (*RouteSelectionOptionsState) ProtoMessage() {} +func (*RouteSelectionOptionsState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{98} } + +func (m *RouteSelectionOptionsState) GetAlwaysCompareMed() bool { + if m != nil { + return m.AlwaysCompareMed + } + return false +} + +func (m *RouteSelectionOptionsState) GetIgnoreAsPathLength() bool { + if m != nil { + return m.IgnoreAsPathLength + } + return false +} + +func (m *RouteSelectionOptionsState) GetExternalCompareRouterId() bool { + if m != nil { + return m.ExternalCompareRouterId + } + return false +} + +func (m *RouteSelectionOptionsState) GetAdvertiseInactiveRoutes() bool { + if m != nil { + return m.AdvertiseInactiveRoutes + } + return false +} + +func (m *RouteSelectionOptionsState) GetEnableAigp() bool { + if m != nil { + return m.EnableAigp + } + return false +} + +func (m *RouteSelectionOptionsState) GetIgnoreNextHopIgpMetric() bool { + if m != nil { + return m.IgnoreNextHopIgpMetric + } + return false +} + +func (m *RouteSelectionOptionsState) GetDisableBestPathSelection() bool { + if m != nil { + return m.DisableBestPathSelection + } + return false +} + +type RouteSelectionOptions struct { + Config *RouteSelectionOptionsConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *RouteSelectionOptionsState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *RouteSelectionOptions) Reset() { *m = RouteSelectionOptions{} } +func (m *RouteSelectionOptions) String() string { return proto.CompactTextString(m) } +func (*RouteSelectionOptions) ProtoMessage() {} +func (*RouteSelectionOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{99} } + +func (m *RouteSelectionOptions) GetConfig() *RouteSelectionOptionsConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *RouteSelectionOptions) GetState() *RouteSelectionOptionsState { + if m != nil { + return m.State + } + return nil +} + +type UseMultiplePathsConfig struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` +} + +func (m *UseMultiplePathsConfig) Reset() { *m = UseMultiplePathsConfig{} } +func (m *UseMultiplePathsConfig) String() string { return proto.CompactTextString(m) } +func (*UseMultiplePathsConfig) ProtoMessage() {} +func (*UseMultiplePathsConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{100} } + +func (m *UseMultiplePathsConfig) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +type UseMultiplePathsState struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` +} + +func (m *UseMultiplePathsState) Reset() { *m = UseMultiplePathsState{} } +func (m *UseMultiplePathsState) String() string { return proto.CompactTextString(m) } +func (*UseMultiplePathsState) ProtoMessage() {} +func (*UseMultiplePathsState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{101} } + +func (m *UseMultiplePathsState) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +type EbgpConfig struct { + AllowMultipleAs bool `protobuf:"varint,1,opt,name=allow_multiple_as,json=allowMultipleAs" json:"allow_multiple_as,omitempty"` + MaximumPaths uint32 `protobuf:"varint,2,opt,name=maximum_paths,json=maximumPaths" json:"maximum_paths,omitempty"` +} + +func (m *EbgpConfig) Reset() { *m = EbgpConfig{} } +func (m *EbgpConfig) String() string { return proto.CompactTextString(m) } +func (*EbgpConfig) ProtoMessage() {} +func (*EbgpConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{102} } + +func (m *EbgpConfig) GetAllowMultipleAs() bool { + if m != nil { + return m.AllowMultipleAs + } + return false +} + +func (m *EbgpConfig) GetMaximumPaths() uint32 { + if m != nil { + return m.MaximumPaths + } + return 0 +} + +type EbgpState struct { + AllowMultipleAs bool `protobuf:"varint,1,opt,name=allow_multiple_as,json=allowMultipleAs" json:"allow_multiple_as,omitempty"` + MaximumPaths uint32 `protobuf:"varint,2,opt,name=maximum_paths,json=maximumPaths" json:"maximum_paths,omitempty"` +} + +func (m *EbgpState) Reset() { *m = EbgpState{} } +func (m *EbgpState) String() string { return proto.CompactTextString(m) } +func (*EbgpState) ProtoMessage() {} +func (*EbgpState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{103} } + +func (m *EbgpState) GetAllowMultipleAs() bool { + if m != nil { + return m.AllowMultipleAs + } + return false +} + +func (m *EbgpState) GetMaximumPaths() uint32 { + if m != nil { + return m.MaximumPaths + } + return 0 +} + +type Ebgp struct { + Config *EbgpConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *EbgpState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *Ebgp) Reset() { *m = Ebgp{} } +func (m *Ebgp) String() string { return proto.CompactTextString(m) } +func (*Ebgp) ProtoMessage() {} +func (*Ebgp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{104} } + +func (m *Ebgp) GetConfig() *EbgpConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *Ebgp) GetState() *EbgpState { + if m != nil { + return m.State + } + return nil +} + +type IbgpConfig struct { + MaximumPaths uint32 `protobuf:"varint,1,opt,name=maximum_paths,json=maximumPaths" json:"maximum_paths,omitempty"` +} + +func (m *IbgpConfig) Reset() { *m = IbgpConfig{} } +func (m *IbgpConfig) String() string { return proto.CompactTextString(m) } +func (*IbgpConfig) ProtoMessage() {} +func (*IbgpConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{105} } + +func (m *IbgpConfig) GetMaximumPaths() uint32 { + if m != nil { + return m.MaximumPaths + } + return 0 +} + +type IbgpState struct { + MaximumPaths uint32 `protobuf:"varint,1,opt,name=maximum_paths,json=maximumPaths" json:"maximum_paths,omitempty"` +} + +func (m *IbgpState) Reset() { *m = IbgpState{} } +func (m *IbgpState) String() string { return proto.CompactTextString(m) } +func (*IbgpState) ProtoMessage() {} +func (*IbgpState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{106} } + +func (m *IbgpState) GetMaximumPaths() uint32 { + if m != nil { + return m.MaximumPaths + } + return 0 +} + +type Ibgp struct { + Config *IbgpConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *IbgpState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *Ibgp) Reset() { *m = Ibgp{} } +func (m *Ibgp) String() string { return proto.CompactTextString(m) } +func (*Ibgp) ProtoMessage() {} +func (*Ibgp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{107} } + +func (m *Ibgp) GetConfig() *IbgpConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *Ibgp) GetState() *IbgpState { + if m != nil { + return m.State + } + return nil +} + +type UseMultiplePaths struct { + Config *UseMultiplePathsConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *UseMultiplePathsState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` + Ebgp *Ebgp `protobuf:"bytes,3,opt,name=ebgp" json:"ebgp,omitempty"` + Ibgp *Ibgp `protobuf:"bytes,4,opt,name=ibgp" json:"ibgp,omitempty"` +} + +func (m *UseMultiplePaths) Reset() { *m = UseMultiplePaths{} } +func (m *UseMultiplePaths) String() string { return proto.CompactTextString(m) } +func (*UseMultiplePaths) ProtoMessage() {} +func (*UseMultiplePaths) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{108} } + +func (m *UseMultiplePaths) GetConfig() *UseMultiplePathsConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *UseMultiplePaths) GetState() *UseMultiplePathsState { + if m != nil { + return m.State + } + return nil +} + +func (m *UseMultiplePaths) GetEbgp() *Ebgp { + if m != nil { + return m.Ebgp + } + return nil +} + +func (m *UseMultiplePaths) GetIbgp() *Ibgp { + if m != nil { + return m.Ibgp + } + return nil +} + +type RouteTargetMembershipConfig struct { + DeferralTime uint32 `protobuf:"varint,1,opt,name=deferral_time,json=deferralTime" json:"deferral_time,omitempty"` +} + +func (m *RouteTargetMembershipConfig) Reset() { *m = RouteTargetMembershipConfig{} } +func (m *RouteTargetMembershipConfig) String() string { return proto.CompactTextString(m) } +func (*RouteTargetMembershipConfig) ProtoMessage() {} +func (*RouteTargetMembershipConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{109} } + +func (m *RouteTargetMembershipConfig) GetDeferralTime() uint32 { + if m != nil { + return m.DeferralTime + } + return 0 +} + +type RouteTargetMembershipState struct { + DeferralTime uint32 `protobuf:"varint,1,opt,name=deferral_time,json=deferralTime" json:"deferral_time,omitempty"` +} + +func (m *RouteTargetMembershipState) Reset() { *m = RouteTargetMembershipState{} } +func (m *RouteTargetMembershipState) String() string { return proto.CompactTextString(m) } +func (*RouteTargetMembershipState) ProtoMessage() {} +func (*RouteTargetMembershipState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{110} } + +func (m *RouteTargetMembershipState) GetDeferralTime() uint32 { + if m != nil { + return m.DeferralTime + } + return 0 +} + +type RouteTargetMembership struct { + Config *RouteTargetMembershipConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *RouteTargetMembershipState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *RouteTargetMembership) Reset() { *m = RouteTargetMembership{} } +func (m *RouteTargetMembership) String() string { return proto.CompactTextString(m) } +func (*RouteTargetMembership) ProtoMessage() {} +func (*RouteTargetMembership) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{111} } + +func (m *RouteTargetMembership) GetConfig() *RouteTargetMembershipConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *RouteTargetMembership) GetState() *RouteTargetMembershipState { + if m != nil { + return m.State + } + return nil +} + +type LongLivedGracefulRestartConfig struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + RestartTime uint32 `protobuf:"varint,2,opt,name=restart_time,json=restartTime" json:"restart_time,omitempty"` +} + +func (m *LongLivedGracefulRestartConfig) Reset() { *m = LongLivedGracefulRestartConfig{} } +func (m *LongLivedGracefulRestartConfig) String() string { return proto.CompactTextString(m) } +func (*LongLivedGracefulRestartConfig) ProtoMessage() {} +func (*LongLivedGracefulRestartConfig) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{112} +} + +func (m *LongLivedGracefulRestartConfig) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *LongLivedGracefulRestartConfig) GetRestartTime() uint32 { + if m != nil { + return m.RestartTime + } + return 0 +} + +type LongLivedGracefulRestartState struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + Received bool `protobuf:"varint,2,opt,name=received" json:"received,omitempty"` + Advertised bool `protobuf:"varint,3,opt,name=advertised" json:"advertised,omitempty"` + PeerRestartTime uint32 `protobuf:"varint,4,opt,name=peer_restart_time,json=peerRestartTime" json:"peer_restart_time,omitempty"` + PeerRestartTimerExpired bool `protobuf:"varint,5,opt,name=peer_restart_timer_expired,json=peerRestartTimerExpired" json:"peer_restart_timer_expired,omitempty"` +} + +func (m *LongLivedGracefulRestartState) Reset() { *m = LongLivedGracefulRestartState{} } +func (m *LongLivedGracefulRestartState) String() string { return proto.CompactTextString(m) } +func (*LongLivedGracefulRestartState) ProtoMessage() {} +func (*LongLivedGracefulRestartState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{113} } + +func (m *LongLivedGracefulRestartState) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *LongLivedGracefulRestartState) GetReceived() bool { + if m != nil { + return m.Received + } + return false +} + +func (m *LongLivedGracefulRestartState) GetAdvertised() bool { + if m != nil { + return m.Advertised + } + return false +} + +func (m *LongLivedGracefulRestartState) GetPeerRestartTime() uint32 { + if m != nil { + return m.PeerRestartTime + } + return 0 +} + +func (m *LongLivedGracefulRestartState) GetPeerRestartTimerExpired() bool { + if m != nil { + return m.PeerRestartTimerExpired + } + return false +} + +type LongLivedGracefulRestart struct { + Config *LongLivedGracefulRestartConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *LongLivedGracefulRestartState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *LongLivedGracefulRestart) Reset() { *m = LongLivedGracefulRestart{} } +func (m *LongLivedGracefulRestart) String() string { return proto.CompactTextString(m) } +func (*LongLivedGracefulRestart) ProtoMessage() {} +func (*LongLivedGracefulRestart) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{114} } + +func (m *LongLivedGracefulRestart) GetConfig() *LongLivedGracefulRestartConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *LongLivedGracefulRestart) GetState() *LongLivedGracefulRestartState { + if m != nil { + return m.State + } + return nil +} + +type AfiSafi struct { + MpGracefulRestart *MpGracefulRestart `protobuf:"bytes,1,opt,name=mp_graceful_restart,json=mpGracefulRestart" json:"mp_graceful_restart,omitempty"` + Config *AfiSafiConfig `protobuf:"bytes,2,opt,name=config" json:"config,omitempty"` + ApplyPolicy *ApplyPolicy `protobuf:"bytes,3,opt,name=apply_policy,json=applyPolicy" json:"apply_policy,omitempty"` + // TODO: + // Support the following structures: + // - Ipv4Unicast + // - Ipv6Unicast + // - Ipv4LabelledUnicast + // - Ipv6LabelledUnicast + // - L3vpnIpv4Unicast + // - L3vpnIpv6Unicast + // - L3vpnIpv4Multicast + // - L3vpnIpv6Multicast + // - L2vpnVpls + // - L2vpnEvpn + RouteSelectionOptions *RouteSelectionOptions `protobuf:"bytes,4,opt,name=route_selection_options,json=routeSelectionOptions" json:"route_selection_options,omitempty"` + UseMultiplePaths *UseMultiplePaths `protobuf:"bytes,5,opt,name=use_multiple_paths,json=useMultiplePaths" json:"use_multiple_paths,omitempty"` + PrefixLimits *PrefixLimit `protobuf:"bytes,6,opt,name=prefix_limits,json=prefixLimits" json:"prefix_limits,omitempty"` + RouteTargetMembership *RouteTargetMembership `protobuf:"bytes,7,opt,name=route_target_membership,json=routeTargetMembership" json:"route_target_membership,omitempty"` + LongLivedGracefulRestart *LongLivedGracefulRestart `protobuf:"bytes,8,opt,name=long_lived_graceful_restart,json=longLivedGracefulRestart" json:"long_lived_graceful_restart,omitempty"` + AddPaths *AddPaths `protobuf:"bytes,9,opt,name=add_paths,json=addPaths" json:"add_paths,omitempty"` +} + +func (m *AfiSafi) Reset() { *m = AfiSafi{} } +func (m *AfiSafi) String() string { return proto.CompactTextString(m) } +func (*AfiSafi) ProtoMessage() {} +func (*AfiSafi) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{115} } + +func (m *AfiSafi) GetMpGracefulRestart() *MpGracefulRestart { + if m != nil { + return m.MpGracefulRestart + } + return nil +} + +func (m *AfiSafi) GetConfig() *AfiSafiConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *AfiSafi) GetApplyPolicy() *ApplyPolicy { + if m != nil { + return m.ApplyPolicy + } + return nil +} + +func (m *AfiSafi) GetRouteSelectionOptions() *RouteSelectionOptions { + if m != nil { + return m.RouteSelectionOptions + } + return nil +} + +func (m *AfiSafi) GetUseMultiplePaths() *UseMultiplePaths { + if m != nil { + return m.UseMultiplePaths + } + return nil +} + +func (m *AfiSafi) GetPrefixLimits() *PrefixLimit { + if m != nil { + return m.PrefixLimits + } + return nil +} + +func (m *AfiSafi) GetRouteTargetMembership() *RouteTargetMembership { + if m != nil { + return m.RouteTargetMembership + } + return nil +} + +func (m *AfiSafi) GetLongLivedGracefulRestart() *LongLivedGracefulRestart { + if m != nil { + return m.LongLivedGracefulRestart + } + return nil +} + +func (m *AfiSafi) GetAddPaths() *AddPaths { + if m != nil { + return m.AddPaths + } + return nil +} + +type AddPathsConfig struct { + Receive bool `protobuf:"varint,1,opt,name=receive" json:"receive,omitempty"` + SendMax uint32 `protobuf:"varint,2,opt,name=send_max,json=sendMax" json:"send_max,omitempty"` +} + +func (m *AddPathsConfig) Reset() { *m = AddPathsConfig{} } +func (m *AddPathsConfig) String() string { return proto.CompactTextString(m) } +func (*AddPathsConfig) ProtoMessage() {} +func (*AddPathsConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{116} } + +func (m *AddPathsConfig) GetReceive() bool { + if m != nil { + return m.Receive + } + return false +} + +func (m *AddPathsConfig) GetSendMax() uint32 { + if m != nil { + return m.SendMax + } + return 0 +} + +type AddPathsState struct { + Receive bool `protobuf:"varint,1,opt,name=receive" json:"receive,omitempty"` + SendMax uint32 `protobuf:"varint,2,opt,name=send_max,json=sendMax" json:"send_max,omitempty"` +} + +func (m *AddPathsState) Reset() { *m = AddPathsState{} } +func (m *AddPathsState) String() string { return proto.CompactTextString(m) } +func (*AddPathsState) ProtoMessage() {} +func (*AddPathsState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{117} } + +func (m *AddPathsState) GetReceive() bool { + if m != nil { + return m.Receive + } + return false +} + +func (m *AddPathsState) GetSendMax() uint32 { + if m != nil { + return m.SendMax + } + return 0 +} + +type AddPaths struct { + Config *AddPathsConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + State *AddPathsState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *AddPaths) Reset() { *m = AddPaths{} } +func (m *AddPaths) String() string { return proto.CompactTextString(m) } +func (*AddPaths) ProtoMessage() {} +func (*AddPaths) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{118} } + +func (m *AddPaths) GetConfig() *AddPathsConfig { + if m != nil { + return m.Config + } + return nil +} + +func (m *AddPaths) GetState() *AddPathsState { + if m != nil { + return m.State + } + return nil +} + +type Prefix struct { + IpPrefix string `protobuf:"bytes,1,opt,name=ip_prefix,json=ipPrefix" json:"ip_prefix,omitempty"` + MaskLengthMin uint32 `protobuf:"varint,2,opt,name=mask_length_min,json=maskLengthMin" json:"mask_length_min,omitempty"` + MaskLengthMax uint32 `protobuf:"varint,3,opt,name=mask_length_max,json=maskLengthMax" json:"mask_length_max,omitempty"` +} + +func (m *Prefix) Reset() { *m = Prefix{} } +func (m *Prefix) String() string { return proto.CompactTextString(m) } +func (*Prefix) ProtoMessage() {} +func (*Prefix) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{119} } + +func (m *Prefix) GetIpPrefix() string { + if m != nil { + return m.IpPrefix + } + return "" +} + +func (m *Prefix) GetMaskLengthMin() uint32 { + if m != nil { + return m.MaskLengthMin + } + return 0 +} + +func (m *Prefix) GetMaskLengthMax() uint32 { + if m != nil { + return m.MaskLengthMax + } + return 0 +} + +type DefinedSet struct { + Type DefinedType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.DefinedType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + List []string `protobuf:"bytes,3,rep,name=list" json:"list,omitempty"` + Prefixes []*Prefix `protobuf:"bytes,4,rep,name=prefixes" json:"prefixes,omitempty"` +} + +func (m *DefinedSet) Reset() { *m = DefinedSet{} } +func (m *DefinedSet) String() string { return proto.CompactTextString(m) } +func (*DefinedSet) ProtoMessage() {} +func (*DefinedSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{120} } + +func (m *DefinedSet) GetType() DefinedType { + if m != nil { + return m.Type + } + return DefinedType_PREFIX +} + +func (m *DefinedSet) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *DefinedSet) GetList() []string { + if m != nil { + return m.List + } + return nil +} + +func (m *DefinedSet) GetPrefixes() []*Prefix { + if m != nil { + return m.Prefixes + } + return nil +} + +type MatchSet struct { + Type MatchType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.MatchType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` +} + +func (m *MatchSet) Reset() { *m = MatchSet{} } +func (m *MatchSet) String() string { return proto.CompactTextString(m) } +func (*MatchSet) ProtoMessage() {} +func (*MatchSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{121} } + +func (m *MatchSet) GetType() MatchType { + if m != nil { + return m.Type + } + return MatchType_ANY +} + +func (m *MatchSet) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type AsPathLength struct { + Type AsPathLengthType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.AsPathLengthType" json:"type,omitempty"` + Length uint32 `protobuf:"varint,2,opt,name=length" json:"length,omitempty"` +} + +func (m *AsPathLength) Reset() { *m = AsPathLength{} } +func (m *AsPathLength) String() string { return proto.CompactTextString(m) } +func (*AsPathLength) ProtoMessage() {} +func (*AsPathLength) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{122} } + +func (m *AsPathLength) GetType() AsPathLengthType { + if m != nil { + return m.Type + } + return AsPathLengthType_EQ +} + +func (m *AsPathLength) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +type Conditions struct { + PrefixSet *MatchSet `protobuf:"bytes,1,opt,name=prefix_set,json=prefixSet" json:"prefix_set,omitempty"` + NeighborSet *MatchSet `protobuf:"bytes,2,opt,name=neighbor_set,json=neighborSet" json:"neighbor_set,omitempty"` + AsPathLength *AsPathLength `protobuf:"bytes,3,opt,name=as_path_length,json=asPathLength" json:"as_path_length,omitempty"` + AsPathSet *MatchSet `protobuf:"bytes,4,opt,name=as_path_set,json=asPathSet" json:"as_path_set,omitempty"` + CommunitySet *MatchSet `protobuf:"bytes,5,opt,name=community_set,json=communitySet" json:"community_set,omitempty"` + ExtCommunitySet *MatchSet `protobuf:"bytes,6,opt,name=ext_community_set,json=extCommunitySet" json:"ext_community_set,omitempty"` + RpkiResult int32 `protobuf:"varint,7,opt,name=rpki_result,json=rpkiResult" json:"rpki_result,omitempty"` + RouteType Conditions_RouteType `protobuf:"varint,8,opt,name=route_type,json=routeType,enum=gobgpapi.Conditions_RouteType" json:"route_type,omitempty"` + LargeCommunitySet *MatchSet `protobuf:"bytes,9,opt,name=large_community_set,json=largeCommunitySet" json:"large_community_set,omitempty"` + NextHopInList []string `protobuf:"bytes,10,rep,name=next_hop_in_list,json=nextHopInList" json:"next_hop_in_list,omitempty"` + AfiSafiIn []*Family `protobuf:"bytes,11,rep,name=afi_safi_in,json=afiSafiIn" json:"afi_safi_in,omitempty"` +} + +func (m *Conditions) Reset() { *m = Conditions{} } +func (m *Conditions) String() string { return proto.CompactTextString(m) } +func (*Conditions) ProtoMessage() {} +func (*Conditions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{123} } + +func (m *Conditions) GetPrefixSet() *MatchSet { + if m != nil { + return m.PrefixSet + } + return nil +} + +func (m *Conditions) GetNeighborSet() *MatchSet { + if m != nil { + return m.NeighborSet + } + return nil +} + +func (m *Conditions) GetAsPathLength() *AsPathLength { + if m != nil { + return m.AsPathLength + } + return nil +} + +func (m *Conditions) GetAsPathSet() *MatchSet { + if m != nil { + return m.AsPathSet + } + return nil +} + +func (m *Conditions) GetCommunitySet() *MatchSet { + if m != nil { + return m.CommunitySet + } + return nil +} + +func (m *Conditions) GetExtCommunitySet() *MatchSet { + if m != nil { + return m.ExtCommunitySet + } + return nil +} + +func (m *Conditions) GetRpkiResult() int32 { + if m != nil { + return m.RpkiResult + } + return 0 +} + +func (m *Conditions) GetRouteType() Conditions_RouteType { + if m != nil { + return m.RouteType + } + return Conditions_ROUTE_TYPE_NONE +} + +func (m *Conditions) GetLargeCommunitySet() *MatchSet { + if m != nil { + return m.LargeCommunitySet + } + return nil +} + +func (m *Conditions) GetNextHopInList() []string { + if m != nil { + return m.NextHopInList + } + return nil +} + +func (m *Conditions) GetAfiSafiIn() []*Family { + if m != nil { + return m.AfiSafiIn + } + return nil +} + +type CommunityAction struct { + Type CommunityActionType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.CommunityActionType" json:"type,omitempty"` + Communities []string `protobuf:"bytes,2,rep,name=communities" json:"communities,omitempty"` +} + +func (m *CommunityAction) Reset() { *m = CommunityAction{} } +func (m *CommunityAction) String() string { return proto.CompactTextString(m) } +func (*CommunityAction) ProtoMessage() {} +func (*CommunityAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{124} } + +func (m *CommunityAction) GetType() CommunityActionType { + if m != nil { + return m.Type + } + return CommunityActionType_COMMUNITY_ADD +} + +func (m *CommunityAction) GetCommunities() []string { + if m != nil { + return m.Communities + } + return nil +} + +type MedAction struct { + Type MedActionType `protobuf:"varint,1,opt,name=type,enum=gobgpapi.MedActionType" json:"type,omitempty"` + Value int64 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` +} + +func (m *MedAction) Reset() { *m = MedAction{} } +func (m *MedAction) String() string { return proto.CompactTextString(m) } +func (*MedAction) ProtoMessage() {} +func (*MedAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{125} } + +func (m *MedAction) GetType() MedActionType { + if m != nil { + return m.Type + } + return MedActionType_MED_MOD +} + +func (m *MedAction) GetValue() int64 { + if m != nil { + return m.Value + } + return 0 +} + +type AsPrependAction struct { + Asn uint32 `protobuf:"varint,1,opt,name=asn" json:"asn,omitempty"` + Repeat uint32 `protobuf:"varint,2,opt,name=repeat" json:"repeat,omitempty"` + UseLeftMost bool `protobuf:"varint,3,opt,name=use_left_most,json=useLeftMost" json:"use_left_most,omitempty"` +} + +func (m *AsPrependAction) Reset() { *m = AsPrependAction{} } +func (m *AsPrependAction) String() string { return proto.CompactTextString(m) } +func (*AsPrependAction) ProtoMessage() {} +func (*AsPrependAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{126} } + +func (m *AsPrependAction) GetAsn() uint32 { + if m != nil { + return m.Asn + } + return 0 +} + +func (m *AsPrependAction) GetRepeat() uint32 { + if m != nil { + return m.Repeat + } + return 0 +} + +func (m *AsPrependAction) GetUseLeftMost() bool { + if m != nil { + return m.UseLeftMost + } + return false +} + +type NexthopAction struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Self bool `protobuf:"varint,2,opt,name=self" json:"self,omitempty"` +} + +func (m *NexthopAction) Reset() { *m = NexthopAction{} } +func (m *NexthopAction) String() string { return proto.CompactTextString(m) } +func (*NexthopAction) ProtoMessage() {} +func (*NexthopAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{127} } + +func (m *NexthopAction) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *NexthopAction) GetSelf() bool { + if m != nil { + return m.Self + } + return false +} + +type LocalPrefAction struct { + Value uint32 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` +} + +func (m *LocalPrefAction) Reset() { *m = LocalPrefAction{} } +func (m *LocalPrefAction) String() string { return proto.CompactTextString(m) } +func (*LocalPrefAction) ProtoMessage() {} +func (*LocalPrefAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{128} } + +func (m *LocalPrefAction) GetValue() uint32 { + if m != nil { + return m.Value + } + return 0 +} + +type Actions struct { + RouteAction RouteAction `protobuf:"varint,1,opt,name=route_action,json=routeAction,enum=gobgpapi.RouteAction" json:"route_action,omitempty"` + Community *CommunityAction `protobuf:"bytes,2,opt,name=community" json:"community,omitempty"` + Med *MedAction `protobuf:"bytes,3,opt,name=med" json:"med,omitempty"` + AsPrepend *AsPrependAction `protobuf:"bytes,4,opt,name=as_prepend,json=asPrepend" json:"as_prepend,omitempty"` + ExtCommunity *CommunityAction `protobuf:"bytes,5,opt,name=ext_community,json=extCommunity" json:"ext_community,omitempty"` + Nexthop *NexthopAction `protobuf:"bytes,6,opt,name=nexthop" json:"nexthop,omitempty"` + LocalPref *LocalPrefAction `protobuf:"bytes,7,opt,name=local_pref,json=localPref" json:"local_pref,omitempty"` + LargeCommunity *CommunityAction `protobuf:"bytes,8,opt,name=large_community,json=largeCommunity" json:"large_community,omitempty"` +} + +func (m *Actions) Reset() { *m = Actions{} } +func (m *Actions) String() string { return proto.CompactTextString(m) } +func (*Actions) ProtoMessage() {} +func (*Actions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{129} } + +func (m *Actions) GetRouteAction() RouteAction { + if m != nil { + return m.RouteAction + } + return RouteAction_NONE +} + +func (m *Actions) GetCommunity() *CommunityAction { + if m != nil { + return m.Community + } + return nil +} + +func (m *Actions) GetMed() *MedAction { + if m != nil { + return m.Med + } + return nil +} + +func (m *Actions) GetAsPrepend() *AsPrependAction { + if m != nil { + return m.AsPrepend + } + return nil +} + +func (m *Actions) GetExtCommunity() *CommunityAction { + if m != nil { + return m.ExtCommunity + } + return nil +} + +func (m *Actions) GetNexthop() *NexthopAction { + if m != nil { + return m.Nexthop + } + return nil +} + +func (m *Actions) GetLocalPref() *LocalPrefAction { + if m != nil { + return m.LocalPref + } + return nil +} + +func (m *Actions) GetLargeCommunity() *CommunityAction { + if m != nil { + return m.LargeCommunity + } + return nil +} + +type Statement struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Conditions *Conditions `protobuf:"bytes,2,opt,name=conditions" json:"conditions,omitempty"` + Actions *Actions `protobuf:"bytes,3,opt,name=actions" json:"actions,omitempty"` +} + +func (m *Statement) Reset() { *m = Statement{} } +func (m *Statement) String() string { return proto.CompactTextString(m) } +func (*Statement) ProtoMessage() {} +func (*Statement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{130} } + +func (m *Statement) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Statement) GetConditions() *Conditions { + if m != nil { + return m.Conditions + } + return nil +} + +func (m *Statement) GetActions() *Actions { + if m != nil { + return m.Actions + } + return nil +} + +type Policy struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Statements []*Statement `protobuf:"bytes,2,rep,name=statements" json:"statements,omitempty"` +} + +func (m *Policy) Reset() { *m = Policy{} } +func (m *Policy) String() string { return proto.CompactTextString(m) } +func (*Policy) ProtoMessage() {} +func (*Policy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{131} } + +func (m *Policy) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Policy) GetStatements() []*Statement { + if m != nil { + return m.Statements + } + return nil +} + +type PolicyAssignment struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Direction PolicyDirection `protobuf:"varint,2,opt,name=direction,enum=gobgpapi.PolicyDirection" json:"direction,omitempty"` + Policies []*Policy `protobuf:"bytes,4,rep,name=policies" json:"policies,omitempty"` + DefaultAction RouteAction `protobuf:"varint,5,opt,name=default_action,json=defaultAction,enum=gobgpapi.RouteAction" json:"default_action,omitempty"` +} + +func (m *PolicyAssignment) Reset() { *m = PolicyAssignment{} } +func (m *PolicyAssignment) String() string { return proto.CompactTextString(m) } +func (*PolicyAssignment) ProtoMessage() {} +func (*PolicyAssignment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{132} } + +func (m *PolicyAssignment) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PolicyAssignment) GetDirection() PolicyDirection { + if m != nil { + return m.Direction + } + return PolicyDirection_UNKNOWN +} + +func (m *PolicyAssignment) GetPolicies() []*Policy { + if m != nil { + return m.Policies + } + return nil +} + +func (m *PolicyAssignment) GetDefaultAction() RouteAction { + if m != nil { + return m.DefaultAction + } + return RouteAction_NONE +} + +type RoutingPolicy struct { + DefinedSets []*DefinedSet `protobuf:"bytes,1,rep,name=defined_sets,json=definedSets" json:"defined_sets,omitempty"` + Policies []*Policy `protobuf:"bytes,2,rep,name=policies" json:"policies,omitempty"` +} + +func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} } +func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) } +func (*RoutingPolicy) ProtoMessage() {} +func (*RoutingPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{133} } + +func (m *RoutingPolicy) GetDefinedSets() []*DefinedSet { + if m != nil { + return m.DefinedSets + } + return nil +} + +func (m *RoutingPolicy) GetPolicies() []*Policy { + if m != nil { + return m.Policies + } + return nil +} + +type Roa struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + Prefixlen uint32 `protobuf:"varint,2,opt,name=prefixlen" json:"prefixlen,omitempty"` + Maxlen uint32 `protobuf:"varint,3,opt,name=maxlen" json:"maxlen,omitempty"` + Prefix string `protobuf:"bytes,4,opt,name=prefix" json:"prefix,omitempty"` + Conf *RPKIConf `protobuf:"bytes,5,opt,name=conf" json:"conf,omitempty"` +} + +func (m *Roa) Reset() { *m = Roa{} } +func (m *Roa) String() string { return proto.CompactTextString(m) } +func (*Roa) ProtoMessage() {} +func (*Roa) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{134} } + +func (m *Roa) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *Roa) GetPrefixlen() uint32 { + if m != nil { + return m.Prefixlen + } + return 0 +} + +func (m *Roa) GetMaxlen() uint32 { + if m != nil { + return m.Maxlen + } + return 0 +} + +func (m *Roa) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +func (m *Roa) GetConf() *RPKIConf { + if m != nil { + return m.Conf + } + return nil +} + +type Vrf struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // Route Distinguisher must be one of + // RouteDistinguisherTwoOctetAS, + // RouteDistinguisherIPAddressAS, + // or RouteDistinguisherFourOctetAS. + Rd *google_protobuf.Any `protobuf:"bytes,2,opt,name=rd" json:"rd,omitempty"` + // List of the Import Route Targets. Each must be one of + // TwoOctetAsSpecificExtended, + // IPv4AddressSpecificExtended, + // or FourOctetAsSpecificExtended. + ImportRt []*google_protobuf.Any `protobuf:"bytes,3,rep,name=import_rt,json=importRt" json:"import_rt,omitempty"` + // List of the Export Route Targets. Each must be one of + // TwoOctetAsSpecificExtended, + // IPv4AddressSpecificExtended, + // or FourOctetAsSpecificExtended. + ExportRt []*google_protobuf.Any `protobuf:"bytes,4,rep,name=export_rt,json=exportRt" json:"export_rt,omitempty"` + Id uint32 `protobuf:"varint,5,opt,name=id" json:"id,omitempty"` +} + +func (m *Vrf) Reset() { *m = Vrf{} } +func (m *Vrf) String() string { return proto.CompactTextString(m) } +func (*Vrf) ProtoMessage() {} +func (*Vrf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{135} } + +func (m *Vrf) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Vrf) GetRd() *google_protobuf.Any { + if m != nil { + return m.Rd + } + return nil +} + +func (m *Vrf) GetImportRt() []*google_protobuf.Any { + if m != nil { + return m.ImportRt + } + return nil +} + +func (m *Vrf) GetExportRt() []*google_protobuf.Any { + if m != nil { + return m.ExportRt + } + return nil +} + +func (m *Vrf) GetId() uint32 { + if m != nil { + return m.Id + } + return 0 +} + +type DefaultRouteDistance struct { + ExternalRouteDistance uint32 `protobuf:"varint,1,opt,name=external_route_distance,json=externalRouteDistance" json:"external_route_distance,omitempty"` + InternalRouteDistance uint32 `protobuf:"varint,2,opt,name=internal_route_distance,json=internalRouteDistance" json:"internal_route_distance,omitempty"` +} + +func (m *DefaultRouteDistance) Reset() { *m = DefaultRouteDistance{} } +func (m *DefaultRouteDistance) String() string { return proto.CompactTextString(m) } +func (*DefaultRouteDistance) ProtoMessage() {} +func (*DefaultRouteDistance) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{136} } + +func (m *DefaultRouteDistance) GetExternalRouteDistance() uint32 { + if m != nil { + return m.ExternalRouteDistance + } + return 0 +} + +func (m *DefaultRouteDistance) GetInternalRouteDistance() uint32 { + if m != nil { + return m.InternalRouteDistance + } + return 0 +} + +type Global struct { + As uint32 `protobuf:"varint,1,opt,name=as" json:"as,omitempty"` + RouterId string `protobuf:"bytes,2,opt,name=router_id,json=routerId" json:"router_id,omitempty"` + ListenPort int32 `protobuf:"varint,3,opt,name=listen_port,json=listenPort" json:"listen_port,omitempty"` + ListenAddresses []string `protobuf:"bytes,4,rep,name=listen_addresses,json=listenAddresses" json:"listen_addresses,omitempty"` + Families []uint32 `protobuf:"varint,5,rep,packed,name=families" json:"families,omitempty"` + UseMultiplePaths bool `protobuf:"varint,6,opt,name=use_multiple_paths,json=useMultiplePaths" json:"use_multiple_paths,omitempty"` + RouteSelectionOptions *RouteSelectionOptionsConfig `protobuf:"bytes,7,opt,name=route_selection_options,json=routeSelectionOptions" json:"route_selection_options,omitempty"` + DefaultRouteDistance *DefaultRouteDistance `protobuf:"bytes,8,opt,name=default_route_distance,json=defaultRouteDistance" json:"default_route_distance,omitempty"` + Confederation *Confederation `protobuf:"bytes,9,opt,name=confederation" json:"confederation,omitempty"` + GracefulRestart *GracefulRestart `protobuf:"bytes,10,opt,name=graceful_restart,json=gracefulRestart" json:"graceful_restart,omitempty"` + ApplyPolicy *ApplyPolicy `protobuf:"bytes,11,opt,name=apply_policy,json=applyPolicy" json:"apply_policy,omitempty"` +} + +func (m *Global) Reset() { *m = Global{} } +func (m *Global) String() string { return proto.CompactTextString(m) } +func (*Global) ProtoMessage() {} +func (*Global) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{137} } + +func (m *Global) GetAs() uint32 { + if m != nil { + return m.As + } + return 0 +} + +func (m *Global) GetRouterId() string { + if m != nil { + return m.RouterId + } + return "" +} + +func (m *Global) GetListenPort() int32 { + if m != nil { + return m.ListenPort + } + return 0 +} + +func (m *Global) GetListenAddresses() []string { + if m != nil { + return m.ListenAddresses + } + return nil +} + +func (m *Global) GetFamilies() []uint32 { + if m != nil { + return m.Families + } + return nil +} + +func (m *Global) GetUseMultiplePaths() bool { + if m != nil { + return m.UseMultiplePaths + } + return false +} + +func (m *Global) GetRouteSelectionOptions() *RouteSelectionOptionsConfig { + if m != nil { + return m.RouteSelectionOptions + } + return nil +} + +func (m *Global) GetDefaultRouteDistance() *DefaultRouteDistance { + if m != nil { + return m.DefaultRouteDistance + } + return nil +} + +func (m *Global) GetConfederation() *Confederation { + if m != nil { + return m.Confederation + } + return nil +} + +func (m *Global) GetGracefulRestart() *GracefulRestart { + if m != nil { + return m.GracefulRestart + } + return nil +} + +func (m *Global) GetApplyPolicy() *ApplyPolicy { + if m != nil { + return m.ApplyPolicy + } + return nil +} + +type Confederation struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` + Identifier uint32 `protobuf:"varint,2,opt,name=identifier" json:"identifier,omitempty"` + MemberAsList []uint32 `protobuf:"varint,3,rep,packed,name=member_as_list,json=memberAsList" json:"member_as_list,omitempty"` +} + +func (m *Confederation) Reset() { *m = Confederation{} } +func (m *Confederation) String() string { return proto.CompactTextString(m) } +func (*Confederation) ProtoMessage() {} +func (*Confederation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{138} } + +func (m *Confederation) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *Confederation) GetIdentifier() uint32 { + if m != nil { + return m.Identifier + } + return 0 +} + +func (m *Confederation) GetMemberAsList() []uint32 { + if m != nil { + return m.MemberAsList + } + return nil +} + +type RPKIConf struct { + Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + RemotePort uint32 `protobuf:"varint,2,opt,name=remote_port,json=remotePort" json:"remote_port,omitempty"` +} + +func (m *RPKIConf) Reset() { *m = RPKIConf{} } +func (m *RPKIConf) String() string { return proto.CompactTextString(m) } +func (*RPKIConf) ProtoMessage() {} +func (*RPKIConf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{139} } + +func (m *RPKIConf) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *RPKIConf) GetRemotePort() uint32 { + if m != nil { + return m.RemotePort + } + return 0 +} + +type RPKIState struct { + Uptime int64 `protobuf:"varint,1,opt,name=uptime" json:"uptime,omitempty"` + Downtime int64 `protobuf:"varint,2,opt,name=downtime" json:"downtime,omitempty"` + Up bool `protobuf:"varint,3,opt,name=up" json:"up,omitempty"` + RecordIpv4 uint32 `protobuf:"varint,4,opt,name=record_ipv4,json=recordIpv4" json:"record_ipv4,omitempty"` + RecordIpv6 uint32 `protobuf:"varint,5,opt,name=record_ipv6,json=recordIpv6" json:"record_ipv6,omitempty"` + PrefixIpv4 uint32 `protobuf:"varint,6,opt,name=prefix_ipv4,json=prefixIpv4" json:"prefix_ipv4,omitempty"` + PrefixIpv6 uint32 `protobuf:"varint,7,opt,name=prefix_ipv6,json=prefixIpv6" json:"prefix_ipv6,omitempty"` + Serial uint32 `protobuf:"varint,8,opt,name=serial" json:"serial,omitempty"` + ReceivedIpv4 int64 `protobuf:"varint,9,opt,name=received_ipv4,json=receivedIpv4" json:"received_ipv4,omitempty"` + ReceivedIpv6 int64 `protobuf:"varint,10,opt,name=received_ipv6,json=receivedIpv6" json:"received_ipv6,omitempty"` + SerialNotify int64 `protobuf:"varint,11,opt,name=serial_notify,json=serialNotify" json:"serial_notify,omitempty"` + CacheReset int64 `protobuf:"varint,12,opt,name=cache_reset,json=cacheReset" json:"cache_reset,omitempty"` + CacheResponse int64 `protobuf:"varint,13,opt,name=cache_response,json=cacheResponse" json:"cache_response,omitempty"` + EndOfData int64 `protobuf:"varint,14,opt,name=end_of_data,json=endOfData" json:"end_of_data,omitempty"` + Error int64 `protobuf:"varint,15,opt,name=error" json:"error,omitempty"` + SerialQuery int64 `protobuf:"varint,16,opt,name=serial_query,json=serialQuery" json:"serial_query,omitempty"` + ResetQuery int64 `protobuf:"varint,17,opt,name=reset_query,json=resetQuery" json:"reset_query,omitempty"` +} + +func (m *RPKIState) Reset() { *m = RPKIState{} } +func (m *RPKIState) String() string { return proto.CompactTextString(m) } +func (*RPKIState) ProtoMessage() {} +func (*RPKIState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{140} } + +func (m *RPKIState) GetUptime() int64 { + if m != nil { + return m.Uptime + } + return 0 +} + +func (m *RPKIState) GetDowntime() int64 { + if m != nil { + return m.Downtime + } + return 0 +} + +func (m *RPKIState) GetUp() bool { + if m != nil { + return m.Up + } + return false +} + +func (m *RPKIState) GetRecordIpv4() uint32 { + if m != nil { + return m.RecordIpv4 + } + return 0 +} + +func (m *RPKIState) GetRecordIpv6() uint32 { + if m != nil { + return m.RecordIpv6 + } + return 0 +} + +func (m *RPKIState) GetPrefixIpv4() uint32 { + if m != nil { + return m.PrefixIpv4 + } + return 0 +} + +func (m *RPKIState) GetPrefixIpv6() uint32 { + if m != nil { + return m.PrefixIpv6 + } + return 0 +} + +func (m *RPKIState) GetSerial() uint32 { + if m != nil { + return m.Serial + } + return 0 +} + +func (m *RPKIState) GetReceivedIpv4() int64 { + if m != nil { + return m.ReceivedIpv4 + } + return 0 +} + +func (m *RPKIState) GetReceivedIpv6() int64 { + if m != nil { + return m.ReceivedIpv6 + } + return 0 +} + +func (m *RPKIState) GetSerialNotify() int64 { + if m != nil { + return m.SerialNotify + } + return 0 +} + +func (m *RPKIState) GetCacheReset() int64 { + if m != nil { + return m.CacheReset + } + return 0 +} + +func (m *RPKIState) GetCacheResponse() int64 { + if m != nil { + return m.CacheResponse + } + return 0 +} + +func (m *RPKIState) GetEndOfData() int64 { + if m != nil { + return m.EndOfData + } + return 0 +} + +func (m *RPKIState) GetError() int64 { + if m != nil { + return m.Error + } + return 0 +} + +func (m *RPKIState) GetSerialQuery() int64 { + if m != nil { + return m.SerialQuery + } + return 0 +} + +func (m *RPKIState) GetResetQuery() int64 { + if m != nil { + return m.ResetQuery + } + return 0 +} + +type Rpki struct { + Conf *RPKIConf `protobuf:"bytes,1,opt,name=conf" json:"conf,omitempty"` + State *RPKIState `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` +} + +func (m *Rpki) Reset() { *m = Rpki{} } +func (m *Rpki) String() string { return proto.CompactTextString(m) } +func (*Rpki) ProtoMessage() {} +func (*Rpki) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{141} } + +func (m *Rpki) GetConf() *RPKIConf { + if m != nil { + return m.Conf + } + return nil +} + +func (m *Rpki) GetState() *RPKIState { + if m != nil { + return m.State + } + return nil +} + +func init() { + proto.RegisterType((*StartBgpRequest)(nil), "gobgpapi.StartBgpRequest") + proto.RegisterType((*StopBgpRequest)(nil), "gobgpapi.StopBgpRequest") + proto.RegisterType((*GetBgpRequest)(nil), "gobgpapi.GetBgpRequest") + proto.RegisterType((*GetBgpResponse)(nil), "gobgpapi.GetBgpResponse") + proto.RegisterType((*AddPeerRequest)(nil), "gobgpapi.AddPeerRequest") + proto.RegisterType((*DeletePeerRequest)(nil), "gobgpapi.DeletePeerRequest") + proto.RegisterType((*ListPeerRequest)(nil), "gobgpapi.ListPeerRequest") + proto.RegisterType((*ListPeerResponse)(nil), "gobgpapi.ListPeerResponse") + proto.RegisterType((*UpdatePeerRequest)(nil), "gobgpapi.UpdatePeerRequest") + proto.RegisterType((*UpdatePeerResponse)(nil), "gobgpapi.UpdatePeerResponse") + proto.RegisterType((*ResetPeerRequest)(nil), "gobgpapi.ResetPeerRequest") + proto.RegisterType((*ShutdownPeerRequest)(nil), "gobgpapi.ShutdownPeerRequest") + proto.RegisterType((*EnablePeerRequest)(nil), "gobgpapi.EnablePeerRequest") + proto.RegisterType((*DisablePeerRequest)(nil), "gobgpapi.DisablePeerRequest") + proto.RegisterType((*MonitorPeerRequest)(nil), "gobgpapi.MonitorPeerRequest") + proto.RegisterType((*MonitorPeerResponse)(nil), "gobgpapi.MonitorPeerResponse") + proto.RegisterType((*AddPeerGroupRequest)(nil), "gobgpapi.AddPeerGroupRequest") + proto.RegisterType((*DeletePeerGroupRequest)(nil), "gobgpapi.DeletePeerGroupRequest") + proto.RegisterType((*UpdatePeerGroupRequest)(nil), "gobgpapi.UpdatePeerGroupRequest") + proto.RegisterType((*UpdatePeerGroupResponse)(nil), "gobgpapi.UpdatePeerGroupResponse") + proto.RegisterType((*AddDynamicNeighborRequest)(nil), "gobgpapi.AddDynamicNeighborRequest") + proto.RegisterType((*AddPathRequest)(nil), "gobgpapi.AddPathRequest") + proto.RegisterType((*AddPathResponse)(nil), "gobgpapi.AddPathResponse") + proto.RegisterType((*DeletePathRequest)(nil), "gobgpapi.DeletePathRequest") + proto.RegisterType((*ListPathRequest)(nil), "gobgpapi.ListPathRequest") + proto.RegisterType((*ListPathResponse)(nil), "gobgpapi.ListPathResponse") + proto.RegisterType((*AddPathStreamRequest)(nil), "gobgpapi.AddPathStreamRequest") + proto.RegisterType((*GetTableRequest)(nil), "gobgpapi.GetTableRequest") + proto.RegisterType((*GetTableResponse)(nil), "gobgpapi.GetTableResponse") + proto.RegisterType((*MonitorTableRequest)(nil), "gobgpapi.MonitorTableRequest") + proto.RegisterType((*MonitorTableResponse)(nil), "gobgpapi.MonitorTableResponse") + proto.RegisterType((*AddVrfRequest)(nil), "gobgpapi.AddVrfRequest") + proto.RegisterType((*DeleteVrfRequest)(nil), "gobgpapi.DeleteVrfRequest") + proto.RegisterType((*ListVrfRequest)(nil), "gobgpapi.ListVrfRequest") + proto.RegisterType((*ListVrfResponse)(nil), "gobgpapi.ListVrfResponse") + proto.RegisterType((*AddPolicyRequest)(nil), "gobgpapi.AddPolicyRequest") + proto.RegisterType((*DeletePolicyRequest)(nil), "gobgpapi.DeletePolicyRequest") + proto.RegisterType((*ListPolicyRequest)(nil), "gobgpapi.ListPolicyRequest") + proto.RegisterType((*ListPolicyResponse)(nil), "gobgpapi.ListPolicyResponse") + proto.RegisterType((*SetPoliciesRequest)(nil), "gobgpapi.SetPoliciesRequest") + proto.RegisterType((*AddDefinedSetRequest)(nil), "gobgpapi.AddDefinedSetRequest") + proto.RegisterType((*DeleteDefinedSetRequest)(nil), "gobgpapi.DeleteDefinedSetRequest") + proto.RegisterType((*ListDefinedSetRequest)(nil), "gobgpapi.ListDefinedSetRequest") + proto.RegisterType((*ListDefinedSetResponse)(nil), "gobgpapi.ListDefinedSetResponse") + proto.RegisterType((*AddStatementRequest)(nil), "gobgpapi.AddStatementRequest") + proto.RegisterType((*DeleteStatementRequest)(nil), "gobgpapi.DeleteStatementRequest") + proto.RegisterType((*ListStatementRequest)(nil), "gobgpapi.ListStatementRequest") + proto.RegisterType((*ListStatementResponse)(nil), "gobgpapi.ListStatementResponse") + proto.RegisterType((*AddPolicyAssignmentRequest)(nil), "gobgpapi.AddPolicyAssignmentRequest") + proto.RegisterType((*DeletePolicyAssignmentRequest)(nil), "gobgpapi.DeletePolicyAssignmentRequest") + proto.RegisterType((*ListPolicyAssignmentRequest)(nil), "gobgpapi.ListPolicyAssignmentRequest") + proto.RegisterType((*ListPolicyAssignmentResponse)(nil), "gobgpapi.ListPolicyAssignmentResponse") + proto.RegisterType((*SetPolicyAssignmentRequest)(nil), "gobgpapi.SetPolicyAssignmentRequest") + proto.RegisterType((*AddRpkiRequest)(nil), "gobgpapi.AddRpkiRequest") + proto.RegisterType((*DeleteRpkiRequest)(nil), "gobgpapi.DeleteRpkiRequest") + proto.RegisterType((*ListRpkiRequest)(nil), "gobgpapi.ListRpkiRequest") + proto.RegisterType((*ListRpkiResponse)(nil), "gobgpapi.ListRpkiResponse") + proto.RegisterType((*EnableRpkiRequest)(nil), "gobgpapi.EnableRpkiRequest") + proto.RegisterType((*DisableRpkiRequest)(nil), "gobgpapi.DisableRpkiRequest") + proto.RegisterType((*ResetRpkiRequest)(nil), "gobgpapi.ResetRpkiRequest") + proto.RegisterType((*ListRpkiTableRequest)(nil), "gobgpapi.ListRpkiTableRequest") + proto.RegisterType((*ListRpkiTableResponse)(nil), "gobgpapi.ListRpkiTableResponse") + proto.RegisterType((*EnableZebraRequest)(nil), "gobgpapi.EnableZebraRequest") + proto.RegisterType((*EnableMrtRequest)(nil), "gobgpapi.EnableMrtRequest") + proto.RegisterType((*DisableMrtRequest)(nil), "gobgpapi.DisableMrtRequest") + proto.RegisterType((*AddBmpRequest)(nil), "gobgpapi.AddBmpRequest") + proto.RegisterType((*DeleteBmpRequest)(nil), "gobgpapi.DeleteBmpRequest") + proto.RegisterType((*Family)(nil), "gobgpapi.Family") + proto.RegisterType((*RPKIValidation)(nil), "gobgpapi.RPKIValidation") + proto.RegisterType((*Path)(nil), "gobgpapi.Path") + proto.RegisterType((*Destination)(nil), "gobgpapi.Destination") + proto.RegisterType((*TableLookupPrefix)(nil), "gobgpapi.TableLookupPrefix") + proto.RegisterType((*Peer)(nil), "gobgpapi.Peer") + proto.RegisterType((*PeerGroup)(nil), "gobgpapi.PeerGroup") + proto.RegisterType((*DynamicNeighbor)(nil), "gobgpapi.DynamicNeighbor") + proto.RegisterType((*ApplyPolicy)(nil), "gobgpapi.ApplyPolicy") + proto.RegisterType((*PrefixLimit)(nil), "gobgpapi.PrefixLimit") + proto.RegisterType((*PeerConf)(nil), "gobgpapi.PeerConf") + proto.RegisterType((*PeerGroupConf)(nil), "gobgpapi.PeerGroupConf") + proto.RegisterType((*PeerGroupState)(nil), "gobgpapi.PeerGroupState") + proto.RegisterType((*EbgpMultihop)(nil), "gobgpapi.EbgpMultihop") + proto.RegisterType((*RouteReflector)(nil), "gobgpapi.RouteReflector") + proto.RegisterType((*PeerState)(nil), "gobgpapi.PeerState") + proto.RegisterType((*Messages)(nil), "gobgpapi.Messages") + proto.RegisterType((*Message)(nil), "gobgpapi.Message") + proto.RegisterType((*Queues)(nil), "gobgpapi.Queues") + proto.RegisterType((*Timers)(nil), "gobgpapi.Timers") + proto.RegisterType((*TimersConfig)(nil), "gobgpapi.TimersConfig") + proto.RegisterType((*TimersState)(nil), "gobgpapi.TimersState") + proto.RegisterType((*Transport)(nil), "gobgpapi.Transport") + proto.RegisterType((*RouteServer)(nil), "gobgpapi.RouteServer") + proto.RegisterType((*GracefulRestart)(nil), "gobgpapi.GracefulRestart") + proto.RegisterType((*MpGracefulRestartConfig)(nil), "gobgpapi.MpGracefulRestartConfig") + proto.RegisterType((*MpGracefulRestartState)(nil), "gobgpapi.MpGracefulRestartState") + proto.RegisterType((*MpGracefulRestart)(nil), "gobgpapi.MpGracefulRestart") + proto.RegisterType((*AfiSafiConfig)(nil), "gobgpapi.AfiSafiConfig") + proto.RegisterType((*AfiSafiState)(nil), "gobgpapi.AfiSafiState") + proto.RegisterType((*RouteSelectionOptionsConfig)(nil), "gobgpapi.RouteSelectionOptionsConfig") + proto.RegisterType((*RouteSelectionOptionsState)(nil), "gobgpapi.RouteSelectionOptionsState") + proto.RegisterType((*RouteSelectionOptions)(nil), "gobgpapi.RouteSelectionOptions") + proto.RegisterType((*UseMultiplePathsConfig)(nil), "gobgpapi.UseMultiplePathsConfig") + proto.RegisterType((*UseMultiplePathsState)(nil), "gobgpapi.UseMultiplePathsState") + proto.RegisterType((*EbgpConfig)(nil), "gobgpapi.EbgpConfig") + proto.RegisterType((*EbgpState)(nil), "gobgpapi.EbgpState") + proto.RegisterType((*Ebgp)(nil), "gobgpapi.Ebgp") + proto.RegisterType((*IbgpConfig)(nil), "gobgpapi.IbgpConfig") + proto.RegisterType((*IbgpState)(nil), "gobgpapi.IbgpState") + proto.RegisterType((*Ibgp)(nil), "gobgpapi.Ibgp") + proto.RegisterType((*UseMultiplePaths)(nil), "gobgpapi.UseMultiplePaths") + proto.RegisterType((*RouteTargetMembershipConfig)(nil), "gobgpapi.RouteTargetMembershipConfig") + proto.RegisterType((*RouteTargetMembershipState)(nil), "gobgpapi.RouteTargetMembershipState") + proto.RegisterType((*RouteTargetMembership)(nil), "gobgpapi.RouteTargetMembership") + proto.RegisterType((*LongLivedGracefulRestartConfig)(nil), "gobgpapi.LongLivedGracefulRestartConfig") + proto.RegisterType((*LongLivedGracefulRestartState)(nil), "gobgpapi.LongLivedGracefulRestartState") + proto.RegisterType((*LongLivedGracefulRestart)(nil), "gobgpapi.LongLivedGracefulRestart") + proto.RegisterType((*AfiSafi)(nil), "gobgpapi.AfiSafi") + proto.RegisterType((*AddPathsConfig)(nil), "gobgpapi.AddPathsConfig") + proto.RegisterType((*AddPathsState)(nil), "gobgpapi.AddPathsState") + proto.RegisterType((*AddPaths)(nil), "gobgpapi.AddPaths") + proto.RegisterType((*Prefix)(nil), "gobgpapi.Prefix") + proto.RegisterType((*DefinedSet)(nil), "gobgpapi.DefinedSet") + proto.RegisterType((*MatchSet)(nil), "gobgpapi.MatchSet") + proto.RegisterType((*AsPathLength)(nil), "gobgpapi.AsPathLength") + proto.RegisterType((*Conditions)(nil), "gobgpapi.Conditions") + proto.RegisterType((*CommunityAction)(nil), "gobgpapi.CommunityAction") + proto.RegisterType((*MedAction)(nil), "gobgpapi.MedAction") + proto.RegisterType((*AsPrependAction)(nil), "gobgpapi.AsPrependAction") + proto.RegisterType((*NexthopAction)(nil), "gobgpapi.NexthopAction") + proto.RegisterType((*LocalPrefAction)(nil), "gobgpapi.LocalPrefAction") + proto.RegisterType((*Actions)(nil), "gobgpapi.Actions") + proto.RegisterType((*Statement)(nil), "gobgpapi.Statement") + proto.RegisterType((*Policy)(nil), "gobgpapi.Policy") + proto.RegisterType((*PolicyAssignment)(nil), "gobgpapi.PolicyAssignment") + proto.RegisterType((*RoutingPolicy)(nil), "gobgpapi.RoutingPolicy") + proto.RegisterType((*Roa)(nil), "gobgpapi.Roa") + proto.RegisterType((*Vrf)(nil), "gobgpapi.Vrf") + proto.RegisterType((*DefaultRouteDistance)(nil), "gobgpapi.DefaultRouteDistance") + proto.RegisterType((*Global)(nil), "gobgpapi.Global") + proto.RegisterType((*Confederation)(nil), "gobgpapi.Confederation") + proto.RegisterType((*RPKIConf)(nil), "gobgpapi.RPKIConf") + proto.RegisterType((*RPKIState)(nil), "gobgpapi.RPKIState") + proto.RegisterType((*Rpki)(nil), "gobgpapi.Rpki") + proto.RegisterEnum("gobgpapi.Resource", Resource_name, Resource_value) + proto.RegisterEnum("gobgpapi.TableLookupOption", TableLookupOption_name, TableLookupOption_value) + proto.RegisterEnum("gobgpapi.DefinedType", DefinedType_name, DefinedType_value) + proto.RegisterEnum("gobgpapi.MatchType", MatchType_name, MatchType_value) + proto.RegisterEnum("gobgpapi.AsPathLengthType", AsPathLengthType_name, AsPathLengthType_value) + proto.RegisterEnum("gobgpapi.RouteAction", RouteAction_name, RouteAction_value) + proto.RegisterEnum("gobgpapi.CommunityActionType", CommunityActionType_name, CommunityActionType_value) + proto.RegisterEnum("gobgpapi.MedActionType", MedActionType_name, MedActionType_value) + proto.RegisterEnum("gobgpapi.PolicyDirection", PolicyDirection_name, PolicyDirection_value) + proto.RegisterEnum("gobgpapi.ResetPeerRequest_SoftResetDirection", ResetPeerRequest_SoftResetDirection_name, ResetPeerRequest_SoftResetDirection_value) + proto.RegisterEnum("gobgpapi.AddBmpRequest_MonitoringPolicy", AddBmpRequest_MonitoringPolicy_name, AddBmpRequest_MonitoringPolicy_value) + proto.RegisterEnum("gobgpapi.Family_Afi", Family_Afi_name, Family_Afi_value) + proto.RegisterEnum("gobgpapi.Family_Safi", Family_Safi_name, Family_Safi_value) + proto.RegisterEnum("gobgpapi.RPKIValidation_State", RPKIValidation_State_name, RPKIValidation_State_value) + proto.RegisterEnum("gobgpapi.RPKIValidation_Reason", RPKIValidation_Reason_name, RPKIValidation_Reason_value) + proto.RegisterEnum("gobgpapi.PeerConf_RemovePrivateAs", PeerConf_RemovePrivateAs_name, PeerConf_RemovePrivateAs_value) + proto.RegisterEnum("gobgpapi.PeerGroupConf_RemovePrivateAs", PeerGroupConf_RemovePrivateAs_name, PeerGroupConf_RemovePrivateAs_value) + proto.RegisterEnum("gobgpapi.PeerGroupState_RemovePrivateAs", PeerGroupState_RemovePrivateAs_name, PeerGroupState_RemovePrivateAs_value) + proto.RegisterEnum("gobgpapi.PeerState_SessionState", PeerState_SessionState_name, PeerState_SessionState_value) + proto.RegisterEnum("gobgpapi.PeerState_AdminState", PeerState_AdminState_name, PeerState_AdminState_value) + proto.RegisterEnum("gobgpapi.Conditions_RouteType", Conditions_RouteType_name, Conditions_RouteType_value) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for GobgpApi service + +type GobgpApiClient interface { + StartBgp(ctx context.Context, in *StartBgpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + StopBgp(ctx context.Context, in *StopBgpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + GetBgp(ctx context.Context, in *GetBgpRequest, opts ...grpc.CallOption) (*GetBgpResponse, error) + AddPeer(ctx context.Context, in *AddPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeletePeer(ctx context.Context, in *DeletePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListPeer(ctx context.Context, in *ListPeerRequest, opts ...grpc.CallOption) (GobgpApi_ListPeerClient, error) + UpdatePeer(ctx context.Context, in *UpdatePeerRequest, opts ...grpc.CallOption) (*UpdatePeerResponse, error) + ResetPeer(ctx context.Context, in *ResetPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ShutdownPeer(ctx context.Context, in *ShutdownPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + EnablePeer(ctx context.Context, in *EnablePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DisablePeer(ctx context.Context, in *DisablePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + MonitorPeer(ctx context.Context, in *MonitorPeerRequest, opts ...grpc.CallOption) (GobgpApi_MonitorPeerClient, error) + AddPeerGroup(ctx context.Context, in *AddPeerGroupRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeletePeerGroup(ctx context.Context, in *DeletePeerGroupRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + UpdatePeerGroup(ctx context.Context, in *UpdatePeerGroupRequest, opts ...grpc.CallOption) (*UpdatePeerGroupResponse, error) + AddDynamicNeighbor(ctx context.Context, in *AddDynamicNeighborRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + AddPath(ctx context.Context, in *AddPathRequest, opts ...grpc.CallOption) (*AddPathResponse, error) + DeletePath(ctx context.Context, in *DeletePathRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListPath(ctx context.Context, in *ListPathRequest, opts ...grpc.CallOption) (GobgpApi_ListPathClient, error) + AddPathStream(ctx context.Context, opts ...grpc.CallOption) (GobgpApi_AddPathStreamClient, error) + GetTable(ctx context.Context, in *GetTableRequest, opts ...grpc.CallOption) (*GetTableResponse, error) + MonitorTable(ctx context.Context, in *MonitorTableRequest, opts ...grpc.CallOption) (GobgpApi_MonitorTableClient, error) + AddVrf(ctx context.Context, in *AddVrfRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeleteVrf(ctx context.Context, in *DeleteVrfRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListVrf(ctx context.Context, in *ListVrfRequest, opts ...grpc.CallOption) (GobgpApi_ListVrfClient, error) + AddPolicy(ctx context.Context, in *AddPolicyRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeletePolicy(ctx context.Context, in *DeletePolicyRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListPolicy(ctx context.Context, in *ListPolicyRequest, opts ...grpc.CallOption) (GobgpApi_ListPolicyClient, error) + SetPolicies(ctx context.Context, in *SetPoliciesRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + AddDefinedSet(ctx context.Context, in *AddDefinedSetRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeleteDefinedSet(ctx context.Context, in *DeleteDefinedSetRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListDefinedSet(ctx context.Context, in *ListDefinedSetRequest, opts ...grpc.CallOption) (GobgpApi_ListDefinedSetClient, error) + AddStatement(ctx context.Context, in *AddStatementRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeleteStatement(ctx context.Context, in *DeleteStatementRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListStatement(ctx context.Context, in *ListStatementRequest, opts ...grpc.CallOption) (GobgpApi_ListStatementClient, error) + AddPolicyAssignment(ctx context.Context, in *AddPolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeletePolicyAssignment(ctx context.Context, in *DeletePolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListPolicyAssignment(ctx context.Context, in *ListPolicyAssignmentRequest, opts ...grpc.CallOption) (GobgpApi_ListPolicyAssignmentClient, error) + SetPolicyAssignment(ctx context.Context, in *SetPolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + AddRpki(ctx context.Context, in *AddRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeleteRpki(ctx context.Context, in *DeleteRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListRpki(ctx context.Context, in *ListRpkiRequest, opts ...grpc.CallOption) (GobgpApi_ListRpkiClient, error) + EnableRpki(ctx context.Context, in *EnableRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DisableRpki(ctx context.Context, in *DisableRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ResetRpki(ctx context.Context, in *ResetRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + ListRpkiTable(ctx context.Context, in *ListRpkiTableRequest, opts ...grpc.CallOption) (GobgpApi_ListRpkiTableClient, error) + EnableZebra(ctx context.Context, in *EnableZebraRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + EnableMrt(ctx context.Context, in *EnableMrtRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DisableMrt(ctx context.Context, in *DisableMrtRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + AddBmp(ctx context.Context, in *AddBmpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + DeleteBmp(ctx context.Context, in *DeleteBmpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) +} + +type gobgpApiClient struct { + cc *grpc.ClientConn +} + +func NewGobgpApiClient(cc *grpc.ClientConn) GobgpApiClient { + return &gobgpApiClient{cc} +} + +func (c *gobgpApiClient) StartBgp(ctx context.Context, in *StartBgpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/StartBgp", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) StopBgp(ctx context.Context, in *StopBgpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/StopBgp", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) GetBgp(ctx context.Context, in *GetBgpRequest, opts ...grpc.CallOption) (*GetBgpResponse, error) { + out := new(GetBgpResponse) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/GetBgp", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddPeer(ctx context.Context, in *AddPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddPeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeletePeer(ctx context.Context, in *DeletePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeletePeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListPeer(ctx context.Context, in *ListPeerRequest, opts ...grpc.CallOption) (GobgpApi_ListPeerClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[0], c.cc, "/gobgpapi.GobgpApi/ListPeer", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListPeerClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListPeerClient interface { + Recv() (*ListPeerResponse, error) + grpc.ClientStream +} + +type gobgpApiListPeerClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListPeerClient) Recv() (*ListPeerResponse, error) { + m := new(ListPeerResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) UpdatePeer(ctx context.Context, in *UpdatePeerRequest, opts ...grpc.CallOption) (*UpdatePeerResponse, error) { + out := new(UpdatePeerResponse) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/UpdatePeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ResetPeer(ctx context.Context, in *ResetPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/ResetPeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ShutdownPeer(ctx context.Context, in *ShutdownPeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/ShutdownPeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) EnablePeer(ctx context.Context, in *EnablePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/EnablePeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DisablePeer(ctx context.Context, in *DisablePeerRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DisablePeer", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) MonitorPeer(ctx context.Context, in *MonitorPeerRequest, opts ...grpc.CallOption) (GobgpApi_MonitorPeerClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[1], c.cc, "/gobgpapi.GobgpApi/MonitorPeer", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiMonitorPeerClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_MonitorPeerClient interface { + Recv() (*MonitorPeerResponse, error) + grpc.ClientStream +} + +type gobgpApiMonitorPeerClient struct { + grpc.ClientStream +} + +func (x *gobgpApiMonitorPeerClient) Recv() (*MonitorPeerResponse, error) { + m := new(MonitorPeerResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddPeerGroup(ctx context.Context, in *AddPeerGroupRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddPeerGroup", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeletePeerGroup(ctx context.Context, in *DeletePeerGroupRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeletePeerGroup", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) UpdatePeerGroup(ctx context.Context, in *UpdatePeerGroupRequest, opts ...grpc.CallOption) (*UpdatePeerGroupResponse, error) { + out := new(UpdatePeerGroupResponse) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/UpdatePeerGroup", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddDynamicNeighbor(ctx context.Context, in *AddDynamicNeighborRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddDynamicNeighbor", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddPath(ctx context.Context, in *AddPathRequest, opts ...grpc.CallOption) (*AddPathResponse, error) { + out := new(AddPathResponse) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddPath", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeletePath(ctx context.Context, in *DeletePathRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeletePath", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListPath(ctx context.Context, in *ListPathRequest, opts ...grpc.CallOption) (GobgpApi_ListPathClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[2], c.cc, "/gobgpapi.GobgpApi/ListPath", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListPathClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListPathClient interface { + Recv() (*ListPathResponse, error) + grpc.ClientStream +} + +type gobgpApiListPathClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListPathClient) Recv() (*ListPathResponse, error) { + m := new(ListPathResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddPathStream(ctx context.Context, opts ...grpc.CallOption) (GobgpApi_AddPathStreamClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[3], c.cc, "/gobgpapi.GobgpApi/AddPathStream", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiAddPathStreamClient{stream} + return x, nil +} + +type GobgpApi_AddPathStreamClient interface { + Send(*AddPathStreamRequest) error + CloseAndRecv() (*google_protobuf1.Empty, error) + grpc.ClientStream +} + +type gobgpApiAddPathStreamClient struct { + grpc.ClientStream +} + +func (x *gobgpApiAddPathStreamClient) Send(m *AddPathStreamRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *gobgpApiAddPathStreamClient) CloseAndRecv() (*google_protobuf1.Empty, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(google_protobuf1.Empty) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) GetTable(ctx context.Context, in *GetTableRequest, opts ...grpc.CallOption) (*GetTableResponse, error) { + out := new(GetTableResponse) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/GetTable", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) MonitorTable(ctx context.Context, in *MonitorTableRequest, opts ...grpc.CallOption) (GobgpApi_MonitorTableClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[4], c.cc, "/gobgpapi.GobgpApi/MonitorTable", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiMonitorTableClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_MonitorTableClient interface { + Recv() (*MonitorTableResponse, error) + grpc.ClientStream +} + +type gobgpApiMonitorTableClient struct { + grpc.ClientStream +} + +func (x *gobgpApiMonitorTableClient) Recv() (*MonitorTableResponse, error) { + m := new(MonitorTableResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddVrf(ctx context.Context, in *AddVrfRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddVrf", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeleteVrf(ctx context.Context, in *DeleteVrfRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeleteVrf", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListVrf(ctx context.Context, in *ListVrfRequest, opts ...grpc.CallOption) (GobgpApi_ListVrfClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[5], c.cc, "/gobgpapi.GobgpApi/ListVrf", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListVrfClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListVrfClient interface { + Recv() (*ListVrfResponse, error) + grpc.ClientStream +} + +type gobgpApiListVrfClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListVrfClient) Recv() (*ListVrfResponse, error) { + m := new(ListVrfResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddPolicy(ctx context.Context, in *AddPolicyRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddPolicy", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeletePolicy(ctx context.Context, in *DeletePolicyRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeletePolicy", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListPolicy(ctx context.Context, in *ListPolicyRequest, opts ...grpc.CallOption) (GobgpApi_ListPolicyClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[6], c.cc, "/gobgpapi.GobgpApi/ListPolicy", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListPolicyClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListPolicyClient interface { + Recv() (*ListPolicyResponse, error) + grpc.ClientStream +} + +type gobgpApiListPolicyClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListPolicyClient) Recv() (*ListPolicyResponse, error) { + m := new(ListPolicyResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) SetPolicies(ctx context.Context, in *SetPoliciesRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/SetPolicies", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddDefinedSet(ctx context.Context, in *AddDefinedSetRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddDefinedSet", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeleteDefinedSet(ctx context.Context, in *DeleteDefinedSetRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeleteDefinedSet", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListDefinedSet(ctx context.Context, in *ListDefinedSetRequest, opts ...grpc.CallOption) (GobgpApi_ListDefinedSetClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[7], c.cc, "/gobgpapi.GobgpApi/ListDefinedSet", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListDefinedSetClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListDefinedSetClient interface { + Recv() (*ListDefinedSetResponse, error) + grpc.ClientStream +} + +type gobgpApiListDefinedSetClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListDefinedSetClient) Recv() (*ListDefinedSetResponse, error) { + m := new(ListDefinedSetResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddStatement(ctx context.Context, in *AddStatementRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddStatement", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeleteStatement(ctx context.Context, in *DeleteStatementRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeleteStatement", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListStatement(ctx context.Context, in *ListStatementRequest, opts ...grpc.CallOption) (GobgpApi_ListStatementClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[8], c.cc, "/gobgpapi.GobgpApi/ListStatement", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListStatementClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListStatementClient interface { + Recv() (*ListStatementResponse, error) + grpc.ClientStream +} + +type gobgpApiListStatementClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListStatementClient) Recv() (*ListStatementResponse, error) { + m := new(ListStatementResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) AddPolicyAssignment(ctx context.Context, in *AddPolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddPolicyAssignment", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeletePolicyAssignment(ctx context.Context, in *DeletePolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeletePolicyAssignment", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListPolicyAssignment(ctx context.Context, in *ListPolicyAssignmentRequest, opts ...grpc.CallOption) (GobgpApi_ListPolicyAssignmentClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[9], c.cc, "/gobgpapi.GobgpApi/ListPolicyAssignment", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListPolicyAssignmentClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListPolicyAssignmentClient interface { + Recv() (*ListPolicyAssignmentResponse, error) + grpc.ClientStream +} + +type gobgpApiListPolicyAssignmentClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListPolicyAssignmentClient) Recv() (*ListPolicyAssignmentResponse, error) { + m := new(ListPolicyAssignmentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) SetPolicyAssignment(ctx context.Context, in *SetPolicyAssignmentRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/SetPolicyAssignment", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddRpki(ctx context.Context, in *AddRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddRpki", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeleteRpki(ctx context.Context, in *DeleteRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeleteRpki", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListRpki(ctx context.Context, in *ListRpkiRequest, opts ...grpc.CallOption) (GobgpApi_ListRpkiClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[10], c.cc, "/gobgpapi.GobgpApi/ListRpki", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListRpkiClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListRpkiClient interface { + Recv() (*ListRpkiResponse, error) + grpc.ClientStream +} + +type gobgpApiListRpkiClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListRpkiClient) Recv() (*ListRpkiResponse, error) { + m := new(ListRpkiResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) EnableRpki(ctx context.Context, in *EnableRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/EnableRpki", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DisableRpki(ctx context.Context, in *DisableRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DisableRpki", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ResetRpki(ctx context.Context, in *ResetRpkiRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/ResetRpki", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) ListRpkiTable(ctx context.Context, in *ListRpkiTableRequest, opts ...grpc.CallOption) (GobgpApi_ListRpkiTableClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GobgpApi_serviceDesc.Streams[11], c.cc, "/gobgpapi.GobgpApi/ListRpkiTable", opts...) + if err != nil { + return nil, err + } + x := &gobgpApiListRpkiTableClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GobgpApi_ListRpkiTableClient interface { + Recv() (*ListRpkiTableResponse, error) + grpc.ClientStream +} + +type gobgpApiListRpkiTableClient struct { + grpc.ClientStream +} + +func (x *gobgpApiListRpkiTableClient) Recv() (*ListRpkiTableResponse, error) { + m := new(ListRpkiTableResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *gobgpApiClient) EnableZebra(ctx context.Context, in *EnableZebraRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/EnableZebra", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) EnableMrt(ctx context.Context, in *EnableMrtRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/EnableMrt", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DisableMrt(ctx context.Context, in *DisableMrtRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DisableMrt", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) AddBmp(ctx context.Context, in *AddBmpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/AddBmp", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gobgpApiClient) DeleteBmp(ctx context.Context, in *DeleteBmpRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/gobgpapi.GobgpApi/DeleteBmp", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for GobgpApi service + +type GobgpApiServer interface { + StartBgp(context.Context, *StartBgpRequest) (*google_protobuf1.Empty, error) + StopBgp(context.Context, *StopBgpRequest) (*google_protobuf1.Empty, error) + GetBgp(context.Context, *GetBgpRequest) (*GetBgpResponse, error) + AddPeer(context.Context, *AddPeerRequest) (*google_protobuf1.Empty, error) + DeletePeer(context.Context, *DeletePeerRequest) (*google_protobuf1.Empty, error) + ListPeer(*ListPeerRequest, GobgpApi_ListPeerServer) error + UpdatePeer(context.Context, *UpdatePeerRequest) (*UpdatePeerResponse, error) + ResetPeer(context.Context, *ResetPeerRequest) (*google_protobuf1.Empty, error) + ShutdownPeer(context.Context, *ShutdownPeerRequest) (*google_protobuf1.Empty, error) + EnablePeer(context.Context, *EnablePeerRequest) (*google_protobuf1.Empty, error) + DisablePeer(context.Context, *DisablePeerRequest) (*google_protobuf1.Empty, error) + MonitorPeer(*MonitorPeerRequest, GobgpApi_MonitorPeerServer) error + AddPeerGroup(context.Context, *AddPeerGroupRequest) (*google_protobuf1.Empty, error) + DeletePeerGroup(context.Context, *DeletePeerGroupRequest) (*google_protobuf1.Empty, error) + UpdatePeerGroup(context.Context, *UpdatePeerGroupRequest) (*UpdatePeerGroupResponse, error) + AddDynamicNeighbor(context.Context, *AddDynamicNeighborRequest) (*google_protobuf1.Empty, error) + AddPath(context.Context, *AddPathRequest) (*AddPathResponse, error) + DeletePath(context.Context, *DeletePathRequest) (*google_protobuf1.Empty, error) + ListPath(*ListPathRequest, GobgpApi_ListPathServer) error + AddPathStream(GobgpApi_AddPathStreamServer) error + GetTable(context.Context, *GetTableRequest) (*GetTableResponse, error) + MonitorTable(*MonitorTableRequest, GobgpApi_MonitorTableServer) error + AddVrf(context.Context, *AddVrfRequest) (*google_protobuf1.Empty, error) + DeleteVrf(context.Context, *DeleteVrfRequest) (*google_protobuf1.Empty, error) + ListVrf(*ListVrfRequest, GobgpApi_ListVrfServer) error + AddPolicy(context.Context, *AddPolicyRequest) (*google_protobuf1.Empty, error) + DeletePolicy(context.Context, *DeletePolicyRequest) (*google_protobuf1.Empty, error) + ListPolicy(*ListPolicyRequest, GobgpApi_ListPolicyServer) error + SetPolicies(context.Context, *SetPoliciesRequest) (*google_protobuf1.Empty, error) + AddDefinedSet(context.Context, *AddDefinedSetRequest) (*google_protobuf1.Empty, error) + DeleteDefinedSet(context.Context, *DeleteDefinedSetRequest) (*google_protobuf1.Empty, error) + ListDefinedSet(*ListDefinedSetRequest, GobgpApi_ListDefinedSetServer) error + AddStatement(context.Context, *AddStatementRequest) (*google_protobuf1.Empty, error) + DeleteStatement(context.Context, *DeleteStatementRequest) (*google_protobuf1.Empty, error) + ListStatement(*ListStatementRequest, GobgpApi_ListStatementServer) error + AddPolicyAssignment(context.Context, *AddPolicyAssignmentRequest) (*google_protobuf1.Empty, error) + DeletePolicyAssignment(context.Context, *DeletePolicyAssignmentRequest) (*google_protobuf1.Empty, error) + ListPolicyAssignment(*ListPolicyAssignmentRequest, GobgpApi_ListPolicyAssignmentServer) error + SetPolicyAssignment(context.Context, *SetPolicyAssignmentRequest) (*google_protobuf1.Empty, error) + AddRpki(context.Context, *AddRpkiRequest) (*google_protobuf1.Empty, error) + DeleteRpki(context.Context, *DeleteRpkiRequest) (*google_protobuf1.Empty, error) + ListRpki(*ListRpkiRequest, GobgpApi_ListRpkiServer) error + EnableRpki(context.Context, *EnableRpkiRequest) (*google_protobuf1.Empty, error) + DisableRpki(context.Context, *DisableRpkiRequest) (*google_protobuf1.Empty, error) + ResetRpki(context.Context, *ResetRpkiRequest) (*google_protobuf1.Empty, error) + ListRpkiTable(*ListRpkiTableRequest, GobgpApi_ListRpkiTableServer) error + EnableZebra(context.Context, *EnableZebraRequest) (*google_protobuf1.Empty, error) + EnableMrt(context.Context, *EnableMrtRequest) (*google_protobuf1.Empty, error) + DisableMrt(context.Context, *DisableMrtRequest) (*google_protobuf1.Empty, error) + AddBmp(context.Context, *AddBmpRequest) (*google_protobuf1.Empty, error) + DeleteBmp(context.Context, *DeleteBmpRequest) (*google_protobuf1.Empty, error) +} + +func RegisterGobgpApiServer(s *grpc.Server, srv GobgpApiServer) { + s.RegisterService(&_GobgpApi_serviceDesc, srv) +} + +func _GobgpApi_StartBgp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartBgpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).StartBgp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/StartBgp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).StartBgp(ctx, req.(*StartBgpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_StopBgp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopBgpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).StopBgp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/StopBgp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).StopBgp(ctx, req.(*StopBgpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_GetBgp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBgpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).GetBgp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/GetBgp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).GetBgp(ctx, req.(*GetBgpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddPeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddPeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddPeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddPeer(ctx, req.(*AddPeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeletePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeletePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeletePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeletePeer(ctx, req.(*DeletePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListPeer_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListPeerRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListPeer(m, &gobgpApiListPeerServer{stream}) +} + +type GobgpApi_ListPeerServer interface { + Send(*ListPeerResponse) error + grpc.ServerStream +} + +type gobgpApiListPeerServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListPeerServer) Send(m *ListPeerResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_UpdatePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdatePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).UpdatePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/UpdatePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).UpdatePeer(ctx, req.(*UpdatePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ResetPeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetPeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).ResetPeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/ResetPeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).ResetPeer(ctx, req.(*ResetPeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ShutdownPeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ShutdownPeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).ShutdownPeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/ShutdownPeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).ShutdownPeer(ctx, req.(*ShutdownPeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_EnablePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnablePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).EnablePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/EnablePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).EnablePeer(ctx, req.(*EnablePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DisablePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisablePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DisablePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DisablePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DisablePeer(ctx, req.(*DisablePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_MonitorPeer_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(MonitorPeerRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).MonitorPeer(m, &gobgpApiMonitorPeerServer{stream}) +} + +type GobgpApi_MonitorPeerServer interface { + Send(*MonitorPeerResponse) error + grpc.ServerStream +} + +type gobgpApiMonitorPeerServer struct { + grpc.ServerStream +} + +func (x *gobgpApiMonitorPeerServer) Send(m *MonitorPeerResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddPeerGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPeerGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddPeerGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddPeerGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddPeerGroup(ctx, req.(*AddPeerGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeletePeerGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePeerGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeletePeerGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeletePeerGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeletePeerGroup(ctx, req.(*DeletePeerGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_UpdatePeerGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdatePeerGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).UpdatePeerGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/UpdatePeerGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).UpdatePeerGroup(ctx, req.(*UpdatePeerGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddDynamicNeighbor_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddDynamicNeighborRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddDynamicNeighbor(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddDynamicNeighbor", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddDynamicNeighbor(ctx, req.(*AddDynamicNeighborRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddPath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPathRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddPath(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddPath", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddPath(ctx, req.(*AddPathRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeletePath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePathRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeletePath(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeletePath", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeletePath(ctx, req.(*DeletePathRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListPath_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListPathRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListPath(m, &gobgpApiListPathServer{stream}) +} + +type GobgpApi_ListPathServer interface { + Send(*ListPathResponse) error + grpc.ServerStream +} + +type gobgpApiListPathServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListPathServer) Send(m *ListPathResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddPathStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GobgpApiServer).AddPathStream(&gobgpApiAddPathStreamServer{stream}) +} + +type GobgpApi_AddPathStreamServer interface { + SendAndClose(*google_protobuf1.Empty) error + Recv() (*AddPathStreamRequest, error) + grpc.ServerStream +} + +type gobgpApiAddPathStreamServer struct { + grpc.ServerStream +} + +func (x *gobgpApiAddPathStreamServer) SendAndClose(m *google_protobuf1.Empty) error { + return x.ServerStream.SendMsg(m) +} + +func (x *gobgpApiAddPathStreamServer) Recv() (*AddPathStreamRequest, error) { + m := new(AddPathStreamRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _GobgpApi_GetTable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTableRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).GetTable(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/GetTable", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).GetTable(ctx, req.(*GetTableRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_MonitorTable_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(MonitorTableRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).MonitorTable(m, &gobgpApiMonitorTableServer{stream}) +} + +type GobgpApi_MonitorTableServer interface { + Send(*MonitorTableResponse) error + grpc.ServerStream +} + +type gobgpApiMonitorTableServer struct { + grpc.ServerStream +} + +func (x *gobgpApiMonitorTableServer) Send(m *MonitorTableResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddVrf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddVrfRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddVrf(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddVrf", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddVrf(ctx, req.(*AddVrfRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeleteVrf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteVrfRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeleteVrf(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeleteVrf", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeleteVrf(ctx, req.(*DeleteVrfRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListVrf_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListVrfRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListVrf(m, &gobgpApiListVrfServer{stream}) +} + +type GobgpApi_ListVrfServer interface { + Send(*ListVrfResponse) error + grpc.ServerStream +} + +type gobgpApiListVrfServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListVrfServer) Send(m *ListVrfResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddPolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddPolicy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddPolicy(ctx, req.(*AddPolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeletePolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeletePolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeletePolicy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeletePolicy(ctx, req.(*DeletePolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListPolicy_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListPolicyRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListPolicy(m, &gobgpApiListPolicyServer{stream}) +} + +type GobgpApi_ListPolicyServer interface { + Send(*ListPolicyResponse) error + grpc.ServerStream +} + +type gobgpApiListPolicyServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListPolicyServer) Send(m *ListPolicyResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_SetPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetPoliciesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).SetPolicies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/SetPolicies", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).SetPolicies(ctx, req.(*SetPoliciesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddDefinedSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddDefinedSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddDefinedSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddDefinedSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddDefinedSet(ctx, req.(*AddDefinedSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeleteDefinedSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteDefinedSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeleteDefinedSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeleteDefinedSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeleteDefinedSet(ctx, req.(*DeleteDefinedSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListDefinedSet_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListDefinedSetRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListDefinedSet(m, &gobgpApiListDefinedSetServer{stream}) +} + +type GobgpApi_ListDefinedSetServer interface { + Send(*ListDefinedSetResponse) error + grpc.ServerStream +} + +type gobgpApiListDefinedSetServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListDefinedSetServer) Send(m *ListDefinedSetResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddStatement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddStatementRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddStatement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddStatement", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddStatement(ctx, req.(*AddStatementRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeleteStatement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteStatementRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeleteStatement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeleteStatement", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeleteStatement(ctx, req.(*DeleteStatementRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListStatement_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListStatementRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListStatement(m, &gobgpApiListStatementServer{stream}) +} + +type GobgpApi_ListStatementServer interface { + Send(*ListStatementResponse) error + grpc.ServerStream +} + +type gobgpApiListStatementServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListStatementServer) Send(m *ListStatementResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_AddPolicyAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPolicyAssignmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddPolicyAssignment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddPolicyAssignment", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddPolicyAssignment(ctx, req.(*AddPolicyAssignmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeletePolicyAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePolicyAssignmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeletePolicyAssignment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeletePolicyAssignment", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeletePolicyAssignment(ctx, req.(*DeletePolicyAssignmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListPolicyAssignment_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListPolicyAssignmentRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListPolicyAssignment(m, &gobgpApiListPolicyAssignmentServer{stream}) +} + +type GobgpApi_ListPolicyAssignmentServer interface { + Send(*ListPolicyAssignmentResponse) error + grpc.ServerStream +} + +type gobgpApiListPolicyAssignmentServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListPolicyAssignmentServer) Send(m *ListPolicyAssignmentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_SetPolicyAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetPolicyAssignmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).SetPolicyAssignment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/SetPolicyAssignment", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).SetPolicyAssignment(ctx, req.(*SetPolicyAssignmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddRpki_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddRpkiRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddRpki(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddRpki", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddRpki(ctx, req.(*AddRpkiRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeleteRpki_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteRpkiRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeleteRpki(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeleteRpki", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeleteRpki(ctx, req.(*DeleteRpkiRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListRpki_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListRpkiRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListRpki(m, &gobgpApiListRpkiServer{stream}) +} + +type GobgpApi_ListRpkiServer interface { + Send(*ListRpkiResponse) error + grpc.ServerStream +} + +type gobgpApiListRpkiServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListRpkiServer) Send(m *ListRpkiResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_EnableRpki_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnableRpkiRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).EnableRpki(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/EnableRpki", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).EnableRpki(ctx, req.(*EnableRpkiRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DisableRpki_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisableRpkiRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DisableRpki(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DisableRpki", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DisableRpki(ctx, req.(*DisableRpkiRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ResetRpki_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetRpkiRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).ResetRpki(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/ResetRpki", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).ResetRpki(ctx, req.(*ResetRpkiRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_ListRpkiTable_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ListRpkiTableRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GobgpApiServer).ListRpkiTable(m, &gobgpApiListRpkiTableServer{stream}) +} + +type GobgpApi_ListRpkiTableServer interface { + Send(*ListRpkiTableResponse) error + grpc.ServerStream +} + +type gobgpApiListRpkiTableServer struct { + grpc.ServerStream +} + +func (x *gobgpApiListRpkiTableServer) Send(m *ListRpkiTableResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _GobgpApi_EnableZebra_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnableZebraRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).EnableZebra(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/EnableZebra", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).EnableZebra(ctx, req.(*EnableZebraRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_EnableMrt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnableMrtRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).EnableMrt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/EnableMrt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).EnableMrt(ctx, req.(*EnableMrtRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DisableMrt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisableMrtRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DisableMrt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DisableMrt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DisableMrt(ctx, req.(*DisableMrtRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_AddBmp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddBmpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).AddBmp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/AddBmp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).AddBmp(ctx, req.(*AddBmpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GobgpApi_DeleteBmp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteBmpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GobgpApiServer).DeleteBmp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gobgpapi.GobgpApi/DeleteBmp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GobgpApiServer).DeleteBmp(ctx, req.(*DeleteBmpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _GobgpApi_serviceDesc = grpc.ServiceDesc{ + ServiceName: "gobgpapi.GobgpApi", + HandlerType: (*GobgpApiServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "StartBgp", + Handler: _GobgpApi_StartBgp_Handler, + }, + { + MethodName: "StopBgp", + Handler: _GobgpApi_StopBgp_Handler, + }, + { + MethodName: "GetBgp", + Handler: _GobgpApi_GetBgp_Handler, + }, + { + MethodName: "AddPeer", + Handler: _GobgpApi_AddPeer_Handler, + }, + { + MethodName: "DeletePeer", + Handler: _GobgpApi_DeletePeer_Handler, + }, + { + MethodName: "UpdatePeer", + Handler: _GobgpApi_UpdatePeer_Handler, + }, + { + MethodName: "ResetPeer", + Handler: _GobgpApi_ResetPeer_Handler, + }, + { + MethodName: "ShutdownPeer", + Handler: _GobgpApi_ShutdownPeer_Handler, + }, + { + MethodName: "EnablePeer", + Handler: _GobgpApi_EnablePeer_Handler, + }, + { + MethodName: "DisablePeer", + Handler: _GobgpApi_DisablePeer_Handler, + }, + { + MethodName: "AddPeerGroup", + Handler: _GobgpApi_AddPeerGroup_Handler, + }, + { + MethodName: "DeletePeerGroup", + Handler: _GobgpApi_DeletePeerGroup_Handler, + }, + { + MethodName: "UpdatePeerGroup", + Handler: _GobgpApi_UpdatePeerGroup_Handler, + }, + { + MethodName: "AddDynamicNeighbor", + Handler: _GobgpApi_AddDynamicNeighbor_Handler, + }, + { + MethodName: "AddPath", + Handler: _GobgpApi_AddPath_Handler, + }, + { + MethodName: "DeletePath", + Handler: _GobgpApi_DeletePath_Handler, + }, + { + MethodName: "GetTable", + Handler: _GobgpApi_GetTable_Handler, + }, + { + MethodName: "AddVrf", + Handler: _GobgpApi_AddVrf_Handler, + }, + { + MethodName: "DeleteVrf", + Handler: _GobgpApi_DeleteVrf_Handler, + }, + { + MethodName: "AddPolicy", + Handler: _GobgpApi_AddPolicy_Handler, + }, + { + MethodName: "DeletePolicy", + Handler: _GobgpApi_DeletePolicy_Handler, + }, + { + MethodName: "SetPolicies", + Handler: _GobgpApi_SetPolicies_Handler, + }, + { + MethodName: "AddDefinedSet", + Handler: _GobgpApi_AddDefinedSet_Handler, + }, + { + MethodName: "DeleteDefinedSet", + Handler: _GobgpApi_DeleteDefinedSet_Handler, + }, + { + MethodName: "AddStatement", + Handler: _GobgpApi_AddStatement_Handler, + }, + { + MethodName: "DeleteStatement", + Handler: _GobgpApi_DeleteStatement_Handler, + }, + { + MethodName: "AddPolicyAssignment", + Handler: _GobgpApi_AddPolicyAssignment_Handler, + }, + { + MethodName: "DeletePolicyAssignment", + Handler: _GobgpApi_DeletePolicyAssignment_Handler, + }, + { + MethodName: "SetPolicyAssignment", + Handler: _GobgpApi_SetPolicyAssignment_Handler, + }, + { + MethodName: "AddRpki", + Handler: _GobgpApi_AddRpki_Handler, + }, + { + MethodName: "DeleteRpki", + Handler: _GobgpApi_DeleteRpki_Handler, + }, + { + MethodName: "EnableRpki", + Handler: _GobgpApi_EnableRpki_Handler, + }, + { + MethodName: "DisableRpki", + Handler: _GobgpApi_DisableRpki_Handler, + }, + { + MethodName: "ResetRpki", + Handler: _GobgpApi_ResetRpki_Handler, + }, + { + MethodName: "EnableZebra", + Handler: _GobgpApi_EnableZebra_Handler, + }, + { + MethodName: "EnableMrt", + Handler: _GobgpApi_EnableMrt_Handler, + }, + { + MethodName: "DisableMrt", + Handler: _GobgpApi_DisableMrt_Handler, + }, + { + MethodName: "AddBmp", + Handler: _GobgpApi_AddBmp_Handler, + }, + { + MethodName: "DeleteBmp", + Handler: _GobgpApi_DeleteBmp_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "ListPeer", + Handler: _GobgpApi_ListPeer_Handler, + ServerStreams: true, + }, + { + StreamName: "MonitorPeer", + Handler: _GobgpApi_MonitorPeer_Handler, + ServerStreams: true, + }, + { + StreamName: "ListPath", + Handler: _GobgpApi_ListPath_Handler, + ServerStreams: true, + }, + { + StreamName: "AddPathStream", + Handler: _GobgpApi_AddPathStream_Handler, + ClientStreams: true, + }, + { + StreamName: "MonitorTable", + Handler: _GobgpApi_MonitorTable_Handler, + ServerStreams: true, + }, + { + StreamName: "ListVrf", + Handler: _GobgpApi_ListVrf_Handler, + ServerStreams: true, + }, + { + StreamName: "ListPolicy", + Handler: _GobgpApi_ListPolicy_Handler, + ServerStreams: true, + }, + { + StreamName: "ListDefinedSet", + Handler: _GobgpApi_ListDefinedSet_Handler, + ServerStreams: true, + }, + { + StreamName: "ListStatement", + Handler: _GobgpApi_ListStatement_Handler, + ServerStreams: true, + }, + { + StreamName: "ListPolicyAssignment", + Handler: _GobgpApi_ListPolicyAssignment_Handler, + ServerStreams: true, + }, + { + StreamName: "ListRpki", + Handler: _GobgpApi_ListRpki_Handler, + ServerStreams: true, + }, + { + StreamName: "ListRpkiTable", + Handler: _GobgpApi_ListRpkiTable_Handler, + ServerStreams: true, + }, + }, + Metadata: "gobgp.proto", +} + +func init() { proto.RegisterFile("gobgp.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 7658 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7c, 0x4d, 0x6f, 0x1b, 0x49, + 0x96, 0xa0, 0xf9, 0x21, 0x8a, 0x7c, 0x24, 0xc5, 0x54, 0x48, 0x96, 0x64, 0xb9, 0xfc, 0x51, 0xd9, + 0x55, 0xb6, 0x4b, 0x55, 0x96, 0x3f, 0xaa, 0xca, 0xae, 0xb6, 0xdb, 0x55, 0x45, 0x4b, 0xb4, 0xcc, + 0x32, 0x45, 0xb2, 0x92, 0x94, 0xcb, 0xd5, 0xd8, 0x42, 0x76, 0x8a, 0x19, 0xa4, 0x72, 0x8b, 0xcc, + 0xcc, 0xca, 0x4c, 0xca, 0x12, 0xf6, 0xd2, 0xdb, 0xbd, 0xdb, 0x97, 0x45, 0x03, 0x8b, 0xdd, 0xdb, + 0xee, 0xde, 0x16, 0xd8, 0x43, 0x63, 0x0f, 0x7b, 0xd8, 0x5d, 0x60, 0x81, 0x05, 0x66, 0x0e, 0x33, + 0x98, 0xc6, 0x34, 0x30, 0xc0, 0x9c, 0x06, 0x98, 0x43, 0x9f, 0xe6, 0x0f, 0xcc, 0x65, 0x30, 0xc7, + 0x41, 0x7c, 0x64, 0x66, 0xe4, 0x07, 0x25, 0xd9, 0xe5, 0xe9, 0x9e, 0x06, 0xfa, 0x44, 0xc6, 0x7b, + 0x2f, 0x5e, 0x7c, 0xbd, 0x78, 0xef, 0x45, 0xc4, 0xcb, 0x07, 0xe5, 0x91, 0xb5, 0x3f, 0xb2, 0x37, + 0x6d, 0xc7, 0xf2, 0x2c, 0x54, 0xa4, 0x05, 0xcd, 0x36, 0xd6, 0x2f, 0x8c, 0x2c, 0x6b, 0x34, 0xc6, + 0xb7, 0x28, 0x7c, 0x7f, 0x3a, 0xbc, 0xa5, 0x99, 0xc7, 0x8c, 0x68, 0xfd, 0x62, 0x1c, 0x85, 0x27, + 0xb6, 0xc7, 0x91, 0xf2, 0x43, 0xa8, 0xf5, 0x3c, 0xcd, 0xf1, 0x1e, 0x8f, 0x6c, 0x05, 0x7f, 0x37, + 0xc5, 0xae, 0x87, 0x6e, 0x40, 0x61, 0x34, 0xb6, 0xf6, 0xb5, 0xf1, 0x5a, 0xe6, 0x6a, 0xe6, 0x46, + 0xf9, 0xae, 0xb4, 0xe9, 0xb7, 0xb2, 0xb9, 0x43, 0xe1, 0x0a, 0xc7, 0xcb, 0x12, 0x2c, 0xf4, 0x3c, + 0xcb, 0x0e, 0xeb, 0xca, 0x35, 0xa8, 0xee, 0x60, 0x81, 0x99, 0xfc, 0x00, 0x16, 0x7c, 0x80, 0x6b, + 0x5b, 0xa6, 0x8b, 0x5f, 0x81, 0xfd, 0x47, 0xb0, 0x50, 0xd7, 0xf5, 0x2e, 0xc6, 0x8e, 0xdf, 0x35, + 0x19, 0xf2, 0x36, 0xc6, 0x0e, 0xaf, 0xb9, 0x10, 0xd6, 0xa4, 0x44, 0x14, 0x27, 0x3f, 0x83, 0xc5, + 0x6d, 0x3c, 0xc6, 0x1e, 0x16, 0x2b, 0xae, 0xc1, 0xbc, 0xa6, 0xeb, 0x0e, 0x76, 0x5d, 0x5a, 0xb7, + 0xa4, 0xf8, 0x45, 0xf4, 0x16, 0x94, 0x0c, 0xd3, 0xc3, 0xce, 0x50, 0x1b, 0xe0, 0xb5, 0x2c, 0xc5, + 0x85, 0x00, 0xf9, 0x2b, 0xa8, 0xb5, 0x0c, 0xd7, 0x3b, 0x1b, 0xab, 0x0d, 0x90, 0xb0, 0xa9, 0xed, + 0x8f, 0x71, 0x5d, 0x3f, 0xc4, 0x8e, 0x67, 0xb8, 0x58, 0xa7, 0x1c, 0x8b, 0x4a, 0x02, 0x2e, 0xdf, + 0x03, 0x29, 0x64, 0xcc, 0x67, 0xe6, 0x2c, 0xa3, 0xfb, 0x09, 0x2c, 0xee, 0xd9, 0xba, 0x16, 0x1d, + 0xdd, 0x19, 0x2a, 0xa2, 0xeb, 0x20, 0xe9, 0x96, 0xea, 0x5a, 0x43, 0x4f, 0x75, 0xb0, 0x8b, 0x3d, + 0xd5, 0x30, 0x79, 0xe7, 0xaa, 0xba, 0xd5, 0xb3, 0x86, 0x9e, 0x42, 0xa0, 0x4d, 0x53, 0xde, 0x02, + 0x24, 0xb6, 0xc0, 0xfb, 0x76, 0x13, 0x96, 0x4c, 0x8c, 0x75, 0x37, 0xc6, 0x21, 0xc3, 0x86, 0x47, + 0x51, 0x22, 0x93, 0xbf, 0xcb, 0x80, 0x44, 0xff, 0x9f, 0x6d, 0xe6, 0xde, 0x81, 0xea, 0xc0, 0x9a, + 0x4c, 0xa6, 0xa6, 0x31, 0xd0, 0x3c, 0xc3, 0x32, 0xf9, 0x42, 0x44, 0x81, 0x08, 0x41, 0x9e, 0xb4, + 0xbe, 0x96, 0xa3, 0x8d, 0xd2, 0xff, 0xe8, 0x19, 0x94, 0x74, 0xc3, 0xc1, 0x03, 0x5a, 0x2b, 0x7f, + 0x35, 0x73, 0x63, 0xe1, 0xee, 0xcd, 0x70, 0xfc, 0xf1, 0x2e, 0x6c, 0x06, 0x7d, 0xdc, 0xf6, 0x2b, + 0x29, 0x61, 0x7d, 0xf9, 0x16, 0xa0, 0x24, 0x01, 0x2a, 0x40, 0xb6, 0xd9, 0x96, 0xce, 0xa1, 0x79, + 0xc8, 0x75, 0xf6, 0xfa, 0x52, 0x06, 0x15, 0x21, 0xff, 0xb8, 0xd3, 0x7f, 0x2a, 0x65, 0xe5, 0x3d, + 0x58, 0xea, 0x1d, 0x4c, 0x3d, 0xdd, 0x7a, 0x69, 0xbe, 0xc1, 0x81, 0xca, 0x37, 0x61, 0xb1, 0x41, + 0x05, 0xe6, 0x4c, 0x4c, 0xe5, 0x3e, 0xa0, 0x6d, 0xc3, 0x3d, 0x33, 0xfd, 0x19, 0x3b, 0xf1, 0x14, + 0xd0, 0xae, 0x65, 0x1a, 0x9e, 0xe5, 0x9c, 0x8d, 0xeb, 0x1a, 0xcc, 0x0f, 0xa6, 0x8e, 0x83, 0x4d, + 0x8f, 0xcb, 0x95, 0x5f, 0x94, 0x7f, 0x08, 0x4b, 0x11, 0x4e, 0xaf, 0x20, 0xee, 0x4d, 0x58, 0xe2, + 0x2a, 0x60, 0xc7, 0xb1, 0xa6, 0x81, 0x8a, 0xba, 0x0b, 0x40, 0xd0, 0xea, 0x88, 0x00, 0x39, 0x83, + 0xa5, 0x28, 0x03, 0x46, 0x5f, 0xb2, 0xfd, 0xbf, 0xf2, 0x07, 0xb0, 0x12, 0xea, 0x85, 0x08, 0x37, + 0x04, 0x79, 0x53, 0x9b, 0x60, 0x3e, 0x20, 0xfa, 0x5f, 0x9e, 0xc2, 0x4a, 0xb8, 0x0b, 0xbe, 0x6f, + 0xdb, 0x67, 0xdf, 0x7c, 0x4f, 0x61, 0x35, 0xd1, 0xec, 0xeb, 0xed, 0x40, 0x0d, 0x2e, 0xd4, 0x75, + 0x7d, 0xfb, 0xd8, 0xd4, 0x26, 0xc6, 0xa0, 0x8d, 0x8d, 0xd1, 0xc1, 0xbe, 0x15, 0xac, 0xe2, 0x36, + 0x48, 0x3a, 0xc3, 0xa8, 0x26, 0x47, 0xf1, 0x91, 0x5c, 0x08, 0x47, 0x12, 0xaf, 0x5b, 0xd3, 0xa3, + 0x00, 0xf9, 0xdf, 0x30, 0xfd, 0xac, 0x79, 0x07, 0x3e, 0xdf, 0x4d, 0x28, 0x3a, 0xd8, 0xb5, 0xa6, + 0xce, 0x80, 0xcd, 0xe6, 0xc2, 0x5d, 0x14, 0xd9, 0x8c, 0x14, 0xa3, 0x04, 0x34, 0xe8, 0x3c, 0x14, + 0x0e, 0x9d, 0xa1, 0x6a, 0xe8, 0x5c, 0x04, 0xe7, 0x0e, 0x9d, 0x61, 0x53, 0xa7, 0x92, 0xa1, 0x79, + 0x07, 0x74, 0xa3, 0x47, 0x25, 0x83, 0xb4, 0x45, 0x71, 0xf2, 0xbb, 0x50, 0x0b, 0x1a, 0xe7, 0x33, + 0x84, 0x20, 0x3f, 0x9d, 0x1a, 0x3a, 0x6d, 0xb9, 0xa2, 0xd0, 0xff, 0xf2, 0xff, 0xcf, 0x04, 0xe6, + 0xe0, 0xcd, 0xf7, 0xf3, 0x06, 0x14, 0x86, 0xda, 0xc4, 0x18, 0x1f, 0xf3, 0x9e, 0x0a, 0xa6, 0xec, + 0x09, 0x85, 0x2b, 0x1c, 0x1f, 0x8c, 0x28, 0x3f, 0x7b, 0x44, 0x41, 0xf7, 0xe7, 0x84, 0xee, 0xff, + 0xaf, 0x0c, 0x37, 0x40, 0x42, 0xe7, 0xaf, 0x41, 0xde, 0x3b, 0xb6, 0x4f, 0xea, 0x38, 0xc5, 0x07, + 0x62, 0x9d, 0x0d, 0xc5, 0xfa, 0x15, 0x7a, 0x7c, 0x1f, 0x8a, 0xb6, 0x83, 0x87, 0xc6, 0x11, 0x76, + 0xd7, 0xf2, 0x57, 0x73, 0x37, 0xca, 0x77, 0x2f, 0x86, 0xb4, 0x7d, 0xa2, 0x6c, 0x5a, 0x96, 0xf5, + 0xed, 0xd4, 0xee, 0x52, 0x22, 0x25, 0x20, 0x96, 0x9f, 0x71, 0xcb, 0x26, 0xae, 0xcc, 0x7d, 0x28, + 0xeb, 0xd8, 0xf5, 0x0c, 0x93, 0xe9, 0x1b, 0x26, 0x6a, 0xe7, 0x05, 0x51, 0x0b, 0x91, 0x8a, 0x48, + 0x29, 0xff, 0x3c, 0x03, 0xcb, 0x7c, 0x99, 0x7b, 0x9e, 0x83, 0xb5, 0xc9, 0x1b, 0x5e, 0xc1, 0x77, + 0x60, 0x8e, 0xcc, 0xbd, 0xbb, 0x96, 0xa3, 0x43, 0x8c, 0x2f, 0x0c, 0x43, 0xca, 0x2f, 0xa1, 0xb6, + 0x83, 0x3d, 0x3a, 0xe8, 0x57, 0x5d, 0x84, 0x70, 0xc2, 0xb3, 0xa7, 0x4c, 0xb8, 0xbf, 0x5c, 0x39, + 0x41, 0x0b, 0x1d, 0x83, 0x14, 0x36, 0xcc, 0xe7, 0xf2, 0x3a, 0xd4, 0xcc, 0xe9, 0x44, 0x8d, 0xcf, + 0x67, 0x5e, 0x59, 0x30, 0xa7, 0x13, 0x61, 0x22, 0xd1, 0x05, 0x28, 0x12, 0x42, 0x2a, 0x77, 0x59, + 0x4a, 0x31, 0x6f, 0x4e, 0x27, 0x64, 0x5c, 0xe8, 0x6d, 0xa8, 0x10, 0x94, 0x36, 0x18, 0x60, 0xdb, + 0xc3, 0x3a, 0x6d, 0x33, 0xaf, 0x94, 0xcd, 0xe9, 0xa4, 0xce, 0x41, 0xf2, 0xff, 0xcd, 0x04, 0x5a, + 0xfb, 0xb5, 0x06, 0xfe, 0xfd, 0xa4, 0x4f, 0x30, 0x26, 0xf9, 0x88, 0x31, 0x41, 0x57, 0xa0, 0x6c, + 0x5b, 0xae, 0xa7, 0xda, 0xd6, 0xd8, 0x18, 0x1c, 0xd3, 0xcd, 0x52, 0x54, 0x80, 0x80, 0xba, 0x14, + 0x22, 0x3f, 0x80, 0xe5, 0x68, 0xbf, 0x05, 0x73, 0x43, 0xa6, 0x22, 0x73, 0x82, 0x52, 0xb9, 0x0d, + 0xd5, 0xba, 0xae, 0x3f, 0x77, 0x86, 0xfe, 0x68, 0xaf, 0x40, 0xee, 0xd0, 0x19, 0xf2, 0x3a, 0xd5, + 0xb0, 0x0e, 0x21, 0x21, 0x18, 0xf9, 0x1a, 0x48, 0x4c, 0xbd, 0x08, 0x95, 0xd2, 0xec, 0xc9, 0x3b, + 0xb0, 0x40, 0x76, 0xc5, 0x29, 0x54, 0x77, 0xd9, 0x6e, 0xa7, 0x54, 0xbc, 0xdb, 0xa7, 0xf6, 0xe0, + 0x08, 0x24, 0xb2, 0x43, 0xe8, 0xe0, 0x05, 0x17, 0x9e, 0xcf, 0x4f, 0xc2, 0xc7, 0xe6, 0x84, 0x1c, + 0x8f, 0x1e, 0xc0, 0x05, 0x07, 0x0f, 0xb1, 0xa3, 0xe2, 0x23, 0x83, 0x88, 0xce, 0x48, 0x75, 0x3d, + 0xcd, 0xc3, 0x13, 0x6c, 0x7a, 0x2e, 0x37, 0x51, 0xab, 0x94, 0xa0, 0xc1, 0xf1, 0xbd, 0x00, 0x2d, + 0xff, 0x2c, 0x03, 0x4b, 0x5c, 0xb7, 0xbe, 0x66, 0xeb, 0xb7, 0x60, 0xc9, 0x26, 0x96, 0xcc, 0x39, + 0xc4, 0xc9, 0x76, 0x91, 0x8f, 0x0a, 0x9b, 0x44, 0x12, 0xe4, 0xb4, 0xf1, 0x98, 0x7b, 0x80, 0xe4, + 0xaf, 0x7c, 0x1d, 0x16, 0xa9, 0xba, 0x89, 0xf4, 0x20, 0x6d, 0x6e, 0x3f, 0x05, 0x24, 0x12, 0x86, + 0xa7, 0x91, 0xb3, 0xf5, 0x55, 0xfe, 0x7f, 0x19, 0x40, 0x3d, 0xcc, 0xea, 0x1b, 0xd8, 0xf5, 0x9b, + 0xba, 0x0f, 0x15, 0x1d, 0x0f, 0x0d, 0x13, 0xeb, 0xaa, 0x8b, 0x3d, 0xe2, 0x15, 0x11, 0x45, 0xb2, + 0x2c, 0xea, 0x36, 0x8a, 0xed, 0x61, 0x8f, 0xa8, 0x36, 0xff, 0xbf, 0x8b, 0x3e, 0x80, 0xa2, 0xcd, + 0x79, 0xad, 0x65, 0x69, 0xa5, 0x64, 0xdb, 0x01, 0x05, 0xfa, 0x11, 0x94, 0x35, 0xd7, 0x35, 0x46, + 0x26, 0x9b, 0x21, 0xa6, 0xae, 0xd6, 0xe3, 0x15, 0xea, 0x01, 0x89, 0x22, 0x92, 0xcb, 0xbb, 0x54, + 0x8b, 0x0a, 0x3d, 0xe1, 0x9d, 0xff, 0x18, 0xca, 0x42, 0xe7, 0xf9, 0x14, 0xa4, 0xf7, 0x1d, 0xc2, + 0xbe, 0xcb, 0xfb, 0xb0, 0xca, 0xd6, 0xfd, 0x4d, 0x71, 0xf4, 0xd7, 0x35, 0x1b, 0xae, 0xeb, 0x73, + 0x38, 0x4f, 0x96, 0x2b, 0xd9, 0xc2, 0x7b, 0x11, 0x05, 0x74, 0x3e, 0xc1, 0xba, 0x7f, 0x6c, 0x9f, + 0xa0, 0x83, 0xe4, 0x0e, 0xac, 0xc4, 0xf9, 0x72, 0x51, 0x78, 0xcd, 0xc9, 0x78, 0x4a, 0x5d, 0xd4, + 0x40, 0x46, 0xfd, 0x6e, 0xde, 0x81, 0x52, 0x20, 0xd1, 0x49, 0x2f, 0x31, 0x24, 0x0f, 0xa9, 0xe4, + 0x6f, 0x7c, 0x0f, 0xf5, 0x0d, 0x30, 0x4b, 0x99, 0xd1, 0x0d, 0x58, 0x26, 0x23, 0x4f, 0x30, 0x4f, + 0xdb, 0x2c, 0x5f, 0xb0, 0xd9, 0x17, 0x68, 0xf9, 0x24, 0xbd, 0xc6, 0xb0, 0x5e, 0xc0, 0x7a, 0xa0, + 0xa0, 0x04, 0x01, 0xe5, 0xad, 0x3f, 0x00, 0x08, 0x25, 0x95, 0x73, 0x3c, 0x49, 0xae, 0x05, 0x6a, + 0x79, 0x02, 0x97, 0x44, 0xfd, 0xf3, 0x46, 0x99, 0xa7, 0x4c, 0xe0, 0xbf, 0x86, 0x8b, 0xa1, 0x06, + 0x49, 0x36, 0x96, 0x32, 0x8f, 0xe8, 0xbe, 0x78, 0x3c, 0xcd, 0x52, 0x89, 0xbd, 0x10, 0x6f, 0x3f, + 0xf5, 0x28, 0xfa, 0x63, 0x78, 0x2b, 0xbd, 0x2d, 0xbe, 0x0e, 0xdf, 0x67, 0xda, 0x5e, 0xc0, 0xba, + 0xaf, 0xc8, 0xde, 0xf0, 0x82, 0xfc, 0x98, 0x9e, 0x08, 0x14, 0xfb, 0x5b, 0xe3, 0xf4, 0xf3, 0x22, + 0x82, 0xbc, 0x6d, 0x39, 0xec, 0xb0, 0x58, 0x55, 0xe8, 0x7f, 0xb4, 0x0e, 0xc5, 0xb1, 0x31, 0xc4, + 0x9e, 0xc1, 0xfd, 0xa0, 0x9c, 0x12, 0x94, 0xe5, 0xba, 0xef, 0xc8, 0xbf, 0x36, 0x7b, 0xf9, 0x21, + 0x33, 0xaf, 0x22, 0x83, 0xd0, 0x25, 0xc9, 0x9c, 0xec, 0x92, 0xc8, 0x0f, 0x98, 0x5f, 0xcb, 0x2a, + 0xf3, 0x55, 0xb8, 0x06, 0x05, 0x6a, 0xa1, 0x52, 0x0e, 0xb1, 0x94, 0x8e, 0x63, 0x49, 0xdf, 0xd9, + 0x81, 0xfe, 0xf5, 0xfb, 0xfe, 0x38, 0x38, 0xe4, 0xbf, 0x3e, 0x8f, 0x3e, 0xbf, 0x94, 0x79, 0xfd, + 0x05, 0x4a, 0xb9, 0x82, 0x91, 0x3f, 0x67, 0x7a, 0x85, 0x30, 0x8d, 0x78, 0x8a, 0x67, 0x9f, 0xda, + 0x4f, 0x98, 0xb6, 0x11, 0x38, 0x84, 0xce, 0x8f, 0x63, 0x69, 0x49, 0xe7, 0x47, 0xb1, 0x34, 0x85, + 0x60, 0xe4, 0xdf, 0x64, 0x00, 0xb1, 0x99, 0xfd, 0x31, 0xde, 0x77, 0x34, 0xbf, 0x69, 0x09, 0x72, + 0x53, 0x67, 0xcc, 0x07, 0x44, 0xfe, 0x12, 0xb7, 0xd1, 0xb1, 0xa6, 0x1e, 0x56, 0x89, 0x61, 0x60, + 0x06, 0xb7, 0xa4, 0x00, 0x05, 0x11, 0x83, 0x41, 0xaf, 0x2f, 0x0e, 0xb1, 0xe3, 0x92, 0x7d, 0x9a, + 0xa3, 0x03, 0xf6, 0x8b, 0xe8, 0x23, 0x58, 0x31, 0xf1, 0x91, 0x77, 0x60, 0xd9, 0xaa, 0xe7, 0x18, + 0xa3, 0x11, 0x71, 0x96, 0x68, 0x93, 0xdc, 0x35, 0x5d, 0xe6, 0xd8, 0x3e, 0x43, 0xb2, 0xee, 0xa0, + 0xbb, 0x70, 0x3e, 0x5e, 0x4b, 0xc7, 0x63, 0x8d, 0x79, 0xac, 0x55, 0x65, 0x29, 0x5a, 0x69, 0x9b, + 0xa0, 0xe4, 0x11, 0x48, 0xac, 0xf6, 0xae, 0x13, 0x6c, 0xc7, 0x8b, 0x50, 0xd2, 0xa7, 0x13, 0x5b, + 0x0d, 0x6c, 0xde, 0x9c, 0x52, 0x24, 0x00, 0xd2, 0x6b, 0xb2, 0x5f, 0x86, 0xc6, 0x18, 0x0b, 0x46, + 0x2e, 0x28, 0x13, 0x1c, 0xbd, 0xc7, 0x3c, 0xd4, 0xc6, 0xdc, 0xbf, 0x0f, 0xca, 0xf2, 0x12, 0x2c, + 0x72, 0x61, 0x0a, 0x5b, 0x92, 0xff, 0x34, 0x43, 0xbd, 0xdf, 0xc7, 0x13, 0xfb, 0xf5, 0x64, 0xe3, + 0x47, 0xdc, 0x30, 0xe7, 0xa8, 0x9a, 0xbb, 0x11, 0xae, 0x56, 0x84, 0xe9, 0x26, 0x77, 0xce, 0x0d, + 0x73, 0xc4, 0x9d, 0x1c, 0x5a, 0x4b, 0xde, 0x02, 0x29, 0x8e, 0x41, 0xf3, 0x90, 0xeb, 0x2a, 0x0d, + 0xe9, 0x1c, 0x2a, 0x42, 0xbe, 0xdb, 0xe9, 0x45, 0xee, 0xde, 0x50, 0x09, 0xe6, 0x5a, 0x9d, 0xad, + 0x7a, 0x4b, 0xca, 0x11, 0xba, 0x7a, 0xab, 0x25, 0xe5, 0xe5, 0xcf, 0x7d, 0x6f, 0xfc, 0x75, 0x07, + 0x21, 0xff, 0x6d, 0x0e, 0x0a, 0x4c, 0x3a, 0xd1, 0x35, 0xc8, 0x69, 0x43, 0x83, 0xfb, 0x19, 0xcb, + 0x71, 0xe1, 0xdd, 0xac, 0x0f, 0x0d, 0x85, 0x10, 0x10, 0x87, 0xc4, 0x25, 0x84, 0xd9, 0xb8, 0x43, + 0xc2, 0x09, 0x7b, 0xda, 0xd0, 0x50, 0x28, 0x89, 0xdc, 0x85, 0x5c, 0x7d, 0x68, 0xa0, 0x1a, 0x94, + 0xeb, 0x4f, 0x9a, 0xea, 0x5e, 0xfb, 0x59, 0xbb, 0xf3, 0x55, 0x5b, 0x3a, 0x87, 0x00, 0x0a, 0x04, + 0xd0, 0xec, 0x4a, 0x19, 0x54, 0x86, 0x79, 0xf6, 0xff, 0x9e, 0x94, 0x45, 0x55, 0x28, 0x91, 0x42, + 0xeb, 0xee, 0xf3, 0x6e, 0x5b, 0xba, 0x80, 0x24, 0x00, 0x52, 0xec, 0x74, 0xeb, 0x5f, 0xee, 0x35, + 0xa4, 0x5f, 0xfe, 0x34, 0x23, 0xff, 0x2a, 0x0b, 0x79, 0xd2, 0x00, 0x92, 0xa0, 0xd2, 0x8b, 0x32, + 0x0d, 0x21, 0xcd, 0xad, 0x3a, 0x9d, 0x3c, 0x04, 0x0b, 0x14, 0xb2, 0xbb, 0xd7, 0xea, 0x33, 0x58, + 0x16, 0x2d, 0x41, 0x8d, 0xc1, 0xba, 0xad, 0x9e, 0xda, 0xaa, 0x3f, 0x6e, 0xb4, 0xa4, 0x3c, 0x5a, + 0x01, 0x44, 0x81, 0x8d, 0xf6, 0x56, 0xbd, 0xdb, 0xdb, 0x6b, 0xd5, 0xfb, 0xcd, 0x4e, 0x5b, 0x9a, + 0x27, 0xdd, 0xa1, 0xf0, 0xe7, 0xdd, 0x56, 0x4f, 0xaa, 0x07, 0xc5, 0x06, 0xe9, 0xdd, 0x13, 0x84, + 0xa0, 0x1a, 0xb2, 0x22, 0xa0, 0x9f, 0x66, 0xd0, 0x5b, 0xb0, 0x1a, 0x81, 0x09, 0x6d, 0xff, 0xdb, + 0x0c, 0x92, 0xe1, 0x12, 0xc5, 0x2a, 0x9d, 0xbd, 0x7e, 0x43, 0xed, 0xd7, 0x95, 0x9d, 0x46, 0x5f, + 0xdd, 0xea, 0xb4, 0x7b, 0x7d, 0xa5, 0xde, 0x6c, 0xf7, 0x7b, 0xd2, 0xbf, 0xcb, 0xa0, 0x8b, 0xb0, + 0x42, 0x69, 0x9e, 0xb4, 0x3a, 0x5f, 0xa9, 0xbd, 0x6e, 0x63, 0x2b, 0x18, 0xd0, 0xbf, 0xcf, 0xa0, + 0x55, 0xde, 0xd1, 0x10, 0x49, 0xda, 0xfd, 0x45, 0x06, 0x2d, 0xf1, 0xa1, 0x3e, 0x6b, 0x7c, 0xad, + 0x3e, 0xaf, 0xb7, 0xf6, 0x1a, 0xd2, 0xdf, 0x67, 0xe4, 0xff, 0x9e, 0x83, 0x05, 0xa5, 0xfb, 0xac, + 0xf9, 0x5c, 0x1b, 0x1b, 0x3a, 0x3b, 0x24, 0x7f, 0x04, 0x73, 0xd4, 0x53, 0xe1, 0xcb, 0x7c, 0x59, + 0xd0, 0x31, 0x11, 0x42, 0xe6, 0xda, 0x28, 0x8c, 0x18, 0xdd, 0x87, 0x82, 0x83, 0x35, 0x37, 0xb0, + 0xe9, 0x57, 0x66, 0x56, 0x53, 0x28, 0x99, 0xc2, 0xc9, 0xd1, 0x75, 0x98, 0x9f, 0x68, 0xde, 0xe0, + 0x80, 0x9e, 0xb9, 0x73, 0x49, 0xa5, 0xe6, 0x63, 0xd1, 0x6d, 0xa8, 0x4c, 0x4d, 0x5e, 0x50, 0x35, + 0xff, 0x0a, 0x26, 0x46, 0x5d, 0x0e, 0x48, 0xea, 0x2e, 0xfa, 0x04, 0xa4, 0xb0, 0xc6, 0x18, 0x9b, + 0x23, 0xef, 0x60, 0x6d, 0x2e, 0xad, 0x56, 0x2d, 0x20, 0x6b, 0x51, 0x2a, 0xb9, 0x0b, 0x73, 0x74, + 0x74, 0x68, 0x01, 0xa0, 0xd7, 0xaf, 0xf7, 0x1b, 0x6a, 0xbb, 0xd3, 0x26, 0xdb, 0x8e, 0xc8, 0x06, + 0x2f, 0xf7, 0xd5, 0x27, 0x9d, 0xbd, 0xf6, 0xb6, 0x94, 0x21, 0xc2, 0xcb, 0x80, 0xcf, 0xeb, 0xad, + 0xe6, 0xb6, 0x94, 0x45, 0x8b, 0x50, 0x65, 0x80, 0x66, 0x9b, 0x81, 0x72, 0xf2, 0x43, 0x28, 0xb0, + 0x81, 0x13, 0x6a, 0xa5, 0x51, 0xef, 0x75, 0xfa, 0x3e, 0xcf, 0x2a, 0x94, 0x28, 0xa0, 0xad, 0xd6, + 0x7b, 0x52, 0x86, 0x54, 0xe6, 0xc5, 0x56, 0xa3, 0xbd, 0x43, 0x2f, 0xd5, 0x7f, 0x3d, 0x07, 0xf9, + 0x2e, 0xbf, 0x10, 0x33, 0xc7, 0x8e, 0xe1, 0xdf, 0xe7, 0x91, 0xff, 0x68, 0x05, 0x0a, 0xb6, 0xe6, + 0x79, 0x0e, 0x53, 0xe1, 0x15, 0x85, 0x97, 0xa8, 0xb7, 0x36, 0xf2, 0x9d, 0x06, 0xf2, 0x97, 0xd4, + 0xde, 0xc7, 0xae, 0x7f, 0x7f, 0x40, 0xff, 0x13, 0x2b, 0x60, 0xb8, 0xea, 0x4b, 0xc3, 0x3b, 0xd0, + 0x1d, 0xed, 0xa5, 0x7f, 0x79, 0x60, 0xb8, 0x5f, 0x71, 0x08, 0x6a, 0xc0, 0xe2, 0x61, 0xb0, 0x78, + 0xaa, 0x8e, 0x3d, 0xcd, 0x18, 0xaf, 0xcd, 0x53, 0xf3, 0xb3, 0x36, 0x6b, 0x8d, 0x15, 0x29, 0xac, + 0xb2, 0x4d, 0x6b, 0xa0, 0xdb, 0xb0, 0x6c, 0x5a, 0xaa, 0x31, 0xb1, 0xc9, 0xe9, 0xcd, 0x0b, 0x1b, + 0x2c, 0xb2, 0x83, 0xad, 0x69, 0x35, 0x39, 0x2a, 0x68, 0x38, 0x34, 0x96, 0xa5, 0x53, 0xae, 0x46, + 0x2e, 0x01, 0xb0, 0x8b, 0x16, 0x55, 0x73, 0xcd, 0x35, 0xa0, 0xba, 0xab, 0xc4, 0x20, 0x75, 0xd7, + 0x24, 0xf6, 0x82, 0xa3, 0x0d, 0x7d, 0xad, 0xcc, 0x6c, 0x02, 0x03, 0x34, 0x75, 0x6e, 0x2f, 0x3c, + 0xec, 0x60, 0x7d, 0xad, 0x42, 0xfb, 0x12, 0x94, 0xd1, 0x32, 0xdd, 0x09, 0x63, 0xbc, 0x56, 0xa5, + 0x08, 0x56, 0x40, 0x37, 0x40, 0x32, 0x5c, 0x75, 0xe8, 0x58, 0x13, 0x15, 0x1f, 0x79, 0xd8, 0x31, + 0xb5, 0xf1, 0xda, 0x02, 0x25, 0x58, 0x30, 0xdc, 0x27, 0x8e, 0x35, 0x69, 0x70, 0x28, 0x99, 0x5b, + 0xff, 0x2e, 0x59, 0x35, 0xec, 0xb5, 0x1a, 0x6d, 0x1a, 0x7c, 0x50, 0xd3, 0x0e, 0xee, 0x37, 0xa5, + 0xf0, 0x7e, 0x13, 0x7d, 0x00, 0xc8, 0x70, 0x55, 0xdf, 0x50, 0x1a, 0x26, 0x9d, 0xc9, 0xb5, 0x45, + 0x76, 0xa7, 0x6d, 0xb8, 0x6d, 0x86, 0x68, 0x32, 0x38, 0xba, 0x0c, 0x60, 0xe8, 0xd8, 0xf4, 0x8c, + 0xa1, 0x81, 0x9d, 0x35, 0x44, 0x87, 0x2e, 0x40, 0xd0, 0x7b, 0x20, 0x8d, 0xad, 0x81, 0x36, 0x56, + 0x05, 0xaa, 0x25, 0x4a, 0x55, 0xa3, 0xf0, 0x66, 0x48, 0x7a, 0x0b, 0x8a, 0x9a, 0x79, 0xac, 0x52, + 0xf9, 0x5a, 0x0e, 0x4e, 0x7a, 0xd6, 0x68, 0x8c, 0x37, 0xfd, 0x77, 0xd2, 0xcd, 0xba, 0x79, 0xac, + 0xcc, 0x6b, 0xe6, 0x71, 0x9b, 0x08, 0xde, 0x87, 0x00, 0xa4, 0x02, 0x17, 0xbe, 0xf3, 0xc1, 0x29, + 0x3f, 0x59, 0xa5, 0xa4, 0x99, 0xc7, 0x5d, 0x4a, 0x26, 0x3f, 0x83, 0xb2, 0x78, 0x23, 0x47, 0x84, + 0x97, 0x5e, 0x93, 0x72, 0x4b, 0xc4, 0x4b, 0xe1, 0x2d, 0x64, 0xf6, 0xa4, 0x5b, 0xc8, 0x09, 0x2c, + 0x26, 0xee, 0x5d, 0x67, 0xb2, 0xfc, 0x1c, 0xaa, 0x63, 0x4a, 0xa7, 0x5a, 0xb6, 0x70, 0xf8, 0x48, + 0xbf, 0xc3, 0xed, 0x50, 0x12, 0xa5, 0x32, 0x16, 0x4a, 0xf2, 0x5f, 0xe7, 0x21, 0xdf, 0xc5, 0xd8, + 0x41, 0x9f, 0x40, 0x45, 0xb3, 0xed, 0xf1, 0xb1, 0x1a, 0xb9, 0x28, 0x11, 0xec, 0x5c, 0x9d, 0x60, + 0xb9, 0x31, 0x2f, 0x6b, 0x61, 0x01, 0x5d, 0x83, 0xfc, 0xc0, 0x32, 0x87, 0xfc, 0xea, 0x13, 0x45, + 0x1f, 0x49, 0xb6, 0x2c, 0x73, 0xa8, 0x50, 0x3c, 0x7a, 0x08, 0x55, 0xbc, 0x3f, 0xb2, 0xd5, 0xc9, + 0x74, 0xec, 0x19, 0x07, 0x96, 0xcd, 0xaf, 0x07, 0x57, 0xc2, 0x0a, 0x8d, 0xfd, 0x91, 0xbd, 0xcb, + 0xb1, 0x4a, 0x05, 0x0b, 0x25, 0x54, 0x87, 0x1a, 0xf3, 0xec, 0x1c, 0x3c, 0x1c, 0xe3, 0x81, 0x67, + 0x39, 0xfc, 0x96, 0x5d, 0xdc, 0xb0, 0x84, 0x40, 0xf1, 0xf1, 0xca, 0x82, 0x13, 0x29, 0xa3, 0xf7, + 0x7c, 0x23, 0x30, 0x97, 0xf6, 0x9a, 0x13, 0xd1, 0xfc, 0x37, 0xa0, 0x40, 0x4e, 0x23, 0x8e, 0xbb, + 0x56, 0x88, 0xef, 0xd3, 0x3e, 0x85, 0x2b, 0x1c, 0x4f, 0x4e, 0xca, 0x9e, 0xa3, 0x99, 0x2e, 0x75, + 0x31, 0xe6, 0xe3, 0x8c, 0xfb, 0x3e, 0x4a, 0x09, 0xa9, 0xc8, 0x4c, 0xb3, 0xa1, 0xf0, 0x43, 0x45, + 0x31, 0x3e, 0xd3, 0x74, 0x1c, 0x3d, 0x8a, 0x54, 0x98, 0x3f, 0xcb, 0x0a, 0x68, 0x1b, 0xa4, 0x91, + 0xa3, 0x0d, 0xf0, 0x70, 0x3a, 0x56, 0x1d, 0xec, 0x7a, 0x9a, 0xe3, 0x71, 0x45, 0x22, 0x1c, 0x37, + 0x77, 0x38, 0x85, 0xc2, 0x08, 0x94, 0xda, 0x28, 0x0a, 0x40, 0x9b, 0x50, 0xd2, 0x86, 0x86, 0x4a, + 0x5c, 0x15, 0x77, 0x0d, 0xa8, 0x2c, 0x2e, 0x0a, 0xcb, 0x3c, 0x34, 0xa8, 0x2b, 0x53, 0xd4, 0xd8, + 0x1f, 0x17, 0xdd, 0x82, 0x92, 0xa6, 0xeb, 0x2a, 0x93, 0xdd, 0x72, 0x7c, 0x91, 0xf9, 0xbd, 0xbd, + 0xab, 0x14, 0x35, 0xfe, 0x4f, 0xfe, 0x6d, 0x1e, 0x4a, 0xc1, 0xcb, 0xd6, 0xf7, 0x10, 0xac, 0xf7, + 0x23, 0x82, 0xb5, 0x9a, 0xf2, 0xfa, 0xf6, 0x2f, 0x48, 0xba, 0x3e, 0x80, 0xbc, 0x61, 0x0e, 0x2d, + 0x2e, 0x5c, 0x6b, 0x29, 0x9d, 0x65, 0x12, 0x46, 0xa9, 0xfe, 0x28, 0x60, 0xdf, 0x4b, 0xc0, 0x9e, + 0x42, 0x2d, 0xf6, 0x6c, 0x39, 0x53, 0x43, 0x5e, 0x8a, 0xbc, 0xe3, 0xf2, 0xc8, 0x8f, 0xf0, 0xb9, + 0xf8, 0xd7, 0x19, 0x28, 0x0b, 0xf2, 0x87, 0xee, 0x43, 0xc9, 0x30, 0xa3, 0x92, 0x7a, 0xd2, 0xad, + 0x48, 0xd1, 0x30, 0x79, 0xc5, 0xcf, 0xa0, 0x8a, 0x8f, 0xc8, 0xec, 0xfb, 0x95, 0xb3, 0xa7, 0x56, + 0xae, 0xb0, 0x0a, 0x21, 0x03, 0x63, 0x22, 0x32, 0xc8, 0x9d, 0xce, 0x80, 0x55, 0xe0, 0x2f, 0x22, + 0xff, 0x31, 0x03, 0x65, 0x66, 0x2e, 0x5a, 0xc6, 0xc4, 0x78, 0x85, 0x83, 0x39, 0x7a, 0x1b, 0x2a, + 0x13, 0xed, 0x48, 0x0d, 0x1e, 0x02, 0xd9, 0x49, 0xa9, 0x3c, 0xd1, 0x8e, 0xba, 0x1c, 0x44, 0x4e, + 0xc7, 0x2e, 0x0f, 0x81, 0x50, 0xbd, 0x03, 0x07, 0xbb, 0x07, 0xd6, 0x58, 0x57, 0xed, 0x81, 0xc7, + 0x8f, 0xd1, 0xcb, 0x3e, 0xb6, 0xef, 0x23, 0xbb, 0x03, 0x4f, 0xfe, 0x8b, 0x39, 0x28, 0xfa, 0x46, + 0x00, 0xfd, 0x00, 0xaa, 0xda, 0xd4, 0x3b, 0x50, 0x6d, 0xcd, 0x75, 0x5f, 0x5a, 0x8e, 0xce, 0x17, + 0xaa, 0x42, 0x80, 0x5d, 0x0e, 0x43, 0x57, 0xe9, 0x13, 0xe2, 0xc0, 0x31, 0x6c, 0x21, 0x64, 0x41, + 0x04, 0xa1, 0x0b, 0x50, 0x64, 0xd6, 0x5f, 0x73, 0xfd, 0x23, 0x3c, 0x2d, 0xd7, 0x5d, 0xe2, 0x18, + 0x04, 0xbe, 0x89, 0x7f, 0x18, 0xcc, 0x53, 0x0e, 0x35, 0x1f, 0x5e, 0xe7, 0x87, 0xc2, 0x55, 0x98, + 0xa7, 0x62, 0xa1, 0xb9, 0xfc, 0xa4, 0x5e, 0x20, 0xc5, 0xba, 0x1b, 0x93, 0x97, 0x42, 0x4c, 0x5e, + 0x88, 0xdf, 0x45, 0xd1, 0xf4, 0x08, 0x3c, 0x4f, 0x6b, 0x16, 0x09, 0x80, 0x9e, 0xd3, 0xdb, 0xb0, + 0xe8, 0xe0, 0x89, 0x75, 0x88, 0x55, 0xdb, 0x31, 0x0e, 0x35, 0x8f, 0xf8, 0x6e, 0x74, 0xf3, 0x2d, + 0xdc, 0x95, 0x93, 0x56, 0x71, 0x53, 0xa1, 0xb4, 0x5d, 0x46, 0x5a, 0x77, 0x95, 0x9a, 0x13, 0x05, + 0x10, 0xb7, 0x89, 0xed, 0xe3, 0xe1, 0x58, 0xb3, 0x55, 0x5d, 0x9b, 0xd8, 0x86, 0x39, 0xa2, 0xfb, + 0xb1, 0xa8, 0x48, 0x14, 0xf3, 0x64, 0xac, 0xd9, 0xdb, 0x0c, 0x8e, 0xde, 0x85, 0x05, 0x17, 0x9b, + 0xba, 0xca, 0xe3, 0x3b, 0xbc, 0x63, 0xee, 0x35, 0x56, 0x09, 0x74, 0xcb, 0x07, 0x12, 0x0f, 0x87, + 0xb4, 0xe3, 0x61, 0x75, 0xa0, 0xd9, 0x6b, 0xe5, 0x93, 0x3c, 0x1c, 0x46, 0xb7, 0xa5, 0xd9, 0x44, + 0x09, 0xb1, 0x49, 0x27, 0x75, 0x2a, 0x27, 0xd4, 0x61, 0x6b, 0x43, 0xaa, 0x2c, 0x40, 0xd6, 0xd0, + 0xa9, 0x97, 0x59, 0x52, 0xb2, 0x86, 0x8e, 0x6e, 0x02, 0x0a, 0x1d, 0xc7, 0x20, 0x14, 0x6b, 0x81, + 0xe2, 0x17, 0x03, 0xff, 0xd1, 0x47, 0x10, 0x4f, 0xff, 0xd0, 0x19, 0x72, 0xff, 0x92, 0xfc, 0x45, + 0x57, 0xa1, 0xa2, 0x8d, 0xc7, 0xd6, 0x4b, 0x95, 0xc8, 0xa0, 0xe6, 0x52, 0x07, 0xb3, 0xaa, 0x00, + 0x85, 0x75, 0x5e, 0x9a, 0x75, 0x17, 0x5d, 0x83, 0x9a, 0x83, 0xed, 0xb1, 0x36, 0xc0, 0xaa, 0xbf, + 0xb8, 0xcc, 0xc7, 0xac, 0x72, 0x70, 0x97, 0xae, 0xb1, 0x7c, 0x07, 0x6a, 0xb1, 0xb9, 0x47, 0x45, + 0xc8, 0xf3, 0x93, 0x0b, 0xbf, 0x65, 0xa0, 0x27, 0x74, 0xa5, 0xd1, 0x6d, 0xd5, 0xb7, 0x1a, 0x52, + 0x56, 0xfe, 0x9f, 0x39, 0xa8, 0x46, 0xac, 0xce, 0xef, 0x40, 0x9c, 0x05, 0x19, 0xcd, 0x47, 0x64, + 0xf4, 0x1a, 0xd4, 0x42, 0x19, 0x55, 0xe9, 0xb5, 0xd0, 0x1c, 0x8b, 0xed, 0x09, 0x04, 0xb5, 0xad, + 0x4d, 0x70, 0x54, 0x58, 0x0b, 0x31, 0x61, 0xed, 0xa5, 0x09, 0xeb, 0x3c, 0x15, 0xd6, 0xeb, 0x33, + 0x2c, 0xed, 0xeb, 0x4a, 0x6c, 0xf1, 0xcc, 0x12, 0x5b, 0x4a, 0x91, 0xd8, 0xd7, 0x59, 0xae, 0x7f, + 0xc8, 0xc1, 0x42, 0xd4, 0xee, 0xfe, 0xc1, 0xaf, 0x57, 0x7f, 0xf6, 0x7a, 0xdd, 0x98, 0xe5, 0x6c, + 0xfc, 0x7e, 0x16, 0x8c, 0x9c, 0x11, 0x3d, 0xcb, 0xd3, 0xc6, 0xdc, 0xa2, 0x33, 0x35, 0x04, 0x14, + 0x44, 0xed, 0x37, 0xe1, 0xc3, 0x09, 0x7c, 0x93, 0x53, 0x66, 0x7c, 0x18, 0x8d, 0x1f, 0x63, 0xf2, + 0x1a, 0x0b, 0xff, 0x0c, 0x2a, 0xa2, 0x9b, 0x87, 0xd6, 0x60, 0x9e, 0xdd, 0xe2, 0xea, 0x3c, 0x84, + 0xca, 0x2f, 0x52, 0xa3, 0xc7, 0xa9, 0x54, 0xcf, 0x1b, 0x07, 0x46, 0x8f, 0xc3, 0xfa, 0xde, 0x58, + 0xfe, 0x79, 0x06, 0x16, 0xa2, 0x5e, 0x1f, 0xb1, 0x83, 0x31, 0x47, 0x51, 0x1d, 0x8c, 0x0d, 0xff, + 0x09, 0xa5, 0xa8, 0x2c, 0x47, 0xbd, 0xc2, 0x2d, 0x8a, 0x43, 0x0f, 0x61, 0x3d, 0x59, 0x6b, 0xea, + 0x7a, 0xd8, 0x09, 0x43, 0x55, 0x56, 0xe3, 0x35, 0x29, 0xbe, 0xa9, 0xcb, 0xff, 0x63, 0x9e, 0x79, + 0xd3, 0xbf, 0x2b, 0x31, 0xde, 0x84, 0xe2, 0x04, 0xbb, 0xae, 0x36, 0xc2, 0x2e, 0x77, 0x82, 0x05, + 0x67, 0x6c, 0x97, 0x63, 0x94, 0x80, 0x26, 0xd5, 0xea, 0xce, 0x9d, 0x6a, 0x75, 0x0b, 0x27, 0x58, + 0xdd, 0xf9, 0x13, 0xad, 0x6e, 0x31, 0xb6, 0x31, 0x6e, 0x40, 0xe1, 0xbb, 0x29, 0x9e, 0x62, 0x37, + 0x79, 0xa7, 0xf2, 0x25, 0x85, 0x2b, 0x1c, 0x8f, 0x36, 0xd2, 0xb6, 0x10, 0x93, 0xce, 0x33, 0x6e, + 0x8c, 0xf2, 0x99, 0x37, 0x46, 0x25, 0x6d, 0x63, 0x34, 0xa0, 0xea, 0x62, 0xd7, 0x35, 0x2c, 0x93, + 0xc5, 0x41, 0x50, 0xf3, 0xb8, 0x70, 0xf7, 0x6a, 0xca, 0x49, 0x74, 0xb3, 0xc7, 0x08, 0xd9, 0xa1, + 0xa1, 0xe2, 0x0a, 0x25, 0xf4, 0x31, 0xac, 0xb8, 0x53, 0x9b, 0xb8, 0x7e, 0x58, 0x27, 0x16, 0x59, + 0xdb, 0x37, 0xc6, 0x86, 0x67, 0x60, 0x77, 0x6d, 0x81, 0x3e, 0x78, 0x9c, 0x0f, 0xb0, 0x5b, 0x02, + 0x12, 0x7d, 0x06, 0x65, 0x4d, 0x9f, 0x18, 0x7e, 0xdb, 0xb5, 0xf8, 0x55, 0x68, 0xd8, 0x76, 0x9d, + 0x90, 0xb1, 0x96, 0x41, 0x0b, 0xfe, 0xa3, 0x75, 0x28, 0x3a, 0x78, 0x80, 0x8d, 0x43, 0xac, 0x73, + 0xeb, 0x1b, 0x94, 0x09, 0x2e, 0x88, 0x33, 0x5a, 0x64, 0x38, 0xbf, 0x8c, 0x2e, 0x03, 0x68, 0x61, + 0xac, 0x34, 0xbf, 0xd0, 0x09, 0x21, 0x68, 0x09, 0xe6, 0xac, 0xa9, 0xa7, 0x7e, 0xc7, 0x6f, 0x71, + 0xf2, 0xd6, 0xd4, 0xfb, 0x12, 0x2d, 0xc3, 0xdc, 0x70, 0x6c, 0xd9, 0x2e, 0xbd, 0xb7, 0xa9, 0x2a, + 0xac, 0x20, 0x9b, 0x50, 0x11, 0x27, 0x86, 0x6c, 0xfe, 0xf0, 0x2a, 0xbc, 0x08, 0xf9, 0xe6, 0x76, + 0xab, 0xc1, 0x74, 0xc2, 0x56, 0xa7, 0xdd, 0x6e, 0x6c, 0xf5, 0xa5, 0x2c, 0xbd, 0x76, 0xdf, 0xea, + 0x37, 0x9f, 0x37, 0xa4, 0x1c, 0xaa, 0x40, 0xb1, 0xd3, 0x6d, 0xb4, 0x7b, 0x8d, 0x76, 0x5f, 0xca, + 0xa3, 0x1a, 0x94, 0x49, 0x69, 0xab, 0xd3, 0x7e, 0xd2, 0x54, 0x76, 0xa5, 0x39, 0x02, 0x68, 0xf4, + 0xfa, 0xf5, 0xc7, 0xad, 0x66, 0xef, 0x69, 0x63, 0x5b, 0x2a, 0xc8, 0x1b, 0x00, 0xe1, 0x64, 0xa0, + 0x02, 0x64, 0xf7, 0xba, 0xac, 0xa1, 0x6d, 0xd2, 0x64, 0x86, 0xf0, 0xee, 0x3e, 0x79, 0xa1, 0x92, + 0x76, 0xe4, 0x9f, 0x40, 0xd1, 0xdf, 0x1e, 0xe8, 0xa6, 0x30, 0x55, 0xcc, 0xfd, 0x5e, 0x4c, 0x6c, + 0x22, 0x61, 0xf6, 0xde, 0x85, 0xbc, 0xeb, 0x87, 0xd4, 0xa6, 0x92, 0x52, 0xb4, 0xfc, 0xe7, 0x19, + 0x98, 0xe7, 0x10, 0x24, 0x43, 0xc5, 0xb4, 0x3c, 0x63, 0xe8, 0x47, 0xf7, 0xb2, 0xe8, 0xb0, 0x08, + 0x8c, 0x1c, 0x8a, 0xa6, 0x34, 0xce, 0x94, 0x47, 0x86, 0xf1, 0x12, 0x42, 0x90, 0xb7, 0x6c, 0x6c, + 0xf2, 0x07, 0x23, 0xfa, 0x1f, 0xbd, 0x05, 0xa5, 0x6f, 0x31, 0xb6, 0xb5, 0xb1, 0x71, 0xc8, 0x9e, + 0xbc, 0xf2, 0x4a, 0x08, 0x20, 0x7a, 0xd4, 0xc1, 0x43, 0xe2, 0xda, 0xd3, 0xbd, 0x9d, 0x57, 0xfc, + 0x22, 0xa9, 0xa7, 0x1b, 0xee, 0x40, 0x73, 0x74, 0xac, 0xd3, 0x5d, 0x9d, 0x57, 0x42, 0x00, 0x59, + 0x45, 0xaa, 0xd3, 0xe9, 0x9e, 0xce, 0x2b, 0xac, 0x20, 0xdf, 0x83, 0x02, 0xdb, 0x9a, 0x04, 0x6f, + 0x98, 0xf6, 0x94, 0xa9, 0xcf, 0xaa, 0xc2, 0x0a, 0xa4, 0xdf, 0xd6, 0xd4, 0x23, 0x60, 0xa6, 0x95, + 0x79, 0x49, 0xc6, 0x50, 0x60, 0xa7, 0x63, 0xb4, 0x09, 0x05, 0x72, 0xea, 0x37, 0x46, 0x7c, 0x76, + 0x57, 0xe2, 0xe7, 0xe7, 0x2d, 0x8a, 0x55, 0x38, 0x15, 0x7a, 0xdf, 0xbf, 0xfb, 0xc9, 0xc6, 0xcf, + 0xc2, 0x8c, 0x5c, 0xbc, 0xfd, 0x21, 0xd3, 0x5c, 0x11, 0xb9, 0x10, 0xa5, 0x3b, 0xb0, 0x4c, 0x13, + 0x0f, 0x3c, 0xd5, 0xc1, 0x9e, 0x73, 0xec, 0x4f, 0x36, 0x07, 0x2a, 0x04, 0x46, 0x94, 0x14, 0x3d, + 0x14, 0xd1, 0x67, 0x6d, 0x36, 0xdf, 0x45, 0x02, 0x20, 0x9c, 0x88, 0xf7, 0x1b, 0x4c, 0xa6, 0x1a, + 0x7b, 0xb0, 0x5b, 0x0c, 0x30, 0x4d, 0x8e, 0x40, 0xdb, 0x70, 0x79, 0x62, 0x98, 0xc6, 0x64, 0x3a, + 0x51, 0x83, 0x7d, 0x42, 0x8e, 0x7c, 0x61, 0x55, 0xb6, 0x42, 0x6f, 0x71, 0xaa, 0xba, 0x48, 0xe4, + 0x73, 0x91, 0x7f, 0x95, 0x85, 0xb2, 0x30, 0xbc, 0x3f, 0xd0, 0x61, 0xd0, 0x6b, 0x76, 0x3c, 0xb2, + 0x3c, 0x43, 0x23, 0xfa, 0x2e, 0xec, 0x1c, 0x13, 0x44, 0x14, 0xe2, 0x9e, 0xfa, 0xdd, 0xa4, 0x72, + 0x4f, 0x69, 0x0a, 0xbe, 0xdc, 0x93, 0x12, 0x51, 0x52, 0xe4, 0x8c, 0x4a, 0x31, 0x4c, 0x20, 0x83, + 0xb2, 0xfc, 0x8f, 0x19, 0x28, 0x05, 0xb7, 0x29, 0x64, 0xaa, 0xb8, 0x7d, 0x8c, 0x3c, 0x2a, 0x56, + 0x98, 0x91, 0xe4, 0xe6, 0xec, 0x12, 0x00, 0x23, 0x12, 0xde, 0x17, 0xd9, 0x39, 0xa9, 0xcb, 0x79, + 0x4c, 0xbc, 0xa9, 0x4a, 0x36, 0x83, 0x75, 0x88, 0x9d, 0x63, 0xfe, 0x9c, 0x5e, 0x99, 0x78, 0xd3, + 0x6d, 0x1f, 0x46, 0xdc, 0x10, 0x62, 0xca, 0xc9, 0x7c, 0x4e, 0x2c, 0xdd, 0x7f, 0x6c, 0x2e, 0x73, + 0xd8, 0xae, 0xa5, 0x63, 0x62, 0x5c, 0xf8, 0x89, 0x2d, 0x6a, 0x5e, 0xab, 0x0c, 0xea, 0xf7, 0xe6, + 0x0a, 0x94, 0x39, 0x19, 0xed, 0x0e, 0x33, 0xb0, 0xfc, 0xac, 0x47, 0xfb, 0xb3, 0x0a, 0xf3, 0xde, + 0xc0, 0x56, 0x27, 0xae, 0xcb, 0x4f, 0xae, 0x05, 0x6f, 0x60, 0xef, 0xba, 0xae, 0xfc, 0x08, 0xca, + 0xc2, 0x8d, 0x10, 0xda, 0x84, 0x25, 0xf1, 0xfa, 0x28, 0xea, 0xe0, 0x2c, 0x0a, 0xd7, 0x45, 0xcc, + 0xbb, 0x91, 0xff, 0x77, 0x0e, 0x6a, 0xb1, 0x3b, 0xa1, 0x93, 0xfd, 0x2e, 0x7e, 0xb3, 0x14, 0x8a, + 0x58, 0x55, 0x29, 0x73, 0x18, 0x5d, 0xbe, 0x2b, 0x50, 0x3e, 0xc0, 0x63, 0x1b, 0x3b, 0xaa, 0x65, + 0x8e, 0xfd, 0x69, 0x03, 0x06, 0xea, 0x98, 0xe3, 0x63, 0x32, 0xb3, 0x3a, 0x1e, 0x62, 0xc7, 0xd1, + 0xc6, 0x8c, 0x09, 0xf3, 0xb7, 0x2b, 0x3e, 0x90, 0x72, 0xb9, 0x03, 0xcb, 0xa2, 0x32, 0x54, 0xfd, + 0xfe, 0xb0, 0xe7, 0xa0, 0x25, 0x11, 0xd7, 0xe0, 0x7d, 0x7b, 0x1f, 0x16, 0xc7, 0x96, 0x39, 0x22, + 0x32, 0xac, 0x07, 0xf4, 0x05, 0x66, 0xf3, 0x03, 0x84, 0x4f, 0xbc, 0x01, 0x8b, 0xf4, 0xf1, 0x44, + 0xa5, 0x33, 0xe2, 0xaa, 0x81, 0x54, 0x55, 0x95, 0x1a, 0x45, 0xd0, 0x39, 0x75, 0x69, 0x5f, 0x36, + 0x60, 0x91, 0x3a, 0x30, 0x91, 0x91, 0x33, 0x47, 0x86, 0x1e, 0x0d, 0x14, 0x61, 0xf4, 0xd7, 0xf9, + 0x69, 0x81, 0xd3, 0x86, 0x47, 0xfe, 0x05, 0x81, 0x92, 0x38, 0x1d, 0xc1, 0x3b, 0x88, 0x40, 0x09, + 0x94, 0x92, 0xbd, 0x83, 0x08, 0xa4, 0x08, 0xf2, 0x54, 0xba, 0xd8, 0x4b, 0x11, 0xfd, 0x2f, 0x7f, + 0x08, 0xab, 0xbb, 0x76, 0x6c, 0xdd, 0xb8, 0xbe, 0x9b, 0xb9, 0x7a, 0xf2, 0x9f, 0x65, 0x60, 0x25, + 0x51, 0x8b, 0x69, 0x97, 0xd9, 0x4b, 0x2e, 0xfa, 0x0d, 0x2c, 0xd0, 0x2a, 0xb4, 0x7c, 0x51, 0xdf, + 0x80, 0x2f, 0xb5, 0xe0, 0x1b, 0xdc, 0x84, 0x25, 0xe2, 0x58, 0x59, 0x43, 0xd5, 0x31, 0xf6, 0xd5, + 0x80, 0x4d, 0xde, 0xff, 0xe0, 0x4a, 0xef, 0x0c, 0x15, 0x63, 0x5f, 0x09, 0x0d, 0x69, 0x4d, 0x20, + 0xa7, 0x36, 0x95, 0xad, 0x77, 0xc5, 0x27, 0xed, 0x11, 0x91, 0xfd, 0x45, 0x06, 0x16, 0x13, 0xc3, + 0x40, 0x3f, 0x8c, 0x19, 0x95, 0xb7, 0x05, 0x3b, 0x9c, 0x3e, 0x53, 0x81, 0x7d, 0xb9, 0x17, 0xb5, + 0x2f, 0x57, 0x4f, 0xa8, 0x19, 0x31, 0x35, 0x3d, 0xa8, 0xf2, 0xfb, 0x50, 0x3e, 0xf5, 0x67, 0xbf, + 0xb5, 0x13, 0xe6, 0x3b, 0x1b, 0x5d, 0xa4, 0xff, 0x92, 0x81, 0x0a, 0xe7, 0xda, 0xf3, 0x9f, 0x33, + 0xbe, 0x2f, 0xd3, 0xf8, 0xa1, 0x2e, 0x77, 0x86, 0x43, 0x5d, 0x3e, 0xed, 0x50, 0xf7, 0x9f, 0x73, + 0x70, 0x91, 0x6b, 0x9b, 0x31, 0x8b, 0x82, 0x63, 0x2f, 0x51, 0xbe, 0xad, 0xfd, 0x00, 0x90, 0x36, + 0x7e, 0xa9, 0x1d, 0xbb, 0xc4, 0x99, 0xb6, 0x35, 0x07, 0xab, 0x93, 0x40, 0xa2, 0x24, 0x86, 0xd9, + 0x62, 0x88, 0x5d, 0xac, 0xa3, 0x3b, 0x70, 0xde, 0x18, 0x99, 0x96, 0x43, 0x5c, 0x79, 0xda, 0x33, + 0xff, 0x4d, 0x9c, 0x07, 0x17, 0x33, 0x64, 0xdd, 0x25, 0x5d, 0x64, 0xef, 0xe0, 0xe4, 0x30, 0xe6, + 0xbf, 0x71, 0x06, 0x4d, 0xd0, 0x2d, 0x4c, 0x0f, 0x63, 0x4c, 0x02, 0x57, 0x7d, 0x0a, 0xde, 0x14, + 0xed, 0xb0, 0xd3, 0xd4, 0xd1, 0x03, 0xb8, 0x10, 0x08, 0xa7, 0x6a, 0x98, 0xda, 0xc0, 0x23, 0x9a, + 0x9b, 0x69, 0x00, 0x2e, 0x94, 0xab, 0x01, 0x41, 0x93, 0xe3, 0x99, 0x22, 0x20, 0x33, 0xc8, 0x26, + 0x53, 0xd5, 0x8c, 0x91, 0xed, 0x3f, 0x4b, 0xf3, 0x6f, 0x06, 0x8d, 0x91, 0x8d, 0x1e, 0xc0, 0x3a, + 0x1f, 0x8c, 0x89, 0x8f, 0x3c, 0x95, 0xbe, 0x95, 0x8e, 0x6c, 0x75, 0x82, 0x3d, 0xc7, 0x18, 0x70, + 0x3d, 0xb4, 0xc2, 0x28, 0xda, 0xf8, 0xc8, 0x7b, 0x6a, 0xd9, 0xcd, 0x91, 0xbd, 0x4b, 0xb1, 0xe8, + 0x11, 0x5c, 0xd4, 0x59, 0xac, 0x8f, 0xba, 0x8f, 0x5d, 0x8f, 0xcd, 0x85, 0xeb, 0x4f, 0x31, 0xd5, + 0x4b, 0x45, 0x65, 0x8d, 0x93, 0x3c, 0xc6, 0xec, 0xcb, 0x8d, 0x60, 0x09, 0xe4, 0xff, 0x94, 0x83, + 0xf5, 0xd4, 0x55, 0x61, 0x02, 0xf4, 0xc7, 0x45, 0xf9, 0xbd, 0x2c, 0x4a, 0x06, 0xce, 0xa7, 0x2e, + 0x0a, 0x7a, 0x14, 0xd3, 0x54, 0xef, 0x26, 0xde, 0x76, 0xd2, 0xf6, 0x56, 0xa0, 0xad, 0x1e, 0x44, + 0xb5, 0xd5, 0x3b, 0xa7, 0xd4, 0x8e, 0x68, 0xac, 0xbb, 0xb0, 0xb2, 0xe7, 0x62, 0x7a, 0xc1, 0x62, + 0x8f, 0xe9, 0xe7, 0x56, 0xee, 0xa9, 0x56, 0xe3, 0x0e, 0x9c, 0x8f, 0xd7, 0x39, 0xc5, 0x66, 0xc8, + 0xdf, 0x00, 0x34, 0xf6, 0x47, 0x36, 0x67, 0xbd, 0x01, 0x8b, 0xec, 0xee, 0x77, 0xc2, 0x79, 0x90, + 0x93, 0x3b, 0xab, 0x51, 0xa3, 0x08, 0x9f, 0x77, 0xdd, 0xa5, 0x6e, 0x97, 0x76, 0x44, 0x9d, 0x4e, + 0xff, 0xb9, 0x9d, 0x3a, 0x07, 0x1c, 0xc8, 0x5e, 0x90, 0xfe, 0x15, 0x94, 0x08, 0x7b, 0xd6, 0x8b, + 0x37, 0xce, 0x5d, 0x85, 0x3c, 0xe1, 0x8e, 0x3e, 0x88, 0x2d, 0xd3, 0x72, 0xf4, 0x31, 0x32, 0xb6, + 0x2a, 0xef, 0x45, 0x57, 0x65, 0x29, 0x4a, 0x1c, 0x59, 0x84, 0x3b, 0x00, 0xcd, 0x70, 0x76, 0x12, + 0x7d, 0xca, 0xa4, 0xf4, 0xe9, 0x36, 0x94, 0x9a, 0xc1, 0x88, 0xcf, 0x54, 0x43, 0x85, 0x7c, 0xf3, + 0x94, 0x51, 0x34, 0x5f, 0x65, 0x14, 0xcd, 0xf8, 0x28, 0x7e, 0x93, 0x01, 0x29, 0x2e, 0x17, 0xe8, + 0x93, 0x58, 0x6b, 0x82, 0x29, 0x4d, 0x97, 0xbb, 0xa0, 0xe5, 0x8f, 0xa3, 0x2d, 0x5f, 0x99, 0x5d, + 0x31, 0xf2, 0xd6, 0x2f, 0x43, 0x1e, 0xef, 0x8f, 0xec, 0xe4, 0x67, 0x88, 0x64, 0xd6, 0x15, 0x8a, + 0x23, 0x34, 0x06, 0xa1, 0x49, 0x7c, 0xd8, 0xd7, 0xa4, 0x34, 0x04, 0x27, 0x3f, 0xe6, 0x76, 0xad, + 0xaf, 0x39, 0x23, 0xec, 0xed, 0xe2, 0xc9, 0x3e, 0x76, 0xdc, 0x03, 0x43, 0x58, 0xa4, 0xa8, 0xcf, + 0x9a, 0x49, 0xfa, 0xac, 0x72, 0x9d, 0x6b, 0xe1, 0x38, 0x8f, 0x60, 0xd5, 0x4e, 0x67, 0x11, 0x28, + 0x8d, 0x38, 0x8f, 0x53, 0x95, 0x46, 0x7a, 0xc7, 0xcf, 0xaa, 0x34, 0x52, 0xbb, 0xec, 0xaf, 0xf4, + 0x37, 0x70, 0xb9, 0x65, 0x99, 0xa3, 0x16, 0xf1, 0xd1, 0x5e, 0xd1, 0xe5, 0x3c, 0xc3, 0x81, 0x41, + 0xfe, 0x6d, 0x06, 0x2e, 0xcd, 0xe2, 0xff, 0xcf, 0xe9, 0x9c, 0xa6, 0xba, 0xf5, 0xf9, 0x74, 0xb7, + 0xfe, 0x21, 0xac, 0x27, 0x68, 0x1d, 0x15, 0x1f, 0xd9, 0x86, 0x13, 0x1c, 0x4a, 0x56, 0x63, 0x95, + 0x9c, 0x06, 0x43, 0xcb, 0xff, 0x35, 0x03, 0x6b, 0xb3, 0x06, 0x88, 0x3e, 0x8f, 0xad, 0xab, 0xf0, + 0x1c, 0x70, 0xf2, 0xa4, 0x07, 0x4b, 0xfb, 0x28, 0xba, 0xb4, 0xd7, 0x4f, 0x67, 0x10, 0x59, 0xdd, + 0x5f, 0xce, 0xc1, 0x3c, 0xf7, 0x37, 0xd1, 0x33, 0x58, 0x9a, 0xd8, 0x6a, 0x22, 0x88, 0x80, 0xf5, + 0xec, 0xe2, 0x09, 0x6e, 0xb1, 0xb2, 0x38, 0x49, 0x38, 0xe4, 0xb7, 0x82, 0x91, 0x25, 0x42, 0x40, + 0x22, 0x5e, 0x73, 0x30, 0x90, 0x78, 0xac, 0x49, 0xee, 0xcc, 0xb1, 0x26, 0x5f, 0xc1, 0xaa, 0x7f, + 0xe8, 0xe5, 0xc6, 0x8f, 0x87, 0x54, 0xf9, 0x97, 0xe0, 0x57, 0x4e, 0x31, 0x92, 0xca, 0x79, 0x27, + 0xd5, 0x54, 0x3f, 0x05, 0x34, 0x75, 0x71, 0x68, 0x5a, 0x98, 0xbe, 0x9d, 0x8b, 0x3f, 0xee, 0xc7, + 0x55, 0x94, 0x22, 0x4d, 0xe3, 0x9a, 0xf1, 0x01, 0x54, 0x99, 0x6b, 0xad, 0x8e, 0x8d, 0x89, 0xe1, + 0xf9, 0xa1, 0x23, 0xc2, 0xe8, 0x84, 0xe7, 0x7f, 0xa5, 0x62, 0x87, 0x05, 0x37, 0x1c, 0x9e, 0x47, + 0xb7, 0xa9, 0x3a, 0x09, 0xf6, 0x29, 0x8f, 0x29, 0xb9, 0x72, 0xca, 0x76, 0xe6, 0xc3, 0x4b, 0x28, + 0x15, 0x0d, 0x2e, 0x92, 0x93, 0xb1, 0xca, 0xce, 0xcc, 0x89, 0x75, 0x67, 0xa1, 0x27, 0xf2, 0xe9, + 0x02, 0xa5, 0xac, 0x8d, 0x67, 0xc9, 0x77, 0x24, 0x3c, 0xa4, 0x74, 0x86, 0xf0, 0x90, 0x46, 0xf0, + 0xc5, 0xba, 0xe0, 0x9a, 0xf0, 0x4d, 0xed, 0x6f, 0x7f, 0x5e, 0x44, 0x17, 0xa0, 0x48, 0x6f, 0xee, + 0x27, 0xda, 0x11, 0xd7, 0x2c, 0xf3, 0xa4, 0xbc, 0xab, 0x1d, 0xc9, 0xdb, 0x34, 0x50, 0x3e, 0xea, + 0xad, 0xbc, 0x3a, 0x97, 0x6f, 0xa1, 0xe8, 0x73, 0x41, 0xb7, 0x63, 0x3b, 0x75, 0x2d, 0x39, 0x8c, + 0x98, 0x40, 0xdf, 0x8c, 0xee, 0xcc, 0xd5, 0x64, 0x85, 0xc8, 0x4e, 0x9c, 0x42, 0x81, 0x47, 0x0c, + 0x5e, 0x84, 0x92, 0x61, 0xab, 0x91, 0x90, 0x98, 0xa2, 0xe1, 0x87, 0x13, 0x5e, 0x83, 0xda, 0x44, + 0x73, 0xbf, 0xe5, 0x6e, 0xb9, 0x3a, 0xe1, 0x79, 0x0a, 0xaa, 0x4a, 0x95, 0x80, 0x99, 0x4b, 0xbe, + 0x6b, 0x98, 0x09, 0x3a, 0xed, 0x88, 0x9f, 0xfb, 0x44, 0x3a, 0xed, 0x48, 0xfe, 0x65, 0x06, 0x20, + 0xfc, 0x6e, 0xee, 0x7b, 0x7e, 0xbb, 0x47, 0x60, 0x63, 0xc3, 0xf5, 0x68, 0xe8, 0x74, 0x49, 0xa1, + 0xff, 0xe9, 0x67, 0x94, 0xd1, 0xef, 0xd4, 0xa5, 0xb8, 0xd8, 0x0b, 0x1f, 0xa7, 0xef, 0x40, 0x71, + 0x57, 0xf3, 0x06, 0x07, 0xa4, 0x33, 0xd7, 0x23, 0x9d, 0x11, 0xdc, 0x11, 0x4a, 0x71, 0xca, 0x67, + 0x84, 0xcf, 0xa1, 0x12, 0x39, 0xa6, 0x6c, 0x46, 0x98, 0x09, 0xdb, 0x57, 0xa4, 0x12, 0x78, 0xae, + 0x40, 0x41, 0x38, 0xfa, 0x54, 0x15, 0x5e, 0x92, 0xff, 0x72, 0x0e, 0x60, 0xcb, 0x32, 0x75, 0x83, + 0xe9, 0x88, 0x3b, 0x00, 0x7c, 0x67, 0x87, 0x9f, 0x24, 0xa2, 0x58, 0x4f, 0x7b, 0xd8, 0x53, 0x4a, + 0x8c, 0x8a, 0x0c, 0xeb, 0x63, 0xa8, 0x04, 0xaf, 0x6e, 0xa4, 0x52, 0x76, 0x66, 0xa5, 0x20, 0x5e, + 0x97, 0x54, 0xfb, 0x11, 0x2c, 0xc4, 0xce, 0x64, 0x89, 0x30, 0x39, 0x71, 0x28, 0x4a, 0x45, 0x13, + 0x87, 0x7f, 0x17, 0xca, 0x7e, 0x6d, 0xd2, 0x66, 0x7e, 0x76, 0x47, 0x59, 0x35, 0xd2, 0xe2, 0xfd, + 0x20, 0x0d, 0x89, 0x77, 0x4c, 0x6b, 0xcd, 0xcd, 0xac, 0x55, 0x09, 0x08, 0x49, 0xc5, 0x4f, 0x61, + 0x91, 0x1c, 0xb8, 0xa2, 0x95, 0x0b, 0x33, 0x2b, 0xd7, 0xf0, 0x91, 0xb7, 0x25, 0xd6, 0xbf, 0x02, + 0x65, 0xc7, 0xfe, 0xd6, 0x20, 0xaa, 0x68, 0x3a, 0x66, 0xa1, 0x73, 0x73, 0x0a, 0x38, 0xec, 0xc3, + 0xae, 0xe9, 0xd8, 0x43, 0x8f, 0x00, 0xc2, 0x8f, 0x85, 0x78, 0x9c, 0x8e, 0xf0, 0x1c, 0x16, 0xae, + 0x0f, 0xd7, 0x88, 0x64, 0x59, 0x4b, 0xc1, 0xb7, 0x44, 0xe8, 0x31, 0x2c, 0x8d, 0x89, 0x36, 0x8c, + 0xf5, 0xb0, 0x34, 0xb3, 0x87, 0x8b, 0x94, 0x3c, 0xd2, 0xc7, 0xeb, 0x20, 0x85, 0xa7, 0x4a, 0x53, + 0xa5, 0x62, 0x0f, 0x54, 0xec, 0xab, 0x26, 0x3f, 0x4c, 0x9a, 0x2d, 0x22, 0xff, 0xb7, 0xa1, 0xec, + 0x87, 0xd4, 0xa9, 0x86, 0xc9, 0xc3, 0x76, 0x92, 0xd7, 0x38, 0x25, 0x1e, 0x53, 0xd7, 0x34, 0xe5, + 0x03, 0x28, 0x05, 0xdd, 0x46, 0x4b, 0x50, 0xe3, 0x1f, 0x5f, 0x7c, 0xdd, 0x0d, 0xe2, 0xfe, 0x57, + 0x61, 0x49, 0x00, 0x36, 0xdb, 0xfd, 0x86, 0xd2, 0xae, 0xb7, 0xa4, 0x4c, 0x0c, 0xd1, 0x78, 0xc1, + 0x11, 0x59, 0xb4, 0x0c, 0x92, 0x80, 0xe0, 0xdf, 0xe5, 0xc8, 0x43, 0xa8, 0x05, 0x83, 0xaa, 0xb3, + 0x64, 0x3a, 0x77, 0x22, 0xfb, 0xe4, 0x92, 0x38, 0xa9, 0x11, 0x42, 0x61, 0xab, 0x5c, 0x85, 0xb2, + 0x3f, 0x91, 0x46, 0xf0, 0xe9, 0x96, 0x08, 0x92, 0xdb, 0x50, 0xda, 0xc5, 0x3a, 0x6f, 0xe1, 0xfd, + 0x48, 0x0b, 0xab, 0xe2, 0x8b, 0x99, 0x9e, 0xe0, 0xbd, 0x0c, 0x73, 0x87, 0xda, 0x78, 0xca, 0xf6, + 0x76, 0x4e, 0x61, 0x05, 0x59, 0x85, 0x5a, 0xdd, 0xed, 0x3a, 0xd8, 0xc6, 0xa6, 0xcf, 0x55, 0x82, + 0x9c, 0xe6, 0x9a, 0xdc, 0xaf, 0x26, 0x7f, 0xc9, 0x0e, 0x26, 0x14, 0x5a, 0xf0, 0x14, 0xc5, 0x4a, + 0x48, 0x86, 0x2a, 0x31, 0xeb, 0x63, 0x3c, 0xf4, 0xd4, 0x89, 0xe5, 0xfa, 0xdf, 0xca, 0x95, 0xa7, + 0x2e, 0x6e, 0xe1, 0xa1, 0xb7, 0x6b, 0xb9, 0x9e, 0xfc, 0x08, 0xaa, 0x3c, 0xb4, 0x9d, 0xb3, 0x3f, + 0xf1, 0x23, 0x25, 0x17, 0x8f, 0x87, 0xdc, 0x03, 0xa5, 0xff, 0xe5, 0xeb, 0x50, 0x6b, 0xd1, 0xc7, + 0x04, 0x07, 0x0f, 0x39, 0x83, 0x60, 0x20, 0xfc, 0xb9, 0x8c, 0x0d, 0xe4, 0xaf, 0x72, 0x30, 0xcf, + 0x08, 0xdc, 0x30, 0xf6, 0x53, 0x1b, 0x04, 0xcf, 0x82, 0x0b, 0x89, 0xd8, 0x4f, 0x46, 0xcd, 0x63, + 0x3f, 0x39, 0xef, 0xfb, 0x50, 0x0a, 0x9f, 0xaf, 0xb3, 0xf1, 0xa0, 0xcf, 0xd8, 0xc2, 0x29, 0x21, + 0x2d, 0x7a, 0x17, 0x72, 0x13, 0xee, 0x1e, 0x47, 0xce, 0x7b, 0xc1, 0x4a, 0x28, 0x04, 0x8f, 0x3e, + 0x01, 0x20, 0xca, 0x83, 0xcd, 0x37, 0xd7, 0x1d, 0x17, 0x22, 0x6a, 0x47, 0x5c, 0x0a, 0xaa, 0x42, + 0x18, 0x00, 0x7d, 0x0a, 0xd5, 0x88, 0x26, 0xe0, 0x2a, 0xe4, 0x84, 0xde, 0x55, 0x44, 0x65, 0x80, + 0xee, 0xc0, 0x3c, 0xff, 0xf6, 0x80, 0xeb, 0x0f, 0x41, 0x5c, 0x22, 0x0b, 0xa4, 0xf8, 0x74, 0xa4, + 0xb3, 0xfc, 0x69, 0xc7, 0xc1, 0x43, 0xee, 0x22, 0x5d, 0x10, 0xbd, 0x98, 0xc8, 0xba, 0xf8, 0xaf, + 0x3e, 0x0e, 0x1e, 0xa2, 0xc7, 0x50, 0x8b, 0xa9, 0x05, 0xee, 0x04, 0x9d, 0xd0, 0xdd, 0x85, 0xa8, + 0x66, 0x90, 0x7f, 0x96, 0x81, 0x52, 0xf0, 0x91, 0x75, 0xea, 0x17, 0xc7, 0x1f, 0x01, 0x0c, 0x02, + 0xfd, 0xc4, 0x57, 0x6b, 0x39, 0x4d, 0x77, 0x29, 0x02, 0x1d, 0x7a, 0x1f, 0xe6, 0x99, 0x58, 0xb8, + 0x7c, 0xb5, 0xc4, 0xb0, 0x5c, 0x86, 0x50, 0x7c, 0x0a, 0xf9, 0x4b, 0x28, 0x70, 0xdf, 0x38, 0xad, + 0x03, 0x1f, 0x02, 0x44, 0x52, 0x39, 0xe4, 0x66, 0x7d, 0x22, 0x2e, 0x90, 0xd1, 0x03, 0x7f, 0x3c, + 0x8a, 0xf5, 0x8d, 0x7e, 0x50, 0x1d, 0x49, 0xb7, 0x90, 0x3f, 0x43, 0xba, 0x85, 0x05, 0x1d, 0x0f, + 0xb5, 0xe9, 0xd8, 0xf3, 0xb7, 0xcb, 0xdc, 0x49, 0xdb, 0xa5, 0xca, 0x89, 0x59, 0x51, 0x3e, 0x84, + 0x2a, 0xc1, 0x86, 0x1f, 0x32, 0xfe, 0x6e, 0x92, 0x44, 0xc8, 0xff, 0x21, 0x03, 0x39, 0xc5, 0xd2, + 0xd0, 0x02, 0x64, 0x35, 0xff, 0xe6, 0x26, 0xab, 0xd1, 0x1c, 0x77, 0xcc, 0x3f, 0x18, 0x63, 0xdf, + 0x9f, 0x0b, 0x01, 0x44, 0x91, 0x4d, 0x34, 0x8a, 0x62, 0x2e, 0x1c, 0x2f, 0x09, 0x81, 0xd3, 0xf9, + 0x48, 0xe0, 0xb4, 0xff, 0x55, 0x47, 0xc2, 0x5c, 0x2b, 0xdd, 0x67, 0xcd, 0x30, 0xee, 0x5e, 0xfe, + 0x3f, 0x19, 0xc8, 0x3d, 0x77, 0x86, 0xa9, 0xcb, 0xf8, 0x0e, 0x64, 0x1d, 0x5d, 0x90, 0xce, 0x64, + 0xbc, 0x68, 0xd6, 0xd1, 0xd1, 0x1d, 0x28, 0xf1, 0xc8, 0x67, 0xc7, 0xe3, 0xdf, 0xcb, 0xcd, 0x08, + 0x2e, 0x65, 0x64, 0x0a, 0xcd, 0x94, 0xc0, 0xa3, 0xad, 0x1d, 0x8f, 0xaf, 0xf3, 0x8c, 0x2a, 0x8c, + 0x4c, 0xf1, 0x78, 0x3c, 0x2a, 0x0b, 0xf6, 0xcd, 0x1a, 0xba, 0xfc, 0x8b, 0x0c, 0x2c, 0x6f, 0xb3, + 0xf5, 0xa4, 0x6b, 0xbc, 0x6d, 0xb8, 0x9e, 0x66, 0x0e, 0x30, 0xba, 0x07, 0xc1, 0x45, 0x33, 0xbb, + 0x43, 0x56, 0x75, 0x8e, 0xe2, 0x73, 0x7d, 0xde, 0x47, 0x27, 0xea, 0xd1, 0x67, 0xed, 0x94, 0x7a, + 0x6c, 0x31, 0xce, 0xfb, 0xe8, 0x48, 0x3d, 0xf9, 0x4f, 0xf2, 0x50, 0x60, 0x29, 0x11, 0x13, 0x2b, + 0x7a, 0x11, 0x4a, 0xe1, 0x25, 0x38, 0xff, 0xf2, 0xd7, 0xf1, 0x6f, 0xbd, 0xaf, 0x40, 0x99, 0xf8, + 0x0b, 0xd8, 0x64, 0xef, 0xbd, 0x39, 0xe6, 0xdf, 0x30, 0x10, 0x7d, 0xef, 0x7d, 0x0f, 0x24, 0x4e, + 0xc0, 0xad, 0x0c, 0xdf, 0x13, 0x25, 0xa5, 0xc6, 0xe0, 0x75, 0x1f, 0x4c, 0xbf, 0x18, 0x23, 0x1e, + 0x04, 0x11, 0xc0, 0xb9, 0xab, 0xb9, 0x1b, 0x55, 0x25, 0x28, 0xa3, 0x0f, 0x52, 0x0f, 0xb0, 0xfc, + 0x55, 0x34, 0x71, 0x48, 0xfd, 0x66, 0xf6, 0x39, 0x7a, 0xfe, 0x55, 0xae, 0xaa, 0x67, 0x9c, 0xa6, + 0xfb, 0xb0, 0xe2, 0xef, 0xd8, 0xd8, 0x1c, 0x33, 0x25, 0x7b, 0x39, 0xb2, 0xd9, 0x12, 0x8b, 0xab, + 0x2c, 0xeb, 0x69, 0x4b, 0xfe, 0x88, 0x06, 0x46, 0x0c, 0xb1, 0x8e, 0x1d, 0x16, 0x4c, 0x53, 0x8a, + 0x9b, 0x89, 0x2d, 0x11, 0xad, 0x44, 0xa9, 0x53, 0xbf, 0x9a, 0x80, 0x57, 0xfe, 0x6a, 0x22, 0x7e, + 0x77, 0x51, 0x3e, 0xeb, 0xdd, 0x85, 0x6c, 0x41, 0x35, 0xd2, 0xbf, 0x13, 0x6e, 0xbb, 0xa2, 0xdf, + 0xd6, 0x65, 0x13, 0xdf, 0xd6, 0xbd, 0x03, 0x0b, 0xec, 0x6a, 0x40, 0xd5, 0x5c, 0x35, 0x38, 0x85, + 0x55, 0x95, 0x0a, 0x83, 0xd6, 0x5d, 0xe2, 0x8d, 0xca, 0x0d, 0x28, 0xfa, 0x5a, 0xe0, 0x04, 0x9f, + 0x26, 0x16, 0x90, 0x90, 0x8d, 0x07, 0x24, 0xc8, 0xff, 0x2d, 0x0f, 0x25, 0xc2, 0x87, 0x9d, 0xae, + 0xc3, 0xa0, 0x8d, 0x0c, 0xf5, 0xd2, 0xd2, 0x82, 0x36, 0x98, 0xff, 0x16, 0x94, 0xc9, 0x86, 0x99, + 0xda, 0xdc, 0xf5, 0xca, 0x4e, 0x6d, 0xd6, 0xe4, 0xc0, 0x72, 0x74, 0xd5, 0xb0, 0x0f, 0x3f, 0xe2, + 0x57, 0x71, 0xc0, 0x40, 0x4d, 0xfb, 0xf0, 0xa3, 0x28, 0xc1, 0x3d, 0xae, 0x0e, 0x42, 0x82, 0x7b, + 0x34, 0xf1, 0x14, 0x3b, 0x8a, 0x51, 0x0e, 0x3c, 0x8a, 0x82, 0x81, 0x7c, 0x0e, 0x21, 0xc1, 0x3d, + 0xfe, 0xe0, 0x1f, 0x12, 0xdc, 0x23, 0xe3, 0x70, 0xb1, 0x63, 0x68, 0x63, 0xfe, 0xc0, 0xcf, 0x4b, + 0xe8, 0x07, 0x50, 0xf5, 0x2f, 0x16, 0x19, 0xef, 0x12, 0x1d, 0x4c, 0xc5, 0x07, 0x52, 0xee, 0x31, + 0xa2, 0x7b, 0x54, 0x8e, 0xa2, 0x44, 0xf7, 0x08, 0x11, 0xe3, 0xa9, 0xd2, 0x20, 0x06, 0x26, 0x2a, + 0x39, 0xa5, 0xc2, 0x80, 0x6d, 0x0a, 0x23, 0xfd, 0x1c, 0x68, 0x83, 0x03, 0xcc, 0x72, 0x08, 0xd2, + 0x78, 0xc4, 0x9c, 0x02, 0x14, 0x44, 0xb3, 0x43, 0xa0, 0x77, 0x61, 0x21, 0x20, 0xa0, 0x79, 0x18, + 0x68, 0x34, 0x62, 0x4e, 0xa9, 0xfa, 0x34, 0x2c, 0x39, 0xc3, 0x65, 0x28, 0xf3, 0x17, 0x75, 0x5d, + 0xf3, 0x34, 0x1a, 0xb0, 0x9f, 0x53, 0x4a, 0xf4, 0x35, 0x7d, 0x5b, 0xf3, 0x34, 0xe2, 0x92, 0x62, + 0xc7, 0xb1, 0x1c, 0x1a, 0x4f, 0x98, 0x53, 0x58, 0x01, 0xbd, 0x0d, 0xbc, 0x37, 0xea, 0x77, 0x53, + 0xec, 0x1c, 0xd3, 0x70, 0xc1, 0x9c, 0x52, 0x66, 0xb0, 0x2f, 0x09, 0x88, 0x2d, 0x85, 0x8b, 0x3d, + 0x4e, 0xb1, 0xc8, 0x3a, 0x48, 0x41, 0x94, 0x40, 0xfe, 0x1a, 0xf2, 0x8a, 0xfd, 0xad, 0x11, 0x58, + 0xa2, 0xcc, 0xc9, 0x96, 0xe8, 0x84, 0x97, 0x87, 0x40, 0xc8, 0xf8, 0x3d, 0xc9, 0xc6, 0x16, 0x14, + 0xfd, 0x44, 0x66, 0x08, 0xa0, 0xb0, 0xd3, 0xea, 0x3c, 0xae, 0xb7, 0xa4, 0x73, 0x61, 0xb6, 0x01, + 0x1a, 0xd9, 0x57, 0xdf, 0xfe, 0x42, 0x6d, 0xb6, 0xa5, 0x2c, 0xfd, 0x58, 0x7f, 0xfb, 0x0b, 0xb5, + 0xb3, 0xd7, 0x67, 0x69, 0x08, 0x9e, 0x2b, 0x4f, 0xa4, 0xfc, 0x46, 0x2b, 0xf2, 0xa5, 0x26, 0xd3, + 0x50, 0x48, 0x82, 0x4a, 0xab, 0xd3, 0x79, 0xb6, 0xd7, 0x55, 0x1b, 0x2f, 0xea, 0x5b, 0x7d, 0xe9, + 0x1c, 0x5a, 0x84, 0x2a, 0x87, 0xb4, 0x3a, 0xed, 0x9d, 0x86, 0xc2, 0xbe, 0xd0, 0xe7, 0xa0, 0xde, + 0xd3, 0x8e, 0xd2, 0x6f, 0x28, 0x52, 0x76, 0xe3, 0x67, 0x19, 0x28, 0x0b, 0xf7, 0x23, 0x34, 0xb2, + 0x50, 0x69, 0x3c, 0x69, 0xbe, 0x90, 0xce, 0xa1, 0x0a, 0x14, 0xdb, 0x8d, 0xe6, 0xce, 0xd3, 0xc7, + 0x1d, 0x52, 0x7b, 0x1e, 0x72, 0xfd, 0xfa, 0x0e, 0xef, 0x56, 0x4f, 0xed, 0xd6, 0xfb, 0x4f, 0xa5, + 0x1c, 0xaa, 0x42, 0x69, 0xab, 0xb3, 0xbb, 0xbb, 0xd7, 0x6e, 0xf6, 0xbf, 0x96, 0xf2, 0xa4, 0xd5, + 0xc6, 0x8b, 0xbe, 0x1a, 0x82, 0xe6, 0xc8, 0x21, 0xb0, 0x55, 0x57, 0x76, 0x1a, 0x02, 0xb0, 0xc0, + 0x58, 0xbf, 0xe8, 0xab, 0x4f, 0x3b, 0x5d, 0x69, 0x7e, 0xe3, 0x3d, 0x28, 0x05, 0xd7, 0x22, 0x34, + 0xc2, 0xba, 0xfd, 0xb5, 0x18, 0x6a, 0x0d, 0x50, 0x68, 0xb6, 0x9f, 0x37, 0x94, 0xbe, 0x94, 0xdd, + 0xd8, 0x00, 0x29, 0x7e, 0xe9, 0x81, 0x0a, 0x90, 0x6d, 0x7c, 0x29, 0x9d, 0x23, 0xbf, 0x3b, 0x0d, + 0x29, 0x43, 0x7e, 0x5b, 0x0d, 0x29, 0xbb, 0x71, 0x8b, 0x07, 0x18, 0xf1, 0x93, 0x46, 0x18, 0xc4, + 0x4d, 0x43, 0x33, 0xb7, 0x1a, 0xdd, 0x3e, 0x63, 0xae, 0x34, 0xbe, 0xa0, 0x21, 0x9b, 0x1b, 0x7b, + 0xb0, 0x94, 0x72, 0x52, 0x24, 0x83, 0x0a, 0xfa, 0xae, 0xd6, 0xb7, 0xb7, 0xa5, 0x73, 0xe4, 0x48, + 0x1a, 0x82, 0x94, 0xc6, 0x6e, 0xe7, 0x39, 0x69, 0xf8, 0x3c, 0x2c, 0x8a, 0x50, 0x1e, 0x1d, 0xbe, + 0x71, 0x13, 0xaa, 0x91, 0xe3, 0x21, 0x99, 0xc1, 0xdd, 0xc6, 0xb6, 0xba, 0xdb, 0x21, 0xac, 0x6a, + 0x50, 0x26, 0x05, 0x9f, 0x3c, 0xb3, 0x71, 0x0f, 0x6a, 0x31, 0x57, 0x33, 0x1a, 0x6f, 0x4a, 0xa6, + 0x63, 0xb7, 0xdb, 0x51, 0x78, 0xef, 0x1b, 0x2f, 0xe8, 0xff, 0xec, 0xdd, 0xbf, 0x79, 0x0b, 0x8a, + 0x3b, 0x44, 0xf6, 0xea, 0xb6, 0x81, 0x1e, 0x41, 0xd1, 0x4f, 0xbd, 0x8c, 0x2e, 0x44, 0x1c, 0x64, + 0x31, 0x1d, 0xf3, 0xfa, 0x4a, 0xc2, 0x7d, 0x69, 0x4c, 0x6c, 0xef, 0x18, 0x3d, 0x84, 0x79, 0x9e, + 0x7c, 0x19, 0xad, 0x89, 0xb5, 0xc5, 0x7c, 0xcc, 0x27, 0x54, 0x2e, 0xb0, 0xb4, 0xcc, 0x48, 0x30, + 0x65, 0x91, 0xcc, 0xcd, 0xeb, 0x6b, 0x49, 0x04, 0xdf, 0xf8, 0x0f, 0x61, 0x9e, 0x27, 0x65, 0x45, + 0xb1, 0x7b, 0xca, 0x30, 0x51, 0xec, 0xcc, 0x96, 0xeb, 0x00, 0x61, 0x1a, 0x56, 0x74, 0x51, 0xb4, + 0xca, 0xb1, 0xa4, 0xcd, 0x33, 0x59, 0x6c, 0x41, 0xd1, 0xcf, 0x9d, 0x2c, 0x4e, 0x5c, 0x2c, 0x51, + 0xf3, 0xfa, 0x7a, 0x1a, 0x8a, 0x0d, 0xe1, 0x76, 0x06, 0xed, 0x00, 0x84, 0x99, 0x56, 0xc5, 0x7e, + 0x24, 0xd2, 0x2b, 0xaf, 0xbf, 0x95, 0x8e, 0xe4, 0xb3, 0xf1, 0x19, 0x94, 0x82, 0x34, 0xc3, 0x68, + 0x7d, 0x76, 0xee, 0xe1, 0x99, 0xc3, 0x69, 0x40, 0x45, 0x4c, 0x22, 0x8c, 0x84, 0x4b, 0x91, 0x94, + 0xe4, 0xc2, 0x27, 0x4d, 0x6c, 0x98, 0x34, 0x58, 0x1c, 0x50, 0x22, 0x95, 0xf0, 0x09, 0x13, 0x5b, + 0x16, 0x12, 0x09, 0x23, 0x61, 0xdc, 0xc9, 0xfc, 0xc2, 0x33, 0x99, 0xb4, 0xa0, 0x2c, 0x64, 0xfb, + 0x15, 0x99, 0x24, 0xd3, 0x09, 0xaf, 0x5f, 0x9a, 0x81, 0x0d, 0x96, 0xa9, 0x01, 0x15, 0x31, 0x01, + 0xb0, 0x38, 0x39, 0x29, 0x89, 0x81, 0x67, 0x76, 0xea, 0x19, 0xd4, 0x62, 0xc9, 0x7f, 0xd1, 0xd5, + 0x34, 0xd1, 0x3b, 0x13, 0xb3, 0xe7, 0x50, 0x8b, 0x25, 0xe9, 0x15, 0x99, 0xa5, 0xa7, 0x0d, 0x5e, + 0x7f, 0xfb, 0x04, 0x0a, 0x2e, 0x49, 0x5f, 0x02, 0x4a, 0xa6, 0xec, 0x45, 0x3f, 0x88, 0x8c, 0x38, + 0x3d, 0xa1, 0xef, 0xcc, 0xae, 0x7e, 0xca, 0xb6, 0xaa, 0xe6, 0x1d, 0xa0, 0xe4, 0x93, 0x82, 0x5f, + 0xf9, 0x42, 0x0a, 0x86, 0x77, 0x29, 0xdc, 0xad, 0x84, 0x45, 0x72, 0xb7, 0x0a, 0x5c, 0x4e, 0xdb, + 0xad, 0x84, 0x41, 0x7c, 0xb7, 0x0a, 0xd5, 0xd7, 0xd3, 0x50, 0x81, 0x18, 0x34, 0x83, 0x17, 0x17, + 0x96, 0x06, 0x16, 0x5d, 0x4e, 0xf4, 0x39, 0x92, 0x1f, 0x76, 0x56, 0x6f, 0x6e, 0x64, 0x50, 0x1d, + 0x8a, 0x7e, 0x4e, 0x55, 0xb1, 0x3f, 0xb1, 0x04, 0xaf, 0x62, 0x7f, 0x12, 0x29, 0x58, 0x3b, 0x50, + 0x11, 0x53, 0x8c, 0xa2, 0xa4, 0x14, 0x47, 0x58, 0x5d, 0x9e, 0x85, 0x0e, 0x86, 0xf7, 0x43, 0x28, + 0xb0, 0xbc, 0xa3, 0x28, 0xfa, 0x8e, 0x13, 0xa6, 0x0b, 0x9d, 0x39, 0xbd, 0x9f, 0x41, 0x29, 0x48, + 0x40, 0x2a, 0xaa, 0x9f, 0x78, 0x56, 0xd2, 0x99, 0x0c, 0x3e, 0x87, 0x79, 0x9e, 0x73, 0x54, 0x14, + 0x91, 0x68, 0xb2, 0xd2, 0xf5, 0x0b, 0x29, 0x98, 0xa0, 0xf7, 0x9f, 0x41, 0x29, 0x48, 0xf0, 0x27, + 0x76, 0x21, 0x9e, 0x96, 0xf4, 0x24, 0x0d, 0x28, 0xe6, 0xf1, 0x13, 0xe7, 0x33, 0x25, 0xbf, 0xe8, + 0x4c, 0x36, 0x4d, 0x80, 0x30, 0x67, 0x9e, 0x28, 0xac, 0x89, 0x04, 0xa1, 0xa2, 0x4a, 0x4f, 0x26, + 0x05, 0xbd, 0x9d, 0x21, 0x9a, 0x50, 0xc8, 0xf5, 0x29, 0x2a, 0xb1, 0x64, 0x0a, 0xd0, 0x99, 0xfd, + 0xd9, 0xa1, 0x42, 0x2b, 0x3c, 0x7f, 0x45, 0x85, 0x36, 0x91, 0xda, 0x72, 0x26, 0xa3, 0x5d, 0x3f, + 0xad, 0x95, 0xc0, 0xeb, 0xed, 0xf8, 0x1c, 0x9d, 0x9d, 0xdd, 0x1e, 0xcb, 0x45, 0x2b, 0x30, 0xbb, + 0x12, 0x9d, 0x8e, 0x24, 0xab, 0xab, 0xb3, 0x09, 0x62, 0xaa, 0x3a, 0xbc, 0x9d, 0x8c, 0xaa, 0xea, + 0x78, 0xda, 0xc9, 0xd3, 0x55, 0x75, 0xc8, 0x29, 0xa1, 0xaa, 0xcf, 0xcc, 0x4c, 0x81, 0x6a, 0x24, + 0x8f, 0xa5, 0xb8, 0x04, 0x69, 0xc9, 0x30, 0xd7, 0xaf, 0xcc, 0xc4, 0x07, 0xe3, 0xec, 0xb1, 0x9c, + 0xf4, 0xf1, 0xdb, 0xca, 0x77, 0x52, 0x04, 0x3f, 0x91, 0x5d, 0x71, 0x66, 0x47, 0xbf, 0x0e, 0xb2, + 0xd3, 0xc7, 0xf9, 0x5e, 0x4f, 0xdf, 0x0c, 0x67, 0x67, 0x3d, 0x62, 0xf9, 0xf9, 0x12, 0x8c, 0xdf, + 0x4d, 0xdb, 0x03, 0x49, 0xb6, 0xd7, 0x4e, 0x23, 0x13, 0x27, 0x26, 0x25, 0xaf, 0xa4, 0x38, 0x31, + 0xb3, 0xd3, 0x4e, 0x9e, 0xe4, 0xe6, 0xf2, 0x94, 0x92, 0x31, 0x0b, 0x26, 0x24, 0x31, 0x3c, 0xdd, + 0xd9, 0xa4, 0xf5, 0x13, 0xe6, 0xeb, 0x2c, 0x2c, 0xb8, 0xf9, 0xa2, 0x0c, 0x62, 0x5a, 0x50, 0xac, + 0xbe, 0x9e, 0x86, 0x0a, 0x66, 0x26, 0xf0, 0xcd, 0xe2, 0xfd, 0x48, 0x64, 0x85, 0x3c, 0x83, 0x6f, + 0x46, 0x79, 0x24, 0x7d, 0xb3, 0xb3, 0x30, 0xf1, 0x7d, 0x55, 0xca, 0x22, 0xee, 0xab, 0x9e, 0x85, + 0x01, 0xdf, 0x4f, 0x41, 0xa6, 0xc6, 0xf8, 0x7e, 0x8a, 0x27, 0x81, 0x8c, 0xef, 0xa7, 0x44, 0x8a, + 0x47, 0xa6, 0x6b, 0x85, 0x14, 0x8e, 0xe2, 0xc8, 0x92, 0x99, 0x1d, 0x4f, 0x1a, 0x59, 0x90, 0x3a, + 0x51, 0x1c, 0x59, 0x3c, 0x9f, 0xe2, 0x89, 0xa2, 0x12, 0xa4, 0x44, 0x8c, 0x88, 0x4a, 0x3c, 0x51, + 0xe2, 0x4c, 0x16, 0xcc, 0x8a, 0x3f, 0x9e, 0xd8, 0x31, 0x2b, 0x1e, 0x26, 0x23, 0x3c, 0xdd, 0x8a, + 0x93, 0xda, 0x09, 0x2b, 0x7e, 0x3a, 0x83, 0xfd, 0x02, 0x2d, 0x7f, 0xf8, 0x4f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xaf, 0x20, 0xcd, 0xe8, 0x1f, 0x68, 0x00, 0x00, +} diff --git a/vendor/github.com/osrg/gobgp/api/gobgp.proto b/vendor/github.com/osrg/gobgp/api/gobgp.proto new file mode 100644 index 0000000..a33e92d --- /dev/null +++ b/vendor/github.com/osrg/gobgp/api/gobgp.proto @@ -0,0 +1,1175 @@ +// Copyright (C) 2015-2017 Nippon Telegraph and Telephone Corporation. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "google/protobuf/empty.proto"; + +package gobgpapi; + +// Interface exported by the server. + +service GobgpApi { + rpc StartBgp(StartBgpRequest) returns (google.protobuf.Empty); + rpc StopBgp(StopBgpRequest) returns (google.protobuf.Empty); + rpc GetBgp(GetBgpRequest) returns (GetBgpResponse); + + rpc AddPeer(AddPeerRequest) returns (google.protobuf.Empty); + rpc DeletePeer(DeletePeerRequest) returns (google.protobuf.Empty); + rpc ListPeer(ListPeerRequest) returns (stream ListPeerResponse); + rpc UpdatePeer(UpdatePeerRequest) returns (UpdatePeerResponse); + rpc ResetPeer(ResetPeerRequest) returns (google.protobuf.Empty); + rpc ShutdownPeer(ShutdownPeerRequest) returns (google.protobuf.Empty); + rpc EnablePeer(EnablePeerRequest) returns (google.protobuf.Empty); + rpc DisablePeer(DisablePeerRequest) returns (google.protobuf.Empty); + rpc MonitorPeer(MonitorPeerRequest) returns (stream MonitorPeerResponse); + + rpc AddPeerGroup(AddPeerGroupRequest) returns (google.protobuf.Empty); + rpc DeletePeerGroup(DeletePeerGroupRequest) returns (google.protobuf.Empty); + rpc UpdatePeerGroup(UpdatePeerGroupRequest) returns (UpdatePeerGroupResponse); + + rpc AddDynamicNeighbor(AddDynamicNeighborRequest) returns (google.protobuf.Empty); + + rpc AddPath(AddPathRequest) returns (AddPathResponse); + rpc DeletePath(DeletePathRequest) returns (google.protobuf.Empty); + rpc ListPath(ListPathRequest) returns (stream ListPathResponse); + rpc AddPathStream(stream AddPathStreamRequest) returns (google.protobuf.Empty); + + rpc GetTable(GetTableRequest) returns (GetTableResponse); + rpc MonitorTable(MonitorTableRequest) returns (stream MonitorTableResponse); + + rpc AddVrf(AddVrfRequest) returns (google.protobuf.Empty); + rpc DeleteVrf(DeleteVrfRequest) returns (google.protobuf.Empty); + rpc ListVrf(ListVrfRequest) returns (stream ListVrfResponse); + + rpc AddPolicy(AddPolicyRequest) returns (google.protobuf.Empty); + rpc DeletePolicy(DeletePolicyRequest) returns (google.protobuf.Empty); + rpc ListPolicy(ListPolicyRequest) returns (stream ListPolicyResponse); + rpc SetPolicies(SetPoliciesRequest) returns (google.protobuf.Empty); + + rpc AddDefinedSet(AddDefinedSetRequest) returns (google.protobuf.Empty); + rpc DeleteDefinedSet(DeleteDefinedSetRequest) returns (google.protobuf.Empty); + rpc ListDefinedSet(ListDefinedSetRequest) returns (stream ListDefinedSetResponse); + + rpc AddStatement(AddStatementRequest) returns (google.protobuf.Empty); + rpc DeleteStatement(DeleteStatementRequest) returns (google.protobuf.Empty); + rpc ListStatement(ListStatementRequest) returns (stream ListStatementResponse); + + rpc AddPolicyAssignment(AddPolicyAssignmentRequest) returns (google.protobuf.Empty); + rpc DeletePolicyAssignment(DeletePolicyAssignmentRequest) returns (google.protobuf.Empty); + rpc ListPolicyAssignment(ListPolicyAssignmentRequest) returns (stream ListPolicyAssignmentResponse); + rpc SetPolicyAssignment(SetPolicyAssignmentRequest) returns (google.protobuf.Empty); + + rpc AddRpki(AddRpkiRequest) returns (google.protobuf.Empty); + rpc DeleteRpki(DeleteRpkiRequest) returns (google.protobuf.Empty); + rpc ListRpki(ListRpkiRequest) returns (stream ListRpkiResponse); + rpc EnableRpki(EnableRpkiRequest) returns (google.protobuf.Empty); + rpc DisableRpki(DisableRpkiRequest) returns (google.protobuf.Empty); + rpc ResetRpki(ResetRpkiRequest) returns (google.protobuf.Empty); + rpc ListRpkiTable(ListRpkiTableRequest) returns (stream ListRpkiTableResponse); + + rpc EnableZebra(EnableZebraRequest) returns (google.protobuf.Empty); + + rpc EnableMrt(EnableMrtRequest) returns (google.protobuf.Empty); + rpc DisableMrt(DisableMrtRequest) returns (google.protobuf.Empty); + + rpc AddBmp(AddBmpRequest) returns (google.protobuf.Empty); + rpc DeleteBmp(DeleteBmpRequest) returns (google.protobuf.Empty); +} + +message StartBgpRequest { + Global global = 1; +} + +message StopBgpRequest { +} + +message GetBgpRequest { +} + +message GetBgpResponse { + Global global = 1; +} + +message AddPeerRequest { + Peer peer = 1; +} + +message DeletePeerRequest { + string address = 1; + string interface = 2; +} + +message ListPeerRequest { + string address = 1; + bool enableAdvertised = 2; +} + +message ListPeerResponse { + Peer peer = 1; +} + +message UpdatePeerRequest { + Peer peer = 1; + // Calls SoftResetIn after updating the peer configuration if needed. + bool do_soft_reset_in = 2; +} + +message UpdatePeerResponse { + // Indicates whether calling SoftResetIn is required due to this update. If + // "true" is set, the client should call SoftResetIn manually. If + // "do_soft_reset_in = true" is set in the request, always returned with + // "false". + bool needs_soft_reset_in = 1; +} + +message ResetPeerRequest { + string address = 1; + string communication = 2; + bool soft = 3; + enum SoftResetDirection { + IN = 0; + OUT = 1; + BOTH = 2; + } + SoftResetDirection direction = 4; +} + +message ShutdownPeerRequest { + string address = 1; + string communication = 2; +} + +message EnablePeerRequest { + string address = 1; +} + +message DisablePeerRequest { + string address = 1; + string communication = 2; +} + +message MonitorPeerRequest { + string address = 1; + bool current = 2; +} + +message MonitorPeerResponse { + Peer peer = 1; +} + +message AddPeerGroupRequest { + PeerGroup peer_group = 1; +} + +message DeletePeerGroupRequest { + string name = 1; +} + +message UpdatePeerGroupRequest { + PeerGroup peer_group = 1; + bool do_soft_reset_in = 2; +} + +message UpdatePeerGroupResponse { + bool needs_soft_reset_in = 1; +} + +message AddDynamicNeighborRequest { + DynamicNeighbor dynamic_neighbor = 1; +} + +message AddPathRequest { + Resource resource = 1; + string vrf_id = 2; + Path path = 3; +} + +message AddPathResponse { + bytes uuid = 1; +} + +message DeletePathRequest { + Resource resource = 1; + string vrf_id = 2; + Family family = 3; + Path path = 4; + bytes uuid = 5; +} + +message ListPathRequest { + Resource type = 1; + string name = 2; + Family family = 3; + repeated TableLookupPrefix prefixes = 4; +} + +message ListPathResponse { + Destination destination = 1; +} + +message AddPathStreamRequest { + Resource resource = 1; + string vrf_id = 2; + repeated Path paths = 3; +} + +message GetTableRequest { + Resource type = 1; + Family family = 2; + string name = 3; +} + +message GetTableResponse { + uint64 num_destination = 1; + uint64 num_path = 2; + uint64 num_accepted = 3; // only meaningful when type == ADJ_IN +} + +message MonitorTableRequest { + Resource type = 1; + string name = 2; + Family family = 3; + bool current = 4; + bool post_policy = 5; +} + +message MonitorTableResponse { + Path path = 1; +} + +message AddVrfRequest { + Vrf vrf = 1; +} + +message DeleteVrfRequest { + string name = 1; +} + +message ListVrfRequest { + string name = 1; +} + +message ListVrfResponse { + Vrf vrf = 1; +} + +message AddPolicyRequest { + Policy policy = 1; + // if this flag is set, gobgpd won't define new statements + // but refer existing statements using statement's names in this arguments. + bool refer_existing_statements = 2; +} + +message DeletePolicyRequest { + Policy policy = 1; + // if this flag is set, gobgpd won't delete any statements + // even if some statements get not used by any policy by this operation. + bool preserve_statements = 2; + bool all = 3; +} + +message ListPolicyRequest { + string name = 1; +} + +message ListPolicyResponse { + Policy policy = 1; +} + +message SetPoliciesRequest { + repeated DefinedSet defined_sets = 1; + repeated Policy policies = 2; + repeated PolicyAssignment assignments = 3; +} + +message AddDefinedSetRequest { + DefinedSet defined_set = 1; +} + +message DeleteDefinedSetRequest { + DefinedSet defined_set = 1; + bool all = 2; +} + +message ListDefinedSetRequest { + DefinedType type = 1; + string name = 2; +} + +message ListDefinedSetResponse { + DefinedSet defined_set = 1; +} + +message AddStatementRequest { + Statement statement = 1; +} + +message DeleteStatementRequest { + Statement statement = 1; + bool all = 2; +} + +message ListStatementRequest { + string name = 1; +} + +message ListStatementResponse { + Statement statement = 1; +} + +message AddPolicyAssignmentRequest { + PolicyAssignment assignment = 1; +} + +message DeletePolicyAssignmentRequest { + PolicyAssignment assignment = 1; + bool all = 2; +} + +message ListPolicyAssignmentRequest { + string name = 1; + PolicyDirection direction = 2; +} + +message ListPolicyAssignmentResponse { + PolicyAssignment assignment = 1; +} + +message SetPolicyAssignmentRequest { + PolicyAssignment assignment = 1; +} + +message AddRpkiRequest { + string address = 1; + uint32 port = 2; + int64 lifetime = 3; +} + +message DeleteRpkiRequest { + string address = 1; + uint32 port = 2; +} + +message ListRpkiRequest { + Family family = 1; +} + +message ListRpkiResponse { + Rpki server = 1; +} + +message EnableRpkiRequest { + string address = 1; + uint32 port = 2; +} + +message DisableRpkiRequest { + string address = 1; + uint32 port = 2; +} + +message ResetRpkiRequest { + string address = 1; + uint32 port = 2; + bool soft = 3; +} + +message ListRpkiTableRequest { + Family family = 1; +} + +message ListRpkiTableResponse { + Roa roa = 1; +} + +message EnableZebraRequest { + string url = 1; + repeated string route_types = 2; + uint32 version = 3; + bool nexthop_trigger_enable = 4; + uint32 nexthop_trigger_delay = 5; +} + +message EnableMrtRequest { + int32 dump_type = 1; + string filename = 2; + uint64 interval = 3; +} + +message DisableMrtRequest { +} + +message AddBmpRequest { + string address = 1; + uint32 port = 2; + enum MonitoringPolicy { + PRE = 0; + POST = 1; + BOTH = 2; + LOCAL = 3; + ALL = 4; + } + MonitoringPolicy type = 3; +} + +message DeleteBmpRequest { + string address = 1; + uint32 port = 2; +} + +message Family { + enum Afi { + AFI_UNKNOWN = 0; + AFI_IP = 1; + AFI_IP6 = 2; + AFI_L2VPN = 25; + AFI_OPAQUE = 16397; + } + + enum Safi { + SAFI_UNKNOWN = 0; + SAFI_UNICAST = 1; + SAFI_MULTICAST = 2; + SAFI_MPLS_LABEL = 4; + SAFI_ENCAPSULATION = 7; + SAFI_VPLS = 65; + SAFI_EVPN = 70; + SAFI_MPLS_VPN = 128; + SAFI_MPLS_VPN_MULTICAST = 129; + SAFI_ROUTE_TARGET_CONSTRAINTS = 132; + SAFI_FLOW_SPEC_UNICAST = 133; + SAFI_FLOW_SPEC_VPN = 134; + SAFI_KEY_VALUE = 241; + } + + Afi afi = 1; + Safi safi = 2; +} + +enum Resource { + GLOBAL = 0; + LOCAL = 1; + ADJ_IN = 2; + ADJ_OUT = 3; + VRF = 4; +} + +message RPKIValidation{ + enum State { + STATE_NONE = 0; + STATE_NOT_FOUND = 1; + STATE_VALID = 2; + STATE_INVALID = 3; + } + + enum Reason { + REASOT_NONE = 0; + REASON_AS = 1; + REASON_LENGTH = 2; + } + + State state = 1; + Reason reason = 2; + repeated Roa matched = 3; + repeated Roa unmatched_as = 4; + repeated Roa unmatched_length = 5; +} + +message Path { + bytes nlri = 1; + repeated bytes pattrs = 2; + int64 age = 3; + bool best = 4; + bool is_withdraw = 5; + RPKIValidation validation_detail = 7; + bool no_implicit_withdraw = 8; + Family family = 9; + uint32 source_asn = 10; + string source_id = 11; + bool filtered = 12; + bool stale = 13; + bool is_from_external = 14; + string neighbor_ip = 15; + bytes uuid = 16; // only paths installed by AddPath API have this + bool is_nexthop_invalid = 17; + uint32 identifier = 18; + uint32 local_identifier = 19; + // One of the following defined in "api/attribute.proto": + // - IPAddressPrefix + // - LabeledIPAddressPrefix + // - EncapsulationNLRI + // - EVPNEthernetAutoDiscoveryRoute + // - EVPNMACIPAdvertisementRoute + // - EVPNInclusiveMulticastEthernetTagRoute + // - EVPNEthernetSegmentRoute + // - EVPNIPPrefixRoute + // - LabeledVPNIPAddressPrefix + // - RouteTargetMembershipNLRI + // - FlowSpecNLRI + // - VPNFlowSpecNLRI + // - OpaqueNLRI + google.protobuf.Any any_nlri = 20; + // Each attribute must be one of *Attribute defined in + // "api/attribute.proto". + repeated google.protobuf.Any any_pattrs = 21; +} + +message Destination { + string prefix = 1; + repeated Path paths = 2; +} + +// API representation of table.LookupOption +enum TableLookupOption { + LOOKUP_EXACT = 0; + LOOKUP_LONGER = 1; + LOOKUP_SHORTER = 2; +} + +// API representation of table.LookupPrefix +message TableLookupPrefix { + string prefix = 1; + TableLookupOption lookup_option = 2; +} + +message Peer { + ApplyPolicy apply_policy = 1; + PeerConf conf = 2; + EbgpMultihop ebgp_multihop = 3; + RouteReflector route_reflector = 4; + PeerState state = 5; + Timers timers = 6; + Transport transport = 7; + RouteServer route_server = 8; + GracefulRestart graceful_restart = 9; + repeated AfiSafi afi_safis = 10; + AddPaths add_paths = 11; +} + +message PeerGroup { + ApplyPolicy apply_policy = 1; + PeerGroupConf conf = 2; + EbgpMultihop ebgp_multihop = 3; + RouteReflector route_reflector = 4; + PeerGroupState info = 5; + Timers timers = 6; + Transport transport = 7; + RouteServer route_server = 8; + GracefulRestart graceful_restart = 9; + repeated AfiSafi afi_safis = 10; + AddPaths add_paths = 11; +} + +message DynamicNeighbor { + string prefix = 1; + string peer_group = 2; +} + +message ApplyPolicy { + PolicyAssignment in_policy = 1; + PolicyAssignment export_policy = 2; + PolicyAssignment import_policy = 3; +} + +message PrefixLimit { + Family family = 1; + uint32 max_prefixes = 2; + uint32 shutdown_threshold_pct = 3; +} + +message PeerConf { + string auth_password = 1; + string description = 2; + uint32 local_as = 3; + string neighbor_address = 4; + uint32 peer_as = 5; + string peer_group = 6; + uint32 peer_type = 7; + enum RemovePrivateAs { + NONE = 0; + ALL = 1; + REPLACE = 2; + } + RemovePrivateAs remove_private_as = 8; + bool route_flap_damping = 9; + uint32 send_community = 10; + // Each attribute must be one of *Capability defined in + // "api/capability.proto". + repeated google.protobuf.Any remote_cap = 11; + repeated google.protobuf.Any local_cap = 12; + string id = 13; + string neighbor_interface = 14; + string vrf = 15; + uint32 allow_own_as = 16; + bool replace_peer_as = 17; +} + +message PeerGroupConf { + string auth_password = 1; + string description = 2; + uint32 local_as = 3; + uint32 peer_as = 4; + string peer_group_name = 5; + uint32 peer_type = 6; + enum RemovePrivateAs { + NONE = 0; + ALL = 1; + REPLACE = 2; + } + RemovePrivateAs remove_private_as = 7; + bool route_flap_damping = 8; + uint32 send_community = 9; +} + +message PeerGroupState { + string auth_password = 1; + string description = 2; + uint32 local_as = 3; + uint32 peer_as = 4; + string peer_group_name = 5; + uint32 peer_type = 6; + enum RemovePrivateAs { + NONE = 0; + ALL = 1; + REPLACE = 2; + } + RemovePrivateAs remove_private_as = 7; + bool route_flap_damping = 8; + uint32 send_community = 9; + uint32 total_paths = 10; + uint32 total_prefixes = 11; +} + +message EbgpMultihop { + bool enabled = 1; + uint32 multihop_ttl = 2; +} + +message RouteReflector { + bool route_reflector_client = 1; + string route_reflector_cluster_id = 2; +} + +message PeerState { + string auth_password = 1; + string description = 2; + uint32 local_as = 3; + Messages messages = 4; + string neighbor_address = 5; + uint32 peer_as = 6; + string peer_group = 7; + uint32 peer_type = 8; + Queues queues = 9; + uint32 remove_private_as = 10; + bool route_flap_damping = 11; + uint32 send_community = 12; + enum SessionState { + UNKNOWN = 0; + IDLE = 1; + CONNECT = 2; + ACTIVE = 3; + OPENSENT = 4; + OPENCONFIRM = 5; + ESTABLISHED = 6; + } + SessionState session_state = 13; + repeated string supported_capabilities = 14; + enum AdminState { + UP = 0; + DOWN = 1; + PFX_CT = 2; // prefix counter over limit + } + AdminState admin_state = 15; + uint32 received = 16; + uint32 accepted = 17; + uint32 advertised = 18; + uint32 out_q = 19; + uint32 flops = 20; +} + +message Messages { + Message received = 1; + Message sent = 2; +} + +message Message { + uint64 notification = 1; + uint64 update = 2; + uint64 open = 3; + uint64 keepalive = 4; + uint64 refresh = 5; + uint64 discarded = 6; + uint64 total = 7; +} + +message Queues { + uint32 input = 1; + uint32 output = 2; +} + +message Timers { + TimersConfig config =1; + TimersState state = 2; +} + +message TimersConfig{ + uint64 connect_retry = 1; + uint64 hold_time = 2; + uint64 keepalive_interval = 3; + uint64 minimum_advertisement_interval = 4; +} + +message TimersState{ + uint64 connect_retry = 1; + uint64 hold_time = 2; + uint64 keepalive_interval = 3; + uint64 minimum_advertisement_interval = 4; + uint64 negotiated_hold_time = 5; + uint64 uptime = 6; + uint64 downtime = 7; +} + +message Transport { + string local_address = 1; + uint32 local_port = 2; + bool mtu_discovery = 3; + bool passive_mode = 4; + string remote_address = 5; + uint32 remote_port = 6; + uint32 tcp_mss = 7; +} + +message RouteServer { + bool route_server_client = 1; +} + +message GracefulRestart { + bool enabled = 1; + uint32 restart_time = 2; + bool helper_only = 3; + uint32 deferral_time = 4; + bool notification_enabled = 5; + bool longlived_enabled = 6; + uint32 stale_routes_time = 7; + uint32 peer_restart_time = 8; + bool peer_restarting = 9; + bool local_restarting = 10; + string mode = 11; +} + +message MpGracefulRestartConfig { + bool enabled = 1; +} + +message MpGracefulRestartState { + bool enabled = 1; + bool received = 2; + bool advertised = 3; + bool end_of_rib_received = 4; + bool end_of_rib_sent = 5; +} +message MpGracefulRestart { + MpGracefulRestartConfig config = 1; + MpGracefulRestartState state = 2; +} + +message AfiSafiConfig { + Family family = 1; + bool enabled = 2; +} + +message AfiSafiState { + Family family = 1; + bool enabled = 2; + uint32 total_paths = 3; + uint32 total_prefixes = 4; +} + +message RouteSelectionOptionsConfig { + bool always_compare_med = 1; + bool ignore_as_path_length = 2; + bool external_compare_router_id = 3; + bool advertise_inactive_routes = 4; + bool enable_aigp = 5; + bool ignore_next_hop_igp_metric = 6; + bool disable_best_path_selection = 7; +} + +message RouteSelectionOptionsState { + bool always_compare_med = 1; + bool ignore_as_path_length = 2; + bool external_compare_router_id = 3; + bool advertise_inactive_routes = 4; + bool enable_aigp = 5; + bool ignore_next_hop_igp_metric = 6; + bool disable_best_path_selection = 7; +} + +message RouteSelectionOptions { + RouteSelectionOptionsConfig config = 1; + RouteSelectionOptionsState state = 2; +} + +message UseMultiplePathsConfig { + bool enabled = 1; +} + +message UseMultiplePathsState { + bool enabled = 1; +} + +message EbgpConfig { + bool allow_multiple_as = 1; + uint32 maximum_paths = 2; +} + +message EbgpState { + bool allow_multiple_as = 1; + uint32 maximum_paths = 2; +} + +message Ebgp { + EbgpConfig config = 1; + EbgpState state = 2; +} + +message IbgpConfig { + uint32 maximum_paths = 1; +} + +message IbgpState { + uint32 maximum_paths = 1; +} + +message Ibgp { + IbgpConfig config = 1; + IbgpState state = 2; +} + +message UseMultiplePaths { + UseMultiplePathsConfig config = 1; + UseMultiplePathsState state = 2; + Ebgp ebgp = 3; + Ibgp ibgp = 4; +} + +message RouteTargetMembershipConfig { + uint32 deferral_time = 1; +} + +message RouteTargetMembershipState { + uint32 deferral_time = 1; +} + +message RouteTargetMembership { + RouteTargetMembershipConfig config = 1; + RouteTargetMembershipState state = 2; +} + +message LongLivedGracefulRestartConfig { + bool enabled = 1; + uint32 restart_time = 2; +} + +message LongLivedGracefulRestartState { + bool enabled = 1; + bool received = 2; + bool advertised = 3; + uint32 peer_restart_time = 4; + bool peer_restart_timer_expired = 5; +} + +message LongLivedGracefulRestart { + LongLivedGracefulRestartConfig config = 1; + LongLivedGracefulRestartState state = 2; +} + +message AfiSafi { + MpGracefulRestart mp_graceful_restart = 1; + AfiSafiConfig config = 2; + ApplyPolicy apply_policy = 3; + // TODO: + // Support the following structures: + // - Ipv4Unicast + // - Ipv6Unicast + // - Ipv4LabelledUnicast + // - Ipv6LabelledUnicast + // - L3vpnIpv4Unicast + // - L3vpnIpv6Unicast + // - L3vpnIpv4Multicast + // - L3vpnIpv6Multicast + // - L2vpnVpls + // - L2vpnEvpn + RouteSelectionOptions route_selection_options = 4; + UseMultiplePaths use_multiple_paths = 5; + PrefixLimit prefix_limits = 6; + RouteTargetMembership route_target_membership = 7; + LongLivedGracefulRestart long_lived_graceful_restart = 8; + AddPaths add_paths = 9; +} + +message AddPathsConfig { + bool receive = 1; + uint32 send_max = 2; +} + +message AddPathsState { + bool receive = 1; + uint32 send_max = 2; +} + +message AddPaths { + AddPathsConfig config = 1; + AddPathsState state = 2; +} + +message Prefix { + string ip_prefix = 1; + uint32 mask_length_min = 2; + uint32 mask_length_max = 3; +} + +enum DefinedType { + PREFIX = 0; + NEIGHBOR = 1; + TAG = 2; + AS_PATH = 3; + COMMUNITY = 4; + EXT_COMMUNITY = 5; + LARGE_COMMUNITY = 6; + NEXT_HOP = 7; +} + +message DefinedSet { + DefinedType type = 1; + string name = 2; + repeated string list = 3; + repeated Prefix prefixes = 4; +} + +enum MatchType { + ANY = 0; + ALL = 1; + INVERT = 2; +} + +message MatchSet { + MatchType type = 1; + string name = 2; +} + +enum AsPathLengthType { + EQ = 0; + GE = 1; + LE = 2; +} + +message AsPathLength { + AsPathLengthType type = 1; + uint32 length = 2; +} + +message Conditions { + MatchSet prefix_set = 1; + MatchSet neighbor_set = 2; + AsPathLength as_path_length = 3; + MatchSet as_path_set = 4; + MatchSet community_set = 5; + MatchSet ext_community_set = 6; + int32 rpki_result = 7; + enum RouteType { + ROUTE_TYPE_NONE = 0; + ROUTE_TYPE_INTERNAL = 1; + ROUTE_TYPE_EXTERNAL = 2; + ROUTE_TYPE_LOCAL = 3; + } + RouteType route_type = 8; + MatchSet large_community_set = 9; + repeated string next_hop_in_list = 10; + repeated Family afi_safi_in = 11; +} + +enum RouteAction { + NONE = 0; + ACCEPT = 1; + REJECT = 2; +} + +enum CommunityActionType { + COMMUNITY_ADD = 0; + COMMUNITY_REMOVE = 1; + COMMUNITY_REPLACE = 2; +} + +message CommunityAction { + CommunityActionType type = 1; + repeated string communities = 2; +} + +enum MedActionType { + MED_MOD = 0; + MED_REPLACE = 1; +} + +message MedAction { + MedActionType type = 1; + int64 value = 2; +} + +message AsPrependAction { + uint32 asn = 1; + uint32 repeat = 2; + bool use_left_most = 3; +} + +message NexthopAction { + string address = 1; + bool self = 2; +} + +message LocalPrefAction { + uint32 value = 1; +} + +message Actions { + RouteAction route_action = 1; + CommunityAction community = 2; + MedAction med = 3; + AsPrependAction as_prepend = 4; + CommunityAction ext_community = 5; + NexthopAction nexthop = 6; + LocalPrefAction local_pref = 7; + CommunityAction large_community = 8; +} + +message Statement { + string name = 1; + Conditions conditions = 2; + Actions actions = 3; +} + +message Policy { + string name = 1; + repeated Statement statements = 2; +} + +enum PolicyDirection { + UNKNOWN = 0; + IMPORT = 1; + EXPORT = 2; +} + +message PolicyAssignment { + string name = 1; + PolicyDirection direction = 2; + repeated Policy policies = 4; + RouteAction default_action = 5; +} + +message RoutingPolicy { + repeated DefinedSet defined_sets = 1; + repeated Policy policies = 2; +} + +message Roa { + uint32 as = 1; + uint32 prefixlen = 2; + uint32 maxlen = 3; + string prefix = 4; + RPKIConf conf = 5; +} + +message Vrf { + string name = 1; + // Route Distinguisher must be one of + // RouteDistinguisherTwoOctetAS, + // RouteDistinguisherIPAddressAS, + // or RouteDistinguisherFourOctetAS. + google.protobuf.Any rd = 2; + // List of the Import Route Targets. Each must be one of + // TwoOctetAsSpecificExtended, + // IPv4AddressSpecificExtended, + // or FourOctetAsSpecificExtended. + repeated google.protobuf.Any import_rt = 3; + // List of the Export Route Targets. Each must be one of + // TwoOctetAsSpecificExtended, + // IPv4AddressSpecificExtended, + // or FourOctetAsSpecificExtended. + repeated google.protobuf.Any export_rt = 4; + uint32 id = 5; +} + +message DefaultRouteDistance { + uint32 external_route_distance = 1; + uint32 internal_route_distance = 2; +} + +message Global { + uint32 as = 1; + string router_id = 2; + int32 listen_port = 3; + repeated string listen_addresses = 4; + repeated uint32 families = 5; + bool use_multiple_paths = 6; + RouteSelectionOptionsConfig route_selection_options = 7; + DefaultRouteDistance default_route_distance = 8; + Confederation confederation = 9; + GracefulRestart graceful_restart = 10; + ApplyPolicy apply_policy = 11; +} + +message Confederation { + bool enabled = 1; + uint32 identifier = 2; + repeated uint32 member_as_list = 3; +} + +message RPKIConf { + string address = 1; + uint32 remote_port = 2; +} + +message RPKIState { + int64 uptime = 1; + int64 downtime = 2; + bool up = 3; + uint32 record_ipv4 = 4; + uint32 record_ipv6 = 5; + uint32 prefix_ipv4 = 6; + uint32 prefix_ipv6 = 7; + uint32 serial = 8; + int64 received_ipv4 = 9; + int64 received_ipv6 = 10; + int64 serial_notify = 11; + int64 cache_reset = 12; + int64 cache_response = 13; + int64 end_of_data = 14; + int64 error = 15; + int64 serial_query = 16; + int64 reset_query = 17; +} + +message Rpki { + RPKIConf conf = 1; + RPKIState state = 2; +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/attribute.go b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/attribute.go new file mode 100644 index 0000000..4d06419 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/attribute.go @@ -0,0 +1,1303 @@ +// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation. +// +// 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 apiutil + +import ( + "errors" + "fmt" + "net" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" + log "github.com/sirupsen/logrus" +) + +func UnmarshalAttribute(an *any.Any) (bgp.PathAttributeInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch a := value.Message.(type) { + case *api.OriginAttribute: + return bgp.NewPathAttributeOrigin(uint8(a.Origin)), nil + case *api.AsPathAttribute: + params := make([]bgp.AsPathParamInterface, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAsPath(params), nil + case *api.NextHopAttribute: + nexthop := net.ParseIP(a.NextHop).To4() + if nexthop == nil { + return nil, fmt.Errorf("invalid nexthop address: %s", a.NextHop) + } + return bgp.NewPathAttributeNextHop(a.NextHop), nil + case *api.MultiExitDiscAttribute: + return bgp.NewPathAttributeMultiExitDisc(a.Med), nil + case *api.LocalPrefAttribute: + return bgp.NewPathAttributeLocalPref(a.LocalPref), nil + case *api.AtomicAggregateAttribute: + return bgp.NewPathAttributeAtomicAggregate(), nil + case *api.AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAggregator(a.As, a.Address), nil + case *api.CommunitiesAttribute: + return bgp.NewPathAttributeCommunities(a.Communities), nil + case *api.OriginatorIdAttribute: + if net.ParseIP(a.Id).To4() == nil { + return nil, fmt.Errorf("invalid originator id: %s", a.Id) + } + return bgp.NewPathAttributeOriginatorId(a.Id), nil + case *api.ClusterListAttribute: + for _, id := range a.Ids { + if net.ParseIP(id).To4() == nil { + return nil, fmt.Errorf("invalid cluster list: %s", a.Ids) + } + } + return bgp.NewPathAttributeClusterList(a.Ids), nil + } + return nil, errors.New("unexpected object") +} + +func NewOriginAttributeFromNative(a *bgp.PathAttributeOrigin) *api.OriginAttribute { + return &api.OriginAttribute{ + Origin: uint32(a.Value), + } +} + +func NewAsPathAttributeFromNative(a *bgp.PathAttributeAsPath) *api.AsPathAttribute { + segments := make([]*api.AsSegment, 0, len(a.Value)) + for _, param := range a.Value { + segments = append(segments, &api.AsSegment{ + Type: uint32(param.GetType()), + Numbers: param.GetAS(), + }) + } + return &api.AsPathAttribute{ + Segments: segments, + } +} + +func NewNextHopAttributeFromNative(a *bgp.PathAttributeNextHop) *api.NextHopAttribute { + return &api.NextHopAttribute{ + NextHop: a.Value.String(), + } +} + +func NewMultiExitDiscAttributeFromNative(a *bgp.PathAttributeMultiExitDisc) *api.MultiExitDiscAttribute { + return &api.MultiExitDiscAttribute{ + Med: a.Value, + } +} + +func NewLocalPrefAttributeFromNative(a *bgp.PathAttributeLocalPref) *api.LocalPrefAttribute { + return &api.LocalPrefAttribute{ + LocalPref: a.Value, + } +} + +func NewAtomicAggregateAttributeFromNative(a *bgp.PathAttributeAtomicAggregate) *api.AtomicAggregateAttribute { + return &api.AtomicAggregateAttribute{} +} + +func NewAggregatorAttributeFromNative(a *bgp.PathAttributeAggregator) *api.AggregatorAttribute { + return &api.AggregatorAttribute{ + As: a.Value.AS, + Address: a.Value.Address.String(), + } +} + +func NewCommunitiesAttributeFromNative(a *bgp.PathAttributeCommunities) *api.CommunitiesAttribute { + return &api.CommunitiesAttribute{ + Communities: a.Value, + } +} + +func NewOriginatorIdAttributeFromNative(a *bgp.PathAttributeOriginatorId) *api.OriginatorIdAttribute { + return &api.OriginatorIdAttribute{ + Id: a.Value.String(), + } +} + +func NewClusterListAttributeFromNative(a *bgp.PathAttributeClusterList) *api.ClusterListAttribute { + ids := make([]string, 0, len(a.Value)) + for _, id := range a.Value { + ids = append(ids, id.String()) + } + return &api.ClusterListAttribute{ + Ids: ids, + } +} + +func MarshalRD(rd bgp.RouteDistinguisherInterface) *any.Any { + var r proto.Message + switch v := rd.(type) { + case *bgp.RouteDistinguisherTwoOctetAS: + r = &api.RouteDistinguisherTwoOctetAS{ + Admin: uint32(v.Admin), + Assigned: v.Assigned, + } + case *bgp.RouteDistinguisherIPAddressAS: + r = &api.RouteDistinguisherIPAddress{ + Admin: v.Admin.String(), + Assigned: uint32(v.Assigned), + } + case *bgp.RouteDistinguisherFourOctetAS: + r = &api.RouteDistinguisherFourOctetAS{ + Admin: v.Admin, + Assigned: uint32(v.Assigned), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "RD": rd, + }).Warn("invalid rd type to marshal") + return nil + } + a, _ := ptypes.MarshalAny(r) + return a +} + +func UnmarshalRD(a *any.Any) (bgp.RouteDistinguisherInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch v := value.Message.(type) { + case *api.RouteDistinguisherTwoOctetAS: + return bgp.NewRouteDistinguisherTwoOctetAS(uint16(v.Admin), v.Assigned), nil + case *api.RouteDistinguisherIPAddress: + rd := bgp.NewRouteDistinguisherIPAddressAS(v.Admin, uint16(v.Assigned)) + if rd == nil { + return nil, fmt.Errorf("invalid address for route distinguisher: %s", v.Admin) + } + return rd, nil + case *api.RouteDistinguisherFourOctetAS: + return bgp.NewRouteDistinguisherFourOctetAS(v.Admin, uint16(v.Assigned)), nil + } + return nil, fmt.Errorf("invalid route distinguisher type: %s", a.TypeUrl) +} + +func NewEthernetSegmentIdentifierFromNative(a *bgp.EthernetSegmentIdentifier) *api.EthernetSegmentIdentifier { + return &api.EthernetSegmentIdentifier{ + Type: uint32(a.Type), + Value: a.Value, + } +} + +func unmarshalESI(a *api.EthernetSegmentIdentifier) (*bgp.EthernetSegmentIdentifier, error) { + return &bgp.EthernetSegmentIdentifier{ + Type: bgp.ESIType(a.Type), + Value: a.Value, + }, nil +} + +func MarshalFlowSpecRules(values []bgp.FlowSpecComponentInterface) []*any.Any { + rules := make([]*any.Any, 0, len(values)) + for _, value := range values { + var rule proto.Message + switch v := value.(type) { + case *bgp.FlowSpecDestinationPrefix: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPAddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPAddrPrefix).Prefix.String(), + } + case *bgp.FlowSpecSourcePrefix: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPAddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPAddrPrefix).Prefix.String(), + } + case *bgp.FlowSpecDestinationPrefix6: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPv6AddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPv6AddrPrefix).Prefix.String(), + Offset: uint32(v.Offset), + } + case *bgp.FlowSpecSourcePrefix6: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPv6AddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPv6AddrPrefix).Prefix.String(), + Offset: uint32(v.Offset), + } + case *bgp.FlowSpecSourceMac: + rule = &api.FlowSpecMAC{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_MAC), + Address: v.Mac.String(), + } + case *bgp.FlowSpecDestinationMac: + rule = &api.FlowSpecMAC{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_MAC), + Address: v.Mac.String(), + } + case *bgp.FlowSpecComponent: + items := make([]*api.FlowSpecComponentItem, 0, len(v.Items)) + for _, i := range v.Items { + items = append(items, &api.FlowSpecComponentItem{ + Op: uint32(i.Op), + Value: i.Value, + }) + } + rule = &api.FlowSpecComponent{ + Type: uint32(v.Type()), + Items: items, + } + } + a, _ := ptypes.MarshalAny(rule) + rules = append(rules, a) + } + return rules +} + +func UnmarshalFlowSpecRules(values []*any.Any) ([]bgp.FlowSpecComponentInterface, error) { + rules := make([]bgp.FlowSpecComponentInterface, 0, len(values)) + for _, an := range values { + var rule bgp.FlowSpecComponentInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal flow spec component: %s", err) + } + switch v := value.Message.(type) { + case *api.FlowSpecIPPrefix: + typ := bgp.BGPFlowSpecType(v.Type) + isIPv4 := net.ParseIP(v.Prefix).To4() != nil + switch { + case typ == bgp.FLOW_SPEC_TYPE_DST_PREFIX && isIPv4: + rule = bgp.NewFlowSpecDestinationPrefix(bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix)) + case typ == bgp.FLOW_SPEC_TYPE_SRC_PREFIX && isIPv4: + rule = bgp.NewFlowSpecSourcePrefix(bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix)) + case typ == bgp.FLOW_SPEC_TYPE_DST_PREFIX && !isIPv4: + rule = bgp.NewFlowSpecDestinationPrefix6(bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix), uint8(v.Offset)) + case typ == bgp.FLOW_SPEC_TYPE_SRC_PREFIX && !isIPv4: + rule = bgp.NewFlowSpecSourcePrefix6(bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix), uint8(v.Offset)) + } + case *api.FlowSpecMAC: + typ := bgp.BGPFlowSpecType(v.Type) + mac, err := net.ParseMAC(v.Address) + if err != nil { + return nil, fmt.Errorf("invalid mac address for %s flow spec component: %s", typ.String(), v.Address) + } + switch typ { + case bgp.FLOW_SPEC_TYPE_SRC_MAC: + rule = bgp.NewFlowSpecSourceMac(mac) + case bgp.FLOW_SPEC_TYPE_DST_MAC: + rule = bgp.NewFlowSpecDestinationMac(mac) + } + case *api.FlowSpecComponent: + items := make([]*bgp.FlowSpecComponentItem, 0, len(v.Items)) + for _, item := range v.Items { + items = append(items, bgp.NewFlowSpecComponentItem(uint8(item.Op), item.Value)) + } + rule = bgp.NewFlowSpecComponent(bgp.BGPFlowSpecType(v.Type), items) + } + if rule == nil { + return nil, fmt.Errorf("invalid flow spec component: %v", value.Message) + } + rules = append(rules, rule) + } + return rules, nil +} + +func MarshalNLRI(value bgp.AddrPrefixInterface) *any.Any { + var nlri proto.Message + + switch v := value.(type) { + case *bgp.IPAddrPrefix: + nlri = &api.IPAddressPrefix{ + PrefixLen: uint32(v.Length), + Prefix: v.Prefix.String(), + } + case *bgp.IPv6AddrPrefix: + nlri = &api.IPAddressPrefix{ + PrefixLen: uint32(v.Length), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledIPAddrPrefix: + nlri = &api.LabeledIPAddressPrefix{ + Labels: v.Labels.Labels, + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledIPv6AddrPrefix: + nlri = &api.LabeledIPAddressPrefix{ + Labels: v.Labels.Labels, + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.EncapNLRI: + nlri = &api.EncapsulationNLRI{ + Address: v.String(), + } + case *bgp.Encapv6NLRI: + nlri = &api.EncapsulationNLRI{ + Address: v.String(), + } + case *bgp.EVPNNLRI: + switch r := v.RouteTypeData.(type) { + case *bgp.EVPNEthernetAutoDiscoveryRoute: + nlri = &api.EVPNEthernetAutoDiscoveryRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + Label: r.Label, + } + case *bgp.EVPNMacIPAdvertisementRoute: + nlri = &api.EVPNMACIPAdvertisementRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + MacAddress: r.MacAddress.String(), + IpAddress: r.IPAddress.String(), + Labels: r.Labels, + } + case *bgp.EVPNMulticastEthernetTagRoute: + nlri = &api.EVPNInclusiveMulticastEthernetTagRoute{ + Rd: MarshalRD(r.RD), + EthernetTag: r.ETag, + IpAddress: r.IPAddress.String(), + } + case *bgp.EVPNEthernetSegmentRoute: + nlri = &api.EVPNEthernetSegmentRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + IpAddress: r.IPAddress.String(), + } + case *bgp.EVPNIPPrefixRoute: + nlri = &api.EVPNIPPrefixRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + IpPrefix: r.IPPrefix.String(), + IpPrefixLen: uint32(r.IPPrefixLength), + Label: r.Label, + GwAddress: r.GWIPAddress.String(), + } + } + case *bgp.LabeledVPNIPAddrPrefix: + nlri = &api.LabeledVPNIPAddressPrefix{ + Labels: v.Labels.Labels, + Rd: MarshalRD(v.RD), + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledVPNIPv6AddrPrefix: + nlri = &api.LabeledVPNIPAddressPrefix{ + Labels: v.Labels.Labels, + Rd: MarshalRD(v.RD), + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.RouteTargetMembershipNLRI: + nlri = &api.RouteTargetMembershipNLRI{ + As: v.AS, + Rt: MarshalRT(v.RouteTarget), + } + case *bgp.FlowSpecIPv4Unicast: + nlri = &api.FlowSpecNLRI{ + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv6Unicast: + nlri = &api.FlowSpecNLRI{ + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv4VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv6VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecL2VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + } + + an, _ := ptypes.MarshalAny(nlri) + return an +} + +func MarshalNLRIs(values []bgp.AddrPrefixInterface) []*any.Any { + nlris := make([]*any.Any, 0, len(values)) + for _, value := range values { + nlris = append(nlris, MarshalNLRI(value)) + } + return nlris +} + +func UnmarshalNLRI(rf bgp.RouteFamily, an *any.Any) (bgp.AddrPrefixInterface, error) { + var nlri bgp.AddrPrefixInterface + + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal nlri: %s", err) + } + + switch v := value.Message.(type) { + case *api.IPAddressPrefix: + switch rf { + case bgp.RF_IPv4_UC: + nlri = bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix) + case bgp.RF_IPv6_UC: + nlri = bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix) + } + case *api.LabeledIPAddressPrefix: + switch rf { + case bgp.RF_IPv4_MPLS: + nlri = bgp.NewLabeledIPAddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...)) + case bgp.RF_IPv6_MPLS: + nlri = bgp.NewLabeledIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...)) + } + case *api.EncapsulationNLRI: + switch rf { + case bgp.RF_IPv4_ENCAP: + nlri = bgp.NewEncapNLRI(v.Address) + case bgp.RF_IPv6_ENCAP: + nlri = bgp.NewEncapv6NLRI(v.Address) + } + case *api.EVPNEthernetAutoDiscoveryRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNEthernetAutoDiscoveryRoute(rd, *esi, v.EthernetTag, v.Label) + } + case *api.EVPNMACIPAdvertisementRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNMacIPAdvertisementRoute(rd, *esi, v.EthernetTag, v.MacAddress, v.IpAddress, v.Labels) + } + case *api.EVPNInclusiveMulticastEthernetTagRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNMulticastEthernetTagRoute(rd, v.EthernetTag, v.IpAddress) + } + case *api.EVPNEthernetSegmentRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNEthernetSegmentRoute(rd, *esi, v.IpAddress) + } + case *api.EVPNIPPrefixRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNIPPrefixRoute(rd, *esi, v.EthernetTag, uint8(v.IpPrefixLen), v.IpPrefix, v.GwAddress, v.Label) + } + case *api.LabeledVPNIPAddressPrefix: + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_IPv4_VPN: + nlri = bgp.NewLabeledVPNIPAddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...), rd) + case bgp.RF_IPv6_VPN: + nlri = bgp.NewLabeledVPNIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...), rd) + } + case *api.RouteTargetMembershipNLRI: + rt, err := UnmarshalRT(v.Rt) + if err != nil { + return nil, err + } + nlri = bgp.NewRouteTargetMembershipNLRI(v.As, rt) + case *api.FlowSpecNLRI: + rules, err := UnmarshalFlowSpecRules(v.Rules) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_FS_IPv4_UC: + nlri = bgp.NewFlowSpecIPv4Unicast(rules) + case bgp.RF_FS_IPv6_UC: + nlri = bgp.NewFlowSpecIPv6Unicast(rules) + } + case *api.VPNFlowSpecNLRI: + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + rules, err := UnmarshalFlowSpecRules(v.Rules) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_FS_IPv4_VPN: + nlri = bgp.NewFlowSpecIPv4VPN(rd, rules) + case bgp.RF_FS_IPv6_VPN: + nlri = bgp.NewFlowSpecIPv6VPN(rd, rules) + case bgp.RF_FS_L2_VPN: + nlri = bgp.NewFlowSpecL2VPN(rd, rules) + } + } + + if nlri == nil { + return nil, fmt.Errorf("invalid nlri for %s family: %s", rf.String(), value.Message) + } + + return nlri, nil +} + +func UnmarshalNLRIs(rf bgp.RouteFamily, values []*any.Any) ([]bgp.AddrPrefixInterface, error) { + nlris := make([]bgp.AddrPrefixInterface, 0, len(values)) + for _, an := range values { + nlri, err := UnmarshalNLRI(rf, an) + if err != nil { + return nil, err + } + nlris = append(nlris, nlri) + } + return nlris, nil +} + +func NewMpReachNLRIAttributeFromNative(a *bgp.PathAttributeMpReachNLRI) *api.MpReachNLRIAttribute { + var nexthops []string + if a.SAFI == bgp.SAFI_FLOW_SPEC_UNICAST || a.SAFI == bgp.SAFI_FLOW_SPEC_VPN { + nexthops = nil + } else { + nexthops = []string{a.Nexthop.String()} + if a.LinkLocalNexthop != nil { + nexthops = append(nexthops, a.LinkLocalNexthop.String()) + } + } + return &api.MpReachNLRIAttribute{ + Family: ToApiFamily(a.AFI, a.SAFI), + NextHops: nexthops, + Nlris: MarshalNLRIs(a.Value), + } +} + +func NewMpUnreachNLRIAttributeFromNative(a *bgp.PathAttributeMpUnreachNLRI) *api.MpUnreachNLRIAttribute { + return &api.MpUnreachNLRIAttribute{ + Family: ToApiFamily(a.AFI, a.SAFI), + Nlris: MarshalNLRIs(a.Value), + } +} + +func MarshalRT(rt bgp.ExtendedCommunityInterface) *any.Any { + var r proto.Message + switch v := rt.(type) { + case *bgp.TwoOctetAsSpecificExtended: + r = &api.TwoOctetAsSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.IPv4AddressSpecificExtended: + r = &api.IPv4AddressSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.FourOctetAsSpecificExtended: + r = &api.FourOctetAsSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "RT": rt, + }).Warn("invalid rt type to marshal") + return nil + } + a, _ := ptypes.MarshalAny(r) + return a +} + +func MarshalRTs(values []bgp.ExtendedCommunityInterface) []*any.Any { + rts := make([]*any.Any, 0, len(values)) + for _, rt := range values { + rts = append(rts, MarshalRT(rt)) + } + return rts +} + +func UnmarshalRT(a *any.Any) (bgp.ExtendedCommunityInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route target: %s", err) + } + switch v := value.Message.(type) { + case *api.TwoOctetAsSpecificExtended: + return bgp.NewTwoOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), uint16(v.As), v.LocalAdmin, v.IsTransitive), nil + case *api.IPv4AddressSpecificExtended: + rt := bgp.NewIPv4AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + if rt == nil { + return nil, fmt.Errorf("invalid address for ipv4 address specific route target: %s", v.Address) + } + return rt, nil + case *api.FourOctetAsSpecificExtended: + return bgp.NewFourOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.As, uint16(v.LocalAdmin), v.IsTransitive), nil + } + return nil, fmt.Errorf("invalid route target type: %s", a.TypeUrl) +} + +func UnmarshalRTs(values []*any.Any) ([]bgp.ExtendedCommunityInterface, error) { + rts := make([]bgp.ExtendedCommunityInterface, 0, len(values)) + for _, an := range values { + rt, err := UnmarshalRT(an) + if err != nil { + return nil, err + } + rts = append(rts, rt) + } + return rts, nil +} + +func NewExtendedCommunitiesAttributeFromNative(a *bgp.PathAttributeExtendedCommunities) *api.ExtendedCommunitiesAttribute { + communities := make([]*any.Any, 0, len(a.Value)) + for _, value := range a.Value { + var community proto.Message + switch v := value.(type) { + case *bgp.TwoOctetAsSpecificExtended: + community = &api.TwoOctetAsSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.IPv4AddressSpecificExtended: + community = &api.IPv4AddressSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.FourOctetAsSpecificExtended: + community = &api.FourOctetAsSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.ValidationExtended: + community = &api.ValidationExtended{ + State: uint32(v.State), + } + case *bgp.ColorExtended: + community = &api.ColorExtended{ + Color: v.Color, + } + case *bgp.EncapExtended: + community = &api.EncapExtended{ + TunnelType: uint32(v.TunnelType), + } + case *bgp.DefaultGatewayExtended: + community = &api.DefaultGatewayExtended{} + case *bgp.OpaqueExtended: + community = &api.OpaqueExtended{ + IsTransitive: v.IsTransitive, + Value: v.Value, + } + case *bgp.ESILabelExtended: + community = &api.ESILabelExtended{ + IsSingleActive: v.IsSingleActive, + Label: v.Label, + } + case *bgp.ESImportRouteTarget: + community = &api.ESImportRouteTarget{ + EsImport: v.ESImport.String(), + } + case *bgp.MacMobilityExtended: + community = &api.MacMobilityExtended{ + IsSticky: v.IsSticky, + SequenceNum: v.Sequence, + } + case *bgp.RouterMacExtended: + community = &api.RouterMacExtended{ + Mac: v.Mac.String(), + } + case *bgp.TrafficRateExtended: + community = &api.TrafficRateExtended{ + As: uint32(v.AS), + Rate: v.Rate, + } + case *bgp.TrafficActionExtended: + community = &api.TrafficActionExtended{ + Terminal: v.Terminal, + Sample: v.Sample, + } + case *bgp.RedirectTwoOctetAsSpecificExtended: + community = &api.RedirectTwoOctetAsSpecificExtended{ + As: uint32(v.AS), + LocalAdmin: v.LocalAdmin, + } + case *bgp.RedirectIPv4AddressSpecificExtended: + community = &api.RedirectIPv4AddressSpecificExtended{ + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.RedirectFourOctetAsSpecificExtended: + community = &api.RedirectFourOctetAsSpecificExtended{ + As: v.AS, + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.TrafficRemarkExtended: + community = &api.TrafficRemarkExtended{ + Dscp: uint32(v.DSCP), + } + case *bgp.UnknownExtended: + community = &api.UnknownExtended{ + Type: uint32(v.Type), + Value: v.Value, + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "Community": value, + }).Warn("unsupported extended community") + return nil + } + an, _ := ptypes.MarshalAny(community) + communities = append(communities, an) + } + return &api.ExtendedCommunitiesAttribute{ + Communities: communities, + } +} + +func unmarshalExComm(a *api.ExtendedCommunitiesAttribute) (*bgp.PathAttributeExtendedCommunities, error) { + communities := make([]bgp.ExtendedCommunityInterface, 0, len(a.Communities)) + for _, an := range a.Communities { + var community bgp.ExtendedCommunityInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal extended community: %s", err) + } + switch v := value.Message.(type) { + case *api.TwoOctetAsSpecificExtended: + community = bgp.NewTwoOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), uint16(v.As), v.LocalAdmin, v.IsTransitive) + case *api.IPv4AddressSpecificExtended: + community = bgp.NewIPv4AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + case *api.FourOctetAsSpecificExtended: + community = bgp.NewFourOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.As, uint16(v.LocalAdmin), v.IsTransitive) + case *api.ValidationExtended: + community = bgp.NewValidationExtended(bgp.ValidationState(v.State)) + case *api.ColorExtended: + community = bgp.NewColorExtended(v.Color) + case *api.EncapExtended: + community = bgp.NewEncapExtended(bgp.TunnelType(v.TunnelType)) + case *api.DefaultGatewayExtended: + community = bgp.NewDefaultGatewayExtended() + case *api.OpaqueExtended: + community = bgp.NewOpaqueExtended(v.IsTransitive, v.Value) + case *api.ESILabelExtended: + community = bgp.NewESILabelExtended(v.Label, v.IsSingleActive) + case *api.ESImportRouteTarget: + community = bgp.NewESImportRouteTarget(v.EsImport) + case *api.MacMobilityExtended: + community = bgp.NewMacMobilityExtended(v.SequenceNum, v.IsSticky) + case *api.RouterMacExtended: + community = bgp.NewRoutersMacExtended(v.Mac) + case *api.TrafficRateExtended: + community = bgp.NewTrafficRateExtended(uint16(v.As), v.Rate) + case *api.TrafficActionExtended: + community = bgp.NewTrafficActionExtended(v.Terminal, v.Sample) + case *api.RedirectTwoOctetAsSpecificExtended: + community = bgp.NewRedirectTwoOctetAsSpecificExtended(uint16(v.As), v.LocalAdmin) + case *api.RedirectIPv4AddressSpecificExtended: + community = bgp.NewRedirectIPv4AddressSpecificExtended(v.Address, uint16(v.LocalAdmin)) + case *api.RedirectFourOctetAsSpecificExtended: + community = bgp.NewRedirectFourOctetAsSpecificExtended(v.As, uint16(v.LocalAdmin)) + case *api.TrafficRemarkExtended: + community = bgp.NewTrafficRemarkExtended(uint8(v.Dscp)) + case *api.UnknownExtended: + community = bgp.NewUnknownExtended(bgp.ExtendedCommunityAttrType(v.Type), v.Value) + } + if community == nil { + return nil, fmt.Errorf("invalid extended community: %v", value.Message) + } + communities = append(communities, community) + } + return bgp.NewPathAttributeExtendedCommunities(communities), nil +} + +func NewAs4PathAttributeFromNative(a *bgp.PathAttributeAs4Path) *api.As4PathAttribute { + segments := make([]*api.AsSegment, 0, len(a.Value)) + for _, param := range a.Value { + segments = append(segments, &api.AsSegment{ + Type: uint32(param.Type), + Numbers: param.AS, + }) + } + return &api.As4PathAttribute{ + Segments: segments, + } +} + +func NewAs4AggregatorAttributeFromNative(a *bgp.PathAttributeAs4Aggregator) *api.As4AggregatorAttribute { + return &api.As4AggregatorAttribute{ + As: a.Value.AS, + Address: a.Value.Address.String(), + } +} + +func NewPmsiTunnelAttributeFromNative(a *bgp.PathAttributePmsiTunnel) *api.PmsiTunnelAttribute { + var flags uint32 + if a.IsLeafInfoRequired { + flags |= 0x01 + } + id, _ := a.TunnelID.Serialize() + return &api.PmsiTunnelAttribute{ + Flags: flags, + Type: uint32(a.TunnelType), + Label: a.Label, + Id: id, + } +} + +func NewTunnelEncapAttributeFromNative(a *bgp.PathAttributeTunnelEncap) *api.TunnelEncapAttribute { + tlvs := make([]*api.TunnelEncapTLV, 0, len(a.Value)) + for _, v := range a.Value { + subTlvs := make([]*any.Any, 0, len(v.Value)) + for _, s := range v.Value { + var subTlv proto.Message + switch sv := s.(type) { + case *bgp.TunnelEncapSubTLVEncapsulation: + subTlv = &api.TunnelEncapSubTLVEncapsulation{ + Key: sv.Key, + Cookie: sv.Cookie, + } + case *bgp.TunnelEncapSubTLVProtocol: + subTlv = &api.TunnelEncapSubTLVProtocol{ + Protocol: uint32(sv.Protocol), + } + case *bgp.TunnelEncapSubTLVColor: + subTlv = &api.TunnelEncapSubTLVColor{ + Color: sv.Color, + } + case *bgp.TunnelEncapSubTLVUnknown: + subTlv = &api.TunnelEncapSubTLVUnknown{ + Type: uint32(sv.Type), + Value: sv.Value, + } + } + an, _ := ptypes.MarshalAny(subTlv) + subTlvs = append(subTlvs, an) + } + tlvs = append(tlvs, &api.TunnelEncapTLV{ + Type: uint32(v.Type), + Tlvs: subTlvs, + }) + } + return &api.TunnelEncapAttribute{ + Tlvs: tlvs, + } +} + +func NewIP6ExtendedCommunitiesAttributeFromNative(a *bgp.PathAttributeIP6ExtendedCommunities) *api.IP6ExtendedCommunitiesAttribute { + communities := make([]*any.Any, 0, len(a.Value)) + for _, value := range a.Value { + var community proto.Message + switch v := value.(type) { + case *bgp.IPv6AddressSpecificExtended: + community = &api.IPv6AddressSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + Address: v.IPv6.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.RedirectIPv6AddressSpecificExtended: + community = &api.RedirectIPv6AddressSpecificExtended{ + Address: v.IPv6.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "Attribute": value, + }).Warn("invalid ipv6 extended community") + return nil + } + an, _ := ptypes.MarshalAny(community) + communities = append(communities, an) + } + return &api.IP6ExtendedCommunitiesAttribute{ + Communities: communities, + } +} + +func NewAigpAttributeFromNative(a *bgp.PathAttributeAigp) *api.AigpAttribute { + tlvs := make([]*any.Any, 0, len(a.Values)) + for _, value := range a.Values { + var tlv proto.Message + switch v := value.(type) { + case *bgp.AigpTLVIgpMetric: + tlv = &api.AigpTLVIGPMetric{ + Metric: v.Metric, + } + case *bgp.AigpTLVDefault: + tlv = &api.AigpTLVUnknown{ + Type: uint32(v.Type()), + Value: v.Value, + } + } + an, _ := ptypes.MarshalAny(tlv) + tlvs = append(tlvs, an) + } + return &api.AigpAttribute{ + Tlvs: tlvs, + } +} + +func NewLargeCommunitiesAttributeFromNative(a *bgp.PathAttributeLargeCommunities) *api.LargeCommunitiesAttribute { + communities := make([]*api.LargeCommunity, 0, len(a.Values)) + for _, v := range a.Values { + communities = append(communities, &api.LargeCommunity{ + GlobalAdmin: v.ASN, + LocalData1: v.LocalData1, + LocalData2: v.LocalData2, + }) + } + return &api.LargeCommunitiesAttribute{ + Communities: communities, + } +} + +func NewUnknownAttributeFromNative(a *bgp.PathAttributeUnknown) *api.UnknownAttribute { + return &api.UnknownAttribute{ + Flags: uint32(a.Flags), + Type: uint32(a.Type), + Value: a.Value, + } +} + +func MarshalPathAttributes(attrList []bgp.PathAttributeInterface) []*any.Any { + anyList := make([]*any.Any, 0, len(attrList)) + for _, attr := range attrList { + switch a := attr.(type) { + case *bgp.PathAttributeOrigin: + n, _ := ptypes.MarshalAny(NewOriginAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAsPath: + n, _ := ptypes.MarshalAny(NewAsPathAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeNextHop: + n, _ := ptypes.MarshalAny(NewNextHopAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMultiExitDisc: + n, _ := ptypes.MarshalAny(NewMultiExitDiscAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeLocalPref: + n, _ := ptypes.MarshalAny(NewLocalPrefAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAtomicAggregate: + n, _ := ptypes.MarshalAny(NewAtomicAggregateAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAggregator: + n, _ := ptypes.MarshalAny(NewAggregatorAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeCommunities: + n, _ := ptypes.MarshalAny(NewCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeOriginatorId: + n, _ := ptypes.MarshalAny(NewOriginatorIdAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeClusterList: + n, _ := ptypes.MarshalAny(NewClusterListAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMpReachNLRI: + n, _ := ptypes.MarshalAny(NewMpReachNLRIAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMpUnreachNLRI: + n, _ := ptypes.MarshalAny(NewMpUnreachNLRIAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeExtendedCommunities: + n, _ := ptypes.MarshalAny(NewExtendedCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAs4Path: + n, _ := ptypes.MarshalAny(NewAs4PathAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAs4Aggregator: + n, _ := ptypes.MarshalAny(NewAs4AggregatorAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributePmsiTunnel: + n, _ := ptypes.MarshalAny(NewPmsiTunnelAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeTunnelEncap: + n, _ := ptypes.MarshalAny(NewTunnelEncapAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeIP6ExtendedCommunities: + n, _ := ptypes.MarshalAny(NewIP6ExtendedCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAigp: + n, _ := ptypes.MarshalAny(NewAigpAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeLargeCommunities: + n, _ := ptypes.MarshalAny(NewLargeCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeUnknown: + n, _ := ptypes.MarshalAny(NewUnknownAttributeFromNative(a)) + anyList = append(anyList, n) + } + } + return anyList +} + +func UnmarshalPathAttributes(values []*any.Any) ([]bgp.PathAttributeInterface, error) { + attrList := make([]bgp.PathAttributeInterface, 0, len(values)) + typeMap := make(map[bgp.BGPAttrType]struct{}) + for _, an := range values { + attr, err := unmarshalAttribute(an) + if err != nil { + return nil, err + } + if _, ok := typeMap[attr.GetType()]; ok { + return nil, fmt.Errorf("duplicated path attribute type: %d", attr.GetType()) + } + typeMap[attr.GetType()] = struct{}{} + attrList = append(attrList, attr) + } + return attrList, nil +} + +func unmarshalAttribute(an *any.Any) (bgp.PathAttributeInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch a := value.Message.(type) { + case *api.OriginAttribute: + return bgp.NewPathAttributeOrigin(uint8(a.Origin)), nil + case *api.AsPathAttribute: + params := make([]bgp.AsPathParamInterface, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAsPath(params), nil + case *api.NextHopAttribute: + nexthop := net.ParseIP(a.NextHop).To4() + if nexthop == nil { + return nil, fmt.Errorf("invalid nexthop address: %s", a.NextHop) + } + return bgp.NewPathAttributeNextHop(a.NextHop), nil + case *api.MultiExitDiscAttribute: + return bgp.NewPathAttributeMultiExitDisc(a.Med), nil + case *api.LocalPrefAttribute: + return bgp.NewPathAttributeLocalPref(a.LocalPref), nil + case *api.AtomicAggregateAttribute: + return bgp.NewPathAttributeAtomicAggregate(), nil + case *api.AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAggregator(a.As, a.Address), nil + case *api.CommunitiesAttribute: + return bgp.NewPathAttributeCommunities(a.Communities), nil + case *api.OriginatorIdAttribute: + if net.ParseIP(a.Id).To4() == nil { + return nil, fmt.Errorf("invalid originator id: %s", a.Id) + } + return bgp.NewPathAttributeOriginatorId(a.Id), nil + case *api.ClusterListAttribute: + for _, id := range a.Ids { + if net.ParseIP(id).To4() == nil { + return nil, fmt.Errorf("invalid cluster list: %s", a.Ids) + } + } + return bgp.NewPathAttributeClusterList(a.Ids), nil + case *api.MpReachNLRIAttribute: + rf := ToRouteFamily(a.Family) + nlris, err := UnmarshalNLRIs(rf, a.Nlris) + if err != nil { + return nil, err + } + afi, safi := bgp.RouteFamilyToAfiSafi(rf) + nexthop := "0.0.0.0" + var linkLocalNexthop net.IP + if afi == bgp.AFI_IP6 { + nexthop = "::" + if len(a.NextHops) > 1 { + linkLocalNexthop = net.ParseIP(a.NextHops[1]).To16() + if linkLocalNexthop == nil { + return nil, fmt.Errorf("invalid nexthop: %s", a.NextHops[1]) + } + } + } + if safi == bgp.SAFI_FLOW_SPEC_UNICAST || safi == bgp.SAFI_FLOW_SPEC_VPN { + nexthop = "" + } else if len(a.NextHops) > 0 { + nexthop = a.NextHops[0] + if net.ParseIP(nexthop) == nil { + return nil, fmt.Errorf("invalid nexthop: %s", nexthop) + } + } + attr := bgp.NewPathAttributeMpReachNLRI(nexthop, nlris) + attr.LinkLocalNexthop = linkLocalNexthop + return attr, nil + case *api.MpUnreachNLRIAttribute: + rf := ToRouteFamily(a.Family) + nlris, err := UnmarshalNLRIs(rf, a.Nlris) + if err != nil { + return nil, err + } + return bgp.NewPathAttributeMpUnreachNLRI(nlris), nil + case *api.ExtendedCommunitiesAttribute: + return unmarshalExComm(a) + case *api.As4PathAttribute: + params := make([]*bgp.As4PathParam, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAs4Path(params), nil + case *api.As4AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid as4 aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAs4Aggregator(a.As, a.Address), nil + case *api.PmsiTunnelAttribute: + typ := bgp.PmsiTunnelType(a.Type) + var isLeafInfoRequired bool + if a.Flags&0x01 > 0 { + isLeafInfoRequired = true + } + var id bgp.PmsiTunnelIDInterface + switch typ { + case bgp.PMSI_TUNNEL_TYPE_INGRESS_REPL: + ip := net.IP(a.Id) + if ip.To4() == nil && ip.To16() == nil { + return nil, fmt.Errorf("invalid pmsi tunnel identifier: %s", a.Id) + } + id = bgp.NewIngressReplTunnelID(ip.String()) + default: + id = bgp.NewDefaultPmsiTunnelID(a.Id) + } + return bgp.NewPathAttributePmsiTunnel(typ, isLeafInfoRequired, a.Label, id), nil + case *api.TunnelEncapAttribute: + tlvs := make([]*bgp.TunnelEncapTLV, 0, len(a.Tlvs)) + for _, tlv := range a.Tlvs { + subTlvs := make([]bgp.TunnelEncapSubTLVInterface, 0, len(tlv.Tlvs)) + for _, an := range tlv.Tlvs { + var subTlv bgp.TunnelEncapSubTLVInterface + var subValue ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &subValue); err != nil { + return nil, fmt.Errorf("failed to unmarshal tunnel encapsulation attribute sub tlv: %s", err) + } + switch sv := subValue.Message.(type) { + case *api.TunnelEncapSubTLVEncapsulation: + subTlv = bgp.NewTunnelEncapSubTLVEncapsulation(sv.Key, sv.Cookie) + case *api.TunnelEncapSubTLVProtocol: + subTlv = bgp.NewTunnelEncapSubTLVProtocol(uint16(sv.Protocol)) + case *api.TunnelEncapSubTLVColor: + subTlv = bgp.NewTunnelEncapSubTLVColor(sv.Color) + case *api.TunnelEncapSubTLVUnknown: + subTlv = bgp.NewTunnelEncapSubTLVUnknown(bgp.EncapSubTLVType(sv.Type), sv.Value) + default: + return nil, fmt.Errorf("invalid tunnel encapsulation attribute sub tlv: %v", subValue.Message) + } + subTlvs = append(subTlvs, subTlv) + } + tlvs = append(tlvs, bgp.NewTunnelEncapTLV(bgp.TunnelType(tlv.Type), subTlvs)) + } + return bgp.NewPathAttributeTunnelEncap(tlvs), nil + case *api.IP6ExtendedCommunitiesAttribute: + communities := make([]bgp.ExtendedCommunityInterface, 0, len(a.Communities)) + for _, an := range a.Communities { + var community bgp.ExtendedCommunityInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal ipv6 extended community: %s", err) + } + switch v := value.Message.(type) { + case *api.IPv6AddressSpecificExtended: + community = bgp.NewIPv6AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + case *api.RedirectIPv6AddressSpecificExtended: + community = bgp.NewRedirectIPv6AddressSpecificExtended(v.Address, uint16(v.LocalAdmin)) + } + if community == nil { + return nil, fmt.Errorf("invalid ipv6 extended community: %v", value.Message) + } + communities = append(communities, community) + } + return bgp.NewPathAttributeIP6ExtendedCommunities(communities), nil + + case *api.AigpAttribute: + tlvs := make([]bgp.AigpTLVInterface, 0, len(a.Tlvs)) + for _, an := range a.Tlvs { + var tlv bgp.AigpTLVInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal aigp attribute tlv: %s", err) + } + switch v := value.Message.(type) { + case *api.AigpTLVIGPMetric: + tlv = bgp.NewAigpTLVIgpMetric(v.Metric) + case *api.AigpTLVUnknown: + tlv = bgp.NewAigpTLVDefault(bgp.AigpTLVType(v.Type), v.Value) + } + if tlv == nil { + return nil, fmt.Errorf("invalid aigp attribute tlv: %v", value.Message) + } + tlvs = append(tlvs, tlv) + } + return bgp.NewPathAttributeAigp(tlvs), nil + + case *api.LargeCommunitiesAttribute: + communities := make([]*bgp.LargeCommunity, 0, len(a.Communities)) + for _, c := range a.Communities { + communities = append(communities, bgp.NewLargeCommunity(c.GlobalAdmin, c.LocalData1, c.LocalData2)) + } + return bgp.NewPathAttributeLargeCommunities(communities), nil + + case *api.UnknownAttribute: + return bgp.NewPathAttributeUnknown(bgp.BGPAttrFlag(a.Flags), bgp.BGPAttrType(a.Type), a.Value), nil + } + return nil, errors.New("unknown path attribute") +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/capability.go b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/capability.go new file mode 100644 index 0000000..d135713 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/capability.go @@ -0,0 +1,246 @@ +// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation. +// +// 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 apiutil + +import ( + "fmt" + + proto "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +func NewMultiProtocolCapability(a *bgp.CapMultiProtocol) *api.MultiProtocolCapability { + afi, safi := bgp.RouteFamilyToAfiSafi(a.CapValue) + return &api.MultiProtocolCapability{ + Family: ToApiFamily(afi, safi), + } +} + +func NewRouteRefreshCapability(a *bgp.CapRouteRefresh) *api.RouteRefreshCapability { + return &api.RouteRefreshCapability{} +} + +func NewCarryingLabelInfoCapability(a *bgp.CapCarryingLabelInfo) *api.CarryingLabelInfoCapability { + return &api.CarryingLabelInfoCapability{} +} + +func NewExtendedNexthopCapability(a *bgp.CapExtendedNexthop) *api.ExtendedNexthopCapability { + tuples := make([]*api.ExtendedNexthopCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.ExtendedNexthopCapabilityTuple{ + NlriFamily: ToApiFamily(t.NLRIAFI, uint8(t.NLRISAFI)), + NexthopFamily: ToApiFamily(t.NexthopAFI, bgp.SAFI_UNICAST), + }) + } + return &api.ExtendedNexthopCapability{ + Tuples: tuples, + } +} + +func NewGracefulRestartCapability(a *bgp.CapGracefulRestart) *api.GracefulRestartCapability { + tuples := make([]*api.GracefulRestartCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.GracefulRestartCapabilityTuple{ + Family: ToApiFamily(t.AFI, t.SAFI), + Flags: uint32(t.Flags), + }) + } + return &api.GracefulRestartCapability{ + Flags: uint32(a.Flags), + Time: uint32(a.Time), + Tuples: tuples, + } +} + +func NewFourOctetASNumberCapability(a *bgp.CapFourOctetASNumber) *api.FourOctetASNumberCapability { + return &api.FourOctetASNumberCapability{ + As: a.CapValue, + } +} + +func NewAddPathCapability(a *bgp.CapAddPath) *api.AddPathCapability { + tuples := make([]*api.AddPathCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + afi, safi := bgp.RouteFamilyToAfiSafi(t.RouteFamily) + tuples = append(tuples, &api.AddPathCapabilityTuple{ + Family: ToApiFamily(afi, safi), + Mode: api.AddPathMode(t.Mode), + }) + } + return &api.AddPathCapability{ + Tuples: tuples, + } +} + +func NewEnhancedRouteRefreshCapability(a *bgp.CapEnhancedRouteRefresh) *api.EnhancedRouteRefreshCapability { + return &api.EnhancedRouteRefreshCapability{} +} + +func NewLongLivedGracefulRestartCapability(a *bgp.CapLongLivedGracefulRestart) *api.LongLivedGracefulRestartCapability { + tuples := make([]*api.LongLivedGracefulRestartCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.LongLivedGracefulRestartCapabilityTuple{ + Family: ToApiFamily(t.AFI, uint8(t.SAFI)), + Flags: uint32(t.Flags), + Time: t.RestartTime, + }) + } + return &api.LongLivedGracefulRestartCapability{ + Tuples: tuples, + } +} + +func NewRouteRefreshCiscoCapability(a *bgp.CapRouteRefreshCisco) *api.RouteRefreshCiscoCapability { + return &api.RouteRefreshCiscoCapability{} +} + +func NewUnknownCapability(a *bgp.CapUnknown) *api.UnknownCapability { + return &api.UnknownCapability{ + Code: uint32(a.CapCode), + Value: a.CapValue, + } +} + +func MarshalCapability(value bgp.ParameterCapabilityInterface) (*any.Any, error) { + var m proto.Message + switch n := value.(type) { + case *bgp.CapMultiProtocol: + m = NewMultiProtocolCapability(n) + case *bgp.CapRouteRefresh: + m = NewRouteRefreshCapability(n) + case *bgp.CapCarryingLabelInfo: + m = NewCarryingLabelInfoCapability(n) + case *bgp.CapExtendedNexthop: + m = NewExtendedNexthopCapability(n) + case *bgp.CapGracefulRestart: + m = NewGracefulRestartCapability(n) + case *bgp.CapFourOctetASNumber: + m = NewFourOctetASNumberCapability(n) + case *bgp.CapAddPath: + m = NewAddPathCapability(n) + case *bgp.CapEnhancedRouteRefresh: + m = NewEnhancedRouteRefreshCapability(n) + case *bgp.CapLongLivedGracefulRestart: + m = NewLongLivedGracefulRestartCapability(n) + case *bgp.CapRouteRefreshCisco: + m = NewRouteRefreshCiscoCapability(n) + case *bgp.CapUnknown: + m = NewUnknownCapability(n) + default: + return nil, fmt.Errorf("invalid capability type to marshal: %+v", value) + } + return ptypes.MarshalAny(m) +} + +func MarshalCapabilities(values []bgp.ParameterCapabilityInterface) ([]*any.Any, error) { + caps := make([]*any.Any, 0, len(values)) + for _, value := range values { + a, err := MarshalCapability(value) + if err != nil { + return nil, err + } + caps = append(caps, a) + } + return caps, nil +} + +func unmarshalCapability(a *any.Any) (bgp.ParameterCapabilityInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal capability: %s", err) + } + switch a := value.Message.(type) { + case *api.MultiProtocolCapability: + return bgp.NewCapMultiProtocol(ToRouteFamily(a.Family)), nil + case *api.RouteRefreshCapability: + return bgp.NewCapRouteRefresh(), nil + case *api.CarryingLabelInfoCapability: + return bgp.NewCapCarryingLabelInfo(), nil + case *api.ExtendedNexthopCapability: + tuples := make([]*bgp.CapExtendedNexthopTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var nhAfi uint16 + switch t.NexthopFamily.Afi { + case api.Family_AFI_IP: + nhAfi = bgp.AFI_IP + case api.Family_AFI_IP6: + nhAfi = bgp.AFI_IP6 + default: + return nil, fmt.Errorf("invalid address family for nexthop afi in extended nexthop capability: %s", t.NexthopFamily) + } + tuples = append(tuples, bgp.NewCapExtendedNexthopTuple(ToRouteFamily(t.NlriFamily), nhAfi)) + } + return bgp.NewCapExtendedNexthop(tuples), nil + case *api.GracefulRestartCapability: + tuples := make([]*bgp.CapGracefulRestartTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var forward bool + if t.Flags&0x80 > 0 { + forward = true + } + tuples = append(tuples, bgp.NewCapGracefulRestartTuple(ToRouteFamily(t.Family), forward)) + } + var restarting bool + if a.Flags&0x08 > 0 { + restarting = true + } + var notification bool + if a.Flags&0x04 > 0 { + notification = true + } + return bgp.NewCapGracefulRestart(restarting, notification, uint16(a.Time), tuples), nil + case *api.FourOctetASNumberCapability: + return bgp.NewCapFourOctetASNumber(a.As), nil + case *api.AddPathCapability: + tuples := make([]*bgp.CapAddPathTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, bgp.NewCapAddPathTuple(ToRouteFamily(t.Family), bgp.BGPAddPathMode(t.Mode))) + } + return bgp.NewCapAddPath(tuples), nil + case *api.EnhancedRouteRefreshCapability: + return bgp.NewCapEnhancedRouteRefresh(), nil + case *api.LongLivedGracefulRestartCapability: + tuples := make([]*bgp.CapLongLivedGracefulRestartTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var forward bool + if t.Flags&0x80 > 0 { + forward = true + } + tuples = append(tuples, bgp.NewCapLongLivedGracefulRestartTuple(ToRouteFamily(t.Family), forward, t.Time)) + } + return bgp.NewCapLongLivedGracefulRestart(tuples), nil + case *api.RouteRefreshCiscoCapability: + return bgp.NewCapRouteRefreshCisco(), nil + case *api.UnknownCapability: + return bgp.NewCapUnknown(bgp.BGPCapabilityCode(a.Code), a.Value), nil + } + return nil, fmt.Errorf("invalid capability type to unmarshal: %s", a.TypeUrl) +} + +func UnmarshalCapabilities(values []*any.Any) ([]bgp.ParameterCapabilityInterface, error) { + caps := make([]bgp.ParameterCapabilityInterface, 0, len(values)) + for _, value := range values { + c, err := unmarshalCapability(value) + if err != nil { + return nil, err + } + caps = append(caps, c) + } + return caps, nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/util.go b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/util.go new file mode 100644 index 0000000..4c18b75 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/apiutil/util.go @@ -0,0 +1,125 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 apiutil + +import ( + "encoding/json" + "net" + "time" + + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +// workaround. This for the json format compatibility. Once we update senario tests, we can remove this. +type Path struct { + Nlri bgp.AddrPrefixInterface `json:"nlri"` + Age int64 `json:"age"` + Best bool `json:"best"` + Attrs []bgp.PathAttributeInterface `json:"attrs"` + Stale bool `json:"stale"` + Withdrawal bool `json:"withdrawal,omitempty"` + SourceID net.IP `json:"source-id,omitempty"` + NeighborIP net.IP `json:"neighbor-ip,omitempty"` +} + +type Destination struct { + Paths []*Path +} + +func (d *Destination) MarshalJSON() ([]byte, error) { + return json.Marshal(d.Paths) +} + +func NewDestination(dst *api.Destination) *Destination { + l := make([]*Path, 0, len(dst.Paths)) + for _, p := range dst.Paths { + nlri, _ := GetNativeNlri(p) + attrs, _ := GetNativePathAttributes(p) + l = append(l, &Path{ + Nlri: nlri, + Age: p.Age, + Best: p.Best, + Attrs: attrs, + Stale: p.Stale, + Withdrawal: p.IsWithdraw, + SourceID: net.ParseIP(p.SourceId), + NeighborIP: net.ParseIP(p.NeighborIp), + }) + } + return &Destination{Paths: l} +} + +func NewPath(nlri bgp.AddrPrefixInterface, isWithdraw bool, attrs []bgp.PathAttributeInterface, age time.Time) *api.Path { + return &api.Path{ + AnyNlri: MarshalNLRI(nlri), + AnyPattrs: MarshalPathAttributes(attrs), + Age: age.Unix(), + IsWithdraw: isWithdraw, + Family: ToApiFamily(nlri.AFI(), nlri.SAFI()), + Identifier: nlri.PathIdentifier(), + } +} + +func getNLRI(family bgp.RouteFamily, buf []byte) (bgp.AddrPrefixInterface, error) { + afi, safi := bgp.RouteFamilyToAfiSafi(family) + nlri, err := bgp.NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return nil, err + } + if err := nlri.DecodeFromBytes(buf); err != nil { + return nil, err + } + return nlri, nil +} + +func GetNativeNlri(p *api.Path) (bgp.AddrPrefixInterface, error) { + if len(p.Nlri) > 0 { + return getNLRI(ToRouteFamily(p.Family), p.Nlri) + } + return UnmarshalNLRI(ToRouteFamily(p.Family), p.AnyNlri) +} + +func GetNativePathAttributes(p *api.Path) ([]bgp.PathAttributeInterface, error) { + pattrsLen := len(p.Pattrs) + if pattrsLen > 0 { + pattrs := make([]bgp.PathAttributeInterface, 0, pattrsLen) + for _, attr := range p.Pattrs { + a, err := bgp.GetPathAttribute(attr) + if err != nil { + return nil, err + } + err = a.DecodeFromBytes(attr) + if err != nil { + return nil, err + } + pattrs = append(pattrs, a) + } + return pattrs, nil + } + return UnmarshalPathAttributes(p.AnyPattrs) +} + +func ToRouteFamily(f *api.Family) bgp.RouteFamily { + return bgp.AfiSafiToRouteFamily(uint16(f.Afi), uint8(f.Safi)) +} + +func ToApiFamily(afi uint16, safi uint8) *api.Family { + return &api.Family{ + Afi: api.Family_Afi(afi), + Safi: api.Family_Safi(safi), + } +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/bgp_configs.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/bgp_configs.go new file mode 100644 index 0000000..5c3cff9 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/bgp_configs.go @@ -0,0 +1,6335 @@ +// DO NOT EDIT +// generated by pyang using OpenConfig https://github.com/openconfig/public +// +// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation. +// +// 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 config + +import ( + "fmt" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +func mapkey(index int, name string) string { + if name != "" { + return name + } + return fmt.Sprintf("%v", index) +} + +// typedef for typedef openconfig-types:std-regexp. +type StdRegexp string + +// typedef for typedef openconfig-types:percentage. +type Percentage uint8 + +// typedef for typedef bgp-types:rr-cluster-id-type. +type RrClusterIdType string + +// typedef for identity bgp-types:remove-private-as-option. +// set of options for configuring how private AS path numbers +// are removed from advertisements. +type RemovePrivateAsOption string + +const ( + REMOVE_PRIVATE_AS_OPTION_ALL RemovePrivateAsOption = "all" + REMOVE_PRIVATE_AS_OPTION_REPLACE RemovePrivateAsOption = "replace" +) + +var RemovePrivateAsOptionToIntMap = map[RemovePrivateAsOption]int{ + REMOVE_PRIVATE_AS_OPTION_ALL: 0, + REMOVE_PRIVATE_AS_OPTION_REPLACE: 1, +} + +func (v RemovePrivateAsOption) ToInt() int { + i, ok := RemovePrivateAsOptionToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToRemovePrivateAsOptionMap = map[int]RemovePrivateAsOption{ + 0: REMOVE_PRIVATE_AS_OPTION_ALL, + 1: REMOVE_PRIVATE_AS_OPTION_REPLACE, +} + +func (v RemovePrivateAsOption) Validate() error { + if _, ok := RemovePrivateAsOptionToIntMap[v]; !ok { + return fmt.Errorf("invalid RemovePrivateAsOption: %s", v) + } + return nil +} + +// typedef for typedef bgp-types:bgp-community-regexp-type. +type BgpCommunityRegexpType StdRegexp + +// typedef for identity bgp-types:community-type. +// type describing variations of community attributes: +// STANDARD: standard BGP community [rfc1997] +// EXTENDED: extended BGP community [rfc4360] +// BOTH: both standard and extended community. +type CommunityType string + +const ( + COMMUNITY_TYPE_STANDARD CommunityType = "standard" + COMMUNITY_TYPE_EXTENDED CommunityType = "extended" + COMMUNITY_TYPE_BOTH CommunityType = "both" + COMMUNITY_TYPE_NONE CommunityType = "none" +) + +var CommunityTypeToIntMap = map[CommunityType]int{ + COMMUNITY_TYPE_STANDARD: 0, + COMMUNITY_TYPE_EXTENDED: 1, + COMMUNITY_TYPE_BOTH: 2, + COMMUNITY_TYPE_NONE: 3, +} + +func (v CommunityType) ToInt() int { + i, ok := CommunityTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToCommunityTypeMap = map[int]CommunityType{ + 0: COMMUNITY_TYPE_STANDARD, + 1: COMMUNITY_TYPE_EXTENDED, + 2: COMMUNITY_TYPE_BOTH, + 3: COMMUNITY_TYPE_NONE, +} + +func (v CommunityType) Validate() error { + if _, ok := CommunityTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid CommunityType: %s", v) + } + return nil +} + +// typedef for typedef bgp-types:bgp-ext-community-type. +type BgpExtCommunityType string + +// typedef for typedef bgp-types:bgp-std-community-type. +type BgpStdCommunityType string + +// typedef for identity bgp-types:peer-type. +// labels a peer or peer group as explicitly internal or +// external. +type PeerType string + +const ( + PEER_TYPE_INTERNAL PeerType = "internal" + PEER_TYPE_EXTERNAL PeerType = "external" +) + +var PeerTypeToIntMap = map[PeerType]int{ + PEER_TYPE_INTERNAL: 0, + PEER_TYPE_EXTERNAL: 1, +} + +func (v PeerType) ToInt() int { + i, ok := PeerTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToPeerTypeMap = map[int]PeerType{ + 0: PEER_TYPE_INTERNAL, + 1: PEER_TYPE_EXTERNAL, +} + +func (v PeerType) Validate() error { + if _, ok := PeerTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid PeerType: %s", v) + } + return nil +} + +// typedef for identity bgp-types:bgp-session-direction. +// Type to describe the direction of NLRI transmission. +type BgpSessionDirection string + +const ( + BGP_SESSION_DIRECTION_INBOUND BgpSessionDirection = "inbound" + BGP_SESSION_DIRECTION_OUTBOUND BgpSessionDirection = "outbound" +) + +var BgpSessionDirectionToIntMap = map[BgpSessionDirection]int{ + BGP_SESSION_DIRECTION_INBOUND: 0, + BGP_SESSION_DIRECTION_OUTBOUND: 1, +} + +func (v BgpSessionDirection) ToInt() int { + i, ok := BgpSessionDirectionToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBgpSessionDirectionMap = map[int]BgpSessionDirection{ + 0: BGP_SESSION_DIRECTION_INBOUND, + 1: BGP_SESSION_DIRECTION_OUTBOUND, +} + +func (v BgpSessionDirection) Validate() error { + if _, ok := BgpSessionDirectionToIntMap[v]; !ok { + return fmt.Errorf("invalid BgpSessionDirection: %s", v) + } + return nil +} + +// typedef for identity bgp-types:bgp-origin-attr-type. +// Type definition for standard BGP origin attribute. +type BgpOriginAttrType string + +const ( + BGP_ORIGIN_ATTR_TYPE_IGP BgpOriginAttrType = "igp" + BGP_ORIGIN_ATTR_TYPE_EGP BgpOriginAttrType = "egp" + BGP_ORIGIN_ATTR_TYPE_INCOMPLETE BgpOriginAttrType = "incomplete" +) + +var BgpOriginAttrTypeToIntMap = map[BgpOriginAttrType]int{ + BGP_ORIGIN_ATTR_TYPE_IGP: 0, + BGP_ORIGIN_ATTR_TYPE_EGP: 1, + BGP_ORIGIN_ATTR_TYPE_INCOMPLETE: 2, +} + +func (v BgpOriginAttrType) ToInt() int { + i, ok := BgpOriginAttrTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBgpOriginAttrTypeMap = map[int]BgpOriginAttrType{ + 0: BGP_ORIGIN_ATTR_TYPE_IGP, + 1: BGP_ORIGIN_ATTR_TYPE_EGP, + 2: BGP_ORIGIN_ATTR_TYPE_INCOMPLETE, +} + +func (v BgpOriginAttrType) Validate() error { + if _, ok := BgpOriginAttrTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid BgpOriginAttrType: %s", v) + } + return nil +} + +// typedef for identity bgp-types:afi-safi-type. +// Base identity type for AFI,SAFI tuples for BGP-4. +type AfiSafiType string + +const ( + AFI_SAFI_TYPE_IPV4_UNICAST AfiSafiType = "ipv4-unicast" + AFI_SAFI_TYPE_IPV6_UNICAST AfiSafiType = "ipv6-unicast" + AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST AfiSafiType = "ipv4-labelled-unicast" + AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST AfiSafiType = "ipv6-labelled-unicast" + AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST AfiSafiType = "l3vpn-ipv4-unicast" + AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST AfiSafiType = "l3vpn-ipv6-unicast" + AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST AfiSafiType = "l3vpn-ipv4-multicast" + AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST AfiSafiType = "l3vpn-ipv6-multicast" + AFI_SAFI_TYPE_L2VPN_VPLS AfiSafiType = "l2vpn-vpls" + AFI_SAFI_TYPE_L2VPN_EVPN AfiSafiType = "l2vpn-evpn" + AFI_SAFI_TYPE_IPV4_MULTICAST AfiSafiType = "ipv4-multicast" + AFI_SAFI_TYPE_IPV6_MULTICAST AfiSafiType = "ipv6-multicast" + AFI_SAFI_TYPE_RTC AfiSafiType = "rtc" + AFI_SAFI_TYPE_IPV4_ENCAP AfiSafiType = "ipv4-encap" + AFI_SAFI_TYPE_IPV6_ENCAP AfiSafiType = "ipv6-encap" + AFI_SAFI_TYPE_IPV4_FLOWSPEC AfiSafiType = "ipv4-flowspec" + AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC AfiSafiType = "l3vpn-ipv4-flowspec" + AFI_SAFI_TYPE_IPV6_FLOWSPEC AfiSafiType = "ipv6-flowspec" + AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC AfiSafiType = "l3vpn-ipv6-flowspec" + AFI_SAFI_TYPE_L2VPN_FLOWSPEC AfiSafiType = "l2vpn-flowspec" + AFI_SAFI_TYPE_OPAQUE AfiSafiType = "opaque" +) + +var AfiSafiTypeToIntMap = map[AfiSafiType]int{ + AFI_SAFI_TYPE_IPV4_UNICAST: 0, + AFI_SAFI_TYPE_IPV6_UNICAST: 1, + AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST: 2, + AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST: 3, + AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST: 4, + AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST: 5, + AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST: 6, + AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST: 7, + AFI_SAFI_TYPE_L2VPN_VPLS: 8, + AFI_SAFI_TYPE_L2VPN_EVPN: 9, + AFI_SAFI_TYPE_IPV4_MULTICAST: 10, + AFI_SAFI_TYPE_IPV6_MULTICAST: 11, + AFI_SAFI_TYPE_RTC: 12, + AFI_SAFI_TYPE_IPV4_ENCAP: 13, + AFI_SAFI_TYPE_IPV6_ENCAP: 14, + AFI_SAFI_TYPE_IPV4_FLOWSPEC: 15, + AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC: 16, + AFI_SAFI_TYPE_IPV6_FLOWSPEC: 17, + AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC: 18, + AFI_SAFI_TYPE_L2VPN_FLOWSPEC: 19, + AFI_SAFI_TYPE_OPAQUE: 20, +} + +func (v AfiSafiType) ToInt() int { + i, ok := AfiSafiTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToAfiSafiTypeMap = map[int]AfiSafiType{ + 0: AFI_SAFI_TYPE_IPV4_UNICAST, + 1: AFI_SAFI_TYPE_IPV6_UNICAST, + 2: AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST, + 3: AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST, + 4: AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST, + 5: AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST, + 6: AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST, + 7: AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST, + 8: AFI_SAFI_TYPE_L2VPN_VPLS, + 9: AFI_SAFI_TYPE_L2VPN_EVPN, + 10: AFI_SAFI_TYPE_IPV4_MULTICAST, + 11: AFI_SAFI_TYPE_IPV6_MULTICAST, + 12: AFI_SAFI_TYPE_RTC, + 13: AFI_SAFI_TYPE_IPV4_ENCAP, + 14: AFI_SAFI_TYPE_IPV6_ENCAP, + 15: AFI_SAFI_TYPE_IPV4_FLOWSPEC, + 16: AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC, + 17: AFI_SAFI_TYPE_IPV6_FLOWSPEC, + 18: AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC, + 19: AFI_SAFI_TYPE_L2VPN_FLOWSPEC, + 20: AFI_SAFI_TYPE_OPAQUE, +} + +func (v AfiSafiType) Validate() error { + if _, ok := AfiSafiTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid AfiSafiType: %s", v) + } + return nil +} + +// typedef for identity bgp-types:bgp-capability. +// Base identity for a BGP capability. +type BgpCapability string + +const ( + BGP_CAPABILITY_MPBGP BgpCapability = "mpbgp" + BGP_CAPABILITY_ROUTE_REFRESH BgpCapability = "route-refresh" + BGP_CAPABILITY_ASN32 BgpCapability = "asn32" + BGP_CAPABILITY_GRACEFUL_RESTART BgpCapability = "graceful-restart" + BGP_CAPABILITY_ADD_PATHS BgpCapability = "add-paths" +) + +var BgpCapabilityToIntMap = map[BgpCapability]int{ + BGP_CAPABILITY_MPBGP: 0, + BGP_CAPABILITY_ROUTE_REFRESH: 1, + BGP_CAPABILITY_ASN32: 2, + BGP_CAPABILITY_GRACEFUL_RESTART: 3, + BGP_CAPABILITY_ADD_PATHS: 4, +} + +func (v BgpCapability) ToInt() int { + i, ok := BgpCapabilityToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBgpCapabilityMap = map[int]BgpCapability{ + 0: BGP_CAPABILITY_MPBGP, + 1: BGP_CAPABILITY_ROUTE_REFRESH, + 2: BGP_CAPABILITY_ASN32, + 3: BGP_CAPABILITY_GRACEFUL_RESTART, + 4: BGP_CAPABILITY_ADD_PATHS, +} + +func (v BgpCapability) Validate() error { + if _, ok := BgpCapabilityToIntMap[v]; !ok { + return fmt.Errorf("invalid BgpCapability: %s", v) + } + return nil +} + +// typedef for identity bgp-types:bgp-well-known-std-community. +// Reserved communities within the standard community space +// defined by RFC1997. These communities must fall within the +// range 0x00000000 to 0xFFFFFFFF. +type BgpWellKnownStdCommunity string + +const ( + BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT BgpWellKnownStdCommunity = "no_export" + BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE BgpWellKnownStdCommunity = "no_advertise" + BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED BgpWellKnownStdCommunity = "no_export_subconfed" + BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER BgpWellKnownStdCommunity = "nopeer" +) + +var BgpWellKnownStdCommunityToIntMap = map[BgpWellKnownStdCommunity]int{ + BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT: 0, + BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE: 1, + BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED: 2, + BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER: 3, +} + +func (v BgpWellKnownStdCommunity) ToInt() int { + i, ok := BgpWellKnownStdCommunityToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBgpWellKnownStdCommunityMap = map[int]BgpWellKnownStdCommunity{ + 0: BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT, + 1: BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE, + 2: BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED, + 3: BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER, +} + +func (v BgpWellKnownStdCommunity) Validate() error { + if _, ok := BgpWellKnownStdCommunityToIntMap[v]; !ok { + return fmt.Errorf("invalid BgpWellKnownStdCommunity: %s", v) + } + return nil +} + +// typedef for identity ptypes:match-set-options-restricted-type. +// Options that govern the behavior of a match statement. The +// default behavior is ANY, i.e., the given value matches any +// of the members of the defined set. Note this type is a +// restricted version of the match-set-options-type. +type MatchSetOptionsRestrictedType string + +const ( + MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY MatchSetOptionsRestrictedType = "any" + MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT MatchSetOptionsRestrictedType = "invert" +) + +var MatchSetOptionsRestrictedTypeToIntMap = map[MatchSetOptionsRestrictedType]int{ + MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY: 0, + MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT: 1, +} + +func (v MatchSetOptionsRestrictedType) ToInt() int { + i, ok := MatchSetOptionsRestrictedTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToMatchSetOptionsRestrictedTypeMap = map[int]MatchSetOptionsRestrictedType{ + 0: MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY, + 1: MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT, +} + +func (v MatchSetOptionsRestrictedType) Validate() error { + if _, ok := MatchSetOptionsRestrictedTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid MatchSetOptionsRestrictedType: %s", v) + } + return nil +} + +func (v MatchSetOptionsRestrictedType) Default() MatchSetOptionsRestrictedType { + return MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY +} + +func (v MatchSetOptionsRestrictedType) DefaultAsNeeded() MatchSetOptionsRestrictedType { + if string(v) == "" { + return v.Default() + } + return v +} + +// typedef for identity ptypes:match-set-options-type. +// Options that govern the behavior of a match statement. The +// default behavior is ANY, i.e., the given value matches any +// of the members of the defined set. +type MatchSetOptionsType string + +const ( + MATCH_SET_OPTIONS_TYPE_ANY MatchSetOptionsType = "any" + MATCH_SET_OPTIONS_TYPE_ALL MatchSetOptionsType = "all" + MATCH_SET_OPTIONS_TYPE_INVERT MatchSetOptionsType = "invert" +) + +var MatchSetOptionsTypeToIntMap = map[MatchSetOptionsType]int{ + MATCH_SET_OPTIONS_TYPE_ANY: 0, + MATCH_SET_OPTIONS_TYPE_ALL: 1, + MATCH_SET_OPTIONS_TYPE_INVERT: 2, +} + +func (v MatchSetOptionsType) ToInt() int { + i, ok := MatchSetOptionsTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToMatchSetOptionsTypeMap = map[int]MatchSetOptionsType{ + 0: MATCH_SET_OPTIONS_TYPE_ANY, + 1: MATCH_SET_OPTIONS_TYPE_ALL, + 2: MATCH_SET_OPTIONS_TYPE_INVERT, +} + +func (v MatchSetOptionsType) Validate() error { + if _, ok := MatchSetOptionsTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid MatchSetOptionsType: %s", v) + } + return nil +} + +func (v MatchSetOptionsType) Default() MatchSetOptionsType { + return MATCH_SET_OPTIONS_TYPE_ANY +} + +func (v MatchSetOptionsType) DefaultAsNeeded() MatchSetOptionsType { + if string(v) == "" { + return v.Default() + } + return v +} + +// typedef for typedef ptypes:tag-type. +type TagType string + +// typedef for identity ptypes:install-protocol-type. +// Base type for protocols which can install prefixes into the +// RIB. +type InstallProtocolType string + +const ( + INSTALL_PROTOCOL_TYPE_BGP InstallProtocolType = "bgp" + INSTALL_PROTOCOL_TYPE_ISIS InstallProtocolType = "isis" + INSTALL_PROTOCOL_TYPE_OSPF InstallProtocolType = "ospf" + INSTALL_PROTOCOL_TYPE_OSPF3 InstallProtocolType = "ospf3" + INSTALL_PROTOCOL_TYPE_STATIC InstallProtocolType = "static" + INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED InstallProtocolType = "directly-connected" + INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE InstallProtocolType = "local-aggregate" +) + +var InstallProtocolTypeToIntMap = map[InstallProtocolType]int{ + INSTALL_PROTOCOL_TYPE_BGP: 0, + INSTALL_PROTOCOL_TYPE_ISIS: 1, + INSTALL_PROTOCOL_TYPE_OSPF: 2, + INSTALL_PROTOCOL_TYPE_OSPF3: 3, + INSTALL_PROTOCOL_TYPE_STATIC: 4, + INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED: 5, + INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE: 6, +} + +func (v InstallProtocolType) ToInt() int { + i, ok := InstallProtocolTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToInstallProtocolTypeMap = map[int]InstallProtocolType{ + 0: INSTALL_PROTOCOL_TYPE_BGP, + 1: INSTALL_PROTOCOL_TYPE_ISIS, + 2: INSTALL_PROTOCOL_TYPE_OSPF, + 3: INSTALL_PROTOCOL_TYPE_OSPF3, + 4: INSTALL_PROTOCOL_TYPE_STATIC, + 5: INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED, + 6: INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE, +} + +func (v InstallProtocolType) Validate() error { + if _, ok := InstallProtocolTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid InstallProtocolType: %s", v) + } + return nil +} + +// typedef for identity ptypes:attribute-comparison. +// base type for supported comparison operators on route +// attributes. +type AttributeComparison string + +const ( + ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ AttributeComparison = "attribute-eq" + ATTRIBUTE_COMPARISON_ATTRIBUTE_GE AttributeComparison = "attribute-ge" + ATTRIBUTE_COMPARISON_ATTRIBUTE_LE AttributeComparison = "attribute-le" + ATTRIBUTE_COMPARISON_EQ AttributeComparison = "eq" + ATTRIBUTE_COMPARISON_GE AttributeComparison = "ge" + ATTRIBUTE_COMPARISON_LE AttributeComparison = "le" +) + +var AttributeComparisonToIntMap = map[AttributeComparison]int{ + ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ: 0, + ATTRIBUTE_COMPARISON_ATTRIBUTE_GE: 1, + ATTRIBUTE_COMPARISON_ATTRIBUTE_LE: 2, + ATTRIBUTE_COMPARISON_EQ: 3, + ATTRIBUTE_COMPARISON_GE: 4, + ATTRIBUTE_COMPARISON_LE: 5, +} + +func (v AttributeComparison) ToInt() int { + i, ok := AttributeComparisonToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToAttributeComparisonMap = map[int]AttributeComparison{ + 0: ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ, + 1: ATTRIBUTE_COMPARISON_ATTRIBUTE_GE, + 2: ATTRIBUTE_COMPARISON_ATTRIBUTE_LE, + 3: ATTRIBUTE_COMPARISON_EQ, + 4: ATTRIBUTE_COMPARISON_GE, + 5: ATTRIBUTE_COMPARISON_LE, +} + +func (v AttributeComparison) Validate() error { + if _, ok := AttributeComparisonToIntMap[v]; !ok { + return fmt.Errorf("invalid AttributeComparison: %s", v) + } + return nil +} + +// typedef for identity rpol:route-disposition. +// Select the final disposition for the route, either +// accept or reject. +type RouteDisposition string + +const ( + ROUTE_DISPOSITION_NONE RouteDisposition = "none" + ROUTE_DISPOSITION_ACCEPT_ROUTE RouteDisposition = "accept-route" + ROUTE_DISPOSITION_REJECT_ROUTE RouteDisposition = "reject-route" +) + +var RouteDispositionToIntMap = map[RouteDisposition]int{ + ROUTE_DISPOSITION_NONE: 0, + ROUTE_DISPOSITION_ACCEPT_ROUTE: 1, + ROUTE_DISPOSITION_REJECT_ROUTE: 2, +} + +func (v RouteDisposition) ToInt() int { + i, ok := RouteDispositionToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToRouteDispositionMap = map[int]RouteDisposition{ + 0: ROUTE_DISPOSITION_NONE, + 1: ROUTE_DISPOSITION_ACCEPT_ROUTE, + 2: ROUTE_DISPOSITION_REJECT_ROUTE, +} + +func (v RouteDisposition) Validate() error { + if _, ok := RouteDispositionToIntMap[v]; !ok { + return fmt.Errorf("invalid RouteDisposition: %s", v) + } + return nil +} + +// typedef for identity rpol:route-type. +// Condition to check the route type in the route update. +type RouteType string + +const ( + ROUTE_TYPE_NONE RouteType = "none" + ROUTE_TYPE_INTERNAL RouteType = "internal" + ROUTE_TYPE_EXTERNAL RouteType = "external" + ROUTE_TYPE_LOCAL RouteType = "local" +) + +var RouteTypeToIntMap = map[RouteType]int{ + ROUTE_TYPE_NONE: 0, + ROUTE_TYPE_INTERNAL: 1, + ROUTE_TYPE_EXTERNAL: 2, + ROUTE_TYPE_LOCAL: 3, +} + +func (v RouteType) ToInt() int { + i, ok := RouteTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToRouteTypeMap = map[int]RouteType{ + 0: ROUTE_TYPE_NONE, + 1: ROUTE_TYPE_INTERNAL, + 2: ROUTE_TYPE_EXTERNAL, + 3: ROUTE_TYPE_LOCAL, +} + +func (v RouteType) Validate() error { + if _, ok := RouteTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid RouteType: %s", v) + } + return nil +} + +// typedef for identity rpol:default-policy-type. +// type used to specify default route disposition in +// a policy chain. +type DefaultPolicyType string + +const ( + DEFAULT_POLICY_TYPE_ACCEPT_ROUTE DefaultPolicyType = "accept-route" + DEFAULT_POLICY_TYPE_REJECT_ROUTE DefaultPolicyType = "reject-route" +) + +var DefaultPolicyTypeToIntMap = map[DefaultPolicyType]int{ + DEFAULT_POLICY_TYPE_ACCEPT_ROUTE: 0, + DEFAULT_POLICY_TYPE_REJECT_ROUTE: 1, +} + +func (v DefaultPolicyType) ToInt() int { + i, ok := DefaultPolicyTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToDefaultPolicyTypeMap = map[int]DefaultPolicyType{ + 0: DEFAULT_POLICY_TYPE_ACCEPT_ROUTE, + 1: DEFAULT_POLICY_TYPE_REJECT_ROUTE, +} + +func (v DefaultPolicyType) Validate() error { + if _, ok := DefaultPolicyTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid DefaultPolicyType: %s", v) + } + return nil +} + +// typedef for identity bgp:session-state. +// Operational state of the BGP peer. +type SessionState string + +const ( + SESSION_STATE_IDLE SessionState = "idle" + SESSION_STATE_CONNECT SessionState = "connect" + SESSION_STATE_ACTIVE SessionState = "active" + SESSION_STATE_OPENSENT SessionState = "opensent" + SESSION_STATE_OPENCONFIRM SessionState = "openconfirm" + SESSION_STATE_ESTABLISHED SessionState = "established" +) + +var SessionStateToIntMap = map[SessionState]int{ + SESSION_STATE_IDLE: 0, + SESSION_STATE_CONNECT: 1, + SESSION_STATE_ACTIVE: 2, + SESSION_STATE_OPENSENT: 3, + SESSION_STATE_OPENCONFIRM: 4, + SESSION_STATE_ESTABLISHED: 5, +} + +func (v SessionState) ToInt() int { + i, ok := SessionStateToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToSessionStateMap = map[int]SessionState{ + 0: SESSION_STATE_IDLE, + 1: SESSION_STATE_CONNECT, + 2: SESSION_STATE_ACTIVE, + 3: SESSION_STATE_OPENSENT, + 4: SESSION_STATE_OPENCONFIRM, + 5: SESSION_STATE_ESTABLISHED, +} + +func (v SessionState) Validate() error { + if _, ok := SessionStateToIntMap[v]; !ok { + return fmt.Errorf("invalid SessionState: %s", v) + } + return nil +} + +// typedef for identity bgp:admin-state. +type AdminState string + +const ( + ADMIN_STATE_UP AdminState = "up" + ADMIN_STATE_DOWN AdminState = "down" + ADMIN_STATE_PFX_CT AdminState = "pfx_ct" +) + +var AdminStateToIntMap = map[AdminState]int{ + ADMIN_STATE_UP: 0, + ADMIN_STATE_DOWN: 1, + ADMIN_STATE_PFX_CT: 2, +} + +func (v AdminState) ToInt() int { + i, ok := AdminStateToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToAdminStateMap = map[int]AdminState{ + 0: ADMIN_STATE_UP, + 1: ADMIN_STATE_DOWN, + 2: ADMIN_STATE_PFX_CT, +} + +func (v AdminState) Validate() error { + if _, ok := AdminStateToIntMap[v]; !ok { + return fmt.Errorf("invalid AdminState: %s", v) + } + return nil +} + +// typedef for identity bgp:mode. +// Ths leaf indicates the mode of operation of BGP graceful +// restart with the peer. +type Mode string + +const ( + MODE_HELPER_ONLY Mode = "helper-only" + MODE_BILATERAL Mode = "bilateral" + MODE_REMOTE_HELPER Mode = "remote-helper" +) + +var ModeToIntMap = map[Mode]int{ + MODE_HELPER_ONLY: 0, + MODE_BILATERAL: 1, + MODE_REMOTE_HELPER: 2, +} + +func (v Mode) ToInt() int { + i, ok := ModeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToModeMap = map[int]Mode{ + 0: MODE_HELPER_ONLY, + 1: MODE_BILATERAL, + 2: MODE_REMOTE_HELPER, +} + +func (v Mode) Validate() error { + if _, ok := ModeToIntMap[v]; !ok { + return fmt.Errorf("invalid Mode: %s", v) + } + return nil +} + +// typedef for typedef bgp-pol:bgp-next-hop-type. +type BgpNextHopType string + +// typedef for typedef bgp-pol:bgp-as-path-prepend-repeat. +type BgpAsPathPrependRepeat uint8 + +// typedef for typedef bgp-pol:bgp-set-med-type. +type BgpSetMedType string + +// typedef for identity bgp-pol:bgp-set-community-option-type. +// Type definition for options when setting the community +// attribute in a policy action. +type BgpSetCommunityOptionType string + +const ( + BGP_SET_COMMUNITY_OPTION_TYPE_ADD BgpSetCommunityOptionType = "add" + BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE BgpSetCommunityOptionType = "remove" + BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE BgpSetCommunityOptionType = "replace" +) + +var BgpSetCommunityOptionTypeToIntMap = map[BgpSetCommunityOptionType]int{ + BGP_SET_COMMUNITY_OPTION_TYPE_ADD: 0, + BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: 1, + BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: 2, +} + +func (v BgpSetCommunityOptionType) ToInt() int { + i, ok := BgpSetCommunityOptionTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBgpSetCommunityOptionTypeMap = map[int]BgpSetCommunityOptionType{ + 0: BGP_SET_COMMUNITY_OPTION_TYPE_ADD, + 1: BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE, + 2: BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE, +} + +func (v BgpSetCommunityOptionType) Validate() error { + if _, ok := BgpSetCommunityOptionTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid BgpSetCommunityOptionType: %s", v) + } + return nil +} + +// typedef for identity gobgp:bmp-route-monitoring-policy-type. +type BmpRouteMonitoringPolicyType string + +const ( + BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY BmpRouteMonitoringPolicyType = "pre-policy" + BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY BmpRouteMonitoringPolicyType = "post-policy" + BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH BmpRouteMonitoringPolicyType = "both" + BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB BmpRouteMonitoringPolicyType = "local-rib" + BMP_ROUTE_MONITORING_POLICY_TYPE_ALL BmpRouteMonitoringPolicyType = "all" +) + +var BmpRouteMonitoringPolicyTypeToIntMap = map[BmpRouteMonitoringPolicyType]int{ + BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY: 0, + BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY: 1, + BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH: 2, + BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB: 3, + BMP_ROUTE_MONITORING_POLICY_TYPE_ALL: 4, +} + +func (v BmpRouteMonitoringPolicyType) ToInt() int { + i, ok := BmpRouteMonitoringPolicyTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToBmpRouteMonitoringPolicyTypeMap = map[int]BmpRouteMonitoringPolicyType{ + 0: BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY, + 1: BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY, + 2: BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH, + 3: BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB, + 4: BMP_ROUTE_MONITORING_POLICY_TYPE_ALL, +} + +func (v BmpRouteMonitoringPolicyType) Validate() error { + if _, ok := BmpRouteMonitoringPolicyTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid BmpRouteMonitoringPolicyType: %s", v) + } + return nil +} + +// typedef for identity gobgp:mrt-type. +type MrtType string + +const ( + MRT_TYPE_UPDATES MrtType = "updates" + MRT_TYPE_TABLE MrtType = "table" +) + +var MrtTypeToIntMap = map[MrtType]int{ + MRT_TYPE_UPDATES: 0, + MRT_TYPE_TABLE: 1, +} + +func (v MrtType) ToInt() int { + i, ok := MrtTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToMrtTypeMap = map[int]MrtType{ + 0: MRT_TYPE_UPDATES, + 1: MRT_TYPE_TABLE, +} + +func (v MrtType) Validate() error { + if _, ok := MrtTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid MrtType: %s", v) + } + return nil +} + +// typedef for identity gobgp:rpki-validation-result-type. +// indicate the validation result of RPKI based on ROA. +type RpkiValidationResultType string + +const ( + RPKI_VALIDATION_RESULT_TYPE_NONE RpkiValidationResultType = "none" + RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND RpkiValidationResultType = "not-found" + RPKI_VALIDATION_RESULT_TYPE_VALID RpkiValidationResultType = "valid" + RPKI_VALIDATION_RESULT_TYPE_INVALID RpkiValidationResultType = "invalid" +) + +var RpkiValidationResultTypeToIntMap = map[RpkiValidationResultType]int{ + RPKI_VALIDATION_RESULT_TYPE_NONE: 0, + RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND: 1, + RPKI_VALIDATION_RESULT_TYPE_VALID: 2, + RPKI_VALIDATION_RESULT_TYPE_INVALID: 3, +} + +func (v RpkiValidationResultType) ToInt() int { + i, ok := RpkiValidationResultTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToRpkiValidationResultTypeMap = map[int]RpkiValidationResultType{ + 0: RPKI_VALIDATION_RESULT_TYPE_NONE, + 1: RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND, + 2: RPKI_VALIDATION_RESULT_TYPE_VALID, + 3: RPKI_VALIDATION_RESULT_TYPE_INVALID, +} + +func (v RpkiValidationResultType) Validate() error { + if _, ok := RpkiValidationResultTypeToIntMap[v]; !ok { + return fmt.Errorf("invalid RpkiValidationResultType: %s", v) + } + return nil +} + +// struct for container gobgp:state. +type DynamicNeighborState struct { + // original -> gobgp:prefix + Prefix string `mapstructure:"prefix" json:"prefix,omitempty"` + // original -> gobgp:peer-group + PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"` +} + +// struct for container gobgp:config. +type DynamicNeighborConfig struct { + // original -> gobgp:prefix + Prefix string `mapstructure:"prefix" json:"prefix,omitempty"` + // original -> gobgp:peer-group + PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"` +} + +func (lhs *DynamicNeighborConfig) Equal(rhs *DynamicNeighborConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Prefix != rhs.Prefix { + return false + } + if lhs.PeerGroup != rhs.PeerGroup { + return false + } + return true +} + +// struct for container gobgp:dynamic-neighbor. +type DynamicNeighbor struct { + // original -> gobgp:prefix + // original -> gobgp:dynamic-neighbor-config + Config DynamicNeighborConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:dynamic-neighbor-state + State DynamicNeighborState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *DynamicNeighbor) Equal(rhs *DynamicNeighbor) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +type CollectorState struct { + // original -> gobgp:url + Url string `mapstructure:"url" json:"url,omitempty"` + // original -> gobgp:db-name + DbName string `mapstructure:"db-name" json:"db-name,omitempty"` + // original -> gobgp:table-dump-interval + TableDumpInterval uint64 `mapstructure:"table-dump-interval" json:"table-dump-interval,omitempty"` +} + +// struct for container gobgp:config. +type CollectorConfig struct { + // original -> gobgp:url + Url string `mapstructure:"url" json:"url,omitempty"` + // original -> gobgp:db-name + DbName string `mapstructure:"db-name" json:"db-name,omitempty"` + // original -> gobgp:table-dump-interval + TableDumpInterval uint64 `mapstructure:"table-dump-interval" json:"table-dump-interval,omitempty"` +} + +func (lhs *CollectorConfig) Equal(rhs *CollectorConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Url != rhs.Url { + return false + } + if lhs.DbName != rhs.DbName { + return false + } + if lhs.TableDumpInterval != rhs.TableDumpInterval { + return false + } + return true +} + +// struct for container gobgp:collector. +type Collector struct { + // original -> gobgp:collector-config + Config CollectorConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:collector-state + State CollectorState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Collector) Equal(rhs *Collector) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +type ZebraState struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + // Configure enabling to connect to zebra. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:url + // Configure url for zebra. + Url string `mapstructure:"url" json:"url,omitempty"` + // original -> gobgp:redistribute-route-type + RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"` + // original -> gobgp:version + // Configure version of zebra protocol. Default is 2. Supported up to 3. + Version uint8 `mapstructure:"version" json:"version,omitempty"` + // original -> gobgp:nexthop-trigger-enable + // gobgp:nexthop-trigger-enable's original type is boolean. + NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"` + // original -> gobgp:nexthop-trigger-delay + NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"` +} + +// struct for container gobgp:config. +type ZebraConfig struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + // Configure enabling to connect to zebra. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:url + // Configure url for zebra. + Url string `mapstructure:"url" json:"url,omitempty"` + // original -> gobgp:redistribute-route-type + RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"` + // original -> gobgp:version + // Configure version of zebra protocol. Default is 2. Supported up to 3. + Version uint8 `mapstructure:"version" json:"version,omitempty"` + // original -> gobgp:nexthop-trigger-enable + // gobgp:nexthop-trigger-enable's original type is boolean. + NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"` + // original -> gobgp:nexthop-trigger-delay + NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"` +} + +func (lhs *ZebraConfig) Equal(rhs *ZebraConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.Url != rhs.Url { + return false + } + if len(lhs.RedistributeRouteTypeList) != len(rhs.RedistributeRouteTypeList) { + return false + } + for idx, l := range lhs.RedistributeRouteTypeList { + if l != rhs.RedistributeRouteTypeList[idx] { + return false + } + } + if lhs.Version != rhs.Version { + return false + } + if lhs.NexthopTriggerEnable != rhs.NexthopTriggerEnable { + return false + } + if lhs.NexthopTriggerDelay != rhs.NexthopTriggerDelay { + return false + } + return true +} + +// struct for container gobgp:zebra. +type Zebra struct { + // original -> gobgp:zebra-config + Config ZebraConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:zebra-state + State ZebraState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Zebra) Equal(rhs *Zebra) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:config. +type MrtConfig struct { + // original -> gobgp:dump-type + DumpType MrtType `mapstructure:"dump-type" json:"dump-type,omitempty"` + // original -> gobgp:file-name + // Configures a file name to be written. + FileName string `mapstructure:"file-name" json:"file-name,omitempty"` + // original -> gobgp:table-name + // specify the table name with route server setup. + TableName string `mapstructure:"table-name" json:"table-name,omitempty"` + // original -> gobgp:dump-interval + DumpInterval uint64 `mapstructure:"dump-interval" json:"dump-interval,omitempty"` + // original -> gobgp:rotation-interval + RotationInterval uint64 `mapstructure:"rotation-interval" json:"rotation-interval,omitempty"` +} + +func (lhs *MrtConfig) Equal(rhs *MrtConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.DumpType != rhs.DumpType { + return false + } + if lhs.FileName != rhs.FileName { + return false + } + if lhs.TableName != rhs.TableName { + return false + } + if lhs.DumpInterval != rhs.DumpInterval { + return false + } + if lhs.RotationInterval != rhs.RotationInterval { + return false + } + return true +} + +// struct for container gobgp:mrt. +type Mrt struct { + // original -> gobgp:file-name + // original -> gobgp:mrt-config + Config MrtConfig `mapstructure:"config" json:"config,omitempty"` +} + +func (lhs *Mrt) Equal(rhs *Mrt) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +// Configured states of VRF. +type VrfState struct { + // original -> gobgp:name + // Unique name among all VRF instances. + Name string `mapstructure:"name" json:"name,omitempty"` + // original -> gobgp:id + // Unique identifier among all VRF instances. + Id uint32 `mapstructure:"id" json:"id,omitempty"` + // original -> gobgp:rd + // Route Distinguisher for this VRF. + Rd string `mapstructure:"rd" json:"rd,omitempty"` + // original -> gobgp:import-rt + // List of import Route Targets for this VRF. + ImportRtList []string `mapstructure:"import-rt-list" json:"import-rt-list,omitempty"` + // original -> gobgp:export-rt + // List of export Route Targets for this VRF. + ExportRtList []string `mapstructure:"export-rt-list" json:"export-rt-list,omitempty"` +} + +// struct for container gobgp:config. +// Configuration parameters for VRF. +type VrfConfig struct { + // original -> gobgp:name + // Unique name among all VRF instances. + Name string `mapstructure:"name" json:"name,omitempty"` + // original -> gobgp:id + // Unique identifier among all VRF instances. + Id uint32 `mapstructure:"id" json:"id,omitempty"` + // original -> gobgp:rd + // Route Distinguisher for this VRF. + Rd string `mapstructure:"rd" json:"rd,omitempty"` + // original -> gobgp:import-rt + // List of import Route Targets for this VRF. + ImportRtList []string `mapstructure:"import-rt-list" json:"import-rt-list,omitempty"` + // original -> gobgp:export-rt + // List of export Route Targets for this VRF. + ExportRtList []string `mapstructure:"export-rt-list" json:"export-rt-list,omitempty"` + // original -> gobgp:both-rt + // List of both import and export Route Targets for this VRF. Each + // configuration for import and export Route Targets will be preferred. + BothRtList []string `mapstructure:"both-rt-list" json:"both-rt-list,omitempty"` +} + +func (lhs *VrfConfig) Equal(rhs *VrfConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Name != rhs.Name { + return false + } + if lhs.Id != rhs.Id { + return false + } + if lhs.Rd != rhs.Rd { + return false + } + if len(lhs.ImportRtList) != len(rhs.ImportRtList) { + return false + } + for idx, l := range lhs.ImportRtList { + if l != rhs.ImportRtList[idx] { + return false + } + } + if len(lhs.ExportRtList) != len(rhs.ExportRtList) { + return false + } + for idx, l := range lhs.ExportRtList { + if l != rhs.ExportRtList[idx] { + return false + } + } + if len(lhs.BothRtList) != len(rhs.BothRtList) { + return false + } + for idx, l := range lhs.BothRtList { + if l != rhs.BothRtList[idx] { + return false + } + } + return true +} + +// struct for container gobgp:vrf. +// VRF instance configurations on the local system. +type Vrf struct { + // original -> gobgp:name + // original -> gobgp:vrf-config + // Configuration parameters for VRF. + Config VrfConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:vrf-state + // Configured states of VRF. + State VrfState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Vrf) Equal(rhs *Vrf) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +// Configuration parameters relating to BMP server. +type BmpServerState struct { + // original -> gobgp:address + // gobgp:address's original type is inet:ip-address. + // Reference to the address of the BMP server used as + // a key in the BMP server list. + Address string `mapstructure:"address" json:"address,omitempty"` + // original -> gobgp:port + // Reference to the port of the BMP server. + Port uint32 `mapstructure:"port" json:"port,omitempty"` + // original -> gobgp:route-monitoring-policy + RouteMonitoringPolicy BmpRouteMonitoringPolicyType `mapstructure:"route-monitoring-policy" json:"route-monitoring-policy,omitempty"` + // original -> gobgp:statistics-timeout + // Interval seconds of statistics messages sent to BMP server. + StatisticsTimeout uint16 `mapstructure:"statistics-timeout" json:"statistics-timeout,omitempty"` + // original -> gobgp:route-mirroring-enabled + // gobgp:route-mirroring-enabled's original type is boolean. + // Enable feature for mirroring of received BGP messages + // mainly for debugging purpose. + RouteMirroringEnabled bool `mapstructure:"route-mirroring-enabled" json:"route-mirroring-enabled,omitempty"` +} + +// struct for container gobgp:config. +// Configuration parameters relating to BMP server. +type BmpServerConfig struct { + // original -> gobgp:address + // gobgp:address's original type is inet:ip-address. + // Reference to the address of the BMP server used as + // a key in the BMP server list. + Address string `mapstructure:"address" json:"address,omitempty"` + // original -> gobgp:port + // Reference to the port of the BMP server. + Port uint32 `mapstructure:"port" json:"port,omitempty"` + // original -> gobgp:route-monitoring-policy + RouteMonitoringPolicy BmpRouteMonitoringPolicyType `mapstructure:"route-monitoring-policy" json:"route-monitoring-policy,omitempty"` + // original -> gobgp:statistics-timeout + // Interval seconds of statistics messages sent to BMP server. + StatisticsTimeout uint16 `mapstructure:"statistics-timeout" json:"statistics-timeout,omitempty"` + // original -> gobgp:route-mirroring-enabled + // gobgp:route-mirroring-enabled's original type is boolean. + // Enable feature for mirroring of received BGP messages + // mainly for debugging purpose. + RouteMirroringEnabled bool `mapstructure:"route-mirroring-enabled" json:"route-mirroring-enabled,omitempty"` +} + +func (lhs *BmpServerConfig) Equal(rhs *BmpServerConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Address != rhs.Address { + return false + } + if lhs.Port != rhs.Port { + return false + } + if lhs.RouteMonitoringPolicy != rhs.RouteMonitoringPolicy { + return false + } + if lhs.StatisticsTimeout != rhs.StatisticsTimeout { + return false + } + if lhs.RouteMirroringEnabled != rhs.RouteMirroringEnabled { + return false + } + return true +} + +// struct for container gobgp:bmp-server. +// List of BMP servers configured on the local system. +type BmpServer struct { + // original -> gobgp:address + // original -> gobgp:bmp-server-config + // Configuration parameters relating to BMP server. + Config BmpServerConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:bmp-server-state + // Configuration parameters relating to BMP server. + State BmpServerState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *BmpServer) Equal(rhs *BmpServer) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:rpki-received. +// Counters for reception RPKI Message types. +type RpkiReceived struct { + // original -> gobgp:serial-notify + // Number of serial notify message received from RPKI server. + SerialNotify int64 `mapstructure:"serial-notify" json:"serial-notify,omitempty"` + // original -> gobgp:cache-reset + // Number of cache reset message received from RPKI server. + CacheReset int64 `mapstructure:"cache-reset" json:"cache-reset,omitempty"` + // original -> gobgp:cache-response + // Number of cache response message received from RPKI server. + CacheResponse int64 `mapstructure:"cache-response" json:"cache-response,omitempty"` + // original -> gobgp:ipv4-prefix + // Number of ipv4 prefix message received from RPKI server. + Ipv4Prefix int64 `mapstructure:"ipv4-prefix" json:"ipv4-prefix,omitempty"` + // original -> gobgp:ipv6-prefix + // Number of ipv6 prefix message received from RPKI server. + Ipv6Prefix int64 `mapstructure:"ipv6-prefix" json:"ipv6-prefix,omitempty"` + // original -> gobgp:end-of-data + // Number of end of data message received from RPKI server. + EndOfData int64 `mapstructure:"end-of-data" json:"end-of-data,omitempty"` + // original -> gobgp:error + // Number of error message received from RPKI server. + Error int64 `mapstructure:"error" json:"error,omitempty"` +} + +func (lhs *RpkiReceived) Equal(rhs *RpkiReceived) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.SerialNotify != rhs.SerialNotify { + return false + } + if lhs.CacheReset != rhs.CacheReset { + return false + } + if lhs.CacheResponse != rhs.CacheResponse { + return false + } + if lhs.Ipv4Prefix != rhs.Ipv4Prefix { + return false + } + if lhs.Ipv6Prefix != rhs.Ipv6Prefix { + return false + } + if lhs.EndOfData != rhs.EndOfData { + return false + } + if lhs.Error != rhs.Error { + return false + } + return true +} + +// struct for container gobgp:rpki-sent. +// Counters for transmission RPKI Message types. +type RpkiSent struct { + // original -> gobgp:serial-query + // Number of serial query message sent to RPKI server. + SerialQuery int64 `mapstructure:"serial-query" json:"serial-query,omitempty"` + // original -> gobgp:reset-query + // Number of reset query message sent to RPKI server. + ResetQuery int64 `mapstructure:"reset-query" json:"reset-query,omitempty"` + // original -> gobgp:error + // Number of error message sent to RPKI server. + Error int64 `mapstructure:"error" json:"error,omitempty"` +} + +func (lhs *RpkiSent) Equal(rhs *RpkiSent) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.SerialQuery != rhs.SerialQuery { + return false + } + if lhs.ResetQuery != rhs.ResetQuery { + return false + } + if lhs.Error != rhs.Error { + return false + } + return true +} + +// struct for container gobgp:rpki-messages. +// Counters for transmission and reception RPKI Message types. +type RpkiMessages struct { + // original -> gobgp:rpki-sent + // Counters for transmission RPKI Message types. + RpkiSent RpkiSent `mapstructure:"rpki-sent" json:"rpki-sent,omitempty"` + // original -> gobgp:rpki-received + // Counters for reception RPKI Message types. + RpkiReceived RpkiReceived `mapstructure:"rpki-received" json:"rpki-received,omitempty"` +} + +func (lhs *RpkiMessages) Equal(rhs *RpkiMessages) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.RpkiSent.Equal(&(rhs.RpkiSent)) { + return false + } + if !lhs.RpkiReceived.Equal(&(rhs.RpkiReceived)) { + return false + } + return true +} + +// struct for container gobgp:state. +// State information relating to RPKI server. +type RpkiServerState struct { + // original -> gobgp:up + // gobgp:up's original type is boolean. + Up bool `mapstructure:"up" json:"up,omitempty"` + // original -> gobgp:serial-number + SerialNumber uint32 `mapstructure:"serial-number" json:"serial-number,omitempty"` + // original -> gobgp:records-v4 + RecordsV4 uint32 `mapstructure:"records-v4" json:"records-v4,omitempty"` + // original -> gobgp:records-v6 + RecordsV6 uint32 `mapstructure:"records-v6" json:"records-v6,omitempty"` + // original -> gobgp:prefixes-v4 + PrefixesV4 uint32 `mapstructure:"prefixes-v4" json:"prefixes-v4,omitempty"` + // original -> gobgp:prefixes-v6 + PrefixesV6 uint32 `mapstructure:"prefixes-v6" json:"prefixes-v6,omitempty"` + // original -> gobgp:uptime + // This timer determines the amount of time since the + // RPKI last transitioned in of the Established state. + Uptime int64 `mapstructure:"uptime" json:"uptime,omitempty"` + // original -> gobgp:downtime + // This timer determines the amount of time since the + // RPKI last transitioned out of the Established state. + Downtime int64 `mapstructure:"downtime" json:"downtime,omitempty"` + // original -> gobgp:last-pdu-recv-time + // last time the received an pdu message from RPKI server. + LastPduRecvTime int64 `mapstructure:"last-pdu-recv-time" json:"last-pdu-recv-time,omitempty"` + // original -> gobgp:rpki-messages + // Counters for transmission and reception RPKI Message types. + RpkiMessages RpkiMessages `mapstructure:"rpki-messages" json:"rpki-messages,omitempty"` +} + +// struct for container gobgp:config. +// Configuration parameters relating to RPKI server. +type RpkiServerConfig struct { + // original -> gobgp:address + // gobgp:address's original type is inet:ip-address. + // Reference to the address of the RPKI server used as + // a key in the RPKI server list. + Address string `mapstructure:"address" json:"address,omitempty"` + // original -> gobgp:port + // Reference to the port of the RPKI server. + Port uint32 `mapstructure:"port" json:"port,omitempty"` + // original -> gobgp:refresh-time + // Check interval for a configured RPKI server. + RefreshTime int64 `mapstructure:"refresh-time" json:"refresh-time,omitempty"` + // original -> gobgp:hold-time + // Specify the length of time in seconds that the session between + // the router and RPKI server is to be considered operational + // without any activity. + HoldTime int64 `mapstructure:"hold-time" json:"hold-time,omitempty"` + // original -> gobgp:record-lifetime + // Indicate the expiration date of the route validation recode + // received from RPKI server. + RecordLifetime int64 `mapstructure:"record-lifetime" json:"record-lifetime,omitempty"` + // original -> gobgp:preference + // RPKI server has a static preference. + // Higher the preference values indicates a higher priority RPKI server. + Preference uint8 `mapstructure:"preference" json:"preference,omitempty"` +} + +func (lhs *RpkiServerConfig) Equal(rhs *RpkiServerConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Address != rhs.Address { + return false + } + if lhs.Port != rhs.Port { + return false + } + if lhs.RefreshTime != rhs.RefreshTime { + return false + } + if lhs.HoldTime != rhs.HoldTime { + return false + } + if lhs.RecordLifetime != rhs.RecordLifetime { + return false + } + if lhs.Preference != rhs.Preference { + return false + } + return true +} + +// struct for container gobgp:rpki-server. +// List of RPKI servers configured on the local system. +type RpkiServer struct { + // original -> gobgp:address + // original -> gobgp:rpki-server-config + // Configuration parameters relating to RPKI server. + Config RpkiServerConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:rpki-server-state + // State information relating to RPKI server. + State RpkiServerState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *RpkiServer) Equal(rhs *RpkiServer) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the BGP neighbor or group. +type PeerGroupState struct { + // original -> bgp:peer-as + // bgp:peer-as's original type is inet:as-number. + // AS number of the peer. + PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"` + // original -> bgp:local-as + // bgp:local-as's original type is inet:as-number. + // The local autonomous system number that is to be used + // when establishing sessions with the remote peer or peer + // group, if this differs from the global BGP router + // autonomous system number. + LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"` + // original -> bgp:peer-type + // Explicitly designate the peer or peer group as internal + // (iBGP) or external (eBGP). + PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"` + // original -> bgp:auth-password + // Configures an MD5 authentication password for use with + // neighboring devices. + AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"` + // original -> bgp:remove-private-as + // Remove private AS numbers from updates sent to peers. + RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"` + // original -> bgp:route-flap-damping + // bgp:route-flap-damping's original type is boolean. + // Enable route flap damping. + RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"` + // original -> bgp:send-community + // Specify which types of community should be sent to the + // neighbor or group. The default is to not send the + // community attribute. + SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"` + // original -> bgp:description + // An optional textual description (intended primarily for use + // with a peer or group. + Description string `mapstructure:"description" json:"description,omitempty"` + // original -> bgp:peer-group-name + // Name of the BGP peer-group. + PeerGroupName string `mapstructure:"peer-group-name" json:"peer-group-name,omitempty"` + // original -> bgp-op:total-paths + // Total number of BGP paths within the context. + TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"` + // original -> bgp-op:total-prefixes + // . + TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to the BGP neighbor or +// group. +type PeerGroupConfig struct { + // original -> bgp:peer-as + // bgp:peer-as's original type is inet:as-number. + // AS number of the peer. + PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"` + // original -> bgp:local-as + // bgp:local-as's original type is inet:as-number. + // The local autonomous system number that is to be used + // when establishing sessions with the remote peer or peer + // group, if this differs from the global BGP router + // autonomous system number. + LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"` + // original -> bgp:peer-type + // Explicitly designate the peer or peer group as internal + // (iBGP) or external (eBGP). + PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"` + // original -> bgp:auth-password + // Configures an MD5 authentication password for use with + // neighboring devices. + AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"` + // original -> bgp:remove-private-as + // Remove private AS numbers from updates sent to peers. + RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"` + // original -> bgp:route-flap-damping + // bgp:route-flap-damping's original type is boolean. + // Enable route flap damping. + RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"` + // original -> bgp:send-community + // Specify which types of community should be sent to the + // neighbor or group. The default is to not send the + // community attribute. + SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"` + // original -> bgp:description + // An optional textual description (intended primarily for use + // with a peer or group. + Description string `mapstructure:"description" json:"description,omitempty"` + // original -> bgp:peer-group-name + // Name of the BGP peer-group. + PeerGroupName string `mapstructure:"peer-group-name" json:"peer-group-name,omitempty"` +} + +func (lhs *PeerGroupConfig) Equal(rhs *PeerGroupConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.PeerAs != rhs.PeerAs { + return false + } + if lhs.LocalAs != rhs.LocalAs { + return false + } + if lhs.PeerType != rhs.PeerType { + return false + } + if lhs.AuthPassword != rhs.AuthPassword { + return false + } + if lhs.RemovePrivateAs != rhs.RemovePrivateAs { + return false + } + if lhs.RouteFlapDamping != rhs.RouteFlapDamping { + return false + } + if lhs.SendCommunity != rhs.SendCommunity { + return false + } + if lhs.Description != rhs.Description { + return false + } + if lhs.PeerGroupName != rhs.PeerGroupName { + return false + } + return true +} + +// struct for container bgp:peer-group. +// List of BGP peer-groups configured on the local system - +// uniquely identified by peer-group name. +type PeerGroup struct { + // original -> bgp:peer-group-name + // original -> bgp:peer-group-config + // Configuration parameters relating to the BGP neighbor or + // group. + Config PeerGroupConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:peer-group-state + // State information relating to the BGP neighbor or group. + State PeerGroupState `mapstructure:"state" json:"state,omitempty"` + // original -> bgp:timers + // Timers related to a BGP neighbor or group. + Timers Timers `mapstructure:"timers" json:"timers,omitempty"` + // original -> bgp:transport + // Transport session parameters for the BGP neighbor or group. + Transport Transport `mapstructure:"transport" json:"transport,omitempty"` + // original -> bgp:error-handling + // Error handling parameters used for the BGP neighbor or + // group. + ErrorHandling ErrorHandling `mapstructure:"error-handling" json:"error-handling,omitempty"` + // original -> bgp:logging-options + // Logging options for events related to the BGP neighbor or + // group. + LoggingOptions LoggingOptions `mapstructure:"logging-options" json:"logging-options,omitempty"` + // original -> bgp:ebgp-multihop + // eBGP multi-hop parameters for the BGP neighbor or group. + EbgpMultihop EbgpMultihop `mapstructure:"ebgp-multihop" json:"ebgp-multihop,omitempty"` + // original -> bgp:route-reflector + // Route reflector parameters for the BGP neighbor or group. + RouteReflector RouteReflector `mapstructure:"route-reflector" json:"route-reflector,omitempty"` + // original -> bgp:as-path-options + // AS_PATH manipulation parameters for the BGP neighbor or + // group. + AsPathOptions AsPathOptions `mapstructure:"as-path-options" json:"as-path-options,omitempty"` + // original -> bgp:add-paths + // Parameters relating to the advertisement and receipt of + // multiple paths for a single NLRI (add-paths). + AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"` + // original -> bgp:afi-safis + // Per-address-family configuration parameters associated with + // the neighbor or group. + AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"` + // original -> bgp:graceful-restart + // Parameters relating the graceful restart mechanism for BGP. + GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"` + // original -> rpol:apply-policy + // Anchor point for routing policies in the model. + // Import and export policies are with respect to the local + // routing table, i.e., export (send) and import (receive), + // depending on the context. + ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"` + // original -> bgp-mp:use-multiple-paths + // Parameters related to the use of multiple paths for the + // same NLRI. + UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"` + // original -> gobgp:route-server + // Configure the local router as a route server. + RouteServer RouteServer `mapstructure:"route-server" json:"route-server,omitempty"` + // original -> gobgp:ttl-security + // Configure TTL Security feature. + TtlSecurity TtlSecurity `mapstructure:"ttl-security" json:"ttl-security,omitempty"` +} + +func (lhs *PeerGroup) Equal(rhs *PeerGroup) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + if !lhs.Timers.Equal(&(rhs.Timers)) { + return false + } + if !lhs.Transport.Equal(&(rhs.Transport)) { + return false + } + if !lhs.ErrorHandling.Equal(&(rhs.ErrorHandling)) { + return false + } + if !lhs.LoggingOptions.Equal(&(rhs.LoggingOptions)) { + return false + } + if !lhs.EbgpMultihop.Equal(&(rhs.EbgpMultihop)) { + return false + } + if !lhs.RouteReflector.Equal(&(rhs.RouteReflector)) { + return false + } + if !lhs.AsPathOptions.Equal(&(rhs.AsPathOptions)) { + return false + } + if !lhs.AddPaths.Equal(&(rhs.AddPaths)) { + return false + } + if len(lhs.AfiSafis) != len(rhs.AfiSafis) { + return false + } + { + lmap := make(map[string]*AfiSafi) + for i, l := range lhs.AfiSafis { + lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i] + } + for i, r := range rhs.AfiSafis { + if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) { + return false + } + if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) { + return false + } + if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) { + return false + } + if !lhs.RouteServer.Equal(&(rhs.RouteServer)) { + return false + } + if !lhs.TtlSecurity.Equal(&(rhs.TtlSecurity)) { + return false + } + return true +} + +// struct for container gobgp:state. +// State information for TTL Security. +type TtlSecurityState struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + // Enable features for TTL Security. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:ttl-min + // Reference to the port of the BMP server. + TtlMin uint8 `mapstructure:"ttl-min" json:"ttl-min,omitempty"` +} + +// struct for container gobgp:config. +// Configuration parameters for TTL Security. +type TtlSecurityConfig struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + // Enable features for TTL Security. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:ttl-min + // Reference to the port of the BMP server. + TtlMin uint8 `mapstructure:"ttl-min" json:"ttl-min,omitempty"` +} + +func (lhs *TtlSecurityConfig) Equal(rhs *TtlSecurityConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.TtlMin != rhs.TtlMin { + return false + } + return true +} + +// struct for container gobgp:ttl-security. +// Configure TTL Security feature. +type TtlSecurity struct { + // original -> gobgp:ttl-security-config + // Configuration parameters for TTL Security. + Config TtlSecurityConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:ttl-security-state + // State information for TTL Security. + State TtlSecurityState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *TtlSecurity) Equal(rhs *TtlSecurity) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +// State information relating to route server +// client(s) used for the BGP neighbor. +type RouteServerState struct { + // original -> gobgp:route-server-client + // gobgp:route-server-client's original type is boolean. + // Configure the neighbor as a route server client. + RouteServerClient bool `mapstructure:"route-server-client" json:"route-server-client,omitempty"` +} + +// struct for container gobgp:config. +// Configuration parameters relating to route server +// client(s) used for the BGP neighbor. +type RouteServerConfig struct { + // original -> gobgp:route-server-client + // gobgp:route-server-client's original type is boolean. + // Configure the neighbor as a route server client. + RouteServerClient bool `mapstructure:"route-server-client" json:"route-server-client,omitempty"` +} + +func (lhs *RouteServerConfig) Equal(rhs *RouteServerConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.RouteServerClient != rhs.RouteServerClient { + return false + } + return true +} + +// struct for container gobgp:route-server. +// Configure the local router as a route server. +type RouteServer struct { + // original -> gobgp:route-server-config + // Configuration parameters relating to route server + // client(s) used for the BGP neighbor. + Config RouteServerConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:route-server-state + // State information relating to route server + // client(s) used for the BGP neighbor. + State RouteServerState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *RouteServer) Equal(rhs *RouteServer) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-op:prefixes. +// Prefix counters for the BGP session. +type Prefixes struct { + // original -> bgp-op:received + // The number of prefixes received from the neighbor. + Received uint32 `mapstructure:"received" json:"received,omitempty"` + // original -> bgp-op:sent + // The number of prefixes advertised to the neighbor. + Sent uint32 `mapstructure:"sent" json:"sent,omitempty"` + // original -> bgp-op:installed + // The number of advertised prefixes installed in the + // Loc-RIB. + Installed uint32 `mapstructure:"installed" json:"installed,omitempty"` +} + +func (lhs *Prefixes) Equal(rhs *Prefixes) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Received != rhs.Received { + return false + } + if lhs.Sent != rhs.Sent { + return false + } + if lhs.Installed != rhs.Installed { + return false + } + return true +} + +// struct for container bgp:state. +// State information associated with ADD_PATHS. +type AddPathsState struct { + // original -> bgp:receive + // bgp:receive's original type is boolean. + // Enable ability to receive multiple path advertisements + // for an NLRI from the neighbor or group. + Receive bool `mapstructure:"receive" json:"receive,omitempty"` + // original -> bgp:send-max + // The maximum number of paths to advertise to neighbors + // for a single NLRI. + SendMax uint8 `mapstructure:"send-max" json:"send-max,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to ADD_PATHS. +type AddPathsConfig struct { + // original -> bgp:receive + // bgp:receive's original type is boolean. + // Enable ability to receive multiple path advertisements + // for an NLRI from the neighbor or group. + Receive bool `mapstructure:"receive" json:"receive,omitempty"` + // original -> bgp:send-max + // The maximum number of paths to advertise to neighbors + // for a single NLRI. + SendMax uint8 `mapstructure:"send-max" json:"send-max,omitempty"` +} + +func (lhs *AddPathsConfig) Equal(rhs *AddPathsConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Receive != rhs.Receive { + return false + } + if lhs.SendMax != rhs.SendMax { + return false + } + return true +} + +// struct for container bgp:add-paths. +// Parameters relating to the advertisement and receipt of +// multiple paths for a single NLRI (add-paths). +type AddPaths struct { + // original -> bgp:add-paths-config + // Configuration parameters relating to ADD_PATHS. + Config AddPathsConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:add-paths-state + // State information associated with ADD_PATHS. + State AddPathsState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *AddPaths) Equal(rhs *AddPaths) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the AS_PATH manipulation +// mechanisms for the BGP peer or group. +type AsPathOptionsState struct { + // original -> bgp:allow-own-as + // Specify the number of occurrences of the local BGP speaker's + // AS that can occur within the AS_PATH before it is rejected. + AllowOwnAs uint8 `mapstructure:"allow-own-as" json:"allow-own-as,omitempty"` + // original -> bgp:replace-peer-as + // bgp:replace-peer-as's original type is boolean. + // Replace occurrences of the peer's AS in the AS_PATH + // with the local autonomous system number. + ReplacePeerAs bool `mapstructure:"replace-peer-as" json:"replace-peer-as,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to AS_PATH manipulation +// for the BGP peer or group. +type AsPathOptionsConfig struct { + // original -> bgp:allow-own-as + // Specify the number of occurrences of the local BGP speaker's + // AS that can occur within the AS_PATH before it is rejected. + AllowOwnAs uint8 `mapstructure:"allow-own-as" json:"allow-own-as,omitempty"` + // original -> bgp:replace-peer-as + // bgp:replace-peer-as's original type is boolean. + // Replace occurrences of the peer's AS in the AS_PATH + // with the local autonomous system number. + ReplacePeerAs bool `mapstructure:"replace-peer-as" json:"replace-peer-as,omitempty"` +} + +func (lhs *AsPathOptionsConfig) Equal(rhs *AsPathOptionsConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AllowOwnAs != rhs.AllowOwnAs { + return false + } + if lhs.ReplacePeerAs != rhs.ReplacePeerAs { + return false + } + return true +} + +// struct for container bgp:as-path-options. +// AS_PATH manipulation parameters for the BGP neighbor or +// group. +type AsPathOptions struct { + // original -> bgp:as-path-options-config + // Configuration parameters relating to AS_PATH manipulation + // for the BGP peer or group. + Config AsPathOptionsConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:as-path-options-state + // State information relating to the AS_PATH manipulation + // mechanisms for the BGP peer or group. + State AsPathOptionsState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *AsPathOptions) Equal(rhs *AsPathOptions) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to route reflection for the +// BGP neighbor or group. +type RouteReflectorState struct { + // original -> bgp:route-reflector-cluster-id + // route-reflector cluster id to use when local router is + // configured as a route reflector. Commonly set at the group + // level, but allows a different cluster + // id to be set for each neighbor. + RouteReflectorClusterId RrClusterIdType `mapstructure:"route-reflector-cluster-id" json:"route-reflector-cluster-id,omitempty"` + // original -> bgp:route-reflector-client + // bgp:route-reflector-client's original type is boolean. + // Configure the neighbor as a route reflector client. + RouteReflectorClient bool `mapstructure:"route-reflector-client" json:"route-reflector-client,omitempty"` +} + +// struct for container bgp:config. +// Configuraton parameters relating to route reflection +// for the BGP neighbor or group. +type RouteReflectorConfig struct { + // original -> bgp:route-reflector-cluster-id + // route-reflector cluster id to use when local router is + // configured as a route reflector. Commonly set at the group + // level, but allows a different cluster + // id to be set for each neighbor. + RouteReflectorClusterId RrClusterIdType `mapstructure:"route-reflector-cluster-id" json:"route-reflector-cluster-id,omitempty"` + // original -> bgp:route-reflector-client + // bgp:route-reflector-client's original type is boolean. + // Configure the neighbor as a route reflector client. + RouteReflectorClient bool `mapstructure:"route-reflector-client" json:"route-reflector-client,omitempty"` +} + +func (lhs *RouteReflectorConfig) Equal(rhs *RouteReflectorConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.RouteReflectorClusterId != rhs.RouteReflectorClusterId { + return false + } + if lhs.RouteReflectorClient != rhs.RouteReflectorClient { + return false + } + return true +} + +// struct for container bgp:route-reflector. +// Route reflector parameters for the BGP neighbor or group. +type RouteReflector struct { + // original -> bgp:route-reflector-config + // Configuraton parameters relating to route reflection + // for the BGP neighbor or group. + Config RouteReflectorConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:route-reflector-state + // State information relating to route reflection for the + // BGP neighbor or group. + State RouteReflectorState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *RouteReflector) Equal(rhs *RouteReflector) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information for eBGP multihop, for the BGP neighbor +// or group. +type EbgpMultihopState struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // When enabled the referenced group or neighbors are permitted + // to be indirectly connected - including cases where the TTL + // can be decremented between the BGP peers. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:multihop-ttl + // Time-to-live value to use when packets are sent to the + // referenced group or neighbors and ebgp-multihop is enabled. + MultihopTtl uint8 `mapstructure:"multihop-ttl" json:"multihop-ttl,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to eBGP multihop for the +// BGP neighbor or group. +type EbgpMultihopConfig struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // When enabled the referenced group or neighbors are permitted + // to be indirectly connected - including cases where the TTL + // can be decremented between the BGP peers. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:multihop-ttl + // Time-to-live value to use when packets are sent to the + // referenced group or neighbors and ebgp-multihop is enabled. + MultihopTtl uint8 `mapstructure:"multihop-ttl" json:"multihop-ttl,omitempty"` +} + +func (lhs *EbgpMultihopConfig) Equal(rhs *EbgpMultihopConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.MultihopTtl != rhs.MultihopTtl { + return false + } + return true +} + +// struct for container bgp:ebgp-multihop. +// eBGP multi-hop parameters for the BGP neighbor or group. +type EbgpMultihop struct { + // original -> bgp:ebgp-multihop-config + // Configuration parameters relating to eBGP multihop for the + // BGP neighbor or group. + Config EbgpMultihopConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:ebgp-multihop-state + // State information for eBGP multihop, for the BGP neighbor + // or group. + State EbgpMultihopState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *EbgpMultihop) Equal(rhs *EbgpMultihop) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to logging for the BGP neighbor +// or group. +type LoggingOptionsState struct { + // original -> bgp:log-neighbor-state-changes + // bgp:log-neighbor-state-changes's original type is boolean. + // Configure logging of peer state changes. Default is + // to enable logging of peer state changes. + LogNeighborStateChanges bool `mapstructure:"log-neighbor-state-changes" json:"log-neighbor-state-changes,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters enabling or modifying logging +// for events relating to the BGP neighbor or group. +type LoggingOptionsConfig struct { + // original -> bgp:log-neighbor-state-changes + // bgp:log-neighbor-state-changes's original type is boolean. + // Configure logging of peer state changes. Default is + // to enable logging of peer state changes. + LogNeighborStateChanges bool `mapstructure:"log-neighbor-state-changes" json:"log-neighbor-state-changes,omitempty"` +} + +func (lhs *LoggingOptionsConfig) Equal(rhs *LoggingOptionsConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.LogNeighborStateChanges != rhs.LogNeighborStateChanges { + return false + } + return true +} + +// struct for container bgp:logging-options. +// Logging options for events related to the BGP neighbor or +// group. +type LoggingOptions struct { + // original -> bgp:logging-options-config + // Configuration parameters enabling or modifying logging + // for events relating to the BGP neighbor or group. + Config LoggingOptionsConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:logging-options-state + // State information relating to logging for the BGP neighbor + // or group. + State LoggingOptionsState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *LoggingOptions) Equal(rhs *LoggingOptions) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to enhanced error handling +// mechanisms for the BGP neighbor or group. +type ErrorHandlingState struct { + // original -> bgp:treat-as-withdraw + // bgp:treat-as-withdraw's original type is boolean. + // Specify whether erroneous UPDATE messages for which the + // NLRI can be extracted are reated as though the NLRI is + // withdrawn - avoiding session reset. + TreatAsWithdraw bool `mapstructure:"treat-as-withdraw" json:"treat-as-withdraw,omitempty"` + // original -> bgp-op:erroneous-update-messages + // The number of BGP UPDATE messages for which the + // treat-as-withdraw mechanism has been applied based + // on erroneous message contents. + ErroneousUpdateMessages uint32 `mapstructure:"erroneous-update-messages" json:"erroneous-update-messages,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters enabling or modifying the +// behavior or enhanced error handling mechanisms for the BGP +// neighbor or group. +type ErrorHandlingConfig struct { + // original -> bgp:treat-as-withdraw + // bgp:treat-as-withdraw's original type is boolean. + // Specify whether erroneous UPDATE messages for which the + // NLRI can be extracted are reated as though the NLRI is + // withdrawn - avoiding session reset. + TreatAsWithdraw bool `mapstructure:"treat-as-withdraw" json:"treat-as-withdraw,omitempty"` +} + +func (lhs *ErrorHandlingConfig) Equal(rhs *ErrorHandlingConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.TreatAsWithdraw != rhs.TreatAsWithdraw { + return false + } + return true +} + +// struct for container bgp:error-handling. +// Error handling parameters used for the BGP neighbor or +// group. +type ErrorHandling struct { + // original -> bgp:error-handling-config + // Configuration parameters enabling or modifying the + // behavior or enhanced error handling mechanisms for the BGP + // neighbor or group. + Config ErrorHandlingConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:error-handling-state + // State information relating to enhanced error handling + // mechanisms for the BGP neighbor or group. + State ErrorHandlingState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *ErrorHandling) Equal(rhs *ErrorHandling) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the transport session(s) +// used for the BGP neighbor or group. +type TransportState struct { + // original -> bgp:tcp-mss + // Sets the max segment size for BGP TCP sessions. + TcpMss uint16 `mapstructure:"tcp-mss" json:"tcp-mss,omitempty"` + // original -> bgp:mtu-discovery + // bgp:mtu-discovery's original type is boolean. + // Turns path mtu discovery for BGP TCP sessions on (true) + // or off (false). + MtuDiscovery bool `mapstructure:"mtu-discovery" json:"mtu-discovery,omitempty"` + // original -> bgp:passive-mode + // bgp:passive-mode's original type is boolean. + // Wait for peers to issue requests to open a BGP session, + // rather than initiating sessions from the local router. + PassiveMode bool `mapstructure:"passive-mode" json:"passive-mode,omitempty"` + // original -> bgp:local-address + // bgp:local-address's original type is union. + // Set the local IP (either IPv4 or IPv6) address to use + // for the session when sending BGP update messages. This + // may be expressed as either an IP address or reference + // to the name of an interface. + LocalAddress string `mapstructure:"local-address" json:"local-address,omitempty"` + // original -> bgp-op:local-port + // bgp-op:local-port's original type is inet:port-number. + // Local TCP port being used for the TCP session supporting + // the BGP session. + LocalPort uint16 `mapstructure:"local-port" json:"local-port,omitempty"` + // original -> bgp-op:remote-address + // bgp-op:remote-address's original type is inet:ip-address. + // Remote address to which the BGP session has been + // established. + RemoteAddress string `mapstructure:"remote-address" json:"remote-address,omitempty"` + // original -> bgp-op:remote-port + // bgp-op:remote-port's original type is inet:port-number. + // Remote port being used by the peer for the TCP session + // supporting the BGP session. + RemotePort uint16 `mapstructure:"remote-port" json:"remote-port,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to the transport +// session(s) used for the BGP neighbor or group. +type TransportConfig struct { + // original -> bgp:tcp-mss + // Sets the max segment size for BGP TCP sessions. + TcpMss uint16 `mapstructure:"tcp-mss" json:"tcp-mss,omitempty"` + // original -> bgp:mtu-discovery + // bgp:mtu-discovery's original type is boolean. + // Turns path mtu discovery for BGP TCP sessions on (true) + // or off (false). + MtuDiscovery bool `mapstructure:"mtu-discovery" json:"mtu-discovery,omitempty"` + // original -> bgp:passive-mode + // bgp:passive-mode's original type is boolean. + // Wait for peers to issue requests to open a BGP session, + // rather than initiating sessions from the local router. + PassiveMode bool `mapstructure:"passive-mode" json:"passive-mode,omitempty"` + // original -> bgp:local-address + // bgp:local-address's original type is union. + // Set the local IP (either IPv4 or IPv6) address to use + // for the session when sending BGP update messages. This + // may be expressed as either an IP address or reference + // to the name of an interface. + LocalAddress string `mapstructure:"local-address" json:"local-address,omitempty"` + // original -> gobgp:remote-port + // gobgp:remote-port's original type is inet:port-number. + RemotePort uint16 `mapstructure:"remote-port" json:"remote-port,omitempty"` + // original -> gobgp:ttl + // TTL value for BGP packets. + Ttl uint8 `mapstructure:"ttl" json:"ttl,omitempty"` +} + +func (lhs *TransportConfig) Equal(rhs *TransportConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.TcpMss != rhs.TcpMss { + return false + } + if lhs.MtuDiscovery != rhs.MtuDiscovery { + return false + } + if lhs.PassiveMode != rhs.PassiveMode { + return false + } + if lhs.LocalAddress != rhs.LocalAddress { + return false + } + if lhs.RemotePort != rhs.RemotePort { + return false + } + if lhs.Ttl != rhs.Ttl { + return false + } + return true +} + +// struct for container bgp:transport. +// Transport session parameters for the BGP neighbor or group. +type Transport struct { + // original -> bgp:transport-config + // Configuration parameters relating to the transport + // session(s) used for the BGP neighbor or group. + Config TransportConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:transport-state + // State information relating to the transport session(s) + // used for the BGP neighbor or group. + State TransportState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Transport) Equal(rhs *Transport) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the timers used for the BGP +// neighbor or group. +type TimersState struct { + // original -> bgp:connect-retry + // bgp:connect-retry's original type is decimal64. + // Time interval in seconds between attempts to establish a + // session with the peer. + ConnectRetry float64 `mapstructure:"connect-retry" json:"connect-retry,omitempty"` + // original -> bgp:hold-time + // bgp:hold-time's original type is decimal64. + // Time interval in seconds that a BGP session will be + // considered active in the absence of keepalive or other + // messages from the peer. The hold-time is typically + // set to 3x the keepalive-interval. + HoldTime float64 `mapstructure:"hold-time" json:"hold-time,omitempty"` + // original -> bgp:keepalive-interval + // bgp:keepalive-interval's original type is decimal64. + // Time interval in seconds between transmission of keepalive + // messages to the neighbor. Typically set to 1/3 the + // hold-time. + KeepaliveInterval float64 `mapstructure:"keepalive-interval" json:"keepalive-interval,omitempty"` + // original -> bgp:minimum-advertisement-interval + // bgp:minimum-advertisement-interval's original type is decimal64. + // Minimum time which must elapse between subsequent UPDATE + // messages relating to a common set of NLRI being transmitted + // to a peer. This timer is referred to as + // MinRouteAdvertisementIntervalTimer by RFC 4721 and serves to + // reduce the number of UPDATE messages transmitted when a + // particular set of NLRI exhibit instability. + MinimumAdvertisementInterval float64 `mapstructure:"minimum-advertisement-interval" json:"minimum-advertisement-interval,omitempty"` + // original -> bgp-op:uptime + // bgp-op:uptime's original type is yang:timeticks. + // This timer determines the amount of time since the + // BGP last transitioned in or out of the Established + // state. + Uptime int64 `mapstructure:"uptime" json:"uptime,omitempty"` + // original -> bgp-op:negotiated-hold-time + // bgp-op:negotiated-hold-time's original type is decimal64. + // The negotiated hold-time for the BGP session. + NegotiatedHoldTime float64 `mapstructure:"negotiated-hold-time" json:"negotiated-hold-time,omitempty"` + // original -> gobgp:idle-hold-time-after-reset + // gobgp:idle-hold-time-after-reset's original type is decimal64. + // Time interval in seconds that a BGP session will be + // in idle state after neighbor reset operation. + IdleHoldTimeAfterReset float64 `mapstructure:"idle-hold-time-after-reset" json:"idle-hold-time-after-reset,omitempty"` + // original -> gobgp:downtime + // gobgp:downtime's original type is yang:timeticks. + // This timer determines the amount of time since the + // BGP last transitioned out of the Established state. + Downtime int64 `mapstructure:"downtime" json:"downtime,omitempty"` + // original -> gobgp:update-recv-time + // The number of seconds elasped since January 1, 1970 UTC + // last time the BGP session received an UPDATE message. + UpdateRecvTime int64 `mapstructure:"update-recv-time" json:"update-recv-time,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to timers used for the +// BGP neighbor or group. +type TimersConfig struct { + // original -> bgp:connect-retry + // bgp:connect-retry's original type is decimal64. + // Time interval in seconds between attempts to establish a + // session with the peer. + ConnectRetry float64 `mapstructure:"connect-retry" json:"connect-retry,omitempty"` + // original -> bgp:hold-time + // bgp:hold-time's original type is decimal64. + // Time interval in seconds that a BGP session will be + // considered active in the absence of keepalive or other + // messages from the peer. The hold-time is typically + // set to 3x the keepalive-interval. + HoldTime float64 `mapstructure:"hold-time" json:"hold-time,omitempty"` + // original -> bgp:keepalive-interval + // bgp:keepalive-interval's original type is decimal64. + // Time interval in seconds between transmission of keepalive + // messages to the neighbor. Typically set to 1/3 the + // hold-time. + KeepaliveInterval float64 `mapstructure:"keepalive-interval" json:"keepalive-interval,omitempty"` + // original -> bgp:minimum-advertisement-interval + // bgp:minimum-advertisement-interval's original type is decimal64. + // Minimum time which must elapse between subsequent UPDATE + // messages relating to a common set of NLRI being transmitted + // to a peer. This timer is referred to as + // MinRouteAdvertisementIntervalTimer by RFC 4721 and serves to + // reduce the number of UPDATE messages transmitted when a + // particular set of NLRI exhibit instability. + MinimumAdvertisementInterval float64 `mapstructure:"minimum-advertisement-interval" json:"minimum-advertisement-interval,omitempty"` + // original -> gobgp:idle-hold-time-after-reset + // gobgp:idle-hold-time-after-reset's original type is decimal64. + // Time interval in seconds that a BGP session will be + // in idle state after neighbor reset operation. + IdleHoldTimeAfterReset float64 `mapstructure:"idle-hold-time-after-reset" json:"idle-hold-time-after-reset,omitempty"` +} + +func (lhs *TimersConfig) Equal(rhs *TimersConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.ConnectRetry != rhs.ConnectRetry { + return false + } + if lhs.HoldTime != rhs.HoldTime { + return false + } + if lhs.KeepaliveInterval != rhs.KeepaliveInterval { + return false + } + if lhs.MinimumAdvertisementInterval != rhs.MinimumAdvertisementInterval { + return false + } + if lhs.IdleHoldTimeAfterReset != rhs.IdleHoldTimeAfterReset { + return false + } + return true +} + +// struct for container bgp:timers. +// Timers related to a BGP neighbor or group. +type Timers struct { + // original -> bgp:timers-config + // Configuration parameters relating to timers used for the + // BGP neighbor or group. + Config TimersConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:timers-state + // State information relating to the timers used for the BGP + // neighbor or group. + State TimersState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Timers) Equal(rhs *Timers) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:adj-table. +type AdjTable struct { + // original -> gobgp:ADVERTISED + Advertised uint32 `mapstructure:"advertised" json:"advertised,omitempty"` + // original -> gobgp:FILTERED + Filtered uint32 `mapstructure:"filtered" json:"filtered,omitempty"` + // original -> gobgp:RECEIVED + Received uint32 `mapstructure:"received" json:"received,omitempty"` + // original -> gobgp:ACCEPTED + Accepted uint32 `mapstructure:"accepted" json:"accepted,omitempty"` +} + +func (lhs *AdjTable) Equal(rhs *AdjTable) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Advertised != rhs.Advertised { + return false + } + if lhs.Filtered != rhs.Filtered { + return false + } + if lhs.Received != rhs.Received { + return false + } + if lhs.Accepted != rhs.Accepted { + return false + } + return true +} + +// struct for container bgp:queues. +// Counters related to queued messages associated with the +// BGP neighbor. +type Queues struct { + // original -> bgp-op:input + // The number of messages received from the peer currently + // queued. + Input uint32 `mapstructure:"input" json:"input,omitempty"` + // original -> bgp-op:output + // The number of messages queued to be sent to the peer. + Output uint32 `mapstructure:"output" json:"output,omitempty"` +} + +func (lhs *Queues) Equal(rhs *Queues) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Input != rhs.Input { + return false + } + if lhs.Output != rhs.Output { + return false + } + return true +} + +// struct for container bgp:received. +// Counters for BGP messages received from the neighbor. +type Received struct { + // original -> bgp-op:UPDATE + // Number of BGP UPDATE messages announcing, withdrawing + // or modifying paths exchanged. + Update uint64 `mapstructure:"update" json:"update,omitempty"` + // original -> bgp-op:NOTIFICATION + // Number of BGP NOTIFICATION messages indicating an + // error condition has occurred exchanged. + Notification uint64 `mapstructure:"notification" json:"notification,omitempty"` + // original -> gobgp:OPEN + // Number of BGP open messages announcing, withdrawing + // or modifying paths exchanged. + Open uint64 `mapstructure:"open" json:"open,omitempty"` + // original -> gobgp:REFRESH + // Number of BGP Route-Refresh messages indicating an + // error condition has occurred exchanged. + Refresh uint64 `mapstructure:"refresh" json:"refresh,omitempty"` + // original -> gobgp:KEEPALIVE + // Number of BGP Keepalive messages indicating an + // error condition has occurred exchanged. + Keepalive uint64 `mapstructure:"keepalive" json:"keepalive,omitempty"` + // original -> gobgp:DYNAMIC-CAP + // Number of BGP dynamic-cap messages indicating an + // error condition has occurred exchanged. + DynamicCap uint64 `mapstructure:"dynamic-cap" json:"dynamic-cap,omitempty"` + // original -> gobgp:WITHDRAW-UPDATE + // Number of updates subjected to treat-as-withdraw treatment. + WithdrawUpdate uint32 `mapstructure:"withdraw-update" json:"withdraw-update,omitempty"` + // original -> gobgp:WITHDRAW-PREFIX + // Number of prefixes subjected to treat-as-withdraw treatment. + WithdrawPrefix uint32 `mapstructure:"withdraw-prefix" json:"withdraw-prefix,omitempty"` + // original -> gobgp:DISCARDED + // Number of discarded messages indicating an + // error condition has occurred exchanged. + Discarded uint64 `mapstructure:"discarded" json:"discarded,omitempty"` + // original -> gobgp:TOTAL + // Number of total messages indicating an + // error condition has occurred exchanged. + Total uint64 `mapstructure:"total" json:"total,omitempty"` +} + +func (lhs *Received) Equal(rhs *Received) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Update != rhs.Update { + return false + } + if lhs.Notification != rhs.Notification { + return false + } + if lhs.Open != rhs.Open { + return false + } + if lhs.Refresh != rhs.Refresh { + return false + } + if lhs.Keepalive != rhs.Keepalive { + return false + } + if lhs.DynamicCap != rhs.DynamicCap { + return false + } + if lhs.WithdrawUpdate != rhs.WithdrawUpdate { + return false + } + if lhs.WithdrawPrefix != rhs.WithdrawPrefix { + return false + } + if lhs.Discarded != rhs.Discarded { + return false + } + if lhs.Total != rhs.Total { + return false + } + return true +} + +// struct for container bgp:sent. +// Counters relating to BGP messages sent to the neighbor. +type Sent struct { + // original -> bgp-op:UPDATE + // Number of BGP UPDATE messages announcing, withdrawing + // or modifying paths exchanged. + Update uint64 `mapstructure:"update" json:"update,omitempty"` + // original -> bgp-op:NOTIFICATION + // Number of BGP NOTIFICATION messages indicating an + // error condition has occurred exchanged. + Notification uint64 `mapstructure:"notification" json:"notification,omitempty"` + // original -> gobgp:OPEN + // Number of BGP open messages announcing, withdrawing + // or modifying paths exchanged. + Open uint64 `mapstructure:"open" json:"open,omitempty"` + // original -> gobgp:REFRESH + // Number of BGP Route-Refresh messages indicating an + // error condition has occurred exchanged. + Refresh uint64 `mapstructure:"refresh" json:"refresh,omitempty"` + // original -> gobgp:KEEPALIVE + // Number of BGP Keepalive messages indicating an + // error condition has occurred exchanged. + Keepalive uint64 `mapstructure:"keepalive" json:"keepalive,omitempty"` + // original -> gobgp:DYNAMIC-CAP + // Number of BGP dynamic-cap messages indicating an + // error condition has occurred exchanged. + DynamicCap uint64 `mapstructure:"dynamic-cap" json:"dynamic-cap,omitempty"` + // original -> gobgp:WITHDRAW-UPDATE + // Number of updates subjected to treat-as-withdraw treatment. + WithdrawUpdate uint32 `mapstructure:"withdraw-update" json:"withdraw-update,omitempty"` + // original -> gobgp:WITHDRAW-PREFIX + // Number of prefixes subjected to treat-as-withdraw treatment. + WithdrawPrefix uint32 `mapstructure:"withdraw-prefix" json:"withdraw-prefix,omitempty"` + // original -> gobgp:DISCARDED + // Number of discarded messages indicating an + // error condition has occurred exchanged. + Discarded uint64 `mapstructure:"discarded" json:"discarded,omitempty"` + // original -> gobgp:TOTAL + // Number of total messages indicating an + // error condition has occurred exchanged. + Total uint64 `mapstructure:"total" json:"total,omitempty"` +} + +func (lhs *Sent) Equal(rhs *Sent) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Update != rhs.Update { + return false + } + if lhs.Notification != rhs.Notification { + return false + } + if lhs.Open != rhs.Open { + return false + } + if lhs.Refresh != rhs.Refresh { + return false + } + if lhs.Keepalive != rhs.Keepalive { + return false + } + if lhs.DynamicCap != rhs.DynamicCap { + return false + } + if lhs.WithdrawUpdate != rhs.WithdrawUpdate { + return false + } + if lhs.WithdrawPrefix != rhs.WithdrawPrefix { + return false + } + if lhs.Discarded != rhs.Discarded { + return false + } + if lhs.Total != rhs.Total { + return false + } + return true +} + +// struct for container bgp:messages. +// Counters for BGP messages sent and received from the +// neighbor. +type Messages struct { + // original -> bgp:sent + // Counters relating to BGP messages sent to the neighbor. + Sent Sent `mapstructure:"sent" json:"sent,omitempty"` + // original -> bgp:received + // Counters for BGP messages received from the neighbor. + Received Received `mapstructure:"received" json:"received,omitempty"` +} + +func (lhs *Messages) Equal(rhs *Messages) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Sent.Equal(&(rhs.Sent)) { + return false + } + if !lhs.Received.Equal(&(rhs.Received)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the BGP neighbor or group. +type NeighborState struct { + // original -> bgp:peer-as + // bgp:peer-as's original type is inet:as-number. + // AS number of the peer. + PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"` + // original -> bgp:local-as + // bgp:local-as's original type is inet:as-number. + // The local autonomous system number that is to be used + // when establishing sessions with the remote peer or peer + // group, if this differs from the global BGP router + // autonomous system number. + LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"` + // original -> bgp:peer-type + // Explicitly designate the peer or peer group as internal + // (iBGP) or external (eBGP). + PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"` + // original -> bgp:auth-password + // Configures an MD5 authentication password for use with + // neighboring devices. + AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"` + // original -> bgp:remove-private-as + // Remove private AS numbers from updates sent to peers. + RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"` + // original -> bgp:route-flap-damping + // bgp:route-flap-damping's original type is boolean. + // Enable route flap damping. + RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"` + // original -> bgp:send-community + // Specify which types of community should be sent to the + // neighbor or group. The default is to not send the + // community attribute. + SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"` + // original -> bgp:description + // An optional textual description (intended primarily for use + // with a peer or group. + Description string `mapstructure:"description" json:"description,omitempty"` + // original -> bgp:peer-group + // The peer-group with which this neighbor is associated. + PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"` + // original -> bgp:neighbor-address + // bgp:neighbor-address's original type is inet:ip-address. + // Address of the BGP peer, either in IPv4 or IPv6. + NeighborAddress string `mapstructure:"neighbor-address" json:"neighbor-address,omitempty"` + // original -> bgp-op:session-state + // Operational state of the BGP peer. + SessionState SessionState `mapstructure:"session-state" json:"session-state,omitempty"` + // original -> bgp-op:supported-capabilities + // BGP capabilities negotiated as supported with the peer. + SupportedCapabilitiesList []BgpCapability `mapstructure:"supported-capabilities-list" json:"supported-capabilities-list,omitempty"` + // original -> bgp:messages + // Counters for BGP messages sent and received from the + // neighbor. + Messages Messages `mapstructure:"messages" json:"messages,omitempty"` + // original -> bgp:queues + // Counters related to queued messages associated with the + // BGP neighbor. + Queues Queues `mapstructure:"queues" json:"queues,omitempty"` + // original -> gobgp:adj-table + AdjTable AdjTable `mapstructure:"adj-table" json:"adj-table,omitempty"` + // original -> gobgp:remote-capability + // original type is list of bgp-capability + RemoteCapabilityList []bgp.ParameterCapabilityInterface `mapstructure:"remote-capability-list" json:"remote-capability-list,omitempty"` + // original -> gobgp:local-capability + // original type is list of bgp-capability + LocalCapabilityList []bgp.ParameterCapabilityInterface `mapstructure:"local-capability-list" json:"local-capability-list,omitempty"` + // original -> gobgp:received-open-message + // gobgp:received-open-message's original type is bgp-open-message. + ReceivedOpenMessage *bgp.BGPMessage `mapstructure:"received-open-message" json:"received-open-message,omitempty"` + // original -> gobgp:admin-down + // gobgp:admin-down's original type is boolean. + // The state of administrative operation. If the state is true, it indicates the neighbor is disabled by the administrator. + AdminDown bool `mapstructure:"admin-down" json:"admin-down,omitempty"` + // original -> gobgp:admin-state + AdminState AdminState `mapstructure:"admin-state" json:"admin-state,omitempty"` + // original -> gobgp:established-count + // The number of how many the peer became established state. + EstablishedCount uint32 `mapstructure:"established-count" json:"established-count,omitempty"` + // original -> gobgp:flops + // The number of flip-flops. + Flops uint32 `mapstructure:"flops" json:"flops,omitempty"` + // original -> gobgp:neighbor-interface + NeighborInterface string `mapstructure:"neighbor-interface" json:"neighbor-interface,omitempty"` + // original -> gobgp:vrf + Vrf string `mapstructure:"vrf" json:"vrf,omitempty"` + // original -> gobgp:remote-router-id + RemoteRouterId string `mapstructure:"remote-router-id" json:"remote-router-id,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to the BGP neighbor or +// group. +type NeighborConfig struct { + // original -> bgp:peer-as + // bgp:peer-as's original type is inet:as-number. + // AS number of the peer. + PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"` + // original -> bgp:local-as + // bgp:local-as's original type is inet:as-number. + // The local autonomous system number that is to be used + // when establishing sessions with the remote peer or peer + // group, if this differs from the global BGP router + // autonomous system number. + LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"` + // original -> bgp:peer-type + // Explicitly designate the peer or peer group as internal + // (iBGP) or external (eBGP). + PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"` + // original -> bgp:auth-password + // Configures an MD5 authentication password for use with + // neighboring devices. + AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"` + // original -> bgp:remove-private-as + // Remove private AS numbers from updates sent to peers. + RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"` + // original -> bgp:route-flap-damping + // bgp:route-flap-damping's original type is boolean. + // Enable route flap damping. + RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"` + // original -> bgp:send-community + // Specify which types of community should be sent to the + // neighbor or group. The default is to not send the + // community attribute. + SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"` + // original -> bgp:description + // An optional textual description (intended primarily for use + // with a peer or group. + Description string `mapstructure:"description" json:"description,omitempty"` + // original -> bgp:peer-group + // The peer-group with which this neighbor is associated. + PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"` + // original -> bgp:neighbor-address + // bgp:neighbor-address's original type is inet:ip-address. + // Address of the BGP peer, either in IPv4 or IPv6. + NeighborAddress string `mapstructure:"neighbor-address" json:"neighbor-address,omitempty"` + // original -> gobgp:admin-down + // gobgp:admin-down's original type is boolean. + // The config of administrative operation. If state, indicates the neighbor is disabled by the administrator. + AdminDown bool `mapstructure:"admin-down" json:"admin-down,omitempty"` + // original -> gobgp:neighbor-interface + NeighborInterface string `mapstructure:"neighbor-interface" json:"neighbor-interface,omitempty"` + // original -> gobgp:vrf + Vrf string `mapstructure:"vrf" json:"vrf,omitempty"` +} + +func (lhs *NeighborConfig) Equal(rhs *NeighborConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.PeerAs != rhs.PeerAs { + return false + } + if lhs.LocalAs != rhs.LocalAs { + return false + } + if lhs.PeerType != rhs.PeerType { + return false + } + if lhs.AuthPassword != rhs.AuthPassword { + return false + } + if lhs.RemovePrivateAs != rhs.RemovePrivateAs { + return false + } + if lhs.RouteFlapDamping != rhs.RouteFlapDamping { + return false + } + if lhs.SendCommunity != rhs.SendCommunity { + return false + } + if lhs.Description != rhs.Description { + return false + } + if lhs.PeerGroup != rhs.PeerGroup { + return false + } + if lhs.NeighborAddress != rhs.NeighborAddress { + return false + } + if lhs.AdminDown != rhs.AdminDown { + return false + } + if lhs.NeighborInterface != rhs.NeighborInterface { + return false + } + if lhs.Vrf != rhs.Vrf { + return false + } + return true +} + +// struct for container bgp:neighbor. +// List of BGP neighbors configured on the local system, +// uniquely identified by peer IPv[46] address. +type Neighbor struct { + // original -> bgp:neighbor-address + // original -> bgp:neighbor-config + // Configuration parameters relating to the BGP neighbor or + // group. + Config NeighborConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:neighbor-state + // State information relating to the BGP neighbor or group. + State NeighborState `mapstructure:"state" json:"state,omitempty"` + // original -> bgp:timers + // Timers related to a BGP neighbor or group. + Timers Timers `mapstructure:"timers" json:"timers,omitempty"` + // original -> bgp:transport + // Transport session parameters for the BGP neighbor or group. + Transport Transport `mapstructure:"transport" json:"transport,omitempty"` + // original -> bgp:error-handling + // Error handling parameters used for the BGP neighbor or + // group. + ErrorHandling ErrorHandling `mapstructure:"error-handling" json:"error-handling,omitempty"` + // original -> bgp:logging-options + // Logging options for events related to the BGP neighbor or + // group. + LoggingOptions LoggingOptions `mapstructure:"logging-options" json:"logging-options,omitempty"` + // original -> bgp:ebgp-multihop + // eBGP multi-hop parameters for the BGP neighbor or group. + EbgpMultihop EbgpMultihop `mapstructure:"ebgp-multihop" json:"ebgp-multihop,omitempty"` + // original -> bgp:route-reflector + // Route reflector parameters for the BGP neighbor or group. + RouteReflector RouteReflector `mapstructure:"route-reflector" json:"route-reflector,omitempty"` + // original -> bgp:as-path-options + // AS_PATH manipulation parameters for the BGP neighbor or + // group. + AsPathOptions AsPathOptions `mapstructure:"as-path-options" json:"as-path-options,omitempty"` + // original -> bgp:add-paths + // Parameters relating to the advertisement and receipt of + // multiple paths for a single NLRI (add-paths). + AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"` + // original -> bgp:afi-safis + // Per-address-family configuration parameters associated with + // the neighbor or group. + AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"` + // original -> bgp:graceful-restart + // Parameters relating the graceful restart mechanism for BGP. + GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"` + // original -> rpol:apply-policy + // Anchor point for routing policies in the model. + // Import and export policies are with respect to the local + // routing table, i.e., export (send) and import (receive), + // depending on the context. + ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"` + // original -> bgp-mp:use-multiple-paths + // Parameters related to the use of multiple-paths for the same + // NLRI when they are received only from this neighbor. + UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"` + // original -> gobgp:route-server + // Configure the local router as a route server. + RouteServer RouteServer `mapstructure:"route-server" json:"route-server,omitempty"` + // original -> gobgp:ttl-security + // Configure TTL Security feature. + TtlSecurity TtlSecurity `mapstructure:"ttl-security" json:"ttl-security,omitempty"` +} + +func (lhs *Neighbor) Equal(rhs *Neighbor) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + if !lhs.Timers.Equal(&(rhs.Timers)) { + return false + } + if !lhs.Transport.Equal(&(rhs.Transport)) { + return false + } + if !lhs.ErrorHandling.Equal(&(rhs.ErrorHandling)) { + return false + } + if !lhs.LoggingOptions.Equal(&(rhs.LoggingOptions)) { + return false + } + if !lhs.EbgpMultihop.Equal(&(rhs.EbgpMultihop)) { + return false + } + if !lhs.RouteReflector.Equal(&(rhs.RouteReflector)) { + return false + } + if !lhs.AsPathOptions.Equal(&(rhs.AsPathOptions)) { + return false + } + if !lhs.AddPaths.Equal(&(rhs.AddPaths)) { + return false + } + if len(lhs.AfiSafis) != len(rhs.AfiSafis) { + return false + } + { + lmap := make(map[string]*AfiSafi) + for i, l := range lhs.AfiSafis { + lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i] + } + for i, r := range rhs.AfiSafis { + if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) { + return false + } + if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) { + return false + } + if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) { + return false + } + if !lhs.RouteServer.Equal(&(rhs.RouteServer)) { + return false + } + if !lhs.TtlSecurity.Equal(&(rhs.TtlSecurity)) { + return false + } + return true +} + +// struct for container gobgp:state. +type LongLivedGracefulRestartState struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:received + // gobgp:received's original type is boolean. + Received bool `mapstructure:"received" json:"received,omitempty"` + // original -> gobgp:advertised + // gobgp:advertised's original type is boolean. + Advertised bool `mapstructure:"advertised" json:"advertised,omitempty"` + // original -> gobgp:peer-restart-time + PeerRestartTime uint32 `mapstructure:"peer-restart-time" json:"peer-restart-time,omitempty"` + // original -> gobgp:peer-restart-timer-expired + // gobgp:peer-restart-timer-expired's original type is boolean. + PeerRestartTimerExpired bool `mapstructure:"peer-restart-timer-expired" json:"peer-restart-timer-expired,omitempty"` +} + +// struct for container gobgp:config. +type LongLivedGracefulRestartConfig struct { + // original -> gobgp:enabled + // gobgp:enabled's original type is boolean. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> gobgp:restart-time + RestartTime uint32 `mapstructure:"restart-time" json:"restart-time,omitempty"` +} + +func (lhs *LongLivedGracefulRestartConfig) Equal(rhs *LongLivedGracefulRestartConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.RestartTime != rhs.RestartTime { + return false + } + return true +} + +// struct for container gobgp:long-lived-graceful-restart. +type LongLivedGracefulRestart struct { + // original -> gobgp:long-lived-graceful-restart-config + Config LongLivedGracefulRestartConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:long-lived-graceful-restart-state + State LongLivedGracefulRestartState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *LongLivedGracefulRestart) Equal(rhs *LongLivedGracefulRestart) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container gobgp:state. +type RouteTargetMembershipState struct { + // original -> gobgp:deferral-time + DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"` +} + +// struct for container gobgp:config. +type RouteTargetMembershipConfig struct { + // original -> gobgp:deferral-time + DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"` +} + +func (lhs *RouteTargetMembershipConfig) Equal(rhs *RouteTargetMembershipConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.DeferralTime != rhs.DeferralTime { + return false + } + return true +} + +// struct for container gobgp:route-target-membership. +type RouteTargetMembership struct { + // original -> gobgp:route-target-membership-config + Config RouteTargetMembershipConfig `mapstructure:"config" json:"config,omitempty"` + // original -> gobgp:route-target-membership-state + State RouteTargetMembershipState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *RouteTargetMembership) Equal(rhs *RouteTargetMembership) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:l2vpn-evpn. +// BGP EVPN configuration options. +type L2vpnEvpn struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L2vpnEvpn) Equal(rhs *L2vpnEvpn) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:l2vpn-vpls. +// BGP-signalled VPLS configuration options. +type L2vpnVpls struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L2vpnVpls) Equal(rhs *L2vpnVpls) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:l3vpn-ipv6-multicast. +// Multicast IPv6 L3VPN configuration options. +type L3vpnIpv6Multicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L3vpnIpv6Multicast) Equal(rhs *L3vpnIpv6Multicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:l3vpn-ipv4-multicast. +// Multicast IPv4 L3VPN configuration options. +type L3vpnIpv4Multicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L3vpnIpv4Multicast) Equal(rhs *L3vpnIpv4Multicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:l3vpn-ipv6-unicast. +// Unicast IPv6 L3VPN configuration options. +type L3vpnIpv6Unicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L3vpnIpv6Unicast) Equal(rhs *L3vpnIpv6Unicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:l3vpn-ipv4-unicast. +// Unicast IPv4 L3VPN configuration options. +type L3vpnIpv4Unicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *L3vpnIpv4Unicast) Equal(rhs *L3vpnIpv4Unicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:ipv6-labelled-unicast. +// IPv6 Labelled Unicast configuration options. +type Ipv6LabelledUnicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *Ipv6LabelledUnicast) Equal(rhs *Ipv6LabelledUnicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:ipv4-labelled-unicast. +// IPv4 Labelled Unicast configuration options. +type Ipv4LabelledUnicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` +} + +func (lhs *Ipv4LabelledUnicast) Equal(rhs *Ipv4LabelledUnicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information for common IPv4 and IPv6 unicast +// parameters. +type Ipv6UnicastState struct { + // original -> bgp-mp:send-default-route + // bgp-mp:send-default-route's original type is boolean. + // If set to true, send the default-route to the neighbour(s). + SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters for common IPv4 and IPv6 unicast +// AFI-SAFI options. +type Ipv6UnicastConfig struct { + // original -> bgp-mp:send-default-route + // bgp-mp:send-default-route's original type is boolean. + // If set to true, send the default-route to the neighbour(s). + SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"` +} + +func (lhs *Ipv6UnicastConfig) Equal(rhs *Ipv6UnicastConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.SendDefaultRoute != rhs.SendDefaultRoute { + return false + } + return true +} + +// struct for container bgp-mp:ipv6-unicast. +// IPv6 unicast configuration options. +type Ipv6Unicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` + // original -> bgp-mp:ipv6-unicast-config + // Configuration parameters for common IPv4 and IPv6 unicast + // AFI-SAFI options. + Config Ipv6UnicastConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:ipv6-unicast-state + // State information for common IPv4 and IPv6 unicast + // parameters. + State Ipv6UnicastState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Ipv6Unicast) Equal(rhs *Ipv6Unicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information for common IPv4 and IPv6 unicast +// parameters. +type Ipv4UnicastState struct { + // original -> bgp-mp:send-default-route + // bgp-mp:send-default-route's original type is boolean. + // If set to true, send the default-route to the neighbour(s). + SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters for common IPv4 and IPv6 unicast +// AFI-SAFI options. +type Ipv4UnicastConfig struct { + // original -> bgp-mp:send-default-route + // bgp-mp:send-default-route's original type is boolean. + // If set to true, send the default-route to the neighbour(s). + SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"` +} + +func (lhs *Ipv4UnicastConfig) Equal(rhs *Ipv4UnicastConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.SendDefaultRoute != rhs.SendDefaultRoute { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information relating to the prefix-limit for the +// AFI-SAFI. +type PrefixLimitState struct { + // original -> bgp-mp:max-prefixes + // Maximum number of prefixes that will be accepted + // from the neighbour. + MaxPrefixes uint32 `mapstructure:"max-prefixes" json:"max-prefixes,omitempty"` + // original -> bgp-mp:shutdown-threshold-pct + // Threshold on number of prefixes that can be received + // from a neighbour before generation of warning messages + // or log entries. Expressed as a percentage of + // max-prefixes. + ShutdownThresholdPct Percentage `mapstructure:"shutdown-threshold-pct" json:"shutdown-threshold-pct,omitempty"` + // original -> bgp-mp:restart-timer + // bgp-mp:restart-timer's original type is decimal64. + // Time interval in seconds after which the BGP session + // is re-established after being torn down due to exceeding + // the max-prefix limit. + RestartTimer float64 `mapstructure:"restart-timer" json:"restart-timer,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters relating to the prefix +// limit for the AFI-SAFI. +type PrefixLimitConfig struct { + // original -> bgp-mp:max-prefixes + // Maximum number of prefixes that will be accepted + // from the neighbour. + MaxPrefixes uint32 `mapstructure:"max-prefixes" json:"max-prefixes,omitempty"` + // original -> bgp-mp:shutdown-threshold-pct + // Threshold on number of prefixes that can be received + // from a neighbour before generation of warning messages + // or log entries. Expressed as a percentage of + // max-prefixes. + ShutdownThresholdPct Percentage `mapstructure:"shutdown-threshold-pct" json:"shutdown-threshold-pct,omitempty"` + // original -> bgp-mp:restart-timer + // bgp-mp:restart-timer's original type is decimal64. + // Time interval in seconds after which the BGP session + // is re-established after being torn down due to exceeding + // the max-prefix limit. + RestartTimer float64 `mapstructure:"restart-timer" json:"restart-timer,omitempty"` +} + +func (lhs *PrefixLimitConfig) Equal(rhs *PrefixLimitConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.MaxPrefixes != rhs.MaxPrefixes { + return false + } + if lhs.ShutdownThresholdPct != rhs.ShutdownThresholdPct { + return false + } + if lhs.RestartTimer != rhs.RestartTimer { + return false + } + return true +} + +// struct for container bgp-mp:prefix-limit. +// Configure the maximum number of prefixes that will be +// accepted from a peer. +type PrefixLimit struct { + // original -> bgp-mp:prefix-limit-config + // Configuration parameters relating to the prefix + // limit for the AFI-SAFI. + Config PrefixLimitConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:prefix-limit-state + // State information relating to the prefix-limit for the + // AFI-SAFI. + State PrefixLimitState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *PrefixLimit) Equal(rhs *PrefixLimit) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:ipv4-unicast. +// IPv4 unicast configuration options. +type Ipv4Unicast struct { + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` + // original -> bgp-mp:ipv4-unicast-config + // Configuration parameters for common IPv4 and IPv6 unicast + // AFI-SAFI options. + Config Ipv4UnicastConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:ipv4-unicast-state + // State information for common IPv4 and IPv6 unicast + // parameters. + State Ipv4UnicastState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Ipv4Unicast) Equal(rhs *Ipv4Unicast) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container rpol:state. +// Operational state for routing policy. +type ApplyPolicyState struct { + // original -> rpol:import-policy + // list of policy names in sequence to be applied on + // receiving a routing update in the current context, e.g., + // for the current peer group, neighbor, address family, + // etc. + ImportPolicyList []string `mapstructure:"import-policy-list" json:"import-policy-list,omitempty"` + // original -> rpol:default-import-policy + // explicitly set a default policy if no policy definition + // in the import policy chain is satisfied. + DefaultImportPolicy DefaultPolicyType `mapstructure:"default-import-policy" json:"default-import-policy,omitempty"` + // original -> rpol:export-policy + // list of policy names in sequence to be applied on + // sending a routing update in the current context, e.g., + // for the current peer group, neighbor, address family, + // etc. + ExportPolicyList []string `mapstructure:"export-policy-list" json:"export-policy-list,omitempty"` + // original -> rpol:default-export-policy + // explicitly set a default policy if no policy definition + // in the export policy chain is satisfied. + DefaultExportPolicy DefaultPolicyType `mapstructure:"default-export-policy" json:"default-export-policy,omitempty"` + // original -> gobgp:in-policy + // list of policy names in sequence to be applied on + // sending a routing update in the current context, e.g., + // for the current other route server clients. + InPolicyList []string `mapstructure:"in-policy-list" json:"in-policy-list,omitempty"` + // original -> gobgp:default-in-policy + // explicitly set a default policy if no policy definition + // in the in-policy chain is satisfied. + DefaultInPolicy DefaultPolicyType `mapstructure:"default-in-policy" json:"default-in-policy,omitempty"` +} + +// struct for container rpol:config. +// Policy configuration data. +type ApplyPolicyConfig struct { + // original -> rpol:import-policy + // list of policy names in sequence to be applied on + // receiving a routing update in the current context, e.g., + // for the current peer group, neighbor, address family, + // etc. + ImportPolicyList []string `mapstructure:"import-policy-list" json:"import-policy-list,omitempty"` + // original -> rpol:default-import-policy + // explicitly set a default policy if no policy definition + // in the import policy chain is satisfied. + DefaultImportPolicy DefaultPolicyType `mapstructure:"default-import-policy" json:"default-import-policy,omitempty"` + // original -> rpol:export-policy + // list of policy names in sequence to be applied on + // sending a routing update in the current context, e.g., + // for the current peer group, neighbor, address family, + // etc. + ExportPolicyList []string `mapstructure:"export-policy-list" json:"export-policy-list,omitempty"` + // original -> rpol:default-export-policy + // explicitly set a default policy if no policy definition + // in the export policy chain is satisfied. + DefaultExportPolicy DefaultPolicyType `mapstructure:"default-export-policy" json:"default-export-policy,omitempty"` + // original -> gobgp:in-policy + // list of policy names in sequence to be applied on + // sending a routing update in the current context, e.g., + // for the current other route server clients. + InPolicyList []string `mapstructure:"in-policy-list" json:"in-policy-list,omitempty"` + // original -> gobgp:default-in-policy + // explicitly set a default policy if no policy definition + // in the in-policy chain is satisfied. + DefaultInPolicy DefaultPolicyType `mapstructure:"default-in-policy" json:"default-in-policy,omitempty"` +} + +func (lhs *ApplyPolicyConfig) Equal(rhs *ApplyPolicyConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.ImportPolicyList) != len(rhs.ImportPolicyList) { + return false + } + for idx, l := range lhs.ImportPolicyList { + if l != rhs.ImportPolicyList[idx] { + return false + } + } + if lhs.DefaultImportPolicy != rhs.DefaultImportPolicy { + return false + } + if len(lhs.ExportPolicyList) != len(rhs.ExportPolicyList) { + return false + } + for idx, l := range lhs.ExportPolicyList { + if l != rhs.ExportPolicyList[idx] { + return false + } + } + if lhs.DefaultExportPolicy != rhs.DefaultExportPolicy { + return false + } + if len(lhs.InPolicyList) != len(rhs.InPolicyList) { + return false + } + for idx, l := range lhs.InPolicyList { + if l != rhs.InPolicyList[idx] { + return false + } + } + if lhs.DefaultInPolicy != rhs.DefaultInPolicy { + return false + } + return true +} + +// struct for container rpol:apply-policy. +// Anchor point for routing policies in the model. +// Import and export policies are with respect to the local +// routing table, i.e., export (send) and import (receive), +// depending on the context. +type ApplyPolicy struct { + // original -> rpol:apply-policy-config + // Policy configuration data. + Config ApplyPolicyConfig `mapstructure:"config" json:"config,omitempty"` + // original -> rpol:apply-policy-state + // Operational state for routing policy. + State ApplyPolicyState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *ApplyPolicy) Equal(rhs *ApplyPolicy) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information relating to the AFI-SAFI. +type AfiSafiState struct { + // original -> bgp-mp:afi-safi-name + // AFI,SAFI. + AfiSafiName AfiSafiType `mapstructure:"afi-safi-name" json:"afi-safi-name,omitempty"` + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // This leaf indicates whether the IPv4 Unicast AFI,SAFI is + // enabled for the neighbour or group. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp-op:total-paths + // Total number of BGP paths within the context. + TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"` + // original -> bgp-op:total-prefixes + // . + TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"` + // original -> gobgp:family + // gobgp:family's original type is route-family. + // Address family value of AFI-SAFI pair translated from afi-safi-name. + Family bgp.RouteFamily `mapstructure:"family" json:"family,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters for the AFI-SAFI. +type AfiSafiConfig struct { + // original -> bgp-mp:afi-safi-name + // AFI,SAFI. + AfiSafiName AfiSafiType `mapstructure:"afi-safi-name" json:"afi-safi-name,omitempty"` + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // This leaf indicates whether the IPv4 Unicast AFI,SAFI is + // enabled for the neighbour or group. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` +} + +func (lhs *AfiSafiConfig) Equal(rhs *AfiSafiConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AfiSafiName != rhs.AfiSafiName { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information for BGP graceful-restart. +type MpGracefulRestartState struct { + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // This leaf indicates whether graceful-restart is enabled for + // this AFI-SAFI. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp-op:received + // bgp-op:received's original type is boolean. + // This leaf indicates whether the neighbor advertised the + // ability to support graceful-restart for this AFI-SAFI. + Received bool `mapstructure:"received" json:"received,omitempty"` + // original -> bgp-op:advertised + // bgp-op:advertised's original type is boolean. + // This leaf indicates whether the ability to support + // graceful-restart has been advertised to the peer. + Advertised bool `mapstructure:"advertised" json:"advertised,omitempty"` + // original -> gobgp:end-of-rib-received + // gobgp:end-of-rib-received's original type is boolean. + EndOfRibReceived bool `mapstructure:"end-of-rib-received" json:"end-of-rib-received,omitempty"` + // original -> gobgp:end-of-rib-sent + // gobgp:end-of-rib-sent's original type is boolean. + EndOfRibSent bool `mapstructure:"end-of-rib-sent" json:"end-of-rib-sent,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration options for BGP graceful-restart. +type MpGracefulRestartConfig struct { + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // This leaf indicates whether graceful-restart is enabled for + // this AFI-SAFI. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` +} + +func (lhs *MpGracefulRestartConfig) Equal(rhs *MpGracefulRestartConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + return true +} + +// struct for container bgp-mp:graceful-restart. +// Parameters relating to BGP graceful-restart. +type MpGracefulRestart struct { + // original -> bgp-mp:mp-graceful-restart-config + // Configuration options for BGP graceful-restart. + Config MpGracefulRestartConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:mp-graceful-restart-state + // State information for BGP graceful-restart. + State MpGracefulRestartState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *MpGracefulRestart) Equal(rhs *MpGracefulRestart) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:afi-safi. +// AFI,SAFI configuration available for the +// neighbour or group. +type AfiSafi struct { + // original -> bgp-mp:afi-safi-name + // original -> bgp-mp:mp-graceful-restart + // Parameters relating to BGP graceful-restart. + MpGracefulRestart MpGracefulRestart `mapstructure:"mp-graceful-restart" json:"mp-graceful-restart,omitempty"` + // original -> bgp-mp:afi-safi-config + // Configuration parameters for the AFI-SAFI. + Config AfiSafiConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:afi-safi-state + // State information relating to the AFI-SAFI. + State AfiSafiState `mapstructure:"state" json:"state,omitempty"` + // original -> rpol:apply-policy + // Anchor point for routing policies in the model. + // Import and export policies are with respect to the local + // routing table, i.e., export (send) and import (receive), + // depending on the context. + ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"` + // original -> bgp-mp:ipv4-unicast + // IPv4 unicast configuration options. + Ipv4Unicast Ipv4Unicast `mapstructure:"ipv4-unicast" json:"ipv4-unicast,omitempty"` + // original -> bgp-mp:ipv6-unicast + // IPv6 unicast configuration options. + Ipv6Unicast Ipv6Unicast `mapstructure:"ipv6-unicast" json:"ipv6-unicast,omitempty"` + // original -> bgp-mp:ipv4-labelled-unicast + // IPv4 Labelled Unicast configuration options. + Ipv4LabelledUnicast Ipv4LabelledUnicast `mapstructure:"ipv4-labelled-unicast" json:"ipv4-labelled-unicast,omitempty"` + // original -> bgp-mp:ipv6-labelled-unicast + // IPv6 Labelled Unicast configuration options. + Ipv6LabelledUnicast Ipv6LabelledUnicast `mapstructure:"ipv6-labelled-unicast" json:"ipv6-labelled-unicast,omitempty"` + // original -> bgp-mp:l3vpn-ipv4-unicast + // Unicast IPv4 L3VPN configuration options. + L3vpnIpv4Unicast L3vpnIpv4Unicast `mapstructure:"l3vpn-ipv4-unicast" json:"l3vpn-ipv4-unicast,omitempty"` + // original -> bgp-mp:l3vpn-ipv6-unicast + // Unicast IPv6 L3VPN configuration options. + L3vpnIpv6Unicast L3vpnIpv6Unicast `mapstructure:"l3vpn-ipv6-unicast" json:"l3vpn-ipv6-unicast,omitempty"` + // original -> bgp-mp:l3vpn-ipv4-multicast + // Multicast IPv4 L3VPN configuration options. + L3vpnIpv4Multicast L3vpnIpv4Multicast `mapstructure:"l3vpn-ipv4-multicast" json:"l3vpn-ipv4-multicast,omitempty"` + // original -> bgp-mp:l3vpn-ipv6-multicast + // Multicast IPv6 L3VPN configuration options. + L3vpnIpv6Multicast L3vpnIpv6Multicast `mapstructure:"l3vpn-ipv6-multicast" json:"l3vpn-ipv6-multicast,omitempty"` + // original -> bgp-mp:l2vpn-vpls + // BGP-signalled VPLS configuration options. + L2vpnVpls L2vpnVpls `mapstructure:"l2vpn-vpls" json:"l2vpn-vpls,omitempty"` + // original -> bgp-mp:l2vpn-evpn + // BGP EVPN configuration options. + L2vpnEvpn L2vpnEvpn `mapstructure:"l2vpn-evpn" json:"l2vpn-evpn,omitempty"` + // original -> bgp-mp:route-selection-options + // Parameters relating to options for route selection. + RouteSelectionOptions RouteSelectionOptions `mapstructure:"route-selection-options" json:"route-selection-options,omitempty"` + // original -> bgp-mp:use-multiple-paths + // Parameters related to the use of multiple paths for the + // same NLRI. + UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"` + // original -> bgp-mp:prefix-limit + // Configure the maximum number of prefixes that will be + // accepted from a peer. + PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"` + // original -> gobgp:route-target-membership + RouteTargetMembership RouteTargetMembership `mapstructure:"route-target-membership" json:"route-target-membership,omitempty"` + // original -> gobgp:long-lived-graceful-restart + LongLivedGracefulRestart LongLivedGracefulRestart `mapstructure:"long-lived-graceful-restart" json:"long-lived-graceful-restart,omitempty"` + // original -> gobgp:add-paths + // add-paths configuration options related to a particular AFI-SAFI. + AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"` +} + +func (lhs *AfiSafi) Equal(rhs *AfiSafi) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.MpGracefulRestart.Equal(&(rhs.MpGracefulRestart)) { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) { + return false + } + if !lhs.Ipv4Unicast.Equal(&(rhs.Ipv4Unicast)) { + return false + } + if !lhs.Ipv6Unicast.Equal(&(rhs.Ipv6Unicast)) { + return false + } + if !lhs.Ipv4LabelledUnicast.Equal(&(rhs.Ipv4LabelledUnicast)) { + return false + } + if !lhs.Ipv6LabelledUnicast.Equal(&(rhs.Ipv6LabelledUnicast)) { + return false + } + if !lhs.L3vpnIpv4Unicast.Equal(&(rhs.L3vpnIpv4Unicast)) { + return false + } + if !lhs.L3vpnIpv6Unicast.Equal(&(rhs.L3vpnIpv6Unicast)) { + return false + } + if !lhs.L3vpnIpv4Multicast.Equal(&(rhs.L3vpnIpv4Multicast)) { + return false + } + if !lhs.L3vpnIpv6Multicast.Equal(&(rhs.L3vpnIpv6Multicast)) { + return false + } + if !lhs.L2vpnVpls.Equal(&(rhs.L2vpnVpls)) { + return false + } + if !lhs.L2vpnEvpn.Equal(&(rhs.L2vpnEvpn)) { + return false + } + if !lhs.RouteSelectionOptions.Equal(&(rhs.RouteSelectionOptions)) { + return false + } + if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) { + return false + } + if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) { + return false + } + if !lhs.RouteTargetMembership.Equal(&(rhs.RouteTargetMembership)) { + return false + } + if !lhs.LongLivedGracefulRestart.Equal(&(rhs.LongLivedGracefulRestart)) { + return false + } + if !lhs.AddPaths.Equal(&(rhs.AddPaths)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information associated with graceful-restart. +type GracefulRestartState struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // Enable or disable the graceful-restart capability. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:restart-time + // Estimated time (in seconds) for the local BGP speaker to + // restart a session. This value is advertise in the graceful + // restart BGP capability. This is a 12-bit value, referred to + // as Restart Time in RFC4724. Per RFC4724, the suggested + // default value is <= the hold-time value. + RestartTime uint16 `mapstructure:"restart-time" json:"restart-time,omitempty"` + // original -> bgp:stale-routes-time + // bgp:stale-routes-time's original type is decimal64. + // An upper-bound on the time thate stale routes will be + // retained by a router after a session is restarted. If an + // End-of-RIB (EOR) marker is received prior to this timer + // expiring stale-routes will be flushed upon its receipt - if + // no EOR is received, then when this timer expires stale paths + // will be purged. This timer is referred to as the + // Selection_Deferral_Timer in RFC4724. + StaleRoutesTime float64 `mapstructure:"stale-routes-time" json:"stale-routes-time,omitempty"` + // original -> bgp:helper-only + // bgp:helper-only's original type is boolean. + // Enable graceful-restart in helper mode only. When this + // leaf is set, the local system does not retain forwarding + // its own state during a restart, but supports procedures + // for the receiving speaker, as defined in RFC4724. + HelperOnly bool `mapstructure:"helper-only" json:"helper-only,omitempty"` + // original -> bgp-op:peer-restart-time + // The period of time (advertised by the peer) that + // the peer expects a restart of a BGP session to + // take. + PeerRestartTime uint16 `mapstructure:"peer-restart-time" json:"peer-restart-time,omitempty"` + // original -> bgp-op:peer-restarting + // bgp-op:peer-restarting's original type is boolean. + // This flag indicates whether the remote neighbor is currently + // in the process of restarting, and hence received routes are + // currently stale. + PeerRestarting bool `mapstructure:"peer-restarting" json:"peer-restarting,omitempty"` + // original -> bgp-op:local-restarting + // bgp-op:local-restarting's original type is boolean. + // This flag indicates whether the local neighbor is currently + // restarting. The flag is unset after all NLRI have been + // advertised to the peer, and the End-of-RIB (EOR) marker has + // been unset. + LocalRestarting bool `mapstructure:"local-restarting" json:"local-restarting,omitempty"` + // original -> bgp-op:mode + // Ths leaf indicates the mode of operation of BGP graceful + // restart with the peer. + Mode Mode `mapstructure:"mode" json:"mode,omitempty"` + // original -> gobgp:deferral-time + DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"` + // original -> gobgp:notification-enabled + // gobgp:notification-enabled's original type is boolean. + NotificationEnabled bool `mapstructure:"notification-enabled" json:"notification-enabled,omitempty"` + // original -> gobgp:long-lived-enabled + // gobgp:long-lived-enabled's original type is boolean. + LongLivedEnabled bool `mapstructure:"long-lived-enabled" json:"long-lived-enabled,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to graceful-restart. +type GracefulRestartConfig struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // Enable or disable the graceful-restart capability. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:restart-time + // Estimated time (in seconds) for the local BGP speaker to + // restart a session. This value is advertise in the graceful + // restart BGP capability. This is a 12-bit value, referred to + // as Restart Time in RFC4724. Per RFC4724, the suggested + // default value is <= the hold-time value. + RestartTime uint16 `mapstructure:"restart-time" json:"restart-time,omitempty"` + // original -> bgp:stale-routes-time + // bgp:stale-routes-time's original type is decimal64. + // An upper-bound on the time thate stale routes will be + // retained by a router after a session is restarted. If an + // End-of-RIB (EOR) marker is received prior to this timer + // expiring stale-routes will be flushed upon its receipt - if + // no EOR is received, then when this timer expires stale paths + // will be purged. This timer is referred to as the + // Selection_Deferral_Timer in RFC4724. + StaleRoutesTime float64 `mapstructure:"stale-routes-time" json:"stale-routes-time,omitempty"` + // original -> bgp:helper-only + // bgp:helper-only's original type is boolean. + // Enable graceful-restart in helper mode only. When this + // leaf is set, the local system does not retain forwarding + // its own state during a restart, but supports procedures + // for the receiving speaker, as defined in RFC4724. + HelperOnly bool `mapstructure:"helper-only" json:"helper-only,omitempty"` + // original -> gobgp:deferral-time + DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"` + // original -> gobgp:notification-enabled + // gobgp:notification-enabled's original type is boolean. + NotificationEnabled bool `mapstructure:"notification-enabled" json:"notification-enabled,omitempty"` + // original -> gobgp:long-lived-enabled + // gobgp:long-lived-enabled's original type is boolean. + LongLivedEnabled bool `mapstructure:"long-lived-enabled" json:"long-lived-enabled,omitempty"` +} + +func (lhs *GracefulRestartConfig) Equal(rhs *GracefulRestartConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.RestartTime != rhs.RestartTime { + return false + } + if lhs.StaleRoutesTime != rhs.StaleRoutesTime { + return false + } + if lhs.HelperOnly != rhs.HelperOnly { + return false + } + if lhs.DeferralTime != rhs.DeferralTime { + return false + } + if lhs.NotificationEnabled != rhs.NotificationEnabled { + return false + } + if lhs.LongLivedEnabled != rhs.LongLivedEnabled { + return false + } + return true +} + +// struct for container bgp:graceful-restart. +// Parameters relating the graceful restart mechanism for BGP. +type GracefulRestart struct { + // original -> bgp:graceful-restart-config + // Configuration parameters relating to graceful-restart. + Config GracefulRestartConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:graceful-restart-state + // State information associated with graceful-restart. + State GracefulRestartState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *GracefulRestart) Equal(rhs *GracefulRestart) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information relating to iBGP multipath. +type IbgpState struct { + // original -> bgp-mp:maximum-paths + // Maximum number of parallel paths to consider when using + // iBGP multipath. The default is to use a single path. + MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters relating to iBGP multipath. +type IbgpConfig struct { + // original -> bgp-mp:maximum-paths + // Maximum number of parallel paths to consider when using + // iBGP multipath. The default is to use a single path. + MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"` +} + +func (lhs *IbgpConfig) Equal(rhs *IbgpConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.MaximumPaths != rhs.MaximumPaths { + return false + } + return true +} + +// struct for container bgp-mp:ibgp. +// Multipath parameters for iBGP. +type Ibgp struct { + // original -> bgp-mp:ibgp-config + // Configuration parameters relating to iBGP multipath. + Config IbgpConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:ibgp-state + // State information relating to iBGP multipath. + State IbgpState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Ibgp) Equal(rhs *Ibgp) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information relating to eBGP multipath. +type EbgpState struct { + // original -> bgp-mp:allow-multiple-as + // bgp-mp:allow-multiple-as's original type is boolean. + // Allow multipath to use paths from different neighbouring + // ASes. The default is to only consider multiple paths from + // the same neighbouring AS. + AllowMultipleAs bool `mapstructure:"allow-multiple-as" json:"allow-multiple-as,omitempty"` + // original -> bgp-mp:maximum-paths + // Maximum number of parallel paths to consider when using + // BGP multipath. The default is use a single path. + MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters relating to eBGP multipath. +type EbgpConfig struct { + // original -> bgp-mp:allow-multiple-as + // bgp-mp:allow-multiple-as's original type is boolean. + // Allow multipath to use paths from different neighbouring + // ASes. The default is to only consider multiple paths from + // the same neighbouring AS. + AllowMultipleAs bool `mapstructure:"allow-multiple-as" json:"allow-multiple-as,omitempty"` + // original -> bgp-mp:maximum-paths + // Maximum number of parallel paths to consider when using + // BGP multipath. The default is use a single path. + MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"` +} + +func (lhs *EbgpConfig) Equal(rhs *EbgpConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AllowMultipleAs != rhs.AllowMultipleAs { + return false + } + if lhs.MaximumPaths != rhs.MaximumPaths { + return false + } + return true +} + +// struct for container bgp-mp:ebgp. +// Multipath parameters for eBGP. +type Ebgp struct { + // original -> bgp-mp:ebgp-config + // Configuration parameters relating to eBGP multipath. + Config EbgpConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:ebgp-state + // State information relating to eBGP multipath. + State EbgpState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Ebgp) Equal(rhs *Ebgp) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State parameters relating to multipath. +type UseMultiplePathsState struct { + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // Whether the use of multiple paths for the same NLRI is + // enabled for the neighbor. This value is overridden by + // any more specific configuration value. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters relating to multipath. +type UseMultiplePathsConfig struct { + // original -> bgp-mp:enabled + // bgp-mp:enabled's original type is boolean. + // Whether the use of multiple paths for the same NLRI is + // enabled for the neighbor. This value is overridden by + // any more specific configuration value. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` +} + +func (lhs *UseMultiplePathsConfig) Equal(rhs *UseMultiplePathsConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + return true +} + +// struct for container bgp-mp:use-multiple-paths. +// Parameters related to the use of multiple paths for the +// same NLRI. +type UseMultiplePaths struct { + // original -> bgp-mp:use-multiple-paths-config + // Configuration parameters relating to multipath. + Config UseMultiplePathsConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:use-multiple-paths-state + // State parameters relating to multipath. + State UseMultiplePathsState `mapstructure:"state" json:"state,omitempty"` + // original -> bgp-mp:ebgp + // Multipath parameters for eBGP. + Ebgp Ebgp `mapstructure:"ebgp" json:"ebgp,omitempty"` + // original -> bgp-mp:ibgp + // Multipath parameters for iBGP. + Ibgp Ibgp `mapstructure:"ibgp" json:"ibgp,omitempty"` +} + +func (lhs *UseMultiplePaths) Equal(rhs *UseMultiplePaths) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + if !lhs.Ebgp.Equal(&(rhs.Ebgp)) { + return false + } + if !lhs.Ibgp.Equal(&(rhs.Ibgp)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the BGP confederations. +type ConfederationState struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // When this leaf is set to true it indicates that + // the local-AS is part of a BGP confederation. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:identifier + // bgp:identifier's original type is inet:as-number. + // Confederation identifier for the autonomous system. + Identifier uint32 `mapstructure:"identifier" json:"identifier,omitempty"` + // original -> bgp:member-as + // original type is list of inet:as-number + // Remote autonomous systems that are to be treated + // as part of the local confederation. + MemberAsList []uint32 `mapstructure:"member-as-list" json:"member-as-list,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to BGP confederations. +type ConfederationConfig struct { + // original -> bgp:enabled + // bgp:enabled's original type is boolean. + // When this leaf is set to true it indicates that + // the local-AS is part of a BGP confederation. + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"` + // original -> bgp:identifier + // bgp:identifier's original type is inet:as-number. + // Confederation identifier for the autonomous system. + Identifier uint32 `mapstructure:"identifier" json:"identifier,omitempty"` + // original -> bgp:member-as + // original type is list of inet:as-number + // Remote autonomous systems that are to be treated + // as part of the local confederation. + MemberAsList []uint32 `mapstructure:"member-as-list" json:"member-as-list,omitempty"` +} + +func (lhs *ConfederationConfig) Equal(rhs *ConfederationConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Enabled != rhs.Enabled { + return false + } + if lhs.Identifier != rhs.Identifier { + return false + } + if len(lhs.MemberAsList) != len(rhs.MemberAsList) { + return false + } + for idx, l := range lhs.MemberAsList { + if l != rhs.MemberAsList[idx] { + return false + } + } + return true +} + +// struct for container bgp:confederation. +// Parameters indicating whether the local system acts as part +// of a BGP confederation. +type Confederation struct { + // original -> bgp:confederation-config + // Configuration parameters relating to BGP confederations. + Config ConfederationConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:confederation-state + // State information relating to the BGP confederations. + State ConfederationState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *Confederation) Equal(rhs *Confederation) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the default route distance. +type DefaultRouteDistanceState struct { + // original -> bgp:external-route-distance + // Administrative distance for routes learned from external + // BGP (eBGP). + ExternalRouteDistance uint8 `mapstructure:"external-route-distance" json:"external-route-distance,omitempty"` + // original -> bgp:internal-route-distance + // Administrative distance for routes learned from internal + // BGP (iBGP). + InternalRouteDistance uint8 `mapstructure:"internal-route-distance" json:"internal-route-distance,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to the default route +// distance. +type DefaultRouteDistanceConfig struct { + // original -> bgp:external-route-distance + // Administrative distance for routes learned from external + // BGP (eBGP). + ExternalRouteDistance uint8 `mapstructure:"external-route-distance" json:"external-route-distance,omitempty"` + // original -> bgp:internal-route-distance + // Administrative distance for routes learned from internal + // BGP (iBGP). + InternalRouteDistance uint8 `mapstructure:"internal-route-distance" json:"internal-route-distance,omitempty"` +} + +func (lhs *DefaultRouteDistanceConfig) Equal(rhs *DefaultRouteDistanceConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.ExternalRouteDistance != rhs.ExternalRouteDistance { + return false + } + if lhs.InternalRouteDistance != rhs.InternalRouteDistance { + return false + } + return true +} + +// struct for container bgp:default-route-distance. +// Administrative distance (or preference) assigned to +// routes received from different sources +// (external, internal, and local). +type DefaultRouteDistance struct { + // original -> bgp:default-route-distance-config + // Configuration parameters relating to the default route + // distance. + Config DefaultRouteDistanceConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:default-route-distance-state + // State information relating to the default route distance. + State DefaultRouteDistanceState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *DefaultRouteDistance) Equal(rhs *DefaultRouteDistance) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp-mp:state. +// State information for the route selection options. +type RouteSelectionOptionsState struct { + // original -> bgp-mp:always-compare-med + // bgp-mp:always-compare-med's original type is boolean. + // Compare multi-exit discriminator (MED) value from + // different ASes when selecting the best route. The + // default behavior is to only compare MEDs for paths + // received from the same AS. + AlwaysCompareMed bool `mapstructure:"always-compare-med" json:"always-compare-med,omitempty"` + // original -> bgp-mp:ignore-as-path-length + // bgp-mp:ignore-as-path-length's original type is boolean. + // Ignore the AS path length when selecting the best path. + // The default is to use the AS path length and prefer paths + // with shorter length. + IgnoreAsPathLength bool `mapstructure:"ignore-as-path-length" json:"ignore-as-path-length,omitempty"` + // original -> bgp-mp:external-compare-router-id + // bgp-mp:external-compare-router-id's original type is boolean. + // When comparing similar routes received from external + // BGP peers, use the router-id as a criterion to select + // the active path. + ExternalCompareRouterId bool `mapstructure:"external-compare-router-id" json:"external-compare-router-id,omitempty"` + // original -> bgp-mp:advertise-inactive-routes + // bgp-mp:advertise-inactive-routes's original type is boolean. + // Advertise inactive routes to external peers. The + // default is to only advertise active routes. + AdvertiseInactiveRoutes bool `mapstructure:"advertise-inactive-routes" json:"advertise-inactive-routes,omitempty"` + // original -> bgp-mp:enable-aigp + // bgp-mp:enable-aigp's original type is boolean. + // Flag to enable sending / receiving accumulated IGP + // attribute in routing updates. + EnableAigp bool `mapstructure:"enable-aigp" json:"enable-aigp,omitempty"` + // original -> bgp-mp:ignore-next-hop-igp-metric + // bgp-mp:ignore-next-hop-igp-metric's original type is boolean. + // Ignore the IGP metric to the next-hop when calculating + // BGP best-path. The default is to select the route for + // which the metric to the next-hop is lowest. + IgnoreNextHopIgpMetric bool `mapstructure:"ignore-next-hop-igp-metric" json:"ignore-next-hop-igp-metric,omitempty"` + // original -> gobgp:disable-best-path-selection + // gobgp:disable-best-path-selection's original type is boolean. + // Disables best path selection process. + DisableBestPathSelection bool `mapstructure:"disable-best-path-selection" json:"disable-best-path-selection,omitempty"` +} + +// struct for container bgp-mp:config. +// Configuration parameters relating to route selection +// options. +type RouteSelectionOptionsConfig struct { + // original -> bgp-mp:always-compare-med + // bgp-mp:always-compare-med's original type is boolean. + // Compare multi-exit discriminator (MED) value from + // different ASes when selecting the best route. The + // default behavior is to only compare MEDs for paths + // received from the same AS. + AlwaysCompareMed bool `mapstructure:"always-compare-med" json:"always-compare-med,omitempty"` + // original -> bgp-mp:ignore-as-path-length + // bgp-mp:ignore-as-path-length's original type is boolean. + // Ignore the AS path length when selecting the best path. + // The default is to use the AS path length and prefer paths + // with shorter length. + IgnoreAsPathLength bool `mapstructure:"ignore-as-path-length" json:"ignore-as-path-length,omitempty"` + // original -> bgp-mp:external-compare-router-id + // bgp-mp:external-compare-router-id's original type is boolean. + // When comparing similar routes received from external + // BGP peers, use the router-id as a criterion to select + // the active path. + ExternalCompareRouterId bool `mapstructure:"external-compare-router-id" json:"external-compare-router-id,omitempty"` + // original -> bgp-mp:advertise-inactive-routes + // bgp-mp:advertise-inactive-routes's original type is boolean. + // Advertise inactive routes to external peers. The + // default is to only advertise active routes. + AdvertiseInactiveRoutes bool `mapstructure:"advertise-inactive-routes" json:"advertise-inactive-routes,omitempty"` + // original -> bgp-mp:enable-aigp + // bgp-mp:enable-aigp's original type is boolean. + // Flag to enable sending / receiving accumulated IGP + // attribute in routing updates. + EnableAigp bool `mapstructure:"enable-aigp" json:"enable-aigp,omitempty"` + // original -> bgp-mp:ignore-next-hop-igp-metric + // bgp-mp:ignore-next-hop-igp-metric's original type is boolean. + // Ignore the IGP metric to the next-hop when calculating + // BGP best-path. The default is to select the route for + // which the metric to the next-hop is lowest. + IgnoreNextHopIgpMetric bool `mapstructure:"ignore-next-hop-igp-metric" json:"ignore-next-hop-igp-metric,omitempty"` + // original -> gobgp:disable-best-path-selection + // gobgp:disable-best-path-selection's original type is boolean. + // Disables best path selection process. + DisableBestPathSelection bool `mapstructure:"disable-best-path-selection" json:"disable-best-path-selection,omitempty"` +} + +func (lhs *RouteSelectionOptionsConfig) Equal(rhs *RouteSelectionOptionsConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AlwaysCompareMed != rhs.AlwaysCompareMed { + return false + } + if lhs.IgnoreAsPathLength != rhs.IgnoreAsPathLength { + return false + } + if lhs.ExternalCompareRouterId != rhs.ExternalCompareRouterId { + return false + } + if lhs.AdvertiseInactiveRoutes != rhs.AdvertiseInactiveRoutes { + return false + } + if lhs.EnableAigp != rhs.EnableAigp { + return false + } + if lhs.IgnoreNextHopIgpMetric != rhs.IgnoreNextHopIgpMetric { + return false + } + if lhs.DisableBestPathSelection != rhs.DisableBestPathSelection { + return false + } + return true +} + +// struct for container bgp-mp:route-selection-options. +// Parameters relating to options for route selection. +type RouteSelectionOptions struct { + // original -> bgp-mp:route-selection-options-config + // Configuration parameters relating to route selection + // options. + Config RouteSelectionOptionsConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp-mp:route-selection-options-state + // State information for the route selection options. + State RouteSelectionOptionsState `mapstructure:"state" json:"state,omitempty"` +} + +func (lhs *RouteSelectionOptions) Equal(rhs *RouteSelectionOptions) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + return true +} + +// struct for container bgp:state. +// State information relating to the global BGP router. +type GlobalState struct { + // original -> bgp:as + // bgp:as's original type is inet:as-number. + // Local autonomous system number of the router. Uses + // the 32-bit as-number type from the model in RFC 6991. + As uint32 `mapstructure:"as" json:"as,omitempty"` + // original -> bgp:router-id + // bgp:router-id's original type is inet:ipv4-address. + // Router id of the router, expressed as an + // 32-bit value, IPv4 address. + RouterId string `mapstructure:"router-id" json:"router-id,omitempty"` + // original -> bgp-op:total-paths + // Total number of BGP paths within the context. + TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"` + // original -> bgp-op:total-prefixes + // . + TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"` + // original -> gobgp:port + Port int32 `mapstructure:"port" json:"port,omitempty"` + // original -> gobgp:local-address + LocalAddressList []string `mapstructure:"local-address-list" json:"local-address-list,omitempty"` +} + +// struct for container bgp:config. +// Configuration parameters relating to the global BGP router. +type GlobalConfig struct { + // original -> bgp:as + // bgp:as's original type is inet:as-number. + // Local autonomous system number of the router. Uses + // the 32-bit as-number type from the model in RFC 6991. + As uint32 `mapstructure:"as" json:"as,omitempty"` + // original -> bgp:router-id + // bgp:router-id's original type is inet:ipv4-address. + // Router id of the router, expressed as an + // 32-bit value, IPv4 address. + RouterId string `mapstructure:"router-id" json:"router-id,omitempty"` + // original -> gobgp:port + Port int32 `mapstructure:"port" json:"port,omitempty"` + // original -> gobgp:local-address + LocalAddressList []string `mapstructure:"local-address-list" json:"local-address-list,omitempty"` +} + +func (lhs *GlobalConfig) Equal(rhs *GlobalConfig) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.As != rhs.As { + return false + } + if lhs.RouterId != rhs.RouterId { + return false + } + if lhs.Port != rhs.Port { + return false + } + if len(lhs.LocalAddressList) != len(rhs.LocalAddressList) { + return false + } + for idx, l := range lhs.LocalAddressList { + if l != rhs.LocalAddressList[idx] { + return false + } + } + return true +} + +// struct for container bgp:global. +// Global configuration for the BGP router. +type Global struct { + // original -> bgp:global-config + // Configuration parameters relating to the global BGP router. + Config GlobalConfig `mapstructure:"config" json:"config,omitempty"` + // original -> bgp:global-state + // State information relating to the global BGP router. + State GlobalState `mapstructure:"state" json:"state,omitempty"` + // original -> bgp-mp:route-selection-options + // Parameters relating to options for route selection. + RouteSelectionOptions RouteSelectionOptions `mapstructure:"route-selection-options" json:"route-selection-options,omitempty"` + // original -> bgp:default-route-distance + // Administrative distance (or preference) assigned to + // routes received from different sources + // (external, internal, and local). + DefaultRouteDistance DefaultRouteDistance `mapstructure:"default-route-distance" json:"default-route-distance,omitempty"` + // original -> bgp:confederation + // Parameters indicating whether the local system acts as part + // of a BGP confederation. + Confederation Confederation `mapstructure:"confederation" json:"confederation,omitempty"` + // original -> bgp-mp:use-multiple-paths + // Parameters related to the use of multiple paths for the + // same NLRI. + UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"` + // original -> bgp:graceful-restart + // Parameters relating the graceful restart mechanism for BGP. + GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"` + // original -> bgp:afi-safis + // Address family specific configuration. + AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"` + // original -> rpol:apply-policy + // Anchor point for routing policies in the model. + // Import and export policies are with respect to the local + // routing table, i.e., export (send) and import (receive), + // depending on the context. + ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"` +} + +func (lhs *Global) Equal(rhs *Global) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Config.Equal(&(rhs.Config)) { + return false + } + if !lhs.RouteSelectionOptions.Equal(&(rhs.RouteSelectionOptions)) { + return false + } + if !lhs.DefaultRouteDistance.Equal(&(rhs.DefaultRouteDistance)) { + return false + } + if !lhs.Confederation.Equal(&(rhs.Confederation)) { + return false + } + if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) { + return false + } + if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) { + return false + } + if len(lhs.AfiSafis) != len(rhs.AfiSafis) { + return false + } + { + lmap := make(map[string]*AfiSafi) + for i, l := range lhs.AfiSafis { + lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i] + } + for i, r := range rhs.AfiSafis { + if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) { + return false + } + return true +} + +// struct for container bgp:bgp. +// Top-level configuration and state for the BGP router. +type Bgp struct { + // original -> bgp:global + // Global configuration for the BGP router. + Global Global `mapstructure:"global" json:"global,omitempty"` + // original -> bgp:neighbors + // Configuration for BGP neighbors. + Neighbors []Neighbor `mapstructure:"neighbors" json:"neighbors,omitempty"` + // original -> bgp:peer-groups + // Configuration for BGP peer-groups. + PeerGroups []PeerGroup `mapstructure:"peer-groups" json:"peer-groups,omitempty"` + // original -> gobgp:rpki-servers + RpkiServers []RpkiServer `mapstructure:"rpki-servers" json:"rpki-servers,omitempty"` + // original -> gobgp:bmp-servers + BmpServers []BmpServer `mapstructure:"bmp-servers" json:"bmp-servers,omitempty"` + // original -> gobgp:vrfs + Vrfs []Vrf `mapstructure:"vrfs" json:"vrfs,omitempty"` + // original -> gobgp:mrt-dump + MrtDump []Mrt `mapstructure:"mrt-dump" json:"mrt-dump,omitempty"` + // original -> gobgp:zebra + Zebra Zebra `mapstructure:"zebra" json:"zebra,omitempty"` + // original -> gobgp:collector + Collector Collector `mapstructure:"collector" json:"collector,omitempty"` + // original -> gobgp:dynamic-neighbors + DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors" json:"dynamic-neighbors,omitempty"` +} + +func (lhs *Bgp) Equal(rhs *Bgp) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.Global.Equal(&(rhs.Global)) { + return false + } + if len(lhs.Neighbors) != len(rhs.Neighbors) { + return false + } + { + lmap := make(map[string]*Neighbor) + for i, l := range lhs.Neighbors { + lmap[mapkey(i, string(l.Config.NeighborAddress))] = &lhs.Neighbors[i] + } + for i, r := range rhs.Neighbors { + if l, y := lmap[mapkey(i, string(r.Config.NeighborAddress))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.PeerGroups) != len(rhs.PeerGroups) { + return false + } + { + lmap := make(map[string]*PeerGroup) + for i, l := range lhs.PeerGroups { + lmap[mapkey(i, string(l.Config.PeerGroupName))] = &lhs.PeerGroups[i] + } + for i, r := range rhs.PeerGroups { + if l, y := lmap[mapkey(i, string(r.Config.PeerGroupName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.RpkiServers) != len(rhs.RpkiServers) { + return false + } + { + lmap := make(map[string]*RpkiServer) + for i, l := range lhs.RpkiServers { + lmap[mapkey(i, string(l.Config.Address))] = &lhs.RpkiServers[i] + } + for i, r := range rhs.RpkiServers { + if l, y := lmap[mapkey(i, string(r.Config.Address))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.BmpServers) != len(rhs.BmpServers) { + return false + } + { + lmap := make(map[string]*BmpServer) + for i, l := range lhs.BmpServers { + lmap[mapkey(i, string(l.Config.Address))] = &lhs.BmpServers[i] + } + for i, r := range rhs.BmpServers { + if l, y := lmap[mapkey(i, string(r.Config.Address))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.Vrfs) != len(rhs.Vrfs) { + return false + } + { + lmap := make(map[string]*Vrf) + for i, l := range lhs.Vrfs { + lmap[mapkey(i, string(l.Config.Name))] = &lhs.Vrfs[i] + } + for i, r := range rhs.Vrfs { + if l, y := lmap[mapkey(i, string(r.Config.Name))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.MrtDump) != len(rhs.MrtDump) { + return false + } + { + lmap := make(map[string]*Mrt) + for i, l := range lhs.MrtDump { + lmap[mapkey(i, string(l.Config.FileName))] = &lhs.MrtDump[i] + } + for i, r := range rhs.MrtDump { + if l, y := lmap[mapkey(i, string(r.Config.FileName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if !lhs.Zebra.Equal(&(rhs.Zebra)) { + return false + } + if !lhs.Collector.Equal(&(rhs.Collector)) { + return false + } + if len(lhs.DynamicNeighbors) != len(rhs.DynamicNeighbors) { + return false + } + { + lmap := make(map[string]*DynamicNeighbor) + for i, l := range lhs.DynamicNeighbors { + lmap[mapkey(i, string(l.Config.Prefix))] = &lhs.DynamicNeighbors[i] + } + for i, r := range rhs.DynamicNeighbors { + if l, y := lmap[mapkey(i, string(r.Config.Prefix))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} + +// struct for container gobgp:set-large-community-method. +type SetLargeCommunityMethod struct { + // original -> gobgp:communities + CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"` +} + +func (lhs *SetLargeCommunityMethod) Equal(rhs *SetLargeCommunityMethod) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) { + return false + } + for idx, l := range lhs.CommunitiesList { + if l != rhs.CommunitiesList[idx] { + return false + } + } + return true +} + +// struct for container gobgp:set-large-community. +type SetLargeCommunity struct { + // original -> gobgp:set-large-community-method + SetLargeCommunityMethod SetLargeCommunityMethod `mapstructure:"set-large-community-method" json:"set-large-community-method,omitempty"` + // original -> gobgp:options + Options BgpSetCommunityOptionType `mapstructure:"options" json:"options,omitempty"` +} + +func (lhs *SetLargeCommunity) Equal(rhs *SetLargeCommunity) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.SetLargeCommunityMethod.Equal(&(rhs.SetLargeCommunityMethod)) { + return false + } + if lhs.Options != rhs.Options { + return false + } + return true +} + +// struct for container bgp-pol:set-ext-community-method. +// Option to set communities using an inline list or +// reference to an existing defined set. +type SetExtCommunityMethod struct { + // original -> bgp-pol:communities + // original type is list of union + // Set the community values for the update inline with + // a list. + CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"` + // original -> bgp-pol:ext-community-set-ref + // References a defined extended community set by + // name. + ExtCommunitySetRef string `mapstructure:"ext-community-set-ref" json:"ext-community-set-ref,omitempty"` +} + +func (lhs *SetExtCommunityMethod) Equal(rhs *SetExtCommunityMethod) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) { + return false + } + for idx, l := range lhs.CommunitiesList { + if l != rhs.CommunitiesList[idx] { + return false + } + } + if lhs.ExtCommunitySetRef != rhs.ExtCommunitySetRef { + return false + } + return true +} + +// struct for container bgp-pol:set-ext-community. +// Action to set the extended community attributes of the +// route, along with options to modify how the community is +// modified. +type SetExtCommunity struct { + // original -> bgp-pol:set-ext-community-method + // Option to set communities using an inline list or + // reference to an existing defined set. + SetExtCommunityMethod SetExtCommunityMethod `mapstructure:"set-ext-community-method" json:"set-ext-community-method,omitempty"` + // original -> bgp-pol:options + // bgp-pol:options's original type is bgp-set-community-option-type. + // options for modifying the extended community + // attribute with the specified values. These options + // apply to both methods of setting the community + // attribute. + Options string `mapstructure:"options" json:"options,omitempty"` +} + +func (lhs *SetExtCommunity) Equal(rhs *SetExtCommunity) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.SetExtCommunityMethod.Equal(&(rhs.SetExtCommunityMethod)) { + return false + } + if lhs.Options != rhs.Options { + return false + } + return true +} + +// struct for container bgp-pol:set-community-method. +// Option to set communities using an inline list or +// reference to an existing defined set. +type SetCommunityMethod struct { + // original -> bgp-pol:communities + // original type is list of union + // Set the community values for the update inline with + // a list. + CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"` + // original -> bgp-pol:community-set-ref + // References a defined community set by name. + CommunitySetRef string `mapstructure:"community-set-ref" json:"community-set-ref,omitempty"` +} + +func (lhs *SetCommunityMethod) Equal(rhs *SetCommunityMethod) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) { + return false + } + for idx, l := range lhs.CommunitiesList { + if l != rhs.CommunitiesList[idx] { + return false + } + } + if lhs.CommunitySetRef != rhs.CommunitySetRef { + return false + } + return true +} + +// struct for container bgp-pol:set-community. +// action to set the community attributes of the route, along +// with options to modify how the community is modified. +type SetCommunity struct { + // original -> bgp-pol:set-community-method + // Option to set communities using an inline list or + // reference to an existing defined set. + SetCommunityMethod SetCommunityMethod `mapstructure:"set-community-method" json:"set-community-method,omitempty"` + // original -> bgp-pol:options + // bgp-pol:options's original type is bgp-set-community-option-type. + // Options for modifying the community attribute with + // the specified values. These options apply to both + // methods of setting the community attribute. + Options string `mapstructure:"options" json:"options,omitempty"` +} + +func (lhs *SetCommunity) Equal(rhs *SetCommunity) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.SetCommunityMethod.Equal(&(rhs.SetCommunityMethod)) { + return false + } + if lhs.Options != rhs.Options { + return false + } + return true +} + +// struct for container bgp-pol:set-as-path-prepend. +// action to prepend local AS number to the AS-path a +// specified number of times. +type SetAsPathPrepend struct { + // original -> bgp-pol:repeat-n + // number of times to prepend the local AS + // number. + RepeatN uint8 `mapstructure:"repeat-n" json:"repeat-n,omitempty"` + // original -> gobgp:as + // gobgp:as's original type is union. + // autonomous system number or 'last-as' which means + // the leftmost as number in the AS-path to be prepended. + As string `mapstructure:"as" json:"as,omitempty"` +} + +func (lhs *SetAsPathPrepend) Equal(rhs *SetAsPathPrepend) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.RepeatN != rhs.RepeatN { + return false + } + if lhs.As != rhs.As { + return false + } + return true +} + +// struct for container bgp-pol:bgp-actions. +// Definitions for policy action statements that +// change BGP-specific attributes of the route. +type BgpActions struct { + // original -> bgp-pol:set-as-path-prepend + // action to prepend local AS number to the AS-path a + // specified number of times. + SetAsPathPrepend SetAsPathPrepend `mapstructure:"set-as-path-prepend" json:"set-as-path-prepend,omitempty"` + // original -> bgp-pol:set-community + // action to set the community attributes of the route, along + // with options to modify how the community is modified. + SetCommunity SetCommunity `mapstructure:"set-community" json:"set-community,omitempty"` + // original -> bgp-pol:set-ext-community + // Action to set the extended community attributes of the + // route, along with options to modify how the community is + // modified. + SetExtCommunity SetExtCommunity `mapstructure:"set-ext-community" json:"set-ext-community,omitempty"` + // original -> bgp-pol:set-route-origin + // set the origin attribute to the specified + // value. + SetRouteOrigin BgpOriginAttrType `mapstructure:"set-route-origin" json:"set-route-origin,omitempty"` + // original -> bgp-pol:set-local-pref + // set the local pref attribute on the route + // update. + SetLocalPref uint32 `mapstructure:"set-local-pref" json:"set-local-pref,omitempty"` + // original -> bgp-pol:set-next-hop + // set the next-hop attribute in the route update. + SetNextHop BgpNextHopType `mapstructure:"set-next-hop" json:"set-next-hop,omitempty"` + // original -> bgp-pol:set-med + // set the med metric attribute in the route + // update. + SetMed BgpSetMedType `mapstructure:"set-med" json:"set-med,omitempty"` + // original -> gobgp:set-large-community + SetLargeCommunity SetLargeCommunity `mapstructure:"set-large-community" json:"set-large-community,omitempty"` +} + +func (lhs *BgpActions) Equal(rhs *BgpActions) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.SetAsPathPrepend.Equal(&(rhs.SetAsPathPrepend)) { + return false + } + if !lhs.SetCommunity.Equal(&(rhs.SetCommunity)) { + return false + } + if !lhs.SetExtCommunity.Equal(&(rhs.SetExtCommunity)) { + return false + } + if lhs.SetRouteOrigin != rhs.SetRouteOrigin { + return false + } + if lhs.SetLocalPref != rhs.SetLocalPref { + return false + } + if lhs.SetNextHop != rhs.SetNextHop { + return false + } + if lhs.SetMed != rhs.SetMed { + return false + } + if !lhs.SetLargeCommunity.Equal(&(rhs.SetLargeCommunity)) { + return false + } + return true +} + +// struct for container rpol:igp-actions. +// Actions to set IGP route attributes; these actions +// apply to multiple IGPs. +type IgpActions struct { + // original -> rpol:set-tag + // Set the tag value for OSPF or IS-IS routes. + SetTag TagType `mapstructure:"set-tag" json:"set-tag,omitempty"` +} + +func (lhs *IgpActions) Equal(rhs *IgpActions) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.SetTag != rhs.SetTag { + return false + } + return true +} + +// struct for container rpol:actions. +// Action statements for this policy +// statement. +type Actions struct { + // original -> rpol:route-disposition + // Select the final disposition for the route, either + // accept or reject. + RouteDisposition RouteDisposition `mapstructure:"route-disposition" json:"route-disposition,omitempty"` + // original -> rpol:igp-actions + // Actions to set IGP route attributes; these actions + // apply to multiple IGPs. + IgpActions IgpActions `mapstructure:"igp-actions" json:"igp-actions,omitempty"` + // original -> bgp-pol:bgp-actions + // Definitions for policy action statements that + // change BGP-specific attributes of the route. + BgpActions BgpActions `mapstructure:"bgp-actions" json:"bgp-actions,omitempty"` +} + +func (lhs *Actions) Equal(rhs *Actions) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.RouteDisposition != rhs.RouteDisposition { + return false + } + if !lhs.IgpActions.Equal(&(rhs.IgpActions)) { + return false + } + if !lhs.BgpActions.Equal(&(rhs.BgpActions)) { + return false + } + return true +} + +// struct for container gobgp:match-large-community-set. +type MatchLargeCommunitySet struct { + // original -> gobgp:large-community-set + LargeCommunitySet string `mapstructure:"large-community-set" json:"large-community-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. + MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchLargeCommunitySet) Equal(rhs *MatchLargeCommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.LargeCommunitySet != rhs.LargeCommunitySet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container bgp-pol:as-path-length. +// Value and comparison operations for conditions based on the +// length of the AS path in the route update. +type AsPathLength struct { + // original -> ptypes:operator + // type of comparison to be performed. + Operator AttributeComparison `mapstructure:"operator" json:"operator,omitempty"` + // original -> ptypes:value + // value to compare with the community count. + Value uint32 `mapstructure:"value" json:"value,omitempty"` +} + +func (lhs *AsPathLength) Equal(rhs *AsPathLength) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Operator != rhs.Operator { + return false + } + if lhs.Value != rhs.Value { + return false + } + return true +} + +// struct for container bgp-pol:community-count. +// Value and comparison operations for conditions based on the +// number of communities in the route update. +type CommunityCount struct { + // original -> ptypes:operator + // type of comparison to be performed. + Operator AttributeComparison `mapstructure:"operator" json:"operator,omitempty"` + // original -> ptypes:value + // value to compare with the community count. + Value uint32 `mapstructure:"value" json:"value,omitempty"` +} + +func (lhs *CommunityCount) Equal(rhs *CommunityCount) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Operator != rhs.Operator { + return false + } + if lhs.Value != rhs.Value { + return false + } + return true +} + +// struct for container bgp-pol:match-as-path-set. +// Match a referenced as-path set according to the logic +// defined in the match-set-options leaf. +type MatchAsPathSet struct { + // original -> bgp-pol:as-path-set + // References a defined AS path set. + AsPathSet string `mapstructure:"as-path-set" json:"as-path-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. + MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchAsPathSet) Equal(rhs *MatchAsPathSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AsPathSet != rhs.AsPathSet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container bgp-pol:match-ext-community-set. +// Match a referenced extended community-set according to the +// logic defined in the match-set-options leaf. +type MatchExtCommunitySet struct { + // original -> bgp-pol:ext-community-set + // References a defined extended community set. + ExtCommunitySet string `mapstructure:"ext-community-set" json:"ext-community-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. + MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchExtCommunitySet) Equal(rhs *MatchExtCommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.ExtCommunitySet != rhs.ExtCommunitySet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container bgp-pol:match-community-set. +// Match a referenced community-set according to the logic +// defined in the match-set-options leaf. +type MatchCommunitySet struct { + // original -> bgp-pol:community-set + // References a defined community set. + CommunitySet string `mapstructure:"community-set" json:"community-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. + MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchCommunitySet) Equal(rhs *MatchCommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.CommunitySet != rhs.CommunitySet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container bgp-pol:bgp-conditions. +// Policy conditions for matching +// BGP-specific defined sets or comparing BGP-specific +// attributes. +type BgpConditions struct { + // original -> bgp-pol:match-community-set + // Match a referenced community-set according to the logic + // defined in the match-set-options leaf. + MatchCommunitySet MatchCommunitySet `mapstructure:"match-community-set" json:"match-community-set,omitempty"` + // original -> bgp-pol:match-ext-community-set + // Match a referenced extended community-set according to the + // logic defined in the match-set-options leaf. + MatchExtCommunitySet MatchExtCommunitySet `mapstructure:"match-ext-community-set" json:"match-ext-community-set,omitempty"` + // original -> bgp-pol:match-as-path-set + // Match a referenced as-path set according to the logic + // defined in the match-set-options leaf. + MatchAsPathSet MatchAsPathSet `mapstructure:"match-as-path-set" json:"match-as-path-set,omitempty"` + // original -> bgp-pol:med-eq + // Condition to check if the received MED value is equal to + // the specified value. + MedEq uint32 `mapstructure:"med-eq" json:"med-eq,omitempty"` + // original -> bgp-pol:origin-eq + // Condition to check if the route origin is equal to the + // specified value. + OriginEq BgpOriginAttrType `mapstructure:"origin-eq" json:"origin-eq,omitempty"` + // original -> bgp-pol:next-hop-in + // original type is list of inet:ip-address + // List of next hop addresses to check for in the route + // update. + NextHopInList []string `mapstructure:"next-hop-in-list" json:"next-hop-in-list,omitempty"` + // original -> bgp-pol:afi-safi-in + // List of address families which the NLRI may be + // within. + AfiSafiInList []AfiSafiType `mapstructure:"afi-safi-in-list" json:"afi-safi-in-list,omitempty"` + // original -> bgp-pol:local-pref-eq + // Condition to check if the local pref attribute is equal to + // the specified value. + LocalPrefEq uint32 `mapstructure:"local-pref-eq" json:"local-pref-eq,omitempty"` + // original -> bgp-pol:community-count + // Value and comparison operations for conditions based on the + // number of communities in the route update. + CommunityCount CommunityCount `mapstructure:"community-count" json:"community-count,omitempty"` + // original -> bgp-pol:as-path-length + // Value and comparison operations for conditions based on the + // length of the AS path in the route update. + AsPathLength AsPathLength `mapstructure:"as-path-length" json:"as-path-length,omitempty"` + // original -> bgp-pol:route-type + // Condition to check the route type in the route update. + RouteType RouteType `mapstructure:"route-type" json:"route-type,omitempty"` + // original -> gobgp:rpki-validation-result + // specify the validation result of RPKI based on ROA as conditions. + RpkiValidationResult RpkiValidationResultType `mapstructure:"rpki-validation-result" json:"rpki-validation-result,omitempty"` + // original -> gobgp:match-large-community-set + MatchLargeCommunitySet MatchLargeCommunitySet `mapstructure:"match-large-community-set" json:"match-large-community-set,omitempty"` +} + +func (lhs *BgpConditions) Equal(rhs *BgpConditions) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.MatchCommunitySet.Equal(&(rhs.MatchCommunitySet)) { + return false + } + if !lhs.MatchExtCommunitySet.Equal(&(rhs.MatchExtCommunitySet)) { + return false + } + if !lhs.MatchAsPathSet.Equal(&(rhs.MatchAsPathSet)) { + return false + } + if lhs.MedEq != rhs.MedEq { + return false + } + if lhs.OriginEq != rhs.OriginEq { + return false + } + if len(lhs.NextHopInList) != len(rhs.NextHopInList) { + return false + } + for idx, l := range lhs.NextHopInList { + if l != rhs.NextHopInList[idx] { + return false + } + } + if len(lhs.AfiSafiInList) != len(rhs.AfiSafiInList) { + return false + } + for idx, l := range lhs.AfiSafiInList { + if l != rhs.AfiSafiInList[idx] { + return false + } + } + if lhs.LocalPrefEq != rhs.LocalPrefEq { + return false + } + if !lhs.CommunityCount.Equal(&(rhs.CommunityCount)) { + return false + } + if !lhs.AsPathLength.Equal(&(rhs.AsPathLength)) { + return false + } + if lhs.RouteType != rhs.RouteType { + return false + } + if lhs.RpkiValidationResult != rhs.RpkiValidationResult { + return false + } + if !lhs.MatchLargeCommunitySet.Equal(&(rhs.MatchLargeCommunitySet)) { + return false + } + return true +} + +// struct for container rpol:igp-conditions. +// Policy conditions for IGP attributes. +type IgpConditions struct { +} + +func (lhs *IgpConditions) Equal(rhs *IgpConditions) bool { + if lhs == nil || rhs == nil { + return false + } + return true +} + +// struct for container rpol:match-tag-set. +// Match a referenced tag set according to the logic defined +// in the match-options-set leaf. +type MatchTagSet struct { + // original -> rpol:tag-set + // References a defined tag set. + TagSet string `mapstructure:"tag-set" json:"tag-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. This leaf only supports matching on ANY + // member of the set or inverting the match. Matching on ALL is + // not supported). + MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchTagSet) Equal(rhs *MatchTagSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.TagSet != rhs.TagSet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container rpol:match-neighbor-set. +// Match a referenced neighbor set according to the logic +// defined in the match-set-options-leaf. +type MatchNeighborSet struct { + // original -> rpol:neighbor-set + // References a defined neighbor set. + NeighborSet string `mapstructure:"neighbor-set" json:"neighbor-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. This leaf only supports matching on ANY + // member of the set or inverting the match. Matching on ALL is + // not supported). + MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchNeighborSet) Equal(rhs *MatchNeighborSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.NeighborSet != rhs.NeighborSet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container rpol:match-prefix-set. +// Match a referenced prefix-set according to the logic +// defined in the match-set-options leaf. +type MatchPrefixSet struct { + // original -> rpol:prefix-set + // References a defined prefix set. + PrefixSet string `mapstructure:"prefix-set" json:"prefix-set,omitempty"` + // original -> rpol:match-set-options + // Optional parameter that governs the behaviour of the + // match operation. This leaf only supports matching on ANY + // member of the set or inverting the match. Matching on ALL is + // not supported). + MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"` +} + +func (lhs *MatchPrefixSet) Equal(rhs *MatchPrefixSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.PrefixSet != rhs.PrefixSet { + return false + } + if lhs.MatchSetOptions != rhs.MatchSetOptions { + return false + } + return true +} + +// struct for container rpol:conditions. +// Condition statements for this +// policy statement. +type Conditions struct { + // original -> rpol:call-policy + // Applies the statements from the specified policy + // definition and then returns control the current + // policy statement. Note that the called policy may + // itself call other policies (subject to + // implementation limitations). This is intended to + // provide a policy 'subroutine' capability. The + // called policy should contain an explicit or a + // default route disposition that returns an + // effective true (accept-route) or false + // (reject-route), otherwise the behavior may be + // ambiguous and implementation dependent. + CallPolicy string `mapstructure:"call-policy" json:"call-policy,omitempty"` + // original -> rpol:match-prefix-set + // Match a referenced prefix-set according to the logic + // defined in the match-set-options leaf. + MatchPrefixSet MatchPrefixSet `mapstructure:"match-prefix-set" json:"match-prefix-set,omitempty"` + // original -> rpol:match-neighbor-set + // Match a referenced neighbor set according to the logic + // defined in the match-set-options-leaf. + MatchNeighborSet MatchNeighborSet `mapstructure:"match-neighbor-set" json:"match-neighbor-set,omitempty"` + // original -> rpol:match-tag-set + // Match a referenced tag set according to the logic defined + // in the match-options-set leaf. + MatchTagSet MatchTagSet `mapstructure:"match-tag-set" json:"match-tag-set,omitempty"` + // original -> rpol:install-protocol-eq + // Condition to check the protocol / method used to install + // which installed the route into the local routing table. + InstallProtocolEq InstallProtocolType `mapstructure:"install-protocol-eq" json:"install-protocol-eq,omitempty"` + // original -> rpol:igp-conditions + // Policy conditions for IGP attributes. + IgpConditions IgpConditions `mapstructure:"igp-conditions" json:"igp-conditions,omitempty"` + // original -> bgp-pol:bgp-conditions + // Policy conditions for matching + // BGP-specific defined sets or comparing BGP-specific + // attributes. + BgpConditions BgpConditions `mapstructure:"bgp-conditions" json:"bgp-conditions,omitempty"` +} + +func (lhs *Conditions) Equal(rhs *Conditions) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.CallPolicy != rhs.CallPolicy { + return false + } + if !lhs.MatchPrefixSet.Equal(&(rhs.MatchPrefixSet)) { + return false + } + if !lhs.MatchNeighborSet.Equal(&(rhs.MatchNeighborSet)) { + return false + } + if !lhs.MatchTagSet.Equal(&(rhs.MatchTagSet)) { + return false + } + if lhs.InstallProtocolEq != rhs.InstallProtocolEq { + return false + } + if !lhs.IgpConditions.Equal(&(rhs.IgpConditions)) { + return false + } + if !lhs.BgpConditions.Equal(&(rhs.BgpConditions)) { + return false + } + return true +} + +// struct for container rpol:statement. +// Policy statements group conditions and actions +// within a policy definition. They are evaluated in +// the order specified (see the description of policy +// evaluation at the top of this module. +type Statement struct { + // original -> rpol:name + // name of the policy statement. + Name string `mapstructure:"name" json:"name,omitempty"` + // original -> rpol:conditions + // Condition statements for this + // policy statement. + Conditions Conditions `mapstructure:"conditions" json:"conditions,omitempty"` + // original -> rpol:actions + // Action statements for this policy + // statement. + Actions Actions `mapstructure:"actions" json:"actions,omitempty"` +} + +func (lhs *Statement) Equal(rhs *Statement) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Name != rhs.Name { + return false + } + if !lhs.Conditions.Equal(&(rhs.Conditions)) { + return false + } + if !lhs.Actions.Equal(&(rhs.Actions)) { + return false + } + return true +} + +// struct for container rpol:policy-definition. +// List of top-level policy definitions, keyed by unique +// name. These policy definitions are expected to be +// referenced (by name) in policy chains specified in import +// or export configuration statements. +type PolicyDefinition struct { + // original -> rpol:name + // Name of the top-level policy definition -- this name + // is used in references to the current policy. + Name string `mapstructure:"name" json:"name,omitempty"` + // original -> rpol:statements + // Enclosing container for policy statements. + Statements []Statement `mapstructure:"statements" json:"statements,omitempty"` +} + +func (lhs *PolicyDefinition) Equal(rhs *PolicyDefinition) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Name != rhs.Name { + return false + } + if len(lhs.Statements) != len(rhs.Statements) { + return false + } + { + lmap := make(map[string]*Statement) + for i, l := range lhs.Statements { + lmap[mapkey(i, string(l.Name))] = &lhs.Statements[i] + } + for i, r := range rhs.Statements { + if l, y := lmap[mapkey(i, string(r.Name))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} + +// struct for container gobgp:large-community-set. +type LargeCommunitySet struct { + // original -> gobgp:large-community-set-name + LargeCommunitySetName string `mapstructure:"large-community-set-name" json:"large-community-set-name,omitempty"` + // original -> gobgp:large-community + // extended community set member. + LargeCommunityList []string `mapstructure:"large-community-list" json:"large-community-list,omitempty"` +} + +func (lhs *LargeCommunitySet) Equal(rhs *LargeCommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.LargeCommunitySetName != rhs.LargeCommunitySetName { + return false + } + if len(lhs.LargeCommunityList) != len(rhs.LargeCommunityList) { + return false + } + for idx, l := range lhs.LargeCommunityList { + if l != rhs.LargeCommunityList[idx] { + return false + } + } + return true +} + +// struct for container bgp-pol:as-path-set. +// Definitions for AS path sets. +type AsPathSet struct { + // original -> bgp-pol:as-path-set-name + // name of the AS path set -- this is used to reference + // the set in match conditions. + AsPathSetName string `mapstructure:"as-path-set-name" json:"as-path-set-name,omitempty"` + // original -> gobgp:as-path + // AS path expression. + AsPathList []string `mapstructure:"as-path-list" json:"as-path-list,omitempty"` +} + +func (lhs *AsPathSet) Equal(rhs *AsPathSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.AsPathSetName != rhs.AsPathSetName { + return false + } + if len(lhs.AsPathList) != len(rhs.AsPathList) { + return false + } + for idx, l := range lhs.AsPathList { + if l != rhs.AsPathList[idx] { + return false + } + } + return true +} + +// struct for container bgp-pol:ext-community-set. +// Definitions for extended community sets. +type ExtCommunitySet struct { + // original -> bgp-pol:ext-community-set-name + // name / label of the extended community set -- this is + // used to reference the set in match conditions. + ExtCommunitySetName string `mapstructure:"ext-community-set-name" json:"ext-community-set-name,omitempty"` + // original -> gobgp:ext-community + // extended community set member. + ExtCommunityList []string `mapstructure:"ext-community-list" json:"ext-community-list,omitempty"` +} + +func (lhs *ExtCommunitySet) Equal(rhs *ExtCommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.ExtCommunitySetName != rhs.ExtCommunitySetName { + return false + } + if len(lhs.ExtCommunityList) != len(rhs.ExtCommunityList) { + return false + } + for idx, l := range lhs.ExtCommunityList { + if l != rhs.ExtCommunityList[idx] { + return false + } + } + return true +} + +// struct for container bgp-pol:community-set. +// Definitions for community sets. +type CommunitySet struct { + // original -> bgp-pol:community-set-name + // name / label of the community set -- this is used to + // reference the set in match conditions. + CommunitySetName string `mapstructure:"community-set-name" json:"community-set-name,omitempty"` + // original -> gobgp:community + // community set member. + CommunityList []string `mapstructure:"community-list" json:"community-list,omitempty"` +} + +func (lhs *CommunitySet) Equal(rhs *CommunitySet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.CommunitySetName != rhs.CommunitySetName { + return false + } + if len(lhs.CommunityList) != len(rhs.CommunityList) { + return false + } + for idx, l := range lhs.CommunityList { + if l != rhs.CommunityList[idx] { + return false + } + } + return true +} + +// struct for container bgp-pol:bgp-defined-sets. +// BGP-related set definitions for policy match conditions. +type BgpDefinedSets struct { + // original -> bgp-pol:community-sets + // Enclosing container for community sets. + CommunitySets []CommunitySet `mapstructure:"community-sets" json:"community-sets,omitempty"` + // original -> bgp-pol:ext-community-sets + // Enclosing container for extended community sets. + ExtCommunitySets []ExtCommunitySet `mapstructure:"ext-community-sets" json:"ext-community-sets,omitempty"` + // original -> bgp-pol:as-path-sets + // Enclosing container for AS path sets. + AsPathSets []AsPathSet `mapstructure:"as-path-sets" json:"as-path-sets,omitempty"` + // original -> gobgp:large-community-sets + LargeCommunitySets []LargeCommunitySet `mapstructure:"large-community-sets" json:"large-community-sets,omitempty"` +} + +func (lhs *BgpDefinedSets) Equal(rhs *BgpDefinedSets) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.CommunitySets) != len(rhs.CommunitySets) { + return false + } + { + lmap := make(map[string]*CommunitySet) + for i, l := range lhs.CommunitySets { + lmap[mapkey(i, string(l.CommunitySetName))] = &lhs.CommunitySets[i] + } + for i, r := range rhs.CommunitySets { + if l, y := lmap[mapkey(i, string(r.CommunitySetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.ExtCommunitySets) != len(rhs.ExtCommunitySets) { + return false + } + { + lmap := make(map[string]*ExtCommunitySet) + for i, l := range lhs.ExtCommunitySets { + lmap[mapkey(i, string(l.ExtCommunitySetName))] = &lhs.ExtCommunitySets[i] + } + for i, r := range rhs.ExtCommunitySets { + if l, y := lmap[mapkey(i, string(r.ExtCommunitySetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.AsPathSets) != len(rhs.AsPathSets) { + return false + } + { + lmap := make(map[string]*AsPathSet) + for i, l := range lhs.AsPathSets { + lmap[mapkey(i, string(l.AsPathSetName))] = &lhs.AsPathSets[i] + } + for i, r := range rhs.AsPathSets { + if l, y := lmap[mapkey(i, string(r.AsPathSetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.LargeCommunitySets) != len(rhs.LargeCommunitySets) { + return false + } + { + lmap := make(map[string]*LargeCommunitySet) + for i, l := range lhs.LargeCommunitySets { + lmap[mapkey(i, string(l.LargeCommunitySetName))] = &lhs.LargeCommunitySets[i] + } + for i, r := range rhs.LargeCommunitySets { + if l, y := lmap[mapkey(i, string(r.LargeCommunitySetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} + +// struct for container rpol:tag. +// list of tags that are part of the tag set. +type Tag struct { + // original -> rpol:value + // Value of the tag set member. + Value TagType `mapstructure:"value" json:"value,omitempty"` +} + +func (lhs *Tag) Equal(rhs *Tag) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.Value != rhs.Value { + return false + } + return true +} + +// struct for container rpol:tag-set. +// Definitions for tag sets. +type TagSet struct { + // original -> rpol:tag-set-name + // name / label of the tag set -- this is used to reference + // the set in match conditions. + TagSetName string `mapstructure:"tag-set-name" json:"tag-set-name,omitempty"` + // original -> rpol:tag + // list of tags that are part of the tag set. + TagList []Tag `mapstructure:"tag-list" json:"tag-list,omitempty"` +} + +func (lhs *TagSet) Equal(rhs *TagSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.TagSetName != rhs.TagSetName { + return false + } + if len(lhs.TagList) != len(rhs.TagList) { + return false + } + { + lmap := make(map[string]*Tag) + for i, l := range lhs.TagList { + lmap[mapkey(i, string(l.Value))] = &lhs.TagList[i] + } + for i, r := range rhs.TagList { + if l, y := lmap[mapkey(i, string(r.Value))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} + +// struct for container rpol:neighbor-set. +// Definitions for neighbor sets. +type NeighborSet struct { + // original -> rpol:neighbor-set-name + // name / label of the neighbor set -- this is used to + // reference the set in match conditions. + NeighborSetName string `mapstructure:"neighbor-set-name" json:"neighbor-set-name,omitempty"` + // original -> gobgp:neighbor-info + // original type is list of inet:ip-address + // neighbor ip address or prefix. + NeighborInfoList []string `mapstructure:"neighbor-info-list" json:"neighbor-info-list,omitempty"` +} + +func (lhs *NeighborSet) Equal(rhs *NeighborSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.NeighborSetName != rhs.NeighborSetName { + return false + } + if len(lhs.NeighborInfoList) != len(rhs.NeighborInfoList) { + return false + } + for idx, l := range lhs.NeighborInfoList { + if l != rhs.NeighborInfoList[idx] { + return false + } + } + return true +} + +// struct for container rpol:prefix. +// List of prefix expressions that are part of the set. +type Prefix struct { + // original -> rpol:ip-prefix + // rpol:ip-prefix's original type is inet:ip-prefix. + // The prefix member in CIDR notation -- while the + // prefix may be either IPv4 or IPv6, most + // implementations require all members of the prefix set + // to be the same address family. Mixing address types in + // the same prefix set is likely to cause an error. + IpPrefix string `mapstructure:"ip-prefix" json:"ip-prefix,omitempty"` + // original -> rpol:masklength-range + // Defines a range for the masklength, or 'exact' if + // the prefix has an exact length. + // + // Example: 10.3.192.0/21 through 10.3.192.0/24 would be + // expressed as prefix: 10.3.192.0/21, + // masklength-range: 21..24. + // + // Example: 10.3.192.0/21 would be expressed as + // prefix: 10.3.192.0/21, + // masklength-range: exact. + MasklengthRange string `mapstructure:"masklength-range" json:"masklength-range,omitempty"` +} + +func (lhs *Prefix) Equal(rhs *Prefix) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.IpPrefix != rhs.IpPrefix { + return false + } + if lhs.MasklengthRange != rhs.MasklengthRange { + return false + } + return true +} + +// struct for container rpol:prefix-set. +// List of the defined prefix sets. +type PrefixSet struct { + // original -> rpol:prefix-set-name + // name / label of the prefix set -- this is used to + // reference the set in match conditions. + PrefixSetName string `mapstructure:"prefix-set-name" json:"prefix-set-name,omitempty"` + // original -> rpol:prefix + // List of prefix expressions that are part of the set. + PrefixList []Prefix `mapstructure:"prefix-list" json:"prefix-list,omitempty"` +} + +func (lhs *PrefixSet) Equal(rhs *PrefixSet) bool { + if lhs == nil || rhs == nil { + return false + } + if lhs.PrefixSetName != rhs.PrefixSetName { + return false + } + if len(lhs.PrefixList) != len(rhs.PrefixList) { + return false + } + { + lmap := make(map[string]*Prefix) + for i, l := range lhs.PrefixList { + lmap[mapkey(i, string(l.IpPrefix+l.MasklengthRange))] = &lhs.PrefixList[i] + } + for i, r := range rhs.PrefixList { + if l, y := lmap[mapkey(i, string(r.IpPrefix+r.MasklengthRange))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} + +// struct for container rpol:defined-sets. +// Predefined sets of attributes used in policy match +// statements. +type DefinedSets struct { + // original -> rpol:prefix-sets + // Enclosing container for defined prefix sets for matching. + PrefixSets []PrefixSet `mapstructure:"prefix-sets" json:"prefix-sets,omitempty"` + // original -> rpol:neighbor-sets + // Enclosing container for defined neighbor sets for matching. + NeighborSets []NeighborSet `mapstructure:"neighbor-sets" json:"neighbor-sets,omitempty"` + // original -> rpol:tag-sets + // Enclosing container for defined tag sets for matching. + TagSets []TagSet `mapstructure:"tag-sets" json:"tag-sets,omitempty"` + // original -> bgp-pol:bgp-defined-sets + // BGP-related set definitions for policy match conditions. + BgpDefinedSets BgpDefinedSets `mapstructure:"bgp-defined-sets" json:"bgp-defined-sets,omitempty"` +} + +func (lhs *DefinedSets) Equal(rhs *DefinedSets) bool { + if lhs == nil || rhs == nil { + return false + } + if len(lhs.PrefixSets) != len(rhs.PrefixSets) { + return false + } + { + lmap := make(map[string]*PrefixSet) + for i, l := range lhs.PrefixSets { + lmap[mapkey(i, string(l.PrefixSetName))] = &lhs.PrefixSets[i] + } + for i, r := range rhs.PrefixSets { + if l, y := lmap[mapkey(i, string(r.PrefixSetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.NeighborSets) != len(rhs.NeighborSets) { + return false + } + { + lmap := make(map[string]*NeighborSet) + for i, l := range lhs.NeighborSets { + lmap[mapkey(i, string(l.NeighborSetName))] = &lhs.NeighborSets[i] + } + for i, r := range rhs.NeighborSets { + if l, y := lmap[mapkey(i, string(r.NeighborSetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if len(lhs.TagSets) != len(rhs.TagSets) { + return false + } + { + lmap := make(map[string]*TagSet) + for i, l := range lhs.TagSets { + lmap[mapkey(i, string(l.TagSetName))] = &lhs.TagSets[i] + } + for i, r := range rhs.TagSets { + if l, y := lmap[mapkey(i, string(r.TagSetName))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + if !lhs.BgpDefinedSets.Equal(&(rhs.BgpDefinedSets)) { + return false + } + return true +} + +// struct for container rpol:routing-policy. +// top-level container for all routing policy configuration. +type RoutingPolicy struct { + // original -> rpol:defined-sets + // Predefined sets of attributes used in policy match + // statements. + DefinedSets DefinedSets `mapstructure:"defined-sets" json:"defined-sets,omitempty"` + // original -> rpol:policy-definitions + // Enclosing container for the list of top-level policy + // definitions. + PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions" json:"policy-definitions,omitempty"` +} + +func (lhs *RoutingPolicy) Equal(rhs *RoutingPolicy) bool { + if lhs == nil || rhs == nil { + return false + } + if !lhs.DefinedSets.Equal(&(rhs.DefinedSets)) { + return false + } + if len(lhs.PolicyDefinitions) != len(rhs.PolicyDefinitions) { + return false + } + { + lmap := make(map[string]*PolicyDefinition) + for i, l := range lhs.PolicyDefinitions { + lmap[mapkey(i, string(l.Name))] = &lhs.PolicyDefinitions[i] + } + for i, r := range rhs.PolicyDefinitions { + if l, y := lmap[mapkey(i, string(r.Name))]; !y { + return false + } else if !r.Equal(l) { + return false + } + } + } + return true +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/default.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/default.go new file mode 100644 index 0000000..1f4105c --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/default.go @@ -0,0 +1,524 @@ +package config + +import ( + "encoding/binary" + "fmt" + "math" + "net" + "reflect" + "strconv" + + "github.com/osrg/gobgp/pkg/packet/bgp" + "github.com/osrg/gobgp/pkg/packet/bmp" + "github.com/osrg/gobgp/pkg/packet/rtr" + "github.com/spf13/viper" +) + +const ( + DEFAULT_HOLDTIME = 90 + DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30 + DEFAULT_CONNECT_RETRY = 120 +) + +var forcedOverwrittenConfig = []string{ + "neighbor.config.peer-as", + "neighbor.timers.config.minimum-advertisement-interval", +} + +var configuredFields map[string]interface{} + +func RegisterConfiguredFields(addr string, n interface{}) { + if configuredFields == nil { + configuredFields = make(map[string]interface{}) + } + configuredFields[addr] = n +} + +func defaultAfiSafi(typ AfiSafiType, enable bool) AfiSafi { + return AfiSafi{ + Config: AfiSafiConfig{ + AfiSafiName: typ, + Enabled: enable, + }, + State: AfiSafiState{ + AfiSafiName: typ, + Family: bgp.AddressFamilyValueMap[string(typ)], + }, + } +} + +func SetDefaultNeighborConfigValues(n *Neighbor, pg *PeerGroup, g *Global) error { + // Determines this function is called against the same Neighbor struct, + // and if already called, returns immediately. + if n.State.LocalAs != 0 { + return nil + } + + return setDefaultNeighborConfigValuesWithViper(nil, n, g, pg) +} + +func setDefaultNeighborConfigValuesWithViper(v *viper.Viper, n *Neighbor, g *Global, pg *PeerGroup) error { + if n == nil { + return fmt.Errorf("neighbor config is nil") + } + if g == nil { + return fmt.Errorf("global config is nil") + } + + if v == nil { + v = viper.New() + } + + if pg != nil { + if err := OverwriteNeighborConfigWithPeerGroup(n, pg); err != nil { + return err + } + } + + if n.Config.LocalAs == 0 { + n.Config.LocalAs = g.Config.As + if !g.Confederation.Config.Enabled || n.IsConfederation(g) { + n.Config.LocalAs = g.Config.As + } else { + n.Config.LocalAs = g.Confederation.Config.Identifier + } + } + n.State.LocalAs = n.Config.LocalAs + + if n.Config.PeerAs != n.Config.LocalAs { + n.Config.PeerType = PEER_TYPE_EXTERNAL + n.State.PeerType = PEER_TYPE_EXTERNAL + n.State.RemovePrivateAs = n.Config.RemovePrivateAs + n.AsPathOptions.State.ReplacePeerAs = n.AsPathOptions.Config.ReplacePeerAs + } else { + n.Config.PeerType = PEER_TYPE_INTERNAL + n.State.PeerType = PEER_TYPE_INTERNAL + if string(n.Config.RemovePrivateAs) != "" { + return fmt.Errorf("can't set remove-private-as for iBGP peer") + } + if n.AsPathOptions.Config.ReplacePeerAs { + return fmt.Errorf("can't set replace-peer-as for iBGP peer") + } + } + + if n.State.NeighborAddress == "" { + n.State.NeighborAddress = n.Config.NeighborAddress + } + + n.State.PeerAs = n.Config.PeerAs + n.AsPathOptions.State.AllowOwnAs = n.AsPathOptions.Config.AllowOwnAs + + if !v.IsSet("neighbor.error-handling.config.treat-as-withdraw") { + n.ErrorHandling.Config.TreatAsWithdraw = true + } + + if !v.IsSet("neighbor.timers.config.connect-retry") && n.Timers.Config.ConnectRetry == 0 { + n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY) + } + if !v.IsSet("neighbor.timers.config.hold-time") && n.Timers.Config.HoldTime == 0 { + n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME) + } + if !v.IsSet("neighbor.timers.config.keepalive-interval") && n.Timers.Config.KeepaliveInterval == 0 { + n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3 + } + if !v.IsSet("neighbor.timers.config.idle-hold-time-after-reset") && n.Timers.Config.IdleHoldTimeAfterReset == 0 { + n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET) + } + + if n.Config.NeighborInterface != "" { + if n.RouteServer.Config.RouteServerClient { + return fmt.Errorf("configuring route server client as unnumbered peer is not supported") + } + addr, err := GetIPv6LinkLocalNeighborAddress(n.Config.NeighborInterface) + if err != nil { + return err + } + n.State.NeighborAddress = addr + } + + if n.Transport.Config.LocalAddress == "" { + if n.State.NeighborAddress == "" { + return fmt.Errorf("no neighbor address/interface specified") + } + ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress) + if err != nil { + return err + } + localAddress := "0.0.0.0" + if ipAddr.IP.To4() == nil { + localAddress = "::" + if ipAddr.Zone != "" { + localAddress, err = getIPv6LinkLocalAddress(ipAddr.Zone) + if err != nil { + return err + } + } + } + n.Transport.Config.LocalAddress = localAddress + } + + if len(n.AfiSafis) == 0 { + if n.Config.NeighborInterface != "" { + n.AfiSafis = []AfiSafi{ + defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true), + defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true), + } + } else if ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress); err != nil { + return fmt.Errorf("invalid neighbor address: %s", n.State.NeighborAddress) + } else if ipAddr.IP.To4() != nil { + n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)} + } else { + n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)} + } + for i := range n.AfiSafis { + n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive + n.AfiSafis[i].AddPaths.State.Receive = n.AddPaths.Config.Receive + n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax + n.AfiSafis[i].AddPaths.State.SendMax = n.AddPaths.Config.SendMax + } + } else { + afs, err := extractArray(v.Get("neighbor.afi-safis")) + if err != nil { + return err + } + for i := range n.AfiSafis { + vv := viper.New() + if len(afs) > i { + vv.Set("afi-safi", afs[i]) + } + rf, err := bgp.GetRouteFamily(string(n.AfiSafis[i].Config.AfiSafiName)) + if err != nil { + return err + } + n.AfiSafis[i].State.Family = rf + n.AfiSafis[i].State.AfiSafiName = n.AfiSafis[i].Config.AfiSafiName + if !vv.IsSet("afi-safi.config.enabled") { + n.AfiSafis[i].Config.Enabled = true + } + n.AfiSafis[i].MpGracefulRestart.State.Enabled = n.AfiSafis[i].MpGracefulRestart.Config.Enabled + if !vv.IsSet("afi-safi.add-paths.config.receive") { + n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive + } + n.AfiSafis[i].AddPaths.State.Receive = n.AfiSafis[i].AddPaths.Config.Receive + if !vv.IsSet("afi-safi.add-paths.config.send-max") { + n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax + } + n.AfiSafis[i].AddPaths.State.SendMax = n.AfiSafis[i].AddPaths.Config.SendMax + } + } + + n.State.Description = n.Config.Description + n.State.AdminDown = n.Config.AdminDown + + if n.GracefulRestart.Config.Enabled { + if !v.IsSet("neighbor.graceful-restart.config.restart-time") && n.GracefulRestart.Config.RestartTime == 0 { + // RFC 4724 4. Operation + // A suggested default for the Restart Time is a value less than or + // equal to the HOLDTIME carried in the OPEN. + n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime) + } + if !v.IsSet("neighbor.graceful-restart.config.deferral-time") && n.GracefulRestart.Config.DeferralTime == 0 { + // RFC 4724 4.1. Procedures for the Restarting Speaker + // The value of this timer should be large + // enough, so as to provide all the peers of the Restarting Speaker with + // enough time to send all the routes to the Restarting Speaker + n.GracefulRestart.Config.DeferralTime = uint16(360) + } + } + + if n.EbgpMultihop.Config.Enabled { + if n.TtlSecurity.Config.Enabled { + return fmt.Errorf("ebgp-multihop and ttl-security are mututally exclusive") + } + if n.EbgpMultihop.Config.MultihopTtl == 0 { + n.EbgpMultihop.Config.MultihopTtl = 255 + } + } else if n.TtlSecurity.Config.Enabled { + if n.TtlSecurity.Config.TtlMin == 0 { + n.TtlSecurity.Config.TtlMin = 255 + } + } + + if n.RouteReflector.Config.RouteReflectorClient { + if n.RouteReflector.Config.RouteReflectorClusterId == "" { + n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(g.Config.RouterId) + } else { + id := string(n.RouteReflector.Config.RouteReflectorClusterId) + if ip := net.ParseIP(id).To4(); ip != nil { + n.RouteReflector.State.RouteReflectorClusterId = n.RouteReflector.Config.RouteReflectorClusterId + } else if num, err := strconv.ParseUint(id, 10, 32); err == nil { + ip = make(net.IP, 4) + binary.BigEndian.PutUint32(ip, uint32(num)) + n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(ip.String()) + } else { + return fmt.Errorf("route-reflector-cluster-id should be specified as IPv4 address or 32-bit unsigned integer") + } + } + } + + return nil +} + +func SetDefaultGlobalConfigValues(g *Global) error { + if len(g.AfiSafis) == 0 { + g.AfiSafis = []AfiSafi{} + for k := range AfiSafiTypeToIntMap { + g.AfiSafis = append(g.AfiSafis, defaultAfiSafi(k, true)) + } + } + + if g.Config.Port == 0 { + g.Config.Port = bgp.BGP_PORT + } + + if len(g.Config.LocalAddressList) == 0 { + g.Config.LocalAddressList = []string{"0.0.0.0", "::"} + } + return nil +} + +func setDefaultVrfConfigValues(v *Vrf) error { + if v == nil { + return fmt.Errorf("cannot set default values for nil vrf config") + } + + if v.Config.Name == "" { + return fmt.Errorf("specify vrf name") + } + + _, err := bgp.ParseRouteDistinguisher(v.Config.Rd) + if err != nil { + return fmt.Errorf("invalid rd for vrf %s: %s", v.Config.Name, v.Config.Rd) + } + + if len(v.Config.ImportRtList) == 0 { + v.Config.ImportRtList = v.Config.BothRtList + } + for _, rtString := range v.Config.ImportRtList { + _, err := bgp.ParseRouteTarget(rtString) + if err != nil { + return fmt.Errorf("invalid import rt for vrf %s: %s", v.Config.Name, rtString) + } + } + + if len(v.Config.ExportRtList) == 0 { + v.Config.ExportRtList = v.Config.BothRtList + } + for _, rtString := range v.Config.ExportRtList { + _, err := bgp.ParseRouteTarget(rtString) + if err != nil { + return fmt.Errorf("invalid export rt for vrf %s: %s", v.Config.Name, rtString) + } + } + + return nil +} + +func SetDefaultConfigValues(b *BgpConfigSet) error { + return setDefaultConfigValuesWithViper(nil, b) +} + +func setDefaultPolicyConfigValuesWithViper(v *viper.Viper, p *PolicyDefinition) error { + stmts, err := extractArray(v.Get("policy.statements")) + if err != nil { + return err + } + for i := range p.Statements { + vv := viper.New() + if len(stmts) > i { + vv.Set("statement", stmts[i]) + } + if !vv.IsSet("statement.actions.route-disposition") { + p.Statements[i].Actions.RouteDisposition = ROUTE_DISPOSITION_NONE + } + } + return nil +} + +func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error { + if v == nil { + v = viper.New() + } + + if err := SetDefaultGlobalConfigValues(&b.Global); err != nil { + return err + } + + for idx, server := range b.BmpServers { + if server.Config.Port == 0 { + server.Config.Port = bmp.BMP_DEFAULT_PORT + } + if server.Config.RouteMonitoringPolicy == "" { + server.Config.RouteMonitoringPolicy = BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY + } + // statistics-timeout is uint16 value and implicitly less than 65536 + if server.Config.StatisticsTimeout != 0 && server.Config.StatisticsTimeout < 15 { + return fmt.Errorf("too small statistics-timeout value: %d", server.Config.StatisticsTimeout) + } + b.BmpServers[idx] = server + } + + vrfNames := make(map[string]struct{}) + vrfIDs := make(map[uint32]struct{}) + for idx, vrf := range b.Vrfs { + if err := setDefaultVrfConfigValues(&vrf); err != nil { + return err + } + + if _, ok := vrfNames[vrf.Config.Name]; ok { + return fmt.Errorf("duplicated vrf name: %s", vrf.Config.Name) + } + vrfNames[vrf.Config.Name] = struct{}{} + + if vrf.Config.Id != 0 { + if _, ok := vrfIDs[vrf.Config.Id]; ok { + return fmt.Errorf("duplicated vrf id: %d", vrf.Config.Id) + } + vrfIDs[vrf.Config.Id] = struct{}{} + } + + b.Vrfs[idx] = vrf + } + // Auto assign VRF identifier + for idx, vrf := range b.Vrfs { + if vrf.Config.Id == 0 { + for id := uint32(1); id < math.MaxUint32; id++ { + if _, ok := vrfIDs[id]; !ok { + vrf.Config.Id = id + vrfIDs[id] = struct{}{} + break + } + } + } + b.Vrfs[idx] = vrf + } + + if b.Zebra.Config.Url == "" { + b.Zebra.Config.Url = "unix:/var/run/quagga/zserv.api" + } + if b.Zebra.Config.Version < 2 { + b.Zebra.Config.Version = 2 + } else if b.Zebra.Config.Version > 5 { + b.Zebra.Config.Version = 5 + } + if !v.IsSet("zebra.config.nexthop-trigger-enable") && !b.Zebra.Config.NexthopTriggerEnable && b.Zebra.Config.Version > 2 { + b.Zebra.Config.NexthopTriggerEnable = true + } + if b.Zebra.Config.NexthopTriggerDelay == 0 { + b.Zebra.Config.NexthopTriggerDelay = 5 + } + + list, err := extractArray(v.Get("neighbors")) + if err != nil { + return err + } + + for idx, n := range b.Neighbors { + vv := viper.New() + if len(list) > idx { + vv.Set("neighbor", list[idx]) + } + + pg, err := b.getPeerGroup(n.Config.PeerGroup) + if err != nil { + return nil + } + + if pg != nil { + identifier := vv.Get("neighbor.config.neighbor-address") + if identifier == nil { + identifier = vv.Get("neighbor.config.neighbor-interface") + } + RegisterConfiguredFields(identifier.(string), list[idx]) + } + + if err := setDefaultNeighborConfigValuesWithViper(vv, &n, &b.Global, pg); err != nil { + return err + } + b.Neighbors[idx] = n + } + + for _, d := range b.DynamicNeighbors { + if err := d.validate(b); err != nil { + return err + } + } + + for idx, r := range b.RpkiServers { + if r.Config.Port == 0 { + b.RpkiServers[idx].Config.Port = rtr.RPKI_DEFAULT_PORT + } + } + + list, err = extractArray(v.Get("policy-definitions")) + if err != nil { + return err + } + + for idx, p := range b.PolicyDefinitions { + vv := viper.New() + if len(list) > idx { + vv.Set("policy", list[idx]) + } + if err := setDefaultPolicyConfigValuesWithViper(vv, &p); err != nil { + return err + } + b.PolicyDefinitions[idx] = p + } + + return nil +} + +func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error { + v := viper.New() + + val, ok := configuredFields[c.Config.NeighborAddress] + if ok { + v.Set("neighbor", val) + } else { + v.Set("neighbor.config.peer-group", c.Config.PeerGroup) + } + + overwriteConfig(&c.Config, &pg.Config, "neighbor.config", v) + overwriteConfig(&c.Timers.Config, &pg.Timers.Config, "neighbor.timers.config", v) + overwriteConfig(&c.Transport.Config, &pg.Transport.Config, "neighbor.transport.config", v) + overwriteConfig(&c.ErrorHandling.Config, &pg.ErrorHandling.Config, "neighbor.error-handling.config", v) + overwriteConfig(&c.LoggingOptions.Config, &pg.LoggingOptions.Config, "neighbor.logging-options.config", v) + overwriteConfig(&c.EbgpMultihop.Config, &pg.EbgpMultihop.Config, "neighbor.ebgp-multihop.config", v) + overwriteConfig(&c.RouteReflector.Config, &pg.RouteReflector.Config, "neighbor.route-reflector.config", v) + overwriteConfig(&c.AsPathOptions.Config, &pg.AsPathOptions.Config, "neighbor.as-path-options.config", v) + overwriteConfig(&c.AddPaths.Config, &pg.AddPaths.Config, "neighbor.add-paths.config", v) + overwriteConfig(&c.GracefulRestart.Config, &pg.GracefulRestart.Config, "neighbor.gradeful-restart.config", v) + overwriteConfig(&c.ApplyPolicy.Config, &pg.ApplyPolicy.Config, "neighbor.apply-policy.config", v) + overwriteConfig(&c.UseMultiplePaths.Config, &pg.UseMultiplePaths.Config, "neighbor.use-multiple-paths.config", v) + overwriteConfig(&c.RouteServer.Config, &pg.RouteServer.Config, "neighbor.route-server.config", v) + overwriteConfig(&c.TtlSecurity.Config, &pg.TtlSecurity.Config, "neighbor.ttl-security.config", v) + + if !v.IsSet("neighbor.afi-safis") { + c.AfiSafis = append(c.AfiSafis, pg.AfiSafis...) + } + + return nil +} + +func overwriteConfig(c, pg interface{}, tagPrefix string, v *viper.Viper) { + nValue := reflect.Indirect(reflect.ValueOf(c)) + nType := reflect.Indirect(nValue).Type() + pgValue := reflect.Indirect(reflect.ValueOf(pg)) + pgType := reflect.Indirect(pgValue).Type() + + for i := 0; i < pgType.NumField(); i++ { + field := pgType.Field(i).Name + tag := tagPrefix + "." + nType.Field(i).Tag.Get("mapstructure") + if func() bool { + for _, t := range forcedOverwrittenConfig { + if t == tag { + return true + } + } + return false + }() || !v.IsSet(tag) { + nValue.FieldByName(field).Set(pgValue.FieldByName(field)) + } + } +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/default_linux.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/default_linux.go new file mode 100644 index 0000000..8cfcc50 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/default_linux.go @@ -0,0 +1,72 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 linux + +package config + +import ( + "fmt" + "net" + + "github.com/vishvananda/netlink" +) + +func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) { + ifi, err := net.InterfaceByName(ifname) + if err != nil { + return "", err + } + neighs, err := netlink.NeighList(ifi.Index, netlink.FAMILY_V6) + if err != nil { + return "", err + } + cnt := 0 + var addr net.IP + for _, neigh := range neighs { + local, err := isLocalLinkLocalAddress(ifi.Index, neigh.IP) + if err != nil { + return "", err + } + if neigh.State&netlink.NUD_FAILED == 0 && neigh.IP.IsLinkLocalUnicast() && !local { + addr = neigh.IP + cnt++ + } + } + + if cnt == 0 { + return "", fmt.Errorf("no ipv6 link-local neighbor found") + } else if cnt > 1 { + return "", fmt.Errorf("found %d link-local neighbors. only support p2p link", cnt) + } + + return fmt.Sprintf("%s%%%s", addr, ifname), nil +} + +func isLocalLinkLocalAddress(ifindex int, addr net.IP) (bool, error) { + ifi, err := net.InterfaceByIndex(ifindex) + if err != nil { + return false, err + } + addrs, err := ifi.Addrs() + if err != nil { + return false, err + } + for _, a := range addrs { + if ip, _, _ := net.ParseCIDR(a.String()); addr.Equal(ip) { + return true, nil + } + } + return false, nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/default_nonlinux.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/default_nonlinux.go new file mode 100644 index 0000000..fe4705a --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/default_nonlinux.go @@ -0,0 +1,25 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 !linux + +package config + +import ( + "fmt" +) + +func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) { + return "", fmt.Errorf("unnumbered peering is not supported") +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/serve.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/serve.go new file mode 100644 index 0000000..19b731b --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/serve.go @@ -0,0 +1,159 @@ +package config + +import ( + "os" + "os/signal" + "syscall" + + log "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +type BgpConfigSet struct { + Global Global `mapstructure:"global"` + Neighbors []Neighbor `mapstructure:"neighbors"` + PeerGroups []PeerGroup `mapstructure:"peer-groups"` + RpkiServers []RpkiServer `mapstructure:"rpki-servers"` + BmpServers []BmpServer `mapstructure:"bmp-servers"` + Vrfs []Vrf `mapstructure:"vrfs"` + MrtDump []Mrt `mapstructure:"mrt-dump"` + Zebra Zebra `mapstructure:"zebra"` + Collector Collector `mapstructure:"collector"` + DefinedSets DefinedSets `mapstructure:"defined-sets"` + PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions"` + DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors"` +} + +func ReadConfigfileServe(path, format string, configCh chan *BgpConfigSet) { + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGHUP) + + // Update config file type, if detectable + format = detectConfigFileType(path, format) + + cnt := 0 + for { + c := &BgpConfigSet{} + v := viper.New() + v.SetConfigFile(path) + v.SetConfigType(format) + var err error + if err = v.ReadInConfig(); err != nil { + goto ERROR + } + if err = v.UnmarshalExact(c); err != nil { + goto ERROR + } + if err = setDefaultConfigValuesWithViper(v, c); err != nil { + goto ERROR + } + if cnt == 0 { + log.WithFields(log.Fields{ + "Topic": "Config", + }).Info("Finished reading the config file") + } + cnt++ + configCh <- c + goto NEXT + ERROR: + if cnt == 0 { + log.WithFields(log.Fields{ + "Topic": "Config", + "Error": err, + }).Fatalf("Can't read config file %s", path) + } else { + log.WithFields(log.Fields{ + "Topic": "Config", + "Error": err, + }).Warningf("Can't read config file %s", path) + } + NEXT: + <-sigCh + log.WithFields(log.Fields{ + "Topic": "Config", + }).Info("Reload the config file") + } +} + +func ConfigSetToRoutingPolicy(c *BgpConfigSet) *RoutingPolicy { + return &RoutingPolicy{ + DefinedSets: c.DefinedSets, + PolicyDefinitions: c.PolicyDefinitions, + } +} + +func UpdatePeerGroupConfig(curC, newC *BgpConfigSet) ([]PeerGroup, []PeerGroup, []PeerGroup) { + addedPg := []PeerGroup{} + deletedPg := []PeerGroup{} + updatedPg := []PeerGroup{} + + for _, n := range newC.PeerGroups { + if idx := existPeerGroup(n.Config.PeerGroupName, curC.PeerGroups); idx < 0 { + addedPg = append(addedPg, n) + } else if !n.Equal(&curC.PeerGroups[idx]) { + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("Current peer-group config:%v", curC.PeerGroups[idx]) + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("New peer-group config:%v", n) + updatedPg = append(updatedPg, n) + } + } + + for _, n := range curC.PeerGroups { + if existPeerGroup(n.Config.PeerGroupName, newC.PeerGroups) < 0 { + deletedPg = append(deletedPg, n) + } + } + return addedPg, deletedPg, updatedPg +} + +func UpdateNeighborConfig(curC, newC *BgpConfigSet) ([]Neighbor, []Neighbor, []Neighbor) { + added := []Neighbor{} + deleted := []Neighbor{} + updated := []Neighbor{} + + for _, n := range newC.Neighbors { + if idx := inSlice(n, curC.Neighbors); idx < 0 { + added = append(added, n) + } else if !n.Equal(&curC.Neighbors[idx]) { + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("Current neighbor config:%v", curC.Neighbors[idx]) + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("New neighbor config:%v", n) + updated = append(updated, n) + } + } + + for _, n := range curC.Neighbors { + if inSlice(n, newC.Neighbors) < 0 { + deleted = append(deleted, n) + } + } + return added, deleted, updated +} + +func CheckPolicyDifference(currentPolicy *RoutingPolicy, newPolicy *RoutingPolicy) bool { + + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("Current policy:%v", currentPolicy) + log.WithFields(log.Fields{ + "Topic": "Config", + }).Debugf("New policy:%v", newPolicy) + + var result bool + if currentPolicy == nil && newPolicy == nil { + result = false + } else { + if currentPolicy != nil && newPolicy != nil { + result = !currentPolicy.Equal(newPolicy) + } else { + result = true + } + } + return result +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/config/util.go b/vendor/github.com/osrg/gobgp/internal/pkg/config/util.go new file mode 100644 index 0000000..591252b --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/config/util.go @@ -0,0 +1,264 @@ +// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 config + +import ( + "fmt" + "net" + "path/filepath" + "regexp" + "strconv" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +// Returns config file type by retrieving extension from the given path. +// If no corresponding type found, returns the given def as the default value. +func detectConfigFileType(path, def string) string { + switch ext := filepath.Ext(path); ext { + case ".toml": + return "toml" + case ".yaml", ".yml": + return "yaml" + case ".json": + return "json" + default: + return def + } +} + +// yaml is decoded as []interface{} +// but toml is decoded as []map[string]interface{}. +// currently, viper can't hide this difference. +// handle the difference here. +func extractArray(intf interface{}) ([]interface{}, error) { + if intf != nil { + list, ok := intf.([]interface{}) + if ok { + return list, nil + } + l, ok := intf.([]map[string]interface{}) + if !ok { + return nil, fmt.Errorf("invalid configuration: neither []interface{} nor []map[string]interface{}") + } + list = make([]interface{}, 0, len(l)) + for _, m := range l { + list = append(list, m) + } + return list, nil + } + return nil, nil +} + +func getIPv6LinkLocalAddress(ifname string) (string, error) { + ifi, err := net.InterfaceByName(ifname) + if err != nil { + return "", err + } + addrs, err := ifi.Addrs() + if err != nil { + return "", err + } + for _, addr := range addrs { + ip := addr.(*net.IPNet).IP + if ip.To4() == nil && ip.IsLinkLocalUnicast() { + return fmt.Sprintf("%s%%%s", ip.String(), ifname), nil + } + } + return "", fmt.Errorf("no ipv6 link local address for %s", ifname) +} + +func (b *BgpConfigSet) getPeerGroup(n string) (*PeerGroup, error) { + if n == "" { + return nil, nil + } + for _, pg := range b.PeerGroups { + if n == pg.Config.PeerGroupName { + return &pg, nil + } + } + return nil, fmt.Errorf("no such peer-group: %s", n) +} + +func (d *DynamicNeighbor) validate(b *BgpConfigSet) error { + if d.Config.PeerGroup == "" { + return fmt.Errorf("dynamic neighbor requires the peer group config") + } + + if _, err := b.getPeerGroup(d.Config.PeerGroup); err != nil { + return err + } + if _, _, err := net.ParseCIDR(d.Config.Prefix); err != nil { + return fmt.Errorf("invalid dynamic neighbor prefix %s", d.Config.Prefix) + } + return nil +} + +func (n *Neighbor) IsConfederationMember(g *Global) bool { + for _, member := range g.Confederation.Config.MemberAsList { + if member == n.Config.PeerAs { + return true + } + } + return false +} + +func (n *Neighbor) IsConfederation(g *Global) bool { + if n.Config.PeerAs == g.Config.As { + return true + } + return n.IsConfederationMember(g) +} + +func (n *Neighbor) IsEBGPPeer(g *Global) bool { + return n.Config.PeerAs != g.Config.As +} + +func (n *Neighbor) CreateRfMap() map[bgp.RouteFamily]bgp.BGPAddPathMode { + rfMap := make(map[bgp.RouteFamily]bgp.BGPAddPathMode) + for _, af := range n.AfiSafis { + mode := bgp.BGP_ADD_PATH_NONE + if af.AddPaths.State.Receive { + mode |= bgp.BGP_ADD_PATH_RECEIVE + } + if af.AddPaths.State.SendMax > 0 { + mode |= bgp.BGP_ADD_PATH_SEND + } + rfMap[af.State.Family] = mode + } + return rfMap +} + +func (n *Neighbor) GetAfiSafi(family bgp.RouteFamily) *AfiSafi { + for _, a := range n.AfiSafis { + if string(a.Config.AfiSafiName) == family.String() { + return &a + } + } + return nil +} + +func (n *Neighbor) ExtractNeighborAddress() (string, error) { + addr := n.State.NeighborAddress + if addr == "" { + addr = n.Config.NeighborAddress + if addr == "" { + return "", fmt.Errorf("NeighborAddress is not configured") + } + } + return addr, nil +} + +func (n *Neighbor) IsAddPathReceiveEnabled(family bgp.RouteFamily) bool { + for _, af := range n.AfiSafis { + if af.State.Family == family { + return af.AddPaths.State.Receive + } + } + return false +} + +type AfiSafis []AfiSafi + +func (c AfiSafis) ToRfList() ([]bgp.RouteFamily, error) { + rfs := make([]bgp.RouteFamily, 0, len(c)) + for _, af := range c { + rfs = append(rfs, af.State.Family) + } + return rfs, nil +} + +func inSlice(n Neighbor, b []Neighbor) int { + for i, nb := range b { + if nb.State.NeighborAddress == n.State.NeighborAddress { + return i + } + } + return -1 +} + +func existPeerGroup(n string, b []PeerGroup) int { + for i, nb := range b { + if nb.Config.PeerGroupName == n { + return i + } + } + return -1 +} + +func isAfiSafiChanged(x, y []AfiSafi) bool { + if len(x) != len(y) { + return true + } + m := make(map[string]bool) + for _, e := range x { + m[string(e.Config.AfiSafiName)] = true + } + for _, e := range y { + if !m[string(e.Config.AfiSafiName)] { + return true + } + } + return false +} + +func (n *Neighbor) NeedsResendOpenMessage(new *Neighbor) bool { + return !n.Config.Equal(&new.Config) || + !n.Transport.Config.Equal(&new.Transport.Config) || + !n.AddPaths.Config.Equal(&new.AddPaths.Config) || + !n.GracefulRestart.Config.Equal(&new.GracefulRestart.Config) || + isAfiSafiChanged(n.AfiSafis, new.AfiSafis) +} + +// TODO: these regexp are duplicated in api +var _regexpPrefixMaskLengthRange = regexp.MustCompile(`(\d+)\.\.(\d+)`) + +func ParseMaskLength(prefix, mask string) (int, int, error) { + _, ipNet, err := net.ParseCIDR(prefix) + if err != nil { + return 0, 0, fmt.Errorf("invalid prefix: %s", prefix) + } + if mask == "" { + l, _ := ipNet.Mask.Size() + return l, l, nil + } + elems := _regexpPrefixMaskLengthRange.FindStringSubmatch(mask) + if len(elems) != 3 { + return 0, 0, fmt.Errorf("invalid mask length range: %s", mask) + } + // we've already checked the range is sane by regexp + min, _ := strconv.ParseUint(elems[1], 10, 8) + max, _ := strconv.ParseUint(elems[2], 10, 8) + if min > max { + return 0, 0, fmt.Errorf("invalid mask length range: %s", mask) + } + if ipv4 := ipNet.IP.To4(); ipv4 != nil { + f := func(i uint64) bool { + return i <= 32 + } + if !f(min) || !f(max) { + return 0, 0, fmt.Errorf("ipv4 mask length range outside scope :%s", mask) + } + } else { + f := func(i uint64) bool { + return i <= 128 + } + if !f(min) || !f(max) { + return 0, 0, fmt.Errorf("ipv6 mask length range outside scope :%s", mask) + } + } + return int(min), int(max), nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/adj.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/adj.go new file mode 100644 index 0000000..95fbf6a --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/adj.go @@ -0,0 +1,186 @@ +// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "fmt" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +type AdjRib struct { + accepted map[bgp.RouteFamily]int + table map[bgp.RouteFamily]map[string]*Path +} + +func NewAdjRib(rfList []bgp.RouteFamily) *AdjRib { + table := make(map[bgp.RouteFamily]map[string]*Path) + for _, rf := range rfList { + table[rf] = make(map[string]*Path) + } + return &AdjRib{ + table: table, + accepted: make(map[bgp.RouteFamily]int), + } +} + +func (adj *AdjRib) Update(pathList []*Path) { + for _, path := range pathList { + if path == nil || path.IsEOR() { + continue + } + rf := path.GetRouteFamily() + key := fmt.Sprintf("%d:%s", path.GetNlri().PathIdentifier(), path.getPrefix()) + + old, found := adj.table[rf][key] + if path.IsWithdraw { + if found { + delete(adj.table[rf], key) + if !old.IsAsLooped() { + adj.accepted[rf]-- + } + } + } else { + if found { + if old.IsAsLooped() && !path.IsAsLooped() { + adj.accepted[rf]++ + } else if !old.IsAsLooped() && path.IsAsLooped() { + adj.accepted[rf]-- + } + } else { + if !path.IsAsLooped() { + adj.accepted[rf]++ + } + } + if found && old.Equal(path) { + path.setTimestamp(old.GetTimestamp()) + } + adj.table[rf][key] = path + } + } +} + +func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path { + pathList := make([]*Path, 0, adj.Count(rfList)) + for _, rf := range rfList { + for _, rr := range adj.table[rf] { + if accepted && rr.IsAsLooped() { + continue + } + pathList = append(pathList, rr) + } + } + return pathList +} + +func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int { + count := 0 + for _, rf := range rfList { + if table, ok := adj.table[rf]; ok { + count += len(table) + } + } + return count +} + +func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int { + count := 0 + for _, rf := range rfList { + if n, ok := adj.accepted[rf]; ok { + count += n + } + } + return count +} + +func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) { + for _, rf := range rfList { + if _, ok := adj.table[rf]; ok { + adj.table[rf] = make(map[string]*Path) + adj.accepted[rf] = 0 + } + } +} + +func (adj *AdjRib) DropStale(rfList []bgp.RouteFamily) []*Path { + pathList := make([]*Path, 0, adj.Count(rfList)) + for _, rf := range rfList { + if table, ok := adj.table[rf]; ok { + for k, p := range table { + if p.IsStale() { + delete(table, k) + if !p.IsAsLooped() { + adj.accepted[rf]-- + } + pathList = append(pathList, p.Clone(true)) + } + } + } + } + return pathList +} + +func (adj *AdjRib) StaleAll(rfList []bgp.RouteFamily) []*Path { + pathList := make([]*Path, 0) + for _, rf := range rfList { + if table, ok := adj.table[rf]; ok { + l := make([]*Path, 0, len(table)) + for k, p := range table { + n := p.Clone(false) + n.MarkStale(true) + table[k] = n + l = append(l, n) + } + if len(l) > 0 { + pathList = append(pathList, l...) + } + } + } + return pathList +} + +func (adj *AdjRib) Select(family bgp.RouteFamily, accepted bool, option ...TableSelectOption) (*Table, error) { + m := make(map[string][]*Path) + pl := adj.PathList([]bgp.RouteFamily{family}, accepted) + for _, path := range pl { + key := path.GetNlri().String() + if _, y := m[key]; y { + m[key] = append(m[key], path) + } else { + m[key] = []*Path{path} + } + } + d := make([]*Destination, 0, len(pl)) + for _, l := range m { + d = append(d, NewDestination(l[0].GetNlri(), 0, l...)) + } + tbl := NewTable(family, d...) + option = append(option, TableSelectOption{adj: true}) + return tbl.Select(option...) +} + +func (adj *AdjRib) TableInfo(family bgp.RouteFamily) (*TableInfo, error) { + if _, ok := adj.table[family]; !ok { + return nil, fmt.Errorf("%s unsupported", family) + } + c := adj.Count([]bgp.RouteFamily{family}) + a := adj.Accepted([]bgp.RouteFamily{family}) + return &TableInfo{ + NumDestination: c, + NumPath: c, + NumAccepted: a, + }, nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/destination.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/destination.go new file mode 100644 index 0000000..fa61572 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/destination.go @@ -0,0 +1,1041 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "net" + "sort" + + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/pkg/packet/bgp" + + log "github.com/sirupsen/logrus" +) + +var SelectionOptions config.RouteSelectionOptionsConfig +var UseMultiplePaths config.UseMultiplePathsConfig + +type BestPathReason uint8 + +const ( + BPR_UNKNOWN BestPathReason = iota + BPR_DISABLED + BPR_ONLY_PATH + BPR_REACHABLE_NEXT_HOP + BPR_HIGHEST_WEIGHT + BPR_LOCAL_PREF + BPR_LOCAL_ORIGIN + BPR_ASPATH + BPR_ORIGIN + BPR_MED + BPR_ASN + BPR_IGP_COST + BPR_ROUTER_ID + BPR_OLDER + BPR_NON_LLGR_STALE +) + +var BestPathReasonStringMap = map[BestPathReason]string{ + BPR_UNKNOWN: "Unknown", + BPR_DISABLED: "Bestpath selection disabled", + BPR_ONLY_PATH: "Only Path", + BPR_REACHABLE_NEXT_HOP: "Reachable Next Hop", + BPR_HIGHEST_WEIGHT: "Highest Weight", + BPR_LOCAL_PREF: "Local Pref", + BPR_LOCAL_ORIGIN: "Local Origin", + BPR_ASPATH: "AS Path", + BPR_ORIGIN: "Origin", + BPR_MED: "MED", + BPR_ASN: "ASN", + BPR_IGP_COST: "IGP Cost", + BPR_ROUTER_ID: "Router ID", + BPR_OLDER: "Older", + BPR_NON_LLGR_STALE: "no LLGR Stale", +} + +func (r *BestPathReason) String() string { + return BestPathReasonStringMap[*r] +} + +func IpToRadixkey(b []byte, max uint8) string { + var buffer bytes.Buffer + for i := 0; i < len(b) && i < int(max); i++ { + fmt.Fprintf(&buffer, "%08b", b[i]) + } + return buffer.String()[:max] +} + +func CidrToRadixkey(cidr string) string { + _, n, _ := net.ParseCIDR(cidr) + ones, _ := n.Mask.Size() + return IpToRadixkey(n.IP, uint8(ones)) +} + +func AddrToRadixkey(addr bgp.AddrPrefixInterface) string { + var ( + ip net.IP + size uint8 + ) + switch T := addr.(type) { + case *bgp.IPAddrPrefix: + mask := net.CIDRMask(int(T.Length), net.IPv4len*8) + ip, size = T.Prefix.Mask(mask).To4(), uint8(T.Length) + case *bgp.IPv6AddrPrefix: + mask := net.CIDRMask(int(T.Length), net.IPv6len*8) + ip, size = T.Prefix.Mask(mask).To16(), uint8(T.Length) + default: + return CidrToRadixkey(addr.String()) + } + return IpToRadixkey(ip, size) +} + +type PeerInfo struct { + AS uint32 + ID net.IP + LocalAS uint32 + LocalID net.IP + Address net.IP + LocalAddress net.IP + RouteReflectorClient bool + RouteReflectorClusterID net.IP + MultihopTtl uint8 + Confederation bool +} + +func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool { + if lhs == rhs { + return true + } + + if rhs == nil { + return false + } + + if (lhs.AS == rhs.AS) && lhs.ID.Equal(rhs.ID) && lhs.LocalID.Equal(rhs.LocalID) && lhs.Address.Equal(rhs.Address) { + return true + } + return false +} + +func (i *PeerInfo) String() string { + if i.Address == nil { + return "local" + } + s := bytes.NewBuffer(make([]byte, 0, 64)) + s.WriteString(fmt.Sprintf("{ %s | ", i.Address)) + s.WriteString(fmt.Sprintf("as: %d", i.AS)) + s.WriteString(fmt.Sprintf(", id: %s", i.ID)) + if i.RouteReflectorClient { + s.WriteString(fmt.Sprintf(", cluster-id: %s", i.RouteReflectorClusterID)) + } + s.WriteString(" }") + return s.String() +} + +func NewPeerInfo(g *config.Global, p *config.Neighbor) *PeerInfo { + clusterID := net.ParseIP(string(p.RouteReflector.State.RouteReflectorClusterId)).To4() + // exclude zone info + naddr, _ := net.ResolveIPAddr("ip", p.State.NeighborAddress) + return &PeerInfo{ + AS: p.Config.PeerAs, + LocalAS: g.Config.As, + LocalID: net.ParseIP(g.Config.RouterId).To4(), + RouteReflectorClient: p.RouteReflector.Config.RouteReflectorClient, + Address: naddr.IP, + RouteReflectorClusterID: clusterID, + MultihopTtl: p.EbgpMultihop.Config.MultihopTtl, + Confederation: p.IsConfederationMember(g), + } +} + +type Destination struct { + routeFamily bgp.RouteFamily + nlri bgp.AddrPrefixInterface + knownPathList []*Path + localIdMap *Bitmap +} + +func NewDestination(nlri bgp.AddrPrefixInterface, mapSize int, known ...*Path) *Destination { + d := &Destination{ + routeFamily: bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()), + nlri: nlri, + knownPathList: known, + localIdMap: NewBitmap(mapSize), + } + // the id zero means id is not allocated yet. + if mapSize != 0 { + d.localIdMap.Flag(0) + } + return d +} + +func (dd *Destination) Family() bgp.RouteFamily { + return dd.routeFamily +} + +func (dd *Destination) setRouteFamily(routeFamily bgp.RouteFamily) { + dd.routeFamily = routeFamily +} + +func (dd *Destination) GetNlri() bgp.AddrPrefixInterface { + return dd.nlri +} + +func (dd *Destination) setNlri(nlri bgp.AddrPrefixInterface) { + dd.nlri = nlri +} + +func (dd *Destination) GetAllKnownPathList() []*Path { + return dd.knownPathList +} + +func rsFilter(id string, as uint32, path *Path) bool { + isASLoop := func(as uint32, path *Path) bool { + for _, v := range path.GetAsList() { + if as == v { + return true + } + } + return false + } + + if id != GLOBAL_RIB_NAME && (path.GetSource().Address.String() == id || isASLoop(as, path)) { + return true + } + return false +} + +func (dd *Destination) GetKnownPathList(id string, as uint32) []*Path { + list := make([]*Path, 0, len(dd.knownPathList)) + for _, p := range dd.knownPathList { + if rsFilter(id, as, p) { + continue + } + list = append(list, p) + } + return list +} + +func getBestPath(id string, as uint32, pathList []*Path) *Path { + for _, p := range pathList { + if rsFilter(id, as, p) { + continue + } + return p + } + return nil +} + +func (dd *Destination) GetBestPath(id string, as uint32) *Path { + p := getBestPath(id, as, dd.knownPathList) + if p == nil || p.IsNexthopInvalid { + return nil + } + return p +} + +func (dd *Destination) GetMultiBestPath(id string) []*Path { + return getMultiBestPath(id, dd.knownPathList) +} + +// Calculates best-path among known paths for this destination. +// +// Modifies destination's state related to stored paths. Removes withdrawn +// paths from known paths. Also, adds new paths to known paths. +func (dest *Destination) Calculate(newPath *Path) *Update { + oldKnownPathList := make([]*Path, len(dest.knownPathList)) + copy(oldKnownPathList, dest.knownPathList) + + if newPath.IsWithdraw { + p := dest.explicitWithdraw(newPath) + if p != nil { + if id := p.GetNlri().PathLocalIdentifier(); id != 0 { + dest.localIdMap.Unflag(uint(id)) + } + } + } else { + dest.implicitWithdraw(newPath) + dest.knownPathList = append(dest.knownPathList, newPath) + } + + for _, path := range dest.knownPathList { + if path.GetNlri().PathLocalIdentifier() == 0 { + id, err := dest.localIdMap.FindandSetZeroBit() + if err != nil { + dest.localIdMap.Expand() + id, _ = dest.localIdMap.FindandSetZeroBit() + } + path.GetNlri().SetPathLocalIdentifier(uint32(id)) + } + } + // Compute new best path + dest.computeKnownBestPath() + + l := make([]*Path, len(dest.knownPathList)) + copy(l, dest.knownPathList) + return &Update{ + KnownPathList: l, + OldKnownPathList: oldKnownPathList, + } +} + +// Removes withdrawn paths. +// +// Note: +// We may have disproportionate number of withdraws compared to know paths +// since not all paths get installed into the table due to bgp policy and +// we can receive withdraws for such paths and withdrawals may not be +// stopped by the same policies. +// +func (dest *Destination) explicitWithdraw(withdraw *Path) *Path { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": dest.GetNlri().String(), + }).Debug("Removing withdrawals") + + // If we have some withdrawals and no know-paths, it means it is safe to + // delete these withdraws. + if len(dest.knownPathList) == 0 { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": dest.GetNlri().String(), + }).Debug("Found withdrawals for path(s) that did not get installed") + return nil + } + + // Match all withdrawals from destination paths. + isFound := -1 + for i, path := range dest.knownPathList { + // We have a match if the source and path-id are same. + if path.GetSource().Equal(withdraw.GetSource()) && path.GetNlri().PathIdentifier() == withdraw.GetNlri().PathIdentifier() { + isFound = i + withdraw.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier()) + } + } + + // We do no have any match for this withdraw. + if isFound == -1 { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": dest.GetNlri().String(), + "Path": withdraw, + }).Warn("No matching path for withdraw found, may be path was not installed into table") + return nil + } else { + p := dest.knownPathList[isFound] + dest.knownPathList = append(dest.knownPathList[:isFound], dest.knownPathList[isFound+1:]...) + return p + } +} + +// Identifies which of known paths are old and removes them. +// +// Known paths will no longer have paths whose new version is present in +// new paths. +func (dest *Destination) implicitWithdraw(newPath *Path) { + found := -1 + for i, path := range dest.knownPathList { + if newPath.NoImplicitWithdraw() { + continue + } + // Here we just check if source is same and not check if path + // version num. as newPaths are implicit withdrawal of old + // paths and when doing RouteRefresh (not EnhancedRouteRefresh) + // we get same paths again. + if newPath.GetSource().Equal(path.GetSource()) && newPath.GetNlri().PathIdentifier() == path.GetNlri().PathIdentifier() { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": dest.GetNlri().String(), + "Path": path, + }).Debug("Implicit withdrawal of old path, since we have learned new path from the same peer") + + found = i + newPath.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier()) + break + } + } + if found != -1 { + dest.knownPathList = append(dest.knownPathList[:found], dest.knownPathList[found+1:]...) + } +} + +func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) { + if SelectionOptions.DisableBestPathSelection { + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("computeKnownBestPath skipped") + return nil, BPR_DISABLED, nil + } + + // If we do not have any paths to this destination, then we do not have + // new best path. + if len(dest.knownPathList) == 0 { + return nil, BPR_UNKNOWN, nil + } + + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("computeKnownBestPath knownPathList: %d", len(dest.knownPathList)) + + // We pick the first path as current best path. This helps in breaking + // tie between two new paths learned in one cycle for which best-path + // calculation steps lead to tie. + if len(dest.knownPathList) == 1 { + // If the first path has the invalidated next-hop, which evaluated by + // IGP, returns no path with the reason of the next-hop reachability. + if dest.knownPathList[0].IsNexthopInvalid { + return nil, BPR_REACHABLE_NEXT_HOP, nil + } + return dest.knownPathList[0], BPR_ONLY_PATH, nil + } + dest.sort() + newBest := dest.knownPathList[0] + // If the first path has the invalidated next-hop, which evaluated by IGP, + // returns no path with the reason of the next-hop reachability. + if dest.knownPathList[0].IsNexthopInvalid { + return nil, BPR_REACHABLE_NEXT_HOP, nil + } + return newBest, newBest.reason, nil +} + +func (dst *Destination) sort() { + sort.SliceStable(dst.knownPathList, func(i, j int) bool { + //Compares given paths and returns best path. + // + //Parameters: + // -`path1`: first path to compare + // -`path2`: second path to compare + // + // Best path processing will involve following steps: + // 1. Select a path with a reachable next hop. + // 2. Select the path with the highest weight. + // 3. If path weights are the same, select the path with the highest + // local preference value. + // 4. Prefer locally originated routes (network routes, redistributed + // routes, or aggregated routes) over received routes. + // 5. Select the route with the shortest AS-path length. + // 6. If all paths have the same AS-path length, select the path based + // on origin: IGP is preferred over EGP; EGP is preferred over + // Incomplete. + // 7. If the origins are the same, select the path with lowest MED + // value. + // 8. If the paths have the same MED values, select the path learned + // via EBGP over one learned via IBGP. + // 9. Select the route with the lowest IGP cost to the next hop. + // 10. Select the route received from the peer with the lowest BGP + // router ID. + // + // Returns None if best-path among given paths cannot be computed else best + // path. + // Assumes paths from NC has source equal to None. + // + + path1 := dst.knownPathList[i] + path2 := dst.knownPathList[j] + + var better *Path + reason := BPR_UNKNOWN + + // draft-uttaro-idr-bgp-persistence-02 + if better == nil { + better = compareByLLGRStaleCommunity(path1, path2) + reason = BPR_NON_LLGR_STALE + } + // Follow best path calculation algorithm steps. + // compare by reachability + if better == nil { + better = compareByReachableNexthop(path1, path2) + reason = BPR_REACHABLE_NEXT_HOP + } + if better == nil { + better = compareByHighestWeight(path1, path2) + reason = BPR_HIGHEST_WEIGHT + } + if better == nil { + better = compareByLocalPref(path1, path2) + reason = BPR_LOCAL_PREF + } + if better == nil { + better = compareByLocalOrigin(path1, path2) + reason = BPR_LOCAL_ORIGIN + } + if better == nil { + better = compareByASPath(path1, path2) + reason = BPR_ASPATH + } + if better == nil { + better = compareByOrigin(path1, path2) + reason = BPR_ORIGIN + } + if better == nil { + better = compareByMED(path1, path2) + reason = BPR_MED + } + if better == nil { + better = compareByASNumber(path1, path2) + reason = BPR_ASN + } + if better == nil { + better = compareByIGPCost(path1, path2) + reason = BPR_IGP_COST + } + if better == nil { + better = compareByAge(path1, path2) + reason = BPR_OLDER + } + if better == nil { + var e error = nil + better, e = compareByRouterID(path1, path2) + if e != nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Error": e, + }).Error("Could not get best path by comparing router ID") + } + reason = BPR_ROUTER_ID + } + if better == nil { + reason = BPR_UNKNOWN + better = path1 + } + + better.reason = reason + + return better == path1 + }) +} + +type Update struct { + KnownPathList []*Path + OldKnownPathList []*Path +} + +func getMultiBestPath(id string, pathList []*Path) []*Path { + list := make([]*Path, 0, len(pathList)) + var best *Path + for _, p := range pathList { + if !p.IsNexthopInvalid { + if best == nil { + best = p + list = append(list, p) + } else if best.Compare(p) == 0 { + list = append(list, p) + } + } + } + return list +} + +func (u *Update) GetWithdrawnPath() []*Path { + if len(u.KnownPathList) == len(u.OldKnownPathList) { + return nil + } + + l := make([]*Path, 0, len(u.OldKnownPathList)) + + for _, p := range u.OldKnownPathList { + y := func() bool { + for _, old := range u.KnownPathList { + if p == old { + return true + } + } + return false + }() + if !y { + l = append(l, p.Clone(true)) + } + } + return l +} + +func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path, []*Path) { + best, old := func(id string) (*Path, *Path) { + old := getBestPath(id, as, u.OldKnownPathList) + best := getBestPath(id, as, u.KnownPathList) + if best != nil && best.Equal(old) { + // RFC4684 3.2. Intra-AS VPN Route Distribution + // When processing RT membership NLRIs received from internal iBGP + // peers, it is necessary to consider all available iBGP paths for a + // given RT prefix, for building the outbound route filter, and not just + // the best path. + if best.GetRouteFamily() == bgp.RF_RTC_UC { + return best, old + } + // For BGP Nexthop Tracking, checks if the nexthop reachability + // was changed or not. + if best.IsNexthopInvalid != old.IsNexthopInvalid { + // If the nexthop of the best path became unreachable, we need + // to withdraw that path. + if best.IsNexthopInvalid { + return best.Clone(true), old + } + return best, old + } + return nil, old + } + if best == nil { + if old == nil { + return nil, nil + } + if peerDown { + // withdraws were generated by peer + // down so paths are not in knowpath + // or adjin. + old.IsWithdraw = true + return old, old + } + return old.Clone(true), old + } + return best, old + }(id) + + var multi []*Path + + if id == GLOBAL_RIB_NAME && UseMultiplePaths.Enabled { + diff := func(lhs, rhs []*Path) bool { + if len(lhs) != len(rhs) { + return true + } + for idx, l := range lhs { + if !l.Equal(rhs[idx]) { + return true + } + } + return false + } + oldM := getMultiBestPath(id, u.OldKnownPathList) + newM := getMultiBestPath(id, u.KnownPathList) + if diff(oldM, newM) { + multi = newM + if len(newM) == 0 { + multi = []*Path{best} + } + } + } + return best, old, multi +} + +func compareByLLGRStaleCommunity(path1, path2 *Path) *Path { + p1 := path1.IsLLGRStale() + p2 := path2.IsLLGRStale() + if p1 == p2 { + return nil + } else if p1 { + return path2 + } + return path1 +} + +func compareByReachableNexthop(path1, path2 *Path) *Path { + // Compares given paths and selects best path based on reachable next-hop. + // + // If no path matches this criteria, return nil. + // For BGP Nexthop Tracking, evaluates next-hop is validated by IGP. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("enter compareByReachableNexthop -- path1: %s, path2: %s", path1, path2) + + if path1.IsNexthopInvalid && !path2.IsNexthopInvalid { + return path2 + } else if !path1.IsNexthopInvalid && path2.IsNexthopInvalid { + return path1 + } + + return nil +} + +func compareByHighestWeight(path1, path2 *Path) *Path { + // Selects a path with highest weight. + // + // Weight is BGPS specific parameter. It is local to the router on which it + // is configured. + // Return: + // nil if best path among given paths cannot be decided, else best path. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("enter compareByHighestWeight -- path1: %s, path2: %s", path1, path2) + return nil +} + +func compareByLocalPref(path1, path2 *Path) *Path { + // Selects a path with highest local-preference. + // + // Unlike the weight attribute, which is only relevant to the local + // router, local preference is an attribute that routers exchange in the + // same AS. Highest local-pref is preferred. If we cannot decide, + // we return None. + // + // # Default local-pref values is 100 + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByLocalPref") + localPref1, _ := path1.GetLocalPref() + localPref2, _ := path2.GetLocalPref() + // Highest local-preference value is preferred. + if localPref1 > localPref2 { + return path1 + } else if localPref1 < localPref2 { + return path2 + } else { + return nil + } +} + +func compareByLocalOrigin(path1, path2 *Path) *Path { + + // Select locally originating path as best path. + // Locally originating routes are network routes, redistributed routes, + // or aggregated routes. + // Returns None if given paths have same source. + // + // If both paths are from same sources we cannot compare them here. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByLocalOrigin") + if path1.GetSource().Equal(path2.GetSource()) { + return nil + } + + // Here we consider prefix from NC as locally originating static route. + // Hence it is preferred. + if path1.IsLocal() { + return path1 + } + + if path2.IsLocal() { + return path2 + } + return nil +} + +func compareByASPath(path1, path2 *Path) *Path { + // Calculated the best-paths by comparing as-path lengths. + // + // Shortest as-path length is preferred. If both path have same lengths, + // we return None. + if SelectionOptions.IgnoreAsPathLength { + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("compareByASPath -- skip") + return nil + } + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByASPath") + attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + + // With addpath support, we could compare paths from API don't + // AS_PATH. No need to warn here. + if !path1.IsLocal() && !path2.IsLocal() && (attribute1 == nil || attribute2 == nil) { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": "compareByASPath", + "ASPath1": attribute1, + "ASPath2": attribute2, + }).Warn("can't compare ASPath because it's not present") + } + + l1 := path1.GetAsPathLen() + l2 := path2.GetAsPathLen() + + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("compareByASPath -- l1: %d, l2: %d", l1, l2) + if l1 > l2 { + return path2 + } else if l1 < l2 { + return path1 + } else { + return nil + } +} + +func compareByOrigin(path1, path2 *Path) *Path { + // Select the best path based on origin attribute. + // + // IGP is preferred over EGP; EGP is preferred over Incomplete. + // If both paths have same origin, we return None. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByOrigin") + attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN) + attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN) + + if attribute1 == nil || attribute2 == nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": "compareByOrigin", + "Origin1": attribute1, + "Origin2": attribute2, + }).Error("can't compare origin because it's not present") + return nil + } + + origin1 := attribute1.(*bgp.PathAttributeOrigin).Value + origin2 := attribute2.(*bgp.PathAttributeOrigin).Value + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("compareByOrigin -- origin1: %d, origin2: %d", origin1, origin2) + + // If both paths have same origins + if origin1 == origin2 { + return nil + } else if origin1 < origin2 { + return path1 + } else { + return path2 + } +} + +func compareByMED(path1, path2 *Path) *Path { + // Select the path based with lowest MED value. + // + // If both paths have same MED, return None. + // By default, a route that arrives with no MED value is treated as if it + // had a MED of 0, the most preferred value. + // RFC says lower MED is preferred over higher MED value. + // compare MED among not only same AS path but also all path, + // like bgp always-compare-med + + isInternal := func() bool { return path1.GetAsPathLen() == 0 && path2.GetAsPathLen() == 0 }() + + isSameAS := func() bool { + firstAS := func(path *Path) uint32 { + if asPath := path.GetAsPath(); asPath != nil { + for _, v := range asPath.Value { + segType := v.GetType() + asList := v.GetAS() + if len(asList) == 0 { + continue + } + switch segType { + case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + continue + } + return asList[0] + } + } + return 0 + } + return firstAS(path1) != 0 && firstAS(path1) == firstAS(path2) + }() + + if SelectionOptions.AlwaysCompareMed || isInternal || isSameAS { + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByMED") + getMed := func(path *Path) uint32 { + attribute := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + if attribute == nil { + return 0 + } + med := attribute.(*bgp.PathAttributeMultiExitDisc).Value + return med + } + + med1 := getMed(path1) + med2 := getMed(path2) + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("compareByMED -- med1: %d, med2: %d", med1, med2) + if med1 == med2 { + return nil + } else if med1 < med2 { + return path1 + } + return path2 + } else { + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("skip compareByMED %v %v %v", SelectionOptions.AlwaysCompareMed, isInternal, isSameAS) + return nil + } +} + +func compareByASNumber(path1, path2 *Path) *Path { + + //Select the path based on source (iBGP/eBGP) peer. + // + //eBGP path is preferred over iBGP. If both paths are from same kind of + //peers, return None. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByASNumber") + + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("compareByASNumber -- p1Asn: %d, p2Asn: %d", path1.GetSource().AS, path2.GetSource().AS) + // Path from confederation member should be treated as internal (IBGP learned) path. + isIBGP1 := path1.GetSource().Confederation || path1.IsIBGP() + isIBGP2 := path2.GetSource().Confederation || path2.IsIBGP() + // If one path is from ibgp peer and another is from ebgp peer, take the ebgp path. + if isIBGP1 != isIBGP2 { + if isIBGP1 { + return path2 + } + return path1 + } + + // If both paths are from ebgp or ibpg peers, we cannot decide. + return nil +} + +func compareByIGPCost(path1, path2 *Path) *Path { + // Select the route with the lowest IGP cost to the next hop. + // + // Return None if igp cost is same. + // Currently BGPS has no concept of IGP and IGP cost. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debugf("enter compareByIGPCost -- path1: %v, path2: %v", path1, path2) + return nil +} + +func compareByRouterID(path1, path2 *Path) (*Path, error) { + // Select the route received from the peer with the lowest BGP router ID. + // + // If both paths are eBGP paths, then we do not do any tie breaking, i.e we do + // not pick best-path based on this criteria. + // RFC: http://tools.ietf.org/html/rfc5004 + // We pick best path between two iBGP paths as usual. + log.WithFields(log.Fields{ + "Topic": "Table", + }).Debug("enter compareByRouterID") + + // If both paths are from NC we have same router Id, hence cannot compare. + if path1.IsLocal() && path2.IsLocal() { + return nil, nil + } + + // If both paths are from eBGP peers, then according to RFC we need + // not tie break using router id. + if !SelectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() { + return nil, nil + } + + if !SelectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() { + return nil, fmt.Errorf("This method does not support comparing ebgp with ibgp path") + } + + // At least one path is not coming from NC, so we get local bgp id. + id1 := binary.BigEndian.Uint32(path1.GetSource().ID) + id2 := binary.BigEndian.Uint32(path2.GetSource().ID) + + // If both router ids are same/equal we cannot decide. + // This case is possible since router ids are arbitrary. + if id1 == id2 { + return nil, nil + } else if id1 < id2 { + return path1, nil + } else { + return path2, nil + } +} + +func compareByAge(path1, path2 *Path) *Path { + if !path1.IsIBGP() && !path2.IsIBGP() && !SelectionOptions.ExternalCompareRouterId { + age1 := path1.GetTimestamp().UnixNano() + age2 := path2.GetTimestamp().UnixNano() + if age1 == age2 { + return nil + } else if age1 < age2 { + return path1 + } + return path2 + } + return nil +} + +func (dest *Destination) String() string { + return fmt.Sprintf("Destination NLRI: %s", dest.nlri.String()) +} + +type DestinationSelectOption struct { + ID string + AS uint32 + VRF *Vrf + adj bool + Best bool + MultiPath bool +} + +func (d *Destination) MarshalJSON() ([]byte, error) { + return json.Marshal(d.GetAllKnownPathList()) +} + +func (d *Destination) Select(option ...DestinationSelectOption) *Destination { + id := GLOBAL_RIB_NAME + var vrf *Vrf + adj := false + best := false + mp := false + as := uint32(0) + for _, o := range option { + if o.ID != "" { + id = o.ID + } + if o.VRF != nil { + vrf = o.VRF + } + adj = o.adj + best = o.Best + mp = o.MultiPath + as = o.AS + } + var paths []*Path + if adj { + paths = make([]*Path, len(d.knownPathList)) + copy(paths, d.knownPathList) + } else { + paths = d.GetKnownPathList(id, as) + if vrf != nil { + ps := make([]*Path, 0, len(paths)) + for _, p := range paths { + if CanImportToVrf(vrf, p) { + ps = append(ps, p.ToLocal()) + } + } + paths = ps + } + if len(paths) == 0 { + return nil + } + if best { + if !mp { + paths = []*Path{paths[0]} + } else { + ps := make([]*Path, 0, len(paths)) + var best *Path + for _, p := range paths { + if best == nil { + best = p + ps = append(ps, p) + } else if best.Compare(p) == 0 { + ps = append(ps, p) + } + } + paths = ps + } + } + } + return NewDestination(d.nlri, 0, paths...) +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/message.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/message.go new file mode 100644 index 0000000..31b9059 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/message.go @@ -0,0 +1,502 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "bytes" + "reflect" + + "github.com/osrg/gobgp/pkg/packet/bgp" + log "github.com/sirupsen/logrus" +) + +func UpdatePathAttrs2ByteAs(msg *bgp.BGPUpdate) error { + ps := msg.PathAttributes + msg.PathAttributes = make([]bgp.PathAttributeInterface, len(ps)) + copy(msg.PathAttributes, ps) + var asAttr *bgp.PathAttributeAsPath + idx := 0 + for i, attr := range msg.PathAttributes { + if a, ok := attr.(*bgp.PathAttributeAsPath); ok { + asAttr = a + idx = i + break + } + } + + if asAttr == nil { + return nil + } + + as4Params := make([]*bgp.As4PathParam, 0, len(asAttr.Value)) + as2Params := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) + mkAs4 := false + for _, param := range asAttr.Value { + segType := param.GetType() + asList := param.GetAS() + as2Path := make([]uint16, 0, len(asList)) + for _, as := range asList { + if as > (1<<16)-1 { + mkAs4 = true + as2Path = append(as2Path, bgp.AS_TRANS) + } else { + as2Path = append(as2Path, uint16(as)) + } + } + as2Params = append(as2Params, bgp.NewAsPathParam(segType, as2Path)) + + // RFC 6793 4.2.2 Generating Updates + // + // Whenever the AS path information contains the AS_CONFED_SEQUENCE or + // AS_CONFED_SET path segment, the NEW BGP speaker MUST exclude such + // path segments from the AS4_PATH attribute being constructed. + switch segType { + case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: + // pass + default: + if as4param, ok := param.(*bgp.As4PathParam); ok { + as4Params = append(as4Params, as4param) + } + } + } + msg.PathAttributes[idx] = bgp.NewPathAttributeAsPath(as2Params) + if mkAs4 { + msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Path(as4Params)) + } + return nil +} + +func UpdatePathAttrs4ByteAs(msg *bgp.BGPUpdate) error { + var asAttr *bgp.PathAttributeAsPath + var as4Attr *bgp.PathAttributeAs4Path + asAttrPos := 0 + as4AttrPos := 0 + for i, attr := range msg.PathAttributes { + switch attr.(type) { + case *bgp.PathAttributeAsPath: + asAttr = attr.(*bgp.PathAttributeAsPath) + for j, param := range asAttr.Value { + as2Param, ok := param.(*bgp.AsPathParam) + if ok { + asPath := make([]uint32, 0, len(as2Param.AS)) + for _, as := range as2Param.AS { + asPath = append(asPath, uint32(as)) + } + as4Param := bgp.NewAs4PathParam(as2Param.Type, asPath) + asAttr.Value[j] = as4Param + } + } + asAttrPos = i + msg.PathAttributes[i] = asAttr + case *bgp.PathAttributeAs4Path: + as4AttrPos = i + as4Attr = attr.(*bgp.PathAttributeAs4Path) + } + } + + if as4Attr != nil { + msg.PathAttributes = append(msg.PathAttributes[:as4AttrPos], msg.PathAttributes[as4AttrPos+1:]...) + } + + if asAttr == nil || as4Attr == nil { + return nil + } + + asLen := 0 + asConfedLen := 0 + asParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) + for _, param := range asAttr.Value { + asLen += param.ASLen() + switch param.GetType() { + case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: + asConfedLen++ + case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + asConfedLen += len(param.GetAS()) + } + asParams = append(asParams, param) + } + + as4Len := 0 + as4Params := make([]bgp.AsPathParamInterface, 0, len(as4Attr.Value)) + if as4Attr != nil { + for _, p := range as4Attr.Value { + // RFC 6793 6. Error Handling + // + // the path segment types AS_CONFED_SEQUENCE and AS_CONFED_SET [RFC5065] + // MUST NOT be carried in the AS4_PATH attribute of an UPDATE message. + // A NEW BGP speaker that receives these path segment types in the AS4_PATH + // attribute of an UPDATE message from an OLD BGP speaker MUST discard + // these path segments, adjust the relevant attribute fields accordingly, + // and continue processing the UPDATE message. + // This case SHOULD be logged locally for analysis. + switch p.Type { + case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: + typ := "CONFED_SEQ" + if p.Type == bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET { + typ = "CONFED_SET" + } + log.WithFields(log.Fields{ + "Topic": "Table", + }).Warnf("AS4_PATH contains %s segment %s. ignore", typ, p.String()) + continue + } + as4Len += p.ASLen() + as4Params = append(as4Params, p) + } + } + + if asLen+asConfedLen < as4Len { + log.WithFields(log.Fields{ + "Topic": "Table", + }).Warn("AS4_PATH is longer than AS_PATH. ignore AS4_PATH") + return nil + } + + keepNum := asLen + asConfedLen - as4Len + + newParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) + for _, param := range asParams { + if keepNum-param.ASLen() >= 0 { + newParams = append(newParams, param) + keepNum -= param.ASLen() + } else { + // only SEQ param reaches here + newParams = append(newParams, bgp.NewAs4PathParam(param.GetType(), param.GetAS()[:keepNum])) + keepNum = 0 + } + + if keepNum <= 0 { + break + } + } + + for _, param := range as4Params { + lastParam := newParams[len(newParams)-1] + lastParamAS := lastParam.GetAS() + paramType := param.GetType() + paramAS := param.GetAS() + if paramType == lastParam.GetType() && paramType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ { + if len(lastParamAS)+len(paramAS) > 255 { + newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS[:255-len(lastParamAS)]...)) + newParams = append(newParams, bgp.NewAs4PathParam(paramType, paramAS[255-len(lastParamAS):])) + } else { + newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS...)) + } + } else { + newParams = append(newParams, param) + } + } + + newIntfParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) + newIntfParams = append(newIntfParams, newParams...) + + msg.PathAttributes[asAttrPos] = bgp.NewPathAttributeAsPath(newIntfParams) + return nil +} + +func UpdatePathAggregator2ByteAs(msg *bgp.BGPUpdate) { + as := uint32(0) + var addr string + for i, attr := range msg.PathAttributes { + switch attr.(type) { + case *bgp.PathAttributeAggregator: + agg := attr.(*bgp.PathAttributeAggregator) + addr = agg.Value.Address.String() + if agg.Value.AS > (1<<16)-1 { + as = agg.Value.AS + msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr) + } else { + msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(agg.Value.AS), addr) + } + } + } + if as != 0 { + msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Aggregator(as, addr)) + } +} + +func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error { + var aggAttr *bgp.PathAttributeAggregator + var agg4Attr *bgp.PathAttributeAs4Aggregator + agg4AttrPos := 0 + for i, attr := range msg.PathAttributes { + switch attr.(type) { + case *bgp.PathAttributeAggregator: + attr := attr.(*bgp.PathAttributeAggregator) + if attr.Value.Askind == reflect.Uint16 { + aggAttr = attr + aggAttr.Value.Askind = reflect.Uint32 + } + case *bgp.PathAttributeAs4Aggregator: + agg4Attr = attr.(*bgp.PathAttributeAs4Aggregator) + agg4AttrPos = i + } + } + if aggAttr == nil && agg4Attr == nil { + return nil + } + + if aggAttr == nil && agg4Attr != nil { + return bgp.NewMessageError(bgp.BGP_ERROR_UPDATE_MESSAGE_ERROR, bgp.BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "AS4 AGGREGATOR attribute exists, but AGGREGATOR doesn't") + } + + if agg4Attr != nil { + msg.PathAttributes = append(msg.PathAttributes[:agg4AttrPos], msg.PathAttributes[agg4AttrPos+1:]...) + aggAttr.Value.AS = agg4Attr.Value.AS + } + return nil +} + +type cage struct { + attrsBytes []byte + paths []*Path +} + +func newCage(b []byte, path *Path) *cage { + return &cage{ + attrsBytes: b, + paths: []*Path{path}, + } +} + +type packerInterface interface { + add(*Path) + pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage +} + +type packer struct { + eof bool + family bgp.RouteFamily + total uint32 +} + +type packerMP struct { + packer + paths []*Path + withdrawals []*Path +} + +func (p *packerMP) add(path *Path) { + p.packer.total++ + + if path.IsEOR() { + p.packer.eof = true + return + } + + if path.IsWithdraw { + p.withdrawals = append(p.withdrawals, path) + return + } + + p.paths = append(p.paths, path) +} + +func createMPReachMessage(path *Path) *bgp.BGPMessage { + oattrs := path.GetPathAttrs() + attrs := make([]bgp.PathAttributeInterface, 0, len(oattrs)) + for _, a := range oattrs { + if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI { + attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()})) + } else { + attrs = append(attrs, a) + } + } + return bgp.NewBGPUpdateMessage(nil, attrs, nil) +} + +func (p *packerMP) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage { + msgs := make([]*bgp.BGPMessage, 0, p.packer.total) + + for _, path := range p.withdrawals { + nlris := []bgp.AddrPrefixInterface{path.GetNlri()} + msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil)) + } + + for _, path := range p.paths { + msgs = append(msgs, createMPReachMessage(path)) + } + + if p.eof { + msgs = append(msgs, bgp.NewEndOfRib(p.family)) + } + return msgs +} + +func newPackerMP(f bgp.RouteFamily) *packerMP { + return &packerMP{ + packer: packer{ + family: f, + }, + withdrawals: make([]*Path, 0), + paths: make([]*Path, 0), + } +} + +type packerV4 struct { + packer + hashmap map[uint32][]*cage + mpPaths []*Path + withdrawals []*Path +} + +func (p *packerV4) add(path *Path) { + p.packer.total++ + + if path.IsEOR() { + p.packer.eof = true + return + } + + if path.IsWithdraw { + p.withdrawals = append(p.withdrawals, path) + return + } + + if path.GetNexthop().To4() == nil { + // RFC 5549 + p.mpPaths = append(p.mpPaths, path) + return + } + + key := path.GetHash() + attrsB := bytes.NewBuffer(make([]byte, 0)) + for _, v := range path.GetPathAttrs() { + b, _ := v.Serialize() + attrsB.Write(b) + } + + if cages, y := p.hashmap[key]; y { + added := false + for _, c := range cages { + if bytes.Equal(c.attrsBytes, attrsB.Bytes()) { + c.paths = append(c.paths, path) + added = true + break + } + } + if !added { + p.hashmap[key] = append(p.hashmap[key], newCage(attrsB.Bytes(), path)) + } + } else { + p.hashmap[key] = []*cage{newCage(attrsB.Bytes(), path)} + } +} + +func (p *packerV4) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage { + split := func(max int, paths []*Path) ([]*bgp.IPAddrPrefix, []*Path) { + nlris := make([]*bgp.IPAddrPrefix, 0, max) + i := 0 + if max > len(paths) { + max = len(paths) + } + for ; i < max; i++ { + nlris = append(nlris, paths[i].GetNlri().(*bgp.IPAddrPrefix)) + } + return nlris, paths[i:] + } + addpathNLRILen := 0 + if bgp.IsAddPathEnabled(false, p.packer.family, options) { + addpathNLRILen = 4 + } + // Header + Update (WithdrawnRoutesLen + + // TotalPathAttributeLen + attributes + maxlen of NLRI). + // the max size of NLRI is 5bytes (plus 4bytes with addpath enabled) + maxNLRIs := func(attrsLen int) int { + return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / (5 + addpathNLRILen) + } + + loop := func(attrsLen int, paths []*Path, cb func([]*bgp.IPAddrPrefix)) { + max := maxNLRIs(attrsLen) + var nlris []*bgp.IPAddrPrefix + for { + nlris, paths = split(max, paths) + if len(nlris) == 0 { + break + } + cb(nlris) + } + } + + msgs := make([]*bgp.BGPMessage, 0, p.packer.total) + + loop(0, p.withdrawals, func(nlris []*bgp.IPAddrPrefix) { + msgs = append(msgs, bgp.NewBGPUpdateMessage(nlris, nil, nil)) + }) + + for _, cages := range p.hashmap { + for _, c := range cages { + paths := c.paths + + attrs := paths[0].GetPathAttrs() + attrsLen := 0 + for _, a := range attrs { + attrsLen += a.Len() + } + + loop(attrsLen, paths, func(nlris []*bgp.IPAddrPrefix) { + msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, attrs, nlris)) + }) + } + } + + for _, path := range p.mpPaths { + msgs = append(msgs, createMPReachMessage(path)) + } + + if p.eof { + msgs = append(msgs, bgp.NewEndOfRib(p.family)) + } + return msgs +} + +func newPackerV4(f bgp.RouteFamily) *packerV4 { + return &packerV4{ + packer: packer{ + family: f, + }, + hashmap: make(map[uint32][]*cage), + withdrawals: make([]*Path, 0), + mpPaths: make([]*Path, 0), + } +} + +func newPacker(f bgp.RouteFamily) packerInterface { + switch f { + case bgp.RF_IPv4_UC: + return newPackerV4(bgp.RF_IPv4_UC) + default: + return newPackerMP(f) + } +} + +func CreateUpdateMsgFromPaths(pathList []*Path, options ...*bgp.MarshallingOption) []*bgp.BGPMessage { + msgs := make([]*bgp.BGPMessage, 0, len(pathList)) + + m := make(map[bgp.RouteFamily]packerInterface) + for _, path := range pathList { + f := path.GetRouteFamily() + if _, y := m[f]; !y { + m[f] = newPacker(f) + } + m[f].add(path) + } + + for _, p := range m { + msgs = append(msgs, p.pack(options...)...) + } + return msgs +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/path.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/path.go new file mode 100644 index 0000000..55748b0 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/path.go @@ -0,0 +1,1179 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "bytes" + "encoding/json" + "fmt" + "math" + "net" + "sort" + "time" + + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/pkg/packet/bgp" + + log "github.com/sirupsen/logrus" +) + +const ( + DEFAULT_LOCAL_PREF = 100 +) + +type Bitmap struct { + bitmap []uint64 +} + +func (b *Bitmap) Flag(i uint) { + b.bitmap[i/64] |= 1 << uint(i%64) +} + +func (b *Bitmap) Unflag(i uint) { + b.bitmap[i/64] &^= 1 << uint(i%64) +} + +func (b *Bitmap) GetFlag(i uint) bool { + return b.bitmap[i/64]&(1< 0 +} + +func (b *Bitmap) FindandSetZeroBit() (uint, error) { + for i := 0; i < len(b.bitmap); i++ { + if b.bitmap[i] == math.MaxUint64 { + continue + } + // replace this with TrailingZero64() when gobgp drops go 1.8 support. + for j := 0; j < 64; j++ { + v := ^b.bitmap[i] + if v&(1< 0 { + r := i*64 + j + b.Flag(uint(r)) + return uint(r), nil + } + } + } + return 0, fmt.Errorf("no space") +} + +func (b *Bitmap) Expand() { + old := b.bitmap + new := make([]uint64, len(old)+1) + for i := 0; i < len(old); i++ { + new[i] = old[i] + } + b.bitmap = new +} + +func NewBitmap(size int) *Bitmap { + b := &Bitmap{} + if size != 0 { + b.bitmap = make([]uint64, (size+64-1)/64) + } + return b +} + +type originInfo struct { + nlri bgp.AddrPrefixInterface + source *PeerInfo + timestamp int64 + validation *Validation + noImplicitWithdraw bool + isFromExternal bool + eor bool + stale bool +} + +type RpkiValidationReasonType string + +const ( + RPKI_VALIDATION_REASON_TYPE_NONE RpkiValidationReasonType = "none" + RPKI_VALIDATION_REASON_TYPE_AS RpkiValidationReasonType = "as" + RPKI_VALIDATION_REASON_TYPE_LENGTH RpkiValidationReasonType = "length" +) + +var RpkiValidationReasonTypeToIntMap = map[RpkiValidationReasonType]int{ + RPKI_VALIDATION_REASON_TYPE_NONE: 0, + RPKI_VALIDATION_REASON_TYPE_AS: 1, + RPKI_VALIDATION_REASON_TYPE_LENGTH: 2, +} + +func (v RpkiValidationReasonType) ToInt() int { + i, ok := RpkiValidationReasonTypeToIntMap[v] + if !ok { + return -1 + } + return i +} + +var IntToRpkiValidationReasonTypeMap = map[int]RpkiValidationReasonType{ + 0: RPKI_VALIDATION_REASON_TYPE_NONE, + 1: RPKI_VALIDATION_REASON_TYPE_AS, + 2: RPKI_VALIDATION_REASON_TYPE_LENGTH, +} + +type Validation struct { + Status config.RpkiValidationResultType + Reason RpkiValidationReasonType + Matched []*ROA + UnmatchedAs []*ROA + UnmatchedLength []*ROA +} + +type Path struct { + info *originInfo + parent *Path + pathAttrs []bgp.PathAttributeInterface + dels []bgp.BGPAttrType + attrsHash uint32 + aslooped bool + reason BestPathReason + + // For BGP Nexthop Tracking, this field shows if nexthop is invalidated by IGP. + IsNexthopInvalid bool + IsWithdraw bool +} + +func NewPath(source *PeerInfo, nlri bgp.AddrPrefixInterface, isWithdraw bool, pattrs []bgp.PathAttributeInterface, timestamp time.Time, noImplicitWithdraw bool) *Path { + if !isWithdraw && pattrs == nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": nlri.String(), + }).Error("Need to provide path attributes for non-withdrawn path.") + return nil + } + + return &Path{ + info: &originInfo{ + nlri: nlri, + source: source, + timestamp: timestamp.Unix(), + noImplicitWithdraw: noImplicitWithdraw, + }, + IsWithdraw: isWithdraw, + pathAttrs: pattrs, + } +} + +func NewEOR(family bgp.RouteFamily) *Path { + afi, safi := bgp.RouteFamilyToAfiSafi(family) + nlri, _ := bgp.NewPrefixFromRouteFamily(afi, safi) + return &Path{ + info: &originInfo{ + nlri: nlri, + eor: true, + }, + } +} + +func (path *Path) IsEOR() bool { + if path.info != nil && path.info.eor { + return true + } + return false +} + +func cloneAsPath(asAttr *bgp.PathAttributeAsPath) *bgp.PathAttributeAsPath { + newASparams := make([]bgp.AsPathParamInterface, len(asAttr.Value)) + for i, param := range asAttr.Value { + asList := param.GetAS() + as := make([]uint32, len(asList)) + copy(as, asList) + newASparams[i] = bgp.NewAs4PathParam(param.GetType(), as) + } + return bgp.NewPathAttributeAsPath(newASparams) +} + +func UpdatePathAttrs(global *config.Global, peer *config.Neighbor, info *PeerInfo, original *Path) *Path { + if peer.RouteServer.Config.RouteServerClient { + return original + } + path := original.Clone(original.IsWithdraw) + + for _, a := range path.GetPathAttrs() { + if _, y := bgp.PathAttrFlags[a.GetType()]; !y { + if a.GetFlags()&bgp.BGP_ATTR_FLAG_TRANSITIVE == 0 { + path.delPathAttr(a.GetType()) + } + } else { + switch a.GetType() { + case bgp.BGP_ATTR_TYPE_CLUSTER_LIST, bgp.BGP_ATTR_TYPE_ORIGINATOR_ID: + if !(peer.State.PeerType == config.PEER_TYPE_INTERNAL && peer.RouteReflector.Config.RouteReflectorClient) { + // send these attributes to only rr clients + path.delPathAttr(a.GetType()) + } + } + } + } + + localAddress := info.LocalAddress + nexthop := path.GetNexthop() + if peer.State.PeerType == config.PEER_TYPE_EXTERNAL { + // NEXTHOP handling + if !path.IsLocal() || nexthop.IsUnspecified() { + path.SetNexthop(localAddress) + } + + // remove-private-as handling + path.RemovePrivateAS(peer.Config.LocalAs, peer.State.RemovePrivateAs) + + // AS_PATH handling + confed := peer.IsConfederationMember(global) + path.PrependAsn(peer.Config.LocalAs, 1, confed) + if !confed { + path.removeConfedAs() + } + + // MED Handling + if med := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); med != nil && !path.IsLocal() { + path.delPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + } + + } else if peer.State.PeerType == config.PEER_TYPE_INTERNAL { + // NEXTHOP handling for iBGP + // if the path generated locally set local address as nexthop. + // if not, don't modify it. + // TODO: NEXT-HOP-SELF support + if path.IsLocal() && nexthop.IsUnspecified() { + path.SetNexthop(localAddress) + } + + // AS_PATH handling for iBGP + // if the path has AS_PATH path attribute, don't modify it. + // if not, attach *empty* AS_PATH path attribute. + if nh := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); nh == nil { + path.PrependAsn(0, 0, false) + } + + // For iBGP peers we are required to send local-pref attribute + // for connected or local prefixes. + // We set default local-pref 100. + if pref := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF); pref == nil { + path.setPathAttr(bgp.NewPathAttributeLocalPref(DEFAULT_LOCAL_PREF)) + } + + // RFC4456: BGP Route Reflection + // 8. Avoiding Routing Information Loops + info := path.GetSource() + if peer.RouteReflector.Config.RouteReflectorClient { + // This attribute will carry the BGP Identifier of the originator of the route in the local AS. + // A BGP speaker SHOULD NOT create an ORIGINATOR_ID attribute if one already exists. + // + // RFC4684 3.2 Intra-AS VPN Route Distribution + // When advertising RT membership NLRI to a route-reflector client, + // the Originator attribute shall be set to the router-id of the + // advertiser, and the Next-hop attribute shall be set of the local + // address for that session. + if path.GetRouteFamily() == bgp.RF_RTC_UC { + path.SetNexthop(localAddress) + path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.LocalID.String())) + } else if path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID) == nil { + if path.IsLocal() { + path.setPathAttr(bgp.NewPathAttributeOriginatorId(global.Config.RouterId)) + } else { + path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.ID.String())) + } + } + // When an RR reflects a route, it MUST prepend the local CLUSTER_ID to the CLUSTER_LIST. + // If the CLUSTER_LIST is empty, it MUST create a new one. + clusterID := string(peer.RouteReflector.State.RouteReflectorClusterId) + if p := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); p == nil { + path.setPathAttr(bgp.NewPathAttributeClusterList([]string{clusterID})) + } else { + clusterList := p.(*bgp.PathAttributeClusterList) + newClusterList := make([]string, 0, len(clusterList.Value)) + for _, ip := range clusterList.Value { + newClusterList = append(newClusterList, ip.String()) + } + path.setPathAttr(bgp.NewPathAttributeClusterList(append([]string{clusterID}, newClusterList...))) + } + } + + } else { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.State.NeighborAddress, + }).Warnf("invalid peer type: %v", peer.State.PeerType) + } + return path +} + +func (path *Path) GetTimestamp() time.Time { + return time.Unix(path.OriginInfo().timestamp, 0) +} + +func (path *Path) setTimestamp(t time.Time) { + path.OriginInfo().timestamp = t.Unix() +} + +func (path *Path) IsLocal() bool { + return path.GetSource().Address == nil +} + +func (path *Path) IsIBGP() bool { + return path.GetSource().AS == path.GetSource().LocalAS +} + +// create new PathAttributes +func (path *Path) Clone(isWithdraw bool) *Path { + return &Path{ + parent: path, + IsWithdraw: isWithdraw, + IsNexthopInvalid: path.IsNexthopInvalid, + attrsHash: path.attrsHash, + } +} + +func (path *Path) root() *Path { + p := path + for p.parent != nil { + p = p.parent + } + return p +} + +func (path *Path) OriginInfo() *originInfo { + return path.root().info +} + +func (path *Path) NoImplicitWithdraw() bool { + return path.OriginInfo().noImplicitWithdraw +} + +func (path *Path) Validation() *Validation { + return path.OriginInfo().validation +} + +func (path *Path) ValidationStatus() config.RpkiValidationResultType { + if v := path.OriginInfo().validation; v != nil { + return v.Status + } else { + return config.RPKI_VALIDATION_RESULT_TYPE_NONE + } +} + +func (path *Path) SetValidation(v *Validation) { + path.OriginInfo().validation = v +} + +func (path *Path) IsFromExternal() bool { + return path.OriginInfo().isFromExternal +} + +func (path *Path) SetIsFromExternal(y bool) { + path.OriginInfo().isFromExternal = y +} + +func (path *Path) GetRouteFamily() bgp.RouteFamily { + return bgp.AfiSafiToRouteFamily(path.OriginInfo().nlri.AFI(), path.OriginInfo().nlri.SAFI()) +} + +func (path *Path) SetSource(source *PeerInfo) { + path.OriginInfo().source = source +} +func (path *Path) GetSource() *PeerInfo { + return path.OriginInfo().source +} + +func (path *Path) MarkStale(s bool) { + path.OriginInfo().stale = s +} + +func (path *Path) IsStale() bool { + return path.OriginInfo().stale +} + +func (path *Path) IsAsLooped() bool { + return path.aslooped +} + +func (path *Path) SetAsLooped(y bool) { + path.aslooped = y +} + +func (path *Path) IsLLGRStale() bool { + for _, c := range path.GetCommunities() { + if c == uint32(bgp.COMMUNITY_LLGR_STALE) { + return true + } + } + return false +} + +func (path *Path) GetSourceAs() uint32 { + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + if attr != nil { + asPathParam := attr.(*bgp.PathAttributeAsPath).Value + if len(asPathParam) == 0 { + return 0 + } + asList := asPathParam[len(asPathParam)-1].GetAS() + if len(asList) == 0 { + return 0 + } + return asList[len(asList)-1] + } + return 0 +} + +func (path *Path) GetNexthop() net.IP { + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) + if attr != nil { + return attr.(*bgp.PathAttributeNextHop).Value + } + attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI) + if attr != nil { + return attr.(*bgp.PathAttributeMpReachNLRI).Nexthop + } + return net.IP{} +} + +func (path *Path) SetNexthop(nexthop net.IP) { + if path.GetRouteFamily() == bgp.RF_IPv4_UC && nexthop.To4() == nil { + path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) + mpreach := bgp.NewPathAttributeMpReachNLRI(nexthop.String(), []bgp.AddrPrefixInterface{path.GetNlri()}) + path.setPathAttr(mpreach) + return + } + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) + if attr != nil { + path.setPathAttr(bgp.NewPathAttributeNextHop(nexthop.String())) + } + attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI) + if attr != nil { + oldNlri := attr.(*bgp.PathAttributeMpReachNLRI) + path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nexthop.String(), oldNlri.Value)) + } +} + +func (path *Path) GetNlri() bgp.AddrPrefixInterface { + return path.OriginInfo().nlri +} + +type PathAttrs []bgp.PathAttributeInterface + +func (a PathAttrs) Len() int { + return len(a) +} + +func (a PathAttrs) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a PathAttrs) Less(i, j int) bool { + return a[i].GetType() < a[j].GetType() +} + +func (path *Path) GetPathAttrs() []bgp.PathAttributeInterface { + deleted := NewBitmap(math.MaxUint8) + modified := make(map[uint]bgp.PathAttributeInterface) + p := path + for { + for _, t := range p.dels { + deleted.Flag(uint(t)) + } + if p.parent == nil { + list := PathAttrs(make([]bgp.PathAttributeInterface, 0, len(p.pathAttrs))) + // we assume that the original pathAttrs are + // in order, that is, other bgp speakers send + // attributes in order. + for _, a := range p.pathAttrs { + typ := uint(a.GetType()) + if m, ok := modified[typ]; ok { + list = append(list, m) + delete(modified, typ) + } else if !deleted.GetFlag(typ) { + list = append(list, a) + } + } + if len(modified) > 0 { + // Huh, some attributes were newly + // added. So we need to sort... + for _, m := range modified { + list = append(list, m) + } + sort.Sort(list) + } + return list + } else { + for _, a := range p.pathAttrs { + typ := uint(a.GetType()) + if _, ok := modified[typ]; !deleted.GetFlag(typ) && !ok { + modified[typ] = a + } + } + } + p = p.parent + } +} + +func (path *Path) getPathAttr(typ bgp.BGPAttrType) bgp.PathAttributeInterface { + p := path + for { + for _, t := range p.dels { + if t == typ { + return nil + } + } + for _, a := range p.pathAttrs { + if a.GetType() == typ { + return a + } + } + if p.parent == nil { + return nil + } + p = p.parent + } +} + +func (path *Path) setPathAttr(a bgp.PathAttributeInterface) { + if len(path.pathAttrs) == 0 { + path.pathAttrs = []bgp.PathAttributeInterface{a} + } else { + for i, b := range path.pathAttrs { + if a.GetType() == b.GetType() { + path.pathAttrs[i] = a + return + } + } + path.pathAttrs = append(path.pathAttrs, a) + } +} + +func (path *Path) delPathAttr(typ bgp.BGPAttrType) { + if len(path.dels) == 0 { + path.dels = []bgp.BGPAttrType{typ} + } else { + path.dels = append(path.dels, typ) + } +} + +// return Path's string representation +func (path *Path) String() string { + s := bytes.NewBuffer(make([]byte, 0, 64)) + if path.IsEOR() { + s.WriteString(fmt.Sprintf("{ %s EOR | src: %s }", path.GetRouteFamily(), path.GetSource())) + return s.String() + } + s.WriteString(fmt.Sprintf("{ %s | ", path.getPrefix())) + s.WriteString(fmt.Sprintf("src: %s", path.GetSource())) + s.WriteString(fmt.Sprintf(", nh: %s", path.GetNexthop())) + if path.IsNexthopInvalid { + s.WriteString(" (not reachable)") + } + if path.IsWithdraw { + s.WriteString(", withdraw") + } + s.WriteString(" }") + return s.String() +} + +func (path *Path) getPrefix() string { + return path.GetNlri().String() +} + +func (path *Path) GetAsPath() *bgp.PathAttributeAsPath { + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + if attr != nil { + return attr.(*bgp.PathAttributeAsPath) + } + return nil +} + +// GetAsPathLen returns the number of AS_PATH +func (path *Path) GetAsPathLen() int { + + var length int = 0 + if aspath := path.GetAsPath(); aspath != nil { + for _, as := range aspath.Value { + length += as.ASLen() + } + } + return length +} + +func (path *Path) GetAsString() string { + s := bytes.NewBuffer(make([]byte, 0, 64)) + if aspath := path.GetAsPath(); aspath != nil { + return bgp.AsPathString(aspath) + } + return s.String() +} + +func (path *Path) GetAsList() []uint32 { + return path.getAsListOfSpecificType(true, true) + +} + +func (path *Path) GetAsSeqList() []uint32 { + return path.getAsListOfSpecificType(true, false) + +} + +func (path *Path) getAsListOfSpecificType(getAsSeq, getAsSet bool) []uint32 { + asList := []uint32{} + if aspath := path.GetAsPath(); aspath != nil { + for _, param := range aspath.Value { + segType := param.GetType() + if getAsSeq && segType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ { + asList = append(asList, param.GetAS()...) + continue + } + if getAsSet && segType == bgp.BGP_ASPATH_ATTR_TYPE_SET { + asList = append(asList, param.GetAS()...) + } else { + asList = append(asList, 0) + } + } + } + return asList +} + +func (path *Path) GetLabelString() string { + return bgp.LabelString(path.GetNlri()) +} + +// PrependAsn prepends AS number. +// This function updates the AS_PATH attribute as follows. +// (If the peer is in the confederation member AS, +// replace AS_SEQUENCE in the following sentence with AS_CONFED_SEQUENCE.) +// 1) if the first path segment of the AS_PATH is of type +// AS_SEQUENCE, the local system prepends the specified AS num as +// the last element of the sequence (put it in the left-most +// position with respect to the position of octets in the +// protocol message) the specified number of times. +// If the act of prepending will cause an overflow in the AS_PATH +// segment (i.e., more than 255 ASes), +// it SHOULD prepend a new segment of type AS_SEQUENCE +// and prepend its own AS number to this new segment. +// +// 2) if the first path segment of the AS_PATH is of other than type +// AS_SEQUENCE, the local system prepends a new path segment of type +// AS_SEQUENCE to the AS_PATH, including the specified AS number in +// that segment. +// +// 3) if the AS_PATH is empty, the local system creates a path +// segment of type AS_SEQUENCE, places the specified AS number +// into that segment, and places that segment into the AS_PATH. +func (path *Path) PrependAsn(asn uint32, repeat uint8, confed bool) { + var segType uint8 + if confed { + segType = bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ + } else { + segType = bgp.BGP_ASPATH_ATTR_TYPE_SEQ + } + + original := path.GetAsPath() + + asns := make([]uint32, repeat) + for i := range asns { + asns[i] = asn + } + + var asPath *bgp.PathAttributeAsPath + if original == nil { + asPath = bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{}) + } else { + asPath = cloneAsPath(original) + } + + if len(asPath.Value) > 0 { + param := asPath.Value[0] + asList := param.GetAS() + if param.GetType() == segType { + if int(repeat)+len(asList) > 255 { + repeat = uint8(255 - len(asList)) + } + newAsList := append(asns[:int(repeat)], asList...) + asPath.Value[0] = bgp.NewAs4PathParam(segType, newAsList) + asns = asns[int(repeat):] + } + } + + if len(asns) > 0 { + p := bgp.NewAs4PathParam(segType, asns) + asPath.Value = append([]bgp.AsPathParamInterface{p}, asPath.Value...) + } + path.setPathAttr(asPath) +} + +func isPrivateAS(as uint32) bool { + return (64512 <= as && as <= 65534) || (4200000000 <= as && as <= 4294967294) +} + +func (path *Path) RemovePrivateAS(localAS uint32, option config.RemovePrivateAsOption) { + original := path.GetAsPath() + if original == nil { + return + } + switch option { + case config.REMOVE_PRIVATE_AS_OPTION_ALL, config.REMOVE_PRIVATE_AS_OPTION_REPLACE: + newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value)) + for _, param := range original.Value { + asList := param.GetAS() + newASParam := make([]uint32, 0, len(asList)) + for _, as := range asList { + if isPrivateAS(as) { + if option == config.REMOVE_PRIVATE_AS_OPTION_REPLACE { + newASParam = append(newASParam, localAS) + } + } else { + newASParam = append(newASParam, as) + } + } + if len(newASParam) > 0 { + newASParams = append(newASParams, bgp.NewAs4PathParam(param.GetType(), newASParam)) + } + } + path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams)) + } +} + +func (path *Path) removeConfedAs() { + original := path.GetAsPath() + if original == nil { + return + } + newAsParams := make([]bgp.AsPathParamInterface, 0, len(original.Value)) + for _, param := range original.Value { + switch param.GetType() { + case bgp.BGP_ASPATH_ATTR_TYPE_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_SET: + newAsParams = append(newAsParams, param) + } + } + path.setPathAttr(bgp.NewPathAttributeAsPath(newAsParams)) +} + +func (path *Path) ReplaceAS(localAS, peerAS uint32) *Path { + original := path.GetAsPath() + if original == nil { + return path + } + newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value)) + changed := false + for _, param := range original.Value { + segType := param.GetType() + asList := param.GetAS() + newASParam := make([]uint32, 0, len(asList)) + for _, as := range asList { + if as == peerAS { + as = localAS + changed = true + } + newASParam = append(newASParam, as) + } + newASParams = append(newASParams, bgp.NewAs4PathParam(segType, newASParam)) + } + if changed { + path = path.Clone(path.IsWithdraw) + path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams)) + } + return path +} + +func (path *Path) GetCommunities() []uint32 { + communityList := []uint32{} + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES); attr != nil { + communities := attr.(*bgp.PathAttributeCommunities) + communityList = append(communityList, communities.Value...) + } + return communityList +} + +// SetCommunities adds or replaces communities with new ones. +// If the length of communities is 0 and doReplace is true, it clears communities. +func (path *Path) SetCommunities(communities []uint32, doReplace bool) { + + if len(communities) == 0 && doReplace { + // clear communities + path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES) + return + } + + newList := make([]uint32, 0) + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES) + if attr != nil { + c := attr.(*bgp.PathAttributeCommunities) + if doReplace { + newList = append(newList, communities...) + } else { + newList = append(newList, c.Value...) + newList = append(newList, communities...) + } + } else { + newList = append(newList, communities...) + } + path.setPathAttr(bgp.NewPathAttributeCommunities(newList)) + +} + +// RemoveCommunities removes specific communities. +// If the length of communities is 0, it does nothing. +// If all communities are removed, it removes Communities path attribute itself. +func (path *Path) RemoveCommunities(communities []uint32) int { + + if len(communities) == 0 { + // do nothing + return 0 + } + + find := func(val uint32) bool { + for _, com := range communities { + if com == val { + return true + } + } + return false + } + + count := 0 + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES) + if attr != nil { + newList := make([]uint32, 0) + c := attr.(*bgp.PathAttributeCommunities) + + for _, value := range c.Value { + if find(value) { + count += 1 + } else { + newList = append(newList, value) + } + } + + if len(newList) != 0 { + path.setPathAttr(bgp.NewPathAttributeCommunities(newList)) + } else { + path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES) + } + } + return count +} + +func (path *Path) GetExtCommunities() []bgp.ExtendedCommunityInterface { + eCommunityList := make([]bgp.ExtendedCommunityInterface, 0) + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES); attr != nil { + eCommunities := attr.(*bgp.PathAttributeExtendedCommunities).Value + eCommunityList = append(eCommunityList, eCommunities...) + } + return eCommunityList +} + +func (path *Path) SetExtCommunities(exts []bgp.ExtendedCommunityInterface, doReplace bool) { + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES) + if attr != nil { + l := attr.(*bgp.PathAttributeExtendedCommunities).Value + if doReplace { + l = exts + } else { + l = append(l, exts...) + } + path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(l)) + } else { + path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(exts)) + } +} + +func (path *Path) GetLargeCommunities() []*bgp.LargeCommunity { + if a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY); a != nil { + v := a.(*bgp.PathAttributeLargeCommunities).Values + ret := make([]*bgp.LargeCommunity, 0, len(v)) + ret = append(ret, v...) + return ret + } + return nil +} + +func (path *Path) SetLargeCommunities(cs []*bgp.LargeCommunity, doReplace bool) { + a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY) + if a == nil || doReplace { + path.setPathAttr(bgp.NewPathAttributeLargeCommunities(cs)) + } else { + l := a.(*bgp.PathAttributeLargeCommunities).Values + path.setPathAttr(bgp.NewPathAttributeLargeCommunities(append(l, cs...))) + } +} + +func (path *Path) GetMed() (uint32, error) { + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + if attr == nil { + return 0, fmt.Errorf("no med path attr") + } + return attr.(*bgp.PathAttributeMultiExitDisc).Value, nil +} + +// SetMed replace, add or subtraction med with new ones. +func (path *Path) SetMed(med int64, doReplace bool) error { + parseMed := func(orgMed uint32, med int64, doReplace bool) (*bgp.PathAttributeMultiExitDisc, error) { + if doReplace { + return bgp.NewPathAttributeMultiExitDisc(uint32(med)), nil + } + + medVal := int64(orgMed) + med + if medVal < 0 { + return nil, fmt.Errorf("med value invalid. it's underflow threshold: %v", medVal) + } else if medVal > int64(math.MaxUint32) { + return nil, fmt.Errorf("med value invalid. it's overflow threshold: %v", medVal) + } + + return bgp.NewPathAttributeMultiExitDisc(uint32(int64(orgMed) + med)), nil + } + + m := uint32(0) + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); attr != nil { + m = attr.(*bgp.PathAttributeMultiExitDisc).Value + } + newMed, err := parseMed(m, med, doReplace) + if err != nil { + return err + } + path.setPathAttr(newMed) + return nil +} + +func (path *Path) RemoveLocalPref() { + if path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) != nil { + path.delPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) + } +} + +func (path *Path) GetOriginatorID() net.IP { + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID); attr != nil { + return attr.(*bgp.PathAttributeOriginatorId).Value + } + return nil +} + +func (path *Path) GetClusterList() []net.IP { + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); attr != nil { + return attr.(*bgp.PathAttributeClusterList).Value + } + return nil +} + +func (path *Path) GetOrigin() (uint8, error) { + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN); attr != nil { + return attr.(*bgp.PathAttributeOrigin).Value, nil + } + return 0, fmt.Errorf("no origin path attr") +} + +func (path *Path) GetLocalPref() (uint32, error) { + lp := uint32(DEFAULT_LOCAL_PREF) + attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) + if attr != nil { + lp = attr.(*bgp.PathAttributeLocalPref).Value + } + return lp, nil +} + +func (lhs *Path) Equal(rhs *Path) bool { + if rhs == nil { + return false + } + + if !lhs.GetSource().Equal(rhs.GetSource()) { + return false + } + + pattrs := func(arg []bgp.PathAttributeInterface) []byte { + ret := make([]byte, 0) + for _, a := range arg { + aa, _ := a.Serialize() + ret = append(ret, aa...) + } + return ret + } + return bytes.Equal(pattrs(lhs.GetPathAttrs()), pattrs(rhs.GetPathAttrs())) +} + +func (path *Path) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Nlri bgp.AddrPrefixInterface `json:"nlri"` + PathAttrs []bgp.PathAttributeInterface `json:"attrs"` + Age int64 `json:"age"` + Withdrawal bool `json:"withdrawal,omitempty"` + Validation string `json:"validation,omitempty"` + SourceID net.IP `json:"source-id,omitempty"` + NeighborIP net.IP `json:"neighbor-ip,omitempty"` + Stale bool `json:"stale,omitempty"` + UUID string `json:"uuid,omitempty"` + ID uint32 `json:"id,omitempty"` + }{ + Nlri: path.GetNlri(), + PathAttrs: path.GetPathAttrs(), + Age: path.GetTimestamp().Unix(), + Withdrawal: path.IsWithdraw, + Validation: string(path.ValidationStatus()), + SourceID: path.GetSource().ID, + NeighborIP: path.GetSource().Address, + Stale: path.IsStale(), + ID: path.GetNlri().PathIdentifier(), + }) +} + +func (lhs *Path) Compare(rhs *Path) int { + if lhs.IsLocal() && !rhs.IsLocal() { + return 1 + } else if !lhs.IsLocal() && rhs.IsLocal() { + return -1 + } + + if !lhs.IsIBGP() && rhs.IsIBGP() { + return 1 + } else if lhs.IsIBGP() && !rhs.IsIBGP() { + return -1 + } + + lp1, _ := lhs.GetLocalPref() + lp2, _ := rhs.GetLocalPref() + if lp1 != lp2 { + return int(lp1 - lp2) + } + + l1 := lhs.GetAsPathLen() + l2 := rhs.GetAsPathLen() + if l1 != l2 { + return int(l2 - l1) + } + + o1, _ := lhs.GetOrigin() + o2, _ := rhs.GetOrigin() + if o1 != o2 { + return int(o2 - o1) + } + + m1, _ := lhs.GetMed() + m2, _ := rhs.GetMed() + return int(m2 - m1) +} + +func (v *Vrf) ToGlobalPath(path *Path) error { + nlri := path.GetNlri() + switch rf := path.GetRouteFamily(); rf { + case bgp.RF_IPv4_UC: + n := nlri.(*bgp.IPAddrPrefix) + pathIdentifier := path.GetNlri().PathIdentifier() + path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd) + path.GetNlri().SetPathIdentifier(pathIdentifier) + case bgp.RF_IPv6_UC: + n := nlri.(*bgp.IPv6AddrPrefix) + pathIdentifier := path.GetNlri().PathIdentifier() + path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd) + path.GetNlri().SetPathIdentifier(pathIdentifier) + case bgp.RF_EVPN: + n := nlri.(*bgp.EVPNNLRI) + switch n.RouteType { + case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT: + n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).RD = v.Rd + case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG: + n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute).RD = v.Rd + } + default: + return fmt.Errorf("unsupported route family for vrf: %s", rf) + } + path.SetExtCommunities(v.ExportRt, false) + return nil +} + +func (p *Path) ToGlobal(vrf *Vrf) *Path { + nlri := p.GetNlri() + nh := p.GetNexthop() + pathId := nlri.PathIdentifier() + switch rf := p.GetRouteFamily(); rf { + case bgp.RF_IPv4_UC: + n := nlri.(*bgp.IPAddrPrefix) + nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd) + nlri.SetPathIdentifier(pathId) + case bgp.RF_IPv6_UC: + n := nlri.(*bgp.IPv6AddrPrefix) + nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd) + nlri.SetPathIdentifier(pathId) + case bgp.RF_EVPN: + n := nlri.(*bgp.EVPNNLRI) + switch n.RouteType { + case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT: + old := n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute) + new := &bgp.EVPNMacIPAdvertisementRoute{ + RD: vrf.Rd, + ESI: old.ESI, + ETag: old.ETag, + MacAddressLength: old.MacAddressLength, + MacAddress: old.MacAddress, + IPAddressLength: old.IPAddressLength, + IPAddress: old.IPAddress, + Labels: old.Labels, + } + nlri = bgp.NewEVPNNLRI(n.RouteType, new) + case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG: + old := n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute) + new := &bgp.EVPNMulticastEthernetTagRoute{ + RD: vrf.Rd, + ETag: old.ETag, + IPAddressLength: old.IPAddressLength, + IPAddress: old.IPAddress, + } + nlri = bgp.NewEVPNNLRI(n.RouteType, new) + } + default: + return p + } + path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false) + path.SetExtCommunities(vrf.ExportRt, false) + path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) + path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nh.String(), []bgp.AddrPrefixInterface{nlri})) + path.IsNexthopInvalid = p.IsNexthopInvalid + return path +} + +func (p *Path) ToLocal() *Path { + nlri := p.GetNlri() + f := p.GetRouteFamily() + pathId := nlri.PathLocalIdentifier() + switch f { + case bgp.RF_IPv4_VPN: + n := nlri.(*bgp.LabeledVPNIPAddrPrefix) + _, c, _ := net.ParseCIDR(n.IPPrefix()) + ones, _ := c.Mask.Size() + nlri = bgp.NewIPAddrPrefix(uint8(ones), c.IP.String()) + nlri.SetPathLocalIdentifier(pathId) + case bgp.RF_IPv6_VPN: + n := nlri.(*bgp.LabeledVPNIPv6AddrPrefix) + _, c, _ := net.ParseCIDR(n.IPPrefix()) + ones, _ := c.Mask.Size() + nlri = bgp.NewIPv6AddrPrefix(uint8(ones), c.IP.String()) + nlri.SetPathLocalIdentifier(pathId) + default: + return p + } + path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false) + path.delPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES) + + if f == bgp.RF_IPv4_VPN { + nh := path.GetNexthop() + path.delPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI) + path.setPathAttr(bgp.NewPathAttributeNextHop(nh.String())) + } + path.IsNexthopInvalid = p.IsNexthopInvalid + return path +} + +func (p *Path) SetHash(v uint32) { + p.attrsHash = v +} + +func (p *Path) GetHash() uint32 { + return p.attrsHash +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/policy.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/policy.go new file mode 100644 index 0000000..44feebd --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/policy.go @@ -0,0 +1,3895 @@ +// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "sync" + + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/pkg/packet/bgp" + + radix "github.com/armon/go-radix" + log "github.com/sirupsen/logrus" +) + +type PolicyOptions struct { + Info *PeerInfo + ValidationResult *Validation + OldNextHop net.IP +} + +type DefinedType int + +const ( + DEFINED_TYPE_PREFIX DefinedType = iota + DEFINED_TYPE_NEIGHBOR + DEFINED_TYPE_TAG + DEFINED_TYPE_AS_PATH + DEFINED_TYPE_COMMUNITY + DEFINED_TYPE_EXT_COMMUNITY + DEFINED_TYPE_LARGE_COMMUNITY + DEFINED_TYPE_NEXT_HOP +) + +type RouteType int + +const ( + ROUTE_TYPE_NONE RouteType = iota + ROUTE_TYPE_ACCEPT + ROUTE_TYPE_REJECT +) + +func (t RouteType) String() string { + switch t { + case ROUTE_TYPE_NONE: + return "continue" + case ROUTE_TYPE_ACCEPT: + return "accept" + case ROUTE_TYPE_REJECT: + return "reject" + } + return fmt.Sprintf("unknown(%d)", t) +} + +type PolicyDirection int + +const ( + POLICY_DIRECTION_NONE PolicyDirection = iota + POLICY_DIRECTION_IMPORT + POLICY_DIRECTION_EXPORT +) + +func (d PolicyDirection) String() string { + switch d { + case POLICY_DIRECTION_IMPORT: + return "import" + case POLICY_DIRECTION_EXPORT: + return "export" + } + return fmt.Sprintf("unknown(%d)", d) +} + +type MatchOption int + +const ( + MATCH_OPTION_ANY MatchOption = iota + MATCH_OPTION_ALL + MATCH_OPTION_INVERT +) + +func (o MatchOption) String() string { + switch o { + case MATCH_OPTION_ANY: + return "any" + case MATCH_OPTION_ALL: + return "all" + case MATCH_OPTION_INVERT: + return "invert" + default: + return fmt.Sprintf("MatchOption(%d)", o) + } +} + +func (o MatchOption) ConvertToMatchSetOptionsRestrictedType() config.MatchSetOptionsRestrictedType { + switch o { + case MATCH_OPTION_ANY: + return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY + case MATCH_OPTION_INVERT: + return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT + } + return "unknown" +} + +type MedActionType int + +const ( + MED_ACTION_MOD MedActionType = iota + MED_ACTION_REPLACE +) + +var CommunityOptionNameMap = map[config.BgpSetCommunityOptionType]string{ + config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: "add", + config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: "remove", + config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: "replace", +} + +var CommunityOptionValueMap = map[string]config.BgpSetCommunityOptionType{ + CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD]: config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD, + CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE, + CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE, +} + +type ConditionType int + +const ( + CONDITION_PREFIX ConditionType = iota + CONDITION_NEIGHBOR + CONDITION_AS_PATH + CONDITION_COMMUNITY + CONDITION_EXT_COMMUNITY + CONDITION_AS_PATH_LENGTH + CONDITION_RPKI + CONDITION_ROUTE_TYPE + CONDITION_LARGE_COMMUNITY + CONDITION_NEXT_HOP + CONDITION_AFI_SAFI_IN +) + +type ActionType int + +const ( + ACTION_ROUTING ActionType = iota + ACTION_COMMUNITY + ACTION_EXT_COMMUNITY + ACTION_MED + ACTION_AS_PATH_PREPEND + ACTION_NEXTHOP + ACTION_LOCAL_PREF + ACTION_LARGE_COMMUNITY +) + +func NewMatchOption(c interface{}) (MatchOption, error) { + switch t := c.(type) { + case config.MatchSetOptionsType: + t = t.DefaultAsNeeded() + switch t { + case config.MATCH_SET_OPTIONS_TYPE_ANY: + return MATCH_OPTION_ANY, nil + case config.MATCH_SET_OPTIONS_TYPE_ALL: + return MATCH_OPTION_ALL, nil + case config.MATCH_SET_OPTIONS_TYPE_INVERT: + return MATCH_OPTION_INVERT, nil + } + case config.MatchSetOptionsRestrictedType: + t = t.DefaultAsNeeded() + switch t { + case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY: + return MATCH_OPTION_ANY, nil + case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT: + return MATCH_OPTION_INVERT, nil + } + } + return MATCH_OPTION_ANY, fmt.Errorf("invalid argument to create match option: %v", c) +} + +type AttributeComparison int + +const ( + // "== comparison" + ATTRIBUTE_EQ AttributeComparison = iota + // ">= comparison" + ATTRIBUTE_GE + // "<= comparison" + ATTRIBUTE_LE +) + +func (c AttributeComparison) String() string { + switch c { + case ATTRIBUTE_EQ: + return "=" + case ATTRIBUTE_GE: + return ">=" + case ATTRIBUTE_LE: + return "<=" + } + return "?" +} + +const ( + ASPATH_REGEXP_MAGIC = "(^|[,{}() ]|$)" +) + +type DefinedSet interface { + Type() DefinedType + Name() string + Append(DefinedSet) error + Remove(DefinedSet) error + Replace(DefinedSet) error + String() string + List() []string +} + +type DefinedSetMap map[DefinedType]map[string]DefinedSet + +type DefinedSetList []DefinedSet + +func (l DefinedSetList) Len() int { + return len(l) +} + +func (l DefinedSetList) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func (l DefinedSetList) Less(i, j int) bool { + if l[i].Type() != l[j].Type() { + return l[i].Type() < l[j].Type() + } + return l[i].Name() < l[j].Name() +} + +type Prefix struct { + Prefix *net.IPNet + AddressFamily bgp.RouteFamily + MasklengthRangeMax uint8 + MasklengthRangeMin uint8 +} + +func (p *Prefix) Match(path *Path) bool { + rf := path.GetRouteFamily() + if rf != p.AddressFamily { + return false + } + + var pAddr net.IP + var pMasklen uint8 + switch rf { + case bgp.RF_IPv4_UC: + pAddr = path.GetNlri().(*bgp.IPAddrPrefix).Prefix + pMasklen = path.GetNlri().(*bgp.IPAddrPrefix).Length + case bgp.RF_IPv6_UC: + pAddr = path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix + pMasklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length + default: + return false + } + + return (p.MasklengthRangeMin <= pMasklen && pMasklen <= p.MasklengthRangeMax) && p.Prefix.Contains(pAddr) +} + +func (lhs *Prefix) Equal(rhs *Prefix) bool { + if lhs == rhs { + return true + } + if rhs == nil { + return false + } + return lhs.Prefix.String() == rhs.Prefix.String() && lhs.MasklengthRangeMin == rhs.MasklengthRangeMin && lhs.MasklengthRangeMax == rhs.MasklengthRangeMax +} + +func (p *Prefix) PrefixString() string { + isZeros := func(p net.IP) bool { + for i := 0; i < len(p); i++ { + if p[i] != 0 { + return false + } + } + return true + } + + ip := p.Prefix.IP + if p.AddressFamily == bgp.RF_IPv6_UC && isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff { + m, _ := p.Prefix.Mask.Size() + return fmt.Sprintf("::FFFF:%s/%d", ip.To16(), m) + } + return p.Prefix.String() +} + +var _regexpPrefixRange = regexp.MustCompile(`(\d+)\.\.(\d+)`) + +func NewPrefix(c config.Prefix) (*Prefix, error) { + _, prefix, err := net.ParseCIDR(c.IpPrefix) + if err != nil { + return nil, err + } + + rf := bgp.RF_IPv4_UC + if strings.Contains(c.IpPrefix, ":") { + rf = bgp.RF_IPv6_UC + } + p := &Prefix{ + Prefix: prefix, + AddressFamily: rf, + } + maskRange := c.MasklengthRange + + if maskRange == "" { + l, _ := prefix.Mask.Size() + maskLength := uint8(l) + p.MasklengthRangeMax = maskLength + p.MasklengthRangeMin = maskLength + return p, nil + } + + elems := _regexpPrefixRange.FindStringSubmatch(maskRange) + if len(elems) != 3 { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Prefix", + "MaskRangeFormat": maskRange, + }).Warn("mask length range format is invalid.") + return nil, fmt.Errorf("mask length range format is invalid") + } + + // we've already checked the range is sane by regexp + min, _ := strconv.ParseUint(elems[1], 10, 8) + max, _ := strconv.ParseUint(elems[2], 10, 8) + p.MasklengthRangeMin = uint8(min) + p.MasklengthRangeMax = uint8(max) + return p, nil +} + +type PrefixSet struct { + name string + tree *radix.Tree + family bgp.RouteFamily +} + +func (s *PrefixSet) Name() string { + return s.name +} + +func (s *PrefixSet) Type() DefinedType { + return DEFINED_TYPE_PREFIX +} + +func (lhs *PrefixSet) Append(arg DefinedSet) error { + rhs, ok := arg.(*PrefixSet) + if !ok { + return fmt.Errorf("type cast failed") + } + // if either is empty, family can be ignored. + if lhs.tree.Len() != 0 && rhs.tree.Len() != 0 { + _, w, _ := lhs.tree.Minimum() + l := w.([]*Prefix) + _, v, _ := rhs.tree.Minimum() + r := v.([]*Prefix) + if l[0].AddressFamily != r[0].AddressFamily { + return fmt.Errorf("can't append different family") + } + } + rhs.tree.Walk(func(key string, v interface{}) bool { + w, ok := lhs.tree.Get(key) + if ok { + r := v.([]*Prefix) + l := w.([]*Prefix) + lhs.tree.Insert(key, append(l, r...)) + } else { + lhs.tree.Insert(key, v) + } + return false + }) + _, w, _ := lhs.tree.Minimum() + lhs.family = w.([]*Prefix)[0].AddressFamily + return nil +} + +func (lhs *PrefixSet) Remove(arg DefinedSet) error { + rhs, ok := arg.(*PrefixSet) + if !ok { + return fmt.Errorf("type cast failed") + } + rhs.tree.Walk(func(key string, v interface{}) bool { + w, ok := lhs.tree.Get(key) + if !ok { + return false + } + r := v.([]*Prefix) + l := w.([]*Prefix) + new := make([]*Prefix, 0, len(l)) + for _, lp := range l { + delete := false + for _, rp := range r { + if lp.Equal(rp) { + delete = true + break + } + } + if !delete { + new = append(new, lp) + } + } + if len(new) == 0 { + lhs.tree.Delete(key) + } else { + lhs.tree.Insert(key, new) + } + return false + }) + return nil +} + +func (lhs *PrefixSet) Replace(arg DefinedSet) error { + rhs, ok := arg.(*PrefixSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.tree = rhs.tree + lhs.family = rhs.family + return nil +} + +func (s *PrefixSet) List() []string { + var list []string + s.tree.Walk(func(s string, v interface{}) bool { + ps := v.([]*Prefix) + for _, p := range ps { + list = append(list, fmt.Sprintf("%s %d..%d", p.PrefixString(), p.MasklengthRangeMin, p.MasklengthRangeMax)) + } + return false + }) + return list +} + +func (s *PrefixSet) ToConfig() *config.PrefixSet { + list := make([]config.Prefix, 0, s.tree.Len()) + s.tree.Walk(func(s string, v interface{}) bool { + ps := v.([]*Prefix) + for _, p := range ps { + list = append(list, config.Prefix{IpPrefix: p.PrefixString(), MasklengthRange: fmt.Sprintf("%d..%d", p.MasklengthRangeMin, p.MasklengthRangeMax)}) + } + return false + }) + return &config.PrefixSet{ + PrefixSetName: s.name, + PrefixList: list, + } +} + +func (s *PrefixSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *PrefixSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +func NewPrefixSetFromApiStruct(name string, prefixes []*Prefix) (*PrefixSet, error) { + if name == "" { + return nil, fmt.Errorf("empty prefix set name") + } + tree := radix.New() + var family bgp.RouteFamily + for i, x := range prefixes { + if i == 0 { + family = x.AddressFamily + } else if family != x.AddressFamily { + return nil, fmt.Errorf("multiple families") + } + key := CidrToRadixkey(x.Prefix.String()) + d, ok := tree.Get(key) + if ok { + ps := d.([]*Prefix) + tree.Insert(key, append(ps, x)) + } else { + tree.Insert(key, []*Prefix{x}) + } + } + return &PrefixSet{ + name: name, + tree: tree, + family: family, + }, nil +} + +func NewPrefixSet(c config.PrefixSet) (*PrefixSet, error) { + name := c.PrefixSetName + if name == "" { + if len(c.PrefixList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty prefix set name") + } + tree := radix.New() + var family bgp.RouteFamily + for i, x := range c.PrefixList { + y, err := NewPrefix(x) + if err != nil { + return nil, err + } + if i == 0 { + family = y.AddressFamily + } else if family != y.AddressFamily { + return nil, fmt.Errorf("multiple families") + } + key := CidrToRadixkey(y.Prefix.String()) + d, ok := tree.Get(key) + if ok { + ps := d.([]*Prefix) + tree.Insert(key, append(ps, y)) + } else { + tree.Insert(key, []*Prefix{y}) + } + } + return &PrefixSet{ + name: name, + tree: tree, + family: family, + }, nil +} + +type NextHopSet struct { + list []net.IPNet +} + +func (s *NextHopSet) Name() string { + return "NextHopSet: NO NAME" +} + +func (s *NextHopSet) Type() DefinedType { + return DEFINED_TYPE_NEXT_HOP +} + +func (lhs *NextHopSet) Append(arg DefinedSet) error { + rhs, ok := arg.(*NextHopSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.list = append(lhs.list, rhs.list...) + return nil +} + +func (lhs *NextHopSet) Remove(arg DefinedSet) error { + rhs, ok := arg.(*NextHopSet) + if !ok { + return fmt.Errorf("type cast failed") + } + ps := make([]net.IPNet, 0, len(lhs.list)) + for _, x := range lhs.list { + found := false + for _, y := range rhs.list { + if x.String() == y.String() { + found = true + break + } + } + if !found { + ps = append(ps, x) + } + } + lhs.list = ps + return nil +} + +func (lhs *NextHopSet) Replace(arg DefinedSet) error { + rhs, ok := arg.(*NextHopSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.list = rhs.list + return nil +} + +func (s *NextHopSet) List() []string { + list := make([]string, 0, len(s.list)) + for _, n := range s.list { + list = append(list, n.String()) + } + return list +} + +func (s *NextHopSet) ToConfig() []string { + return s.List() +} + +func (s *NextHopSet) String() string { + return "[ " + strings.Join(s.List(), ", ") + " ]" +} + +func (s *NextHopSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +func NewNextHopSetFromApiStruct(name string, list []net.IPNet) (*NextHopSet, error) { + return &NextHopSet{ + list: list, + }, nil +} + +func NewNextHopSet(c []string) (*NextHopSet, error) { + list := make([]net.IPNet, 0, len(c)) + for _, x := range c { + _, cidr, err := net.ParseCIDR(x) + if err != nil { + addr := net.ParseIP(x) + if addr == nil { + return nil, fmt.Errorf("invalid address or prefix: %s", x) + } + mask := net.CIDRMask(32, 32) + if addr.To4() == nil { + mask = net.CIDRMask(128, 128) + } + cidr = &net.IPNet{ + IP: addr, + Mask: mask, + } + } + list = append(list, *cidr) + } + return &NextHopSet{ + list: list, + }, nil +} + +type NeighborSet struct { + name string + list []net.IPNet +} + +func (s *NeighborSet) Name() string { + return s.name +} + +func (s *NeighborSet) Type() DefinedType { + return DEFINED_TYPE_NEIGHBOR +} + +func (lhs *NeighborSet) Append(arg DefinedSet) error { + rhs, ok := arg.(*NeighborSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.list = append(lhs.list, rhs.list...) + return nil +} + +func (lhs *NeighborSet) Remove(arg DefinedSet) error { + rhs, ok := arg.(*NeighborSet) + if !ok { + return fmt.Errorf("type cast failed") + } + ps := make([]net.IPNet, 0, len(lhs.list)) + for _, x := range lhs.list { + found := false + for _, y := range rhs.list { + if x.String() == y.String() { + found = true + break + } + } + if !found { + ps = append(ps, x) + } + } + lhs.list = ps + return nil +} + +func (lhs *NeighborSet) Replace(arg DefinedSet) error { + rhs, ok := arg.(*NeighborSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.list = rhs.list + return nil +} + +func (s *NeighborSet) List() []string { + list := make([]string, 0, len(s.list)) + for _, n := range s.list { + list = append(list, n.String()) + } + return list +} + +func (s *NeighborSet) ToConfig() *config.NeighborSet { + return &config.NeighborSet{ + NeighborSetName: s.name, + NeighborInfoList: s.List(), + } +} + +func (s *NeighborSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *NeighborSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +func NewNeighborSetFromApiStruct(name string, list []net.IPNet) (*NeighborSet, error) { + return &NeighborSet{ + name: name, + list: list, + }, nil +} + +func NewNeighborSet(c config.NeighborSet) (*NeighborSet, error) { + name := c.NeighborSetName + if name == "" { + if len(c.NeighborInfoList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty neighbor set name") + } + list := make([]net.IPNet, 0, len(c.NeighborInfoList)) + for _, x := range c.NeighborInfoList { + _, cidr, err := net.ParseCIDR(x) + if err != nil { + addr := net.ParseIP(x) + if addr == nil { + return nil, fmt.Errorf("invalid address or prefix: %s", x) + } + mask := net.CIDRMask(32, 32) + if addr.To4() == nil { + mask = net.CIDRMask(128, 128) + } + cidr = &net.IPNet{ + IP: addr, + Mask: mask, + } + } + list = append(list, *cidr) + } + return &NeighborSet{ + name: name, + list: list, + }, nil +} + +type singleAsPathMatchMode int + +const ( + INCLUDE singleAsPathMatchMode = iota + LEFT_MOST + ORIGIN + ONLY +) + +type singleAsPathMatch struct { + asn uint32 + mode singleAsPathMatchMode +} + +func (lhs *singleAsPathMatch) Equal(rhs *singleAsPathMatch) bool { + return lhs.asn == rhs.asn && lhs.mode == rhs.mode +} + +func (lhs *singleAsPathMatch) String() string { + switch lhs.mode { + case INCLUDE: + return fmt.Sprintf("_%d_", lhs.asn) + case LEFT_MOST: + return fmt.Sprintf("^%d_", lhs.asn) + case ORIGIN: + return fmt.Sprintf("_%d$", lhs.asn) + case ONLY: + return fmt.Sprintf("^%d$", lhs.asn) + } + return "" +} + +func (m *singleAsPathMatch) Match(aspath []uint32) bool { + if len(aspath) == 0 { + return false + } + switch m.mode { + case INCLUDE: + for _, asn := range aspath { + if m.asn == asn { + return true + } + } + case LEFT_MOST: + if m.asn == aspath[0] { + return true + } + case ORIGIN: + if m.asn == aspath[len(aspath)-1] { + return true + } + case ONLY: + if len(aspath) == 1 && m.asn == aspath[0] { + return true + } + } + return false +} + +var ( + _regexpLeftMostRe = regexp.MustCompile(`$\^([0-9]+)_^`) + _regexpOriginRe = regexp.MustCompile(`^_([0-9]+)\$$`) + _regexpIncludeRe = regexp.MustCompile("^_([0-9]+)_$") + _regexpOnlyRe = regexp.MustCompile(`^\^([0-9]+)\$$`) +) + +func NewSingleAsPathMatch(arg string) *singleAsPathMatch { + switch { + case _regexpLeftMostRe.MatchString(arg): + asn, _ := strconv.ParseUint(_regexpLeftMostRe.FindStringSubmatch(arg)[1], 10, 32) + return &singleAsPathMatch{ + asn: uint32(asn), + mode: LEFT_MOST, + } + case _regexpOriginRe.MatchString(arg): + asn, _ := strconv.ParseUint(_regexpOriginRe.FindStringSubmatch(arg)[1], 10, 32) + return &singleAsPathMatch{ + asn: uint32(asn), + mode: ORIGIN, + } + case _regexpIncludeRe.MatchString(arg): + asn, _ := strconv.ParseUint(_regexpIncludeRe.FindStringSubmatch(arg)[1], 10, 32) + return &singleAsPathMatch{ + asn: uint32(asn), + mode: INCLUDE, + } + case _regexpOnlyRe.MatchString(arg): + asn, _ := strconv.ParseUint(_regexpOnlyRe.FindStringSubmatch(arg)[1], 10, 32) + return &singleAsPathMatch{ + asn: uint32(asn), + mode: ONLY, + } + } + return nil +} + +type AsPathSet struct { + typ DefinedType + name string + list []*regexp.Regexp + singleList []*singleAsPathMatch +} + +func (s *AsPathSet) Name() string { + return s.name +} + +func (s *AsPathSet) Type() DefinedType { + return s.typ +} + +func (lhs *AsPathSet) Append(arg DefinedSet) error { + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") + } + lhs.list = append(lhs.list, arg.(*AsPathSet).list...) + lhs.singleList = append(lhs.singleList, arg.(*AsPathSet).singleList...) + return nil +} + +func (lhs *AsPathSet) Remove(arg DefinedSet) error { + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") + } + newList := make([]*regexp.Regexp, 0, len(lhs.list)) + for _, x := range lhs.list { + found := false + for _, y := range arg.(*AsPathSet).list { + if x.String() == y.String() { + found = true + break + } + } + if !found { + newList = append(newList, x) + } + } + lhs.list = newList + newSingleList := make([]*singleAsPathMatch, 0, len(lhs.singleList)) + for _, x := range lhs.singleList { + found := false + for _, y := range arg.(*AsPathSet).singleList { + if x.Equal(y) { + found = true + break + } + } + if !found { + newSingleList = append(newSingleList, x) + } + } + lhs.singleList = newSingleList + return nil +} + +func (lhs *AsPathSet) Replace(arg DefinedSet) error { + rhs, ok := arg.(*AsPathSet) + if !ok { + return fmt.Errorf("type cast failed") + } + lhs.list = rhs.list + lhs.singleList = rhs.singleList + return nil +} + +func (s *AsPathSet) List() []string { + list := make([]string, 0, len(s.list)+len(s.singleList)) + for _, exp := range s.singleList { + list = append(list, exp.String()) + } + for _, exp := range s.list { + list = append(list, exp.String()) + } + return list +} + +func (s *AsPathSet) ToConfig() *config.AsPathSet { + return &config.AsPathSet{ + AsPathSetName: s.name, + AsPathList: s.List(), + } +} + +func (s *AsPathSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *AsPathSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +func NewAsPathSet(c config.AsPathSet) (*AsPathSet, error) { + name := c.AsPathSetName + if name == "" { + if len(c.AsPathList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty as-path set name") + } + list := make([]*regexp.Regexp, 0, len(c.AsPathList)) + singleList := make([]*singleAsPathMatch, 0, len(c.AsPathList)) + for _, x := range c.AsPathList { + if s := NewSingleAsPathMatch(x); s != nil { + singleList = append(singleList, s) + } else { + exp, err := regexp.Compile(strings.Replace(x, "_", ASPATH_REGEXP_MAGIC, -1)) + if err != nil { + return nil, fmt.Errorf("invalid regular expression: %s", x) + } + list = append(list, exp) + } + } + return &AsPathSet{ + typ: DEFINED_TYPE_AS_PATH, + name: name, + list: list, + singleList: singleList, + }, nil +} + +type regExpSet struct { + typ DefinedType + name string + list []*regexp.Regexp +} + +func (s *regExpSet) Name() string { + return s.name +} + +func (s *regExpSet) Type() DefinedType { + return s.typ +} + +func (lhs *regExpSet) Append(arg DefinedSet) error { + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") + } + var list []*regexp.Regexp + switch lhs.Type() { + case DEFINED_TYPE_AS_PATH: + list = arg.(*AsPathSet).list + case DEFINED_TYPE_COMMUNITY: + list = arg.(*CommunitySet).list + case DEFINED_TYPE_EXT_COMMUNITY: + list = arg.(*ExtCommunitySet).list + case DEFINED_TYPE_LARGE_COMMUNITY: + list = arg.(*LargeCommunitySet).list + default: + return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) + } + lhs.list = append(lhs.list, list...) + return nil +} + +func (lhs *regExpSet) Remove(arg DefinedSet) error { + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") + } + var list []*regexp.Regexp + switch lhs.Type() { + case DEFINED_TYPE_AS_PATH: + list = arg.(*AsPathSet).list + case DEFINED_TYPE_COMMUNITY: + list = arg.(*CommunitySet).list + case DEFINED_TYPE_EXT_COMMUNITY: + list = arg.(*ExtCommunitySet).list + case DEFINED_TYPE_LARGE_COMMUNITY: + list = arg.(*LargeCommunitySet).list + default: + return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) + } + ps := make([]*regexp.Regexp, 0, len(lhs.list)) + for _, x := range lhs.list { + found := false + for _, y := range list { + if x.String() == y.String() { + found = true + break + } + } + if !found { + ps = append(ps, x) + } + } + lhs.list = ps + return nil +} + +func (lhs *regExpSet) Replace(arg DefinedSet) error { + switch c := arg.(type) { + case *CommunitySet: + lhs.list = c.list + case *ExtCommunitySet: + lhs.list = c.list + case *LargeCommunitySet: + lhs.list = c.list + default: + return fmt.Errorf("type cast failed") + } + return nil +} + +type CommunitySet struct { + regExpSet +} + +func (s *CommunitySet) List() []string { + list := make([]string, 0, len(s.list)) + for _, exp := range s.list { + list = append(list, exp.String()) + } + return list +} + +func (s *CommunitySet) ToConfig() *config.CommunitySet { + return &config.CommunitySet{ + CommunitySetName: s.name, + CommunityList: s.List(), + } +} + +func (s *CommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *CommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +var _regexpCommunity = regexp.MustCompile(`(\d+):(\d+)`) + +func ParseCommunity(arg string) (uint32, error) { + i, err := strconv.ParseUint(arg, 10, 32) + if err == nil { + return uint32(i), nil + } + + elems := _regexpCommunity.FindStringSubmatch(arg) + if len(elems) == 3 { + fst, _ := strconv.ParseUint(elems[1], 10, 16) + snd, _ := strconv.ParseUint(elems[2], 10, 16) + return uint32(fst<<16 | snd), nil + } + for i, v := range bgp.WellKnownCommunityNameMap { + if arg == v { + return uint32(i), nil + } + } + return 0, fmt.Errorf("failed to parse %s as community", arg) +} + +func ParseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) { + var subtype bgp.ExtendedCommunityAttrSubType + var value string + elems := strings.SplitN(arg, ":", 2) + + isValidationState := func(s string) bool { + s = strings.ToLower(s) + r := s == bgp.VALIDATION_STATE_VALID.String() + r = r || s == bgp.VALIDATION_STATE_NOT_FOUND.String() + return r || s == bgp.VALIDATION_STATE_INVALID.String() + } + if len(elems) < 2 && (len(elems) < 1 && !isValidationState(elems[0])) { + return nil, fmt.Errorf("invalid ext-community (rt|soo): | valid | not-found | invalid") + } + if isValidationState(elems[0]) { + subtype = bgp.EC_SUBTYPE_ORIGIN_VALIDATION + value = elems[0] + } else { + switch strings.ToLower(elems[0]) { + case "rt": + subtype = bgp.EC_SUBTYPE_ROUTE_TARGET + case "soo": + subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN + default: + return nil, fmt.Errorf("invalid ext-community (rt|soo): | valid | not-found | invalid") + } + value = elems[1] + } + return bgp.ParseExtendedCommunity(subtype, value) +} + +var _regexpCommunity2 = regexp.MustCompile(`(\d+.)*\d+:\d+`) + +func ParseCommunityRegexp(arg string) (*regexp.Regexp, error) { + i, err := strconv.ParseUint(arg, 10, 32) + if err == nil { + return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff)) + } + + if _regexpCommunity2.MatchString(arg) { + return regexp.Compile(fmt.Sprintf("^%s$", arg)) + } + + for i, v := range bgp.WellKnownCommunityNameMap { + if strings.Replace(strings.ToLower(arg), "_", "-", -1) == v { + return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff)) + } + } + + return regexp.Compile(arg) +} + +func ParseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) { + var subtype bgp.ExtendedCommunityAttrSubType + elems := strings.SplitN(arg, ":", 2) + if len(elems) < 2 { + return subtype, nil, fmt.Errorf("invalid ext-community format([rt|soo]:)") + } + switch strings.ToLower(elems[0]) { + case "rt": + subtype = bgp.EC_SUBTYPE_ROUTE_TARGET + case "soo": + subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN + default: + return subtype, nil, fmt.Errorf("unknown ext-community subtype. rt, soo is supported") + } + exp, err := ParseCommunityRegexp(elems[1]) + return subtype, exp, err +} + +func NewCommunitySet(c config.CommunitySet) (*CommunitySet, error) { + name := c.CommunitySetName + if name == "" { + if len(c.CommunityList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty community set name") + } + list := make([]*regexp.Regexp, 0, len(c.CommunityList)) + for _, x := range c.CommunityList { + exp, err := ParseCommunityRegexp(x) + if err != nil { + return nil, err + } + list = append(list, exp) + } + return &CommunitySet{ + regExpSet: regExpSet{ + typ: DEFINED_TYPE_COMMUNITY, + name: name, + list: list, + }, + }, nil +} + +type ExtCommunitySet struct { + regExpSet + subtypeList []bgp.ExtendedCommunityAttrSubType +} + +func (s *ExtCommunitySet) List() []string { + list := make([]string, 0, len(s.list)) + f := func(idx int, arg string) string { + switch s.subtypeList[idx] { + case bgp.EC_SUBTYPE_ROUTE_TARGET: + return fmt.Sprintf("rt:%s", arg) + case bgp.EC_SUBTYPE_ROUTE_ORIGIN: + return fmt.Sprintf("soo:%s", arg) + case bgp.EC_SUBTYPE_ORIGIN_VALIDATION: + return arg + default: + return fmt.Sprintf("%d:%s", s.subtypeList[idx], arg) + } + } + for idx, exp := range s.list { + list = append(list, f(idx, exp.String())) + } + return list +} + +func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet { + return &config.ExtCommunitySet{ + ExtCommunitySetName: s.name, + ExtCommunityList: s.List(), + } +} + +func (s *ExtCommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *ExtCommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) { + name := c.ExtCommunitySetName + if name == "" { + if len(c.ExtCommunityList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty ext-community set name") + } + list := make([]*regexp.Regexp, 0, len(c.ExtCommunityList)) + subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.ExtCommunityList)) + for _, x := range c.ExtCommunityList { + subtype, exp, err := ParseExtCommunityRegexp(x) + if err != nil { + return nil, err + } + list = append(list, exp) + subtypeList = append(subtypeList, subtype) + } + return &ExtCommunitySet{ + regExpSet: regExpSet{ + typ: DEFINED_TYPE_EXT_COMMUNITY, + name: name, + list: list, + }, + subtypeList: subtypeList, + }, nil +} + +func (s *ExtCommunitySet) Append(arg DefinedSet) error { + err := s.regExpSet.Append(arg) + if err != nil { + return err + } + sList := arg.(*ExtCommunitySet).subtypeList + s.subtypeList = append(s.subtypeList, sList...) + return nil +} + +type LargeCommunitySet struct { + regExpSet +} + +func (s *LargeCommunitySet) List() []string { + list := make([]string, 0, len(s.list)) + for _, exp := range s.list { + list = append(list, exp.String()) + } + return list +} + +func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet { + return &config.LargeCommunitySet{ + LargeCommunitySetName: s.name, + LargeCommunityList: s.List(), + } +} + +func (s *LargeCommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *LargeCommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +var _regexpCommunityLarge = regexp.MustCompile(`\d+:\d+:\d+`) + +func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) { + if _regexpCommunityLarge.MatchString(arg) { + return regexp.Compile(fmt.Sprintf("^%s$", arg)) + } + exp, err := regexp.Compile(arg) + if err != nil { + return nil, fmt.Errorf("invalid large-community format: %v", err) + } + + return exp, nil +} + +func NewLargeCommunitySet(c config.LargeCommunitySet) (*LargeCommunitySet, error) { + name := c.LargeCommunitySetName + if name == "" { + if len(c.LargeCommunityList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty large community set name") + } + list := make([]*regexp.Regexp, 0, len(c.LargeCommunityList)) + for _, x := range c.LargeCommunityList { + exp, err := ParseLargeCommunityRegexp(x) + if err != nil { + return nil, err + } + list = append(list, exp) + } + return &LargeCommunitySet{ + regExpSet: regExpSet{ + typ: DEFINED_TYPE_LARGE_COMMUNITY, + name: name, + list: list, + }, + }, nil +} + +type Condition interface { + Name() string + Type() ConditionType + Evaluate(*Path, *PolicyOptions) bool + Set() DefinedSet +} + +type NextHopCondition struct { + set *NextHopSet +} + +func (c *NextHopCondition) Type() ConditionType { + return CONDITION_NEXT_HOP +} + +func (c *NextHopCondition) Set() DefinedSet { + return c.set +} + +func (c *NextHopCondition) Name() string { return "" } + +func (c *NextHopCondition) String() string { + return c.set.String() +} + +// compare next-hop ipaddress of this condition and source address of path +// and, subsequent comparisons are skipped if that matches the conditions. +// If NextHopSet's length is zero, return true. +func (c *NextHopCondition) Evaluate(path *Path, options *PolicyOptions) bool { + if len(c.set.list) == 0 { + log.WithFields(log.Fields{ + "Topic": "Policy", + }).Debug("NextHop doesn't have elements") + return true + } + + nexthop := path.GetNexthop() + + // In cases where we advertise routes from iBGP to eBGP, we want to filter + // on the "original" nexthop. The current paths' nexthop has already been + // set and is ready to be advertised as per: + // https://tools.ietf.org/html/rfc4271#section-5.1.3 + if options != nil && options.OldNextHop != nil && + !options.OldNextHop.IsUnspecified() && !options.OldNextHop.Equal(nexthop) { + nexthop = options.OldNextHop + } + + if nexthop == nil { + return false + } + + for _, n := range c.set.list { + if n.Contains(nexthop) { + return true + } + } + + return false +} + +func NewNextHopCondition(c []string) (*NextHopCondition, error) { + if len(c) == 0 { + return nil, nil + } + + list, err := NewNextHopSet(c) + if err != nil { + return nil, nil + } + + return &NextHopCondition{ + set: list, + }, nil +} + +type PrefixCondition struct { + set *PrefixSet + option MatchOption +} + +func (c *PrefixCondition) Type() ConditionType { + return CONDITION_PREFIX +} + +func (c *PrefixCondition) Set() DefinedSet { + return c.set +} + +func (c *PrefixCondition) Option() MatchOption { + return c.option +} + +// compare prefixes in this condition and nlri of path and +// subsequent comparison is skipped if that matches the conditions. +// If PrefixList's length is zero, return true. +func (c *PrefixCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + var key string + var masklen uint8 + keyf := func(ip net.IP, ones int) string { + var buffer bytes.Buffer + for i := 0; i < len(ip) && i < ones; i++ { + buffer.WriteString(fmt.Sprintf("%08b", ip[i])) + } + return buffer.String()[:ones] + } + family := path.GetRouteFamily() + switch family { + case bgp.RF_IPv4_UC: + masklen = path.GetNlri().(*bgp.IPAddrPrefix).Length + key = keyf(path.GetNlri().(*bgp.IPAddrPrefix).Prefix, int(masklen)) + case bgp.RF_IPv6_UC: + masklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length + key = keyf(path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix, int(masklen)) + default: + return false + } + if family != c.set.family { + return false + } + + result := false + _, ps, ok := c.set.tree.LongestPrefix(key) + if ok { + for _, p := range ps.([]*Prefix) { + if p.MasklengthRangeMin <= masklen && masklen <= p.MasklengthRangeMax { + result = true + break + } + } + } + + if c.option == MATCH_OPTION_INVERT { + result = !result + } + + return result +} + +func (c *PrefixCondition) Name() string { return c.set.name } + +func NewPrefixCondition(c config.MatchPrefixSet) (*PrefixCondition, error) { + if c.PrefixSet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &PrefixCondition{ + set: &PrefixSet{ + name: c.PrefixSet, + }, + option: o, + }, nil +} + +type NeighborCondition struct { + set *NeighborSet + option MatchOption +} + +func (c *NeighborCondition) Type() ConditionType { + return CONDITION_NEIGHBOR +} + +func (c *NeighborCondition) Set() DefinedSet { + return c.set +} + +func (c *NeighborCondition) Option() MatchOption { + return c.option +} + +// compare neighbor ipaddress of this condition and source address of path +// and, subsequent comparisons are skipped if that matches the conditions. +// If NeighborList's length is zero, return true. +func (c *NeighborCondition) Evaluate(path *Path, options *PolicyOptions) bool { + if len(c.set.list) == 0 { + log.WithFields(log.Fields{ + "Topic": "Policy", + }).Debug("NeighborList doesn't have elements") + return true + } + + neighbor := path.GetSource().Address + if options != nil && options.Info != nil && options.Info.Address != nil { + neighbor = options.Info.Address + } + + if neighbor == nil { + return false + } + result := false + for _, n := range c.set.list { + if n.Contains(neighbor) { + result = true + break + } + } + + if c.option == MATCH_OPTION_INVERT { + result = !result + } + + return result +} + +func (c *NeighborCondition) Name() string { return c.set.name } + +func NewNeighborCondition(c config.MatchNeighborSet) (*NeighborCondition, error) { + if c.NeighborSet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &NeighborCondition{ + set: &NeighborSet{ + name: c.NeighborSet, + }, + option: o, + }, nil +} + +type AsPathCondition struct { + set *AsPathSet + option MatchOption +} + +func (c *AsPathCondition) Type() ConditionType { + return CONDITION_AS_PATH +} + +func (c *AsPathCondition) Set() DefinedSet { + return c.set +} + +func (c *AsPathCondition) Option() MatchOption { + return c.option +} + +func (c *AsPathCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + if len(c.set.singleList) > 0 { + aspath := path.GetAsSeqList() + for _, m := range c.set.singleList { + result := m.Match(aspath) + if c.option == MATCH_OPTION_ALL && !result { + return false + } + if c.option == MATCH_OPTION_ANY && result { + return true + } + if c.option == MATCH_OPTION_INVERT && result { + return false + } + } + } + if len(c.set.list) > 0 { + aspath := path.GetAsString() + for _, r := range c.set.list { + result := r.MatchString(aspath) + if c.option == MATCH_OPTION_ALL && !result { + return false + } + if c.option == MATCH_OPTION_ANY && result { + return true + } + if c.option == MATCH_OPTION_INVERT && result { + return false + } + } + } + if c.option == MATCH_OPTION_ANY { + return false + } + return true +} + +func (c *AsPathCondition) Name() string { return c.set.name } + +func NewAsPathCondition(c config.MatchAsPathSet) (*AsPathCondition, error) { + if c.AsPathSet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &AsPathCondition{ + set: &AsPathSet{ + name: c.AsPathSet, + }, + option: o, + }, nil +} + +type CommunityCondition struct { + set *CommunitySet + option MatchOption +} + +func (c *CommunityCondition) Type() ConditionType { + return CONDITION_COMMUNITY +} + +func (c *CommunityCondition) Set() DefinedSet { + return c.set +} + +func (c *CommunityCondition) Option() MatchOption { + return c.option +} + +func (c *CommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + cs := path.GetCommunities() + result := false + for _, x := range c.set.list { + result = false + for _, y := range cs { + if x.MatchString(fmt.Sprintf("%d:%d", y>>16, y&0x0000ffff)) { + result = true + break + } + } + if c.option == MATCH_OPTION_ALL && !result { + break + } + if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result { + break + } + } + if c.option == MATCH_OPTION_INVERT { + result = !result + } + return result +} + +func (c *CommunityCondition) Name() string { return c.set.name } + +func NewCommunityCondition(c config.MatchCommunitySet) (*CommunityCondition, error) { + if c.CommunitySet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &CommunityCondition{ + set: &CommunitySet{ + regExpSet: regExpSet{ + name: c.CommunitySet, + }, + }, + option: o, + }, nil +} + +type ExtCommunityCondition struct { + set *ExtCommunitySet + option MatchOption +} + +func (c *ExtCommunityCondition) Type() ConditionType { + return CONDITION_EXT_COMMUNITY +} + +func (c *ExtCommunityCondition) Set() DefinedSet { + return c.set +} + +func (c *ExtCommunityCondition) Option() MatchOption { + return c.option +} + +func (c *ExtCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + es := path.GetExtCommunities() + result := false + for _, x := range es { + result = false + typ, subtype := x.GetTypes() + // match only with transitive community. see RFC7153 + if typ >= 0x3f { + continue + } + for idx, y := range c.set.list { + if subtype == c.set.subtypeList[idx] && y.MatchString(x.String()) { + result = true + break + } + } + if c.option == MATCH_OPTION_ALL && !result { + break + } + if c.option == MATCH_OPTION_ANY && result { + break + } + } + if c.option == MATCH_OPTION_INVERT { + result = !result + } + return result +} + +func (c *ExtCommunityCondition) Name() string { return c.set.name } + +func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondition, error) { + if c.ExtCommunitySet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &ExtCommunityCondition{ + set: &ExtCommunitySet{ + regExpSet: regExpSet{ + name: c.ExtCommunitySet, + }, + }, + option: o, + }, nil +} + +type LargeCommunityCondition struct { + set *LargeCommunitySet + option MatchOption +} + +func (c *LargeCommunityCondition) Type() ConditionType { + return CONDITION_LARGE_COMMUNITY +} + +func (c *LargeCommunityCondition) Set() DefinedSet { + return c.set +} + +func (c *LargeCommunityCondition) Option() MatchOption { + return c.option +} + +func (c *LargeCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + result := false + cs := path.GetLargeCommunities() + for _, x := range c.set.list { + result = false + for _, y := range cs { + if x.MatchString(y.String()) { + result = true + break + } + } + if c.option == MATCH_OPTION_ALL && !result { + break + } + if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result { + break + } + } + if c.option == MATCH_OPTION_INVERT { + result = !result + } + return result +} + +func (c *LargeCommunityCondition) Name() string { return c.set.name } + +func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunityCondition, error) { + if c.LargeCommunitySet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &LargeCommunityCondition{ + set: &LargeCommunitySet{ + regExpSet: regExpSet{ + name: c.LargeCommunitySet, + }, + }, + option: o, + }, nil +} + +type AsPathLengthCondition struct { + length uint32 + operator AttributeComparison +} + +func (c *AsPathLengthCondition) Type() ConditionType { + return CONDITION_AS_PATH_LENGTH +} + +// compare AS_PATH length in the message's AS_PATH attribute with +// the one in condition. +func (c *AsPathLengthCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + + length := uint32(path.GetAsPathLen()) + result := false + switch c.operator { + case ATTRIBUTE_EQ: + result = c.length == length + case ATTRIBUTE_GE: + result = c.length <= length + case ATTRIBUTE_LE: + result = c.length >= length + } + + return result +} + +func (c *AsPathLengthCondition) Set() DefinedSet { + return nil +} + +func (c *AsPathLengthCondition) Name() string { return "" } + +func (c *AsPathLengthCondition) String() string { + return fmt.Sprintf("%s%d", c.operator, c.length) +} + +func NewAsPathLengthCondition(c config.AsPathLength) (*AsPathLengthCondition, error) { + if c.Value == 0 && c.Operator == "" { + return nil, nil + } + var op AttributeComparison + if i := c.Operator.ToInt(); i < 0 { + return nil, fmt.Errorf("invalid as path length operator: %s", c.Operator) + } else { + // take mod 3 because we have extended openconfig attribute-comparison + // for simple configuration. see config.AttributeComparison definition + op = AttributeComparison(i % 3) + } + return &AsPathLengthCondition{ + length: c.Value, + operator: op, + }, nil +} + +type RpkiValidationCondition struct { + result config.RpkiValidationResultType +} + +func (c *RpkiValidationCondition) Type() ConditionType { + return CONDITION_RPKI +} + +func (c *RpkiValidationCondition) Evaluate(path *Path, options *PolicyOptions) bool { + if options != nil && options.ValidationResult != nil { + return c.result == options.ValidationResult.Status + } + return false +} + +func (c *RpkiValidationCondition) Set() DefinedSet { + return nil +} + +func (c *RpkiValidationCondition) Name() string { return "" } + +func (c *RpkiValidationCondition) String() string { + return string(c.result) +} + +func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidationCondition, error) { + if c == config.RpkiValidationResultType("") || c == config.RPKI_VALIDATION_RESULT_TYPE_NONE { + return nil, nil + } + return &RpkiValidationCondition{ + result: c, + }, nil +} + +type RouteTypeCondition struct { + typ config.RouteType +} + +func (c *RouteTypeCondition) Type() ConditionType { + return CONDITION_ROUTE_TYPE +} + +func (c *RouteTypeCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + switch c.typ { + case config.ROUTE_TYPE_LOCAL: + return path.IsLocal() + case config.ROUTE_TYPE_INTERNAL: + return !path.IsLocal() && path.IsIBGP() + case config.ROUTE_TYPE_EXTERNAL: + return !path.IsLocal() && !path.IsIBGP() + } + return false +} + +func (c *RouteTypeCondition) Set() DefinedSet { + return nil +} + +func (c *RouteTypeCondition) Name() string { return "" } + +func (c *RouteTypeCondition) String() string { + return string(c.typ) +} + +func NewRouteTypeCondition(c config.RouteType) (*RouteTypeCondition, error) { + if string(c) == "" || c == config.ROUTE_TYPE_NONE { + return nil, nil + } + if err := c.Validate(); err != nil { + return nil, err + } + return &RouteTypeCondition{ + typ: c, + }, nil +} + +type AfiSafiInCondition struct { + routeFamilies []bgp.RouteFamily +} + +func (c *AfiSafiInCondition) Type() ConditionType { + return CONDITION_AFI_SAFI_IN +} + +func (c *AfiSafiInCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + for _, rf := range c.routeFamilies { + if path.GetRouteFamily() == rf { + return true + } + } + return false +} + +func (c *AfiSafiInCondition) Set() DefinedSet { + return nil +} + +func (c *AfiSafiInCondition) Name() string { return "" } + +func (c *AfiSafiInCondition) String() string { + tmp := make([]string, 0, len(c.routeFamilies)) + for _, afiSafi := range c.routeFamilies { + tmp = append(tmp, afiSafi.String()) + } + return strings.Join(tmp, " ") +} + +func NewAfiSafiInCondition(afiSafInConfig []config.AfiSafiType) (*AfiSafiInCondition, error) { + if afiSafInConfig == nil { + return nil, nil + } + + routeFamilies := make([]bgp.RouteFamily, 0, len(afiSafInConfig)) + for _, afiSafiValue := range afiSafInConfig { + if err := afiSafiValue.Validate(); err != nil { + return nil, err + } + rf, err := bgp.GetRouteFamily(string(afiSafiValue)) + if err != nil { + return nil, err + } + routeFamilies = append(routeFamilies, rf) + } + return &AfiSafiInCondition{ + routeFamilies: routeFamilies, + }, nil +} + +type Action interface { + Type() ActionType + Apply(*Path, *PolicyOptions) *Path + String() string +} + +type RoutingAction struct { + AcceptRoute bool +} + +func (a *RoutingAction) Type() ActionType { + return ACTION_ROUTING +} + +func (a *RoutingAction) Apply(path *Path, _ *PolicyOptions) *Path { + if a.AcceptRoute { + return path + } + return nil +} + +func (a *RoutingAction) String() string { + action := "reject" + if a.AcceptRoute { + action = "accept" + } + return action +} + +func NewRoutingAction(c config.RouteDisposition) (*RoutingAction, error) { + var accept bool + switch c { + case config.RouteDisposition(""), config.ROUTE_DISPOSITION_NONE: + return nil, nil + case config.ROUTE_DISPOSITION_ACCEPT_ROUTE: + accept = true + case config.ROUTE_DISPOSITION_REJECT_ROUTE: + accept = false + default: + return nil, fmt.Errorf("invalid route disposition") + } + return &RoutingAction{ + AcceptRoute: accept, + }, nil +} + +type CommunityAction struct { + action config.BgpSetCommunityOptionType + list []uint32 + removeList []*regexp.Regexp +} + +func RegexpRemoveCommunities(path *Path, exps []*regexp.Regexp) { + comms := path.GetCommunities() + newComms := make([]uint32, 0, len(comms)) + for _, comm := range comms { + c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff) + match := false + for _, exp := range exps { + if exp.MatchString(c) { + match = true + break + } + } + if !match { + newComms = append(newComms, comm) + } + } + path.SetCommunities(newComms, true) +} + +func RegexpRemoveExtCommunities(path *Path, exps []*regexp.Regexp, subtypes []bgp.ExtendedCommunityAttrSubType) { + comms := path.GetExtCommunities() + newComms := make([]bgp.ExtendedCommunityInterface, 0, len(comms)) + for _, comm := range comms { + match := false + typ, subtype := comm.GetTypes() + // match only with transitive community. see RFC7153 + if typ >= 0x3f { + continue + } + for idx, exp := range exps { + if subtype == subtypes[idx] && exp.MatchString(comm.String()) { + match = true + break + } + } + if !match { + newComms = append(newComms, comm) + } + } + path.SetExtCommunities(newComms, true) +} + +func RegexpRemoveLargeCommunities(path *Path, exps []*regexp.Regexp) { + comms := path.GetLargeCommunities() + newComms := make([]*bgp.LargeCommunity, 0, len(comms)) + for _, comm := range comms { + c := comm.String() + match := false + for _, exp := range exps { + if exp.MatchString(c) { + match = true + break + } + } + if !match { + newComms = append(newComms, comm) + } + } + path.SetLargeCommunities(newComms, true) +} + +func (a *CommunityAction) Type() ActionType { + return ACTION_COMMUNITY +} + +func (a *CommunityAction) Apply(path *Path, _ *PolicyOptions) *Path { + switch a.action { + case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: + path.SetCommunities(a.list, false) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: + RegexpRemoveCommunities(path, a.removeList) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: + path.SetCommunities(a.list, true) + } + return path +} + +func (a *CommunityAction) ToConfig() *config.SetCommunity { + cs := make([]string, 0, len(a.list)+len(a.removeList)) + for _, comm := range a.list { + c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff) + cs = append(cs, c) + } + for _, exp := range a.removeList { + cs = append(cs, exp.String()) + } + return &config.SetCommunity{ + Options: string(a.action), + SetCommunityMethod: config.SetCommunityMethod{CommunitiesList: cs}, + } +} + +func (a *CommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +// TODO: this is not efficient use of regexp, probably slow +var _regexpCommunityReplaceString = regexp.MustCompile(`[\^\$]`) + +func (a *CommunityAction) String() string { + list := a.ToConfig().SetCommunityMethod.CommunitiesList + l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + +func NewCommunityAction(c config.SetCommunity) (*CommunityAction, error) { + a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)] + if !ok { + if len(c.SetCommunityMethod.CommunitiesList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("invalid option name: %s", c.Options) + } + var list []uint32 + var removeList []*regexp.Regexp + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + removeList = make([]*regexp.Regexp, 0, len(c.SetCommunityMethod.CommunitiesList)) + } else { + list = make([]uint32, 0, len(c.SetCommunityMethod.CommunitiesList)) + } + for _, x := range c.SetCommunityMethod.CommunitiesList { + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + exp, err := ParseCommunityRegexp(x) + if err != nil { + return nil, err + } + removeList = append(removeList, exp) + } else { + comm, err := ParseCommunity(x) + if err != nil { + return nil, err + } + list = append(list, comm) + } + } + return &CommunityAction{ + action: a, + list: list, + removeList: removeList, + }, nil +} + +type ExtCommunityAction struct { + action config.BgpSetCommunityOptionType + list []bgp.ExtendedCommunityInterface + removeList []*regexp.Regexp + subtypeList []bgp.ExtendedCommunityAttrSubType +} + +func (a *ExtCommunityAction) Type() ActionType { + return ACTION_EXT_COMMUNITY +} + +func (a *ExtCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path { + switch a.action { + case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: + path.SetExtCommunities(a.list, false) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: + RegexpRemoveExtCommunities(path, a.removeList, a.subtypeList) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: + path.SetExtCommunities(a.list, true) + } + return path +} + +func (a *ExtCommunityAction) ToConfig() *config.SetExtCommunity { + cs := make([]string, 0, len(a.list)+len(a.removeList)) + f := func(idx int, arg string) string { + switch a.subtypeList[idx] { + case bgp.EC_SUBTYPE_ROUTE_TARGET: + return fmt.Sprintf("rt:%s", arg) + case bgp.EC_SUBTYPE_ROUTE_ORIGIN: + return fmt.Sprintf("soo:%s", arg) + case bgp.EC_SUBTYPE_ORIGIN_VALIDATION: + return arg + default: + return fmt.Sprintf("%d:%s", a.subtypeList[idx], arg) + } + } + for idx, c := range a.list { + cs = append(cs, f(idx, c.String())) + } + for idx, exp := range a.removeList { + cs = append(cs, f(idx, exp.String())) + } + return &config.SetExtCommunity{ + Options: string(a.action), + SetExtCommunityMethod: config.SetExtCommunityMethod{ + CommunitiesList: cs, + }, + } +} + +func (a *ExtCommunityAction) String() string { + list := a.ToConfig().SetExtCommunityMethod.CommunitiesList + l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + +func (a *ExtCommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error) { + a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)] + if !ok { + if len(c.SetExtCommunityMethod.CommunitiesList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("invalid option name: %s", c.Options) + } + var list []bgp.ExtendedCommunityInterface + var removeList []*regexp.Regexp + subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.SetExtCommunityMethod.CommunitiesList)) + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + removeList = make([]*regexp.Regexp, 0, len(c.SetExtCommunityMethod.CommunitiesList)) + } else { + list = make([]bgp.ExtendedCommunityInterface, 0, len(c.SetExtCommunityMethod.CommunitiesList)) + } + for _, x := range c.SetExtCommunityMethod.CommunitiesList { + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + subtype, exp, err := ParseExtCommunityRegexp(x) + if err != nil { + return nil, err + } + removeList = append(removeList, exp) + subtypeList = append(subtypeList, subtype) + } else { + comm, err := ParseExtCommunity(x) + if err != nil { + return nil, err + } + list = append(list, comm) + _, subtype := comm.GetTypes() + subtypeList = append(subtypeList, subtype) + } + } + return &ExtCommunityAction{ + action: a, + list: list, + removeList: removeList, + subtypeList: subtypeList, + }, nil +} + +type LargeCommunityAction struct { + action config.BgpSetCommunityOptionType + list []*bgp.LargeCommunity + removeList []*regexp.Regexp +} + +func (a *LargeCommunityAction) Type() ActionType { + return ACTION_LARGE_COMMUNITY +} + +func (a *LargeCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path { + switch a.action { + case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: + path.SetLargeCommunities(a.list, false) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: + RegexpRemoveLargeCommunities(path, a.removeList) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: + path.SetLargeCommunities(a.list, true) + } + return path +} + +func (a *LargeCommunityAction) ToConfig() *config.SetLargeCommunity { + cs := make([]string, 0, len(a.list)+len(a.removeList)) + for _, comm := range a.list { + cs = append(cs, comm.String()) + } + for _, exp := range a.removeList { + cs = append(cs, exp.String()) + } + return &config.SetLargeCommunity{ + SetLargeCommunityMethod: config.SetLargeCommunityMethod{CommunitiesList: cs}, + Options: config.BgpSetCommunityOptionType(a.action), + } +} + +func (a *LargeCommunityAction) String() string { + list := a.ToConfig().SetLargeCommunityMethod.CommunitiesList + l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + +func (a *LargeCommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +func NewLargeCommunityAction(c config.SetLargeCommunity) (*LargeCommunityAction, error) { + a, ok := CommunityOptionValueMap[strings.ToLower(string(c.Options))] + if !ok { + if len(c.SetLargeCommunityMethod.CommunitiesList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("invalid option name: %s", c.Options) + } + var list []*bgp.LargeCommunity + var removeList []*regexp.Regexp + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + removeList = make([]*regexp.Regexp, 0, len(c.SetLargeCommunityMethod.CommunitiesList)) + } else { + list = make([]*bgp.LargeCommunity, 0, len(c.SetLargeCommunityMethod.CommunitiesList)) + } + for _, x := range c.SetLargeCommunityMethod.CommunitiesList { + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + exp, err := ParseLargeCommunityRegexp(x) + if err != nil { + return nil, err + } + removeList = append(removeList, exp) + } else { + comm, err := bgp.ParseLargeCommunity(x) + if err != nil { + return nil, err + } + list = append(list, comm) + } + } + return &LargeCommunityAction{ + action: a, + list: list, + removeList: removeList, + }, nil + +} + +type MedAction struct { + value int64 + action MedActionType +} + +func (a *MedAction) Type() ActionType { + return ACTION_MED +} + +func (a *MedAction) Apply(path *Path, _ *PolicyOptions) *Path { + var err error + switch a.action { + case MED_ACTION_MOD: + err = path.SetMed(a.value, false) + case MED_ACTION_REPLACE: + err = path.SetMed(a.value, true) + } + + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Med Action", + "Error": err, + }).Warn("Could not set Med on path") + } + return path +} + +func (a *MedAction) ToConfig() config.BgpSetMedType { + if a.action == MED_ACTION_MOD && a.value > 0 { + return config.BgpSetMedType(fmt.Sprintf("+%d", a.value)) + } + return config.BgpSetMedType(fmt.Sprintf("%d", a.value)) +} + +func (a *MedAction) String() string { + return string(a.ToConfig()) +} + +func (a *MedAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +var _regexpParseMedAction = regexp.MustCompile(`^(\+|\-)?(\d+)$`) + +func NewMedAction(c config.BgpSetMedType) (*MedAction, error) { + if string(c) == "" { + return nil, nil + } + + elems := _regexpParseMedAction.FindStringSubmatch(string(c)) + if len(elems) != 3 { + return nil, fmt.Errorf("invalid med action format") + } + action := MED_ACTION_REPLACE + switch elems[1] { + case "+", "-": + action = MED_ACTION_MOD + } + value, _ := strconv.ParseInt(string(c), 10, 64) + return &MedAction{ + value: value, + action: action, + }, nil +} + +func NewMedActionFromApiStruct(action MedActionType, value int64) *MedAction { + return &MedAction{action: action, value: value} +} + +type LocalPrefAction struct { + value uint32 +} + +func (a *LocalPrefAction) Type() ActionType { + return ACTION_LOCAL_PREF +} + +func (a *LocalPrefAction) Apply(path *Path, _ *PolicyOptions) *Path { + path.setPathAttr(bgp.NewPathAttributeLocalPref(a.value)) + return path +} + +func (a *LocalPrefAction) ToConfig() uint32 { + return a.value +} + +func (a *LocalPrefAction) String() string { + return fmt.Sprintf("%d", a.value) +} + +func (a *LocalPrefAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +func NewLocalPrefAction(value uint32) (*LocalPrefAction, error) { + if value == 0 { + return nil, nil + } + return &LocalPrefAction{ + value: value, + }, nil +} + +type AsPathPrependAction struct { + asn uint32 + useLeftMost bool + repeat uint8 +} + +func (a *AsPathPrependAction) Type() ActionType { + return ACTION_AS_PATH_PREPEND +} + +func (a *AsPathPrependAction) Apply(path *Path, option *PolicyOptions) *Path { + var asn uint32 + if a.useLeftMost { + aspath := path.GetAsSeqList() + if len(aspath) == 0 { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPathPrepend Action", + }).Warn("aspath length is zero.") + return path + } + asn = aspath[0] + if asn == 0 { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPathPrepend Action", + }).Warn("left-most ASN is not seq") + return path + } + } else { + asn = a.asn + } + + confed := option != nil && option.Info.Confederation + path.PrependAsn(asn, a.repeat, confed) + + return path +} + +func (a *AsPathPrependAction) ToConfig() *config.SetAsPathPrepend { + return &config.SetAsPathPrepend{ + RepeatN: uint8(a.repeat), + As: func() string { + if a.useLeftMost { + return "last-as" + } + return fmt.Sprintf("%d", a.asn) + }(), + } +} + +func (a *AsPathPrependAction) String() string { + c := a.ToConfig() + return fmt.Sprintf("prepend %s %d times", c.As, c.RepeatN) +} + +func (a *AsPathPrependAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +// NewAsPathPrependAction creates AsPathPrependAction object. +// If ASN cannot be parsed, nil will be returned. +func NewAsPathPrependAction(action config.SetAsPathPrepend) (*AsPathPrependAction, error) { + a := &AsPathPrependAction{ + repeat: action.RepeatN, + } + switch action.As { + case "": + if a.repeat == 0 { + return nil, nil + } + return nil, fmt.Errorf("specify as to prepend") + case "last-as": + a.useLeftMost = true + default: + asn, err := strconv.ParseUint(action.As, 10, 32) + if err != nil { + return nil, fmt.Errorf("As number string invalid") + } + a.asn = uint32(asn) + } + return a, nil +} + +type NexthopAction struct { + value net.IP + self bool +} + +func (a *NexthopAction) Type() ActionType { + return ACTION_NEXTHOP +} + +func (a *NexthopAction) Apply(path *Path, options *PolicyOptions) *Path { + if a.self { + if options != nil && options.Info != nil && options.Info.LocalAddress != nil { + path.SetNexthop(options.Info.LocalAddress) + } + return path + } + path.SetNexthop(a.value) + return path +} + +func (a *NexthopAction) ToConfig() config.BgpNextHopType { + if a.self { + return config.BgpNextHopType("self") + } + return config.BgpNextHopType(a.value.String()) +} + +func (a *NexthopAction) String() string { + return string(a.ToConfig()) +} + +func (a *NexthopAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +func NewNexthopAction(c config.BgpNextHopType) (*NexthopAction, error) { + switch strings.ToLower(string(c)) { + case "": + return nil, nil + case "self": + return &NexthopAction{ + self: true, + }, nil + } + addr := net.ParseIP(string(c)) + if addr == nil { + return nil, fmt.Errorf("invalid ip address format: %s", string(c)) + } + return &NexthopAction{ + value: addr, + }, nil +} + +type Statement struct { + Name string + Conditions []Condition + RouteAction Action + ModActions []Action +} + +// evaluate each condition in the statement according to MatchSetOptions +func (s *Statement) Evaluate(p *Path, options *PolicyOptions) bool { + for _, c := range s.Conditions { + if !c.Evaluate(p, options) { + return false + } + } + return true +} + +func (s *Statement) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) { + result := s.Evaluate(path, options) + if result { + if len(s.ModActions) != 0 { + // apply all modification actions + path = path.Clone(path.IsWithdraw) + for _, action := range s.ModActions { + path = action.Apply(path, options) + } + } + //Routing action + if s.RouteAction == nil || reflect.ValueOf(s.RouteAction).IsNil() { + return ROUTE_TYPE_NONE, path + } + p := s.RouteAction.Apply(path, options) + if p == nil { + return ROUTE_TYPE_REJECT, path + } + return ROUTE_TYPE_ACCEPT, path + } + return ROUTE_TYPE_NONE, path +} + +func (s *Statement) ToConfig() *config.Statement { + return &config.Statement{ + Name: s.Name, + Conditions: func() config.Conditions { + cond := config.Conditions{} + for _, c := range s.Conditions { + switch c.(type) { + case *PrefixCondition: + v := c.(*PrefixCondition) + cond.MatchPrefixSet = config.MatchPrefixSet{PrefixSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()} + case *NeighborCondition: + v := c.(*NeighborCondition) + cond.MatchNeighborSet = config.MatchNeighborSet{NeighborSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()} + case *AsPathLengthCondition: + v := c.(*AsPathLengthCondition) + cond.BgpConditions.AsPathLength = config.AsPathLength{Operator: config.IntToAttributeComparisonMap[int(v.operator)], Value: v.length} + case *AsPathCondition: + v := c.(*AsPathCondition) + cond.BgpConditions.MatchAsPathSet = config.MatchAsPathSet{AsPathSet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} + case *CommunityCondition: + v := c.(*CommunityCondition) + cond.BgpConditions.MatchCommunitySet = config.MatchCommunitySet{CommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} + case *ExtCommunityCondition: + v := c.(*ExtCommunityCondition) + cond.BgpConditions.MatchExtCommunitySet = config.MatchExtCommunitySet{ExtCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} + case *LargeCommunityCondition: + v := c.(*LargeCommunityCondition) + cond.BgpConditions.MatchLargeCommunitySet = config.MatchLargeCommunitySet{LargeCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} + case *NextHopCondition: + v := c.(*NextHopCondition) + cond.BgpConditions.NextHopInList = v.set.List() + case *RpkiValidationCondition: + v := c.(*RpkiValidationCondition) + cond.BgpConditions.RpkiValidationResult = v.result + case *RouteTypeCondition: + v := c.(*RouteTypeCondition) + cond.BgpConditions.RouteType = v.typ + case *AfiSafiInCondition: + v := c.(*AfiSafiInCondition) + res := make([]config.AfiSafiType, 0, len(v.routeFamilies)) + for _, rf := range v.routeFamilies { + res = append(res, config.AfiSafiType(rf.String())) + } + cond.BgpConditions.AfiSafiInList = res + } + } + return cond + }(), + Actions: func() config.Actions { + act := config.Actions{} + if s.RouteAction != nil && !reflect.ValueOf(s.RouteAction).IsNil() { + a := s.RouteAction.(*RoutingAction) + if a.AcceptRoute { + act.RouteDisposition = config.ROUTE_DISPOSITION_ACCEPT_ROUTE + } else { + act.RouteDisposition = config.ROUTE_DISPOSITION_REJECT_ROUTE + } + } else { + act.RouteDisposition = config.ROUTE_DISPOSITION_NONE + } + for _, a := range s.ModActions { + switch a.(type) { + case *AsPathPrependAction: + act.BgpActions.SetAsPathPrepend = *a.(*AsPathPrependAction).ToConfig() + case *CommunityAction: + act.BgpActions.SetCommunity = *a.(*CommunityAction).ToConfig() + case *ExtCommunityAction: + act.BgpActions.SetExtCommunity = *a.(*ExtCommunityAction).ToConfig() + case *LargeCommunityAction: + act.BgpActions.SetLargeCommunity = *a.(*LargeCommunityAction).ToConfig() + case *MedAction: + act.BgpActions.SetMed = a.(*MedAction).ToConfig() + case *LocalPrefAction: + act.BgpActions.SetLocalPref = a.(*LocalPrefAction).ToConfig() + case *NexthopAction: + act.BgpActions.SetNextHop = a.(*NexthopAction).ToConfig() + } + } + return act + }(), + } +} + +func (s *Statement) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + +type opType int + +const ( + ADD opType = iota + REMOVE + REPLACE +) + +func (lhs *Statement) mod(op opType, rhs *Statement) error { + cs := make([]Condition, len(lhs.Conditions)) + copy(cs, lhs.Conditions) + ra := lhs.RouteAction + as := make([]Action, len(lhs.ModActions)) + copy(as, lhs.ModActions) + for _, x := range rhs.Conditions { + var c Condition + i := 0 + for idx, y := range lhs.Conditions { + if x.Type() == y.Type() { + c = y + i = idx + break + } + } + switch op { + case ADD: + if c != nil { + return fmt.Errorf("condition %d is already set", x.Type()) + } + if cs == nil { + cs = make([]Condition, 0, len(rhs.Conditions)) + } + cs = append(cs, x) + case REMOVE: + if c == nil { + return fmt.Errorf("condition %d is not set", x.Type()) + } + cs = append(cs[:i], cs[i+1:]...) + if len(cs) == 0 { + cs = nil + } + case REPLACE: + if c == nil { + return fmt.Errorf("condition %d is not set", x.Type()) + } + cs[i] = x + } + } + if rhs.RouteAction != nil && !reflect.ValueOf(rhs.RouteAction).IsNil() { + switch op { + case ADD: + if lhs.RouteAction != nil && !reflect.ValueOf(lhs.RouteAction).IsNil() { + return fmt.Errorf("route action is already set") + } + ra = rhs.RouteAction + case REMOVE: + if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() { + return fmt.Errorf("route action is not set") + } + ra = nil + case REPLACE: + if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() { + return fmt.Errorf("route action is not set") + } + ra = rhs.RouteAction + } + } + for _, x := range rhs.ModActions { + var a Action + i := 0 + for idx, y := range lhs.ModActions { + if x.Type() == y.Type() { + a = y + i = idx + break + } + } + switch op { + case ADD: + if a != nil { + return fmt.Errorf("action %d is already set", x.Type()) + } + if as == nil { + as = make([]Action, 0, len(rhs.ModActions)) + } + as = append(as, x) + case REMOVE: + if a == nil { + return fmt.Errorf("action %d is not set", x.Type()) + } + as = append(as[:i], as[i+1:]...) + if len(as) == 0 { + as = nil + } + case REPLACE: + if a == nil { + return fmt.Errorf("action %d is not set", x.Type()) + } + as[i] = x + } + } + lhs.Conditions = cs + lhs.RouteAction = ra + lhs.ModActions = as + return nil +} + +func (lhs *Statement) Add(rhs *Statement) error { + return lhs.mod(ADD, rhs) +} + +func (lhs *Statement) Remove(rhs *Statement) error { + return lhs.mod(REMOVE, rhs) +} + +func (lhs *Statement) Replace(rhs *Statement) error { + return lhs.mod(REPLACE, rhs) +} + +func NewStatement(c config.Statement) (*Statement, error) { + if c.Name == "" { + return nil, fmt.Errorf("empty statement name") + } + var ra Action + var as []Action + var cs []Condition + var err error + cfs := []func() (Condition, error){ + func() (Condition, error) { + return NewPrefixCondition(c.Conditions.MatchPrefixSet) + }, + func() (Condition, error) { + return NewNeighborCondition(c.Conditions.MatchNeighborSet) + }, + func() (Condition, error) { + return NewAsPathLengthCondition(c.Conditions.BgpConditions.AsPathLength) + }, + func() (Condition, error) { + return NewRpkiValidationCondition(c.Conditions.BgpConditions.RpkiValidationResult) + }, + func() (Condition, error) { + return NewRouteTypeCondition(c.Conditions.BgpConditions.RouteType) + }, + func() (Condition, error) { + return NewAsPathCondition(c.Conditions.BgpConditions.MatchAsPathSet) + }, + func() (Condition, error) { + return NewCommunityCondition(c.Conditions.BgpConditions.MatchCommunitySet) + }, + func() (Condition, error) { + return NewExtCommunityCondition(c.Conditions.BgpConditions.MatchExtCommunitySet) + }, + func() (Condition, error) { + return NewLargeCommunityCondition(c.Conditions.BgpConditions.MatchLargeCommunitySet) + }, + func() (Condition, error) { + return NewNextHopCondition(c.Conditions.BgpConditions.NextHopInList) + }, + func() (Condition, error) { + return NewAfiSafiInCondition(c.Conditions.BgpConditions.AfiSafiInList) + }, + } + cs = make([]Condition, 0, len(cfs)) + for _, f := range cfs { + c, err := f() + if err != nil { + return nil, err + } + if !reflect.ValueOf(c).IsNil() { + cs = append(cs, c) + } + } + ra, err = NewRoutingAction(c.Actions.RouteDisposition) + if err != nil { + return nil, err + } + afs := []func() (Action, error){ + func() (Action, error) { + return NewCommunityAction(c.Actions.BgpActions.SetCommunity) + }, + func() (Action, error) { + return NewExtCommunityAction(c.Actions.BgpActions.SetExtCommunity) + }, + func() (Action, error) { + return NewLargeCommunityAction(c.Actions.BgpActions.SetLargeCommunity) + }, + func() (Action, error) { + return NewMedAction(c.Actions.BgpActions.SetMed) + }, + func() (Action, error) { + return NewLocalPrefAction(c.Actions.BgpActions.SetLocalPref) + }, + func() (Action, error) { + return NewAsPathPrependAction(c.Actions.BgpActions.SetAsPathPrepend) + }, + func() (Action, error) { + return NewNexthopAction(c.Actions.BgpActions.SetNextHop) + }, + } + as = make([]Action, 0, len(afs)) + for _, f := range afs { + a, err := f() + if err != nil { + return nil, err + } + if !reflect.ValueOf(a).IsNil() { + as = append(as, a) + } + } + return &Statement{ + Name: c.Name, + Conditions: cs, + RouteAction: ra, + ModActions: as, + }, nil +} + +type Policy struct { + Name string + Statements []*Statement +} + +// Compare path with a policy's condition in stored order in the policy. +// If a condition match, then this function stops evaluation and +// subsequent conditions are skipped. +func (p *Policy) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) { + for _, stmt := range p.Statements { + var result RouteType + result, path = stmt.Apply(path, options) + if result != ROUTE_TYPE_NONE { + return result, path + } + } + return ROUTE_TYPE_NONE, path +} + +func (p *Policy) ToConfig() *config.PolicyDefinition { + ss := make([]config.Statement, 0, len(p.Statements)) + for _, s := range p.Statements { + ss = append(ss, *s.ToConfig()) + } + return &config.PolicyDefinition{ + Name: p.Name, + Statements: ss, + } +} + +func (p *Policy) FillUp(m map[string]*Statement) error { + stmts := make([]*Statement, 0, len(p.Statements)) + for _, x := range p.Statements { + y, ok := m[x.Name] + if !ok { + return fmt.Errorf("not found statement %s", x.Name) + } + stmts = append(stmts, y) + } + p.Statements = stmts + return nil +} + +func (lhs *Policy) Add(rhs *Policy) error { + lhs.Statements = append(lhs.Statements, rhs.Statements...) + return nil +} + +func (lhs *Policy) Remove(rhs *Policy) error { + stmts := make([]*Statement, 0, len(lhs.Statements)) + for _, x := range lhs.Statements { + found := false + for _, y := range rhs.Statements { + if x.Name == y.Name { + found = true + break + } + } + if !found { + stmts = append(stmts, x) + } + } + lhs.Statements = stmts + return nil +} + +func (lhs *Policy) Replace(rhs *Policy) error { + lhs.Statements = rhs.Statements + return nil +} + +func (p *Policy) MarshalJSON() ([]byte, error) { + return json.Marshal(p.ToConfig()) +} + +func NewPolicy(c config.PolicyDefinition) (*Policy, error) { + if c.Name == "" { + return nil, fmt.Errorf("empty policy name") + } + var st []*Statement + stmts := c.Statements + if len(stmts) != 0 { + st = make([]*Statement, 0, len(stmts)) + for idx, stmt := range stmts { + if stmt.Name == "" { + stmt.Name = fmt.Sprintf("%s_stmt%d", c.Name, idx) + } + s, err := NewStatement(stmt) + if err != nil { + return nil, err + } + st = append(st, s) + } + } + return &Policy{ + Name: c.Name, + Statements: st, + }, nil +} + +type Policies []*Policy + +func (p Policies) Len() int { + return len(p) +} + +func (p Policies) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p Policies) Less(i, j int) bool { + return p[i].Name < p[j].Name +} + +type Assignment struct { + importPolicies []*Policy + defaultImportPolicy RouteType + exportPolicies []*Policy + defaultExportPolicy RouteType +} + +type RoutingPolicy struct { + definedSetMap DefinedSetMap + policyMap map[string]*Policy + statementMap map[string]*Statement + assignmentMap map[string]*Assignment + mu sync.RWMutex +} + +func (r *RoutingPolicy) ApplyPolicy(id string, dir PolicyDirection, before *Path, options *PolicyOptions) *Path { + r.mu.RLock() + defer r.mu.RUnlock() + + if before == nil { + return nil + } + + if before.IsWithdraw { + return before + } + result := ROUTE_TYPE_NONE + after := before + for _, p := range r.getPolicy(id, dir) { + result, after = p.Apply(after, options) + if result != ROUTE_TYPE_NONE { + break + } + } + if result == ROUTE_TYPE_NONE { + result = r.getDefaultPolicy(id, dir) + } + switch result { + case ROUTE_TYPE_ACCEPT: + return after + default: + return nil + } +} + +func (r *RoutingPolicy) getPolicy(id string, dir PolicyDirection) []*Policy { + a, ok := r.assignmentMap[id] + if !ok { + return nil + } + switch dir { + case POLICY_DIRECTION_IMPORT: + return a.importPolicies + case POLICY_DIRECTION_EXPORT: + return a.exportPolicies + default: + return nil + } +} + +func (r *RoutingPolicy) getDefaultPolicy(id string, dir PolicyDirection) RouteType { + a, ok := r.assignmentMap[id] + if !ok { + return ROUTE_TYPE_NONE + } + switch dir { + case POLICY_DIRECTION_IMPORT: + return a.defaultImportPolicy + case POLICY_DIRECTION_EXPORT: + return a.defaultExportPolicy + default: + return ROUTE_TYPE_NONE + } + +} + +func (r *RoutingPolicy) setPolicy(id string, dir PolicyDirection, policies []*Policy) error { + a, ok := r.assignmentMap[id] + if !ok { + a = &Assignment{} + } + switch dir { + case POLICY_DIRECTION_IMPORT: + a.importPolicies = policies + case POLICY_DIRECTION_EXPORT: + a.exportPolicies = policies + } + r.assignmentMap[id] = a + return nil +} + +func (r *RoutingPolicy) setDefaultPolicy(id string, dir PolicyDirection, typ RouteType) error { + a, ok := r.assignmentMap[id] + if !ok { + a = &Assignment{} + } + switch dir { + case POLICY_DIRECTION_IMPORT: + a.defaultImportPolicy = typ + case POLICY_DIRECTION_EXPORT: + a.defaultExportPolicy = typ + } + r.assignmentMap[id] = a + return nil +} + +func (r *RoutingPolicy) getAssignmentFromConfig(dir PolicyDirection, a config.ApplyPolicy) ([]*Policy, RouteType, error) { + var names []string + var cdef config.DefaultPolicyType + def := ROUTE_TYPE_ACCEPT + c := a.Config + switch dir { + case POLICY_DIRECTION_IMPORT: + names = c.ImportPolicyList + cdef = c.DefaultImportPolicy + case POLICY_DIRECTION_EXPORT: + names = c.ExportPolicyList + cdef = c.DefaultExportPolicy + default: + return nil, def, fmt.Errorf("invalid policy direction") + } + if cdef == config.DEFAULT_POLICY_TYPE_REJECT_ROUTE { + def = ROUTE_TYPE_REJECT + } + ps := make([]*Policy, 0, len(names)) + seen := make(map[string]bool) + for _, name := range names { + p, ok := r.policyMap[name] + if !ok { + return nil, def, fmt.Errorf("not found policy %s", name) + } + if seen[name] { + return nil, def, fmt.Errorf("duplicated policy %s", name) + } + seen[name] = true + ps = append(ps, p) + } + return ps, def, nil +} + +func (r *RoutingPolicy) validateCondition(v Condition) (err error) { + switch v.Type() { + case CONDITION_PREFIX: + m := r.definedSetMap[DEFINED_TYPE_PREFIX] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found prefix set %s", v.Name()) + } else { + c := v.(*PrefixCondition) + c.set = i.(*PrefixSet) + } + case CONDITION_NEIGHBOR: + m := r.definedSetMap[DEFINED_TYPE_NEIGHBOR] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found neighbor set %s", v.Name()) + } else { + c := v.(*NeighborCondition) + c.set = i.(*NeighborSet) + } + case CONDITION_AS_PATH: + m := r.definedSetMap[DEFINED_TYPE_AS_PATH] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found as path set %s", v.Name()) + } else { + c := v.(*AsPathCondition) + c.set = i.(*AsPathSet) + } + case CONDITION_COMMUNITY: + m := r.definedSetMap[DEFINED_TYPE_COMMUNITY] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found community set %s", v.Name()) + } else { + c := v.(*CommunityCondition) + c.set = i.(*CommunitySet) + } + case CONDITION_EXT_COMMUNITY: + m := r.definedSetMap[DEFINED_TYPE_EXT_COMMUNITY] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found ext-community set %s", v.Name()) + } else { + c := v.(*ExtCommunityCondition) + c.set = i.(*ExtCommunitySet) + } + case CONDITION_LARGE_COMMUNITY: + m := r.definedSetMap[DEFINED_TYPE_LARGE_COMMUNITY] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found large-community set %s", v.Name()) + } else { + c := v.(*LargeCommunityCondition) + c.set = i.(*LargeCommunitySet) + } + case CONDITION_NEXT_HOP: + case CONDITION_AFI_SAFI_IN: + case CONDITION_AS_PATH_LENGTH: + case CONDITION_RPKI: + } + return nil +} + +func (r *RoutingPolicy) inUse(d DefinedSet) bool { + name := d.Name() + for _, p := range r.policyMap { + for _, s := range p.Statements { + for _, c := range s.Conditions { + if c.Set() != nil && c.Set().Name() == name { + return true + } + } + } + } + return false +} + +func (r *RoutingPolicy) statementInUse(x *Statement) bool { + for _, p := range r.policyMap { + for _, y := range p.Statements { + if x.Name == y.Name { + return true + } + } + } + return false +} + +func (r *RoutingPolicy) reload(c config.RoutingPolicy) error { + dmap := make(map[DefinedType]map[string]DefinedSet) + dmap[DEFINED_TYPE_PREFIX] = make(map[string]DefinedSet) + d := c.DefinedSets + for _, x := range d.PrefixSets { + y, err := NewPrefixSet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty prefix set") + } + dmap[DEFINED_TYPE_PREFIX][y.Name()] = y + } + dmap[DEFINED_TYPE_NEIGHBOR] = make(map[string]DefinedSet) + for _, x := range d.NeighborSets { + y, err := NewNeighborSet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty neighbor set") + } + dmap[DEFINED_TYPE_NEIGHBOR][y.Name()] = y + } + // dmap[DEFINED_TYPE_TAG] = make(map[string]DefinedSet) + // for _, x := range c.DefinedSets.TagSets{ + // y, err := NewTagSet(x) + // if err != nil { + // return nil, err + // } + // dmap[DEFINED_TYPE_TAG][y.Name()] = y + // } + bd := c.DefinedSets.BgpDefinedSets + dmap[DEFINED_TYPE_AS_PATH] = make(map[string]DefinedSet) + for _, x := range bd.AsPathSets { + y, err := NewAsPathSet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty as path set") + } + dmap[DEFINED_TYPE_AS_PATH][y.Name()] = y + } + dmap[DEFINED_TYPE_COMMUNITY] = make(map[string]DefinedSet) + for _, x := range bd.CommunitySets { + y, err := NewCommunitySet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty community set") + } + dmap[DEFINED_TYPE_COMMUNITY][y.Name()] = y + } + dmap[DEFINED_TYPE_EXT_COMMUNITY] = make(map[string]DefinedSet) + for _, x := range bd.ExtCommunitySets { + y, err := NewExtCommunitySet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty ext-community set") + } + dmap[DEFINED_TYPE_EXT_COMMUNITY][y.Name()] = y + } + dmap[DEFINED_TYPE_LARGE_COMMUNITY] = make(map[string]DefinedSet) + for _, x := range bd.LargeCommunitySets { + y, err := NewLargeCommunitySet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty large-community set") + } + dmap[DEFINED_TYPE_LARGE_COMMUNITY][y.Name()] = y + } + + pmap := make(map[string]*Policy) + smap := make(map[string]*Statement) + for _, x := range c.PolicyDefinitions { + y, err := NewPolicy(x) + if err != nil { + return err + } + if _, ok := pmap[y.Name]; ok { + return fmt.Errorf("duplicated policy name. policy name must be unique.") + } + pmap[y.Name] = y + for _, s := range y.Statements { + _, ok := smap[s.Name] + if ok { + return fmt.Errorf("duplicated statement name. statement name must be unique.") + } + smap[s.Name] = s + } + } + + // hacky + oldMap := r.definedSetMap + r.definedSetMap = dmap + for _, y := range pmap { + for _, s := range y.Statements { + for _, c := range s.Conditions { + if err := r.validateCondition(c); err != nil { + r.definedSetMap = oldMap + return err + } + } + } + } + + r.definedSetMap = dmap + r.policyMap = pmap + r.statementMap = smap + r.assignmentMap = make(map[string]*Assignment) + // allow all routes coming in and going out by default + r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_IMPORT, ROUTE_TYPE_ACCEPT) + r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_EXPORT, ROUTE_TYPE_ACCEPT) + return nil +} + +func (r *RoutingPolicy) GetDefinedSet(typ DefinedType, name string) (*config.DefinedSets, error) { + r.mu.RLock() + + set, ok := r.definedSetMap[typ] + if !ok { + return nil, fmt.Errorf("invalid defined-set type: %d", typ) + } + + var dl DefinedSetList + for _, s := range set { + dl = append(dl, s) + } + r.mu.RUnlock() + + sort.Sort(dl) + + sets := &config.DefinedSets{ + PrefixSets: make([]config.PrefixSet, 0), + NeighborSets: make([]config.NeighborSet, 0), + BgpDefinedSets: config.BgpDefinedSets{ + CommunitySets: make([]config.CommunitySet, 0), + ExtCommunitySets: make([]config.ExtCommunitySet, 0), + LargeCommunitySets: make([]config.LargeCommunitySet, 0), + AsPathSets: make([]config.AsPathSet, 0), + }, + } + for _, s := range dl { + if name != "" && s.Name() != name { + continue + } + switch s.(type) { + case *PrefixSet: + sets.PrefixSets = append(sets.PrefixSets, *s.(*PrefixSet).ToConfig()) + case *NeighborSet: + sets.NeighborSets = append(sets.NeighborSets, *s.(*NeighborSet).ToConfig()) + case *CommunitySet: + sets.BgpDefinedSets.CommunitySets = append(sets.BgpDefinedSets.CommunitySets, *s.(*CommunitySet).ToConfig()) + case *ExtCommunitySet: + sets.BgpDefinedSets.ExtCommunitySets = append(sets.BgpDefinedSets.ExtCommunitySets, *s.(*ExtCommunitySet).ToConfig()) + case *LargeCommunitySet: + sets.BgpDefinedSets.LargeCommunitySets = append(sets.BgpDefinedSets.LargeCommunitySets, *s.(*LargeCommunitySet).ToConfig()) + case *AsPathSet: + sets.BgpDefinedSets.AsPathSets = append(sets.BgpDefinedSets.AsPathSets, *s.(*AsPathSet).ToConfig()) + } + } + return sets, nil +} + +func (r *RoutingPolicy) AddDefinedSet(s DefinedSet) error { + r.mu.Lock() + defer r.mu.Unlock() + + if m, ok := r.definedSetMap[s.Type()]; !ok { + return fmt.Errorf("invalid defined-set type: %d", s.Type()) + } else { + if d, ok := m[s.Name()]; ok { + if err := d.Append(s); err != nil { + return err + } + } else { + m[s.Name()] = s + } + } + return nil +} + +func (r *RoutingPolicy) DeleteDefinedSet(a DefinedSet, all bool) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + if m, ok := r.definedSetMap[a.Type()]; !ok { + err = fmt.Errorf("invalid defined-set type: %d", a.Type()) + } else { + d, ok := m[a.Name()] + if !ok { + return fmt.Errorf("not found defined-set: %s", a.Name()) + } + if all { + if r.inUse(d) { + err = fmt.Errorf("can't delete. defined-set %s is in use", a.Name()) + } else { + delete(m, a.Name()) + } + } else { + err = d.Remove(a) + } + } + return err +} + +func (r *RoutingPolicy) GetStatement(name string) []*config.Statement { + r.mu.RLock() + defer r.mu.RUnlock() + + l := make([]*config.Statement, 0, len(r.statementMap)) + for _, st := range r.statementMap { + if name != "" && name != st.Name { + continue + } + l = append(l, st.ToConfig()) + } + return l +} + +func (r *RoutingPolicy) AddStatement(st *Statement) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + for _, c := range st.Conditions { + if err = r.validateCondition(c); err != nil { + return + } + } + m := r.statementMap + name := st.Name + if d, ok := m[name]; ok { + err = d.Add(st) + } else { + m[name] = st + } + + return err +} + +func (r *RoutingPolicy) DeleteStatement(st *Statement, all bool) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + m := r.statementMap + name := st.Name + if d, ok := m[name]; ok { + if all { + if r.statementInUse(d) { + err = fmt.Errorf("can't delete. statement %s is in use", name) + } else { + delete(m, name) + } + } else { + err = d.Remove(st) + } + } else { + err = fmt.Errorf("not found statement: %s", name) + } + return err +} + +func (r *RoutingPolicy) GetPolicy(name string) []*config.PolicyDefinition { + r.mu.RLock() + + var ps Policies + for _, p := range r.policyMap { + if name != "" && name != p.Name { + continue + } + ps = append(ps, p) + } + r.mu.RUnlock() + + sort.Sort(ps) + + l := make([]*config.PolicyDefinition, 0, len(ps)) + for _, p := range ps { + l = append(l, p.ToConfig()) + } + return l +} + +func (r *RoutingPolicy) AddPolicy(x *Policy, refer bool) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + for _, st := range x.Statements { + for _, c := range st.Conditions { + if err = r.validateCondition(c); err != nil { + return + } + } + } + + pMap := r.policyMap + sMap := r.statementMap + name := x.Name + y, ok := pMap[name] + if refer { + err = x.FillUp(sMap) + } else { + for _, st := range x.Statements { + if _, ok := sMap[st.Name]; ok { + err = fmt.Errorf("statement %s already defined", st.Name) + return + } + sMap[st.Name] = st + } + } + if ok { + err = y.Add(x) + } else { + pMap[name] = x + } + + return err +} + +func (r *RoutingPolicy) DeletePolicy(x *Policy, all, preserve bool, activeId []string) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + pMap := r.policyMap + sMap := r.statementMap + name := x.Name + y, ok := pMap[name] + if !ok { + err = fmt.Errorf("not found policy: %s", name) + return + } + inUse := func(ids []string) bool { + for _, id := range ids { + for _, dir := range []PolicyDirection{POLICY_DIRECTION_EXPORT, POLICY_DIRECTION_EXPORT} { + for _, y := range r.getPolicy(id, dir) { + if x.Name == y.Name { + return true + } + } + } + } + return false + } + + if all { + if inUse(activeId) { + err = fmt.Errorf("can't delete. policy %s is in use", name) + return + } + log.WithFields(log.Fields{ + "Topic": "Policy", + "Key": name, + }).Debug("delete policy") + delete(pMap, name) + } else { + err = y.Remove(x) + } + if err == nil && !preserve { + for _, st := range y.Statements { + if !r.statementInUse(st) { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Key": st.Name, + }).Debug("delete unused statement") + delete(sMap, st.Name) + } + } + } + return err +} + +func (r *RoutingPolicy) GetPolicyAssignment(id string, dir PolicyDirection) (RouteType, []*Policy, error) { + r.mu.RLock() + defer r.mu.RUnlock() + + rt := r.getDefaultPolicy(id, dir) + l := make([]*Policy, 0) + l = append(l, r.getPolicy(id, dir)...) + return rt, l, nil +} + +func (r *RoutingPolicy) AddPolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + ps := make([]*Policy, 0, len(policies)) + seen := make(map[string]bool) + for _, x := range policies { + p, ok := r.policyMap[x.Name] + if !ok { + err = fmt.Errorf("not found policy %s", x.Name) + return + } + if seen[x.Name] { + err = fmt.Errorf("duplicated policy %s", x.Name) + return + } + seen[x.Name] = true + ps = append(ps, p) + } + cur := r.getPolicy(id, dir) + if cur == nil { + err = r.setPolicy(id, dir, ps) + } else { + seen = make(map[string]bool) + ps = append(cur, ps...) + for _, x := range ps { + if seen[x.Name] { + err = fmt.Errorf("duplicated policy %s", x.Name) + return + } + seen[x.Name] = true + } + err = r.setPolicy(id, dir, ps) + } + if err == nil && def != ROUTE_TYPE_NONE { + err = r.setDefaultPolicy(id, dir, def) + } + return err +} + +func (r *RoutingPolicy) DeletePolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, all bool) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + ps := make([]*Policy, 0, len(policies)) + seen := make(map[string]bool) + for _, x := range policies { + p, ok := r.policyMap[x.Name] + if !ok { + err = fmt.Errorf("not found policy %s", x.Name) + return + } + if seen[x.Name] { + err = fmt.Errorf("duplicated policy %s", x.Name) + return + } + seen[x.Name] = true + ps = append(ps, p) + } + cur := r.getPolicy(id, dir) + + if all { + err = r.setPolicy(id, dir, nil) + if err != nil { + return + } + err = r.setDefaultPolicy(id, dir, ROUTE_TYPE_NONE) + } else { + l := len(cur) - len(ps) + if l < 0 { + // try to remove more than the assigned policies... + l = len(cur) + } + n := make([]*Policy, 0, l) + for _, y := range cur { + found := false + for _, x := range ps { + if x.Name == y.Name { + found = true + break + } + } + if !found { + n = append(n, y) + } + } + err = r.setPolicy(id, dir, n) + } + return err +} + +func (r *RoutingPolicy) SetPolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + ps := make([]*Policy, 0, len(policies)) + seen := make(map[string]bool) + for _, x := range policies { + p, ok := r.policyMap[x.Name] + if !ok { + err = fmt.Errorf("not found policy %s", x.Name) + return + } + if seen[x.Name] { + err = fmt.Errorf("duplicated policy %s", x.Name) + return + } + seen[x.Name] = true + ps = append(ps, p) + } + r.getPolicy(id, dir) + err = r.setPolicy(id, dir, ps) + if err == nil && def != ROUTE_TYPE_NONE { + err = r.setDefaultPolicy(id, dir, def) + } + return err +} + +func (r *RoutingPolicy) Reset(rp *config.RoutingPolicy, ap map[string]config.ApplyPolicy) error { + r.mu.Lock() + defer r.mu.Unlock() + + if rp != nil { + if err := r.reload(*rp); err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + }).Errorf("failed to create routing policy: %s", err) + return err + } + } + + for id, c := range ap { + for _, dir := range []PolicyDirection{POLICY_DIRECTION_IMPORT, POLICY_DIRECTION_EXPORT} { + ps, def, err := r.getAssignmentFromConfig(dir, c) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Dir": dir, + }).Errorf("failed to get policy info: %s", err) + continue + } + r.setDefaultPolicy(id, dir, def) + r.setPolicy(id, dir, ps) + } + } + return nil +} + +func NewRoutingPolicy() *RoutingPolicy { + return &RoutingPolicy{ + definedSetMap: make(map[DefinedType]map[string]DefinedSet), + policyMap: make(map[string]*Policy), + statementMap: make(map[string]*Statement), + assignmentMap: make(map[string]*Assignment), + } +} + +func CanImportToVrf(v *Vrf, path *Path) bool { + f := func(arg []bgp.ExtendedCommunityInterface) []string { + ret := make([]string, 0, len(arg)) + for _, a := range arg { + ret = append(ret, fmt.Sprintf("RT:%s", a.String())) + } + return ret + } + set, _ := NewExtCommunitySet(config.ExtCommunitySet{ + ExtCommunitySetName: v.Name, + ExtCommunityList: f(v.ImportRt), + }) + matchSet := config.MatchExtCommunitySet{ + ExtCommunitySet: v.Name, + MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY, + } + c, _ := NewExtCommunityCondition(matchSet) + c.set = set + return c.Evaluate(path, nil) +} + +type PolicyAssignment struct { + Name string + Type PolicyDirection + Policies []*Policy + Default RouteType +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/roa.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/roa.go new file mode 100644 index 0000000..fe08fe5 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/roa.go @@ -0,0 +1,60 @@ +// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "fmt" + "net" +) + +type IPPrefix struct { + Prefix net.IP + Length uint8 +} + +func (p *IPPrefix) String() string { + return fmt.Sprintf("%s/%d", p.Prefix, p.Length) +} + +type ROA struct { + Family int + Prefix *IPPrefix + MaxLen uint8 + AS uint32 + Src string +} + +func NewROA(family int, prefixByte []byte, prefixLen uint8, maxLen uint8, as uint32, src string) *ROA { + p := make([]byte, len(prefixByte)) + copy(p, prefixByte) + return &ROA{ + Family: family, + Prefix: &IPPrefix{ + Prefix: p, + Length: prefixLen, + }, + MaxLen: maxLen, + AS: as, + Src: src, + } +} + +func (r *ROA) Equal(roa *ROA) bool { + if r.MaxLen == roa.MaxLen && r.Src == roa.Src && r.AS == roa.AS { + return true + } + return false +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/table.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/table.go new file mode 100644 index 0000000..bcde936 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/table.go @@ -0,0 +1,451 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "fmt" + "net" + "strings" + "unsafe" + + "github.com/armon/go-radix" + "github.com/osrg/gobgp/pkg/packet/bgp" + log "github.com/sirupsen/logrus" +) + +type LookupOption uint8 + +const ( + LOOKUP_EXACT LookupOption = iota + LOOKUP_LONGER + LOOKUP_SHORTER +) + +type LookupPrefix struct { + Prefix string + LookupOption +} + +type TableSelectOption struct { + ID string + AS uint32 + LookupPrefixes []*LookupPrefix + VRF *Vrf + adj bool + Best bool + MultiPath bool +} + +type Table struct { + routeFamily bgp.RouteFamily + destinations map[string]*Destination +} + +func NewTable(rf bgp.RouteFamily, dsts ...*Destination) *Table { + t := &Table{ + routeFamily: rf, + destinations: make(map[string]*Destination), + } + for _, dst := range dsts { + t.setDestination(dst) + } + return t +} + +func (t *Table) GetRoutefamily() bgp.RouteFamily { + return t.routeFamily +} + +func (t *Table) deletePathsByVrf(vrf *Vrf) []*Path { + pathList := make([]*Path, 0) + for _, dest := range t.destinations { + for _, p := range dest.knownPathList { + var rd bgp.RouteDistinguisherInterface + nlri := p.GetNlri() + switch nlri.(type) { + case *bgp.LabeledVPNIPAddrPrefix: + rd = nlri.(*bgp.LabeledVPNIPAddrPrefix).RD + case *bgp.LabeledVPNIPv6AddrPrefix: + rd = nlri.(*bgp.LabeledVPNIPv6AddrPrefix).RD + case *bgp.EVPNNLRI: + rd = nlri.(*bgp.EVPNNLRI).RD() + default: + return pathList + } + if p.IsLocal() && vrf.Rd.String() == rd.String() { + pathList = append(pathList, p.Clone(true)) + break + } + } + } + return pathList +} + +func (t *Table) deleteRTCPathsByVrf(vrf *Vrf, vrfs map[string]*Vrf) []*Path { + pathList := make([]*Path, 0) + if t.routeFamily != bgp.RF_RTC_UC { + return pathList + } + for _, target := range vrf.ImportRt { + lhs := target.String() + for _, dest := range t.destinations { + nlri := dest.GetNlri().(*bgp.RouteTargetMembershipNLRI) + rhs := nlri.RouteTarget.String() + if lhs == rhs && isLastTargetUser(vrfs, target) { + for _, p := range dest.knownPathList { + if p.IsLocal() { + pathList = append(pathList, p.Clone(true)) + break + } + } + } + } + } + return pathList +} + +func (t *Table) deleteDestByNlri(nlri bgp.AddrPrefixInterface) *Destination { + if dst := t.GetDestination(nlri); dst != nil { + t.deleteDest(dst) + return dst + } + return nil +} + +func (t *Table) deleteDest(dest *Destination) { + destinations := t.GetDestinations() + delete(destinations, t.tableKey(dest.GetNlri())) + if len(destinations) == 0 { + t.destinations = make(map[string]*Destination) + } +} + +func (t *Table) validatePath(path *Path) { + if path == nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": t.routeFamily, + }).Error("path is nil") + } + if path.GetRouteFamily() != t.routeFamily { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": t.routeFamily, + "Prefix": path.GetNlri().String(), + "ReceivedRf": path.GetRouteFamily().String(), + }).Error("Invalid path. RouteFamily mismatch") + } + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); attr != nil { + pathParam := attr.(*bgp.PathAttributeAsPath).Value + for _, as := range pathParam { + _, y := as.(*bgp.As4PathParam) + if !y { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": t.routeFamily, + "As": as, + }).Fatal("AsPathParam must be converted to As4PathParam") + } + } + } + if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS4_PATH); attr != nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": t.routeFamily, + }).Fatal("AS4_PATH must be converted to AS_PATH") + } + if path.GetNlri() == nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Key": t.routeFamily, + }).Fatal("path's nlri is nil") + } +} + +func (t *Table) getOrCreateDest(nlri bgp.AddrPrefixInterface) *Destination { + dest := t.GetDestination(nlri) + // If destination for given prefix does not exist we create it. + if dest == nil { + log.WithFields(log.Fields{ + "Topic": "Table", + "Nlri": nlri, + }).Debugf("create Destination") + dest = NewDestination(nlri, 64) + t.setDestination(dest) + } + return dest +} + +func (t *Table) GetDestinations() map[string]*Destination { + return t.destinations +} +func (t *Table) setDestinations(destinations map[string]*Destination) { + t.destinations = destinations +} +func (t *Table) GetDestination(nlri bgp.AddrPrefixInterface) *Destination { + dest, ok := t.destinations[t.tableKey(nlri)] + if ok { + return dest + } else { + return nil + } +} + +func (t *Table) GetLongerPrefixDestinations(key string) ([]*Destination, error) { + results := make([]*Destination, 0, len(t.GetDestinations())) + switch t.routeFamily { + case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC, bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS: + _, prefix, err := net.ParseCIDR(key) + if err != nil { + return nil, err + } + k := CidrToRadixkey(prefix.String()) + r := radix.New() + for _, dst := range t.GetDestinations() { + r.Insert(AddrToRadixkey(dst.nlri), dst) + } + r.WalkPrefix(k, func(s string, v interface{}) bool { + results = append(results, v.(*Destination)) + return false + }) + default: + for _, dst := range t.GetDestinations() { + results = append(results, dst) + } + } + return results, nil +} + +func (t *Table) GetEvpnDestinationsWithRouteType(typ string) ([]*Destination, error) { + var routeType uint8 + switch strings.ToLower(typ) { + case "a-d": + routeType = bgp.EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY + case "macadv": + routeType = bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT + case "multicast": + routeType = bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG + case "esi": + routeType = bgp.EVPN_ETHERNET_SEGMENT_ROUTE + case "prefix": + routeType = bgp.EVPN_IP_PREFIX + default: + return nil, fmt.Errorf("unsupported evpn route type: %s", typ) + } + destinations := t.GetDestinations() + results := make([]*Destination, 0, len(destinations)) + switch t.routeFamily { + case bgp.RF_EVPN: + for _, dst := range destinations { + if nlri, ok := dst.nlri.(*bgp.EVPNNLRI); !ok { + return nil, fmt.Errorf("invalid evpn nlri type detected: %T", dst.nlri) + } else if nlri.RouteType == routeType { + results = append(results, dst) + } + } + default: + for _, dst := range destinations { + results = append(results, dst) + } + } + return results, nil +} + +func (t *Table) setDestination(dst *Destination) { + t.destinations[t.tableKey(dst.nlri)] = dst +} + +func (t *Table) tableKey(nlri bgp.AddrPrefixInterface) string { + switch T := nlri.(type) { + case *bgp.IPAddrPrefix: + b := make([]byte, 5) + copy(b, T.Prefix.To4()) + b[4] = T.Length + return *(*string)(unsafe.Pointer(&b)) + case *bgp.IPv6AddrPrefix: + b := make([]byte, 17) + copy(b, T.Prefix.To16()) + b[16] = T.Length + return *(*string)(unsafe.Pointer(&b)) + } + return nlri.String() +} + +func (t *Table) Bests(id string, as uint32) []*Path { + paths := make([]*Path, 0, len(t.destinations)) + for _, dst := range t.destinations { + path := dst.GetBestPath(id, as) + if path != nil { + paths = append(paths, path) + } + } + return paths +} + +func (t *Table) MultiBests(id string) [][]*Path { + paths := make([][]*Path, 0, len(t.destinations)) + for _, dst := range t.destinations { + path := dst.GetMultiBestPath(id) + if path != nil { + paths = append(paths, path) + } + } + return paths +} + +func (t *Table) GetKnownPathList(id string, as uint32) []*Path { + paths := make([]*Path, 0, len(t.destinations)) + for _, dst := range t.destinations { + paths = append(paths, dst.GetKnownPathList(id, as)...) + } + return paths +} + +func (t *Table) Select(option ...TableSelectOption) (*Table, error) { + id := GLOBAL_RIB_NAME + var vrf *Vrf + adj := false + prefixes := make([]*LookupPrefix, 0, len(option)) + best := false + mp := false + as := uint32(0) + for _, o := range option { + if o.ID != "" { + id = o.ID + } + if o.VRF != nil { + vrf = o.VRF + } + adj = o.adj + prefixes = append(prefixes, o.LookupPrefixes...) + best = o.Best + mp = o.MultiPath + as = o.AS + } + dOption := DestinationSelectOption{ID: id, AS: as, VRF: vrf, adj: adj, Best: best, MultiPath: mp} + r := &Table{ + routeFamily: t.routeFamily, + destinations: make(map[string]*Destination), + } + + if len(prefixes) != 0 { + switch t.routeFamily { + case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC: + f := func(prefixStr string) bool { + var nlri bgp.AddrPrefixInterface + if t.routeFamily == bgp.RF_IPv4_UC { + nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, prefixStr) + } else { + nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP6, bgp.SAFI_UNICAST, prefixStr) + } + if dst := t.GetDestination(nlri); dst != nil { + if d := dst.Select(dOption); d != nil { + r.setDestination(d) + return true + } + } + return false + } + + for _, p := range prefixes { + key := p.Prefix + switch p.LookupOption { + case LOOKUP_LONGER: + ds, err := t.GetLongerPrefixDestinations(key) + if err != nil { + return nil, err + } + for _, dst := range ds { + if d := dst.Select(dOption); d != nil { + r.setDestination(d) + } + } + case LOOKUP_SHORTER: + addr, prefix, err := net.ParseCIDR(key) + if err != nil { + return nil, err + } + ones, _ := prefix.Mask.Size() + for i := ones; i >= 0; i-- { + _, prefix, _ := net.ParseCIDR(fmt.Sprintf("%s/%d", addr.String(), i)) + f(prefix.String()) + } + default: + if host := net.ParseIP(key); host != nil { + masklen := 32 + if t.routeFamily == bgp.RF_IPv6_UC { + masklen = 128 + } + for i := masklen; i >= 0; i-- { + _, prefix, err := net.ParseCIDR(fmt.Sprintf("%s/%d", key, i)) + if err != nil { + return nil, err + } + if f(prefix.String()) { + break + } + } + } else { + f(key) + } + } + } + case bgp.RF_EVPN: + for _, p := range prefixes { + // Uses LookupPrefix.Prefix as EVPN Route Type string + ds, err := t.GetEvpnDestinationsWithRouteType(p.Prefix) + if err != nil { + return nil, err + } + for _, dst := range ds { + if d := dst.Select(dOption); d != nil { + r.setDestination(d) + } + } + } + default: + return nil, fmt.Errorf("route filtering is not supported for this family") + } + } else { + for _, dst := range t.GetDestinations() { + if d := dst.Select(dOption); d != nil { + r.setDestination(d) + } + } + } + return r, nil +} + +type TableInfo struct { + NumDestination int + NumPath int + NumAccepted int +} + +func (t *Table) Info(id string, as uint32) *TableInfo { + var numD, numP int + for _, d := range t.destinations { + ps := d.GetKnownPathList(id, as) + if len(ps) > 0 { + numD += 1 + numP += len(ps) + } + } + return &TableInfo{ + NumDestination: numD, + NumPath: numP, + } +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/table_manager.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/table_manager.go new file mode 100644 index 0000000..2640f62 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/table_manager.go @@ -0,0 +1,370 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "bytes" + "fmt" + "net" + "time" + + farm "github.com/dgryski/go-farm" + log "github.com/sirupsen/logrus" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +const ( + GLOBAL_RIB_NAME = "global" +) + +func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time) []*Path { + update := m.Body.(*bgp.BGPUpdate) + + if y, f := update.IsEndOfRib(); y { + // this message has no normal updates or withdrawals. + return []*Path{NewEOR(f)} + } + + adds := make([]bgp.AddrPrefixInterface, 0, len(update.NLRI)) + for _, nlri := range update.NLRI { + adds = append(adds, nlri) + } + + dels := make([]bgp.AddrPrefixInterface, 0, len(update.WithdrawnRoutes)) + for _, nlri := range update.WithdrawnRoutes { + dels = append(dels, nlri) + } + + attrs := make([]bgp.PathAttributeInterface, 0, len(update.PathAttributes)) + var reach *bgp.PathAttributeMpReachNLRI + for _, attr := range update.PathAttributes { + switch a := attr.(type) { + case *bgp.PathAttributeMpReachNLRI: + reach = a + case *bgp.PathAttributeMpUnreachNLRI: + l := make([]bgp.AddrPrefixInterface, 0, len(a.Value)) + l = append(l, a.Value...) + dels = append(dels, l...) + default: + attrs = append(attrs, attr) + } + } + + listLen := len(adds) + len(dels) + if reach != nil { + listLen += len(reach.Value) + } + + var hash uint32 + if len(adds) > 0 || reach != nil { + total := bytes.NewBuffer(make([]byte, 0)) + for _, a := range attrs { + b, _ := a.Serialize() + total.Write(b) + } + hash = farm.Hash32(total.Bytes()) + } + + pathList := make([]*Path, 0, listLen) + for _, nlri := range adds { + p := NewPath(peerInfo, nlri, false, attrs, timestamp, false) + p.SetHash(hash) + pathList = append(pathList, p) + } + if reach != nil { + reachAttrs := make([]bgp.PathAttributeInterface, len(attrs)+1) + copy(reachAttrs, attrs) + // we sort attributes when creating a bgp message from paths + reachAttrs[len(reachAttrs)-1] = reach + + for _, nlri := range reach.Value { + p := NewPath(peerInfo, nlri, false, reachAttrs, timestamp, false) + p.SetHash(hash) + pathList = append(pathList, p) + } + } + for _, nlri := range dels { + p := NewPath(peerInfo, nlri, true, []bgp.PathAttributeInterface{}, timestamp, false) + pathList = append(pathList, p) + } + return pathList +} + +type TableManager struct { + Tables map[bgp.RouteFamily]*Table + Vrfs map[string]*Vrf + rfList []bgp.RouteFamily +} + +func NewTableManager(rfList []bgp.RouteFamily) *TableManager { + t := &TableManager{ + Tables: make(map[bgp.RouteFamily]*Table), + Vrfs: make(map[string]*Vrf), + rfList: rfList, + } + for _, rf := range rfList { + t.Tables[rf] = NewTable(rf) + } + return t +} + +func (manager *TableManager) GetRFlist() []bgp.RouteFamily { + return manager.rfList +} + +func (manager *TableManager) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, importRt, exportRt []bgp.ExtendedCommunityInterface, info *PeerInfo) ([]*Path, error) { + if _, ok := manager.Vrfs[name]; ok { + return nil, fmt.Errorf("vrf %s already exists", name) + } + log.WithFields(log.Fields{ + "Topic": "Vrf", + "Key": name, + "Rd": rd, + "ImportRt": importRt, + "ExportRt": exportRt, + }).Debugf("add vrf") + manager.Vrfs[name] = &Vrf{ + Name: name, + Id: id, + Rd: rd, + ImportRt: importRt, + ExportRt: exportRt, + } + msgs := make([]*Path, 0, len(importRt)) + nexthop := "0.0.0.0" + for _, target := range importRt { + nlri := bgp.NewRouteTargetMembershipNLRI(info.AS, target) + pattr := make([]bgp.PathAttributeInterface, 0, 2) + pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)) + pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})) + msgs = append(msgs, NewPath(info, nlri, false, pattr, time.Now(), false)) + } + return msgs, nil +} + +func (manager *TableManager) DeleteVrf(name string) ([]*Path, error) { + if _, ok := manager.Vrfs[name]; !ok { + return nil, fmt.Errorf("vrf %s not found", name) + } + msgs := make([]*Path, 0) + vrf := manager.Vrfs[name] + for _, t := range manager.Tables { + msgs = append(msgs, t.deletePathsByVrf(vrf)...) + } + log.WithFields(log.Fields{ + "Topic": "Vrf", + "Key": vrf.Name, + "Rd": vrf.Rd, + "ImportRt": vrf.ImportRt, + "ExportRt": vrf.ExportRt, + }).Debugf("delete vrf") + delete(manager.Vrfs, name) + rtcTable := manager.Tables[bgp.RF_RTC_UC] + msgs = append(msgs, rtcTable.deleteRTCPathsByVrf(vrf, manager.Vrfs)...) + return msgs, nil +} + +func (tm *TableManager) update(newPath *Path) *Update { + t := tm.Tables[newPath.GetRouteFamily()] + t.validatePath(newPath) + dst := t.getOrCreateDest(newPath.GetNlri()) + u := dst.Calculate(newPath) + if len(dst.knownPathList) == 0 { + t.deleteDest(dst) + } + return u +} + +func (manager *TableManager) GetPathListByPeer(info *PeerInfo, rf bgp.RouteFamily) []*Path { + if t, ok := manager.Tables[rf]; ok { + pathList := make([]*Path, 0, len(t.destinations)) + for _, dst := range t.destinations { + for _, p := range dst.knownPathList { + if p.GetSource().Equal(info) { + pathList = append(pathList, p) + } + } + } + return pathList + } + return nil +} + +func (manager *TableManager) Update(newPath *Path) []*Update { + if newPath == nil || newPath.IsEOR() { + return nil + } + + // Except for a special case with EVPN, we'll have one destination. + updates := make([]*Update, 0, 1) + family := newPath.GetRouteFamily() + if _, ok := manager.Tables[family]; ok { + updates = append(updates, manager.update(newPath)) + + if family == bgp.RF_EVPN { + for _, p := range manager.handleMacMobility(newPath) { + updates = append(updates, manager.update(p)) + } + } + } + return updates +} + +// EVPN MAC MOBILITY HANDLING +// +// RFC7432 15. MAC Mobility +// +// A PE receiving a MAC/IP Advertisement route for a MAC address with a +// different Ethernet segment identifier and a higher sequence number +// than that which it had previously advertised withdraws its MAC/IP +// Advertisement route. +func (manager *TableManager) handleMacMobility(path *Path) []*Path { + pathList := make([]*Path, 0) + nlri := path.GetNlri().(*bgp.EVPNNLRI) + if path.IsWithdraw || path.IsLocal() || nlri.RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT { + return nil + } + for _, path2 := range manager.GetPathList(GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}) { + if !path2.IsLocal() || path2.GetNlri().(*bgp.EVPNNLRI).RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT { + continue + } + f := func(p *Path) (bgp.EthernetSegmentIdentifier, net.HardwareAddr, int) { + nlri := p.GetNlri().(*bgp.EVPNNLRI) + d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute) + ecs := p.GetExtCommunities() + seq := -1 + for _, ec := range ecs { + if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY { + seq = int(ec.(*bgp.MacMobilityExtended).Sequence) + break + } + } + return d.ESI, d.MacAddress, seq + } + e1, m1, s1 := f(path) + e2, m2, s2 := f(path2) + if bytes.Equal(m1, m2) && !bytes.Equal(e1.Value, e2.Value) && s1 > s2 { + pathList = append(pathList, path2.Clone(true)) + } + } + return pathList +} + +func (manager *TableManager) tables(list ...bgp.RouteFamily) []*Table { + l := make([]*Table, 0, len(manager.Tables)) + if len(list) == 0 { + for _, v := range manager.Tables { + l = append(l, v) + } + return l + } + for _, f := range list { + if t, ok := manager.Tables[f]; ok { + l = append(l, t) + } + } + return l +} + +func (manager *TableManager) getDestinationCount(rfList []bgp.RouteFamily) int { + count := 0 + for _, t := range manager.tables(rfList...) { + count += len(t.GetDestinations()) + } + return count +} + +func (manager *TableManager) GetBestPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path { + if SelectionOptions.DisableBestPathSelection { + // Note: If best path selection disabled, there is no best path. + return nil + } + paths := make([]*Path, 0, manager.getDestinationCount(rfList)) + for _, t := range manager.tables(rfList...) { + paths = append(paths, t.Bests(id, as)...) + } + return paths +} + +func (manager *TableManager) GetBestMultiPathList(id string, rfList []bgp.RouteFamily) [][]*Path { + if !UseMultiplePaths.Enabled || SelectionOptions.DisableBestPathSelection { + // Note: If multi path not enabled or best path selection disabled, + // there is no best multi path. + return nil + } + paths := make([][]*Path, 0, manager.getDestinationCount(rfList)) + for _, t := range manager.tables(rfList...) { + paths = append(paths, t.MultiBests(id)...) + } + return paths +} + +func (manager *TableManager) GetPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path { + paths := make([]*Path, 0, manager.getDestinationCount(rfList)) + for _, t := range manager.tables(rfList...) { + paths = append(paths, t.GetKnownPathList(id, as)...) + } + return paths +} + +func (manager *TableManager) GetPathListWithNexthop(id string, rfList []bgp.RouteFamily, nexthop net.IP) []*Path { + paths := make([]*Path, 0, manager.getDestinationCount(rfList)) + for _, rf := range rfList { + if t, ok := manager.Tables[rf]; ok { + for _, path := range t.GetKnownPathList(id, 0) { + if path.GetNexthop().Equal(nexthop) { + paths = append(paths, path) + } + } + } + } + return paths +} + +func (manager *TableManager) GetPathListWithSource(id string, rfList []bgp.RouteFamily, source *PeerInfo) []*Path { + paths := make([]*Path, 0, manager.getDestinationCount(rfList)) + for _, rf := range rfList { + if t, ok := manager.Tables[rf]; ok { + for _, path := range t.GetKnownPathList(id, 0) { + if path.GetSource().Equal(source) { + paths = append(paths, path) + } + } + } + } + return paths +} + +func (manager *TableManager) GetDestination(path *Path) *Destination { + if path == nil { + return nil + } + family := path.GetRouteFamily() + t, ok := manager.Tables[family] + if !ok { + return nil + } + return t.GetDestination(path.GetNlri()) +} + +func (manager *TableManager) TableInfo(id string, as uint32, family bgp.RouteFamily) (*TableInfo, error) { + t, ok := manager.Tables[family] + if !ok { + return nil, fmt.Errorf("address family %s is not configured", family) + } + return t.Info(id, as), nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/table/vrf.go b/vendor/github.com/osrg/gobgp/internal/pkg/table/vrf.go new file mode 100644 index 0000000..053f85c --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/table/vrf.go @@ -0,0 +1,53 @@ +// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation. +// +// 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 table + +import ( + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +type Vrf struct { + Name string + Id uint32 + Rd bgp.RouteDistinguisherInterface + ImportRt []bgp.ExtendedCommunityInterface + ExportRt []bgp.ExtendedCommunityInterface +} + +func (v *Vrf) Clone() *Vrf { + f := func(rt []bgp.ExtendedCommunityInterface) []bgp.ExtendedCommunityInterface { + l := make([]bgp.ExtendedCommunityInterface, 0, len(rt)) + return append(l, rt...) + } + return &Vrf{ + Name: v.Name, + Id: v.Id, + Rd: v.Rd, + ImportRt: f(v.ImportRt), + ExportRt: f(v.ExportRt), + } +} + +func isLastTargetUser(vrfs map[string]*Vrf, target bgp.ExtendedCommunityInterface) bool { + for _, vrf := range vrfs { + for _, rt := range vrf.ImportRt { + if target.String() == rt.String() { + return false + } + } + } + return true +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/afi_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/afi_string.go new file mode 100644 index 0000000..6a4278a --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/afi_string.go @@ -0,0 +1,17 @@ +// Code generated by "stringer -type=AFI"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _AFI_name = "AFI_IPAFI_IP6AFI_ETHERAFI_MAX" + +var _AFI_index = [...]uint8{0, 6, 13, 22, 29} + +func (i AFI) String() string { + i -= 1 + if i >= AFI(len(_AFI_index)-1) { + return "AFI(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _AFI_name[_AFI_index[i]:_AFI_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/api_type_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/api_type_string.go new file mode 100644 index 0000000..3d78085 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/api_type_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=API_TYPE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _API_TYPE_name = "FRR_ZAPI5_INTERFACE_ADDFRR_ZAPI5_INTERFACE_DELETEFRR_ZAPI5_INTERFACE_ADDRESS_ADDFRR_ZAPI5_INTERFACE_ADDRESS_DELETEFRR_ZAPI5_INTERFACE_UPFRR_ZAPI5_INTERFACE_DOWNFRR_ZAPI5_INTERFACE_SET_MASTERFRR_ZAPI5_ROUTE_ADDFRR_ZAPI5_ROUTE_DELETEFRR_ZAPI5_ROUTE_NOTIFY_OWNERFRR_ZAPI5_IPV4_ROUTE_ADDFRR_ZAPI5_IPV4_ROUTE_DELETEFRR_ZAPI5_IPV6_ROUTE_ADDFRR_ZAPI5_IPV6_ROUTE_DELETEFRR_ZAPI5_REDISTRIBUTE_ADDFRR_ZAPI5_REDISTRIBUTE_DELETEFRR_ZAPI5_REDISTRIBUTE_DEFAULT_ADDFRR_ZAPI5_REDISTRIBUTE_DEFAULT_DELETEFRR_ZAPI5_ROUTER_ID_ADDFRR_ZAPI5_ROUTER_ID_DELETEFRR_ZAPI5_ROUTER_ID_UPDATEFRR_ZAPI5_HELLOFRR_ZAPI5_CAPABILITIESFRR_ZAPI5_NEXTHOP_REGISTERFRR_ZAPI5_NEXTHOP_UNREGISTERFRR_ZAPI5_NEXTHOP_UPDATEFRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADDFRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETEFRR_ZAPI5_INTERFACE_BFD_DEST_UPDATEFRR_ZAPI5_IMPORT_ROUTE_REGISTERFRR_ZAPI5_IMPORT_ROUTE_UNREGISTERFRR_ZAPI5_IMPORT_CHECK_UPDATEFRR_ZAPI5_IPV4_ROUTE_IPV6_NEXTHOP_ADDFRR_ZAPI5_BFD_DEST_REGISTERFRR_ZAPI5_BFD_DEST_DEREGISTERFRR_ZAPI5_BFD_DEST_UPDATEFRR_ZAPI5_BFD_DEST_REPLAYFRR_ZAPI5_REDISTRIBUTE_ROUTE_ADDFRR_ZAPI5_REDISTRIBUTE_ROUTE_DELFRR_ZAPI5_VRF_UNREGISTERFRR_ZAPI5_VRF_ADDFRR_ZAPI5_VRF_DELETEFRR_ZAPI5_VRF_LABELFRR_ZAPI5_INTERFACE_VRF_UPDATEFRR_ZAPI5_BFD_CLIENT_REGISTERFRR_ZAPI5_INTERFACE_ENABLE_RADVFRR_ZAPI5_INTERFACE_DISABLE_RADVFRR_ZAPI5_IPV4_NEXTHOP_LOOKUP_MRIBFRR_ZAPI5_INTERFACE_LINK_PARAMSFRR_ZAPI5_MPLS_LABELS_ADDFRR_ZAPI5_MPLS_LABELS_DELETEFRR_ZAPI5_IPMR_ROUTE_STATSFRR_ZAPI5_LABEL_MANAGER_CONNECTFRR_ZAPI5_GET_LABEL_CHUNKFRR_ZAPI5_RELEASE_LABEL_CHUNKFRR_ZAPI5_FEC_REGISTERFRR_ZAPI5_FEC_UNREGISTERFRR_ZAPI5_FEC_UPDATEFRR_ZAPI5_ADVERTISE_DEFAULT_GWFRR_ZAPI5_ADVERTISE_SUBNETFRR_ZAPI5_ADVERTISE_ALL_VNIFRR_ZAPI5_VNI_ADDFRR_ZAPI5_VNI_DELFRR_ZAPI5_L3VNI_ADDFRR_ZAPI5_L3VNI_DELFRR_ZAPI5_REMOTE_VTEP_ADDFRR_ZAPI5_REMOTE_VTEP_DELFRR_ZAPI5_MACIP_ADDFRR_ZAPI5_MACIP_DELFRR_ZAPI5_IP_PREFIX_ROUTE_ADDFRR_ZAPI5_IP_PREFIX_ROUTE_DELFRR_ZAPI5_REMOTE_MACIP_ADDFRR_ZAPI5_REMOTE_MACIP_DELFRR_ZAPI5_PW_ADDFRR_ZAPI5_PW_DELETEFRR_ZAPI5_PW_SETFRR_ZAPI5_PW_UNSETFRR_ZAPI5_PW_STATUS_UPDATEFRR_ZAPI5_RULE_ADDFRR_ZAPI5_RULE_DELETEFRR_ZAPI5_RULE_NOTIFY_OWNERFRR_ZAPI5_TABLE_MANAGER_CONNECTFRR_ZAPI5_GET_TABLE_CHUNKFRR_ZAPI5_RELEASE_TABLE_CHUNKFRR_ZAPI5_IPSET_CREATEFRR_ZAPI5_IPSET_DESTROYFRR_ZAPI5_IPSET_ENTRY_ADDFRR_ZAPI5_IPSET_ENTRY_DELETEFRR_ZAPI5_IPSET_NOTIFY_OWNERFRR_ZAPI5_IPSET_ENTRY_NOTIFY_OWNERFRR_ZAPI5_IPTABLE_ADDFRR_ZAPI5_IPTABLE_DELETEFRR_ZAPI5_IPTABLE_NOTIFY_OWNER" + +var _API_TYPE_index = [...]uint16{0, 23, 49, 80, 114, 136, 160, 190, 209, 231, 259, 283, 310, 334, 361, 387, 416, 450, 487, 510, 536, 562, 577, 599, 625, 653, 677, 712, 750, 785, 816, 849, 878, 915, 942, 971, 996, 1021, 1053, 1085, 1109, 1126, 1146, 1165, 1195, 1224, 1255, 1287, 1321, 1352, 1377, 1405, 1431, 1462, 1487, 1516, 1538, 1562, 1582, 1612, 1638, 1665, 1682, 1699, 1718, 1737, 1762, 1787, 1806, 1825, 1854, 1883, 1909, 1935, 1951, 1970, 1986, 2004, 2030, 2048, 2069, 2096, 2127, 2152, 2181, 2203, 2226, 2251, 2279, 2307, 2341, 2362, 2386, 2416} + +func (i API_TYPE) String() string { + if i >= API_TYPE(len(_API_TYPE_index)-1) { + return "API_TYPE(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _API_TYPE_name[_API_TYPE_index[i]:_API_TYPE_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/link_type_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/link_type_string.go new file mode 100644 index 0000000..f04a24c --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/link_type_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=LINK_TYPE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _LINK_TYPE_name = "LINK_TYPE_UNKNOWNLINK_TYPE_ETHERLINK_TYPE_EETHERLINK_TYPE_AX25LINK_TYPE_PRONETLINK_TYPE_IEEE802LINK_TYPE_ARCNETLINK_TYPE_APPLETLKLINK_TYPE_DLCILINK_TYPE_ATMLINK_TYPE_METRICOMLINK_TYPE_IEEE1394LINK_TYPE_EUI64LINK_TYPE_INFINIBANDLINK_TYPE_SLIPLINK_TYPE_CSLIPLINK_TYPE_SLIP6LINK_TYPE_CSLIP6LINK_TYPE_RSRVDLINK_TYPE_ADAPTLINK_TYPE_ROSELINK_TYPE_X25LINK_TYPE_PPPLINK_TYPE_CHDLCLINK_TYPE_LAPBLINK_TYPE_RAWHDLCLINK_TYPE_IPIPLINK_TYPE_IPIP6LINK_TYPE_FRADLINK_TYPE_SKIPLINK_TYPE_LOOPBACKLINK_TYPE_LOCALTLKLINK_TYPE_FDDILINK_TYPE_SITLINK_TYPE_IPDDPLINK_TYPE_IPGRELINK_TYPE_IP6GRELINK_TYPE_PIMREGLINK_TYPE_HIPPILINK_TYPE_ECONETLINK_TYPE_IRDALINK_TYPE_FCPPLINK_TYPE_FCALLINK_TYPE_FCPLLINK_TYPE_FCFABRICLINK_TYPE_IEEE802_TRLINK_TYPE_IEEE80211LINK_TYPE_IEEE80211_RADIOTAPLINK_TYPE_IEEE802154LINK_TYPE_IEEE802154_PHY" + +var _LINK_TYPE_index = [...]uint16{0, 17, 32, 48, 62, 78, 95, 111, 129, 143, 156, 174, 192, 207, 227, 241, 256, 271, 287, 302, 317, 331, 344, 357, 372, 386, 403, 417, 432, 446, 460, 478, 496, 510, 523, 538, 553, 569, 585, 600, 616, 630, 644, 658, 672, 690, 710, 729, 757, 777, 801} + +func (i LINK_TYPE) String() string { + if i >= LINK_TYPE(len(_LINK_TYPE_index)-1) { + return "LINK_TYPE(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _LINK_TYPE_name[_LINK_TYPE_index[i]:_LINK_TYPE_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_flag_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_flag_string.go new file mode 100644 index 0000000..1ec9d6a --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_flag_string.go @@ -0,0 +1,41 @@ +// Code generated by "stringer -type=NEXTHOP_FLAG"; DO NOT EDIT. + +package zebra + +import "strconv" + +const ( + _NEXTHOP_FLAG_name_0 = "NEXTHOP_FLAG_ACTIVENEXTHOP_FLAG_FIB" + _NEXTHOP_FLAG_name_1 = "NEXTHOP_FLAG_RECURSIVE" + _NEXTHOP_FLAG_name_2 = "NEXTHOP_FLAG_ONLINK" + _NEXTHOP_FLAG_name_3 = "NEXTHOP_FLAG_MATCHED" + _NEXTHOP_FLAG_name_4 = "NEXTHOP_FLAG_FILTERED" + _NEXTHOP_FLAG_name_5 = "NEXTHOP_FLAG_DUPLICATE" + _NEXTHOP_FLAG_name_6 = "NEXTHOP_FLAG_EVPN_RVTEP" +) + +var ( + _NEXTHOP_FLAG_index_0 = [...]uint8{0, 19, 35} +) + +func (i NEXTHOP_FLAG) String() string { + switch { + case 1 <= i && i <= 2: + i -= 1 + return _NEXTHOP_FLAG_name_0[_NEXTHOP_FLAG_index_0[i]:_NEXTHOP_FLAG_index_0[i+1]] + case i == 4: + return _NEXTHOP_FLAG_name_1 + case i == 8: + return _NEXTHOP_FLAG_name_2 + case i == 16: + return _NEXTHOP_FLAG_name_3 + case i == 32: + return _NEXTHOP_FLAG_name_4 + case i == 64: + return _NEXTHOP_FLAG_name_5 + case i == 128: + return _NEXTHOP_FLAG_name_6 + default: + return "NEXTHOP_FLAG(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_type_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_type_string.go new file mode 100644 index 0000000..adfc73f --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/nexthop_type_string.go @@ -0,0 +1,17 @@ +// Code generated by "stringer -type=NEXTHOP_TYPE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _NEXTHOP_TYPE_name = "FRR_NEXTHOP_TYPE_IFINDEXFRR_NEXTHOP_TYPE_IPV4FRR_NEXTHOP_TYPE_IPV4_IFINDEXFRR_NEXTHOP_TYPE_IPV6FRR_NEXTHOP_TYPE_IPV6_IFINDEXFRR_NEXTHOP_TYPE_BLACKHOLENEXTHOP_TYPE_IPV6_IFINDEXNEXTHOP_TYPE_IPV6_IFNAMENEXTHOP_TYPE_BLACKHOLE" + +var _NEXTHOP_TYPE_index = [...]uint8{0, 24, 45, 74, 95, 124, 150, 175, 199, 221} + +func (i NEXTHOP_TYPE) String() string { + i -= 1 + if i >= NEXTHOP_TYPE(len(_NEXTHOP_TYPE_index)-1) { + return "NEXTHOP_TYPE(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _NEXTHOP_TYPE_name[_NEXTHOP_TYPE_index[i]:_NEXTHOP_TYPE_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_enable_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_enable_string.go new file mode 100644 index 0000000..e9d829f --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_enable_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=PTM_ENABLE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _PTM_ENABLE_name = "PTM_ENABLE_OFFPTM_ENABLE_ONPTM_ENABLE_UNSPEC" + +var _PTM_ENABLE_index = [...]uint8{0, 14, 27, 44} + +func (i PTM_ENABLE) String() string { + if i >= PTM_ENABLE(len(_PTM_ENABLE_index)-1) { + return "PTM_ENABLE(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _PTM_ENABLE_name[_PTM_ENABLE_index[i]:_PTM_ENABLE_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_status_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_status_string.go new file mode 100644 index 0000000..031e7c0 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/ptm_status_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=PTM_STATUS"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _PTM_STATUS_name = "PTM_STATUS_DOWNPTM_STATUS_UPPTM_STATUS_UNKNOWN" + +var _PTM_STATUS_index = [...]uint8{0, 15, 28, 46} + +func (i PTM_STATUS) String() string { + if i >= PTM_STATUS(len(_PTM_STATUS_index)-1) { + return "PTM_STATUS(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _PTM_STATUS_name[_PTM_STATUS_index[i]:_PTM_STATUS_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/route_type_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/route_type_string.go new file mode 100644 index 0000000..47d6f27 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/route_type_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=ROUTE_TYPE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _ROUTE_TYPE_name = "FRR_ZAPI5_ROUTE_SYSTEMFRR_ZAPI5_ROUTE_KERNELFRR_ZAPI5_ROUTE_CONNECTFRR_ZAPI5_ROUTE_STATICFRR_ZAPI5_ROUTE_RIPFRR_ZAPI5_ROUTE_RIPNGFRR_ZAPI5_ROUTE_OSPFFRR_ZAPI5_ROUTE_OSPF6FRR_ZAPI5_ROUTE_ISISFRR_ZAPI5_ROUTE_BGPFRR_ZAPI5_ROUTE_PIMFRR_ZAPI5_ROUTE_EIGRPFRR_ZAPI5_ROUTE_NHRPFRR_ZAPI5_ROUTE_HSLSFRR_ZAPI5_ROUTE_OLSRFRR_ZAPI5_ROUTE_TABLEFRR_ZAPI5_ROUTE_LDPFRR_ZAPI5_ROUTE_VNCFRR_ZAPI5_ROUTE_VNC_DIRECTFRR_ZAPI5_ROUTE_VNC_DIRECT_RHFRR_ZAPI5_ROUTE_BGP_DIRECTFRR_ZAPI5_ROUTE_BGP_DIRECT_EXTFRR_ZAPI5_ROUTE_BABELFRR_ZAPI5_ROUTE_SHARPFRR_ZAPI5_ROUTE_PBRFRR_ZAPI5_ROUTE_ALLFRR_ZAPI5_ROUTE_MAX" + +var _ROUTE_TYPE_index = [...]uint16{0, 22, 44, 67, 89, 108, 129, 149, 170, 190, 209, 228, 249, 269, 289, 309, 330, 349, 368, 394, 423, 449, 479, 500, 521, 540, 559, 578} + +func (i ROUTE_TYPE) String() string { + if i >= ROUTE_TYPE(len(_ROUTE_TYPE_index)-1) { + return "ROUTE_TYPE(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ROUTE_TYPE_name[_ROUTE_TYPE_index[i]:_ROUTE_TYPE_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/safi_string.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/safi_string.go new file mode 100644 index 0000000..8199a0e --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/safi_string.go @@ -0,0 +1,17 @@ +// Code generated by "stringer -type=SAFI"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _SAFI_name = "SAFI_UNICASTSAFI_MULTICASTSAFI_RESERVED_3SAFI_MPLS_VPNSAFI_MAX" + +var _SAFI_index = [...]uint8{0, 12, 26, 41, 54, 62} + +func (i SAFI) String() string { + i -= 1 + if i >= SAFI(len(_SAFI_index)-1) { + return "SAFI(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _SAFI_name[_SAFI_index[i]:_SAFI_index[i+1]] +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi.go new file mode 100644 index 0000000..9d658f1 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi.go @@ -0,0 +1,2534 @@ +// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 zebra + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "net" + "strings" + "syscall" + + log "github.com/sirupsen/logrus" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +const ( + HEADER_MARKER = 255 + FRR_HEADER_MARKER = 254 + INTERFACE_NAMSIZ = 20 +) + +// Internal Interface Status. +type INTERFACE_STATUS uint8 + +const ( + INTERFACE_ACTIVE INTERFACE_STATUS = 0x01 + INTERFACE_SUB INTERFACE_STATUS = 0x02 + INTERFACE_LINKDETECTION INTERFACE_STATUS = 0x04 + INTERFACE_VRF_LOOPBACK INTERFACE_STATUS = 0x08 +) + +// Interface Link Layer Types. +//go:generate stringer -type=LINK_TYPE +type LINK_TYPE uint32 + +const ( + LINK_TYPE_UNKNOWN LINK_TYPE = iota + LINK_TYPE_ETHER + LINK_TYPE_EETHER + LINK_TYPE_AX25 + LINK_TYPE_PRONET + LINK_TYPE_IEEE802 + LINK_TYPE_ARCNET + LINK_TYPE_APPLETLK + LINK_TYPE_DLCI + LINK_TYPE_ATM + LINK_TYPE_METRICOM + LINK_TYPE_IEEE1394 + LINK_TYPE_EUI64 + LINK_TYPE_INFINIBAND + LINK_TYPE_SLIP + LINK_TYPE_CSLIP + LINK_TYPE_SLIP6 + LINK_TYPE_CSLIP6 + LINK_TYPE_RSRVD + LINK_TYPE_ADAPT + LINK_TYPE_ROSE + LINK_TYPE_X25 + LINK_TYPE_PPP + LINK_TYPE_CHDLC + LINK_TYPE_LAPB + LINK_TYPE_RAWHDLC + LINK_TYPE_IPIP + LINK_TYPE_IPIP6 + LINK_TYPE_FRAD + LINK_TYPE_SKIP + LINK_TYPE_LOOPBACK + LINK_TYPE_LOCALTLK + LINK_TYPE_FDDI + LINK_TYPE_SIT + LINK_TYPE_IPDDP + LINK_TYPE_IPGRE + LINK_TYPE_IP6GRE + LINK_TYPE_PIMREG + LINK_TYPE_HIPPI + LINK_TYPE_ECONET + LINK_TYPE_IRDA + LINK_TYPE_FCPP + LINK_TYPE_FCAL + LINK_TYPE_FCPL + LINK_TYPE_FCFABRIC + LINK_TYPE_IEEE802_TR + LINK_TYPE_IEEE80211 + LINK_TYPE_IEEE80211_RADIOTAP + LINK_TYPE_IEEE802154 + LINK_TYPE_IEEE802154_PHY +) + +const VRF_DEFAULT = 0 +const MAXPATH_NUM = 64 +const MPLS_MAX_LABLE = 16 + +func HeaderSize(version uint8) uint16 { + switch version { + case 3, 4: + return 8 + case 5: + return 10 + default: + return 6 + } +} + +func (t INTERFACE_STATUS) String() string { + ss := make([]string, 0, 3) + if t&INTERFACE_ACTIVE > 0 { + ss = append(ss, "ACTIVE") + } + if t&INTERFACE_SUB > 0 { + ss = append(ss, "SUB") + } + if t&INTERFACE_LINKDETECTION > 0 { + ss = append(ss, "LINKDETECTION") + } + if t&INTERFACE_VRF_LOOPBACK > 0 { + ss = append(ss, "VRF_LOOPBACK") + } + return strings.Join(ss, "|") +} + +// Interface Connected Address Flags +type INTERFACE_ADDRESS_FLAG uint8 + +const ( + INTERFACE_ADDRESS_SECONDARY INTERFACE_ADDRESS_FLAG = 0x01 + INTERFACE_ADDRESS_PEER INTERFACE_ADDRESS_FLAG = 0x02 + INTERFACE_ADDRESS_UNNUMBERED INTERFACE_ADDRESS_FLAG = 0x04 +) + +func (t INTERFACE_ADDRESS_FLAG) String() string { + ss := make([]string, 0, 3) + if t&INTERFACE_ADDRESS_SECONDARY > 0 { + ss = append(ss, "SECONDARY") + } + if t&INTERFACE_ADDRESS_PEER > 0 { + ss = append(ss, "PEER") + } + if t&INTERFACE_ADDRESS_UNNUMBERED > 0 { + ss = append(ss, "UNNUMBERED") + } + return strings.Join(ss, "|") +} + +// Address Family Identifier. +//go:generate stringer -type=AFI +type AFI uint8 + +const ( + AFI_IP AFI = 1 + AFI_IP6 AFI = 2 + AFI_ETHER AFI = 3 + AFI_MAX AFI = 4 +) + +// Subsequent Address Family Identifier. +//go:generate stringer -type=SAFI +type SAFI uint8 + +const ( + _ SAFI = iota + SAFI_UNICAST + SAFI_MULTICAST + SAFI_RESERVED_3 + SAFI_MPLS_VPN + SAFI_MAX +) + +// API Types. +//go:generate stringer -type=API_TYPE +type API_TYPE uint16 + +// For FRRouting version 4 and 5. (ZAPI version 5) +const ( + FRR_ZAPI5_INTERFACE_ADD API_TYPE = iota + FRR_ZAPI5_INTERFACE_DELETE + FRR_ZAPI5_INTERFACE_ADDRESS_ADD + FRR_ZAPI5_INTERFACE_ADDRESS_DELETE + FRR_ZAPI5_INTERFACE_UP + FRR_ZAPI5_INTERFACE_DOWN + FRR_ZAPI5_INTERFACE_SET_MASTER + FRR_ZAPI5_ROUTE_ADD + FRR_ZAPI5_ROUTE_DELETE + FRR_ZAPI5_ROUTE_NOTIFY_OWNER + FRR_ZAPI5_IPV4_ROUTE_ADD + FRR_ZAPI5_IPV4_ROUTE_DELETE + FRR_ZAPI5_IPV6_ROUTE_ADD + FRR_ZAPI5_IPV6_ROUTE_DELETE + FRR_ZAPI5_REDISTRIBUTE_ADD + FRR_ZAPI5_REDISTRIBUTE_DELETE + FRR_ZAPI5_REDISTRIBUTE_DEFAULT_ADD + FRR_ZAPI5_REDISTRIBUTE_DEFAULT_DELETE + FRR_ZAPI5_ROUTER_ID_ADD + FRR_ZAPI5_ROUTER_ID_DELETE + FRR_ZAPI5_ROUTER_ID_UPDATE + FRR_ZAPI5_HELLO + FRR_ZAPI5_CAPABILITIES + FRR_ZAPI5_NEXTHOP_REGISTER + FRR_ZAPI5_NEXTHOP_UNREGISTER + FRR_ZAPI5_NEXTHOP_UPDATE + FRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADD + FRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETE + FRR_ZAPI5_INTERFACE_BFD_DEST_UPDATE + FRR_ZAPI5_IMPORT_ROUTE_REGISTER + FRR_ZAPI5_IMPORT_ROUTE_UNREGISTER + FRR_ZAPI5_IMPORT_CHECK_UPDATE + FRR_ZAPI5_IPV4_ROUTE_IPV6_NEXTHOP_ADD + FRR_ZAPI5_BFD_DEST_REGISTER + FRR_ZAPI5_BFD_DEST_DEREGISTER + FRR_ZAPI5_BFD_DEST_UPDATE + FRR_ZAPI5_BFD_DEST_REPLAY + FRR_ZAPI5_REDISTRIBUTE_ROUTE_ADD + FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL + FRR_ZAPI5_VRF_UNREGISTER + FRR_ZAPI5_VRF_ADD + FRR_ZAPI5_VRF_DELETE + FRR_ZAPI5_VRF_LABEL + FRR_ZAPI5_INTERFACE_VRF_UPDATE + FRR_ZAPI5_BFD_CLIENT_REGISTER + FRR_ZAPI5_INTERFACE_ENABLE_RADV + FRR_ZAPI5_INTERFACE_DISABLE_RADV + FRR_ZAPI5_IPV4_NEXTHOP_LOOKUP_MRIB + FRR_ZAPI5_INTERFACE_LINK_PARAMS + FRR_ZAPI5_MPLS_LABELS_ADD + FRR_ZAPI5_MPLS_LABELS_DELETE + FRR_ZAPI5_IPMR_ROUTE_STATS + FRR_ZAPI5_LABEL_MANAGER_CONNECT + FRR_ZAPI5_GET_LABEL_CHUNK + FRR_ZAPI5_RELEASE_LABEL_CHUNK + FRR_ZAPI5_FEC_REGISTER + FRR_ZAPI5_FEC_UNREGISTER + FRR_ZAPI5_FEC_UPDATE + FRR_ZAPI5_ADVERTISE_DEFAULT_GW + FRR_ZAPI5_ADVERTISE_SUBNET + FRR_ZAPI5_ADVERTISE_ALL_VNI + FRR_ZAPI5_VNI_ADD + FRR_ZAPI5_VNI_DEL + FRR_ZAPI5_L3VNI_ADD + FRR_ZAPI5_L3VNI_DEL + FRR_ZAPI5_REMOTE_VTEP_ADD + FRR_ZAPI5_REMOTE_VTEP_DEL + FRR_ZAPI5_MACIP_ADD + FRR_ZAPI5_MACIP_DEL + FRR_ZAPI5_IP_PREFIX_ROUTE_ADD + FRR_ZAPI5_IP_PREFIX_ROUTE_DEL + FRR_ZAPI5_REMOTE_MACIP_ADD + FRR_ZAPI5_REMOTE_MACIP_DEL + FRR_ZAPI5_PW_ADD + FRR_ZAPI5_PW_DELETE + FRR_ZAPI5_PW_SET + FRR_ZAPI5_PW_UNSET + FRR_ZAPI5_PW_STATUS_UPDATE + FRR_ZAPI5_RULE_ADD + FRR_ZAPI5_RULE_DELETE + FRR_ZAPI5_RULE_NOTIFY_OWNER + FRR_ZAPI5_TABLE_MANAGER_CONNECT + FRR_ZAPI5_GET_TABLE_CHUNK + FRR_ZAPI5_RELEASE_TABLE_CHUNK + FRR_ZAPI5_IPSET_CREATE + FRR_ZAPI5_IPSET_DESTROY + FRR_ZAPI5_IPSET_ENTRY_ADD + FRR_ZAPI5_IPSET_ENTRY_DELETE + FRR_ZAPI5_IPSET_NOTIFY_OWNER + FRR_ZAPI5_IPSET_ENTRY_NOTIFY_OWNER + FRR_ZAPI5_IPTABLE_ADD + FRR_ZAPI5_IPTABLE_DELETE + FRR_ZAPI5_IPTABLE_NOTIFY_OWNER +) + +// For FRRouting. +const ( + FRR_INTERFACE_ADD API_TYPE = iota + FRR_INTERFACE_DELETE + FRR_INTERFACE_ADDRESS_ADD + FRR_INTERFACE_ADDRESS_DELETE + FRR_INTERFACE_UP + FRR_INTERFACE_DOWN + FRR_IPV4_ROUTE_ADD + FRR_IPV4_ROUTE_DELETE + FRR_IPV6_ROUTE_ADD + FRR_IPV6_ROUTE_DELETE + FRR_REDISTRIBUTE_ADD + FRR_REDISTRIBUTE_DELETE + FRR_REDISTRIBUTE_DEFAULT_ADD + FRR_REDISTRIBUTE_DEFAULT_DELETE + FRR_ROUTER_ID_ADD + FRR_ROUTER_ID_DELETE + FRR_ROUTER_ID_UPDATE + FRR_HELLO + FRR_NEXTHOP_REGISTER + FRR_NEXTHOP_UNREGISTER + FRR_NEXTHOP_UPDATE + FRR_INTERFACE_NBR_ADDRESS_ADD + FRR_INTERFACE_NBR_ADDRESS_DELETE + FRR_INTERFACE_BFD_DEST_UPDATE + FRR_IMPORT_ROUTE_REGISTER + FRR_IMPORT_ROUTE_UNREGISTER + FRR_IMPORT_CHECK_UPDATE + FRR_IPV4_ROUTE_IPV6_NEXTHOP_ADD + FRR_BFD_DEST_REGISTER + FRR_BFD_DEST_DEREGISTER + FRR_BFD_DEST_UPDATE + FRR_BFD_DEST_REPLAY + FRR_REDISTRIBUTE_IPV4_ADD + FRR_REDISTRIBUTE_IPV4_DEL + FRR_REDISTRIBUTE_IPV6_ADD + FRR_REDISTRIBUTE_IPV6_DEL + FRR_VRF_UNREGISTER + FRR_VRF_ADD + FRR_VRF_DELETE + FRR_INTERFACE_VRF_UPDATE + FRR_BFD_CLIENT_REGISTER + FRR_INTERFACE_ENABLE_RADV + FRR_INTERFACE_DISABLE_RADV + FRR_IPV4_NEXTHOP_LOOKUP_MRIB + FRR_INTERFACE_LINK_PARAMS + FRR_MPLS_LABELS_ADD + FRR_MPLS_LABELS_DELETE + FRR_IPV4_NEXTHOP_ADD + FRR_IPV4_NEXTHOP_DELETE + FRR_IPV6_NEXTHOP_ADD + FRR_IPV6_NEXTHOP_DELETE + FRR_IPMR_ROUTE_STATS + FRR_LABEL_MANAGER_CONNECT + FRR_GET_LABEL_CHUNK + FRR_RELEASE_LABEL_CHUNK + FRR_PW_ADD + FRR_PW_DELETE + FRR_PW_SET + FRR_PW_UNSET + FRR_PW_STATUS_UPDATE +) + +// For Quagga. +const ( + _ API_TYPE = iota + INTERFACE_ADD + INTERFACE_DELETE + INTERFACE_ADDRESS_ADD + INTERFACE_ADDRESS_DELETE + INTERFACE_UP + INTERFACE_DOWN + IPV4_ROUTE_ADD + IPV4_ROUTE_DELETE + IPV6_ROUTE_ADD + IPV6_ROUTE_DELETE + REDISTRIBUTE_ADD + REDISTRIBUTE_DELETE + REDISTRIBUTE_DEFAULT_ADD + REDISTRIBUTE_DEFAULT_DELETE + IPV4_NEXTHOP_LOOKUP + IPV6_NEXTHOP_LOOKUP + IPV4_IMPORT_LOOKUP + IPV6_IMPORT_LOOKUP + INTERFACE_RENAME + ROUTER_ID_ADD + ROUTER_ID_DELETE + ROUTER_ID_UPDATE + HELLO + IPV4_NEXTHOP_LOOKUP_MRIB + VRF_UNREGISTER + INTERFACE_LINK_PARAMS + NEXTHOP_REGISTER + NEXTHOP_UNREGISTER + NEXTHOP_UPDATE + MESSAGE_MAX +) + +// Route Types. +//go:generate stringer -type=ROUTE_TYPE +type ROUTE_TYPE uint8 + +// For FRRouting version 4 and 5 (ZAPI version 5). +const ( + FRR_ZAPI5_ROUTE_SYSTEM ROUTE_TYPE = iota + FRR_ZAPI5_ROUTE_KERNEL + FRR_ZAPI5_ROUTE_CONNECT + FRR_ZAPI5_ROUTE_STATIC + FRR_ZAPI5_ROUTE_RIP + FRR_ZAPI5_ROUTE_RIPNG + FRR_ZAPI5_ROUTE_OSPF + FRR_ZAPI5_ROUTE_OSPF6 + FRR_ZAPI5_ROUTE_ISIS + FRR_ZAPI5_ROUTE_BGP + FRR_ZAPI5_ROUTE_PIM + FRR_ZAPI5_ROUTE_EIGRP + FRR_ZAPI5_ROUTE_NHRP + FRR_ZAPI5_ROUTE_HSLS + FRR_ZAPI5_ROUTE_OLSR + FRR_ZAPI5_ROUTE_TABLE + FRR_ZAPI5_ROUTE_LDP + FRR_ZAPI5_ROUTE_VNC + FRR_ZAPI5_ROUTE_VNC_DIRECT + FRR_ZAPI5_ROUTE_VNC_DIRECT_RH + FRR_ZAPI5_ROUTE_BGP_DIRECT + FRR_ZAPI5_ROUTE_BGP_DIRECT_EXT + FRR_ZAPI5_ROUTE_BABEL + FRR_ZAPI5_ROUTE_SHARP + FRR_ZAPI5_ROUTE_PBR + FRR_ZAPI5_ROUTE_ALL + FRR_ZAPI5_ROUTE_MAX +) + +// For FRRouting. +const ( + FRR_ROUTE_SYSTEM ROUTE_TYPE = iota + FRR_ROUTE_KERNEL + FRR_ROUTE_CONNECT + FRR_ROUTE_STATIC + FRR_ROUTE_RIP + FRR_ROUTE_RIPNG + FRR_ROUTE_OSPF + FRR_ROUTE_OSPF6 + FRR_ROUTE_ISIS + FRR_ROUTE_BGP + FRR_ROUTE_PIM + FRR_ROUTE_HSLS + FRR_ROUTE_OLSR + FRR_ROUTE_TABLE + FRR_ROUTE_LDP + FRR_ROUTE_VNC + FRR_ROUTE_VNC_DIRECT + FRR_ROUTE_VNC_DIRECT_RH + FRR_ROUTE_BGP_DIRECT + FRR_ROUTE_BGP_DIRECT_EXT + FRR_ROUTE_ALL + FRR_ROUTE_MAX +) + +// For Quagga. +const ( + ROUTE_SYSTEM ROUTE_TYPE = iota + ROUTE_KERNEL + ROUTE_CONNECT + ROUTE_STATIC + ROUTE_RIP + ROUTE_RIPNG + ROUTE_OSPF + ROUTE_OSPF6 + ROUTE_ISIS + ROUTE_BGP + ROUTE_PIM + ROUTE_HSLS + ROUTE_OLSR + ROUTE_BABEL + ROUTE_MAX +) + +var routeTypeValueMapFrrZapi5 = map[string]ROUTE_TYPE{ + "system": FRR_ZAPI5_ROUTE_SYSTEM, + "kernel": FRR_ZAPI5_ROUTE_KERNEL, + "connect": FRR_ZAPI5_ROUTE_CONNECT, // hack for backward compatibility + "directly-connected": FRR_ZAPI5_ROUTE_CONNECT, + "static": FRR_ZAPI5_ROUTE_STATIC, + "rip": FRR_ZAPI5_ROUTE_RIP, + "ripng": FRR_ZAPI5_ROUTE_RIPNG, + "ospf": FRR_ZAPI5_ROUTE_OSPF, + "ospf3": FRR_ZAPI5_ROUTE_OSPF6, + "isis": FRR_ZAPI5_ROUTE_ISIS, + "bgp": FRR_ZAPI5_ROUTE_BGP, + "pim": FRR_ZAPI5_ROUTE_PIM, + "eigrp": FRR_ZAPI5_ROUTE_EIGRP, + "nhrp": FRR_ZAPI5_ROUTE_EIGRP, + "hsls": FRR_ZAPI5_ROUTE_HSLS, + "olsr": FRR_ZAPI5_ROUTE_OLSR, + "table": FRR_ZAPI5_ROUTE_TABLE, + "ldp": FRR_ZAPI5_ROUTE_LDP, + "vnc": FRR_ZAPI5_ROUTE_VNC, + "vnc-direct": FRR_ZAPI5_ROUTE_VNC_DIRECT, + "vnc-direct-rh": FRR_ZAPI5_ROUTE_VNC_DIRECT_RH, + "bgp-direct": FRR_ZAPI5_ROUTE_BGP_DIRECT, + "bgp-direct-ext": FRR_ZAPI5_ROUTE_BGP_DIRECT_EXT, + "babel": FRR_ZAPI5_ROUTE_BABEL, + "sharp": FRR_ZAPI5_ROUTE_SHARP, + "pbr": FRR_ZAPI5_ROUTE_PBR, + "all": FRR_ZAPI5_ROUTE_ALL, +} + +var routeTypeValueMapFrr = map[string]ROUTE_TYPE{ + "system": FRR_ROUTE_SYSTEM, + "kernel": FRR_ROUTE_KERNEL, + "connect": FRR_ROUTE_CONNECT, // hack for backward compatibility + "directly-connected": FRR_ROUTE_CONNECT, + "static": FRR_ROUTE_STATIC, + "rip": FRR_ROUTE_RIP, + "ripng": FRR_ROUTE_RIPNG, + "ospf": FRR_ROUTE_OSPF, + "ospf3": FRR_ROUTE_OSPF6, + "isis": FRR_ROUTE_ISIS, + "bgp": FRR_ROUTE_BGP, + "pim": FRR_ROUTE_PIM, + "hsls": FRR_ROUTE_HSLS, + "olsr": FRR_ROUTE_OLSR, + "table": FRR_ROUTE_TABLE, + "ldp": FRR_ROUTE_LDP, + "vnc": FRR_ROUTE_VNC, + "vnc-direct": FRR_ROUTE_VNC_DIRECT, + "vnc-direct-rh": FRR_ROUTE_VNC_DIRECT_RH, + "bgp-direct": FRR_ROUTE_BGP_DIRECT, + "bgp-direct-ext": FRR_ROUTE_BGP_DIRECT_EXT, + "all": FRR_ROUTE_ALL, +} + +var routeTypeValueMap = map[string]ROUTE_TYPE{ + "system": ROUTE_SYSTEM, + "kernel": ROUTE_KERNEL, + "connect": ROUTE_CONNECT, // hack for backward compatibility + "directly-connected": ROUTE_CONNECT, + "static": ROUTE_STATIC, + "rip": ROUTE_RIP, + "ripng": ROUTE_RIPNG, + "ospf": ROUTE_OSPF, + "ospf3": ROUTE_OSPF6, + "isis": ROUTE_ISIS, + "bgp": ROUTE_BGP, + "pim": ROUTE_PIM, + "hsls": ROUTE_HSLS, + "olsr": ROUTE_OLSR, + "babel": ROUTE_BABEL, +} + +func RouteTypeFromString(typ string, version uint8) (ROUTE_TYPE, error) { + delegateRouteTypeValueMap := routeTypeValueMap + if version == 4 { + delegateRouteTypeValueMap = routeTypeValueMapFrr + } else if version >= 5 { + delegateRouteTypeValueMap = routeTypeValueMapFrrZapi5 + } + t, ok := delegateRouteTypeValueMap[typ] + if ok { + return t, nil + } + return t, fmt.Errorf("unknown route type: %s", typ) +} + +func addressFamilyFromApi(Api API_TYPE, version uint8) uint8 { + if version <= 3 { + switch Api { + case IPV4_ROUTE_ADD, IPV4_ROUTE_DELETE, IPV4_NEXTHOP_LOOKUP, IPV4_IMPORT_LOOKUP: + return syscall.AF_INET + case IPV6_ROUTE_ADD, IPV6_ROUTE_DELETE, IPV6_NEXTHOP_LOOKUP, IPV6_IMPORT_LOOKUP: + return syscall.AF_INET6 + } + } else if version == 4 { + switch Api { + case FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL, FRR_IPV4_ROUTE_ADD, FRR_IPV4_ROUTE_DELETE, FRR_IPV4_NEXTHOP_LOOKUP_MRIB: + return syscall.AF_INET + case FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL, FRR_IPV6_ROUTE_ADD, FRR_IPV6_ROUTE_DELETE: + return syscall.AF_INET6 + } + } else if version == 5 { + switch Api { + case FRR_ZAPI5_IPV4_ROUTE_ADD, FRR_ZAPI5_IPV4_ROUTE_DELETE, FRR_ZAPI5_IPV4_NEXTHOP_LOOKUP_MRIB: + return syscall.AF_INET + case FRR_ZAPI5_IPV6_ROUTE_ADD, FRR_ZAPI5_IPV6_ROUTE_DELETE: + return syscall.AF_INET6 + } + } + return syscall.AF_UNSPEC +} + +func addressByteLength(family uint8) (int, error) { + switch family { + case syscall.AF_INET: + return net.IPv4len, nil + case syscall.AF_INET6: + return net.IPv6len, nil + } + return 0, fmt.Errorf("unknown address family: %d", family) +} + +func ipFromFamily(family uint8, buf []byte) net.IP { + switch family { + case syscall.AF_INET: + return net.IP(buf).To4() + case syscall.AF_INET6: + return net.IP(buf).To16() + } + return nil +} + +// API Message Flags. +type MESSAGE_FLAG uint8 + +// For FRRouting version 4 and 5 (ZAPI version 5). +const ( + FRR_ZAPI5_MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01 + FRR_ZAPI5_MESSAGE_DISTANCE MESSAGE_FLAG = 0x02 + FRR_ZAPI5_MESSAGE_METRIC MESSAGE_FLAG = 0x04 + FRR_ZAPI5_MESSAGE_TAG MESSAGE_FLAG = 0x08 + FRR_ZAPI5_MESSAGE_MTU MESSAGE_FLAG = 0x10 + FRR_ZAPI5_MESSAGE_SRCPFX MESSAGE_FLAG = 0x20 + FRR_ZAPI5_MESSAGE_LABEL MESSAGE_FLAG = 0x40 +) + +// For FRRouting. +const ( + FRR_MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01 + FRR_MESSAGE_IFINDEX MESSAGE_FLAG = 0x02 + FRR_MESSAGE_DISTANCE MESSAGE_FLAG = 0x04 + FRR_MESSAGE_METRIC MESSAGE_FLAG = 0x08 + FRR_MESSAGE_TAG MESSAGE_FLAG = 0x10 + FRR_MESSAGE_MTU MESSAGE_FLAG = 0x20 + FRR_MESSAGE_SRCPFX MESSAGE_FLAG = 0x40 +) + +// For Quagga. +const ( + MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01 + MESSAGE_IFINDEX MESSAGE_FLAG = 0x02 + MESSAGE_DISTANCE MESSAGE_FLAG = 0x04 + MESSAGE_METRIC MESSAGE_FLAG = 0x08 + MESSAGE_MTU MESSAGE_FLAG = 0x10 + MESSAGE_TAG MESSAGE_FLAG = 0x20 +) + +func (t MESSAGE_FLAG) String(version uint8) string { + var ss []string + if (version <= 3 && t&MESSAGE_NEXTHOP > 0) || + (version == 4 && t&FRR_MESSAGE_NEXTHOP > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { + ss = append(ss, "NEXTHOP") + } + if (version <= 3 && t&MESSAGE_IFINDEX > 0) || (version == 4 && t&FRR_MESSAGE_IFINDEX > 0) { + ss = append(ss, "IFINDEX") + } + if (version <= 3 && t&MESSAGE_DISTANCE > 0) || + (version == 4 && t&FRR_MESSAGE_DISTANCE > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { + ss = append(ss, "DISTANCE") + } + if (version <= 3 && t&MESSAGE_METRIC > 0) || + (version == 4 && t&FRR_MESSAGE_METRIC > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_METRIC > 0) { + ss = append(ss, "METRIC") + } + if (version <= 3 && t&MESSAGE_MTU > 0) || (version == 4 && t&FRR_MESSAGE_MTU > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_MTU > 0) { + ss = append(ss, "MTU") + } + if (version <= 3 && t&MESSAGE_TAG > 0) || (version == 4 && t&FRR_MESSAGE_TAG > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_TAG > 0) { + ss = append(ss, "TAG") + } + if (version == 4 && t&FRR_MESSAGE_SRCPFX > 0) || + (version == 5 && t&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { + ss = append(ss, "SRCPFX") + } + if version == 5 && t&FRR_ZAPI5_MESSAGE_LABEL > 0 { + ss = append(ss, "LABLE") + } + + return strings.Join(ss, "|") +} + +// Message Flags +type FLAG uint64 + +const ( + FLAG_INTERNAL FLAG = 0x01 + FLAG_SELFROUTE FLAG = 0x02 + FLAG_BLACKHOLE FLAG = 0x04 + FLAG_IBGP FLAG = 0x08 + FLAG_SELECTED FLAG = 0x10 + FLAG_CHANGED FLAG = 0x20 + FLAG_STATIC FLAG = 0x40 + FLAG_REJECT FLAG = 0x80 + FLAG_SCOPE_LINK FLAG = 0x100 + FLAG_FIB_OVERRIDE FLAG = 0x200 + FLAG_EVPN_ROUTE FLAG = 0x400 +) + +func (t FLAG) String() string { + var ss []string + if t&FLAG_INTERNAL > 0 { + ss = append(ss, "FLAG_INTERNAL") + } + if t&FLAG_SELFROUTE > 0 { + ss = append(ss, "FLAG_SELFROUTE") + } + if t&FLAG_BLACKHOLE > 0 { + ss = append(ss, "FLAG_BLACKHOLE") + } + if t&FLAG_IBGP > 0 { + ss = append(ss, "FLAG_IBGP") + } + if t&FLAG_SELECTED > 0 { + ss = append(ss, "FLAG_SELECTED") + } + if t&FLAG_CHANGED > 0 { + ss = append(ss, "FLAG_CHANGED") + } + if t&FLAG_STATIC > 0 { + ss = append(ss, "FLAG_STATIC") + } + if t&FLAG_REJECT > 0 { + ss = append(ss, "FLAG_REJECT") + } + if t&FLAG_SCOPE_LINK > 0 { + ss = append(ss, "FLAG_SCOPE_LINK") + } + if t&FLAG_FIB_OVERRIDE > 0 { + ss = append(ss, "FLAG_FIB_OVERRIDE") + } + if t&FLAG_EVPN_ROUTE > 0 { + ss = append(ss, "FLAG_EVPN_ROUTE") + } + + return strings.Join(ss, "|") +} + +// Nexthop Types. +//go:generate stringer -type=NEXTHOP_TYPE +type NEXTHOP_TYPE uint8 + +// For FRRouting. +const ( + _ NEXTHOP_TYPE = iota + FRR_NEXTHOP_TYPE_IFINDEX + FRR_NEXTHOP_TYPE_IPV4 + FRR_NEXTHOP_TYPE_IPV4_IFINDEX + FRR_NEXTHOP_TYPE_IPV6 + FRR_NEXTHOP_TYPE_IPV6_IFINDEX + FRR_NEXTHOP_TYPE_BLACKHOLE +) + +// For Quagga. +const ( + _ NEXTHOP_TYPE = iota + NEXTHOP_TYPE_IFINDEX + NEXTHOP_TYPE_IFNAME + NEXTHOP_TYPE_IPV4 + NEXTHOP_TYPE_IPV4_IFINDEX + NEXTHOP_TYPE_IPV4_IFNAME + NEXTHOP_TYPE_IPV6 + NEXTHOP_TYPE_IPV6_IFINDEX + NEXTHOP_TYPE_IPV6_IFNAME + NEXTHOP_TYPE_BLACKHOLE +) + +// Nexthop Flags. +//go:generate stringer -type=NEXTHOP_FLAG +type NEXTHOP_FLAG uint8 + +const ( + NEXTHOP_FLAG_ACTIVE NEXTHOP_FLAG = 0x01 // This nexthop is alive. + NEXTHOP_FLAG_FIB NEXTHOP_FLAG = 0x02 // FIB nexthop. + NEXTHOP_FLAG_RECURSIVE NEXTHOP_FLAG = 0x04 // Recursive nexthop. + NEXTHOP_FLAG_ONLINK NEXTHOP_FLAG = 0x08 // Nexthop should be installed onlink. + NEXTHOP_FLAG_MATCHED NEXTHOP_FLAG = 0x10 // Already matched vs a nexthop + NEXTHOP_FLAG_FILTERED NEXTHOP_FLAG = 0x20 // rmap filtered (version >= 4) + NEXTHOP_FLAG_DUPLICATE NEXTHOP_FLAG = 0x40 // nexthop duplicates (version >= 5) + NEXTHOP_FLAG_EVPN_RVTEP NEXTHOP_FLAG = 0x80 // EVPN remote vtep nexthop (version >= 5) +) + +// Interface PTM Enable Configuration. +//go:generate stringer -type=PTM_ENABLE +type PTM_ENABLE uint8 + +const ( + PTM_ENABLE_OFF PTM_ENABLE = 0 + PTM_ENABLE_ON PTM_ENABLE = 1 + PTM_ENABLE_UNSPEC PTM_ENABLE = 2 +) + +// PTM Status. +//go:generate stringer -type=PTM_STATUS +type PTM_STATUS uint8 + +const ( + PTM_STATUS_DOWN PTM_STATUS = 0 + PTM_STATUS_UP PTM_STATUS = 1 + PTM_STATUS_UNKNOWN PTM_STATUS = 2 +) + +type Client struct { + outgoing chan *Message + incoming chan *Message + redistDefault ROUTE_TYPE + conn net.Conn + Version uint8 +} + +func NewClient(network, address string, typ ROUTE_TYPE, version uint8) (*Client, error) { + conn, err := net.Dial(network, address) + if err != nil { + return nil, err + } + outgoing := make(chan *Message) + incoming := make(chan *Message, 64) + if version < 2 { + version = 2 + } else if version > 5 { + version = 5 + } + + c := &Client{ + outgoing: outgoing, + incoming: incoming, + redistDefault: typ, + conn: conn, + Version: version, + } + + go func() { + for { + m, more := <-outgoing + if more { + b, err := m.Serialize() + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Warnf("failed to serialize: %v", m) + continue + } + + _, err = conn.Write(b) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Errorf("failed to write: %s", err) + close(outgoing) + } + } else { + log.Debug("finish outgoing loop") + return + } + } + }() + + // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version. + c.SendHello() + c.SendRouterIDAdd() + + receiveSingleMsg := func() (*Message, error) { + headerBuf, err := readAll(conn, int(HeaderSize(version))) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Error": err, + }).Error("failed to read header") + return nil, err + } + + hd := &Header{} + err = hd.DecodeFromBytes(headerBuf) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Data": headerBuf, + "Error": err, + }).Error("failed to decode header") + return nil, err + } + + bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version))) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Header": hd, + "Error": err, + }).Error("failed to read body") + return nil, err + } + + m, err := ParseMessage(hd, bodyBuf) + if err != nil { + // Just outputting warnings (not error message) and ignore this + // error considering the case that body parser is not implemented + // yet. + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Header": hd, + "Data": bodyBuf, + "Error": err, + }).Warn("failed to decode body") + return nil, nil + } + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Message": m, + }).Debug("read message from zebra") + + return m, nil + } + + // Try to receive the first message from Zebra. + if m, err := receiveSingleMsg(); err != nil { + c.Close() + // Return error explicitly in order to retry connection. + return nil, err + } else if m != nil { + incoming <- m + } + + // Start receive loop only when the first message successfully received. + go func() { + defer close(incoming) + for { + if m, err := receiveSingleMsg(); err != nil { + return + } else if m != nil { + incoming <- m + } + } + }() + + return c, nil +} + +func readAll(conn net.Conn, length int) ([]byte, error) { + buf := make([]byte, length) + _, err := io.ReadFull(conn, buf) + return buf, err +} + +func (c *Client) Receive() chan *Message { + return c.incoming +} + +func (c *Client) Send(m *Message) { + defer func() { + if err := recover(); err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Debugf("recovered: %s", err) + } + }() + log.WithFields(log.Fields{ + "Topic": "Zebra", + "Header": m.Header, + "Body": m.Body, + }).Debug("send command to zebra") + c.outgoing <- m +} + +func (c *Client) SendCommand(command API_TYPE, vrfId uint32, body Body) error { + var marker uint8 = HEADER_MARKER + if c.Version >= 4 { + marker = FRR_HEADER_MARKER + } + m := &Message{ + Header: Header{ + Len: HeaderSize(c.Version), + Marker: marker, + Version: c.Version, + VrfId: vrfId, + Command: command, + }, + Body: body, + } + c.Send(m) + return nil +} + +func (c *Client) SendHello() error { + if c.redistDefault > 0 { + command := HELLO + if c.Version == 4 { + command = FRR_HELLO + } else if c.Version >= 5 { + command = FRR_ZAPI5_HELLO + } + body := &HelloBody{ + RedistDefault: c.redistDefault, + Instance: 0, + } + return c.SendCommand(command, VRF_DEFAULT, body) + } + return nil +} + +func (c *Client) SendRouterIDAdd() error { + command := ROUTER_ID_ADD + if c.Version == 4 { + command = FRR_ROUTER_ID_ADD + } else if c.Version >= 5 { + command = FRR_ZAPI5_ROUTER_ID_ADD + } + return c.SendCommand(command, VRF_DEFAULT, nil) +} + +func (c *Client) SendInterfaceAdd() error { + command := INTERFACE_ADD + if c.Version == 4 { + command = FRR_INTERFACE_ADD + } else if c.Version >= 5 { + command = FRR_ZAPI5_INTERFACE_ADD + } + return c.SendCommand(command, VRF_DEFAULT, nil) +} + +func (c *Client) SendRedistribute(t ROUTE_TYPE, vrfId uint32) error { + command := REDISTRIBUTE_ADD + if c.redistDefault != t { + bodies := make([]*RedistributeBody, 0) + if c.Version <= 3 { + bodies = append(bodies, &RedistributeBody{ + Redist: t, + }) + } else { // version >= 4 + command = FRR_REDISTRIBUTE_ADD + if c.Version >= 5 { + command = FRR_ZAPI5_REDISTRIBUTE_ADD + } + for _, afi := range []AFI{AFI_IP, AFI_IP6} { + bodies = append(bodies, &RedistributeBody{ + Afi: afi, + Redist: t, + Instance: 0, + }) + } + } + + for _, body := range bodies { + return c.SendCommand(command, vrfId, body) + } + } + + return nil +} + +func (c *Client) SendRedistributeDelete(t ROUTE_TYPE) error { + if t < ROUTE_MAX { + command := REDISTRIBUTE_DELETE + if c.Version == 4 { + command = FRR_REDISTRIBUTE_DELETE + } else if c.Version >= 5 { + command = FRR_ZAPI5_REDISTRIBUTE_DELETE + } + body := &RedistributeBody{ + Redist: t, + } + return c.SendCommand(command, VRF_DEFAULT, body) + } else { + return fmt.Errorf("unknown route type: %d", t) + } +} + +func (c *Client) SendIPRoute(vrfId uint32, body *IPRouteBody, isWithdraw bool) error { + command := IPV4_ROUTE_ADD + if c.Version <= 3 { + if body.Prefix.Prefix.To4() != nil { + if isWithdraw { + command = IPV4_ROUTE_DELETE + } + } else { + if isWithdraw { + command = IPV6_ROUTE_DELETE + } else { + command = IPV6_ROUTE_ADD + } + } + } else if c.Version == 4 { // version >= 4 + if body.Prefix.Prefix.To4() != nil { + if isWithdraw { + command = FRR_IPV4_ROUTE_DELETE + } else { + command = FRR_IPV4_ROUTE_ADD + } + } else { + if isWithdraw { + command = FRR_IPV6_ROUTE_DELETE + } else { + command = FRR_IPV6_ROUTE_ADD + } + } + } else { // version >= 5 + if isWithdraw { + command = FRR_ZAPI5_ROUTE_DELETE + } else { + command = FRR_ZAPI5_ROUTE_ADD + } + } + return c.SendCommand(command, vrfId, body) +} + +func (c *Client) SendNexthopRegister(vrfId uint32, body *NexthopRegisterBody, isWithdraw bool) error { + // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not + // supported in Zebra protocol version<3. + if c.Version < 3 { + return fmt.Errorf("NEXTHOP_REGISTER/NEXTHOP_UNREGISTER are not supported in version: %d", c.Version) + } + command := NEXTHOP_REGISTER + if c.Version == 3 { + if isWithdraw { + command = NEXTHOP_UNREGISTER + } + } else if c.Version == 4 { // version >= 4 + if isWithdraw { + command = FRR_NEXTHOP_UNREGISTER + } else { + command = FRR_NEXTHOP_REGISTER + } + } else { // version >= 5 + if isWithdraw { + command = FRR_ZAPI5_NEXTHOP_UNREGISTER + } else { + command = FRR_ZAPI5_NEXTHOP_REGISTER + } + } + return c.SendCommand(command, vrfId, body) +} + +func (c *Client) Close() error { + close(c.outgoing) + return c.conn.Close() +} + +type Header struct { + Len uint16 + Marker uint8 + Version uint8 + VrfId uint32 // ZAPI v4: 16bits, v5: 32bits + Command API_TYPE +} + +func (h *Header) Serialize() ([]byte, error) { + buf := make([]byte, HeaderSize(h.Version)) + binary.BigEndian.PutUint16(buf[0:2], h.Len) + buf[2] = h.Marker + buf[3] = h.Version + switch h.Version { + case 2: + binary.BigEndian.PutUint16(buf[4:6], uint16(h.Command)) + case 3, 4: + binary.BigEndian.PutUint16(buf[4:6], uint16(h.VrfId)) + binary.BigEndian.PutUint16(buf[6:8], uint16(h.Command)) + case 5: + binary.BigEndian.PutUint32(buf[4:8], uint32(h.VrfId)) + binary.BigEndian.PutUint16(buf[8:10], uint16(h.Command)) + default: + return nil, fmt.Errorf("Unsupported ZAPI version: %d", h.Version) + } + return buf, nil +} + +func (h *Header) DecodeFromBytes(data []byte) error { + if uint16(len(data)) < 4 { + return fmt.Errorf("Not all ZAPI message header") + } + h.Len = binary.BigEndian.Uint16(data[0:2]) + h.Marker = data[2] + h.Version = data[3] + if uint16(len(data)) < HeaderSize(h.Version) { + return fmt.Errorf("Not all ZAPI message header") + } + switch h.Version { + case 2: + h.Command = API_TYPE(binary.BigEndian.Uint16(data[4:6])) + case 3, 4: + h.VrfId = uint32(binary.BigEndian.Uint16(data[4:6])) + h.Command = API_TYPE(binary.BigEndian.Uint16(data[6:8])) + case 5: + h.VrfId = binary.BigEndian.Uint32(data[4:8]) + h.Command = API_TYPE(binary.BigEndian.Uint16(data[8:10])) + default: + return fmt.Errorf("Unsupported ZAPI version: %d", h.Version) + } + return nil +} + +type Body interface { + DecodeFromBytes([]byte, uint8) error + Serialize(uint8) ([]byte, error) + String() string +} + +type UnknownBody struct { + Data []byte +} + +func (b *UnknownBody) DecodeFromBytes(data []byte, version uint8) error { + b.Data = data + return nil +} + +func (b *UnknownBody) Serialize(version uint8) ([]byte, error) { + return b.Data, nil +} + +func (b *UnknownBody) String() string { + return fmt.Sprintf("data: %v", b.Data) +} + +type HelloBody struct { + RedistDefault ROUTE_TYPE + Instance uint16 + ReceiveNotify uint8 +} + +// Reference: zread_hello function in zebra/zserv.c of Quagga1.2.x (ZAPI3) +// Reference: zread_hello function in zebra/zserv.c of FRR3.x (ZAPI4) +// Reference: zread_hello function in zebra/zapi_msg.c of FRR5.x (ZAPI5) +func (b *HelloBody) DecodeFromBytes(data []byte, version uint8) error { + b.RedistDefault = ROUTE_TYPE(data[0]) + if version >= 4 { + b.Instance = binary.BigEndian.Uint16(data[1:3]) + if version >= 5 { + b.ReceiveNotify = data[3] + } + } + return nil +} + +// Reference: zebra_hello_send function in lib/zclient.c of Quagga1.2.x (ZAPI3) +// Reference: zebra_hello_send function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zebra_hello_send function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *HelloBody) Serialize(version uint8) ([]byte, error) { + if version <= 3 { + return []byte{uint8(b.RedistDefault)}, nil + } else { // version >= 4 + var buf []byte + if version == 4 { + buf = make([]byte, 3) + } else if version >= 5 { + buf = make([]byte, 4) + } + buf[0] = uint8(b.RedistDefault) + binary.BigEndian.PutUint16(buf[1:3], b.Instance) + if version >= 5 { + buf[3] = b.ReceiveNotify + } + return buf, nil + } +} + +func (b *HelloBody) String() string { + return fmt.Sprintf( + "route_type: %s, instance :%d", + b.RedistDefault.String(), b.Instance) +} + +type RedistributeBody struct { + Afi AFI + Redist ROUTE_TYPE + Instance uint16 +} + +// Reference: zebra_redistribute_add function in zebra/redistribute.c of Quagga1.2.x (ZAPI3) +// Reference: zebra_redistribute_add function in zebra/redistribute.c of FRR3.x (ZAPI4) +// Reference: zebra_redistribute_add function in zebra/redistribute.c of FRR5.x (ZAPI5) +func (b *RedistributeBody) DecodeFromBytes(data []byte, version uint8) error { + if version <= 3 { + b.Redist = ROUTE_TYPE(data[0]) + } else { // version >= 4 + b.Afi = AFI(data[0]) + b.Redist = ROUTE_TYPE(data[1]) + b.Instance = binary.BigEndian.Uint16(data[2:4]) + } + return nil +} + +// Reference: zebra_redistribute_send function in lib/zclient.c of Quagga1.2.x (ZAPI4) +// Reference: zebra_redistribute_send function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zebra_redistribute_send function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *RedistributeBody) Serialize(version uint8) ([]byte, error) { + if version <= 3 { + return []byte{uint8(b.Redist)}, nil + } else { // version >= 4 + buf := make([]byte, 4) + buf[0] = uint8(b.Afi) + buf[1] = uint8(b.Redist) + binary.BigEndian.PutUint16(buf[2:4], b.Instance) + return buf, nil + } +} + +func (b *RedistributeBody) String() string { + return fmt.Sprintf( + "afi: %s, route_type: %s, instance :%d", + b.Afi.String(), b.Redist.String(), b.Instance) +} + +type LinkParam struct { + Status uint32 + TeMetric uint32 + MaxBw float32 + MaxRsvBw float32 + UnrsvBw [8]float32 + BwClassNum uint32 + AdminGroup uint32 + RemoteAS uint32 + RemoteIP net.IP + AveDelay uint32 + MinDelay uint32 + MaxDelay uint32 + DelayVar uint32 + PktLoss float32 + ResidualBw float32 + AvailableBw float32 + UseBw float32 +} + +type InterfaceUpdateBody struct { + Name string + Index uint32 + Status INTERFACE_STATUS + Flags uint64 + PTMEnable PTM_ENABLE + PTMStatus PTM_STATUS + Metric uint32 + Speed uint32 + MTU uint32 + MTU6 uint32 + Bandwidth uint32 + Linktype LINK_TYPE + HardwareAddr net.HardwareAddr + LinkParam LinkParam +} + +// Reference: zebra_interface_if_set_value function in lib/zclient.c of Quagga1.2.x (ZAPI4) +// Reference: zebra_interface_if_set_value function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zebra_interface_if_set_value function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *InterfaceUpdateBody) DecodeFromBytes(data []byte, version uint8) error { + if len(data) < INTERFACE_NAMSIZ+29 { + return fmt.Errorf("lack of bytes. need %d but %d", INTERFACE_NAMSIZ+29, len(data)) + } + + b.Name = strings.Trim(string(data[:INTERFACE_NAMSIZ]), "\u0000") + data = data[INTERFACE_NAMSIZ:] + b.Index = binary.BigEndian.Uint32(data[0:4]) + b.Status = INTERFACE_STATUS(data[4]) + b.Flags = binary.BigEndian.Uint64(data[5:13]) + if version >= 4 { + b.PTMEnable = PTM_ENABLE(data[13]) + b.PTMStatus = PTM_STATUS(data[14]) + b.Metric = binary.BigEndian.Uint32(data[15:19]) + b.Speed = binary.BigEndian.Uint32(data[19:23]) + data = data[23:] + } else { + b.Metric = binary.BigEndian.Uint32(data[13:17]) + data = data[17:] + } + b.MTU = binary.BigEndian.Uint32(data[0:4]) + b.MTU6 = binary.BigEndian.Uint32(data[4:8]) + b.Bandwidth = binary.BigEndian.Uint32(data[8:12]) + if version >= 3 { + b.Linktype = LINK_TYPE(binary.BigEndian.Uint32(data[12:16])) + data = data[16:] + } else { + data = data[12:] + } + l := binary.BigEndian.Uint32(data[:4]) + if l > 0 { + if len(data) < 4+int(l) { + return fmt.Errorf("lack of bytes. need %d but %d", 4+l, len(data)) + } + b.HardwareAddr = data[4 : 4+l] + } + if version >= 5 { + LinkParam := data[4+l] + if LinkParam > 0 { + data = data[5+l:] + b.LinkParam.Status = binary.BigEndian.Uint32(data[0:4]) + b.LinkParam.TeMetric = binary.BigEndian.Uint32(data[4:8]) + b.LinkParam.MaxBw = math.Float32frombits(binary.BigEndian.Uint32(data[8:12])) + b.LinkParam.MaxRsvBw = math.Float32frombits(binary.BigEndian.Uint32(data[12:16])) + b.LinkParam.BwClassNum = binary.BigEndian.Uint32(data[16:20]) + for i := uint32(0); i < b.LinkParam.BwClassNum; i++ { + b.LinkParam.UnrsvBw[i] = math.Float32frombits(binary.BigEndian.Uint32(data[20+i*4 : 24+i*4])) + } + data = data[20+b.LinkParam.BwClassNum*4:] + b.LinkParam.AdminGroup = binary.BigEndian.Uint32(data[0:4]) + b.LinkParam.RemoteAS = binary.BigEndian.Uint32(data[4:8]) + b.LinkParam.RemoteIP = data[8:12] + b.LinkParam.AveDelay = binary.BigEndian.Uint32(data[12:16]) + b.LinkParam.MinDelay = binary.BigEndian.Uint32(data[16:20]) + b.LinkParam.MaxDelay = binary.BigEndian.Uint32(data[20:24]) + b.LinkParam.DelayVar = binary.BigEndian.Uint32(data[24:28]) + b.LinkParam.PktLoss = math.Float32frombits(binary.BigEndian.Uint32(data[28:32])) + b.LinkParam.ResidualBw = math.Float32frombits(binary.BigEndian.Uint32(data[32:36])) + b.LinkParam.AvailableBw = math.Float32frombits(binary.BigEndian.Uint32(data[36:40])) + b.LinkParam.UseBw = math.Float32frombits(binary.BigEndian.Uint32(data[40:44])) + } + } + return nil +} + +func (b *InterfaceUpdateBody) Serialize(version uint8) ([]byte, error) { + return []byte{}, nil +} + +func (b *InterfaceUpdateBody) String() string { + s := fmt.Sprintf( + "name: %s, idx: %d, status: %s, flags: %s, ptm_enable: %s, ptm_status: %s, metric: %d, speed: %d, mtu: %d, mtu6: %d, bandwidth: %d, linktype: %s", + b.Name, b.Index, b.Status.String(), intfflag2string(b.Flags), b.PTMEnable.String(), b.PTMStatus.String(), b.Metric, b.Speed, b.MTU, b.MTU6, b.Bandwidth, b.Linktype.String()) + if len(b.HardwareAddr) > 0 { + return s + fmt.Sprintf(", mac: %s", b.HardwareAddr.String()) + } + return s +} + +type InterfaceAddressUpdateBody struct { + Index uint32 + Flags INTERFACE_ADDRESS_FLAG + Prefix net.IP + Length uint8 + Destination net.IP +} + +// Reference: zebra_interface_address_read function in lib/zclient.c of Quagga1.2.x (ZAPI4) +// Reference: zebra_interface_address_read function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zebra_interface_address_read function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *InterfaceAddressUpdateBody) DecodeFromBytes(data []byte, version uint8) error { + b.Index = binary.BigEndian.Uint32(data[:4]) + b.Flags = INTERFACE_ADDRESS_FLAG(data[4]) + family := data[5] + addrlen, err := addressByteLength(family) + if err != nil { + return err + } + b.Prefix = data[6 : 6+addrlen] + b.Length = data[6+addrlen] + b.Destination = data[7+addrlen : 7+addrlen*2] + return nil +} + +func (b *InterfaceAddressUpdateBody) Serialize(version uint8) ([]byte, error) { + return []byte{}, nil +} + +func (b *InterfaceAddressUpdateBody) String() string { + return fmt.Sprintf( + "idx: %d, flags: %s, addr: %s/%d", + b.Index, b.Flags.String(), b.Prefix.String(), b.Length) +} + +type RouterIDUpdateBody struct { + Length uint8 + Prefix net.IP +} + +// Reference: zebra_router_id_update_read function in lib/zclient.c of Quagga1.2.x (ZAPI4) +// Reference: zebra_router_id_update_read function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zebra_router_id_update_read function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *RouterIDUpdateBody) DecodeFromBytes(data []byte, version uint8) error { + family := data[0] + + addrlen, err := addressByteLength(family) + if err != nil { + return err + } + b.Prefix = data[1 : 1+addrlen] + b.Length = data[1+addrlen] + return nil +} + +func (b *RouterIDUpdateBody) Serialize(version uint8) ([]byte, error) { + return []byte{}, nil +} + +func (b *RouterIDUpdateBody) String() string { + return fmt.Sprintf("id: %s/%d", b.Prefix.String(), b.Length) +} + +/* + Reference: struct zapi_nexthop in lib/zclient.h of FRR5.x (ZAPI5) +*/ +type Nexthop struct { + Type NEXTHOP_TYPE + VrfId uint32 + Ifindex uint32 + Gate net.IP + BlackholeType uint8 + LabelNum uint8 + MplsLabels []uint32 +} + +func (n *Nexthop) String() string { + s := fmt.Sprintf( + "type: %s, gate: %s, ifindex: %d", + n.Type.String(), n.Gate.String(), n.Ifindex) + return s +} + +type Prefix struct { + Family uint8 + PrefixLen uint8 + Prefix net.IP +} + +type IPRouteBody struct { + Type ROUTE_TYPE + Instance uint16 + Flags FLAG + Message MESSAGE_FLAG + SAFI SAFI + Prefix Prefix + SrcPrefix Prefix + Nexthops []Nexthop + Distance uint8 + Metric uint32 + Mtu uint32 + Tag uint32 + Rmac [6]byte + Api API_TYPE +} + +func (b *IPRouteBody) RouteFamily(version uint8) bgp.RouteFamily { + if b == nil { + return bgp.RF_OPAQUE + } + family := addressFamilyFromApi(b.Api, version) + if family == syscall.AF_UNSPEC { + if b.Prefix.Prefix.To4() != nil { + family = syscall.AF_INET + } else if b.Prefix.Prefix.To16() != nil { + family = syscall.AF_INET6 + } + } + switch family { + case syscall.AF_INET: + return bgp.RF_IPv4_UC + case syscall.AF_INET6: + return bgp.RF_IPv6_UC + } + return bgp.RF_OPAQUE +} + +func (b *IPRouteBody) IsWithdraw(version uint8) bool { + if version <= 3 { + switch b.Api { + case IPV4_ROUTE_DELETE, IPV6_ROUTE_DELETE: + return true + } + } else if version == 4 { + switch b.Api { + case FRR_IPV4_ROUTE_DELETE, FRR_IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_DEL, FRR_REDISTRIBUTE_IPV6_DEL: + return true + } + } else if version >= 5 { + switch b.Api { + case FRR_ZAPI5_ROUTE_DELETE, FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL: + return true + } + } + return false +} + +// Reference: zapi_ipv4_route function in lib/zclient.c of Quagga1.2.x (ZAPI3) +// Reference: zapi_ipv4_route function in lib/zclient.c of FRR3.x (ZAPI4) +// Reference: zapi_route_encode function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *IPRouteBody) Serialize(version uint8) ([]byte, error) { + var buf []byte + if version <= 3 { + buf = make([]byte, 5) + } else if version == 4 { + buf = make([]byte, 10) + } else { // version >= 5 + buf = make([]byte, 9) + } + buf[0] = uint8(b.Type) + if version <= 3 { + buf[1] = uint8(b.Flags) + buf[2] = uint8(b.Message) + binary.BigEndian.PutUint16(buf[3:5], uint16(b.SAFI)) + } else { // version >= 4 + binary.BigEndian.PutUint16(buf[1:3], uint16(b.Instance)) + binary.BigEndian.PutUint32(buf[3:7], uint32(b.Flags)) + buf[7] = uint8(b.Message) + if version == 4 { + binary.BigEndian.PutUint16(buf[8:10], uint16(b.SAFI)) + } else { // version >= 5 + buf[8] = uint8(b.SAFI) + if b.Flags&FLAG_EVPN_ROUTE > 0 { + // size of struct ethaddr is 6 octets defined by ETH_ALEN + buf = append(buf, b.Rmac[:6]...) + } + if b.Prefix.Family == syscall.AF_UNSPEC { + if b.Prefix.Prefix.To4() != nil { + b.Prefix.Family = syscall.AF_INET + } else if b.Prefix.Prefix.To16() != nil { + b.Prefix.Family = syscall.AF_INET6 + } + } + buf = append(buf, b.Prefix.Family) + } + } + byteLen := (int(b.Prefix.PrefixLen) + 7) / 8 + buf = append(buf, b.Prefix.PrefixLen) + buf = append(buf, b.Prefix.Prefix[:byteLen]...) + + if (version == 4 && b.Message&FRR_MESSAGE_SRCPFX > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { + byteLen = (int(b.SrcPrefix.PrefixLen) + 7) / 8 + buf = append(buf, b.SrcPrefix.PrefixLen) + buf = append(buf, b.SrcPrefix.Prefix[:byteLen]...) + } + if (version <= 3 && b.Message&MESSAGE_NEXTHOP > 0) || + (version == 4 && b.Message&FRR_MESSAGE_NEXTHOP > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { + if version < 5 { + if b.Flags&FLAG_BLACKHOLE > 0 { + buf = append(buf, []byte{1, uint8(NEXTHOP_TYPE_BLACKHOLE)}...) + } else { + buf = append(buf, uint8(len(b.Nexthops))) + } + } else { // version == 5 + bbuf := make([]byte, 2) + binary.BigEndian.PutUint16(bbuf, uint16(len(b.Nexthops))) + buf = append(buf, bbuf...) + } + for _, nexthop := range b.Nexthops { + if version == 5 { + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, nexthop.VrfId) + buf = append(buf, bbuf...) + } + + if nexthop.Type == NEXTHOP_TYPE(0) { + if nexthop.Gate.To4() != nil { + if version <= 3 { + nexthop.Type = NEXTHOP_TYPE_IPV4 + } else { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV4 + } + if version == 5 && nexthop.Ifindex > 0 { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV4_IFINDEX + } + } else if nexthop.Gate.To16() != nil { + if version <= 3 { + nexthop.Type = NEXTHOP_TYPE_IPV6 + } else { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV6 + } + if version == 5 && nexthop.Ifindex > 0 { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV6_IFINDEX + } + } else if nexthop.Ifindex > 0 { + if version <= 3 { + nexthop.Type = NEXTHOP_TYPE_IFINDEX + } else { + nexthop.Type = FRR_NEXTHOP_TYPE_IFINDEX + } + } else if version >= 5 { + nexthop.Type = FRR_NEXTHOP_TYPE_BLACKHOLE + } + } + + buf = append(buf, uint8(nexthop.Type)) + + if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4) { + buf = append(buf, nexthop.Gate.To4()...) + } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6) { + buf = append(buf, nexthop.Gate.To16()...) + } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IFINDEX) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX) { + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) + buf = append(buf, bbuf...) + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX { + buf = append(buf, nexthop.Gate.To4()...) + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) + buf = append(buf, bbuf...) + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX { + buf = append(buf, nexthop.Gate.To16()...) + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) + buf = append(buf, bbuf...) + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_BLACKHOLE { + buf = append(buf, uint8(nexthop.BlackholeType)) + } + if version == 5 && b.Message&FRR_ZAPI5_MESSAGE_LABEL > 0 { + buf = append(buf, nexthop.LabelNum) + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, nexthop.MplsLabels[0]) + buf = append(buf, bbuf...) + } + } + if (version <= 3 && b.Message&MESSAGE_DISTANCE > 0) || + (version == 4 && b.Message&FRR_MESSAGE_DISTANCE > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { + buf = append(buf, b.Distance) + } + if (version <= 3 && b.Message&MESSAGE_METRIC > 0) || + (version == 4 && b.Message&FRR_MESSAGE_METRIC > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_METRIC > 0) { + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, b.Metric) + buf = append(buf, bbuf...) + } + if (version <= 3 && b.Message&MESSAGE_MTU > 0) || + (version == 4 && b.Message&FRR_MESSAGE_MTU > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_MTU > 0) { + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, b.Mtu) + buf = append(buf, bbuf...) + } + if (version <= 3 && b.Message&MESSAGE_TAG > 0) || + (version == 4 && b.Message&FRR_MESSAGE_TAG > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_TAG > 0) { + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, b.Tag) + buf = append(buf, bbuf...) + } + } + return buf, nil +} + +// Reference: zebra_read_ipv4 function in bgpd/bgp_zebra.c of Quagga1.2.x (ZAPI3) +// Reference: zebra_read_ipv4 function in bgpd/bgp_zebra.c of FRR4.x (ZAPI4) +// Reference: zapi_route_decode function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *IPRouteBody) DecodeFromBytes(data []byte, version uint8) error { + if b == nil { + return fmt.Errorf("[IPRouteBody DecodeFromBytes] IPRouteBody is nil") + } + b.Prefix.Family = addressFamilyFromApi(b.Api, version) + /* REDSTRIBUTE_IPV4_ADD|DEL and REDSITRBUTE_IPV6_ADD|DEL have merged to + REDISTRIBUTE_ROUTE_ADD|DEL in ZAPI version 5. + Therefore it can not judge the protocol famiiy from API. */ + + b.Type = ROUTE_TYPE(data[0]) + if version <= 3 { + b.Flags = FLAG(data[1]) + data = data[2:] + } else { // version >= 4 + b.Instance = binary.BigEndian.Uint16(data[1:3]) + b.Flags = FLAG(binary.BigEndian.Uint32(data[3:7])) + data = data[7:] + } + + b.Message = MESSAGE_FLAG(data[0]) + b.SAFI = SAFI(SAFI_UNICAST) + if version >= 5 { + b.SAFI = SAFI(data[1]) + data = data[2:] + if b.Flags&FLAG_EVPN_ROUTE > 0 { + // size of struct ethaddr is 6 octets defined by ETH_ALEN + copy(b.Rmac[0:6], data[0:6]) + data = data[6:] + } + b.Prefix.Family = data[0] + } + + addrByteLen, err := addressByteLength(b.Prefix.Family) + if err != nil { + return err + } + + addrBitLen := uint8(addrByteLen * 8) + + b.Prefix.PrefixLen = data[1] + if b.Prefix.PrefixLen > addrBitLen { + return fmt.Errorf("prefix length is greater than %d", addrByteLen*8) + } + pos := 2 + rest := len(data[pos:]) + 2 + + buf := make([]byte, addrByteLen) + byteLen := int((b.Prefix.PrefixLen + 7) / 8) + if pos+byteLen > rest { + return fmt.Errorf("message length invalid pos:%d rest:%d", pos, rest) + } + copy(buf, data[pos:pos+byteLen]) + b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, buf) + pos += byteLen + + if (version == 4 && b.Message&FRR_MESSAGE_SRCPFX > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { + if pos+1 > rest { + return fmt.Errorf("MESSAGE_SRCPFX message length invalid pos:%d rest:%d", pos, rest) + } + b.SrcPrefix.PrefixLen = data[pos] + if b.SrcPrefix.PrefixLen > addrBitLen { + return fmt.Errorf("prefix length is greater than %d", addrByteLen*8) + } + pos += 1 + buf = make([]byte, addrByteLen) + byteLen = int((b.SrcPrefix.PrefixLen + 7) / 8) + copy(buf, data[pos:pos+byteLen]) + if pos+byteLen > rest { + return fmt.Errorf("MESSAGE_SRCPFX message length invalid pos:%d rest:%d", pos, rest) + } + b.SrcPrefix.Prefix = ipFromFamily(b.Prefix.Family, buf) + pos += byteLen + } + + b.Nexthops = []Nexthop{} + if (version <= 3 && b.Message&MESSAGE_NEXTHOP > 0) || + (version == 4 && b.Message&FRR_MESSAGE_NEXTHOP > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { + var numNexthop uint16 + if version <= 4 { + if pos+1 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) + } + numNexthop = uint16(data[pos]) + pos += 1 + } else { // version >= 5 + if pos+2 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) + } + numNexthop = binary.BigEndian.Uint16(data[pos : pos+2]) + pos += 2 + } + for i := 0; i < int(numNexthop); i++ { + var nexthop Nexthop + if version <= 3 { + if b.Prefix.Family == syscall.AF_INET { + nexthop.Type = NEXTHOP_TYPE_IPV4 + } else if b.Prefix.Family == syscall.AF_INET6 { + nexthop.Type = NEXTHOP_TYPE_IPV6 + } + } else if version == 4 { + if b.Prefix.Family == syscall.AF_INET { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV4 + } else if b.Prefix.Family == syscall.AF_INET6 { + nexthop.Type = FRR_NEXTHOP_TYPE_IPV6 + } + } else { // version >= 5 + if pos+5 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) + } + nexthop.VrfId = binary.BigEndian.Uint32(data[pos : pos+4]) + nexthop.Type = NEXTHOP_TYPE(data[pos+4]) + pos += 5 + } + + if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4) { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV4 message length invalid pos:%d rest:%d", pos, rest) + } + addr := data[pos : pos+4] + nexthop.Gate = net.IP(addr).To4() + pos += 4 + } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6) { + if pos+16 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV6 message length invalid pos:%d rest:%d", pos, rest) + } + addr := data[pos : pos+16] + nexthop.Gate = net.IP(addr).To16() + pos += 16 + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) + } + nexthop.Ifindex = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + // barkward compatibility + if b.Prefix.Family == syscall.AF_INET { + nexthop.Gate = net.ParseIP("0.0.0.0") + } else if b.Prefix.Family == syscall.AF_INET6 { + nexthop.Gate = net.ParseIP("::") + } + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX { + if pos+8 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV4_IFINDEX message length invalid pos:%d rest:%d", pos, rest) + } + addr := data[pos : pos+4] + nexthop.Gate = net.IP(addr).To4() + nexthop.Ifindex = binary.BigEndian.Uint32(data[pos+4 : pos+8]) + pos += 8 + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX { + if pos+20 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV6_IFINDEX message length invalid pos:%d rest:%d", pos, rest) + } + addr := data[pos : pos+16] + nexthop.Gate = net.IP(addr).To16() + nexthop.Ifindex = binary.BigEndian.Uint32(data[pos+16 : pos+20]) + pos += 20 + } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_BLACKHOLE { + if pos+1 > rest { + return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_BLACKHOLE message length invalid pos:%d rest:%d", pos, rest) + } + nexthop.BlackholeType = data[pos] + pos += 1 + } + b.Nexthops = append(b.Nexthops, nexthop) + } + } + + if (version <= 3 && b.Message&MESSAGE_IFINDEX > 0) || + (version == 4 && b.Message&FRR_MESSAGE_IFINDEX > 0) { + if pos+1 > rest { + return fmt.Errorf("MESSAGE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) + } + numIfIndex := uint8(data[pos]) + pos += 1 + for i := 0; i < int(numIfIndex); i++ { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) + } + var nexthop Nexthop + nexthop.Ifindex = binary.BigEndian.Uint32(data[pos : pos+4]) + if version <= 3 { + nexthop.Type = NEXTHOP_TYPE_IFINDEX + } else if version == 4 { + nexthop.Type = FRR_NEXTHOP_TYPE_IFINDEX + } + b.Nexthops = append(b.Nexthops, nexthop) + pos += 4 + } + } + + if (version <= 3 && b.Message&MESSAGE_DISTANCE > 0) || + (version == 4 && b.Message&FRR_MESSAGE_DISTANCE > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { + if pos+1 > rest { + return fmt.Errorf("MESSAGE_DISTANCE message length invalid pos:%d rest:%d", pos, rest) + } + b.Distance = data[pos] + pos += 1 + } + if (version <= 3 && b.Message&MESSAGE_METRIC > 0) || + (version == 4 && b.Message&FRR_MESSAGE_METRIC > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_METRIC > 0) { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_METRIC message length invalid pos:%d rest:%d", pos, rest) + } + b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + } + if (version <= 3 && b.Message&MESSAGE_MTU > 0) || + (version == 4 && b.Message&FRR_MESSAGE_MTU > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_MTU > 0) { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_MTU message length invalid pos:%d rest:%d", pos, rest) + } + b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + } + if (version <= 3 && b.Message&MESSAGE_TAG > 0) || + (version == 4 && b.Message&FRR_MESSAGE_TAG > 0) || + (version == 5 && b.Message&FRR_ZAPI5_MESSAGE_TAG > 0) { + if pos+4 > rest { + return fmt.Errorf("MESSAGE_TAG message length invalid pos:%d rest:%d", pos, rest) + } + b.Tag = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + } + if pos != rest { + return fmt.Errorf("message length invalid") + } + + return nil +} + +func (b *IPRouteBody) String() string { + s := fmt.Sprintf( + "type: %s, instance: %d, flags: %s, message: %d, safi: %s, prefix: %s/%d, src_prefix: %s/%d", + b.Type.String(), b.Instance, b.Flags.String(), b.Message, b.SAFI.String(), b.Prefix.Prefix.String(), b.Prefix.PrefixLen, b.SrcPrefix.Prefix.String(), b.SrcPrefix.PrefixLen) + for i, nh := range b.Nexthops { + s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.String()) + /* + s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.Gate.String()) + s += fmt.Sprintf(", ifindex[%d]: %d", i, nh.Ifindex) + */ + } + return s + fmt.Sprintf( + ", distance: %d, metric: %d, mtu: %d, tag: %d", + b.Distance, b.Metric, b.Mtu, b.Tag) +} + +func decodeNexthopsFromBytes(nexthops *[]Nexthop, data []byte, family uint8, version uint8) (int, error) { + addrByteLen, err := addressByteLength(family) + if err != nil { + return 0, err + } + + numNexthop := int(data[0]) + offset := 1 + + for i := 0; i < numNexthop; i++ { + nexthop := Nexthop{} + nexthop.Type = NEXTHOP_TYPE(data[offset]) + offset += 1 + + // On Quagga, NEXTHOP_TYPE_IFNAME is same as NEXTHOP_TYPE_IFINDEX, + // NEXTHOP_TYPE_IPV4_IFNAME is same as NEXTHOP_TYPE_IPV4_IFINDEX, + // NEXTHOP_TYPE_IPV6_IFNAME is same as NEXTHOP_TYPE_IPV6_IFINDEX + + // On FRRouting version 3.0 or later, NEXTHOP_TYPE_IPV4 and NEXTHOP_TYPE_IPV6 have + // the same structure with NEXTHOP_TYPE_IPV4_IFINDEX and NEXTHOP_TYPE_IPV6_IFINDEX. + + if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IFNAME)) || + (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX) { + nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } else if version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4 { + nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To4() + offset += addrByteLen + } else if version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6 { + nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To16() + offset += addrByteLen + } else if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IPV4_IFNAME)) || + (version >= 4 && (nexthop.Type == FRR_NEXTHOP_TYPE_IPV4 || nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX)) { + nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To4() + offset += addrByteLen + nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } else if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IPV6_IFNAME)) || + (version >= 4 && (nexthop.Type == FRR_NEXTHOP_TYPE_IPV6 || nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX)) { + nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To16() + offset += addrByteLen + nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + if version >= 5 { + nexthop.LabelNum = data[offset] + offset += 1 + if nexthop.LabelNum > MPLS_MAX_LABLE { + nexthop.LabelNum = MPLS_MAX_LABLE + } + var n uint8 + for ; n < nexthop.LabelNum; n++ { + nexthop.MplsLabels[n] = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + } + *nexthops = append(*nexthops, nexthop) + } + + return offset, nil +} + +type NexthopLookupBody struct { + Api API_TYPE + Addr net.IP + Distance uint8 + Metric uint32 + Nexthops []Nexthop +} + +// Quagga only. Reference: zread_ipv[4|6]_nexthop_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) +func (b *NexthopLookupBody) Serialize(version uint8) ([]byte, error) { + family := addressFamilyFromApi(b.Api, version) + buf := make([]byte, 0) + + if family == syscall.AF_INET { + buf = append(buf, b.Addr.To4()...) + } else if family == syscall.AF_INET6 { + buf = append(buf, b.Addr.To16()...) + } + return buf, nil +} + +// Quagga only. Reference: zsend_ipv[4|6]_nexthop_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) +func (b *NexthopLookupBody) DecodeFromBytes(data []byte, version uint8) error { + family := addressFamilyFromApi(b.Api, version) + addrByteLen, err := addressByteLength(family) + if err != nil { + return err + } + + if len(data) < addrByteLen { + return fmt.Errorf("message length invalid") + } + + buf := make([]byte, addrByteLen) + copy(buf, data[0:addrByteLen]) + pos := addrByteLen + b.Addr = ipFromFamily(family, buf) + + if version >= 4 { + b.Distance = data[pos] + pos++ + } + + if len(data[pos:]) > int(1+addrByteLen) { + b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + b.Nexthops = []Nexthop{} + if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], family, version); err != nil { + return err + } else { + pos += nexthopsByteLen + } + } + + return nil +} + +func (b *NexthopLookupBody) String() string { + s := fmt.Sprintf( + "addr: %s, distance:%d, metric: %d", + b.Addr.String(), b.Distance, b.Metric) + if len(b.Nexthops) > 0 { + for _, nh := range b.Nexthops { + s = s + fmt.Sprintf(", nexthop:{%s}", nh.String()) + } + } + return s +} + +type ImportLookupBody struct { + Api API_TYPE + PrefixLength uint8 + Prefix net.IP + Addr net.IP + Metric uint32 + Nexthops []Nexthop +} + +// Quagga only. Reference: zread_ipv4_import_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) +func (b *ImportLookupBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 1) + buf[0] = b.PrefixLength + buf = append(buf, b.Addr.To4()...) + return buf, nil +} + +// Quagga only. Reference: zsend_ipv4_import_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) +func (b *ImportLookupBody) DecodeFromBytes(data []byte, version uint8) error { + family := addressFamilyFromApi(b.Api, version) + addrByteLen, err := addressByteLength(family) + if err != nil { + return err + } + + if len(data) < addrByteLen { + return fmt.Errorf("message length invalid") + } + + buf := make([]byte, addrByteLen) + copy(buf, data[0:addrByteLen]) + pos := addrByteLen + + b.Addr = net.IP(buf).To4() + + if len(data[pos:]) > int(1+addrByteLen) { + b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) + pos += 4 + b.Nexthops = []Nexthop{} + if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], family, version); err != nil { + return err + } else { + pos += nexthopsByteLen + } + } + + return nil +} + +func (b *ImportLookupBody) String() string { + s := fmt.Sprintf( + "prefix: %s/%d, addr: %s, metric: %d", + b.Prefix.String(), b.PrefixLength, b.Addr.String(), b.Metric) + if len(b.Nexthops) > 0 { + for _, nh := range b.Nexthops { + s = s + fmt.Sprintf(", nexthop:{%s}", nh.String()) + } + } + return s +} + +type RegisteredNexthop struct { + Connected uint8 + Family uint16 + // Note: Ignores PrefixLength (uint8), + // because this field should be always: + // - 32 if Address Family is AF_INET + // - 128 if Address Family is AF_INET6 + Prefix net.IP +} + +func (n *RegisteredNexthop) Len() int { + // Connected (1 byte) + Address Family (2 bytes) + Prefix Length (1 byte) + Prefix (variable) + if n.Family == uint16(syscall.AF_INET) { + return 4 + net.IPv4len + } else { + return 4 + net.IPv6len + } +} + +// Reference: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) +// Reference: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4) +// Reference: zclient_send_rnh function in lib/zclient.c of FRR5.x (ZAPI5) +func (n *RegisteredNexthop) Serialize() ([]byte, error) { + // Connected (1 byte) + buf := make([]byte, 4) + buf[0] = byte(n.Connected) + + // Address Family (2 bytes) + binary.BigEndian.PutUint16(buf[1:3], n.Family) + // Prefix Length (1 byte) + addrByteLen, err := addressByteLength(uint8(n.Family)) + if err != nil { + return nil, err + } + + buf[3] = byte(addrByteLen * 8) + // Prefix (variable) + switch n.Family { + case uint16(syscall.AF_INET): + buf = append(buf, n.Prefix.To4()...) + case uint16(syscall.AF_INET6): + buf = append(buf, n.Prefix.To16()...) + default: + return nil, fmt.Errorf("invalid address family: %d", n.Family) + } + + return buf, nil +} + +// Reference: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3) +// Reference: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4) +// Reference: zread_rnh_register in zebra/zapi_msg.c of FRR5.x (ZAPI5) +func (n *RegisteredNexthop) DecodeFromBytes(data []byte) error { + // Connected (1 byte) + n.Connected = uint8(data[0]) + // Address Family (2 bytes) + n.Family = binary.BigEndian.Uint16(data[1:3]) + // Note: Ignores Prefix Length (1 byte) + addrByteLen := (int(data[3]) + 7) / 8 + // Prefix (variable) + n.Prefix = ipFromFamily(uint8(n.Family), data[4:4+addrByteLen]) + + return nil +} + +func (n *RegisteredNexthop) String() string { + return fmt.Sprintf( + "connected: %d, family: %d, prefix: %s", + n.Connected, n.Family, n.Prefix.String()) +} + +type NexthopRegisterBody struct { + Api API_TYPE + Nexthops []*RegisteredNexthop +} + +// Reference: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) +// Reference: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4) +// Reference: zclient_send_rnh function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *NexthopRegisterBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 0) + + // List of Registered Nexthops + for _, nh := range b.Nexthops { + nhBuf, err := nh.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, nhBuf...) + } + + return buf, nil +} + +// Reference: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3) +// Reference: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4) +// Reference: zread_rnh_register in zebra/zapi_msg.c of FRR5.x (ZAPI5) +func (b *NexthopRegisterBody) DecodeFromBytes(data []byte, version uint8) error { + offset := 0 + + // List of Registered Nexthops + b.Nexthops = []*RegisteredNexthop{} + for len(data[offset:]) > 0 { + nh := new(RegisteredNexthop) + err := nh.DecodeFromBytes(data[offset:]) + if err != nil { + return err + } + b.Nexthops = append(b.Nexthops, nh) + + offset += nh.Len() + if len(data) < offset { + break + } + } + + return nil +} + +func (b *NexthopRegisterBody) String() string { + s := make([]string, 0) + for _, nh := range b.Nexthops { + s = append(s, fmt.Sprintf("nexthop:{%s}", nh.String())) + } + return strings.Join(s, ", ") +} + +/* NEXTHOP_UPDATE message uses same data structure as IPRoute (zapi_route) + in FRR version 4, 5 (ZApi version 5) */ +type NexthopUpdateBody IPRouteBody + +// Reference: send_client function in zebra/zebra_rnh.c of Quagga1.2.x (ZAPI3) +// Reference: send_client function in zebra/zebra_rnh.c of FRR3.x (ZAPI4) +// Reference: send_client function in zebra/zebra_rnh.c of FRR5.x (ZAPI5) +func (b *NexthopUpdateBody) Serialize(version uint8) ([]byte, error) { + // Address Family (2 bytes) + buf := make([]byte, 3) + binary.BigEndian.PutUint16(buf, uint16(b.Prefix.Family)) + addrByteLen, err := addressByteLength(b.Prefix.Family) + if err != nil { + return nil, err + } + + buf[2] = byte(addrByteLen * 8) + // Prefix Length (1 byte) + Prefix (variable) + switch b.Prefix.Family { + case syscall.AF_INET: + buf = append(buf, b.Prefix.Prefix.To4()...) + case syscall.AF_INET6: + buf = append(buf, b.Prefix.Prefix.To16()...) + default: + return nil, fmt.Errorf("invalid address family: %d", b.Prefix.Family) + } + if version >= 5 { + // Type (1 byte) (if version>=5) + // Instance (2 bytes) (if version>=5) + buf = append(buf, byte(b.Type)) + bbuf := make([]byte, 2) + binary.BigEndian.PutUint16(bbuf, b.Instance) + buf = append(buf, bbuf...) + } + if version >= 4 { + // Distance (1 byte) (if version>=4) + buf = append(buf, b.Distance) + } + // Metric (4 bytes) + bbuf := make([]byte, 4) + binary.BigEndian.PutUint32(bbuf, b.Metric) + buf = append(buf, bbuf...) + // Number of Nexthops (1 byte) + buf = append(buf, uint8(0)) // Temporary code + // ToDo Processing Route Entry + + return buf, nil +} + +// Reference: bgp_parse_nexthop_update function in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) +// Reference: bgp_parse_nexthop_update function in bgpd/bgp_nht.c of FRR3.x (ZAPI4) +// Reference: zapi_nexthop_update_decode function in lib/zclient.c of FRR5.x (ZAPI5) +func (b *NexthopUpdateBody) DecodeFromBytes(data []byte, version uint8) error { + // Address Family (2 bytes) + prefixFamily := binary.BigEndian.Uint16(data[0:2]) + b.Prefix.Family = uint8(prefixFamily) + b.Prefix.PrefixLen = data[2] + offset := 3 + + addrByteLen, err := addressByteLength(b.Prefix.Family) + if err != nil { + return err + } + + b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, data[offset:offset+addrByteLen]) + offset += addrByteLen + + if version >= 5 { + b.Type = ROUTE_TYPE(data[offset]) + b.Instance = binary.BigEndian.Uint16(data[offset+1 : offset+3]) + offset += 3 + } + // Distance (1 byte) (if version>=4) + if version >= 4 { + b.Distance = data[offset] + offset += 1 + } + // Metric (4 bytes) + // Number of Nexthops (1 byte) + if len(data[offset:]) < 5 { + return fmt.Errorf("invalid message length: missing metric(4 bytes) or nexthops(1 byte): %d<5", len(data[offset:])) + } + b.Metric = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + + // List of Nexthops + b.Nexthops = []Nexthop{} + if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[offset:], b.Prefix.Family, version); err != nil { + return err + } else { + offset += nexthopsByteLen + } + return nil +} + +func (b *NexthopUpdateBody) String() string { + s := fmt.Sprintf( + "family: %d, prefix: %s, distance: %d, metric: %d", + b.Prefix.Family, b.Prefix.Prefix.String(), b.Distance, b.Metric) + for _, nh := range b.Nexthops { + s = s + fmt.Sprintf(", nexthop:{%s}", nh.String()) + } + return s +} + +type Message struct { + Header Header + Body Body +} + +func (m *Message) Serialize() ([]byte, error) { + var body []byte + if m.Body != nil { + var err error + body, err = m.Body.Serialize(m.Header.Version) + if err != nil { + return nil, err + } + } + m.Header.Len = uint16(len(body)) + HeaderSize(m.Header.Version) + hdr, err := m.Header.Serialize() + if err != nil { + return nil, err + } + return append(hdr, body...), nil +} + +func (m *Message) parseMessage(data []byte) error { + switch m.Header.Command { + case INTERFACE_ADD, INTERFACE_DELETE, INTERFACE_UP, INTERFACE_DOWN: + m.Body = &InterfaceUpdateBody{} + case INTERFACE_ADDRESS_ADD, INTERFACE_ADDRESS_DELETE: + m.Body = &InterfaceAddressUpdateBody{} + case ROUTER_ID_UPDATE: + m.Body = &RouterIDUpdateBody{} + case IPV4_ROUTE_ADD, IPV6_ROUTE_ADD, IPV4_ROUTE_DELETE, IPV6_ROUTE_DELETE: + m.Body = &IPRouteBody{Api: m.Header.Command} + case IPV4_NEXTHOP_LOOKUP, IPV6_NEXTHOP_LOOKUP: + m.Body = &NexthopLookupBody{Api: m.Header.Command} + case IPV4_IMPORT_LOOKUP: + m.Body = &ImportLookupBody{Api: m.Header.Command} + case NEXTHOP_UPDATE: + m.Body = &NexthopUpdateBody{Api: m.Header.Command} + default: + m.Body = &UnknownBody{} + } + return m.Body.DecodeFromBytes(data, m.Header.Version) +} + +func (m *Message) parseFrrMessage(data []byte) error { + switch m.Header.Command { + case FRR_INTERFACE_ADD, FRR_INTERFACE_DELETE, FRR_INTERFACE_UP, FRR_INTERFACE_DOWN: + m.Body = &InterfaceUpdateBody{} + case FRR_INTERFACE_ADDRESS_ADD, FRR_INTERFACE_ADDRESS_DELETE: + m.Body = &InterfaceAddressUpdateBody{} + case FRR_ROUTER_ID_UPDATE: + m.Body = &RouterIDUpdateBody{} + case FRR_NEXTHOP_UPDATE: + m.Body = &NexthopUpdateBody{} + case FRR_INTERFACE_NBR_ADDRESS_ADD, FRR_INTERFACE_NBR_ADDRESS_DELETE: + // TODO + m.Body = &UnknownBody{} + case FRR_INTERFACE_BFD_DEST_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_IMPORT_CHECK_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_BFD_DEST_REPLAY: + // TODO + m.Body = &UnknownBody{} + case FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL: + m.Body = &IPRouteBody{Api: m.Header.Command} + case FRR_INTERFACE_VRF_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_INTERFACE_LINK_PARAMS: + // TODO + m.Body = &UnknownBody{} + case FRR_PW_STATUS_UPDATE: + // TODO + m.Body = &UnknownBody{} + default: + m.Body = &UnknownBody{} + } + return m.Body.DecodeFromBytes(data, m.Header.Version) +} + +func (m *Message) parseFrrZapi5Message(data []byte) error { + switch m.Header.Command { + case FRR_ZAPI5_INTERFACE_ADD, FRR_ZAPI5_INTERFACE_DELETE, FRR_ZAPI5_INTERFACE_UP, FRR_ZAPI5_INTERFACE_DOWN: + m.Body = &InterfaceUpdateBody{} + case FRR_ZAPI5_INTERFACE_ADDRESS_ADD, FRR_ZAPI5_INTERFACE_ADDRESS_DELETE: + m.Body = &InterfaceAddressUpdateBody{} + case FRR_ZAPI5_ROUTER_ID_UPDATE: + m.Body = &RouterIDUpdateBody{} + case FRR_ZAPI5_NEXTHOP_UPDATE: + m.Body = &NexthopUpdateBody{} + case FRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADD, FRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETE: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_INTERFACE_BFD_DEST_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_IMPORT_CHECK_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_BFD_DEST_REPLAY: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_REDISTRIBUTE_ROUTE_ADD, FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL: + m.Body = &IPRouteBody{Api: m.Header.Command} + case FRR_ZAPI5_INTERFACE_VRF_UPDATE: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_INTERFACE_LINK_PARAMS: + // TODO + m.Body = &UnknownBody{} + case FRR_ZAPI5_PW_STATUS_UPDATE: + // TODO + m.Body = &UnknownBody{} + default: + m.Body = &UnknownBody{} + } + return m.Body.DecodeFromBytes(data, m.Header.Version) +} + +func ParseMessage(hdr *Header, data []byte) (m *Message, err error) { + m = &Message{Header: *hdr} + if m.Header.Version == 4 { + err = m.parseFrrMessage(data) + } else if m.Header.Version == 5 { + err = m.parseFrrZapi5Message(data) + } else { + err = m.parseMessage(data) + } + if err != nil { + return nil, err + } + return m, nil +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_bsd.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_bsd.go new file mode 100644 index 0000000..8960e79 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_bsd.go @@ -0,0 +1,58 @@ +// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 freebsd netbsd openbsd + +package zebra + +import ( + "strings" + "syscall" +) + +func intfflag2string(flag uint64) string { + ss := make([]string, 0, 10) + if flag&syscall.IFF_UP > 0 { + ss = append(ss, "UP") + } + if flag&syscall.IFF_BROADCAST > 0 { + ss = append(ss, "BROADCAST") + } + if flag&syscall.IFF_DEBUG > 0 { + ss = append(ss, "DEBUG") + } + if flag&syscall.IFF_LOOPBACK > 0 { + ss = append(ss, "LOOPBACK") + } + if flag&syscall.IFF_POINTOPOINT > 0 { + ss = append(ss, "POINTOPOINT") + } + if flag&syscall.IFF_RUNNING > 0 { + ss = append(ss, "RUNNING") + } + if flag&syscall.IFF_NOARP > 0 { + ss = append(ss, "NOARP") + } + if flag&syscall.IFF_PROMISC > 0 { + ss = append(ss, "PROMISC") + } + if flag&syscall.IFF_ALLMULTI > 0 { + ss = append(ss, "ALLMULTI") + } + if flag&syscall.IFF_MULTICAST > 0 { + ss = append(ss, "MULTICAST") + } + return strings.Join(ss, " | ") +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_darwin.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_darwin.go new file mode 100644 index 0000000..a253691 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_darwin.go @@ -0,0 +1,59 @@ +// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 zebra + +import ( + "strings" + "syscall" +) + +func intfflag2string(flag uint64) string { + ss := make([]string, 0, 10) + if flag&syscall.IFF_UP > 0 { + ss = append(ss, "UP") + } + if flag&syscall.IFF_BROADCAST > 0 { + ss = append(ss, "BROADCAST") + } + if flag&syscall.IFF_DEBUG > 0 { + ss = append(ss, "DEBUG") + } + if flag&syscall.IFF_LOOPBACK > 0 { + ss = append(ss, "LOOPBACK") + } + if flag&syscall.IFF_POINTOPOINT > 0 { + ss = append(ss, "POINTOPOINT") + } + if flag&syscall.IFF_NOTRAILERS > 0 { + ss = append(ss, "NOTRAILERS") + } + if flag&syscall.IFF_RUNNING > 0 { + ss = append(ss, "RUNNING") + } + if flag&syscall.IFF_NOARP > 0 { + ss = append(ss, "NOARP") + } + if flag&syscall.IFF_PROMISC > 0 { + ss = append(ss, "PROMISC") + } + if flag&syscall.IFF_ALLMULTI > 0 { + ss = append(ss, "ALLMULTI") + } + if flag&syscall.IFF_MULTICAST > 0 { + ss = append(ss, "MULTICAST") + } + return strings.Join(ss, " | ") +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_linux.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_linux.go new file mode 100644 index 0000000..66fccb7 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_linux.go @@ -0,0 +1,83 @@ +// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 zebra + +import ( + "strings" + "syscall" +) + +func intfflag2string(flag uint64) string { + ss := make([]string, 0, 10) + if flag&syscall.IFF_UP > 0 { + ss = append(ss, "UP") + } + if flag&syscall.IFF_BROADCAST > 0 { + ss = append(ss, "BROADCAST") + } + if flag&syscall.IFF_DEBUG > 0 { + ss = append(ss, "DEBUG") + } + if flag&syscall.IFF_LOOPBACK > 0 { + ss = append(ss, "LOOPBACK") + } + if flag&syscall.IFF_POINTOPOINT > 0 { + ss = append(ss, "POINTOPOINT") + } + if flag&syscall.IFF_NOTRAILERS > 0 { + ss = append(ss, "NOTRAILERS") + } + if flag&syscall.IFF_RUNNING > 0 { + ss = append(ss, "RUNNING") + } + if flag&syscall.IFF_NOARP > 0 { + ss = append(ss, "NOARP") + } + if flag&syscall.IFF_PROMISC > 0 { + ss = append(ss, "PROMISC") + } + if flag&syscall.IFF_ALLMULTI > 0 { + ss = append(ss, "ALLMULTI") + } + if flag&syscall.IFF_MASTER > 0 { + ss = append(ss, "MASTER") + } + if flag&syscall.IFF_SLAVE > 0 { + ss = append(ss, "SLAVE") + } + if flag&syscall.IFF_MULTICAST > 0 { + ss = append(ss, "MULTICAST") + } + if flag&syscall.IFF_PORTSEL > 0 { + ss = append(ss, "PORTSEL") + } + if flag&syscall.IFF_AUTOMEDIA > 0 { + ss = append(ss, "AUTOMEDIA") + } + if flag&syscall.IFF_DYNAMIC > 0 { + ss = append(ss, "DYNAMIC") + } + // if flag&syscall.IFF_LOWER_UP > 0 { + // ss = append(ss, "LOWER_UP") + // } + // if flag&syscall.IFF_DORMANT > 0 { + // ss = append(ss, "DORMANT") + // } + // if flag&syscall.IFF_ECHO > 0 { + // ss = append(ss, "ECHO") + // } + return strings.Join(ss, " | ") +} diff --git a/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_windows.go b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_windows.go new file mode 100644 index 0000000..d55525d --- /dev/null +++ b/vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi_windows.go @@ -0,0 +1,38 @@ +// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation. +// +// 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 zebra + +import ( + "strings" + "syscall" +) + +func intfflag2string(flag uint64) string { + ss := make([]string, 0, 10) + if flag&syscall.IFF_UP > 0 { + ss = append(ss, "UP") + } + if flag&syscall.IFF_BROADCAST > 0 { + ss = append(ss, "BROADCAST") + } + if flag&syscall.IFF_LOOPBACK > 0 { + ss = append(ss, "LOOPBACK") + } + if flag&syscall.IFF_MULTICAST > 0 { + ss = append(ss, "MULTICAST") + } + return strings.Join(ss, " | ") +} diff --git a/vendor/github.com/osrg/gobgp/pkg/packet/bgp/bgp.go b/vendor/github.com/osrg/gobgp/pkg/packet/bgp/bgp.go new file mode 100644 index 0000000..7586f47 --- /dev/null +++ b/vendor/github.com/osrg/gobgp/pkg/packet/bgp/bgp.go @@ -0,0 +1,9677 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// 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 bgp + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "math" + "net" + "reflect" + "regexp" + "sort" + "strconv" + "strings" +) + +type MarshallingOption struct { + AddPath map[RouteFamily]BGPAddPathMode +} + +func IsAddPathEnabled(decode bool, f RouteFamily, options []*MarshallingOption) bool { + for _, opt := range options { + if opt == nil { + continue + } + if o := opt.AddPath; o != nil { + if decode && o[f]&BGP_ADD_PATH_RECEIVE > 0 { + return true + } else if !decode && o[f]&BGP_ADD_PATH_SEND > 0 { + return true + } + } + } + return false +} + +const ( + AFI_IP = 1 + AFI_IP6 = 2 + AFI_L2VPN = 25 + AFI_OPAQUE = 16397 +) + +const ( + SAFI_UNICAST = 1 + SAFI_MULTICAST = 2 + SAFI_MPLS_LABEL = 4 + SAFI_ENCAPSULATION = 7 + SAFI_VPLS = 65 + SAFI_EVPN = 70 + SAFI_MPLS_VPN = 128 + SAFI_MPLS_VPN_MULTICAST = 129 + SAFI_ROUTE_TARGET_CONSTRAINTS = 132 + SAFI_FLOW_SPEC_UNICAST = 133 + SAFI_FLOW_SPEC_VPN = 134 + SAFI_KEY_VALUE = 241 +) + +const ( + BGP_ORIGIN_ATTR_TYPE_IGP uint8 = 0 + BGP_ORIGIN_ATTR_TYPE_EGP uint8 = 1 + BGP_ORIGIN_ATTR_TYPE_INCOMPLETE uint8 = 2 +) + +const ( + BGP_ASPATH_ATTR_TYPE_SET = 1 + BGP_ASPATH_ATTR_TYPE_SEQ = 2 + BGP_ASPATH_ATTR_TYPE_CONFED_SEQ = 3 + BGP_ASPATH_ATTR_TYPE_CONFED_SET = 4 +) + +// RFC7153 5.1. Registries for the "Type" Field +// RANGE REGISTRATION PROCEDURES +// 0x00-0x3F Transitive First Come First Served +// 0x40-0x7F Non-Transitive First Come First Served +// 0x80-0x8F Transitive Experimental Use +// 0x90-0xBF Transitive Standards Action +// 0xC0-0xCF Non-Transitive Experimental Use +// 0xD0-0xFF Non-Transitive Standards Action +type ExtendedCommunityAttrType uint8 + +const ( + EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x00 + EC_TYPE_TRANSITIVE_IP6_SPECIFIC ExtendedCommunityAttrType = 0x00 // RFC5701 + EC_TYPE_TRANSITIVE_IP4_SPECIFIC ExtendedCommunityAttrType = 0x01 + EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x02 + EC_TYPE_TRANSITIVE_OPAQUE ExtendedCommunityAttrType = 0x03 + EC_TYPE_TRANSITIVE_QOS_MARKING ExtendedCommunityAttrType = 0x04 + EC_TYPE_COS_CAPABILITY ExtendedCommunityAttrType = 0x05 + EC_TYPE_EVPN ExtendedCommunityAttrType = 0x06 + EC_TYPE_FLOWSPEC_REDIRECT_MIRROR ExtendedCommunityAttrType = 0x08 + EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x40 + EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC ExtendedCommunityAttrType = 0x40 // RFC5701 + EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC ExtendedCommunityAttrType = 0x41 + EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x42 + EC_TYPE_NON_TRANSITIVE_OPAQUE ExtendedCommunityAttrType = 0x43 + EC_TYPE_NON_TRANSITIVE_QOS_MARKING ExtendedCommunityAttrType = 0x44 + EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL ExtendedCommunityAttrType = 0x80 + EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 ExtendedCommunityAttrType = 0x81 // RFC7674 + EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 ExtendedCommunityAttrType = 0x82 // RFC7674 +) + +// RFC7153 5.2. Registries for the "Sub-Type" Field +// RANGE REGISTRATION PROCEDURES +// 0x00-0xBF First Come First Served +// 0xC0-0xFF IETF Review +type ExtendedCommunityAttrSubType uint8 + +const ( + EC_SUBTYPE_ROUTE_TARGET ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x00, 0x01, 0x02 + EC_SUBTYPE_ROUTE_ORIGIN ExtendedCommunityAttrSubType = 0x03 // EC_TYPE: 0x00, 0x01, 0x02 + EC_SUBTYPE_LINK_BANDWIDTH ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x40 + EC_SUBTYPE_GENERIC ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x02, 0x42 + EC_SUBTYPE_OSPF_DOMAIN_ID ExtendedCommunityAttrSubType = 0x05 // EC_TYPE: 0x00, 0x01, 0x02 + EC_SUBTYPE_OSPF_ROUTE_ID ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x01 + EC_SUBTYPE_BGP_DATA_COLLECTION ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x00, 0x02 + EC_SUBTYPE_SOURCE_AS ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x00, 0x02 + EC_SUBTYPE_L2VPN_ID ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x00, 0x01 + EC_SUBTYPE_VRF_ROUTE_IMPORT ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x01 + EC_SUBTYPE_CISCO_VPN_DISTINGUISHER ExtendedCommunityAttrSubType = 0x10 // EC_TYPE: 0x00, 0x01, 0x02 + + EC_SUBTYPE_OSPF_ROUTE_TYPE ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x03 + EC_SUBTYPE_COLOR ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x03 + EC_SUBTYPE_ENCAPSULATION ExtendedCommunityAttrSubType = 0x0C // EC_TYPE: 0x03 + EC_SUBTYPE_DEFAULT_GATEWAY ExtendedCommunityAttrSubType = 0x0D // EC_TYPE: 0x03 + + EC_SUBTYPE_ORIGIN_VALIDATION ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x43 + + EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x80 + EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x80 + EC_SUBTYPE_FLOWSPEC_REDIRECT ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x80 + EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x80 + EC_SUBTYPE_L2_INFO ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x80 + EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6 ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x80 + + EC_SUBTYPE_MAC_MOBILITY ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x06 + EC_SUBTYPE_ESI_LABEL ExtendedCommunityAttrSubType = 0x01 // EC_TYPE: 0x06 + EC_SUBTYPE_ES_IMPORT ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x06 + EC_SUBTYPE_ROUTER_MAC ExtendedCommunityAttrSubType = 0x03 // EC_TYPE: 0x06 + + EC_SUBTYPE_UUID_BASED_RT ExtendedCommunityAttrSubType = 0x11 +) + +type TunnelType uint16 + +const ( + TUNNEL_TYPE_L2TP3 TunnelType = 1 + TUNNEL_TYPE_GRE TunnelType = 2 + TUNNEL_TYPE_IP_IN_IP TunnelType = 7 + TUNNEL_TYPE_VXLAN TunnelType = 8 + TUNNEL_TYPE_NVGRE TunnelType = 9 + TUNNEL_TYPE_MPLS TunnelType = 10 + TUNNEL_TYPE_MPLS_IN_GRE TunnelType = 11 + TUNNEL_TYPE_VXLAN_GRE TunnelType = 12 + TUNNEL_TYPE_MPLS_IN_UDP TunnelType = 13 +) + +func (p TunnelType) String() string { + switch p { + case TUNNEL_TYPE_L2TP3: + return "l2tp3" + case TUNNEL_TYPE_GRE: + return "gre" + case TUNNEL_TYPE_IP_IN_IP: + return "ip-in-ip" + case TUNNEL_TYPE_VXLAN: + return "vxlan" + case TUNNEL_TYPE_NVGRE: + return "nvgre" + case TUNNEL_TYPE_MPLS: + return "mpls" + case TUNNEL_TYPE_MPLS_IN_GRE: + return "mpls-in-gre" + case TUNNEL_TYPE_VXLAN_GRE: + return "vxlan-gre" + case TUNNEL_TYPE_MPLS_IN_UDP: + return "mpls-in-udp" + default: + return fmt.Sprintf("TunnelType(%d)", uint8(p)) + } +} + +type PmsiTunnelType uint8 + +const ( + PMSI_TUNNEL_TYPE_NO_TUNNEL PmsiTunnelType = 0 + PMSI_TUNNEL_TYPE_RSVP_TE_P2MP PmsiTunnelType = 1 + PMSI_TUNNEL_TYPE_MLDP_P2MP PmsiTunnelType = 2 + PMSI_TUNNEL_TYPE_PIM_SSM_TREE PmsiTunnelType = 3 + PMSI_TUNNEL_TYPE_PIM_SM_TREE PmsiTunnelType = 4 + PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE PmsiTunnelType = 5 + PMSI_TUNNEL_TYPE_INGRESS_REPL PmsiTunnelType = 6 + PMSI_TUNNEL_TYPE_MLDP_MP2MP PmsiTunnelType = 7 +) + +func (p PmsiTunnelType) String() string { + switch p { + case PMSI_TUNNEL_TYPE_NO_TUNNEL: + return "no-tunnel" + case PMSI_TUNNEL_TYPE_RSVP_TE_P2MP: + return "rsvp-te-p2mp" + case PMSI_TUNNEL_TYPE_MLDP_P2MP: + return "mldp-p2mp" + case PMSI_TUNNEL_TYPE_PIM_SSM_TREE: + return "pim-ssm-tree" + case PMSI_TUNNEL_TYPE_PIM_SM_TREE: + return "pim-sm-tree" + case PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE: + return "bidir-pim-tree" + case PMSI_TUNNEL_TYPE_INGRESS_REPL: + return "ingress-repl" + case PMSI_TUNNEL_TYPE_MLDP_MP2MP: + return "mldp-mp2mp" + default: + return fmt.Sprintf("PmsiTunnelType(%d)", uint8(p)) + } +} + +type EncapSubTLVType uint8 + +const ( + ENCAP_SUBTLV_TYPE_ENCAPSULATION EncapSubTLVType = 1 + ENCAP_SUBTLV_TYPE_PROTOCOL EncapSubTLVType = 2 + ENCAP_SUBTLV_TYPE_COLOR EncapSubTLVType = 4 +) + +const ( + _ = iota + BGP_MSG_OPEN + BGP_MSG_UPDATE + BGP_MSG_NOTIFICATION + BGP_MSG_KEEPALIVE + BGP_MSG_ROUTE_REFRESH +) + +const ( + BGP_OPT_CAPABILITY = 2 +) + +type BGPCapabilityCode uint8 + +const ( + BGP_CAP_MULTIPROTOCOL BGPCapabilityCode = 1 + BGP_CAP_ROUTE_REFRESH BGPCapabilityCode = 2 + BGP_CAP_CARRYING_LABEL_INFO BGPCapabilityCode = 4 + BGP_CAP_EXTENDED_NEXTHOP BGPCapabilityCode = 5 + BGP_CAP_GRACEFUL_RESTART BGPCapabilityCode = 64 + BGP_CAP_FOUR_OCTET_AS_NUMBER BGPCapabilityCode = 65 + BGP_CAP_ADD_PATH BGPCapabilityCode = 69 + BGP_CAP_ENHANCED_ROUTE_REFRESH BGPCapabilityCode = 70 + BGP_CAP_LONG_LIVED_GRACEFUL_RESTART BGPCapabilityCode = 71 + BGP_CAP_ROUTE_REFRESH_CISCO BGPCapabilityCode = 128 +) + +var CapNameMap = map[BGPCapabilityCode]string{ + BGP_CAP_MULTIPROTOCOL: "multiprotocol", + BGP_CAP_ROUTE_REFRESH: "route-refresh", + BGP_CAP_CARRYING_LABEL_INFO: "carrying-label-info", + BGP_CAP_GRACEFUL_RESTART: "graceful-restart", + BGP_CAP_EXTENDED_NEXTHOP: "extended-nexthop", + BGP_CAP_FOUR_OCTET_AS_NUMBER: "4-octet-as", + BGP_CAP_ADD_PATH: "add-path", + BGP_CAP_ENHANCED_ROUTE_REFRESH: "enhanced-route-refresh", + BGP_CAP_ROUTE_REFRESH_CISCO: "cisco-route-refresh", + BGP_CAP_LONG_LIVED_GRACEFUL_RESTART: "long-lived-graceful-restart", +} + +func (c BGPCapabilityCode) String() string { + if n, y := CapNameMap[c]; y { + return n + } + return fmt.Sprintf("UnknownCapability(%d)", c) +} + +var ( + // Used parsing RouteDistinguisher + _regexpRouteDistinguisher = regexp.MustCompile(`^((\d+)\.(\d+)\.(\d+)\.(\d+)|((\d+)\.)?(\d+)|([\w]+:[\w:]*:[\w]+)):(\d+)$`) + + // Used for operator and value for the FlowSpec numeric type + // Example: + // re.FindStringSubmatch("&==80") + // >>> ["&==80" "&" "==" "80"] + _regexpFlowSpecNumericType = regexp.MustCompile(`(&?)(==|=|>|>=|<|<=|!|!=|=!)?(\d+|-\d|true|false)`) + + // - "=!" is used in the old style format of "tcp-flags" and "fragment". + // - The value field should be one of the followings: + // * Decimal value (e.g., 80) + // * Combination of the small letters, decimals, "-" and "+" + // (e.g., tcp, ipv4, is-fragment+first-fragment) + // * Capital letters (e.g., SA) + _regexpFlowSpecOperator = regexp.MustCompile(`&|=|>|<|!|[\w\-+]+`) + _regexpFlowSpecOperatorValue = regexp.MustCompile(`[\w\-+]+`) + + // Note: "(-*)" and "(.*)" catch the invalid flags + // Example: In this case, "Z" is unsupported flag type. + // re.FindStringSubmatch("&==-SZU") + // >>> ["&==-SZU" "&" "==" "-" "S" "ZU"] + _regexpFlowSpecTCPFlag = regexp.MustCompile("(&?)(==|=|!|!=|=!)?(-*)([FSRPAUCE]+)(.*)") + + // Note: "(.*)" catches the invalid flags + // re.FindStringSubmatch("&!=+first-fragment+last-fragment+invalid-fragment") + // >>> ["&!=+first-fragment+last-fragment+invalid-fragment" "&" "!=" "+first-fragment+last-fragment" "+last-fragment" "+" "last" "+invalid-fragment"] + _regexpFlowSpecFragment = regexp.MustCompile(`(&?)(==|=|!|!=|=!)?(((\+)?(dont|is|first|last|not-a)-fragment)+)(.*)`) + + // re.FindStringSubmatch("192.168.0.0/24") + // >>> ["192.168.0.0/24" "192.168.0.0" "/24" "24"] + // re.FindStringSubmatch("192.168.0.1") + // >>> ["192.168.0.1" "192.168.0.1" "" ""] + _regexpFindIPv4Prefix = regexp.MustCompile(`^([\d.]+)(/(\d{1,2}))?`) + + // re.FindStringSubmatch("2001:dB8::/64") + // >>> ["2001:dB8::/64" "2001:dB8::" "/64" "64" "" ""] + // re.FindStringSubmatch("2001:dB8::/64/8") + // >>> ["2001:dB8::/64/8" "2001:dB8::" "/64" "64" "/8" "8"] + // re.FindStringSubmatch("2001:dB8::1") + // >>> ["2001:dB8::1" "2001:dB8::1" "" "" "" ""] + _regexpFindIPv6Prefix = regexp.MustCompile(`^([a-fA-F\d:.]+)(/(\d{1,3}))?(/(\d{1,3}))?`) +) + +type ParameterCapabilityInterface interface { + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) + Len() int + Code() BGPCapabilityCode +} + +type DefaultParameterCapability struct { + CapCode BGPCapabilityCode `json:"code"` + CapLen uint8 `json:"-"` + CapValue []byte `json:"value,omitempty"` +} + +func (c *DefaultParameterCapability) Code() BGPCapabilityCode { + return c.CapCode +} + +func (c *DefaultParameterCapability) DecodeFromBytes(data []byte) error { + c.CapCode = BGPCapabilityCode(data[0]) + c.CapLen = data[1] + if len(data) < 2+int(c.CapLen) { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all OptionParameterCapability bytes available") + } + if c.CapLen > 0 { + c.CapValue = data[2 : 2+c.CapLen] + } + return nil +} + +func (c *DefaultParameterCapability) Serialize() ([]byte, error) { + c.CapLen = uint8(len(c.CapValue)) + buf := make([]byte, 2) + buf[0] = uint8(c.CapCode) + buf[1] = c.CapLen + buf = append(buf, c.CapValue...) + return buf, nil +} + +func (c *DefaultParameterCapability) Len() int { + return int(c.CapLen + 2) +} + +type CapMultiProtocol struct { + DefaultParameterCapability + CapValue RouteFamily +} + +func (c *CapMultiProtocol) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + if len(data) < 4 { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityMultiProtocol bytes available") + } + c.CapValue = AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[0:2]), data[3]) + return nil +} + +func (c *CapMultiProtocol) Serialize() ([]byte, error) { + buf := make([]byte, 4) + afi, safi := RouteFamilyToAfiSafi(c.CapValue) + binary.BigEndian.PutUint16(buf[0:], afi) + buf[3] = safi + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapMultiProtocol) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Value RouteFamily `json:"value"` + }{ + Code: c.Code(), + Value: c.CapValue, + }) +} + +func NewCapMultiProtocol(rf RouteFamily) *CapMultiProtocol { + return &CapMultiProtocol{ + DefaultParameterCapability{ + CapCode: BGP_CAP_MULTIPROTOCOL, + }, + rf, + } +} + +type CapRouteRefresh struct { + DefaultParameterCapability +} + +func NewCapRouteRefresh() *CapRouteRefresh { + return &CapRouteRefresh{ + DefaultParameterCapability{ + CapCode: BGP_CAP_ROUTE_REFRESH, + }, + } +} + +type CapCarryingLabelInfo struct { + DefaultParameterCapability +} + +func NewCapCarryingLabelInfo() *CapCarryingLabelInfo { + return &CapCarryingLabelInfo{ + DefaultParameterCapability{ + CapCode: BGP_CAP_CARRYING_LABEL_INFO, + }, + } +} + +type CapExtendedNexthopTuple struct { + NLRIAFI uint16 + NLRISAFI uint16 + NexthopAFI uint16 +} + +func (c *CapExtendedNexthopTuple) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + NLRIAddressFamily RouteFamily `json:"nlri_address_family"` + NexthopAddressFamily uint16 `json:"nexthop_address_family"` + }{ + NLRIAddressFamily: AfiSafiToRouteFamily(c.NLRIAFI, uint8(c.NLRISAFI)), + NexthopAddressFamily: c.NexthopAFI, + }) +} + +func NewCapExtendedNexthopTuple(af RouteFamily, nexthop uint16) *CapExtendedNexthopTuple { + afi, safi := RouteFamilyToAfiSafi(af) + return &CapExtendedNexthopTuple{ + NLRIAFI: afi, + NLRISAFI: uint16(safi), + NexthopAFI: nexthop, + } +} + +type CapExtendedNexthop struct { + DefaultParameterCapability + Tuples []*CapExtendedNexthopTuple +} + +func (c *CapExtendedNexthop) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + if len(data) < 6 { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityExtendedNexthop bytes available") + } + c.Tuples = []*CapExtendedNexthopTuple{} + for len(data) >= 6 { + t := &CapExtendedNexthopTuple{ + binary.BigEndian.Uint16(data[0:2]), + binary.BigEndian.Uint16(data[2:4]), + binary.BigEndian.Uint16(data[4:6]), + } + c.Tuples = append(c.Tuples, t) + data = data[6:] + } + return nil +} + +func (c *CapExtendedNexthop) Serialize() ([]byte, error) { + buf := make([]byte, len(c.Tuples)*6) + for i, t := range c.Tuples { + binary.BigEndian.PutUint16(buf[i*6:i*6+2], t.NLRIAFI) + binary.BigEndian.PutUint16(buf[i*6+2:i*6+4], t.NLRISAFI) + binary.BigEndian.PutUint16(buf[i*6+4:i*6+6], t.NexthopAFI) + } + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapExtendedNexthop) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Tuples []*CapExtendedNexthopTuple `json:"tuples"` + }{ + Code: c.Code(), + Tuples: c.Tuples, + }) +} + +func NewCapExtendedNexthop(tuples []*CapExtendedNexthopTuple) *CapExtendedNexthop { + return &CapExtendedNexthop{ + DefaultParameterCapability{ + CapCode: BGP_CAP_EXTENDED_NEXTHOP, + }, + tuples, + } +} + +type CapGracefulRestartTuple struct { + AFI uint16 + SAFI uint8 + Flags uint8 +} + +func (c *CapGracefulRestartTuple) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RouteFamily RouteFamily `json:"route_family"` + Flags uint8 `json:"flags"` + }{ + RouteFamily: AfiSafiToRouteFamily(c.AFI, c.SAFI), + Flags: c.Flags, + }) +} + +func NewCapGracefulRestartTuple(rf RouteFamily, forward bool) *CapGracefulRestartTuple { + afi, safi := RouteFamilyToAfiSafi(rf) + flags := 0 + if forward { + flags = 0x80 + } + return &CapGracefulRestartTuple{ + AFI: afi, + SAFI: safi, + Flags: uint8(flags), + } +} + +type CapGracefulRestart struct { + DefaultParameterCapability + Flags uint8 + Time uint16 + Tuples []*CapGracefulRestartTuple +} + +func (c *CapGracefulRestart) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + if len(data) < 2 { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityGracefulRestart bytes available") + } + restart := binary.BigEndian.Uint16(data[0:2]) + c.Flags = uint8(restart >> 12) + c.Time = restart & 0xfff + data = data[2:] + + valueLen := int(c.CapLen) - 2 + + if valueLen >= 4 && len(data) >= valueLen { + c.Tuples = make([]*CapGracefulRestartTuple, 0, valueLen/4) + + for i := valueLen; i >= 4; i -= 4 { + t := &CapGracefulRestartTuple{binary.BigEndian.Uint16(data[0:2]), + data[2], data[3]} + c.Tuples = append(c.Tuples, t) + data = data[4:] + } + } + return nil +} + +func (c *CapGracefulRestart) Serialize() ([]byte, error) { + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf[0:], uint16(c.Flags)<<12|c.Time) + for _, t := range c.Tuples { + tbuf := make([]byte, 4) + binary.BigEndian.PutUint16(tbuf[0:2], t.AFI) + tbuf[2] = t.SAFI + tbuf[3] = t.Flags + buf = append(buf, tbuf...) + } + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapGracefulRestart) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Flags uint8 `json:"flags"` + Time uint16 `json:"time"` + Tuples []*CapGracefulRestartTuple `json:"tuples"` + }{ + Code: c.Code(), + Flags: c.Flags, + Time: c.Time, + Tuples: c.Tuples, + }) +} + +func NewCapGracefulRestart(restarting, notification bool, time uint16, tuples []*CapGracefulRestartTuple) *CapGracefulRestart { + flags := 0 + if restarting { + flags = 0x08 + } + if notification { + flags |= 0x04 + } + return &CapGracefulRestart{ + DefaultParameterCapability: DefaultParameterCapability{ + CapCode: BGP_CAP_GRACEFUL_RESTART, + }, + Flags: uint8(flags), + Time: time, + Tuples: tuples, + } +} + +type CapFourOctetASNumber struct { + DefaultParameterCapability + CapValue uint32 +} + +func (c *CapFourOctetASNumber) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + if len(data) < 4 { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFourOctetASNumber bytes available") + } + c.CapValue = binary.BigEndian.Uint32(data[0:4]) + return nil +} + +func (c *CapFourOctetASNumber) Serialize() ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, c.CapValue) + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapFourOctetASNumber) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Value uint32 `json:"value"` + }{ + Code: c.Code(), + Value: c.CapValue, + }) +} + +func NewCapFourOctetASNumber(asnum uint32) *CapFourOctetASNumber { + return &CapFourOctetASNumber{ + DefaultParameterCapability{ + CapCode: BGP_CAP_FOUR_OCTET_AS_NUMBER, + }, + asnum, + } +} + +type BGPAddPathMode uint8 + +const ( + BGP_ADD_PATH_NONE BGPAddPathMode = iota + BGP_ADD_PATH_RECEIVE + BGP_ADD_PATH_SEND + BGP_ADD_PATH_BOTH +) + +func (m BGPAddPathMode) String() string { + switch m { + case BGP_ADD_PATH_NONE: + return "none" + case BGP_ADD_PATH_RECEIVE: + return "receive" + case BGP_ADD_PATH_SEND: + return "send" + case BGP_ADD_PATH_BOTH: + return "receive/send" + default: + return fmt.Sprintf("unknown(%d)", m) + } +} + +type CapAddPathTuple struct { + RouteFamily RouteFamily + Mode BGPAddPathMode +} + +func (t *CapAddPathTuple) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RouteFamily RouteFamily `json:"family"` + Mode uint8 `json:"mode"` + }{ + RouteFamily: t.RouteFamily, + Mode: uint8(t.Mode), + }) +} + +func NewCapAddPathTuple(family RouteFamily, mode BGPAddPathMode) *CapAddPathTuple { + return &CapAddPathTuple{ + RouteFamily: family, + Mode: mode, + } +} + +type CapAddPath struct { + DefaultParameterCapability + Tuples []*CapAddPathTuple +} + +func (c *CapAddPath) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + if len(data) < 4 { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityAddPath bytes available") + } + c.Tuples = []*CapAddPathTuple{} + for len(data) >= 4 { + t := &CapAddPathTuple{ + RouteFamily: AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[:2]), data[2]), + Mode: BGPAddPathMode(data[3]), + } + c.Tuples = append(c.Tuples, t) + data = data[4:] + } + return nil +} + +func (c *CapAddPath) Serialize() ([]byte, error) { + buf := make([]byte, len(c.Tuples)*4) + for i, t := range c.Tuples { + afi, safi := RouteFamilyToAfiSafi(t.RouteFamily) + binary.BigEndian.PutUint16(buf[i*4:i*4+2], afi) + buf[i*4+2] = safi + buf[i*4+3] = byte(t.Mode) + } + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapAddPath) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Tuples []*CapAddPathTuple `json:"tuples"` + }{ + Code: c.Code(), + Tuples: c.Tuples, + }) +} + +func NewCapAddPath(tuples []*CapAddPathTuple) *CapAddPath { + return &CapAddPath{ + DefaultParameterCapability: DefaultParameterCapability{ + CapCode: BGP_CAP_ADD_PATH, + }, + Tuples: tuples, + } +} + +type CapEnhancedRouteRefresh struct { + DefaultParameterCapability +} + +func NewCapEnhancedRouteRefresh() *CapEnhancedRouteRefresh { + return &CapEnhancedRouteRefresh{ + DefaultParameterCapability{ + CapCode: BGP_CAP_ENHANCED_ROUTE_REFRESH, + }, + } +} + +type CapRouteRefreshCisco struct { + DefaultParameterCapability +} + +func NewCapRouteRefreshCisco() *CapRouteRefreshCisco { + return &CapRouteRefreshCisco{ + DefaultParameterCapability{ + CapCode: BGP_CAP_ROUTE_REFRESH_CISCO, + }, + } +} + +type CapLongLivedGracefulRestartTuple struct { + AFI uint16 + SAFI uint8 + Flags uint8 + RestartTime uint32 +} + +func (c *CapLongLivedGracefulRestartTuple) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RouteFamily RouteFamily `json:"route_family"` + Flags uint8 `json:"flags"` + RestartTime uint32 `json:"restart_time"` + }{ + RouteFamily: AfiSafiToRouteFamily(c.AFI, c.SAFI), + Flags: c.Flags, + RestartTime: c.RestartTime, + }) +} + +func NewCapLongLivedGracefulRestartTuple(rf RouteFamily, forward bool, restartTime uint32) *CapLongLivedGracefulRestartTuple { + afi, safi := RouteFamilyToAfiSafi(rf) + flags := 0 + if forward { + flags = 0x80 + } + return &CapLongLivedGracefulRestartTuple{ + AFI: afi, + SAFI: safi, + Flags: uint8(flags), + RestartTime: restartTime, + } +} + +type CapLongLivedGracefulRestart struct { + DefaultParameterCapability + Tuples []*CapLongLivedGracefulRestartTuple +} + +func (c *CapLongLivedGracefulRestart) DecodeFromBytes(data []byte) error { + c.DefaultParameterCapability.DecodeFromBytes(data) + data = data[2:] + + valueLen := int(c.CapLen) + if valueLen%7 != 0 || len(data) < valueLen { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "invalid length of long lived graceful restart capablity") + } + for i := valueLen; i >= 7; i -= 7 { + t := &CapLongLivedGracefulRestartTuple{ + binary.BigEndian.Uint16(data), + data[2], + data[3], + uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]), + } + c.Tuples = append(c.Tuples, t) + data = data[7:] + } + return nil +} + +func (c *CapLongLivedGracefulRestart) Serialize() ([]byte, error) { + buf := make([]byte, 7*len(c.Tuples)) + for idx, t := range c.Tuples { + binary.BigEndian.PutUint16(buf[idx*7:], t.AFI) + buf[idx*7+2] = t.SAFI + buf[idx*7+3] = t.Flags + buf[idx*7+4] = uint8((t.RestartTime >> 16) & 0xff) + buf[idx*7+5] = uint8((t.RestartTime >> 8) & 0xff) + buf[idx*7+6] = uint8(t.RestartTime & 0xff) + } + c.DefaultParameterCapability.CapValue = buf + return c.DefaultParameterCapability.Serialize() +} + +func (c *CapLongLivedGracefulRestart) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Code BGPCapabilityCode `json:"code"` + Tuples []*CapLongLivedGracefulRestartTuple `json:"tuples"` + }{ + Code: c.Code(), + Tuples: c.Tuples, + }) +} + +func NewCapLongLivedGracefulRestart(tuples []*CapLongLivedGracefulRestartTuple) *CapLongLivedGracefulRestart { + return &CapLongLivedGracefulRestart{ + DefaultParameterCapability: DefaultParameterCapability{ + CapCode: BGP_CAP_LONG_LIVED_GRACEFUL_RESTART, + }, + Tuples: tuples, + } +} + +type CapUnknown struct { + DefaultParameterCapability +} + +func NewCapUnknown(code BGPCapabilityCode, value []byte) *CapUnknown { + return &CapUnknown{ + DefaultParameterCapability{ + CapCode: code, + CapValue: value, + }, + } +} + +func DecodeCapability(data []byte) (ParameterCapabilityInterface, error) { + if len(data) < 2 { + return nil, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all ParameterCapability bytes available") + } + var c ParameterCapabilityInterface + switch BGPCapabilityCode(data[0]) { + case BGP_CAP_MULTIPROTOCOL: + c = &CapMultiProtocol{} + case BGP_CAP_ROUTE_REFRESH: + c = &CapRouteRefresh{} + case BGP_CAP_CARRYING_LABEL_INFO: + c = &CapCarryingLabelInfo{} + case BGP_CAP_EXTENDED_NEXTHOP: + c = &CapExtendedNexthop{} + case BGP_CAP_GRACEFUL_RESTART: + c = &CapGracefulRestart{} + case BGP_CAP_FOUR_OCTET_AS_NUMBER: + c = &CapFourOctetASNumber{} + case BGP_CAP_ADD_PATH: + c = &CapAddPath{} + case BGP_CAP_ENHANCED_ROUTE_REFRESH: + c = &CapEnhancedRouteRefresh{} + case BGP_CAP_ROUTE_REFRESH_CISCO: + c = &CapRouteRefreshCisco{} + case BGP_CAP_LONG_LIVED_GRACEFUL_RESTART: + c = &CapLongLivedGracefulRestart{} + default: + c = &CapUnknown{} + } + err := c.DecodeFromBytes(data) + return c, err +} + +type OptionParameterInterface interface { + Serialize() ([]byte, error) +} + +type OptionParameterCapability struct { + ParamType uint8 + ParamLen uint8 + Capability []ParameterCapabilityInterface +} + +func (o *OptionParameterCapability) DecodeFromBytes(data []byte) error { + if uint8(len(data)) < o.ParamLen { + return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER, nil, "Not all OptionParameterCapability bytes available") + } + for len(data) >= 2 { + c, err := DecodeCapability(data) + if err != nil { + return err + } + o.Capability = append(o.Capability, c) + data = data[c.Len():] + } + return nil +} + +func (o *OptionParameterCapability) Serialize() ([]byte, error) { + buf := make([]byte, 2) + buf[0] = o.ParamType + for _, p := range o.Capability { + pbuf, err := p.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + } + o.ParamLen = uint8(len(buf) - 2) + buf[1] = o.ParamLen + return buf, nil +} + +func NewOptionParameterCapability(capability []ParameterCapabilityInterface) *OptionParameterCapability { + return &OptionParameterCapability{ + ParamType: BGP_OPT_CAPABILITY, + Capability: capability, + } +} + +type OptionParameterUnknown struct { + ParamType uint8 + ParamLen uint8 + Value []byte +} + +func (o *OptionParameterUnknown) Serialize() ([]byte, error) { + buf := make([]byte, 2) + buf[0] = o.ParamType + if o.ParamLen == 0 { + o.ParamLen = uint8(len(o.Value)) + } + buf[1] = o.ParamLen + return append(buf, o.Value...), nil +} + +type BGPOpen struct { + Version uint8 + MyAS uint16 + HoldTime uint16 + ID net.IP + OptParamLen uint8 + OptParams []OptionParameterInterface +} + +func (msg *BGPOpen) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if len(data) < 10 { + return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all BGP Open message bytes available") + } + msg.Version = data[0] + msg.MyAS = binary.BigEndian.Uint16(data[1:3]) + msg.HoldTime = binary.BigEndian.Uint16(data[3:5]) + msg.ID = net.IP(data[5:9]).To4() + msg.OptParamLen = data[9] + data = data[10:] + if len(data) < int(msg.OptParamLen) { + return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all BGP Open message bytes available") + } + + msg.OptParams = []OptionParameterInterface{} + for rest := msg.OptParamLen; rest > 0; { + if rest < 2 { + return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP Open message") + } + paramtype := data[0] + paramlen := data[1] + if rest < paramlen+2 { + return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP Open message") + } + rest -= paramlen + 2 + + if paramtype == BGP_OPT_CAPABILITY { + p := &OptionParameterCapability{} + p.ParamType = paramtype + p.ParamLen = paramlen + p.DecodeFromBytes(data[2 : 2+paramlen]) + msg.OptParams = append(msg.OptParams, p) + } else { + p := &OptionParameterUnknown{} + p.ParamType = paramtype + p.ParamLen = paramlen + p.Value = data[2 : 2+paramlen] + msg.OptParams = append(msg.OptParams, p) + } + data = data[2+paramlen:] + } + return nil +} + +func (msg *BGPOpen) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 10) + buf[0] = msg.Version + binary.BigEndian.PutUint16(buf[1:3], msg.MyAS) + binary.BigEndian.PutUint16(buf[3:5], msg.HoldTime) + copy(buf[5:9], msg.ID.To4()) + pbuf := make([]byte, 0) + for _, p := range msg.OptParams { + onepbuf, err := p.Serialize() + if err != nil { + return nil, err + } + pbuf = append(pbuf, onepbuf...) + } + msg.OptParamLen = uint8(len(pbuf)) + buf[9] = msg.OptParamLen + return append(buf, pbuf...), nil +} + +func NewBGPOpenMessage(myas uint16, holdtime uint16, id string, optparams []OptionParameterInterface) *BGPMessage { + return &BGPMessage{ + Header: BGPHeader{Type: BGP_MSG_OPEN}, + Body: &BGPOpen{4, myas, holdtime, net.ParseIP(id).To4(), 0, optparams}, + } +} + +type AddrPrefixInterface interface { + DecodeFromBytes([]byte, ...*MarshallingOption) error + Serialize(...*MarshallingOption) ([]byte, error) + AFI() uint16 + SAFI() uint8 + Len(...*MarshallingOption) int + String() string + MarshalJSON() ([]byte, error) + // Create a flat map to describe attributes and their + // values. This can be used to create structured outputs. + Flat() map[string]string + PathIdentifier() uint32 + SetPathIdentifier(uint32) + PathLocalIdentifier() uint32 + SetPathLocalIdentifier(uint32) +} + +func LabelString(nlri AddrPrefixInterface) string { + label := "" + switch n := nlri.(type) { + case *LabeledIPAddrPrefix: + label = n.Labels.String() + case *LabeledIPv6AddrPrefix: + label = n.Labels.String() + case *LabeledVPNIPAddrPrefix: + label = n.Labels.String() + case *LabeledVPNIPv6AddrPrefix: + label = n.Labels.String() + case *EVPNNLRI: + switch route := n.RouteTypeData.(type) { + case *EVPNEthernetAutoDiscoveryRoute: + label = fmt.Sprintf("[%d]", route.Label) + case *EVPNMacIPAdvertisementRoute: + var l []string + for _, i := range route.Labels { + l = append(l, strconv.Itoa(int(i))) + } + label = fmt.Sprintf("[%s]", strings.Join(l, ",")) + case *EVPNIPPrefixRoute: + label = fmt.Sprintf("[%d]", route.Label) + } + } + return label +} + +type PrefixDefault struct { + id uint32 + localId uint32 +} + +func (p *PrefixDefault) PathIdentifier() uint32 { + return p.id +} + +func (p *PrefixDefault) SetPathIdentifier(id uint32) { + p.id = id +} + +func (p *PrefixDefault) PathLocalIdentifier() uint32 { + return p.localId +} + +func (p *PrefixDefault) SetPathLocalIdentifier(id uint32) { + p.localId = id +} + +func (p *PrefixDefault) decodePathIdentifier(data []byte) ([]byte, error) { + if len(data) < 4 { + code := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + subcode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return nil, NewMessageError(code, subcode, nil, "prefix misses path identifier field") + } + p.SetPathIdentifier(binary.BigEndian.Uint32(data[:4])) + return data[4:], nil +} + +func (p *PrefixDefault) serializeIdentifier() ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, p.PathLocalIdentifier()) + return buf, nil +} + +type IPAddrPrefixDefault struct { + PrefixDefault + Length uint8 + Prefix net.IP +} + +func (r *IPAddrPrefixDefault) decodePrefix(data []byte, bitlen uint8, addrlen uint8) error { + bytelen := (int(bitlen) + 7) / 8 + if len(data) < bytelen { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "network bytes is short") + } + b := make([]byte, addrlen) + copy(b, data[:bytelen]) + // clear trailing bits in the last byte. rfc doesn't require + // this but some bgp implementations need this... + rem := bitlen % 8 + if rem != 0 { + mask := 0xff00 >> rem + lastByte := b[bytelen-1] & byte(mask) + b[bytelen-1] = lastByte + } + r.Prefix = b + return nil +} + +func (r *IPAddrPrefixDefault) serializePrefix(bitLen uint8) ([]byte, error) { + byteLen := (int(bitLen) + 7) / 8 + buf := make([]byte, byteLen) + copy(buf, r.Prefix) + return buf, nil +} + +func (r *IPAddrPrefixDefault) String() string { + return fmt.Sprintf("%s/%d", r.Prefix.String(), r.Length) +} + +func (r *IPAddrPrefixDefault) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Prefix string `json:"prefix"` + }{ + Prefix: r.String(), + }) +} + +type IPAddrPrefix struct { + IPAddrPrefixDefault + addrlen uint8 +} + +func (r *IPAddrPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if r.addrlen == 0 { + r.addrlen = 4 + } + f := RF_IPv4_UC + if r.addrlen == 16 { + f = RF_IPv6_UC + } + if IsAddPathEnabled(true, f, options) { + var err error + data, err = r.decodePathIdentifier(data) + if err != nil { + return err + } + } + if len(data) < 1 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "prefix misses length field") + } + r.Length = data[0] + return r.decodePrefix(data[1:], r.Length, r.addrlen) +} + +func (r *IPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) { + f := RF_IPv4_UC + if r.addrlen == 16 { + f = RF_IPv6_UC + } + var buf []byte + if IsAddPathEnabled(false, f, options) { + var err error + buf, err = r.serializeIdentifier() + if err != nil { + return nil, err + } + } + buf = append(buf, r.Length) + pbuf, err := r.serializePrefix(r.Length) + if err != nil { + return nil, err + } + return append(buf, pbuf...), nil +} + +func (r *IPAddrPrefix) AFI() uint16 { + return AFI_IP +} + +func (r *IPAddrPrefix) SAFI() uint8 { + return SAFI_UNICAST +} + +func (r *IPAddrPrefix) Len(options ...*MarshallingOption) int { + return 1 + ((int(r.Length) + 7) / 8) +} + +func NewIPAddrPrefix(length uint8, prefix string) *IPAddrPrefix { + p := &IPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length, + }, + 4, + } + p.IPAddrPrefixDefault.decodePrefix(net.ParseIP(prefix).To4(), length, 4) + return p +} + +func isIPv4MappedIPv6(ip net.IP) bool { + return len(ip) == net.IPv6len && ip.To4() != nil +} + +type IPv6AddrPrefix struct { + IPAddrPrefix +} + +func (r *IPv6AddrPrefix) AFI() uint16 { + return AFI_IP6 +} + +func (r *IPv6AddrPrefix) String() string { + prefix := r.Prefix.String() + if isIPv4MappedIPv6(r.Prefix) { + prefix = "::ffff:" + prefix + } + return fmt.Sprintf("%s/%d", prefix, r.Length) +} + +func NewIPv6AddrPrefix(length uint8, prefix string) *IPv6AddrPrefix { + p := &IPv6AddrPrefix{ + IPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length, + }, + 16, + }, + } + p.IPAddrPrefixDefault.decodePrefix(net.ParseIP(prefix), length, 16) + return p +} + +const ( + BGP_RD_TWO_OCTET_AS = iota + BGP_RD_IPV4_ADDRESS + BGP_RD_FOUR_OCTET_AS +) + +type RouteDistinguisherInterface interface { + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) + Len() int + String() string + MarshalJSON() ([]byte, error) +} + +type DefaultRouteDistinguisher struct { + Type uint16 +} + +func (rd *DefaultRouteDistinguisher) serialize(value []byte) ([]byte, error) { + buf := make([]byte, 8) + binary.BigEndian.PutUint16(buf, rd.Type) + copy(buf[2:], value) + return buf, nil +} + +func (rd *DefaultRouteDistinguisher) Len() int { + return 8 +} + +type RouteDistinguisherTwoOctetAS struct { + DefaultRouteDistinguisher + Admin uint16 + Assigned uint32 +} + +func (rd *RouteDistinguisherTwoOctetAS) DecodeFromBytes(data []byte) error { + rd.Admin = binary.BigEndian.Uint16(data[0:2]) + rd.Assigned = binary.BigEndian.Uint32(data[2:6]) + return nil +} + +func (rd *RouteDistinguisherTwoOctetAS) Serialize() ([]byte, error) { + buf := make([]byte, 6) + binary.BigEndian.PutUint16(buf[0:2], rd.Admin) + binary.BigEndian.PutUint32(buf[2:6], rd.Assigned) + return rd.serialize(buf) +} + +func (rd *RouteDistinguisherTwoOctetAS) String() string { + return fmt.Sprintf("%d:%d", rd.Admin, rd.Assigned) +} + +func (rd *RouteDistinguisherTwoOctetAS) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint16 `json:"type"` + Admin uint16 `json:"admin"` + Assigned uint32 `json:"assigned"` + }{ + Type: rd.Type, + Admin: rd.Admin, + Assigned: rd.Assigned, + }) +} + +func NewRouteDistinguisherTwoOctetAS(admin uint16, assigned uint32) *RouteDistinguisherTwoOctetAS { + return &RouteDistinguisherTwoOctetAS{ + DefaultRouteDistinguisher: DefaultRouteDistinguisher{ + Type: BGP_RD_TWO_OCTET_AS, + }, + Admin: admin, + Assigned: assigned, + } +} + +type RouteDistinguisherIPAddressAS struct { + DefaultRouteDistinguisher + Admin net.IP + Assigned uint16 +} + +func (rd *RouteDistinguisherIPAddressAS) DecodeFromBytes(data []byte) error { + rd.Admin = data[0:4] + rd.Assigned = binary.BigEndian.Uint16(data[4:6]) + return nil +} + +func (rd *RouteDistinguisherIPAddressAS) Serialize() ([]byte, error) { + buf := make([]byte, 6) + copy(buf[0:4], rd.Admin.To4()) + binary.BigEndian.PutUint16(buf[4:6], rd.Assigned) + return rd.serialize(buf) +} + +func (rd *RouteDistinguisherIPAddressAS) String() string { + return fmt.Sprintf("%s:%d", rd.Admin.String(), rd.Assigned) +} + +func (rd *RouteDistinguisherIPAddressAS) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint16 `json:"type"` + Admin string `json:"admin"` + Assigned uint16 `json:"assigned"` + }{ + Type: rd.Type, + Admin: rd.Admin.String(), + Assigned: rd.Assigned, + }) +} + +func NewRouteDistinguisherIPAddressAS(admin string, assigned uint16) *RouteDistinguisherIPAddressAS { + return &RouteDistinguisherIPAddressAS{ + DefaultRouteDistinguisher: DefaultRouteDistinguisher{ + Type: BGP_RD_IPV4_ADDRESS, + }, + Admin: net.ParseIP(admin).To4(), + Assigned: assigned, + } +} + +type RouteDistinguisherFourOctetAS struct { + DefaultRouteDistinguisher + Admin uint32 + Assigned uint16 +} + +func (rd *RouteDistinguisherFourOctetAS) DecodeFromBytes(data []byte) error { + rd.Admin = binary.BigEndian.Uint32(data[0:4]) + rd.Assigned = binary.BigEndian.Uint16(data[4:6]) + return nil +} + +func (rd *RouteDistinguisherFourOctetAS) Serialize() ([]byte, error) { + buf := make([]byte, 6) + binary.BigEndian.PutUint32(buf[0:4], rd.Admin) + binary.BigEndian.PutUint16(buf[4:6], rd.Assigned) + return rd.serialize(buf) +} + +func (rd *RouteDistinguisherFourOctetAS) String() string { + fst := rd.Admin >> 16 & 0xffff + snd := rd.Admin & 0xffff + return fmt.Sprintf("%d.%d:%d", fst, snd, rd.Assigned) +} + +func (rd *RouteDistinguisherFourOctetAS) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint16 `json:"type"` + Admin uint32 `json:"admin"` + Assigned uint16 `json:"assigned"` + }{ + Type: rd.Type, + Admin: rd.Admin, + Assigned: rd.Assigned, + }) +} + +func NewRouteDistinguisherFourOctetAS(admin uint32, assigned uint16) *RouteDistinguisherFourOctetAS { + return &RouteDistinguisherFourOctetAS{ + DefaultRouteDistinguisher: DefaultRouteDistinguisher{ + Type: BGP_RD_FOUR_OCTET_AS, + }, + Admin: admin, + Assigned: assigned, + } +} + +type RouteDistinguisherUnknown struct { + DefaultRouteDistinguisher + Value []byte +} + +func (rd *RouteDistinguisherUnknown) DecodeFromBytes(data []byte) error { + rd.Value = data[0:6] + return nil +} + +func (rd *RouteDistinguisherUnknown) Serialize() ([]byte, error) { + return rd.DefaultRouteDistinguisher.serialize(rd.Value) +} + +func (rd *RouteDistinguisherUnknown) String() string { + return fmt.Sprintf("%v", rd.Value) +} + +func (rd *RouteDistinguisherUnknown) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint16 `json:"type"` + Value []byte `json:"value"` + }{ + Type: rd.Type, + Value: rd.Value, + }) +} + +func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface { + typ := binary.BigEndian.Uint16(data[0:2]) + switch typ { + case BGP_RD_TWO_OCTET_AS: + return NewRouteDistinguisherTwoOctetAS(binary.BigEndian.Uint16(data[2:4]), binary.BigEndian.Uint32(data[4:8])) + case BGP_RD_IPV4_ADDRESS: + return NewRouteDistinguisherIPAddressAS(net.IP(data[2:6]).String(), binary.BigEndian.Uint16(data[6:8])) + case BGP_RD_FOUR_OCTET_AS: + return NewRouteDistinguisherFourOctetAS(binary.BigEndian.Uint32(data[2:6]), binary.BigEndian.Uint16(data[6:8])) + } + rd := &RouteDistinguisherUnknown{ + DefaultRouteDistinguisher: DefaultRouteDistinguisher{ + Type: typ, + }, + } + return rd +} + +func parseRdAndRt(input string) ([]string, error) { + elems := _regexpRouteDistinguisher.FindStringSubmatch(input) + if len(elems) != 11 { + return nil, fmt.Errorf("failed to parse") + } + return elems, nil +} + +func ParseRouteDistinguisher(rd string) (RouteDistinguisherInterface, error) { + elems, err := parseRdAndRt(rd) + if err != nil { + return nil, err + } + assigned, _ := strconv.ParseUint(elems[10], 10, 32) + ip := net.ParseIP(elems[1]) + switch { + case ip.To4() != nil: + return NewRouteDistinguisherIPAddressAS(elems[1], uint16(assigned)), nil + case elems[6] == "" && elems[7] == "": + asn, _ := strconv.ParseUint(elems[8], 10, 16) + return NewRouteDistinguisherTwoOctetAS(uint16(asn), uint32(assigned)), nil + default: + fst, _ := strconv.ParseUint(elems[7], 10, 16) + snd, _ := strconv.ParseUint(elems[8], 10, 16) + asn := fst<<16 | snd + return NewRouteDistinguisherFourOctetAS(uint32(asn), uint16(assigned)), nil + } +} + +// +// RFC3107 Carrying Label Information in BGP-4 +// +// 3. Carrying Label Mapping Information +// +// b) Label: +// +// The Label field carries one or more labels (that corresponds to +// the stack of labels [MPLS-ENCAPS(RFC3032)]). Each label is encoded as +// 4 octets, where the high-order 20 bits contain the label value, and +// the low order bit contains "Bottom of Stack" +// +// RFC3032 MPLS Label Stack Encoding +// +// 2.1. Encoding the Label Stack +// +// 0 1 2 3 +// 0 ... 9 0 ... 9 0 1 2 3 4 ... 9 0 1 +// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+ +// | Label | Exp |S| TTL | +// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+ +// + +// RFC3107 Carrying Label Information in BGP-4 +// +// 3. Carrying Label Mapping Information +// +// The label information carried (as part of NLRI) in the Withdrawn +// Routes field should be set to 0x800000. +const WITHDRAW_LABEL = uint32(0x800000) +const ZERO_LABEL = uint32(0) // some platform uses this as withdraw label + +type MPLSLabelStack struct { + Labels []uint32 +} + +func (l *MPLSLabelStack) DecodeFromBytes(data []byte) error { + labels := []uint32{} + foundBottom := false + for len(data) >= 3 { + label := uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]) + if label == WITHDRAW_LABEL || label == ZERO_LABEL { + l.Labels = []uint32{label} + return nil + } + data = data[3:] + labels = append(labels, label>>4) + if label&1 == 1 { + foundBottom = true + break + } + } + + if !foundBottom { + l.Labels = []uint32{} + return nil + } + l.Labels = labels + return nil +} + +func (l *MPLSLabelStack) Serialize() ([]byte, error) { + buf := make([]byte, len(l.Labels)*3) + for i, label := range l.Labels { + if label == WITHDRAW_LABEL { + return []byte{128, 0, 0}, nil + } + label = label << 4 + buf[i*3] = byte((label >> 16) & 0xff) + buf[i*3+1] = byte((label >> 8) & 0xff) + buf[i*3+2] = byte(label & 0xff) + } + buf[len(buf)-1] |= 1 + return buf, nil +} + +func (l *MPLSLabelStack) Len() int { return 3 * len(l.Labels) } + +func (l *MPLSLabelStack) String() string { + if len(l.Labels) == 0 { + return "" + } + s := bytes.NewBuffer(make([]byte, 0, 64)) + s.WriteString("[") + ss := make([]string, 0, len(l.Labels)) + for _, label := range l.Labels { + ss = append(ss, fmt.Sprintf("%d", label)) + } + s.WriteString(strings.Join(ss, ", ")) + s.WriteString("]") + return s.String() +} + +func NewMPLSLabelStack(labels ...uint32) *MPLSLabelStack { + if len(labels) == 0 { + labels = []uint32{0} + } + return &MPLSLabelStack{labels} +} + +func ParseMPLSLabelStack(buf string) (*MPLSLabelStack, error) { + elems := strings.Split(buf, "/") + labels := make([]uint32, 0, len(elems)) + if len(elems) == 0 { + goto ERR + } + for _, elem := range elems { + i, err := strconv.ParseUint(elem, 10, 32) + if err != nil { + goto ERR + } + if i > ((1 << 20) - 1) { + goto ERR + } + labels = append(labels, uint32(i)) + } + return NewMPLSLabelStack(labels...), nil +ERR: + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "invalid mpls label stack format") +} + +// +// RFC3107 Carrying Label Information in BGP-4 +// +// 3. Carrying Label Mapping Information +// +// +----------------------+ +// | Length (1 octet) | +// +----------------------+ +// | Label (3 octets) | +// +----------------------+ +// ....................... +// +----------------------+ +// | Prefix (variable) | +// +----------------------+ +// +// RFC4364 BGP/MPLS IP VPNs +// +// 4.3.4. How VPN-IPv4 NLRI Is Carried in BGP +// +// The labeled VPN-IPv4 NLRI itself is encoded as specified in +// [MPLS-BGP(RFC3107)], where the prefix consists of an 8-byte RD +// followed by an IPv4 prefix. +// + +type LabeledVPNIPAddrPrefix struct { + IPAddrPrefixDefault + Labels MPLSLabelStack + RD RouteDistinguisherInterface + addrlen uint8 +} + +func (l *LabeledVPNIPAddrPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + f := RF_IPv4_VPN + if l.addrlen == 16 { + f = RF_IPv6_VPN + } + if IsAddPathEnabled(true, f, options) { + var err error + data, err = l.decodePathIdentifier(data) + if err != nil { + return err + } + } + if len(data) < 1 { + return NewMessageError(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), nil, "prefix misses length field") + } + l.Length = uint8(data[0]) + data = data[1:] + l.Labels.DecodeFromBytes(data) + if int(l.Length)-8*(l.Labels.Len()) < 0 { + l.Labels.Labels = []uint32{} + } + data = data[l.Labels.Len():] + l.RD = GetRouteDistinguisher(data) + data = data[l.RD.Len():] + restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len()) + return l.decodePrefix(data, uint8(restbits), l.addrlen) +} + +func (l *LabeledVPNIPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) { + f := RF_IPv4_VPN + if l.addrlen == 16 { + f = RF_IPv6_VPN + } + var buf []byte + if IsAddPathEnabled(false, f, options) { + var err error + buf, err = l.serializeIdentifier() + if err != nil { + return nil, err + } + } + buf = append(buf, l.Length) + lbuf, err := l.Labels.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, lbuf...) + rbuf, err := l.RD.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, rbuf...) + restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len()) + pbuf, err := l.serializePrefix(uint8(restbits)) + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + return buf, nil +} + +func (l *LabeledVPNIPAddrPrefix) AFI() uint16 { + return AFI_IP +} + +func (l *LabeledVPNIPAddrPrefix) SAFI() uint8 { + return SAFI_MPLS_VPN +} + +func (l *LabeledVPNIPAddrPrefix) IPPrefixLen() uint8 { + return l.Length - 8*uint8(l.Labels.Len()+l.RD.Len()) +} + +func (l *LabeledVPNIPAddrPrefix) Len(options ...*MarshallingOption) int { + return 1 + l.Labels.Len() + l.RD.Len() + int((l.IPPrefixLen()+7)/8) +} + +func (l *LabeledVPNIPAddrPrefix) String() string { + return fmt.Sprintf("%s:%s", l.RD, l.IPPrefix()) +} + +func (l *LabeledVPNIPAddrPrefix) IPPrefix() string { + masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len())) + return fmt.Sprintf("%s/%d", l.IPAddrPrefixDefault.Prefix, masklen) +} + +func (l *LabeledVPNIPAddrPrefix) MarshalJSON() ([]byte, error) { + masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len())) + return json.Marshal(struct { + Prefix string `json:"prefix"` + Labels []uint32 `json:"labels"` + RD RouteDistinguisherInterface `json:"rd"` + }{ + Prefix: fmt.Sprintf("%s/%d", l.IPAddrPrefixDefault.Prefix, masklen), + Labels: l.Labels.Labels, + RD: l.RD, + }) +} + +func NewLabeledVPNIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPAddrPrefix { + rdlen := 0 + if rd != nil { + rdlen = rd.Len() + } + return &LabeledVPNIPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length + uint8(8*(label.Len()+rdlen)), + Prefix: net.ParseIP(prefix).To4(), + }, + label, + rd, + 4, + } +} + +type LabeledVPNIPv6AddrPrefix struct { + LabeledVPNIPAddrPrefix +} + +func (l *LabeledVPNIPv6AddrPrefix) AFI() uint16 { + return AFI_IP6 +} + +func NewLabeledVPNIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPv6AddrPrefix { + rdlen := 0 + if rd != nil { + rdlen = rd.Len() + } + return &LabeledVPNIPv6AddrPrefix{ + LabeledVPNIPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length + uint8(8*(label.Len()+rdlen)), + Prefix: net.ParseIP(prefix), + }, + label, + rd, + 16, + }, + } +} + +type LabeledIPAddrPrefix struct { + IPAddrPrefixDefault + Labels MPLSLabelStack + addrlen uint8 +} + +func (r *LabeledIPAddrPrefix) AFI() uint16 { + return AFI_IP +} + +func (r *LabeledIPAddrPrefix) SAFI() uint8 { + return SAFI_MPLS_LABEL +} + +func (l *LabeledIPAddrPrefix) IPPrefixLen() uint8 { + return l.Length - 8*uint8(l.Labels.Len()) +} + +func (l *LabeledIPAddrPrefix) Len(options ...*MarshallingOption) int { + return 1 + l.Labels.Len() + int((l.IPPrefixLen()+7)/8) +} + +func (l *LabeledIPAddrPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + f := RF_IPv4_MPLS + if l.addrlen == 16 { + f = RF_IPv6_MPLS + } + if IsAddPathEnabled(true, f, options) { + var err error + data, err = l.decodePathIdentifier(data) + if err != nil { + return err + } + } + l.Length = uint8(data[0]) + data = data[1:] + l.Labels.DecodeFromBytes(data) + + if int(l.Length)-8*(l.Labels.Len()) < 0 { + l.Labels.Labels = []uint32{} + } + restbits := int(l.Length) - 8*(l.Labels.Len()) + data = data[l.Labels.Len():] + return l.decodePrefix(data, uint8(restbits), l.addrlen) +} + +func (l *LabeledIPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) { + f := RF_IPv4_MPLS + if l.addrlen == 16 { + f = RF_IPv6_MPLS + } + var buf []byte + if IsAddPathEnabled(false, f, options) { + var err error + buf, err = l.serializeIdentifier() + if err != nil { + return nil, err + } + } + buf = append(buf, l.Length) + restbits := int(l.Length) - 8*(l.Labels.Len()) + lbuf, err := l.Labels.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, lbuf...) + pbuf, err := l.serializePrefix(uint8(restbits)) + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + return buf, nil +} + +func (l *LabeledIPAddrPrefix) String() string { + prefix := l.Prefix.String() + if isIPv4MappedIPv6(l.Prefix) { + prefix = "::ffff:" + prefix + } + return fmt.Sprintf("%s/%d", prefix, int(l.Length)-l.Labels.Len()*8) +} + +func (l *LabeledIPAddrPrefix) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Prefix string `json:"prefix"` + Labels []uint32 `json:"labels"` + }{ + Prefix: l.String(), + Labels: l.Labels.Labels, + }) +} + +func NewLabeledIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPAddrPrefix { + return &LabeledIPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length + uint8(label.Len()*8), + Prefix: net.ParseIP(prefix).To4(), + }, + label, + 4, + } +} + +type LabeledIPv6AddrPrefix struct { + LabeledIPAddrPrefix +} + +func (l *LabeledIPv6AddrPrefix) AFI() uint16 { + return AFI_IP6 +} + +func NewLabeledIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPv6AddrPrefix { + return &LabeledIPv6AddrPrefix{ + LabeledIPAddrPrefix{ + IPAddrPrefixDefault{ + Length: length + uint8(label.Len()*8), + Prefix: net.ParseIP(prefix), + }, + label, + 16, + }, + } +} + +type RouteTargetMembershipNLRI struct { + PrefixDefault + Length uint8 + AS uint32 + RouteTarget ExtendedCommunityInterface +} + +func (n *RouteTargetMembershipNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if IsAddPathEnabled(true, RF_RTC_UC, options) { + var err error + data, err = n.decodePathIdentifier(data) + if err != nil { + return err + } + } + if len(data) < 1 { + return NewMessageError(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), nil, "prefix misses length field") + } + n.Length = data[0] + data = data[1 : n.Length/8+1] + if len(data) == 0 { + return nil + } else if len(data) != 12 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all RouteTargetMembershipNLRI bytes available") + } + n.AS = binary.BigEndian.Uint32(data[0:4]) + rt, err := ParseExtended(data[4:]) + n.RouteTarget = rt + if err != nil { + return err + } + return nil +} + +func (n *RouteTargetMembershipNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + var buf []byte + if IsAddPathEnabled(false, RF_RTC_UC, options) { + var err error + buf, err = n.serializeIdentifier() + if err != nil { + return nil, err + } + } + if n.RouteTarget == nil { + return append(buf, 0), nil + } + offset := len(buf) + buf = append(buf, make([]byte, 5)...) + buf[offset] = 96 + binary.BigEndian.PutUint32(buf[offset+1:], n.AS) + ebuf, err := n.RouteTarget.Serialize() + if err != nil { + return nil, err + } + return append(buf, ebuf...), nil +} + +func (n *RouteTargetMembershipNLRI) AFI() uint16 { + return AFI_IP +} + +func (n *RouteTargetMembershipNLRI) SAFI() uint8 { + return SAFI_ROUTE_TARGET_CONSTRAINTS +} + +func (n *RouteTargetMembershipNLRI) Len(options ...*MarshallingOption) int { + if n.AS == 0 && n.RouteTarget == nil { + return 1 + } + return 13 +} + +func (n *RouteTargetMembershipNLRI) String() string { + target := "default" + if n.RouteTarget != nil { + target = n.RouteTarget.String() + } + return fmt.Sprintf("%d:%s", n.AS, target) +} + +func (n *RouteTargetMembershipNLRI) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Prefix string `json:"prefix"` + }{ + Prefix: n.String(), + }) +} + +func NewRouteTargetMembershipNLRI(as uint32, target ExtendedCommunityInterface) *RouteTargetMembershipNLRI { + l := 12 * 8 + if as == 0 && target == nil { + l = 1 + } + return &RouteTargetMembershipNLRI{ + Length: uint8(l), + AS: as, + RouteTarget: target, + } +} + +type ESIType uint8 + +const ( + ESI_ARBITRARY ESIType = iota + ESI_LACP + ESI_MSTP + ESI_MAC + ESI_ROUTERID + ESI_AS +) + +type EthernetSegmentIdentifier struct { + Type ESIType + Value []byte +} + +func (esi *EthernetSegmentIdentifier) DecodeFromBytes(data []byte) error { + esi.Type = ESIType(data[0]) + esi.Value = data[1:10] + switch esi.Type { + case ESI_LACP, ESI_MSTP, ESI_ROUTERID, ESI_AS: + if esi.Value[8] != 0x00 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("invalid %s. last octet must be 0x00 (0x%02x)", esi.Type.String(), esi.Value[8])) + } + } + return nil +} + +func (esi *EthernetSegmentIdentifier) Serialize() ([]byte, error) { + buf := make([]byte, 10) + buf[0] = uint8(esi.Type) + copy(buf[1:], esi.Value) + return buf, nil +} + +func isZeroBuf(buf []byte) bool { + for _, b := range buf { + if b != 0 { + return false + } + } + return true +} + +func (esi *EthernetSegmentIdentifier) String() string { + toHexArray := func(data []byte) string { + // Converts byte slice into the colon separated hex values and the + // number of elements are 9 at most (excluding Type field). + values := make([]string, 0, 9) + for _, v := range data { + values = append(values, fmt.Sprintf("%02x", v)) + } + return strings.Join(values, ":") + } + + s := bytes.NewBuffer(make([]byte, 0, 64)) + s.WriteString(fmt.Sprintf("%s | ", esi.Type.String())) + switch esi.Type { + case ESI_LACP: + s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String())) + s.WriteString(fmt.Sprintf("port key %d", binary.BigEndian.Uint16(esi.Value[6:8]))) + case ESI_MSTP: + s.WriteString(fmt.Sprintf("bridge mac %s, ", net.HardwareAddr(esi.Value[:6]).String())) + s.WriteString(fmt.Sprintf("priority %d", binary.BigEndian.Uint16(esi.Value[6:8]))) + case ESI_MAC: + s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String())) + s.WriteString(fmt.Sprintf("local discriminator %d", uint32(esi.Value[6])<<16|uint32(esi.Value[7])<<8|uint32(esi.Value[8]))) + case ESI_ROUTERID: + s.WriteString(fmt.Sprintf("router id %s, ", net.IP(esi.Value[:4]))) + s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8]))) + case ESI_AS: + s.WriteString(fmt.Sprintf("as %d, ", binary.BigEndian.Uint32(esi.Value[:4]))) + s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8]))) + case ESI_ARBITRARY: + if isZeroBuf(esi.Value) { + return "single-homed" + } + fallthrough + default: + s.WriteString(toHexArray(esi.Value)) + } + return s.String() +} + +// Decode Ethernet Segment Identifier (ESI) from string slice. +// +// The first element of args should be the Type field (e.g., "ARBITRARY", +// "arbitrary", "ESI_ARBITRARY" or "esi_arbitrary") and "single-homed" is +// the special keyword for all zeroed ESI. +// For the "ARBITRARY" Value field (Type 0), it should be the colon separated +// hex values and the number of elements should be 9 at most. +// e.g.) args := []string{"ARBITRARY", "11:22:33:44:55:66:77:88:99"} +// For the other types, the Value field format is the similar to the string +// format of ESI. +// e.g.) args := []string{"lacp", "aa:bb:cc:dd:ee:ff", "100"} +func ParseEthernetSegmentIdentifier(args []string) (EthernetSegmentIdentifier, error) { + esi := EthernetSegmentIdentifier{} + argLen := len(args) + if argLen == 0 || args[0] == "single-homed" { + return esi, nil + } + + typeStr := strings.TrimPrefix(strings.ToUpper(args[0]), "ESI_") + switch typeStr { + case "ARBITRARY": + esi.Type = ESI_ARBITRARY + case "LACP": + esi.Type = ESI_LACP + case "MSTP": + esi.Type = ESI_MSTP + case "MAC": + esi.Type = ESI_MAC + case "ROUTERID": + esi.Type = ESI_ROUTERID + case "AS": + esi.Type = ESI_AS + default: + typ, err := strconv.ParseUint(args[0], 10, 8) + if err != nil { + return esi, fmt.Errorf("invalid esi type: %s", args[0]) + } + esi.Type = ESIType(typ) + } + + invalidEsiValuesError := fmt.Errorf("invalid esi values for type %s: %s", esi.Type.String(), args[1:]) + esi.Value = make([]byte, 9) + switch esi.Type { + case ESI_LACP: + fallthrough + case ESI_MSTP: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // MAC + mac, err := net.ParseMAC(args[1]) + if err != nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:6], mac) + // Port Key or Bridge Priority + i, err := strconv.ParseUint(args[2], 10, 16) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint16(esi.Value[6:8], uint16(i)) + case ESI_MAC: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // MAC + mac, err := net.ParseMAC(args[1]) + if err != nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:6], mac) + // Local Discriminator + i, err := strconv.ParseUint(args[2], 10, 32) + if err != nil { + return esi, invalidEsiValuesError + } + iBuf := make([]byte, 4) + binary.BigEndian.PutUint32(iBuf, uint32(i)) + copy(esi.Value[6:9], iBuf[1:4]) + case ESI_ROUTERID: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // Router ID + ip := net.ParseIP(args[1]) + if ip == nil || ip.To4() == nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:4], ip.To4()) + // Local Discriminator + i, err := strconv.ParseUint(args[2], 10, 32) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i)) + case ESI_AS: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // AS + as, err := strconv.ParseUint(args[1], 10, 32) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[0:4], uint32(as)) + // Local Discriminator + i, err := strconv.ParseUint(args[2], 10, 32) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i)) + case ESI_ARBITRARY: + fallthrough + default: + if argLen < 2 { + // Assumes the Value field is omitted + break + } + values := make([]byte, 0, 9) + for _, e := range strings.SplitN(args[1], ":", 9) { + v, err := strconv.ParseUint(e, 16, 16) + if err != nil { + return esi, invalidEsiValuesError + } + values = append(values, byte(v)) + } + copy(esi.Value, values) + } + + return esi, nil +} + +// +// I-D bess-evpn-overlay-01 +// +// 5.1.3 Constructing EVPN BGP Routes +// +// For the balance of this memo, the MPLS label field will be +// referred to as the VNI/VSID field. The VNI/VSID field is used for +// both local and global VNIs/VSIDs, and for either case the entire 24- +// bit field is used to encode the VNI/VSID value. +// +// We can't use type MPLSLabelStack for EVPN NLRI, because EVPN NLRI's MPLS +// field can be filled with VXLAN VNI. In that case, we must avoid modifying +// bottom of stack bit. +// + +func labelDecode(data []byte) (uint32, error) { + if len(data) < 3 { + return 0, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all Label bytes available") + } + return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]), nil +} + +func labelSerialize(label uint32) ([]byte, error) { + if label > 0xffffff { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Out of range Label: %d", label)) + } + buf := make([]byte, 3) + buf[0] = byte((label >> 16) & 0xff) + buf[1] = byte((label >> 8) & 0xff) + buf[2] = byte(label & 0xff) + return buf, nil +} + +type EVPNEthernetAutoDiscoveryRoute struct { + RD RouteDistinguisherInterface + ESI EthernetSegmentIdentifier + ETag uint32 + Label uint32 +} + +func (er *EVPNEthernetAutoDiscoveryRoute) Len() int { + // RD(8) + ESI(10) + ETag(4) + Label(3) + return 25 +} + +func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error { + er.RD = GetRouteDistinguisher(data) + data = data[er.RD.Len():] + err := er.ESI.DecodeFromBytes(data) + if err != nil { + return err + } + data = data[10:] + er.ETag = binary.BigEndian.Uint32(data[0:4]) + data = data[4:] + if er.Label, err = labelDecode(data); err != nil { + return err + } + return nil +} + +func (er *EVPNEthernetAutoDiscoveryRoute) Serialize() ([]byte, error) { + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) + } + tbuf, err := er.ESI.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + + tbuf = make([]byte, 4) + binary.BigEndian.PutUint32(tbuf, er.ETag) + buf = append(buf, tbuf...) + + tbuf, err = labelSerialize(er.Label) + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + + return buf, nil +} + +func (er *EVPNEthernetAutoDiscoveryRoute) String() string { + // RFC7432: BGP MPLS-Based Ethernet VPN + // 7.1. Ethernet Auto-discovery Route + // For the purpose of BGP route key processing, only the Ethernet + // Segment Identifier and the Ethernet Tag ID are considered to be part + // of the prefix in the NLRI. The MPLS Label field is to be treated as + // a route attribute as opposed to being part of the route. + return fmt.Sprintf("[type:A-D][rd:%s][esi:%s][etag:%d]", er.RD, er.ESI.String(), er.ETag) +} + +func (er *EVPNEthernetAutoDiscoveryRoute) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + ESI string `json:"esi"` + Etag uint32 `json:"etag"` + Label uint32 `json:"label"` + }{ + RD: er.RD, + ESI: er.ESI.String(), + Etag: er.ETag, + Label: er.Label, + }) +} + +func (er *EVPNEthernetAutoDiscoveryRoute) rd() RouteDistinguisherInterface { + return er.RD +} + +func NewEVPNEthernetAutoDiscoveryRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, label uint32) *EVPNNLRI { + return NewEVPNNLRI(EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY, &EVPNEthernetAutoDiscoveryRoute{ + RD: rd, + ESI: esi, + ETag: etag, + Label: label, + }) +} + +type EVPNMacIPAdvertisementRoute struct { + RD RouteDistinguisherInterface + ESI EthernetSegmentIdentifier + ETag uint32 + MacAddressLength uint8 + MacAddress net.HardwareAddr + IPAddressLength uint8 + IPAddress net.IP + Labels []uint32 +} + +func (er *EVPNMacIPAdvertisementRoute) Len() int { + // RD(8) + ESI(10) + ETag(4) + MacAddressLength(1) + MacAddress(6) + // + IPAddressLength(1) + IPAddress(0, 4 or 16) + Labels(3 or 6) + return 30 + int(er.IPAddressLength)/8 + len(er.Labels)*3 +} + +func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error { + er.RD = GetRouteDistinguisher(data) + data = data[er.RD.Len():] + err := er.ESI.DecodeFromBytes(data) + if err != nil { + return err + } + data = data[10:] + er.ETag = binary.BigEndian.Uint32(data[0:4]) + data = data[4:] + er.MacAddressLength = data[0] + er.MacAddress = net.HardwareAddr(data[1:7]) + er.IPAddressLength = data[7] + data = data[8:] + if er.IPAddressLength == 32 || er.IPAddressLength == 128 { + er.IPAddress = net.IP(data[0:((er.IPAddressLength) / 8)]) + } else if er.IPAddressLength != 0 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength)) + } + data = data[(er.IPAddressLength / 8):] + var label uint32 + if label, err = labelDecode(data); err != nil { + return err + } + er.Labels = append(er.Labels, label) + data = data[3:] + if len(data) == 3 { + if label, err = labelDecode(data); err != nil { + return err + } + er.Labels = append(er.Labels, label) + } + return nil +} + +func (er *EVPNMacIPAdvertisementRoute) Serialize() ([]byte, error) { + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) + } + + tbuf, err := er.ESI.Serialize() + if err != nil { + return nil, err + } + + buf = append(buf, tbuf...) + tbuf = make([]byte, 4) + binary.BigEndian.PutUint32(tbuf, er.ETag) + buf = append(buf, tbuf...) + tbuf = make([]byte, 7) + tbuf[0] = er.MacAddressLength + copy(tbuf[1:], er.MacAddress) + buf = append(buf, tbuf...) + + buf = append(buf, er.IPAddressLength) + switch er.IPAddressLength { + case 0: + // IP address omitted + case 32: + buf = append(buf, []byte(er.IPAddress.To4())...) + case 128: + buf = append(buf, []byte(er.IPAddress.To16())...) + default: + return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength) + } + + for _, l := range er.Labels { + tbuf, err = labelSerialize(l) + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + } + return buf, nil +} + +func (er *EVPNMacIPAdvertisementRoute) String() string { + // RFC7432: BGP MPLS-Based Ethernet VPN + // 7.2. MAC/IP Advertisement Route + // For the purpose of BGP route key processing, only the Ethernet Tag + // ID, MAC Address Length, MAC Address, IP Address Length, and IP + // Address fields are considered to be part of the prefix in the NLRI. + // The Ethernet Segment Identifier, MPLS Label1, and MPLS Label2 fields + // are to be treated as route attributes as opposed to being part of the + // "route". + return fmt.Sprintf("[type:macadv][rd:%s][etag:%d][mac:%s][ip:%s]", er.RD, er.ETag, er.MacAddress, er.IPAddress) +} + +func (er *EVPNMacIPAdvertisementRoute) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + ESI string `json:"esi"` + Etag uint32 `json:"etag"` + MacAddress string `json:"mac"` + IPAddress string `json:"ip"` + Labels []uint32 `json:"labels"` + }{ + RD: er.RD, + ESI: er.ESI.String(), + Etag: er.ETag, + MacAddress: er.MacAddress.String(), + IPAddress: er.IPAddress.String(), + Labels: er.Labels, + }) +} + +func (er *EVPNMacIPAdvertisementRoute) rd() RouteDistinguisherInterface { + return er.RD +} + +func NewEVPNMacIPAdvertisementRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, macAddress string, ipAddress string, labels []uint32) *EVPNNLRI { + mac, _ := net.ParseMAC(macAddress) + var ipLen uint8 + ip := net.ParseIP(ipAddress) + if ip != nil { + if ipv4 := ip.To4(); ipv4 != nil { + ipLen = 32 + ip = ipv4 + } else { + ipLen = 128 + } + } + return NewEVPNNLRI(EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, &EVPNMacIPAdvertisementRoute{ + RD: rd, + ESI: esi, + ETag: etag, + MacAddressLength: 48, + MacAddress: mac, + IPAddressLength: ipLen, + IPAddress: ip, + Labels: labels, + }) +} + +type EVPNMulticastEthernetTagRoute struct { + RD RouteDistinguisherInterface + ETag uint32 + IPAddressLength uint8 + IPAddress net.IP +} + +func (er *EVPNMulticastEthernetTagRoute) Len() int { + // RD(8) + ETag(4) + IPAddressLength(1) + IPAddress(4 or 16) + return 13 + int(er.IPAddressLength)/8 +} + +func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error { + er.RD = GetRouteDistinguisher(data) + data = data[er.RD.Len():] + er.ETag = binary.BigEndian.Uint32(data[0:4]) + er.IPAddressLength = data[4] + data = data[5:] + if er.IPAddressLength == 32 || er.IPAddressLength == 128 { + er.IPAddress = net.IP(data[:er.IPAddressLength/8]) + } else { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength)) + } + return nil +} + +func (er *EVPNMulticastEthernetTagRoute) Serialize() ([]byte, error) { + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) + } + tbuf := make([]byte, 4) + binary.BigEndian.PutUint32(tbuf, er.ETag) + buf = append(buf, tbuf...) + buf = append(buf, er.IPAddressLength) + switch er.IPAddressLength { + case 32: + buf = append(buf, []byte(er.IPAddress.To4())...) + case 128: + buf = append(buf, []byte(er.IPAddress.To16())...) + default: + return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength) + } + if err != nil { + return nil, err + } + return buf, nil +} + +func (er *EVPNMulticastEthernetTagRoute) String() string { + // RFC7432: BGP MPLS-Based Ethernet VPN + // 7.3. Inclusive Multicast Ethernet Tag Route + // ...(snip)... For the purpose of BGP route key + // processing, only the Ethernet Tag ID, IP Address Length, and + // Originating Router's IP Address fields are considered to be part of + // the prefix in the NLRI. + return fmt.Sprintf("[type:multicast][rd:%s][etag:%d][ip:%s]", er.RD, er.ETag, er.IPAddress) +} + +func (er *EVPNMulticastEthernetTagRoute) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + Etag uint32 `json:"etag"` + IPAddress string `json:"ip"` + }{ + RD: er.RD, + Etag: er.ETag, + IPAddress: er.IPAddress.String(), + }) +} + +func (er *EVPNMulticastEthernetTagRoute) rd() RouteDistinguisherInterface { + return er.RD +} + +func NewEVPNMulticastEthernetTagRoute(rd RouteDistinguisherInterface, etag uint32, ipAddress string) *EVPNNLRI { + ipLen := uint8(32) + ip := net.ParseIP(ipAddress) + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } else { + ipLen = 128 + } + return NewEVPNNLRI(EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, &EVPNMulticastEthernetTagRoute{ + RD: rd, + ETag: etag, + IPAddressLength: ipLen, + IPAddress: ip, + }) +} + +type EVPNEthernetSegmentRoute struct { + RD RouteDistinguisherInterface + ESI EthernetSegmentIdentifier + IPAddressLength uint8 + IPAddress net.IP +} + +func (er *EVPNEthernetSegmentRoute) Len() int { + // RD(8) + ESI(10) + IPAddressLength(1) + IPAddress(4 or 16) + return 19 + int(er.IPAddressLength)/8 +} + +func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error { + er.RD = GetRouteDistinguisher(data) + data = data[er.RD.Len():] + er.ESI.DecodeFromBytes(data) + data = data[10:] + er.IPAddressLength = data[0] + data = data[1:] + if er.IPAddressLength == 32 || er.IPAddressLength == 128 { + er.IPAddress = net.IP(data[:er.IPAddressLength/8]) + } else { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength)) + } + return nil +} + +func (er *EVPNEthernetSegmentRoute) Serialize() ([]byte, error) { + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) + } + tbuf, err := er.ESI.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + buf = append(buf, er.IPAddressLength) + switch er.IPAddressLength { + case 32: + buf = append(buf, []byte(er.IPAddress.To4())...) + case 128: + buf = append(buf, []byte(er.IPAddress.To16())...) + default: + return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength) + } + return buf, nil +} + +func (er *EVPNEthernetSegmentRoute) String() string { + // RFC7432: BGP MPLS-Based Ethernet VPN + // 7.4. Ethernet Segment Route + // For the purpose of BGP route key processing, only the Ethernet + // Segment ID, IP Address Length, and Originating Router's IP Address + // fields are considered to be part of the prefix in the NLRI. + return fmt.Sprintf("[type:esi][rd:%s][esi:%s][ip:%s]", er.RD, er.ESI.String(), er.IPAddress) +} + +func (er *EVPNEthernetSegmentRoute) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + ESI string `json:"esi"` + IPAddress string `json:"ip"` + }{ + RD: er.RD, + ESI: er.ESI.String(), + IPAddress: er.IPAddress.String(), + }) +} + +func (er *EVPNEthernetSegmentRoute) rd() RouteDistinguisherInterface { + return er.RD +} + +func NewEVPNEthernetSegmentRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, ipAddress string) *EVPNNLRI { + ipLen := uint8(32) + ip := net.ParseIP(ipAddress) + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } else { + ipLen = 128 + } + return NewEVPNNLRI(EVPN_ETHERNET_SEGMENT_ROUTE, &EVPNEthernetSegmentRoute{ + RD: rd, + ESI: esi, + IPAddressLength: ipLen, + IPAddress: ip, + }) +} + +type EVPNIPPrefixRoute struct { + RD RouteDistinguisherInterface + ESI EthernetSegmentIdentifier + ETag uint32 + IPPrefixLength uint8 + IPPrefix net.IP + GWIPAddress net.IP + Label uint32 +} + +func (er *EVPNIPPrefixRoute) Len() int { + if er.IPPrefix.To4() != nil { + return 34 + } + return 58 +} + +func (er *EVPNIPPrefixRoute) DecodeFromBytes(data []byte) error { + addrLen := net.IPv4len + switch len(data) { + case 34: + // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + IPv4 Prefix(4) + GW IPv4(4) + Label(3) + case 58: + // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + IPv6 Prefix(16) + GW IPv6(16) + Label(3) + addrLen = net.IPv6len + default: + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPN IP Prefix Route bytes available") + } + + er.RD = GetRouteDistinguisher(data[0:8]) + + err := er.ESI.DecodeFromBytes(data[8:18]) + if err != nil { + return err + } + + er.ETag = binary.BigEndian.Uint32(data[18:22]) + + er.IPPrefixLength = data[22] + + offset := 23 // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + er.IPPrefix = data[offset : offset+addrLen] + offset += addrLen + + er.GWIPAddress = data[offset : offset+addrLen] + offset += addrLen + + if er.Label, err = labelDecode(data[offset : offset+3]); err != nil { + return err + } + //offset += 3 + + return nil +} + +func (er *EVPNIPPrefixRoute) Serialize() ([]byte, error) { + buf := make([]byte, 23) // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + + if er.RD != nil { + tbuf, err := er.RD.Serialize() + if err != nil { + return nil, err + } + copy(buf[0:8], tbuf) + } + + tbuf, err := er.ESI.Serialize() + if err != nil { + return nil, err + } + copy(buf[8:18], tbuf) + + binary.BigEndian.PutUint32(buf[18:22], er.ETag) + + buf[22] = er.IPPrefixLength + + if er.IPPrefix == nil { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("IP Prefix is nil")) + } else if er.IPPrefix.To4() != nil { + buf = append(buf, er.IPPrefix.To4()...) + if er.GWIPAddress == nil { + // draft-ietf-bess-evpn-prefix-advertisement: IP Prefix Advertisement in EVPN + // The GW IP field SHOULD be zero if it is not used as an Overlay Index. + er.GWIPAddress = net.IPv4zero + } + buf = append(buf, er.GWIPAddress.To4()...) + } else { + buf = append(buf, er.IPPrefix.To16()...) + if er.GWIPAddress == nil { + er.GWIPAddress = net.IPv6zero + } + buf = append(buf, er.GWIPAddress.To16()...) + } + + tbuf, err = labelSerialize(er.Label) + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + + return buf, nil +} + +func (er *EVPNIPPrefixRoute) String() string { + // draft-ietf-bess-evpn-prefix-advertisement: IP Prefix Advertisement in EVPN + // 3.1 IP Prefix Route Encoding + // The RD, Eth-Tag ID, IP Prefix Length and IP Prefix will be part of + // the route key used by BGP to compare routes. The rest of the fields + // will not be part of the route key. + return fmt.Sprintf("[type:Prefix][rd:%s][etag:%d][prefix:%s/%d]", er.RD, er.ETag, er.IPPrefix, er.IPPrefixLength) +} + +func (er *EVPNIPPrefixRoute) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + ESI string `json:"esi"` + Etag uint32 `json:"etag"` + Prefix string `json:"prefix"` + Gateway string `json:"gateway"` + Label uint32 `json:"label"` + }{ + RD: er.RD, + ESI: er.ESI.String(), + Etag: er.ETag, + Prefix: fmt.Sprintf("%s/%d", er.IPPrefix, er.IPPrefixLength), + Gateway: er.GWIPAddress.String(), + Label: er.Label, + }) +} + +func (er *EVPNIPPrefixRoute) rd() RouteDistinguisherInterface { + return er.RD +} + +func NewEVPNIPPrefixRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, ipPrefixLength uint8, ipPrefix string, gateway string, label uint32) *EVPNNLRI { + ip := net.ParseIP(ipPrefix) + gw := net.ParseIP(gateway) + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + gw = gw.To4() + } + return NewEVPNNLRI(EVPN_IP_PREFIX, &EVPNIPPrefixRoute{ + RD: rd, + ESI: esi, + ETag: etag, + IPPrefixLength: ipPrefixLength, + IPPrefix: ip, + GWIPAddress: gw, + Label: label, + }) +} + +type EVPNRouteTypeInterface interface { + Len() int + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) + String() string + rd() RouteDistinguisherInterface + MarshalJSON() ([]byte, error) +} + +func getEVPNRouteType(t uint8) (EVPNRouteTypeInterface, error) { + switch t { + case EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY: + return &EVPNEthernetAutoDiscoveryRoute{}, nil + case EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT: + return &EVPNMacIPAdvertisementRoute{}, nil + case EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG: + return &EVPNMulticastEthernetTagRoute{}, nil + case EVPN_ETHERNET_SEGMENT_ROUTE: + return &EVPNEthernetSegmentRoute{}, nil + case EVPN_IP_PREFIX: + return &EVPNIPPrefixRoute{}, nil + } + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Unknown EVPN Route type: %d", t)) +} + +const ( + EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY = 1 + EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT = 2 + EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG = 3 + EVPN_ETHERNET_SEGMENT_ROUTE = 4 + EVPN_IP_PREFIX = 5 +) + +type EVPNNLRI struct { + PrefixDefault + RouteType uint8 + Length uint8 + RouteTypeData EVPNRouteTypeInterface +} + +func (n *EVPNNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if IsAddPathEnabled(true, RF_EVPN, options) { + var err error + data, err = n.decodePathIdentifier(data) + if err != nil { + return err + } + } + if len(data) < 2 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPNNLRI bytes available") + } + n.RouteType = data[0] + n.Length = data[1] + data = data[2:] + if len(data) < int(n.Length) { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPNNLRI Route type bytes available") + } + r, err := getEVPNRouteType(n.RouteType) + if err != nil { + return err + } + n.RouteTypeData = r + return n.RouteTypeData.DecodeFromBytes(data[:n.Length]) +} + +func (n *EVPNNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + var buf []byte + if IsAddPathEnabled(false, RF_EVPN, options) { + var err error + buf, err = n.serializeIdentifier() + if err != nil { + return nil, err + } + } + offset := len(buf) + buf = append(buf, make([]byte, 2)...) + buf[offset] = n.RouteType + tbuf, err := n.RouteTypeData.Serialize() + buf[offset+1] = n.Length + if err != nil { + return nil, err + } + return append(buf, tbuf...), nil +} + +func (n *EVPNNLRI) AFI() uint16 { + return AFI_L2VPN +} + +func (n *EVPNNLRI) SAFI() uint8 { + return SAFI_EVPN +} + +func (n *EVPNNLRI) Len(options ...*MarshallingOption) int { + return int(n.Length) + 2 +} + +func (n *EVPNNLRI) String() string { + if n.RouteTypeData != nil { + return n.RouteTypeData.String() + } + return fmt.Sprintf("%d:%d", n.RouteType, n.Length) +} + +func (n *EVPNNLRI) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint8 `json:"type"` + Value EVPNRouteTypeInterface `json:"value"` + }{ + Type: n.RouteType, + Value: n.RouteTypeData, + }) +} + +func (n *EVPNNLRI) RD() RouteDistinguisherInterface { + return n.RouteTypeData.rd() +} + +func NewEVPNNLRI(routeType uint8, routeTypeData EVPNRouteTypeInterface) *EVPNNLRI { + var l uint8 + if routeTypeData != nil { + l = uint8(routeTypeData.Len()) + } + return &EVPNNLRI{ + RouteType: routeType, + Length: l, + RouteTypeData: routeTypeData, + } +} + +type EncapNLRI struct { + IPAddrPrefixDefault + addrlen uint8 +} + +func (n *EncapNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if n.addrlen == 0 { + n.addrlen = 4 + } + f := RF_IPv4_ENCAP + if n.addrlen == 16 { + f = RF_IPv6_ENCAP + } + if IsAddPathEnabled(true, f, options) { + var err error + data, err = n.decodePathIdentifier(data) + if err != nil { + return err + } + } + if len(data) < 4 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "prefix misses length field") + } + n.Length = data[0] + if n.addrlen == 0 { + n.addrlen = 4 + } + return n.decodePrefix(data[1:], n.Length, n.addrlen) +} + +func (n *EncapNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + var buf []byte + f := RF_IPv4_ENCAP + if n.addrlen == 16 { + f = RF_IPv6_ENCAP + } + if IsAddPathEnabled(false, f, options) { + var err error + buf, err = n.serializeIdentifier() + if err != nil { + return nil, err + } + } + if n.Prefix.To4() != nil { + buf = append(buf, net.IPv4len*8) + n.Prefix = n.Prefix.To4() + } else { + buf = append(buf, net.IPv6len*8) + } + n.Length = buf[len(buf)-1] + pbuf, err := n.serializePrefix(n.Length) + if err != nil { + return nil, err + } + return append(buf, pbuf...), nil +} + +func (n *EncapNLRI) String() string { + return n.Prefix.String() +} + +func (n *EncapNLRI) AFI() uint16 { + return AFI_IP +} + +func (n *EncapNLRI) SAFI() uint8 { + return SAFI_ENCAPSULATION +} + +func (n *EncapNLRI) Len(options ...*MarshallingOption) int { + return 1 + len(n.Prefix) +} + +func NewEncapNLRI(endpoint string) *EncapNLRI { + return &EncapNLRI{ + IPAddrPrefixDefault{Length: 32, Prefix: net.ParseIP(endpoint).To4()}, + 4, + } +} + +type Encapv6NLRI struct { + EncapNLRI +} + +func (n *Encapv6NLRI) AFI() uint16 { + return AFI_IP6 +} + +func NewEncapv6NLRI(endpoint string) *Encapv6NLRI { + return &Encapv6NLRI{ + EncapNLRI{ + IPAddrPrefixDefault{Length: 128, Prefix: net.ParseIP(endpoint)}, + 16, + }, + } +} + +type BGPFlowSpecType uint8 + +const ( + FLOW_SPEC_TYPE_UNKNOWN BGPFlowSpecType = iota + FLOW_SPEC_TYPE_DST_PREFIX + FLOW_SPEC_TYPE_SRC_PREFIX + FLOW_SPEC_TYPE_IP_PROTO + FLOW_SPEC_TYPE_PORT + FLOW_SPEC_TYPE_DST_PORT + FLOW_SPEC_TYPE_SRC_PORT + FLOW_SPEC_TYPE_ICMP_TYPE + FLOW_SPEC_TYPE_ICMP_CODE + FLOW_SPEC_TYPE_TCP_FLAG + FLOW_SPEC_TYPE_PKT_LEN + FLOW_SPEC_TYPE_DSCP + FLOW_SPEC_TYPE_FRAGMENT + FLOW_SPEC_TYPE_LABEL + FLOW_SPEC_TYPE_ETHERNET_TYPE // 14 + FLOW_SPEC_TYPE_SRC_MAC + FLOW_SPEC_TYPE_DST_MAC + FLOW_SPEC_TYPE_LLC_DSAP + FLOW_SPEC_TYPE_LLC_SSAP + FLOW_SPEC_TYPE_LLC_CONTROL + FLOW_SPEC_TYPE_SNAP + FLOW_SPEC_TYPE_VID + FLOW_SPEC_TYPE_COS + FLOW_SPEC_TYPE_INNER_VID + FLOW_SPEC_TYPE_INNER_COS +) + +var FlowSpecNameMap = map[BGPFlowSpecType]string{ + FLOW_SPEC_TYPE_UNKNOWN: "unknown", + FLOW_SPEC_TYPE_DST_PREFIX: "destination", + FLOW_SPEC_TYPE_SRC_PREFIX: "source", + FLOW_SPEC_TYPE_IP_PROTO: "protocol", + FLOW_SPEC_TYPE_PORT: "port", + FLOW_SPEC_TYPE_DST_PORT: "destination-port", + FLOW_SPEC_TYPE_SRC_PORT: "source-port", + FLOW_SPEC_TYPE_ICMP_TYPE: "icmp-type", + FLOW_SPEC_TYPE_ICMP_CODE: "icmp-code", + FLOW_SPEC_TYPE_TCP_FLAG: "tcp-flags", + FLOW_SPEC_TYPE_PKT_LEN: "packet-length", + FLOW_SPEC_TYPE_DSCP: "dscp", + FLOW_SPEC_TYPE_FRAGMENT: "fragment", + FLOW_SPEC_TYPE_LABEL: "label", + FLOW_SPEC_TYPE_ETHERNET_TYPE: "ether-type", + FLOW_SPEC_TYPE_SRC_MAC: "source-mac", + FLOW_SPEC_TYPE_DST_MAC: "destination-mac", + FLOW_SPEC_TYPE_LLC_DSAP: "llc-dsap", + FLOW_SPEC_TYPE_LLC_SSAP: "llc-ssap", + FLOW_SPEC_TYPE_LLC_CONTROL: "llc-control", + FLOW_SPEC_TYPE_SNAP: "snap", + FLOW_SPEC_TYPE_VID: "vid", + FLOW_SPEC_TYPE_COS: "cos", + FLOW_SPEC_TYPE_INNER_VID: "inner-vid", + FLOW_SPEC_TYPE_INNER_COS: "inner-cos", +} + +var FlowSpecValueMap = map[string]BGPFlowSpecType{ + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]: FLOW_SPEC_TYPE_DST_PREFIX, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]: FLOW_SPEC_TYPE_SRC_PREFIX, + FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO]: FLOW_SPEC_TYPE_IP_PROTO, + FlowSpecNameMap[FLOW_SPEC_TYPE_PORT]: FLOW_SPEC_TYPE_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PORT]: FLOW_SPEC_TYPE_DST_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PORT]: FLOW_SPEC_TYPE_SRC_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_TYPE]: FLOW_SPEC_TYPE_ICMP_TYPE, + FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_CODE]: FLOW_SPEC_TYPE_ICMP_CODE, + FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG]: FLOW_SPEC_TYPE_TCP_FLAG, + FlowSpecNameMap[FLOW_SPEC_TYPE_PKT_LEN]: FLOW_SPEC_TYPE_PKT_LEN, + FlowSpecNameMap[FLOW_SPEC_TYPE_DSCP]: FLOW_SPEC_TYPE_DSCP, + FlowSpecNameMap[FLOW_SPEC_TYPE_FRAGMENT]: FLOW_SPEC_TYPE_FRAGMENT, + FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]: FLOW_SPEC_TYPE_LABEL, + FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE]: FLOW_SPEC_TYPE_ETHERNET_TYPE, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]: FLOW_SPEC_TYPE_SRC_MAC, + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]: FLOW_SPEC_TYPE_DST_MAC, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_DSAP]: FLOW_SPEC_TYPE_LLC_DSAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_SSAP]: FLOW_SPEC_TYPE_LLC_SSAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_CONTROL]: FLOW_SPEC_TYPE_LLC_CONTROL, + FlowSpecNameMap[FLOW_SPEC_TYPE_SNAP]: FLOW_SPEC_TYPE_SNAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_VID]: FLOW_SPEC_TYPE_VID, + FlowSpecNameMap[FLOW_SPEC_TYPE_COS]: FLOW_SPEC_TYPE_COS, + FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_VID]: FLOW_SPEC_TYPE_INNER_VID, + FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_COS]: FLOW_SPEC_TYPE_INNER_COS, +} + +// Joins the given and args into a single string and normalize it. +// Example: +// args := []string{" & <=80", " tcp != udp ", " =! SA & =U! F", " = is-fragment+last-fragment"} +// fmt.Printf("%q", normalizeFlowSpecOpValues(args)) +// >>> ["<=80" "tcp" "!=udp" "=!SA" "&=U" "!F" "=is-fragment+last-fragment"] +func normalizeFlowSpecOpValues(args []string) []string { + // Extracts keywords from the given args. + sub := "" + subs := make([]string, 0) + for _, s := range _regexpFlowSpecOperator.FindAllString(strings.Join(args, " "), -1) { + sub += s + if _regexpFlowSpecOperatorValue.MatchString(s) { + subs = append(subs, sub) + sub = "" + } + } + + // RFC5575 says "It should be unset in the first operator byte of a + // sequence". + if len(subs) > 0 { + subs[0] = strings.TrimPrefix(subs[0], "&") + } + + return subs +} + +// Parses the FlowSpec numeric operator using the given submatch which should be +// the return value of func (*Regexp) FindStringSubmatch. +func parseFlowSpecNumericOperator(submatch []string) (operator uint8, err error) { + if submatch[1] == "&" { + operator = DEC_NUM_OP_AND + } + value, ok := DECNumOpValueMap[submatch[2]] + if !ok { + return 0, fmt.Errorf("invalid numeric operator: %s%s", submatch[1], submatch[2]) + } + operator |= uint8(value) + return operator, nil +} + +// Parses the pairs of operator and value for the FlowSpec numeric type. The +// given validationFunc is applied to evaluate whether the parsed value is +// valid or not (e.g., if exceeds range or not). +// Note: Each of the args should be formatted in single pair of operator and +// value before calling this function. +// e.g.) "&==100", ">=200" or "&<300" +func parseFlowSpecNumericOpValues(typ BGPFlowSpecType, args []string, validationFunc func(uint64) error) (FlowSpecComponentInterface, error) { + argsLen := len(args) + items := make([]*FlowSpecComponentItem, 0, argsLen) + for idx, arg := range args { + m := _regexpFlowSpecNumericType.FindStringSubmatch(arg) + if len(m) < 4 { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operator, err := parseFlowSpecNumericOperator(m) + if err != nil { + return nil, err + } + // "true" and "false" is operator, but here handles them as value. + var value uint64 + switch m[3] { + case "true", "false": + if idx != argsLen-1 { + return nil, fmt.Errorf("%s should be the last of each rule", m[3]) + } + operator = uint8(DECNumOpValueMap[m[3]]) + default: + if value, err = strconv.ParseUint(m[3], 10, 64); err != nil { + return nil, fmt.Errorf("invalid numeric value: %s", m[3]) + } + if err = validationFunc(value); err != nil { + return nil, err + } + } + items = append(items, NewFlowSpecComponentItem(operator, value)) + } + + // Marks end-of-list bit + items[argsLen-1].Op |= uint8(DEC_NUM_OP_END) + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecNumeric1ByteParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i uint64) error { + if i <= 0xff { // 1 byte + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecNumeric2BytesParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i uint64) error { + if i <= 0xffff { // 2 bytes + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +// Parses the FlowSpec bitmask operand using the given submatch which should be +// the return value of func (*Regexp) FindStringSubmatch. +func parseFlowSpecBitmaskOperand(submatch []string) (operand uint8, err error) { + if submatch[1] == "&" { + operand = BITMASK_FLAG_OP_AND + } + value, ok := BitmaskFlagOpValueMap[submatch[2]] + if !ok { + return 0, fmt.Errorf("invalid bitmask operand: %s%s", submatch[1], submatch[2]) + } + operand |= uint8(value) + return operand, nil +} + +func flowSpecPrefixParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args[0]: IP Prefix or IP Address (suppose prefix length is 32) + // args[1]: Offset in bit (IPv6 only) + // + // Example: + // - IPv4 Prefix + // args := []string{"192.168.0.0/24"} + // - IPv4 Address + // args := []string{"192.168.0.1"} + // - IPv6 Prefix + // args := []string{"2001:db8:1::/64"} + // - IPv6 Prefix with offset + // args := []string{"0:db8:1::/64/16"} + // args := []string{"0:db8:1::/64", "16"} + // - IPv6 Address + // args := []string{"2001:db8:1::1"} + // - IPv6 Address with offset + // args := []string{"0:db8:1::1", "16"} + afi, _ := RouteFamilyToAfiSafi(rf) + switch afi { + case AFI_IP: + if len(args) > 1 { + return nil, fmt.Errorf("cannot specify offset for ipv4 prefix") + } + invalidIPv4PrefixError := fmt.Errorf("invalid ipv4 prefix: %s", args[0]) + m := _regexpFindIPv4Prefix.FindStringSubmatch(args[0]) + if len(m) < 4 { + return nil, invalidIPv4PrefixError + } + prefix := net.ParseIP(m[1]) + if prefix.To4() == nil { + return nil, invalidIPv4PrefixError + } + var prefixLen uint64 = 32 + if m[3] != "" { + var err error + prefixLen, err = strconv.ParseUint(m[3], 10, 8) + if err != nil || prefixLen > 32 { + return nil, invalidIPv4PrefixError + } + } + switch typ { + case FLOW_SPEC_TYPE_DST_PREFIX: + return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(prefixLen), prefix.String())), nil + case FLOW_SPEC_TYPE_SRC_PREFIX: + return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(prefixLen), prefix.String())), nil + } + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) + case AFI_IP6: + if len(args) > 2 { + return nil, fmt.Errorf("invalid arguments for ipv6 prefix: %q", args) + } + invalidIPv6PrefixError := fmt.Errorf("invalid ipv6 prefix: %s", args[0]) + m := _regexpFindIPv6Prefix.FindStringSubmatch(args[0]) + if len(m) < 4 { + return nil, invalidIPv6PrefixError + } + prefix := net.ParseIP(m[1]) + if prefix.To16() == nil { + return nil, invalidIPv6PrefixError + } + var prefixLen uint64 = 128 + if m[3] != "" { + var err error + prefixLen, err = strconv.ParseUint(m[3], 10, 8) + if err != nil || prefixLen > 128 { + return nil, invalidIPv6PrefixError + } + } + var offset uint64 + if len(args) == 1 && m[5] != "" { + var err error + offset, err = strconv.ParseUint(m[5], 10, 8) + if err != nil || offset > 128 { + return nil, fmt.Errorf("invalid ipv6 prefix offset: %s", m[5]) + } + } else if len(args) == 2 { + if m[5] != "" { + return nil, fmt.Errorf("multiple ipv6 prefix offset arguments detected: %q", args) + } + var err error + offset, err = strconv.ParseUint(args[1], 10, 8) + if err != nil || offset > 128 { + return nil, fmt.Errorf("invalid ipv6 prefix offset: %s", args[1]) + } + } + switch typ { + case FLOW_SPEC_TYPE_DST_PREFIX: + return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(prefixLen), prefix.String()), uint8(offset)), nil + case FLOW_SPEC_TYPE_SRC_PREFIX: + return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(prefixLen), prefix.String()), uint8(offset)), nil + } + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) + } + return nil, fmt.Errorf("invalid address family: %s", rf.String()) +} + +func flowSpecIpProtoParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and IP protocol type + // + // Example: + // - TCP or UDP + // args := []string{"tcp", "==udp"} + // - Not TCP and not UDP + // args := []string{"!=tcp", "&!=udp"} + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range ProtocolNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) + } + args = strings.Split(s, " ") + + f := func(i uint64) error { + if i <= 0xff { // 1 byte + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecTcpFlagParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operand and TCP Flags + // + // Example: + // - SYN or SYN/ACK + // args := []string{"==S", "==SA"} + // - Not FIN and not URG + // args := []string{"!=F", "&!=U"} + args = normalizeFlowSpecOpValues(args) + + argsLen := len(args) + items := make([]*FlowSpecComponentItem, 0, argsLen) + + for _, arg := range args { + m := _regexpFlowSpecTCPFlag.FindStringSubmatch(arg) + if len(m) < 6 { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } else if mLast := m[len(m)-1]; mLast != "" || m[3] != "" { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operand, err := parseFlowSpecBitmaskOperand(m) + if err != nil { + return nil, err + } + var value uint64 + for flag, name := range TCPFlagNameMap { + if strings.Contains(m[4], name) { + value |= uint64(flag) + } + } + items = append(items, NewFlowSpecComponentItem(operand, value)) + } + + // Marks end-of-list bit + items[argsLen-1].Op |= BITMASK_FLAG_OP_END + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecDscpParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i uint64) error { + if i < 64 { // 6 bits + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecFragmentParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and Fragment flags + // + // Example: + // - is-fragment or last-fragment + // args := []string{"==is-fragment", "==last-fragment"} + // - is-fragment and last-fragment (exact match) + // args := []string{"==is-fragment+last-fragment"} + args = normalizeFlowSpecOpValues(args) + + argsLen := len(args) + items := make([]*FlowSpecComponentItem, 0, argsLen) + + for _, arg := range args { + m := _regexpFlowSpecFragment.FindStringSubmatch(arg) + if len(m) < 4 { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } else if mLast := m[len(m)-1]; mLast != "" { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operand, err := parseFlowSpecBitmaskOperand(m) + if err != nil { + return nil, err + } + var value uint64 + // Example: + // m[3] = "first-fragment+last-fragment" + for flag, name := range FragmentFlagNameMap { + if strings.Contains(m[3], name) { + value |= uint64(flag) + } + } + items = append(items, NewFlowSpecComponentItem(operand, value)) + } + + // Marks end-of-list bit + items[argsLen-1].Op |= BITMASK_FLAG_OP_END + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecLabelParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + afi, _ := RouteFamilyToAfiSafi(rf) + if afi == AFI_IP { + return nil, fmt.Errorf("%s is not supported for ipv4", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + + f := func(i uint64) error { + if i <= 0xfffff { // 20 bits + return nil + } + return fmt.Errorf("flow label range exceeded") + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecEtherTypeParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and Ether Types + // + // Example: + // - ARP or IPv4 + // args := []string{"==arp", "==ipv4"} + // - Not IPv4 and not IPv6 + // args := []string{"!=ipv4", "&!=ipv6"} + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range EthernetTypeNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) + } + args = strings.Split(s, " ") + + f := func(i uint64) error { + if i <= 0xffff { // 2 bytes + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecMacParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args[0]: MAC address + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + mac, err := net.ParseMAC(args[0]) + if err != nil { + return nil, fmt.Errorf("invalid mac address: %s", args[0]) + } + + switch typ { + case FLOW_SPEC_TYPE_DST_MAC: + return NewFlowSpecDestinationMac(mac), nil + case FLOW_SPEC_TYPE_SRC_MAC: + return NewFlowSpecSourceMac(mac), nil + } + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) +} + +func flowSpecLlcParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + return flowSpecNumeric1ByteParser(rf, typ, args) +} + +func flowSpecSnapParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + + f := func(i uint64) error { + if i <= 0xffffffffff { // 5 bytes + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecVlanIDParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range EthernetTypeNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) + } + args = strings.Split(s, " ") + + f := func(i uint64) error { + if i <= 4095 { // 12 bits + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +func flowSpecVlanCosParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range EthernetTypeNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) + } + args = strings.Split(s, " ") + + f := func(i uint64) error { + if i <= 7 { // 3 bits + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) + } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, BGPFlowSpecType, []string) (FlowSpecComponentInterface, error){ + FLOW_SPEC_TYPE_DST_PREFIX: flowSpecPrefixParser, + FLOW_SPEC_TYPE_SRC_PREFIX: flowSpecPrefixParser, + FLOW_SPEC_TYPE_IP_PROTO: flowSpecIpProtoParser, + FLOW_SPEC_TYPE_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_DST_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_SRC_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_ICMP_TYPE: flowSpecNumeric1ByteParser, + FLOW_SPEC_TYPE_ICMP_CODE: flowSpecNumeric1ByteParser, + FLOW_SPEC_TYPE_TCP_FLAG: flowSpecTcpFlagParser, + FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_DSCP: flowSpecDscpParser, + FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, + FLOW_SPEC_TYPE_LABEL: flowSpecLabelParser, + FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser, + FLOW_SPEC_TYPE_DST_MAC: flowSpecMacParser, + FLOW_SPEC_TYPE_SRC_MAC: flowSpecMacParser, + FLOW_SPEC_TYPE_LLC_DSAP: flowSpecLlcParser, + FLOW_SPEC_TYPE_LLC_SSAP: flowSpecLlcParser, + FLOW_SPEC_TYPE_LLC_CONTROL: flowSpecLlcParser, + FLOW_SPEC_TYPE_SNAP: flowSpecSnapParser, + FLOW_SPEC_TYPE_VID: flowSpecVlanIDParser, + FLOW_SPEC_TYPE_COS: flowSpecVlanCosParser, + FLOW_SPEC_TYPE_INNER_VID: flowSpecVlanIDParser, + FLOW_SPEC_TYPE_INNER_COS: flowSpecVlanCosParser, +} + +func extractFlowSpecArgs(args []string) map[BGPFlowSpecType][]string { + m := make(map[BGPFlowSpecType][]string, len(FlowSpecValueMap)) + var typ BGPFlowSpecType + for _, arg := range args { + if t, ok := FlowSpecValueMap[arg]; ok { + typ = t + m[typ] = make([]string, 0) + } else { + m[typ] = append(m[typ], arg) + } + } + return m +} + +func ParseFlowSpecComponents(rf RouteFamily, arg string) ([]FlowSpecComponentInterface, error) { + _, safi := RouteFamilyToAfiSafi(rf) + switch safi { + case SAFI_FLOW_SPEC_UNICAST, SAFI_FLOW_SPEC_VPN: + // Valid + default: + return nil, fmt.Errorf("invalid address family: %s", rf.String()) + } + + typeArgs := extractFlowSpecArgs(strings.Split(arg, " ")) + rules := make([]FlowSpecComponentInterface, 0, len(typeArgs)) + for typ, args := range typeArgs { + parser, ok := flowSpecParserMap[typ] + if !ok { + return nil, fmt.Errorf("unsupported traffic filtering rule type: %s", typ.String()) + } + if len(args) == 0 { + return nil, fmt.Errorf("specify traffic filtering rules for %s", typ.String()) + } + rule, err := parser(rf, typ, args) + if err != nil { + return nil, err + } + rules = append(rules, rule) + } + return rules, nil +} + +func (t BGPFlowSpecType) String() string { + name, ok := FlowSpecNameMap[t] + if !ok { + return fmt.Sprintf("%s(%d)", FlowSpecNameMap[FLOW_SPEC_TYPE_UNKNOWN], t) + } + return name +} + +type FlowSpecComponentInterface interface { + DecodeFromBytes([]byte, ...*MarshallingOption) error + Serialize(...*MarshallingOption) ([]byte, error) + Len(...*MarshallingOption) int + Type() BGPFlowSpecType + String() string +} + +type flowSpecPrefix struct { + Prefix AddrPrefixInterface + typ BGPFlowSpecType +} + +func (p *flowSpecPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + p.typ = BGPFlowSpecType(data[0]) + return p.Prefix.DecodeFromBytes(data[1:], options...) +} + +func (p *flowSpecPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := []byte{byte(p.Type())} + bbuf, err := p.Prefix.Serialize(options...) + if err != nil { + return nil, err + } + return append(buf, bbuf...), nil +} + +func (p *flowSpecPrefix) Len(options ...*MarshallingOption) int { + buf, _ := p.Serialize(options...) + return len(buf) +} + +func (p *flowSpecPrefix) Type() BGPFlowSpecType { + return p.typ +} + +func (p *flowSpecPrefix) String() string { + return fmt.Sprintf("[%s: %s]", p.Type(), p.Prefix.String()) +} + +func (p *flowSpecPrefix) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value AddrPrefixInterface `json:"value"` + }{ + Type: p.Type(), + Value: p.Prefix, + }) +} + +type flowSpecPrefix6 struct { + Prefix AddrPrefixInterface + Offset uint8 + typ BGPFlowSpecType +} + +// draft-ietf-idr-flow-spec-v6-06 +// +func (p *flowSpecPrefix6) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + p.typ = BGPFlowSpecType(data[0]) + p.Offset = data[2] + prefix := append([]byte{data[1]}, data[3:]...) + return p.Prefix.DecodeFromBytes(prefix, options...) +} + +func (p *flowSpecPrefix6) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := []byte{byte(p.Type())} + bbuf, err := p.Prefix.Serialize(options...) + if err != nil { + return nil, err + } + buf = append(buf, bbuf[0]) + buf = append(buf, p.Offset) + return append(buf, bbuf[1:]...), nil +} + +func (p *flowSpecPrefix6) Len(options ...*MarshallingOption) int { + buf, _ := p.Serialize(options...) + return len(buf) +} + +func (p *flowSpecPrefix6) Type() BGPFlowSpecType { + return p.typ +} + +func (p *flowSpecPrefix6) String() string { + return fmt.Sprintf("[%s: %s/%d]", p.Type(), p.Prefix.String(), p.Offset) +} + +func (p *flowSpecPrefix6) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value AddrPrefixInterface `json:"value"` + Offset uint8 `json:"offset"` + }{ + Type: p.Type(), + Value: p.Prefix, + Offset: p.Offset, + }) +} + +type FlowSpecDestinationPrefix struct { + flowSpecPrefix +} + +func NewFlowSpecDestinationPrefix(prefix AddrPrefixInterface) *FlowSpecDestinationPrefix { + return &FlowSpecDestinationPrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_DST_PREFIX}} +} + +type FlowSpecSourcePrefix struct { + flowSpecPrefix +} + +func NewFlowSpecSourcePrefix(prefix AddrPrefixInterface) *FlowSpecSourcePrefix { + return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}} +} + +type FlowSpecDestinationPrefix6 struct { + flowSpecPrefix6 +} + +func NewFlowSpecDestinationPrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecDestinationPrefix6 { + return &FlowSpecDestinationPrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_DST_PREFIX}} +} + +type FlowSpecSourcePrefix6 struct { + flowSpecPrefix6 +} + +func NewFlowSpecSourcePrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecSourcePrefix6 { + return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}} +} + +type flowSpecMac struct { + Mac net.HardwareAddr + typ BGPFlowSpecType +} + +func (p *flowSpecMac) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if len(data) < 2 || len(data) < 2+int(data[1]) { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all mac bits available") + } + p.typ = BGPFlowSpecType(data[0]) + p.Mac = net.HardwareAddr(data[2 : 2+int(data[1])]) + return nil +} + +func (p *flowSpecMac) Serialize(options ...*MarshallingOption) ([]byte, error) { + if len(p.Mac) == 0 { + return nil, fmt.Errorf("mac unset") + } + buf := []byte{byte(p.Type()), byte(len(p.Mac))} + return append(buf, []byte(p.Mac)...), nil +} + +func (p *flowSpecMac) Len(options ...*MarshallingOption) int { + return 2 + len(p.Mac) +} + +func (p *flowSpecMac) Type() BGPFlowSpecType { + return p.typ +} + +func (p *flowSpecMac) String() string { + return fmt.Sprintf("[%s: %s]", p.Type(), p.Mac.String()) +} + +func (p *flowSpecMac) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value string `json:"value"` + }{ + Type: p.Type(), + Value: p.Mac.String(), + }) +} + +type FlowSpecSourceMac struct { + flowSpecMac +} + +func NewFlowSpecSourceMac(mac net.HardwareAddr) *FlowSpecSourceMac { + return &FlowSpecSourceMac{flowSpecMac{Mac: mac, typ: FLOW_SPEC_TYPE_SRC_MAC}} +} + +type FlowSpecDestinationMac struct { + flowSpecMac +} + +func NewFlowSpecDestinationMac(mac net.HardwareAddr) *FlowSpecDestinationMac { + return &FlowSpecDestinationMac{flowSpecMac{Mac: mac, typ: FLOW_SPEC_TYPE_DST_MAC}} +} + +type FlowSpecComponentItem struct { + Op uint8 `json:"op"` + Value uint64 `json:"value"` +} + +func (v *FlowSpecComponentItem) Len() int { + return 1 << ((uint32(v.Op) >> 4) & 0x3) +} + +func (v *FlowSpecComponentItem) Serialize() ([]byte, error) { + if v.Op > math.MaxUint8 { + return nil, fmt.Errorf("invalid op size: %d", v.Op) + } + + order := uint32(math.Log2(float64(v.Len()))) + buf := make([]byte, 1+(1< 3 { + return nil + } + v.Op = uint8(uint32(v.Op) | order<<4) + return v +} + +type FlowSpecComponent struct { + Items []*FlowSpecComponentItem + typ BGPFlowSpecType +} + +func (p *FlowSpecComponent) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + p.typ = BGPFlowSpecType(data[0]) + data = data[1:] + p.Items = make([]*FlowSpecComponentItem, 0) + for { + if len(data) < 2 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available") + } + op := data[0] + end := op & 0x80 + l := 1 << ((op >> 4) & 0x3) // (min, max) = (1, 8) + v := make([]byte, 8) + copy(v[8-l:], data[1:1+l]) + i := binary.BigEndian.Uint64(v) + item := &FlowSpecComponentItem{op, i} + p.Items = append(p.Items, item) + if end > 0 { + break + } + data = data[1+l:] + } + return nil +} + +func (p *FlowSpecComponent) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := []byte{byte(p.Type())} + for _, v := range p.Items { + bbuf, err := v.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + } + return buf, nil +} + +func (p *FlowSpecComponent) Len(options ...*MarshallingOption) int { + l := 1 + for _, item := range p.Items { + l += item.Len() + 1 + } + return l +} + +func (p *FlowSpecComponent) Type() BGPFlowSpecType { + return p.typ +} + +func formatRaw(op uint8, value uint64) string { + return fmt.Sprintf("op:%b,value:%d", op, value) +} + +func formatNumeric(op uint8, value uint64) string { + cmpFlag := DECNumOp(op & 0x7) // lower 3 bits + if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE { + // Omit value field + return DECNumOp(op).String() + } + return fmt.Sprint(DECNumOp(op).String(), value) +} + +func formatProto(op uint8, value uint64) string { + cmpFlag := DECNumOp(op & 0x7) // lower 3 bits + if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE { + // Omit value field + return DECNumOp(op).String() + } + return fmt.Sprint(DECNumOp(op).String(), Protocol(value).String()) +} + +func formatTCPFlag(op uint8, value uint64) string { + return fmt.Sprint(BitmaskFlagOp(op).String(), TCPFlag(value).String()) +} + +func formatFragment(op uint8, value uint64) string { + return fmt.Sprint(BitmaskFlagOp(op).String(), FragmentFlag(value).String()) +} + +func formatEtherType(op uint8, value uint64) string { + cmpFlag := DECNumOp(op & 0x7) // lower 3 bits + if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE { + // Omit value field + return DECNumOp(op).String() + } + return fmt.Sprint(DECNumOp(op).String(), EthernetType(value).String()) +} + +var flowSpecFormatMap = map[BGPFlowSpecType]func(op uint8, value uint64) string{ + FLOW_SPEC_TYPE_UNKNOWN: formatRaw, + FLOW_SPEC_TYPE_IP_PROTO: formatProto, + FLOW_SPEC_TYPE_PORT: formatNumeric, + FLOW_SPEC_TYPE_DST_PORT: formatNumeric, + FLOW_SPEC_TYPE_SRC_PORT: formatNumeric, + FLOW_SPEC_TYPE_ICMP_TYPE: formatNumeric, + FLOW_SPEC_TYPE_ICMP_CODE: formatNumeric, + FLOW_SPEC_TYPE_TCP_FLAG: formatTCPFlag, + FLOW_SPEC_TYPE_PKT_LEN: formatNumeric, + FLOW_SPEC_TYPE_DSCP: formatNumeric, + FLOW_SPEC_TYPE_FRAGMENT: formatFragment, + FLOW_SPEC_TYPE_LABEL: formatNumeric, + FLOW_SPEC_TYPE_ETHERNET_TYPE: formatEtherType, + FLOW_SPEC_TYPE_LLC_DSAP: formatNumeric, + FLOW_SPEC_TYPE_LLC_SSAP: formatNumeric, + FLOW_SPEC_TYPE_LLC_CONTROL: formatNumeric, + FLOW_SPEC_TYPE_SNAP: formatNumeric, + FLOW_SPEC_TYPE_VID: formatNumeric, + FLOW_SPEC_TYPE_COS: formatNumeric, + FLOW_SPEC_TYPE_INNER_VID: formatNumeric, + FLOW_SPEC_TYPE_INNER_COS: formatNumeric, +} + +func (p *FlowSpecComponent) String() string { + f := flowSpecFormatMap[FLOW_SPEC_TYPE_UNKNOWN] + if _, ok := flowSpecFormatMap[p.typ]; ok { + f = flowSpecFormatMap[p.typ] + } + + items := make([]string, 0, len(p.Items)) + for _, i := range p.Items { + items = append(items, f(i.Op, i.Value)) + } + // Removes leading and tailing spaces + value := strings.TrimSpace(strings.Join(items, "")) + + return fmt.Sprintf("[%s: %s]", p.typ, value) +} + +func (p *FlowSpecComponent) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value []*FlowSpecComponentItem `json:"value"` + }{ + Type: p.Type(), + Value: p.Items, + }) +} + +func NewFlowSpecComponent(typ BGPFlowSpecType, items []*FlowSpecComponentItem) *FlowSpecComponent { + // Set end-of-list bit on the last item and unset them on the others. + for i, v := range items { + if i == len(items)-1 { + v.Op |= 0x80 + } else { + v.Op &^= 0x80 + } + + } + return &FlowSpecComponent{ + Items: items, + typ: typ, + } +} + +type FlowSpecUnknown struct { + Value []byte +} + +func (p *FlowSpecUnknown) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + p.Value = data + return nil +} + +func (p *FlowSpecUnknown) Serialize(options ...*MarshallingOption) ([]byte, error) { + return p.Value, nil +} + +func (p *FlowSpecUnknown) Len(options ...*MarshallingOption) int { + return len(p.Value) +} + +func (p *FlowSpecUnknown) Type() BGPFlowSpecType { + if len(p.Value) > 0 { + return BGPFlowSpecType(p.Value[0]) + } + return FLOW_SPEC_TYPE_UNKNOWN +} + +func (p *FlowSpecUnknown) String() string { + return fmt.Sprintf("[unknown:%v]", p.Value) +} + +func (p *FlowSpecUnknown) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value string `json:"value"` + }{ + Type: p.Type(), + Value: string(p.Value), + }) +} + +type FlowSpecNLRI struct { + PrefixDefault + Value []FlowSpecComponentInterface + rf RouteFamily + rd RouteDistinguisherInterface +} + +func (n *FlowSpecNLRI) AFI() uint16 { + afi, _ := RouteFamilyToAfiSafi(n.rf) + return afi +} + +func (n *FlowSpecNLRI) SAFI() uint8 { + _, safi := RouteFamilyToAfiSafi(n.rf) + return safi +} + +func (n *FlowSpecNLRI) RD() RouteDistinguisherInterface { + return n.rd +} + +func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte, options ...*MarshallingOption) error { + if IsAddPathEnabled(true, rf, options) { + var err error + data, err = n.decodePathIdentifier(data) + if err != nil { + return err + } + } + var length int + if (data[0]>>4) == 0xf && len(data) > 2 { + length = int(binary.BigEndian.Uint16(data[0:2])) + data = data[2:] + } else if len(data) > 1 { + length = int(data[0]) + data = data[1:] + } else { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available") + } + + n.rf = rf + + if n.SAFI() == SAFI_FLOW_SPEC_VPN { + if length < 8 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available") + } + n.rd = GetRouteDistinguisher(data[:8]) + data = data[8:] + length -= 8 + } + + for l := length; l > 0; { + if len(data) == 0 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available") + } + t := BGPFlowSpecType(data[0]) + var i FlowSpecComponentInterface + switch t { + case FLOW_SPEC_TYPE_DST_PREFIX: + switch { + case rf>>16 == AFI_IP: + i = NewFlowSpecDestinationPrefix(NewIPAddrPrefix(0, "")) + case rf>>16 == AFI_IP6: + i = NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(0, ""), 0) + default: + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", rf)) + } + case FLOW_SPEC_TYPE_SRC_PREFIX: + switch { + case rf>>16 == AFI_IP: + i = NewFlowSpecSourcePrefix(NewIPAddrPrefix(0, "")) + case rf>>16 == AFI_IP6: + i = NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(0, ""), 0) + default: + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", rf)) + } + case FLOW_SPEC_TYPE_SRC_MAC: + switch rf { + case RF_FS_L2_VPN: + i = NewFlowSpecSourceMac(nil) + default: + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", rf)) + } + case FLOW_SPEC_TYPE_DST_MAC: + switch rf { + case RF_FS_L2_VPN: + i = NewFlowSpecDestinationMac(nil) + default: + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", rf)) + } + case FLOW_SPEC_TYPE_IP_PROTO, FLOW_SPEC_TYPE_PORT, FLOW_SPEC_TYPE_DST_PORT, FLOW_SPEC_TYPE_SRC_PORT, + FLOW_SPEC_TYPE_ICMP_TYPE, FLOW_SPEC_TYPE_ICMP_CODE, FLOW_SPEC_TYPE_TCP_FLAG, FLOW_SPEC_TYPE_PKT_LEN, + FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT, FLOW_SPEC_TYPE_LABEL, FLOW_SPEC_TYPE_ETHERNET_TYPE, + FLOW_SPEC_TYPE_LLC_DSAP, FLOW_SPEC_TYPE_LLC_SSAP, FLOW_SPEC_TYPE_LLC_CONTROL, FLOW_SPEC_TYPE_SNAP, + FLOW_SPEC_TYPE_VID, FLOW_SPEC_TYPE_COS, FLOW_SPEC_TYPE_INNER_VID, FLOW_SPEC_TYPE_INNER_COS: + i = NewFlowSpecComponent(t, nil) + default: + i = &FlowSpecUnknown{} + } + + err := i.DecodeFromBytes(data, options...) + if err != nil { + i = &FlowSpecUnknown{data} + } + l -= i.Len(options...) + data = data[i.Len(options...):] + n.Value = append(n.Value, i) + } + + // Sort Traffic Filtering Rules in types order to avoid the unordered rules + // are determined different. + sort.SliceStable(n.Value, func(i, j int) bool { return n.Value[i].Type() < n.Value[j].Type() }) + + return nil +} + +func (n *FlowSpecNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 0, 32) + if n.SAFI() == SAFI_FLOW_SPEC_VPN { + if n.rd == nil { + return nil, fmt.Errorf("RD is nil") + } + b, err := n.rd.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, b...) + } + for _, v := range n.Value { + b, err := v.Serialize(options...) + if err != nil { + return nil, err + } + buf = append(buf, b...) + } + length := n.Len(options...) + if length > 0xfff { + return nil, fmt.Errorf("Too large: %d", length) + } else if length < 0xf0 { + length -= 1 + buf = append([]byte{byte(length)}, buf...) + } else { + length -= 2 + b := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(length)) + buf = append(b, buf...) + } + + if IsAddPathEnabled(false, n.rf, options) { + id, err := n.serializeIdentifier() + if err != nil { + return nil, err + } + return append(id, buf...), nil + } + return buf, nil +} + +func (n *FlowSpecNLRI) Len(options ...*MarshallingOption) int { + l := 0 + if n.SAFI() == SAFI_FLOW_SPEC_VPN { + l += n.RD().Len() + } + for _, v := range n.Value { + l += v.Len(options...) + } + if l < 0xf0 { + return l + 1 + } else { + return l + 2 + } +} + +func (n *FlowSpecNLRI) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + if n.SAFI() == SAFI_FLOW_SPEC_VPN { + buf.WriteString(fmt.Sprintf("[rd: %s]", n.rd)) + } + for _, v := range n.Value { + buf.WriteString(v.String()) + } + return buf.String() +} + +func (n *FlowSpecNLRI) MarshalJSON() ([]byte, error) { + if n.rd != nil { + return json.Marshal(struct { + RD RouteDistinguisherInterface `json:"rd"` + Value []FlowSpecComponentInterface `json:"value"` + }{ + RD: n.rd, + Value: n.Value, + }) + } + return json.Marshal(struct { + Value []FlowSpecComponentInterface `json:"value"` + }{ + Value: n.Value, + }) + +} + +// +// CompareFlowSpecNLRI(n, m) returns +// -1 when m has precedence +// 0 when n and m have same precedence +// 1 when n has precedence +// +func CompareFlowSpecNLRI(n, m *FlowSpecNLRI) (int, error) { + family := AfiSafiToRouteFamily(n.AFI(), n.SAFI()) + if family != AfiSafiToRouteFamily(m.AFI(), m.SAFI()) { + return 0, fmt.Errorf("address family mismatch") + } + longer := n.Value + shorter := m.Value + invert := 1 + if n.SAFI() == SAFI_FLOW_SPEC_VPN { + k, _ := n.Serialize() + l, _ := m.Serialize() + if result := bytes.Compare(k, l); result != 0 { + return result, nil + } + } + if len(n.Value) < len(m.Value) { + longer = m.Value + shorter = n.Value + invert = -1 + } + for idx, v := range longer { + if len(shorter) < idx+1 { + return invert, nil + } + w := shorter[idx] + if v.Type() < w.Type() { + return invert, nil + } else if v.Type() > w.Type() { + return invert * -1, nil + } else if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX || v.Type() == FLOW_SPEC_TYPE_SRC_PREFIX { + // RFC5575 5.1 + // + // For IP prefix values (IP destination and source prefix) precedence is + // given to the lowest IP value of the common prefix length; if the + // common prefix is equal, then the most specific prefix has precedence. + var p, q *IPAddrPrefixDefault + var pCommon, qCommon uint64 + if n.AFI() == AFI_IP { + if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX { + p = &v.(*FlowSpecDestinationPrefix).Prefix.(*IPAddrPrefix).IPAddrPrefixDefault + q = &w.(*FlowSpecDestinationPrefix).Prefix.(*IPAddrPrefix).IPAddrPrefixDefault + } else { + p = &v.(*FlowSpecSourcePrefix).Prefix.(*IPAddrPrefix).IPAddrPrefixDefault + q = &w.(*FlowSpecSourcePrefix).Prefix.(*IPAddrPrefix).IPAddrPrefixDefault + } + min := p.Length + if q.Length < p.Length { + min = q.Length + } + pCommon = uint64(binary.BigEndian.Uint32([]byte(p.Prefix.To4())) >> (32 - min)) + qCommon = uint64(binary.BigEndian.Uint32([]byte(q.Prefix.To4())) >> (32 - min)) + } else if n.AFI() == AFI_IP6 { + if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX { + p = &v.(*FlowSpecDestinationPrefix6).Prefix.(*IPv6AddrPrefix).IPAddrPrefixDefault + q = &w.(*FlowSpecDestinationPrefix6).Prefix.(*IPv6AddrPrefix).IPAddrPrefixDefault + } else { + p = &v.(*FlowSpecSourcePrefix6).Prefix.(*IPv6AddrPrefix).IPAddrPrefixDefault + q = &w.(*FlowSpecSourcePrefix6).Prefix.(*IPv6AddrPrefix).IPAddrPrefixDefault + } + min := uint(p.Length) + if q.Length < p.Length { + min = uint(q.Length) + } + var mask uint + if min-64 > 0 { + mask = min - 64 + } + pCommon = binary.BigEndian.Uint64([]byte(p.Prefix.To16()[:8])) >> mask + qCommon = binary.BigEndian.Uint64([]byte(q.Prefix.To16()[:8])) >> mask + if pCommon == qCommon && mask == 0 { + mask = 64 - min + pCommon = binary.BigEndian.Uint64([]byte(p.Prefix.To16()[8:])) >> mask + qCommon = binary.BigEndian.Uint64([]byte(q.Prefix.To16()[8:])) >> mask + } + } + + if pCommon < qCommon { + return invert, nil + } else if pCommon > qCommon { + return invert * -1, nil + } else if p.Length > q.Length { + return invert, nil + } else if p.Length < q.Length { + return invert * -1, nil + } + + } else { + // RFC5575 5.1 + // + // For all other component types, unless otherwise specified, the + // comparison is performed by comparing the component data as a binary + // string using the memcmp() function as defined by the ISO C standard. + // For strings of different lengths, the common prefix is compared. If + // equal, the longest string is considered to have higher precedence + // than the shorter one. + p, _ := v.Serialize() + q, _ := w.Serialize() + min := len(p) + if len(q) < len(p) { + min = len(q) + } + if result := bytes.Compare(p[:min], q[:min]); result < 0 { + return invert, nil + } else if result > 0 { + return invert * -1, nil + } else if len(p) > len(q) { + return invert, nil + } else if len(q) > len(p) { + return invert * -1, nil + } + } + } + return 0, nil +} + +type FlowSpecIPv4Unicast struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv4Unicast) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data, options...) +} + +func NewFlowSpecIPv4Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv4Unicast { + sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() }) + return &FlowSpecIPv4Unicast{ + FlowSpecNLRI: FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv4_UC, + }, + } +} + +type FlowSpecIPv4VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv4VPN) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data, options...) +} + +func NewFlowSpecIPv4VPN(rd RouteDistinguisherInterface, value []FlowSpecComponentInterface) *FlowSpecIPv4VPN { + sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() }) + return &FlowSpecIPv4VPN{ + FlowSpecNLRI: FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv4_VPN, + rd: rd, + }, + } +} + +type FlowSpecIPv6Unicast struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv6Unicast) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data, options...) +} + +func NewFlowSpecIPv6Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv6Unicast { + sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() }) + return &FlowSpecIPv6Unicast{ + FlowSpecNLRI: FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv6_UC, + }, + } +} + +type FlowSpecIPv6VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv6VPN) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data, options...) +} + +func NewFlowSpecIPv6VPN(rd RouteDistinguisherInterface, value []FlowSpecComponentInterface) *FlowSpecIPv6VPN { + sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() }) + return &FlowSpecIPv6VPN{ + FlowSpecNLRI: FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv6_VPN, + rd: rd, + }, + } +} + +type FlowSpecL2VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecL2VPN) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func NewFlowSpecL2VPN(rd RouteDistinguisherInterface, value []FlowSpecComponentInterface) *FlowSpecL2VPN { + sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() }) + return &FlowSpecL2VPN{ + FlowSpecNLRI: FlowSpecNLRI{ + Value: value, + rf: RF_FS_L2_VPN, + rd: rd, + }, + } +} + +type OpaqueNLRI struct { + PrefixDefault + Length uint16 + Key []byte + Value []byte +} + +func (n *OpaqueNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + if len(data) < 2 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all OpaqueNLRI bytes available") + } + if IsAddPathEnabled(true, RF_OPAQUE, options) { + var err error + data, err = n.decodePathIdentifier(data) + if err != nil { + return err + } + } + n.Length = binary.BigEndian.Uint16(data[0:2]) + if len(data)-2 < int(n.Length) { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all OpaqueNLRI bytes available") + } + n.Key = data[2 : 2+n.Length] + n.Value = data[2+n.Length:] + return nil +} + +func (n *OpaqueNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + if len(n.Key) > math.MaxUint16 { + return nil, fmt.Errorf("Key length too big") + } + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(len(n.Key))) + buf = append(buf, n.Key...) + buf = append(buf, n.Value...) + if IsAddPathEnabled(false, RF_OPAQUE, options) { + id, err := n.serializeIdentifier() + if err != nil { + return nil, err + } + return append(id, buf...), nil + } + return buf, nil +} + +func (n *OpaqueNLRI) AFI() uint16 { + return AFI_OPAQUE +} + +func (n *OpaqueNLRI) SAFI() uint8 { + return SAFI_KEY_VALUE +} + +func (n *OpaqueNLRI) Len(options ...*MarshallingOption) int { + return 2 + len(n.Key) + len(n.Value) +} + +func (n *OpaqueNLRI) String() string { + return fmt.Sprintf("%s", n.Key) +} + +func (n *OpaqueNLRI) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Key string `json:"key"` + Value string `json:"value"` + }{ + Key: string(n.Key), + Value: string(n.Value), + }) +} + +func NewOpaqueNLRI(key, value []byte) *OpaqueNLRI { + return &OpaqueNLRI{ + Key: key, + Value: value, + } +} + +func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily { + return RouteFamily(int(afi)<<16 | int(safi)) +} + +func RouteFamilyToAfiSafi(rf RouteFamily) (uint16, uint8) { + return uint16(int(rf) >> 16), uint8(int(rf) & 0xff) +} + +type RouteFamily int + +func (f RouteFamily) String() string { + if n, y := AddressFamilyNameMap[f]; y { + return n + } + return fmt.Sprintf("UnknownFamily(%d)", f) +} + +const ( + RF_IPv4_UC RouteFamily = AFI_IP<<16 | SAFI_UNICAST + RF_IPv6_UC RouteFamily = AFI_IP6<<16 | SAFI_UNICAST + RF_IPv4_MC RouteFamily = AFI_IP<<16 | SAFI_MULTICAST + RF_IPv6_MC RouteFamily = AFI_IP6<<16 | SAFI_MULTICAST + RF_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN + RF_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN + RF_IPv4_VPN_MC RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN_MULTICAST + RF_IPv6_VPN_MC RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN_MULTICAST + RF_IPv4_MPLS RouteFamily = AFI_IP<<16 | SAFI_MPLS_LABEL + RF_IPv6_MPLS RouteFamily = AFI_IP6<<16 | SAFI_MPLS_LABEL + RF_VPLS RouteFamily = AFI_L2VPN<<16 | SAFI_VPLS + RF_EVPN RouteFamily = AFI_L2VPN<<16 | SAFI_EVPN + RF_RTC_UC RouteFamily = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRAINTS + RF_IPv4_ENCAP RouteFamily = AFI_IP<<16 | SAFI_ENCAPSULATION + RF_IPv6_ENCAP RouteFamily = AFI_IP6<<16 | SAFI_ENCAPSULATION + RF_FS_IPv4_UC RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST + RF_FS_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_VPN + RF_FS_IPv6_UC RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_UNICAST + RF_FS_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN + RF_FS_L2_VPN RouteFamily = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN + RF_OPAQUE RouteFamily = AFI_OPAQUE<<16 | SAFI_KEY_VALUE +) + +var AddressFamilyNameMap = map[RouteFamily]string{ + RF_IPv4_UC: "ipv4-unicast", + RF_IPv6_UC: "ipv6-unicast", + RF_IPv4_MC: "ipv4-multicast", + RF_IPv6_MC: "ipv6-multicast", + RF_IPv4_MPLS: "ipv4-labelled-unicast", + RF_IPv6_MPLS: "ipv6-labelled-unicast", + RF_IPv4_VPN: "l3vpn-ipv4-unicast", + RF_IPv6_VPN: "l3vpn-ipv6-unicast", + RF_IPv4_VPN_MC: "l3vpn-ipv4-multicast", + RF_IPv6_VPN_MC: "l3vpn-ipv6-multicast", + RF_VPLS: "l2vpn-vpls", + RF_EVPN: "l2vpn-evpn", + RF_RTC_UC: "rtc", + RF_IPv4_ENCAP: "ipv4-encap", + RF_IPv6_ENCAP: "ipv6-encap", + RF_FS_IPv4_UC: "ipv4-flowspec", + RF_FS_IPv4_VPN: "l3vpn-ipv4-flowspec", + RF_FS_IPv6_UC: "ipv6-flowspec", + RF_FS_IPv6_VPN: "l3vpn-ipv6-flowspec", + RF_FS_L2_VPN: "l2vpn-flowspec", + RF_OPAQUE: "opaque", +} + +var AddressFamilyValueMap = map[string]RouteFamily{ + AddressFamilyNameMap[RF_IPv4_UC]: RF_IPv4_UC, + AddressFamilyNameMap[RF_IPv6_UC]: RF_IPv6_UC, + AddressFamilyNameMap[RF_IPv4_MC]: RF_IPv4_MC, + AddressFamilyNameMap[RF_IPv6_MC]: RF_IPv6_MC, + AddressFamilyNameMap[RF_IPv4_MPLS]: RF_IPv4_MPLS, + AddressFamilyNameMap[RF_IPv6_MPLS]: RF_IPv6_MPLS, + AddressFamilyNameMap[RF_IPv4_VPN]: RF_IPv4_VPN, + AddressFamilyNameMap[RF_IPv6_VPN]: RF_IPv6_VPN, + AddressFamilyNameMap[RF_IPv4_VPN_MC]: RF_IPv4_VPN_MC, + AddressFamilyNameMap[RF_IPv6_VPN_MC]: RF_IPv6_VPN_MC, + AddressFamilyNameMap[RF_VPLS]: RF_VPLS, + AddressFamilyNameMap[RF_EVPN]: RF_EVPN, + AddressFamilyNameMap[RF_RTC_UC]: RF_RTC_UC, + AddressFamilyNameMap[RF_IPv4_ENCAP]: RF_IPv4_ENCAP, + AddressFamilyNameMap[RF_IPv6_ENCAP]: RF_IPv6_ENCAP, + AddressFamilyNameMap[RF_FS_IPv4_UC]: RF_FS_IPv4_UC, + AddressFamilyNameMap[RF_FS_IPv4_VPN]: RF_FS_IPv4_VPN, + AddressFamilyNameMap[RF_FS_IPv6_UC]: RF_FS_IPv6_UC, + AddressFamilyNameMap[RF_FS_IPv6_VPN]: RF_FS_IPv6_VPN, + AddressFamilyNameMap[RF_FS_L2_VPN]: RF_FS_L2_VPN, + AddressFamilyNameMap[RF_OPAQUE]: RF_OPAQUE, +} + +func GetRouteFamily(name string) (RouteFamily, error) { + if v, ok := AddressFamilyValueMap[name]; ok { + return v, nil + } + return RouteFamily(0), fmt.Errorf("%s isn't a valid route family name", name) +} + +func NewPrefixFromRouteFamily(afi uint16, safi uint8, prefixStr ...string) (prefix AddrPrefixInterface, err error) { + family := AfiSafiToRouteFamily(afi, safi) + + f := func(s string) AddrPrefixInterface { + addr, net, _ := net.ParseCIDR(s) + len, _ := net.Mask.Size() + switch family { + case RF_IPv4_UC, RF_IPv4_MC: + return NewIPAddrPrefix(uint8(len), addr.String()) + } + return NewIPv6AddrPrefix(uint8(len), addr.String()) + } + + switch family { + case RF_IPv4_UC, RF_IPv4_MC: + if len(prefixStr) > 0 { + prefix = f(prefixStr[0]) + } else { + prefix = NewIPAddrPrefix(0, "") + } + case RF_IPv6_UC, RF_IPv6_MC: + if len(prefixStr) > 0 { + prefix = f(prefixStr[0]) + } else { + prefix = NewIPv6AddrPrefix(0, "") + } + case RF_IPv4_VPN: + prefix = NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil) + case RF_IPv6_VPN: + prefix = NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil) + case RF_IPv4_MPLS: + prefix = NewLabeledIPAddrPrefix(0, "", *NewMPLSLabelStack()) + case RF_IPv6_MPLS: + prefix = NewLabeledIPv6AddrPrefix(0, "", *NewMPLSLabelStack()) + case RF_EVPN: + prefix = NewEVPNNLRI(0, nil) + case RF_RTC_UC: + prefix = &RouteTargetMembershipNLRI{} + case RF_IPv4_ENCAP: + prefix = NewEncapNLRI("") + case RF_IPv6_ENCAP: + prefix = NewEncapv6NLRI("") + case RF_FS_IPv4_UC: + prefix = &FlowSpecIPv4Unicast{FlowSpecNLRI{rf: RF_FS_IPv4_UC}} + case RF_FS_IPv4_VPN: + prefix = &FlowSpecIPv4VPN{FlowSpecNLRI{rf: RF_FS_IPv4_VPN}} + case RF_FS_IPv6_UC: + prefix = &FlowSpecIPv6Unicast{FlowSpecNLRI{rf: RF_FS_IPv6_UC}} + case RF_FS_IPv6_VPN: + prefix = &FlowSpecIPv6VPN{FlowSpecNLRI{rf: RF_FS_IPv6_VPN}} + case RF_FS_L2_VPN: + prefix = &FlowSpecL2VPN{FlowSpecNLRI{rf: RF_FS_L2_VPN}} + case RF_OPAQUE: + prefix = &OpaqueNLRI{} + default: + err = fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi) + } + return prefix, err +} + +type BGPAttrFlag uint8 + +const ( + BGP_ATTR_FLAG_EXTENDED_LENGTH BGPAttrFlag = 1 << 4 + BGP_ATTR_FLAG_PARTIAL BGPAttrFlag = 1 << 5 + BGP_ATTR_FLAG_TRANSITIVE BGPAttrFlag = 1 << 6 + BGP_ATTR_FLAG_OPTIONAL BGPAttrFlag = 1 << 7 +) + +func (f BGPAttrFlag) String() string { + strs := make([]string, 0, 4) + if f&BGP_ATTR_FLAG_EXTENDED_LENGTH > 0 { + strs = append(strs, "EXTENDED_LENGTH") + } + if f&BGP_ATTR_FLAG_PARTIAL > 0 { + strs = append(strs, "PARTIAL") + } + if f&BGP_ATTR_FLAG_TRANSITIVE > 0 { + strs = append(strs, "TRANSITIVE") + } + if f&BGP_ATTR_FLAG_OPTIONAL > 0 { + strs = append(strs, "OPTIONAL") + } + return strings.Join(strs, "|") +} + +type BGPAttrType uint8 + +const ( + _ BGPAttrType = iota + BGP_ATTR_TYPE_ORIGIN + BGP_ATTR_TYPE_AS_PATH + BGP_ATTR_TYPE_NEXT_HOP + BGP_ATTR_TYPE_MULTI_EXIT_DISC + BGP_ATTR_TYPE_LOCAL_PREF + BGP_ATTR_TYPE_ATOMIC_AGGREGATE + BGP_ATTR_TYPE_AGGREGATOR + BGP_ATTR_TYPE_COMMUNITIES + BGP_ATTR_TYPE_ORIGINATOR_ID + BGP_ATTR_TYPE_CLUSTER_LIST + _ + _ + _ + BGP_ATTR_TYPE_MP_REACH_NLRI // = 14 + BGP_ATTR_TYPE_MP_UNREACH_NLRI + BGP_ATTR_TYPE_EXTENDED_COMMUNITIES + BGP_ATTR_TYPE_AS4_PATH + BGP_ATTR_TYPE_AS4_AGGREGATOR + _ + _ + _ + BGP_ATTR_TYPE_PMSI_TUNNEL // = 22 + BGP_ATTR_TYPE_TUNNEL_ENCAP + _ + BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES // = 25 + BGP_ATTR_TYPE_AIGP // = 26 + BGP_ATTR_TYPE_LARGE_COMMUNITY BGPAttrType = 32 +) + +// NOTIFICATION Error Code RFC 4271 4.5. +const ( + _ = iota + BGP_ERROR_MESSAGE_HEADER_ERROR + BGP_ERROR_OPEN_MESSAGE_ERROR + BGP_ERROR_UPDATE_MESSAGE_ERROR + BGP_ERROR_HOLD_TIMER_EXPIRED + BGP_ERROR_FSM_ERROR + BGP_ERROR_CEASE + BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR +) + +// NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR +const ( + _ = iota + BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED + BGP_ERROR_SUB_BAD_MESSAGE_LENGTH + BGP_ERROR_SUB_BAD_MESSAGE_TYPE +) + +// NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR +const ( + _ = iota + BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER + BGP_ERROR_SUB_BAD_PEER_AS + BGP_ERROR_SUB_BAD_BGP_IDENTIFIER + BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER + BGP_ERROR_SUB_DEPRECATED_AUTHENTICATION_FAILURE + BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME + BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY +) + +// NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR +const ( + _ = iota + BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST + BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE + BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE + BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR + BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR + BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE + BGP_ERROR_SUB_DEPRECATED_ROUTING_LOOP + BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE + BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR + BGP_ERROR_SUB_INVALID_NETWORK_FIELD + BGP_ERROR_SUB_MALFORMED_AS_PATH +) + +// NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED +const ( + _ = iota + BGP_ERROR_SUB_HOLD_TIMER_EXPIRED +) + +// NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR +const ( + _ = iota + BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE + BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE + BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE +) + +// NOTIFICATION Error Subcode for BGP_ERROR_CEASE (RFC 4486) +const ( + _ = iota + BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED + BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN + BGP_ERROR_SUB_PEER_DECONFIGURED + BGP_ERROR_SUB_ADMINISTRATIVE_RESET + BGP_ERROR_SUB_CONNECTION_REJECTED + BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE + BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION + BGP_ERROR_SUB_OUT_OF_RESOURCES + BGP_ERROR_SUB_HARD_RESET //draft-ietf-idr-bgp-gr-notification-07 +) + +// Constants for BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN and BGP_ERROR_SUB_ADMINISTRATIVE_RESET +const ( + BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX = 128 +) + +// NOTIFICATION Error Subcode for BGP_ERROR_ROUTE_REFRESH +const ( + _ = iota + BGP_ERROR_SUB_INVALID_MESSAGE_LENGTH +) + +type NotificationErrorCode uint16 + +func (c NotificationErrorCode) String() string { + code := uint8(uint16(c) >> 8) + subcode := uint8(uint16(c) & 0xff) + UNDEFINED := "undefined" + codeStr := UNDEFINED + subcodeList := []string{} + switch code { + case BGP_ERROR_MESSAGE_HEADER_ERROR: + codeStr = "header" + subcodeList = []string{ + UNDEFINED, + "connection not synchronized", + "bad message length", + "bad message type"} + case BGP_ERROR_OPEN_MESSAGE_ERROR: + codeStr = "open" + subcodeList = []string{ + UNDEFINED, + "unsupported version number", + "bad peer as", + "bad bgp identifier", + "unsupported optional parameter", + "deprecated authentication failure", + "unacceptable hold time", + "unsupported capability"} + case BGP_ERROR_UPDATE_MESSAGE_ERROR: + codeStr = "update" + subcodeList = []string{ + UNDEFINED, + "malformed attribute list", + "unrecognized well known attribute", + "missing well known attribute", + "attribute flags error", + "attribute length error", + "invalid origin attribute", + "deprecated routing loop", + "invalid next hop attribute", + "optional attribute error", + "invalid network field", + "sub malformed as path"} + case BGP_ERROR_HOLD_TIMER_EXPIRED: + codeStr = "hold timer expired" + subcodeList = []string{ + UNDEFINED, + "hold timer expired"} + case BGP_ERROR_FSM_ERROR: + codeStr = "fsm" + subcodeList = []string{ + UNDEFINED, + "receive unexpected message in opensent state", + "receive unexpected message in openconfirm state", + "receive unexpected message in established state"} + case BGP_ERROR_CEASE: + codeStr = "cease" + subcodeList = []string{ + UNDEFINED, + "maximum number of prefixes reached", + "administrative shutdown", + "peer deconfigured", + "administrative reset", + "connection rejected", + "other configuration change", + "connection collision resolution", + "out of resources"} + case BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR: + codeStr = "route refresh" + subcodeList = []string{"invalid message length"} + } + subcodeStr := func(idx uint8, l []string) string { + if len(l) == 0 || int(idx) > len(l)-1 { + return UNDEFINED + } + return l[idx] + }(subcode, subcodeList) + return fmt.Sprintf("code %v(%v) subcode %v(%v)", code, codeStr, subcode, subcodeStr) +} + +func NewNotificationErrorCode(code, subcode uint8) NotificationErrorCode { + return NotificationErrorCode(uint16(code)<<8 | uint16(subcode)) +} + +var PathAttrFlags map[BGPAttrType]BGPAttrFlag = map[BGPAttrType]BGPAttrFlag{ + BGP_ATTR_TYPE_ORIGIN: BGP_ATTR_FLAG_TRANSITIVE, + BGP_ATTR_TYPE_AS_PATH: BGP_ATTR_FLAG_TRANSITIVE, + BGP_ATTR_TYPE_NEXT_HOP: BGP_ATTR_FLAG_TRANSITIVE, + BGP_ATTR_TYPE_MULTI_EXIT_DISC: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_LOCAL_PREF: BGP_ATTR_FLAG_TRANSITIVE, + BGP_ATTR_TYPE_ATOMIC_AGGREGATE: BGP_ATTR_FLAG_TRANSITIVE, + BGP_ATTR_TYPE_AGGREGATOR: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_COMMUNITIES: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_ORIGINATOR_ID: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_CLUSTER_LIST: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_MP_REACH_NLRI: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_MP_UNREACH_NLRI: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_AS4_PATH: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_AS4_AGGREGATOR: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_PMSI_TUNNEL: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_TUNNEL_ENCAP: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_AIGP: BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_TYPE_LARGE_COMMUNITY: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL, +} + +// getPathAttrFlags returns BGP Path Attribute flags value from its type and +// length (byte length of value field). +func getPathAttrFlags(typ BGPAttrType, length int) BGPAttrFlag { + flags := PathAttrFlags[typ] + if length > 255 { + flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH + } + return flags +} + +type PathAttributeInterface interface { + DecodeFromBytes([]byte, ...*MarshallingOption) error + Serialize(...*MarshallingOption) ([]byte, error) + Len(...*MarshallingOption) int + GetFlags() BGPAttrFlag + GetType() BGPAttrType + String() string + MarshalJSON() ([]byte, error) + Flat() map[string]string +} + +type PathAttribute struct { + Flags BGPAttrFlag + Type BGPAttrType + Length uint16 // length of Value +} + +func (p *PathAttribute) Len(options ...*MarshallingOption) int { + if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 { + return 4 + int(p.Length) + } + return 3 + int(p.Length) +} + +func (p *PathAttribute) GetFlags() BGPAttrFlag { + return p.Flags +} + +func (p *PathAttribute) GetType() BGPAttrType { + return p.Type +} + +func (p *PathAttribute) DecodeFromBytes(data []byte, options ...*MarshallingOption) (value []byte, err error) { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + if len(data) < 2 { + return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short") + } + p.Flags = BGPAttrFlag(data[0]) + p.Type = BGPAttrType(data[1]) + if eMsg := validatePathAttributeFlags(p.Type, p.Flags); eMsg != "" { + return nil, NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data, eMsg) + } + + if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 { + if len(data) < 4 { + return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short") + } + p.Length = binary.BigEndian.Uint16(data[2:4]) + data = data[4:] + } else { + if len(data) < 3 { + return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short") + } + p.Length = uint16(data[2]) + data = data[3:] + } + if len(data) < int(p.Length) { + return nil, NewMessageError(eCode, eSubCode, data, "attribute value length is short") + } + + return data[:p.Length], nil +} + +func (p *PathAttribute) Serialize(value []byte, options ...*MarshallingOption) ([]byte, error) { + // Note: Do not update "p.Flags" and "p.Length" to avoid data race. + flags := p.Flags + length := uint16(len(value)) + if flags&BGP_ATTR_FLAG_EXTENDED_LENGTH == 0 && length > 255 { + flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH + } + var buf []byte + if flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 { + buf = append(make([]byte, 4), value...) + binary.BigEndian.PutUint16(buf[2:4], length) + } else { + buf = append(make([]byte, 3), value...) + buf[2] = byte(length) + } + buf[0] = uint8(flags) + buf[1] = uint8(p.Type) + return buf, nil +} + +type PathAttributeOrigin struct { + PathAttribute + Value uint8 +} + +func (p *PathAttributeOrigin) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 1 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "Origin attribute length is incorrect") + } + p.Value = value[0] + return nil +} + +func (p *PathAttributeOrigin) Serialize(options ...*MarshallingOption) ([]byte, error) { + return p.PathAttribute.Serialize([]byte{p.Value}, options...) +} + +func (p *PathAttributeOrigin) String() string { + typ := "-" + switch p.Value { + case BGP_ORIGIN_ATTR_TYPE_IGP: + typ = "i" + case BGP_ORIGIN_ATTR_TYPE_EGP: + typ = "e" + case BGP_ORIGIN_ATTR_TYPE_INCOMPLETE: + typ = "?" + } + return fmt.Sprintf("{Origin: %s}", typ) +} + +func (p *PathAttributeOrigin) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value uint8 `json:"value"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeOrigin(value uint8) *PathAttributeOrigin { + t := BGP_ATTR_TYPE_ORIGIN + return &PathAttributeOrigin{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 1, + }, + Value: value, + } +} + +type AsPathParamFormat struct { + start string + end string + separator string +} + +var asPathParamFormatMap = map[uint8]*AsPathParamFormat{ + BGP_ASPATH_ATTR_TYPE_SET: {"{", "}", ","}, + BGP_ASPATH_ATTR_TYPE_SEQ: {"", "", " "}, + BGP_ASPATH_ATTR_TYPE_CONFED_SET: {"(", ")", " "}, + BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: {"[", "]", ","}, +} + +type AsPathParamInterface interface { + GetType() uint8 + GetAS() []uint32 + Serialize() ([]byte, error) + DecodeFromBytes([]byte) error + Len() int + ASLen() int + MarshalJSON() ([]byte, error) + String() string +} + +func AsPathString(aspath *PathAttributeAsPath) string { + s := bytes.NewBuffer(make([]byte, 0, 64)) + for i, param := range aspath.Value { + segType := param.GetType() + asList := param.GetAS() + if i != 0 { + s.WriteString(" ") + } + + sep := " " + switch segType { + case BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + s.WriteString("(") + case BGP_ASPATH_ATTR_TYPE_CONFED_SET: + s.WriteString("[") + sep = "," + case BGP_ASPATH_ATTR_TYPE_SET: + s.WriteString("{") + sep = "," + } + for j, as := range asList { + s.WriteString(fmt.Sprintf("%d", as)) + if j != len(asList)-1 { + s.WriteString(sep) + } + } + switch segType { + case BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + s.WriteString(")") + case BGP_ASPATH_ATTR_TYPE_CONFED_SET: + s.WriteString("]") + case BGP_ASPATH_ATTR_TYPE_SET: + s.WriteString("}") + } + } + return s.String() +} + +type AsPathParam struct { + Type uint8 + Num uint8 + AS []uint16 +} + +func (a *AsPathParam) GetType() uint8 { + return a.Type +} + +func (a *AsPathParam) GetAS() []uint32 { + nums := make([]uint32, 0, len(a.AS)) + for _, as := range a.AS { + nums = append(nums, uint32(as)) + } + return nums +} + +func (a *AsPathParam) Serialize() ([]byte, error) { + buf := make([]byte, 2+len(a.AS)*2) + buf[0] = uint8(a.Type) + buf[1] = a.Num + for j, as := range a.AS { + binary.BigEndian.PutUint16(buf[2+j*2:], as) + } + return buf, nil +} + +func (a *AsPathParam) DecodeFromBytes(data []byte) error { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH) + if len(data) < 2 { + return NewMessageError(eCode, eSubCode, nil, "AS param header length is short") + } + a.Type = data[0] + a.Num = data[1] + data = data[2:] + if len(data) < int(a.Num*2) { + return NewMessageError(eCode, eSubCode, nil, "AS param data length is short") + } + for i := 0; i < int(a.Num); i++ { + a.AS = append(a.AS, binary.BigEndian.Uint16(data)) + data = data[2:] + } + return nil +} + +func (a *AsPathParam) Len() int { + return 2 + len(a.AS)*2 +} + +func (a *AsPathParam) ASLen() int { + switch a.Type { + case BGP_ASPATH_ATTR_TYPE_SEQ: + return len(a.AS) + case BGP_ASPATH_ATTR_TYPE_SET: + return 1 + case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + return 0 + } + return 0 +} + +func (a *AsPathParam) String() string { + format, ok := asPathParamFormatMap[a.Type] + if !ok { + return fmt.Sprintf("%v", a.AS) + } + aspath := make([]string, 0, len(a.AS)) + for _, asn := range a.AS { + aspath = append(aspath, fmt.Sprintf("%d", asn)) + } + s := bytes.NewBuffer(make([]byte, 0, 32)) + s.WriteString(format.start) + s.WriteString(strings.Join(aspath, format.separator)) + s.WriteString(format.end) + return s.String() +} + +func (a *AsPathParam) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint8 `json:"segment_type"` + Num uint8 `json:"num"` + AS []uint16 `json:"asns"` + }{ + Type: a.Type, + Num: a.Num, + AS: a.AS, + }) +} + +func NewAsPathParam(segType uint8, as []uint16) *AsPathParam { + return &AsPathParam{ + Type: segType, + Num: uint8(len(as)), + AS: as, + } +} + +type As4PathParam struct { + Type uint8 + Num uint8 + AS []uint32 +} + +func (a *As4PathParam) GetType() uint8 { + return a.Type +} + +func (a *As4PathParam) GetAS() []uint32 { + return a.AS +} + +func (a *As4PathParam) Serialize() ([]byte, error) { + buf := make([]byte, 2+len(a.AS)*4) + buf[0] = a.Type + buf[1] = a.Num + for j, as := range a.AS { + binary.BigEndian.PutUint32(buf[2+j*4:], as) + } + return buf, nil +} + +func (a *As4PathParam) DecodeFromBytes(data []byte) error { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH) + if len(data) < 2 { + return NewMessageError(eCode, eSubCode, nil, "AS4 param header length is short") + } + a.Type = data[0] + a.Num = data[1] + data = data[2:] + if len(data) < int(a.Num)*4 { + return NewMessageError(eCode, eSubCode, nil, "AS4 param data length is short") + } + for i := 0; i < int(a.Num); i++ { + a.AS = append(a.AS, binary.BigEndian.Uint32(data)) + data = data[4:] + } + return nil +} + +func (a *As4PathParam) Len() int { + return 2 + len(a.AS)*4 +} + +func (a *As4PathParam) ASLen() int { + switch a.Type { + case BGP_ASPATH_ATTR_TYPE_SEQ: + return len(a.AS) + case BGP_ASPATH_ATTR_TYPE_SET: + return 1 + case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + return 0 + } + return 0 +} + +func (a *As4PathParam) String() string { + format, ok := asPathParamFormatMap[a.Type] + if !ok { + return fmt.Sprintf("%v", a.AS) + } + aspath := make([]string, 0, len(a.AS)) + for _, asn := range a.AS { + aspath = append(aspath, fmt.Sprintf("%d", asn)) + } + s := bytes.NewBuffer(make([]byte, 0, 32)) + s.WriteString(format.start) + s.WriteString(strings.Join(aspath, format.separator)) + s.WriteString(format.end) + return s.String() +} + +func (a *As4PathParam) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type uint8 `json:"segment_type"` + Num uint8 `json:"num"` + AS []uint32 `json:"asns"` + }{ + Type: a.Type, + Num: a.Num, + AS: a.AS, + }) +} + +func NewAs4PathParam(segType uint8, as []uint32) *As4PathParam { + return &As4PathParam{ + Type: segType, + Num: uint8(len(as)), + AS: as, + } +} + +type PathAttributeAsPath struct { + PathAttribute + Value []AsPathParamInterface +} + +func (p *PathAttributeAsPath) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length == 0 { + // ibgp or something + return nil + } + isAs4, err := validateAsPathValueBytes(value) + if err != nil { + err.(*MessageError).Data, _ = p.PathAttribute.Serialize(value, options...) + return err + } + for len(value) > 0 { + var tuple AsPathParamInterface + if isAs4 { + tuple = &As4PathParam{} + } else { + tuple = &AsPathParam{} + } + err := tuple.DecodeFromBytes(value) + if err != nil { + return err + } + p.Value = append(p.Value, tuple) + value = value[tuple.Len():] + } + return nil +} + +func (p *PathAttributeAsPath) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 0) + for _, v := range p.Value { + vbuf, err := v.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, vbuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeAsPath) String() string { + params := make([]string, 0, len(p.Value)) + for _, param := range p.Value { + params = append(params, param.String()) + } + return strings.Join(params, " ") +} + +func (p *PathAttributeAsPath) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []AsPathParamInterface `json:"as_paths"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeAsPath(value []AsPathParamInterface) *PathAttributeAsPath { + var l int + for _, v := range value { + l += v.Len() + } + t := BGP_ATTR_TYPE_AS_PATH + return &PathAttributeAsPath{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: value, + } +} + +type PathAttributeNextHop struct { + PathAttribute + Value net.IP +} + +func (p *PathAttributeNextHop) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 4 && p.Length != 16 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "nexthop length isn't correct") + } + p.Value = value + return nil +} + +func (p *PathAttributeNextHop) Serialize(options ...*MarshallingOption) ([]byte, error) { + return p.PathAttribute.Serialize(p.Value, options...) +} + +func (p *PathAttributeNextHop) String() string { + return fmt.Sprintf("{Nexthop: %s}", p.Value) +} + +func (p *PathAttributeNextHop) MarshalJSON() ([]byte, error) { + value := "0.0.0.0" + if p.Value != nil { + value = p.Value.String() + } + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value string `json:"nexthop"` + }{ + Type: p.GetType(), + Value: value, + }) +} + +func NewPathAttributeNextHop(value string) *PathAttributeNextHop { + t := BGP_ATTR_TYPE_NEXT_HOP + return &PathAttributeNextHop{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 4, + }, + Value: net.ParseIP(value).To4(), + } +} + +type PathAttributeMultiExitDisc struct { + PathAttribute + Value uint32 +} + +func (p *PathAttributeMultiExitDisc) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 4 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "med length isn't correct") + } + p.Value = binary.BigEndian.Uint32(value) + return nil +} + +func (p *PathAttributeMultiExitDisc) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, p.Value) + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeMultiExitDisc) String() string { + return fmt.Sprintf("{Med: %d}", p.Value) +} + +func (p *PathAttributeMultiExitDisc) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value uint32 `json:"metric"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeMultiExitDisc(value uint32) *PathAttributeMultiExitDisc { + t := BGP_ATTR_TYPE_MULTI_EXIT_DISC + return &PathAttributeMultiExitDisc{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 4, + }, + Value: value, + } +} + +type PathAttributeLocalPref struct { + PathAttribute + Value uint32 +} + +func (p *PathAttributeLocalPref) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 4 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "local pref length isn't correct") + } + p.Value = binary.BigEndian.Uint32(value) + return nil +} + +func (p *PathAttributeLocalPref) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, p.Value) + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeLocalPref) String() string { + return fmt.Sprintf("{LocalPref: %d}", p.Value) +} + +func (p *PathAttributeLocalPref) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value uint32 `json:"value"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeLocalPref(value uint32) *PathAttributeLocalPref { + t := BGP_ATTR_TYPE_LOCAL_PREF + return &PathAttributeLocalPref{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 4, + }, + Value: value, + } +} + +type PathAttributeAtomicAggregate struct { + PathAttribute +} + +func (p *PathAttributeAtomicAggregate) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + _, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 0 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "atomic aggregate should have no value") + } + return nil +} + +func (p *PathAttributeAtomicAggregate) Serialize(options ...*MarshallingOption) ([]byte, error) { + return p.PathAttribute.Serialize(nil, options...) +} + +func (p *PathAttributeAtomicAggregate) String() string { + return "{AtomicAggregate}" +} + +func (p *PathAttributeAtomicAggregate) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + }{ + Type: p.GetType(), + }) +} + +func NewPathAttributeAtomicAggregate() *PathAttributeAtomicAggregate { + t := BGP_ATTR_TYPE_ATOMIC_AGGREGATE + return &PathAttributeAtomicAggregate{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 0, + }, + } +} + +type PathAttributeAggregatorParam struct { + AS uint32 + Askind reflect.Kind + Address net.IP +} + +type PathAttributeAggregator struct { + PathAttribute + Value PathAttributeAggregatorParam +} + +func (p *PathAttributeAggregator) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + switch p.Length { + case 6: + p.Value.Askind = reflect.Uint16 + p.Value.AS = uint32(binary.BigEndian.Uint16(value[0:2])) + p.Value.Address = value[2:] + case 8: + p.Value.Askind = reflect.Uint32 + p.Value.AS = binary.BigEndian.Uint32(value[0:4]) + p.Value.Address = value[4:] + default: + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "aggregator length isn't correct") + } + return nil +} + +func (p *PathAttributeAggregator) Serialize(options ...*MarshallingOption) ([]byte, error) { + var buf []byte + switch p.Value.Askind { + case reflect.Uint16: + buf = make([]byte, 6) + binary.BigEndian.PutUint16(buf, uint16(p.Value.AS)) + copy(buf[2:], p.Value.Address) + case reflect.Uint32: + buf = make([]byte, 8) + binary.BigEndian.PutUint32(buf, p.Value.AS) + copy(buf[4:], p.Value.Address) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeAggregator) String() string { + return fmt.Sprintf("{Aggregate: {AS: %d, Address: %s}}", p.Value.AS, p.Value.Address) +} + +func (p *PathAttributeAggregator) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + AS uint32 `json:"as"` + Address string `json:"address"` + }{ + Type: p.GetType(), + AS: p.Value.AS, + Address: p.Value.Address.String(), + }) +} + +func NewPathAttributeAggregator(as interface{}, address string) *PathAttributeAggregator { + v := reflect.ValueOf(as) + asKind := v.Kind() + var l uint16 + switch asKind { + case reflect.Uint16: + l = 6 + case reflect.Uint32: + l = 8 + default: + // Invalid type + return nil + } + t := BGP_ATTR_TYPE_AGGREGATOR + return &PathAttributeAggregator{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: l, + }, + Value: PathAttributeAggregatorParam{ + AS: uint32(v.Uint()), + Askind: asKind, + Address: net.ParseIP(address).To4(), + }, + } +} + +type PathAttributeCommunities struct { + PathAttribute + Value []uint32 +} + +func (p *PathAttributeCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length%4 != 0 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "communities length isn't correct") + } + for len(value) >= 4 { + p.Value = append(p.Value, binary.BigEndian.Uint32(value)) + value = value[4:] + } + return nil +} + +func (p *PathAttributeCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, len(p.Value)*4) + for i, v := range p.Value { + binary.BigEndian.PutUint32(buf[i*4:], v) + } + return p.PathAttribute.Serialize(buf, options...) +} + +type WellKnownCommunity uint32 + +const ( + COMMUNITY_INTERNET WellKnownCommunity = 0x00000000 + COMMUNITY_PLANNED_SHUT WellKnownCommunity = 0xffff0000 + COMMUNITY_ACCEPT_OWN WellKnownCommunity = 0xffff0001 + COMMUNITY_ROUTE_FILTER_TRANSLATED_v4 WellKnownCommunity = 0xffff0002 + COMMUNITY_ROUTE_FILTER_v4 WellKnownCommunity = 0xffff0003 + COMMUNITY_ROUTE_FILTER_TRANSLATED_v6 WellKnownCommunity = 0xffff0004 + COMMUNITY_ROUTE_FILTER_v6 WellKnownCommunity = 0xffff0005 + COMMUNITY_LLGR_STALE WellKnownCommunity = 0xffff0006 + COMMUNITY_NO_LLGR WellKnownCommunity = 0xffff0007 + COMMUNITY_BLACKHOLE WellKnownCommunity = 0xffff029a + COMMUNITY_NO_EXPORT WellKnownCommunity = 0xffffff01 + COMMUNITY_NO_ADVERTISE WellKnownCommunity = 0xffffff02 + COMMUNITY_NO_EXPORT_SUBCONFED WellKnownCommunity = 0xffffff03 + COMMUNITY_NO_PEER WellKnownCommunity = 0xffffff04 +) + +var WellKnownCommunityNameMap = map[WellKnownCommunity]string{ + COMMUNITY_INTERNET: "internet", + COMMUNITY_PLANNED_SHUT: "planned-shut", + COMMUNITY_ACCEPT_OWN: "accept-own", + COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: "route-filter-translated-v4", + COMMUNITY_ROUTE_FILTER_v4: "route-filter-v4", + COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: "route-filter-translated-v6", + COMMUNITY_ROUTE_FILTER_v6: "route-filter-v6", + COMMUNITY_LLGR_STALE: "llgr-stale", + COMMUNITY_NO_LLGR: "no-llgr", + COMMUNITY_BLACKHOLE: "blackhole", + COMMUNITY_NO_EXPORT: "no-export", + COMMUNITY_NO_ADVERTISE: "no-advertise", + COMMUNITY_NO_EXPORT_SUBCONFED: "no-export-subconfed", + COMMUNITY_NO_PEER: "no-peer", +} + +var WellKnownCommunityValueMap = map[string]WellKnownCommunity{ + WellKnownCommunityNameMap[COMMUNITY_INTERNET]: COMMUNITY_INTERNET, + WellKnownCommunityNameMap[COMMUNITY_PLANNED_SHUT]: COMMUNITY_PLANNED_SHUT, + WellKnownCommunityNameMap[COMMUNITY_ACCEPT_OWN]: COMMUNITY_ACCEPT_OWN, + WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v4]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v4, + WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v4]: COMMUNITY_ROUTE_FILTER_v4, + WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v6]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v6, + WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v6]: COMMUNITY_ROUTE_FILTER_v6, + WellKnownCommunityNameMap[COMMUNITY_LLGR_STALE]: COMMUNITY_LLGR_STALE, + WellKnownCommunityNameMap[COMMUNITY_NO_LLGR]: COMMUNITY_NO_LLGR, + WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT]: COMMUNITY_NO_EXPORT, + WellKnownCommunityNameMap[COMMUNITY_BLACKHOLE]: COMMUNITY_BLACKHOLE, + WellKnownCommunityNameMap[COMMUNITY_NO_ADVERTISE]: COMMUNITY_NO_ADVERTISE, + WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT_SUBCONFED]: COMMUNITY_NO_EXPORT_SUBCONFED, + WellKnownCommunityNameMap[COMMUNITY_NO_PEER]: COMMUNITY_NO_PEER, +} + +func (p *PathAttributeCommunities) String() string { + l := []string{} + for _, v := range p.Value { + n, ok := WellKnownCommunityNameMap[WellKnownCommunity(v)] + if ok { + l = append(l, n) + } else { + l = append(l, fmt.Sprintf("%d:%d", (0xffff0000&v)>>16, 0xffff&v)) + } + } + return fmt.Sprintf("{Communities: %s}", strings.Join(l, ", ")) +} + +func (p *PathAttributeCommunities) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []uint32 `json:"communities"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeCommunities(value []uint32) *PathAttributeCommunities { + l := len(value) * 4 + t := BGP_ATTR_TYPE_COMMUNITIES + return &PathAttributeCommunities{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: value, + } +} + +type PathAttributeOriginatorId struct { + PathAttribute + Value net.IP +} + +func (p *PathAttributeOriginatorId) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 4 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "originator id length isn't correct") + } + p.Value = value + return nil +} + +func (p *PathAttributeOriginatorId) String() string { + return fmt.Sprintf("{Originator: %s}", p.Value) +} + +func (p *PathAttributeOriginatorId) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value string `json:"value"` + }{ + Type: p.GetType(), + Value: p.Value.String(), + }) +} + +func (p *PathAttributeOriginatorId) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 4) + copy(buf, p.Value) + return p.PathAttribute.Serialize(buf, options...) +} + +func NewPathAttributeOriginatorId(value string) *PathAttributeOriginatorId { + t := BGP_ATTR_TYPE_ORIGINATOR_ID + return &PathAttributeOriginatorId{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 4, + }, + Value: net.ParseIP(value).To4(), + } +} + +type PathAttributeClusterList struct { + PathAttribute + Value []net.IP +} + +func (p *PathAttributeClusterList) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length%4 != 0 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "clusterlist length isn't correct") + } + for len(value) >= 4 { + p.Value = append(p.Value, value[:4]) + value = value[4:] + } + return nil +} + +func (p *PathAttributeClusterList) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, len(p.Value)*4) + for i, v := range p.Value { + copy(buf[i*4:], v) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeClusterList) String() string { + return fmt.Sprintf("{ClusterList: %v}", p.Value) +} + +func (p *PathAttributeClusterList) MarshalJSON() ([]byte, error) { + value := make([]string, 0, len(p.Value)) + for _, v := range p.Value { + value = append(value, v.String()) + } + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []string `json:"value"` + }{ + Type: p.GetType(), + Value: value, + }) +} + +func NewPathAttributeClusterList(value []string) *PathAttributeClusterList { + l := len(value) * 4 + list := make([]net.IP, len(value)) + for i, v := range value { + list[i] = net.ParseIP(v).To4() + } + t := BGP_ATTR_TYPE_CLUSTER_LIST + return &PathAttributeClusterList{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: list, + } +} + +type PathAttributeMpReachNLRI struct { + PathAttribute + Nexthop net.IP + LinkLocalNexthop net.IP + AFI uint16 + SAFI uint8 + Value []AddrPrefixInterface +} + +func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + eData, _ := p.PathAttribute.Serialize(value, options...) + if p.Length < 3 { + return NewMessageError(eCode, eSubCode, value, "mpreach header length is short") + } + afi := binary.BigEndian.Uint16(value[0:2]) + safi := value[2] + p.AFI = afi + p.SAFI = safi + _, err = NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error()) + } + nexthoplen := int(value[3]) + if len(value) < 4+nexthoplen { + return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is short") + } + nexthopbin := value[4 : 4+nexthoplen] + if nexthoplen > 0 { + v4addrlen := 4 + v6addrlen := 16 + offset := 0 + if safi == SAFI_MPLS_VPN { + offset = 8 + } + switch nexthoplen { + case 2 * (offset + v6addrlen): + p.LinkLocalNexthop = nexthopbin[offset+v6addrlen+offset : 2*(offset+v6addrlen)] + fallthrough + case offset + v6addrlen: + p.Nexthop = nexthopbin[offset : offset+v6addrlen] + case offset + v4addrlen: + p.Nexthop = nexthopbin[offset : offset+v4addrlen] + default: + return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is incorrect") + } + } + value = value[4+nexthoplen:] + // skip reserved + if len(value) == 0 { + return NewMessageError(eCode, eSubCode, value, "no skip byte") + } + value = value[1:] + addpathLen := 0 + if IsAddPathEnabled(true, AfiSafiToRouteFamily(afi, safi), options) { + addpathLen = 4 + } + for len(value) > 0 { + prefix, err := NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error()) + } + err = prefix.DecodeFromBytes(value, options...) + if err != nil { + return err + } + if prefix.Len(options...)+addpathLen > len(value) { + return NewMessageError(eCode, eSubCode, value, "prefix length is incorrect") + } + value = value[prefix.Len(options...)+addpathLen:] + p.Value = append(p.Value, prefix) + } + return nil +} + +func (p *PathAttributeMpReachNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + afi := p.AFI + safi := p.SAFI + nexthoplen := 4 + if afi == AFI_IP6 || p.Nexthop.To4() == nil { + nexthoplen = 16 + } + offset := 0 + switch safi { + case SAFI_MPLS_VPN: + offset = 8 + nexthoplen += offset + case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST: + nexthoplen = 0 + } + if p.LinkLocalNexthop != nil { + nexthoplen *= 2 + } + buf := make([]byte, 4+nexthoplen) + binary.BigEndian.PutUint16(buf[0:], afi) + buf[2] = safi + buf[3] = uint8(nexthoplen) + if nexthoplen != 0 { + if p.Nexthop.To4() == nil { + copy(buf[4+offset:], p.Nexthop.To16()) + if p.LinkLocalNexthop != nil { + copy(buf[4+offset+16:], p.LinkLocalNexthop.To16()) + } + } else { + copy(buf[4+offset:], p.Nexthop) + } + } + buf = append(buf, make([]byte, 1)...) + for _, prefix := range p.Value { + pbuf, err := prefix.Serialize(options...) + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeMpReachNLRI) MarshalJSON() ([]byte, error) { + nexthop := p.Nexthop.String() + if p.Nexthop == nil { + switch p.AFI { + case AFI_IP: + nexthop = "0.0.0.0" + case AFI_IP6: + nexthop = "::" + default: + nexthop = "fictitious" + } + } + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Nexthop string `json:"nexthop"` + AFI uint16 `json:"afi"` + SAFI uint8 `json:"safi"` + Value []AddrPrefixInterface `json:"value"` + }{ + Type: p.GetType(), + Nexthop: nexthop, + AFI: p.AFI, + SAFI: p.SAFI, + Value: p.Value, + }) +} + +func (p *PathAttributeMpReachNLRI) String() string { + return fmt.Sprintf("{MpReach(%s): {Nexthop: %s, NLRIs: %s}}", AfiSafiToRouteFamily(p.AFI, p.SAFI), p.Nexthop, p.Value) +} + +func NewPathAttributeMpReachNLRI(nexthop string, nlri []AddrPrefixInterface) *PathAttributeMpReachNLRI { + // AFI(2) + SAFI(1) + NexthopLength(1) + Nexthop(variable) + // + Reserved(1) + NLRI(variable) + l := 5 + var afi uint16 + var safi uint8 + if len(nlri) > 0 { + afi = nlri[0].AFI() + safi = nlri[0].SAFI() + } + nh := net.ParseIP(nexthop) + if nh.To4() != nil && afi != AFI_IP6 { + nh = nh.To4() + switch safi { + case SAFI_MPLS_VPN: + l += 12 + case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST: + // Should not have Nexthop + default: + l += 4 + } + } else { + switch safi { + case SAFI_MPLS_VPN: + l += 24 + case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST: + // Should not have Nexthop + default: + l += 16 + } + } + var nlriLen int + for _, n := range nlri { + l += n.Len() + nBuf, _ := n.Serialize() + nlriLen += len(nBuf) + } + t := BGP_ATTR_TYPE_MP_REACH_NLRI + return &PathAttributeMpReachNLRI{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Nexthop: nh, + AFI: afi, + SAFI: safi, + Value: nlri, + } +} + +type PathAttributeMpUnreachNLRI struct { + PathAttribute + AFI uint16 + SAFI uint8 + Value []AddrPrefixInterface +} + +func (p *PathAttributeMpUnreachNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + eData, _ := p.PathAttribute.Serialize(value, options...) + if p.Length < 3 { + return NewMessageError(eCode, eSubCode, value, "unreach header length is incorrect") + } + afi := binary.BigEndian.Uint16(value[0:2]) + safi := value[2] + _, err = NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error()) + } + value = value[3:] + p.AFI = afi + p.SAFI = safi + addpathLen := 0 + if IsAddPathEnabled(true, AfiSafiToRouteFamily(afi, safi), options) { + addpathLen = 4 + } + for len(value) > 0 { + prefix, err := NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error()) + } + err = prefix.DecodeFromBytes(value, options...) + if err != nil { + return err + } + if prefix.Len(options...)+addpathLen > len(value) { + return NewMessageError(eCode, eSubCode, eData, "prefix length is incorrect") + } + value = value[prefix.Len(options...)+addpathLen:] + p.Value = append(p.Value, prefix) + } + return nil +} + +func (p *PathAttributeMpUnreachNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 3) + binary.BigEndian.PutUint16(buf, p.AFI) + buf[2] = p.SAFI + for _, prefix := range p.Value { + pbuf, err := prefix.Serialize(options...) + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeMpUnreachNLRI) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + AFI uint16 `json:"afi"` + SAFI uint8 `json:"safi"` + Value []AddrPrefixInterface `json:"value"` + }{ + Type: p.GetType(), + AFI: p.AFI, + SAFI: p.SAFI, + Value: p.Value, + }) +} + +func (p *PathAttributeMpUnreachNLRI) String() string { + if len(p.Value) > 0 { + return fmt.Sprintf("{MpUnreach(%s): {NLRIs: %s}}", AfiSafiToRouteFamily(p.AFI, p.SAFI), p.Value) + } + return fmt.Sprintf("{MpUnreach(%s): End-of-Rib}", AfiSafiToRouteFamily(p.AFI, p.SAFI)) +} + +func NewPathAttributeMpUnreachNLRI(nlri []AddrPrefixInterface) *PathAttributeMpUnreachNLRI { + // AFI(2) + SAFI(1) + NLRI(variable) + l := 3 + var afi uint16 + var safi uint8 + if len(nlri) > 0 { + afi = nlri[0].AFI() + safi = nlri[0].SAFI() + } + for _, n := range nlri { + l += n.Len() + } + t := BGP_ATTR_TYPE_MP_UNREACH_NLRI + return &PathAttributeMpUnreachNLRI{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + AFI: afi, + SAFI: safi, + Value: nlri, + } +} + +type ExtendedCommunityInterface interface { + Serialize() ([]byte, error) + String() string + GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) + MarshalJSON() ([]byte, error) + Flat() map[string]string +} + +type TwoOctetAsSpecificExtended struct { + SubType ExtendedCommunityAttrSubType + AS uint16 + LocalAdmin uint32 + IsTransitive bool +} + +func (e *TwoOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + if e.IsTransitive { + buf[0] = byte(EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC) + } else { + buf[0] = byte(EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC) + } + buf[1] = byte(e.SubType) + binary.BigEndian.PutUint16(buf[2:], e.AS) + binary.BigEndian.PutUint32(buf[4:], e.LocalAdmin) + return buf, nil +} + +func (e *TwoOctetAsSpecificExtended) String() string { + return fmt.Sprintf("%d:%d", e.AS, e.LocalAdmin) +} + +func (e *TwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.String(), + }) +} + +func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC + } + return t, e.SubType +} + +func NewTwoOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint16, localAdmin uint32, isTransitive bool) *TwoOctetAsSpecificExtended { + return &TwoOctetAsSpecificExtended{ + SubType: subtype, + AS: as, + LocalAdmin: localAdmin, + IsTransitive: isTransitive, + } +} + +type IPv4AddressSpecificExtended struct { + SubType ExtendedCommunityAttrSubType + IPv4 net.IP + LocalAdmin uint16 + IsTransitive bool +} + +func (e *IPv4AddressSpecificExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + if e.IsTransitive { + buf[0] = byte(EC_TYPE_TRANSITIVE_IP4_SPECIFIC) + } else { + buf[0] = byte(EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC) + } + buf[1] = byte(e.SubType) + copy(buf[2:6], e.IPv4) + binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin) + return buf, nil +} + +func (e *IPv4AddressSpecificExtended) String() string { + return fmt.Sprintf("%s:%d", e.IPv4.String(), e.LocalAdmin) +} + +func (e *IPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.String(), + }) +} + +func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_IP4_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC + } + return t, e.SubType +} + +func NewIPv4AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip string, localAdmin uint16, isTransitive bool) *IPv4AddressSpecificExtended { + ipv4 := net.ParseIP(ip) + if ipv4.To4() == nil { + return nil + } + return &IPv4AddressSpecificExtended{ + SubType: subtype, + IPv4: ipv4.To4(), + LocalAdmin: localAdmin, + IsTransitive: isTransitive, + } +} + +type IPv6AddressSpecificExtended struct { + SubType ExtendedCommunityAttrSubType + IPv6 net.IP + LocalAdmin uint16 + IsTransitive bool +} + +func (e *IPv6AddressSpecificExtended) Serialize() ([]byte, error) { + buf := make([]byte, 20) + if e.IsTransitive { + buf[0] = byte(EC_TYPE_TRANSITIVE_IP6_SPECIFIC) + } else { + buf[0] = byte(EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC) + } + buf[1] = byte(e.SubType) + copy(buf[2:18], e.IPv6) + binary.BigEndian.PutUint16(buf[18:], e.LocalAdmin) + return buf, nil +} + +func (e *IPv6AddressSpecificExtended) String() string { + return fmt.Sprintf("%s:%d", e.IPv6.String(), e.LocalAdmin) +} + +func (e *IPv6AddressSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.String(), + }) +} + +func (e *IPv6AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_IP6_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC + } + return t, e.SubType +} + +func NewIPv6AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip string, localAdmin uint16, isTransitive bool) *IPv6AddressSpecificExtended { + ipv6 := net.ParseIP(ip) + if ipv6.To16() == nil { + return nil + } + return &IPv6AddressSpecificExtended{ + SubType: subtype, + IPv6: ipv6.To16(), + LocalAdmin: localAdmin, + IsTransitive: isTransitive, + } +} + +type FourOctetAsSpecificExtended struct { + SubType ExtendedCommunityAttrSubType + AS uint32 + LocalAdmin uint16 + IsTransitive bool +} + +func (e *FourOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + if e.IsTransitive { + buf[0] = byte(EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC) + } else { + buf[0] = byte(EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC) + } + buf[1] = byte(e.SubType) + binary.BigEndian.PutUint32(buf[2:], e.AS) + binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin) + return buf, nil +} + +func (e *FourOctetAsSpecificExtended) String() string { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, e.AS) + asUpper := binary.BigEndian.Uint16(buf[0:2]) + asLower := binary.BigEndian.Uint16(buf[2:]) + return fmt.Sprintf("%d.%d:%d", asUpper, asLower, e.LocalAdmin) +} + +func (e *FourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.String(), + }) +} + +func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC + } + return t, e.SubType +} + +func NewFourOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint32, localAdmin uint16, isTransitive bool) *FourOctetAsSpecificExtended { + return &FourOctetAsSpecificExtended{ + SubType: subtype, + AS: as, + LocalAdmin: localAdmin, + IsTransitive: isTransitive, + } +} + +func ParseExtendedCommunity(subtype ExtendedCommunityAttrSubType, com string) (ExtendedCommunityInterface, error) { + if subtype == EC_SUBTYPE_ORIGIN_VALIDATION { + var state ValidationState + switch com { + case VALIDATION_STATE_VALID.String(): + state = VALIDATION_STATE_VALID + case VALIDATION_STATE_NOT_FOUND.String(): + state = VALIDATION_STATE_NOT_FOUND + case VALIDATION_STATE_INVALID.String(): + state = VALIDATION_STATE_INVALID + default: + return nil, fmt.Errorf("invalid validation state") + } + return &ValidationExtended{ + State: state, + }, nil + } + elems, err := parseRdAndRt(com) + if err != nil { + return nil, err + } + localAdmin, _ := strconv.ParseUint(elems[10], 10, 32) + ip := net.ParseIP(elems[1]) + isTransitive := true + switch { + case ip.To4() != nil: + return NewIPv4AddressSpecificExtended(subtype, elems[1], uint16(localAdmin), isTransitive), nil + case ip.To16() != nil: + return NewIPv6AddressSpecificExtended(subtype, elems[1], uint16(localAdmin), isTransitive), nil + case elems[6] == "" && elems[7] == "": + asn, _ := strconv.ParseUint(elems[8], 10, 16) + return NewTwoOctetAsSpecificExtended(subtype, uint16(asn), uint32(localAdmin), isTransitive), nil + default: + fst, _ := strconv.ParseUint(elems[7], 10, 16) + snd, _ := strconv.ParseUint(elems[8], 10, 16) + asn := fst<<16 | snd + return NewFourOctetAsSpecificExtended(subtype, uint32(asn), uint16(localAdmin), isTransitive), nil + } +} + +func ParseRouteTarget(rt string) (ExtendedCommunityInterface, error) { + return ParseExtendedCommunity(EC_SUBTYPE_ROUTE_TARGET, rt) +} + +func SerializeExtendedCommunities(comms []ExtendedCommunityInterface) ([][]byte, error) { + var bufs [][]byte + var err error + for _, c := range comms { + buf, err := c.Serialize() + if err != nil { + return nil, err + } + bufs = append(bufs, buf) + } + return bufs, err +} + +type ValidationState uint8 + +const ( + VALIDATION_STATE_VALID ValidationState = 0 + VALIDATION_STATE_NOT_FOUND ValidationState = 1 + VALIDATION_STATE_INVALID ValidationState = 2 +) + +func (s ValidationState) String() string { + switch s { + case VALIDATION_STATE_VALID: + return "valid" + case VALIDATION_STATE_NOT_FOUND: + return "not-found" + case VALIDATION_STATE_INVALID: + return "invalid" + } + return fmt.Sprintf("unknown validation state(%d)", s) +} + +type ValidationExtended struct { + State ValidationState +} + +func (e *ValidationExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + typ, subType := e.GetTypes() + buf[0] = byte(typ) + buf[1] = byte(subType) + buf[7] = byte(e.State) + return buf, nil +} + +func (e *ValidationExtended) String() string { + return e.State.String() +} + +func (e *ValidationExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_NON_TRANSITIVE_OPAQUE, EC_SUBTYPE_ORIGIN_VALIDATION +} + +func (e *ValidationExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + SubType ExtendedCommunityAttrSubType `json:"subtype"` + State ValidationState `json:"value"` + }{ + Type: t, + SubType: s, + State: e.State, + }) +} + +func NewValidationExtended(state ValidationState) *ValidationExtended { + return &ValidationExtended{ + State: state, + } +} + +type ColorExtended struct { + Color uint32 +} + +func (e *ColorExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + typ, subType := e.GetTypes() + buf[0] = byte(typ) + buf[1] = byte(subType) + binary.BigEndian.PutUint32(buf[4:8], uint32(e.Color)) + return buf, nil +} + +func (e *ColorExtended) String() string { + return fmt.Sprintf("%d", e.Color) +} + +func (e *ColorExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_COLOR +} + +func (e *ColorExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + SubType ExtendedCommunityAttrSubType `json:"subtype"` + Color uint32 `json:"color"` + }{ + Type: t, + SubType: s, + Color: e.Color, + }) +} + +func NewColorExtended(color uint32) *ColorExtended { + return &ColorExtended{ + Color: color, + } +} + +type EncapExtended struct { + TunnelType TunnelType +} + +func (e *EncapExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + typ, subType := e.GetTypes() + buf[0] = byte(typ) + buf[1] = byte(subType) + binary.BigEndian.PutUint16(buf[6:8], uint16(e.TunnelType)) + return buf, nil +} + +func (e *EncapExtended) String() string { + switch e.TunnelType { + case TUNNEL_TYPE_L2TP3: + return "L2TPv3 over IP" + case TUNNEL_TYPE_GRE: + return "GRE" + case TUNNEL_TYPE_IP_IN_IP: + return "IP in IP" + case TUNNEL_TYPE_VXLAN: + return "VXLAN" + case TUNNEL_TYPE_NVGRE: + return "NVGRE" + case TUNNEL_TYPE_MPLS: + return "MPLS" + case TUNNEL_TYPE_MPLS_IN_GRE: + return "MPLS in GRE" + case TUNNEL_TYPE_VXLAN_GRE: + return "VXLAN GRE" + case TUNNEL_TYPE_MPLS_IN_UDP: + return "MPLS in UDP" + default: + return fmt.Sprintf("tunnel: %d", e.TunnelType) + } +} + +func (e *EncapExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_ENCAPSULATION +} + +func (e *EncapExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + SubType ExtendedCommunityAttrSubType `json:"subtype"` + TunnelType TunnelType `json:"tunnel_type"` + }{ + Type: t, + SubType: s, + TunnelType: e.TunnelType, + }) +} + +func NewEncapExtended(tunnelType TunnelType) *EncapExtended { + return &EncapExtended{ + TunnelType: tunnelType, + } +} + +type DefaultGatewayExtended struct { +} + +func (e *DefaultGatewayExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + typ, subType := e.GetTypes() + buf[0] = byte(typ) + buf[1] = byte(subType) + return buf, nil +} + +func (e *DefaultGatewayExtended) String() string { + return "default-gateway" +} + +func (e *DefaultGatewayExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_DEFAULT_GATEWAY +} + +func (e *DefaultGatewayExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + SubType ExtendedCommunityAttrSubType `json:"subtype"` + }{ + Type: t, + SubType: s, + }) +} + +func NewDefaultGatewayExtended() *DefaultGatewayExtended { + return &DefaultGatewayExtended{} +} + +type OpaqueExtended struct { + IsTransitive bool + Value []byte +} + +func (e *OpaqueExtended) Serialize() ([]byte, error) { + if len(e.Value) != 7 { + return nil, fmt.Errorf("invalid value length for opaque extended community: %d", len(e.Value)) + } + buf := make([]byte, 8) + if e.IsTransitive { + buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE) + } else { + buf[0] = byte(EC_TYPE_NON_TRANSITIVE_OPAQUE) + } + copy(buf[1:], e.Value) + return buf, nil +} + +func (e *OpaqueExtended) String() string { + buf := make([]byte, 8) + copy(buf[1:], e.Value) + return fmt.Sprintf("%d", binary.BigEndian.Uint64(buf)) +} + +func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + var subType ExtendedCommunityAttrSubType + if len(e.Value) > 0 { + // Use the first byte of value as the sub type + subType = ExtendedCommunityAttrSubType(e.Value[0]) + } + if e.IsTransitive { + return EC_TYPE_TRANSITIVE_OPAQUE, subType + } + return EC_TYPE_NON_TRANSITIVE_OPAQUE, subType +} + +func (e *OpaqueExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value []byte `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.Value, + }) +} + +func NewOpaqueExtended(isTransitive bool, value []byte) *OpaqueExtended { + v := make([]byte, 7) + copy(v, value) + return &OpaqueExtended{ + IsTransitive: isTransitive, + Value: v, + } +} + +func parseOpaqueExtended(data []byte) (ExtendedCommunityInterface, error) { + typ := ExtendedCommunityAttrType(data[0]) + isTransitive := false + switch typ { + case EC_TYPE_TRANSITIVE_OPAQUE: + isTransitive = true + case EC_TYPE_NON_TRANSITIVE_OPAQUE: + // isTransitive = false + default: + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("invalid opaque extended community type: %d", data[0])) + } + subType := ExtendedCommunityAttrSubType(data[1]) + + if isTransitive { + switch subType { + case EC_SUBTYPE_COLOR: + return &ColorExtended{ + Color: binary.BigEndian.Uint32(data[4:8]), + }, nil + case EC_SUBTYPE_ENCAPSULATION: + return &EncapExtended{ + TunnelType: TunnelType(binary.BigEndian.Uint16(data[6:8])), + }, nil + case EC_SUBTYPE_DEFAULT_GATEWAY: + return &DefaultGatewayExtended{}, nil + } + } else { + switch subType { + case EC_SUBTYPE_ORIGIN_VALIDATION: + return &ValidationExtended{ + State: ValidationState(data[7]), + }, nil + } + } + return NewOpaqueExtended(isTransitive, data[1:8]), nil +} + +type ESILabelExtended struct { + Label uint32 + IsSingleActive bool +} + +func (e *ESILabelExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_EVPN) + buf[1] = byte(EC_SUBTYPE_ESI_LABEL) + if e.IsSingleActive { + buf[2] = byte(1) + } + buf[3] = 0 + buf[4] = 0 + buf[5] = byte((e.Label >> 16) & 0xff) + buf[6] = byte((e.Label >> 8) & 0xff) + buf[7] = byte(e.Label & 0xff) + return buf, nil +} + +func (e *ESILabelExtended) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString(fmt.Sprintf("esi-label: %d", e.Label)) + if e.IsSingleActive { + buf.WriteString(", single-active") + } + return buf.String() +} + +func (e *ESILabelExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Label uint32 `json:"label"` + IsSingleActive bool `json:"is_single_active"` + }{ + Type: t, + Subtype: s, + Label: e.Label, + IsSingleActive: e.IsSingleActive, + }) +} + +func (e *ESILabelExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_EVPN, EC_SUBTYPE_ESI_LABEL +} + +func NewESILabelExtended(label uint32, isSingleActive bool) *ESILabelExtended { + return &ESILabelExtended{ + Label: label, + IsSingleActive: isSingleActive, + } +} + +type ESImportRouteTarget struct { + ESImport net.HardwareAddr +} + +func (e *ESImportRouteTarget) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_EVPN) + buf[1] = byte(EC_SUBTYPE_ES_IMPORT) + copy(buf[2:], e.ESImport) + return buf, nil +} + +func (e *ESImportRouteTarget) String() string { + return fmt.Sprintf("es-import rt: %s", e.ESImport.String()) +} + +func (e *ESImportRouteTarget) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.ESImport.String(), + }) +} + +func (e *ESImportRouteTarget) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_EVPN, EC_SUBTYPE_ES_IMPORT +} + +func NewESImportRouteTarget(mac string) *ESImportRouteTarget { + esImport, err := net.ParseMAC(mac) + if err != nil { + return nil + } + return &ESImportRouteTarget{ + ESImport: esImport, + } +} + +type MacMobilityExtended struct { + Sequence uint32 + IsSticky bool +} + +func (e *MacMobilityExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_EVPN) + buf[1] = byte(EC_SUBTYPE_MAC_MOBILITY) + if e.IsSticky { + buf[2] = byte(1) + } + binary.BigEndian.PutUint32(buf[4:], e.Sequence) + return buf, nil +} + +func (e *MacMobilityExtended) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString(fmt.Sprintf("mac-mobility: %d", e.Sequence)) + if e.IsSticky { + buf.WriteString(", sticky") + } + return buf.String() +} + +func (e *MacMobilityExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Sequence uint32 `json:"sequence"` + IsSticky bool `json:"is_sticky"` + }{ + Type: t, + Subtype: s, + Sequence: e.Sequence, + IsSticky: e.IsSticky, + }) +} + +func (e *MacMobilityExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_EVPN, EC_SUBTYPE_MAC_MOBILITY +} + +func NewMacMobilityExtended(seq uint32, isSticky bool) *MacMobilityExtended { + return &MacMobilityExtended{ + Sequence: seq, + IsSticky: isSticky, + } +} + +type RouterMacExtended struct { + Mac net.HardwareAddr +} + +func (e *RouterMacExtended) Serialize() ([]byte, error) { + buf := make([]byte, 2, 8) + buf[0] = byte(EC_TYPE_EVPN) + buf[1] = byte(EC_SUBTYPE_ROUTER_MAC) + buf = append(buf, e.Mac...) + return buf, nil +} + +func (e *RouterMacExtended) String() string { + return fmt.Sprintf("router's mac: %s", e.Mac.String()) +} + +func (e *RouterMacExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Mac string `json:"mac"` + }{ + Type: t, + Subtype: s, + Mac: e.Mac.String(), + }) +} + +func (e *RouterMacExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_EVPN, EC_SUBTYPE_ROUTER_MAC +} + +func NewRoutersMacExtended(mac string) *RouterMacExtended { + hw, err := net.ParseMAC(mac) + if err != nil { + return nil + } + return &RouterMacExtended{ + Mac: hw, + } +} + +func parseEvpnExtended(data []byte) (ExtendedCommunityInterface, error) { + if ExtendedCommunityAttrType(data[0]) != EC_TYPE_EVPN { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_EVPN: %d", data[0])) + } + subType := ExtendedCommunityAttrSubType(data[1]) + switch subType { + case EC_SUBTYPE_ESI_LABEL: + var isSingleActive bool + if data[2] > 0 { + isSingleActive = true + } + label := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) + return &ESILabelExtended{ + IsSingleActive: isSingleActive, + Label: label, + }, nil + case EC_SUBTYPE_ES_IMPORT: + return &ESImportRouteTarget{ + ESImport: net.HardwareAddr(data[2:8]), + }, nil + case EC_SUBTYPE_MAC_MOBILITY: + var isSticky bool + if data[2] > 0 { + isSticky = true + } + seq := binary.BigEndian.Uint32(data[4:8]) + return &MacMobilityExtended{ + Sequence: seq, + IsSticky: isSticky, + }, nil + case EC_SUBTYPE_ROUTER_MAC: + return &RouterMacExtended{ + Mac: net.HardwareAddr(data[2:8]), + }, nil + } + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("unknown evpn subtype: %d", subType)) +} + +type TrafficRateExtended struct { + AS uint16 + Rate float32 +} + +func (e *TrafficRateExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE) + binary.BigEndian.PutUint16(buf[2:4], e.AS) + binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(e.Rate)) + return buf, nil +} + +func (e *TrafficRateExtended) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + if e.Rate == 0 { + buf.WriteString("discard") + } else { + buf.WriteString(fmt.Sprintf("rate: %f", e.Rate)) + } + if e.AS != 0 { + buf.WriteString(fmt.Sprintf("(as: %d)", e.AS)) + } + return buf.String() +} + +func (e *TrafficRateExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + As uint16 `json:"as"` + Rate float32 `json:"rate"` + }{t, s, e.AS, e.Rate}) +} + +func (e *TrafficRateExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE +} + +func NewTrafficRateExtended(as uint16, rate float32) *TrafficRateExtended { + return &TrafficRateExtended{ + AS: as, + Rate: rate, + } +} + +type TrafficActionExtended struct { + Terminal bool + Sample bool +} + +func (e *TrafficActionExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION) + if e.Terminal { + buf[7] = 0x01 + } + if e.Sample { + buf[7] = buf[7] | 0x2 + } + return buf, nil +} + +func (e *TrafficActionExtended) String() string { + ss := make([]string, 0, 2) + if e.Terminal { + ss = append(ss, "terminal") + } + if e.Sample { + ss = append(ss, "sample") + } + return fmt.Sprintf("action: %s", strings.Join(ss, "-")) +} + +func (e *TrafficActionExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Terminal bool `json:"terminal"` + Sample bool `json:"sample"` + }{t, s, e.Terminal, e.Sample}) +} + +func (e *TrafficActionExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION +} + +func NewTrafficActionExtended(terminal bool, sample bool) *TrafficActionExtended { + return &TrafficActionExtended{ + Terminal: terminal, + Sample: sample, + } +} + +type RedirectTwoOctetAsSpecificExtended struct { + TwoOctetAsSpecificExtended +} + +func (e *RedirectTwoOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.TwoOctetAsSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectTwoOctetAsSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.TwoOctetAsSpecificExtended.String()) +} + +func (e *RedirectTwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.TwoOctetAsSpecificExtended.String()}) +} + +func (e *RedirectTwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectTwoOctetAsSpecificExtended(as uint16, localAdmin uint32) *RedirectTwoOctetAsSpecificExtended { + return &RedirectTwoOctetAsSpecificExtended{*NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)} +} + +type RedirectIPv4AddressSpecificExtended struct { + IPv4AddressSpecificExtended +} + +func (e *RedirectIPv4AddressSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.IPv4AddressSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectIPv4AddressSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.IPv4AddressSpecificExtended.String()) +} + +func (e *RedirectIPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.IPv4AddressSpecificExtended.String()}) +} + +func (e *RedirectIPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectIPv4AddressSpecificExtended(ipv4 string, localAdmin uint16) *RedirectIPv4AddressSpecificExtended { + e := NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv4, localAdmin, false) + if e == nil { + return nil + } + return &RedirectIPv4AddressSpecificExtended{*e} +} + +type RedirectIPv6AddressSpecificExtended struct { + IPv6AddressSpecificExtended +} + +func (e *RedirectIPv6AddressSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.IPv6AddressSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6) + return buf, err +} + +func (e *RedirectIPv6AddressSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.IPv6AddressSpecificExtended.String()) +} + +func (e *RedirectIPv6AddressSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.IPv6AddressSpecificExtended.String()}) +} + +func (e *RedirectIPv6AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6 +} + +func NewRedirectIPv6AddressSpecificExtended(ipv6 string, localAdmin uint16) *RedirectIPv6AddressSpecificExtended { + e := NewIPv6AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv6, localAdmin, false) + if e == nil { + return nil + } + return &RedirectIPv6AddressSpecificExtended{*e} +} + +type RedirectFourOctetAsSpecificExtended struct { + FourOctetAsSpecificExtended +} + +func (e *RedirectFourOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.FourOctetAsSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectFourOctetAsSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.FourOctetAsSpecificExtended.String()) +} + +func (e *RedirectFourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.FourOctetAsSpecificExtended.String()}) +} + +func (e *RedirectFourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectFourOctetAsSpecificExtended(as uint32, localAdmin uint16) *RedirectFourOctetAsSpecificExtended { + return &RedirectFourOctetAsSpecificExtended{*NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)} +} + +type TrafficRemarkExtended struct { + DSCP uint8 +} + +func (e *TrafficRemarkExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK) + buf[7] = byte(e.DSCP) + return buf, nil +} + +func (e *TrafficRemarkExtended) String() string { + return fmt.Sprintf("remark: %d", e.DSCP) +} + +func (e *TrafficRemarkExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value uint8 `json:"value"` + }{t, s, e.DSCP}) +} + +func (e *TrafficRemarkExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK +} + +func NewTrafficRemarkExtended(dscp uint8) *TrafficRemarkExtended { + return &TrafficRemarkExtended{ + DSCP: dscp, + } +} + +func parseFlowSpecExtended(data []byte) (ExtendedCommunityInterface, error) { + typ := ExtendedCommunityAttrType(data[0]) + if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_FLOWSPEC: %d", data[0])) + } + subType := ExtendedCommunityAttrSubType(data[1]) + switch subType { + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE: + asn := binary.BigEndian.Uint16(data[2:4]) + bits := binary.BigEndian.Uint32(data[4:8]) + rate := math.Float32frombits(bits) + return NewTrafficRateExtended(asn, rate), nil + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION: + terminal := data[7]&0x1 == 1 + sample := (data[7]>>1)&0x1 == 1 + return NewTrafficActionExtended(terminal, sample), nil + case EC_SUBTYPE_FLOWSPEC_REDIRECT: + // RFC7674 + switch typ { + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL: + as := binary.BigEndian.Uint16(data[2:4]) + localAdmin := binary.BigEndian.Uint32(data[4:8]) + return NewRedirectTwoOctetAsSpecificExtended(as, localAdmin), nil + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2: + ipv4 := net.IP(data[2:6]).String() + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewRedirectIPv4AddressSpecificExtended(ipv4, localAdmin), nil + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3: + as := binary.BigEndian.Uint32(data[2:6]) + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewRedirectFourOctetAsSpecificExtended(as, localAdmin), nil + } + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK: + dscp := data[7] + return NewTrafficRemarkExtended(dscp), nil + case EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6: + ipv6 := net.IP(data[2:18]).String() + localAdmin := binary.BigEndian.Uint16(data[18:20]) + return NewRedirectIPv6AddressSpecificExtended(ipv6, localAdmin), nil + } + return &UnknownExtended{ + Type: ExtendedCommunityAttrType(data[0]), + Value: data[1:8], + }, nil +} + +func parseIP6FlowSpecExtended(data []byte) (ExtendedCommunityInterface, error) { + typ := ExtendedCommunityAttrType(data[0]) + if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_FLOWSPEC: %d", data[0])) + } + subType := ExtendedCommunityAttrSubType(data[1]) + switch subType { + case EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6: + // RFC7674 + switch typ { + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL: + ipv6 := net.IP(data[2:18]).String() + localAdmin := binary.BigEndian.Uint16(data[18:20]) + return NewRedirectIPv6AddressSpecificExtended(ipv6, localAdmin), nil + } + } + return &UnknownExtended{ + Type: ExtendedCommunityAttrType(data[0]), + Value: data[1:20], + }, nil +} + +type UnknownExtended struct { + Type ExtendedCommunityAttrType + Value []byte +} + +func (e *UnknownExtended) Serialize() ([]byte, error) { + if len(e.Value) != 7 { + return nil, fmt.Errorf("invalid value length for unknown extended community: %d", len(e.Value)) + } + buf := make([]byte, 8) + buf[0] = uint8(e.Type) + copy(buf[1:], e.Value) + return buf, nil +} + +func (e *UnknownExtended) String() string { + buf := make([]byte, 8) + copy(buf[1:], e.Value) + return fmt.Sprintf("%d", binary.BigEndian.Uint64(buf)) +} + +func (e *UnknownExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value []byte `json:"value"` + }{ + Type: t, + Subtype: s, + Value: e.Value, + }) +} + +func (e *UnknownExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + var subType ExtendedCommunityAttrSubType + if len(e.Value) > 0 { + // Use the first byte of value as the sub type + subType = ExtendedCommunityAttrSubType(e.Value[0]) + } + return e.Type, subType +} + +func NewUnknownExtended(typ ExtendedCommunityAttrType, value []byte) *UnknownExtended { + v := make([]byte, 7) + copy(v, value) + return &UnknownExtended{ + Type: typ, + Value: v, + } +} + +type PathAttributeExtendedCommunities struct { + PathAttribute + Value []ExtendedCommunityInterface +} + +func ParseExtended(data []byte) (ExtendedCommunityInterface, error) { + if len(data) < 8 { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all extended community bytes are available") + } + attrType := ExtendedCommunityAttrType(data[0]) + subtype := ExtendedCommunityAttrSubType(data[1]) + transitive := false + switch attrType { + case EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: + transitive = true + fallthrough + case EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: + as := binary.BigEndian.Uint16(data[2:4]) + localAdmin := binary.BigEndian.Uint32(data[4:8]) + return NewTwoOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil + case EC_TYPE_TRANSITIVE_IP4_SPECIFIC: + transitive = true + fallthrough + case EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC: + ipv4 := net.IP(data[2:6]).String() + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewIPv4AddressSpecificExtended(subtype, ipv4, localAdmin, transitive), nil + case EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: + transitive = true + fallthrough + case EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: + as := binary.BigEndian.Uint32(data[2:6]) + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewFourOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil + case EC_TYPE_TRANSITIVE_OPAQUE: + transitive = true + fallthrough + case EC_TYPE_NON_TRANSITIVE_OPAQUE: + return parseOpaqueExtended(data) + case EC_TYPE_EVPN: + return parseEvpnExtended(data) + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3: + return parseFlowSpecExtended(data) + default: + return &UnknownExtended{ + Type: ExtendedCommunityAttrType(data[0]), + Value: data[1:8], + }, nil + } +} + +func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length%8 != 0 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR) + return NewMessageError(eCode, eSubCode, nil, "extendedcommunities length isn't correct") + } + for len(value) >= 8 { + e, err := ParseExtended(value) + if err != nil { + return err + } + p.Value = append(p.Value, e) + value = value[8:] + } + return nil +} + +func (p *PathAttributeExtendedCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 0) + for _, p := range p.Value { + ebuf, err := p.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, ebuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeExtendedCommunities) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + for idx, v := range p.Value { + buf.WriteString("[") + buf.WriteString(v.String()) + buf.WriteString("]") + if idx < len(p.Value)-1 { + buf.WriteString(", ") + } + } + return fmt.Sprintf("{Extcomms: %s}", buf.String()) +} + +func (p *PathAttributeExtendedCommunities) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []ExtendedCommunityInterface `json:"value"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeExtendedCommunities(value []ExtendedCommunityInterface) *PathAttributeExtendedCommunities { + l := len(value) * 8 + t := BGP_ATTR_TYPE_EXTENDED_COMMUNITIES + return &PathAttributeExtendedCommunities{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: value, + } +} + +type PathAttributeAs4Path struct { + PathAttribute + Value []*As4PathParam +} + +func (p *PathAttributeAs4Path) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + isAs4, err := validateAsPathValueBytes(value) + if err != nil { + return err + } + + if !isAs4 { + return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed") + } + + for len(value) > 0 { + tuple := &As4PathParam{} + tuple.DecodeFromBytes(value) + p.Value = append(p.Value, tuple) + if len(value) < tuple.Len() { + return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed") + } + value = value[tuple.Len():] + } + return nil +} + +func (p *PathAttributeAs4Path) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 0) + for _, v := range p.Value { + vbuf, err := v.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, vbuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeAs4Path) String() string { + params := make([]string, 0, len(p.Value)) + for _, param := range p.Value { + params = append(params, param.String()) + } + return strings.Join(params, " ") +} + +func (p *PathAttributeAs4Path) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []*As4PathParam `json:"as_paths"` + }{ + Type: p.GetType(), + Value: p.Value, + }) +} + +func NewPathAttributeAs4Path(value []*As4PathParam) *PathAttributeAs4Path { + var l int + for _, v := range value { + l += v.Len() + } + t := BGP_ATTR_TYPE_AS4_PATH + return &PathAttributeAs4Path{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: value, + } +} + +type PathAttributeAs4Aggregator struct { + PathAttribute + Value PathAttributeAggregatorParam +} + +func (p *PathAttributeAs4Aggregator) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length != 8 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "AS4 Aggregator length is incorrect") + } + p.Value.AS = binary.BigEndian.Uint32(value[0:4]) + p.Value.Address = value[4:] + return nil +} + +func (p *PathAttributeAs4Aggregator) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 8) + binary.BigEndian.PutUint32(buf[0:], p.Value.AS) + copy(buf[4:], p.Value.Address.To4()) + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeAs4Aggregator) String() string { + return fmt.Sprintf("{As4Aggregator: {AS: %d, Address: %s}}", p.Value.AS, p.Value.Address) +} + +func (p *PathAttributeAs4Aggregator) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + AS uint32 `json:"as"` + Address string `json:"address"` + }{ + Type: p.GetType(), + AS: p.Value.AS, + Address: p.Value.Address.String(), + }) +} + +func NewPathAttributeAs4Aggregator(as uint32, address string) *PathAttributeAs4Aggregator { + t := BGP_ATTR_TYPE_AS4_AGGREGATOR + return &PathAttributeAs4Aggregator{ + PathAttribute: PathAttribute{ + Flags: PathAttrFlags[t], + Type: t, + Length: 8, + }, + Value: PathAttributeAggregatorParam{ + AS: as, + Address: net.ParseIP(address).To4(), + }, + } +} + +type TunnelEncapSubTLVInterface interface { + Len() int + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) + String() string + MarshalJSON() ([]byte, error) +} + +type TunnelEncapSubTLV struct { + Type EncapSubTLVType + Length uint16 +} + +func (t *TunnelEncapSubTLV) Len() int { + if t.Type >= 0x80 { + return 3 + int(t.Length) + } + return 2 + int(t.Length) +} + +func (t *TunnelEncapSubTLV) DecodeFromBytes(data []byte) (value []byte, err error) { + t.Type = EncapSubTLVType(data[0]) + if t.Type >= 0x80 { + t.Length = binary.BigEndian.Uint16(data[1:3]) + data = data[3:] + } else { + t.Length = uint16(data[1]) + data = data[2:] + } + if len(data) < int(t.Length) { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLV bytes available") + } + return data, nil +} + +func (t *TunnelEncapSubTLV) Serialize(value []byte) (buf []byte, err error) { + t.Length = uint16(len(value)) + if t.Type >= 0x80 { + buf = append(make([]byte, 3), value...) + binary.BigEndian.PutUint16(buf[1:3], t.Length) + } else { + buf = append(make([]byte, 2), value...) + buf[1] = uint8(t.Length) + } + buf[0] = uint8(t.Type) + return buf, nil +} + +type TunnelEncapSubTLVUnknown struct { + TunnelEncapSubTLV + Value []byte +} + +func (t *TunnelEncapSubTLVUnknown) DecodeFromBytes(data []byte) error { + value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data) + if err != nil { + return err + } + t.Value = value + return nil +} + +func (t *TunnelEncapSubTLVUnknown) Serialize() ([]byte, error) { + return t.TunnelEncapSubTLV.Serialize(t.Value) +} + +func (t *TunnelEncapSubTLVUnknown) String() string { + return fmt.Sprintf("{Type: %d, Value: %x}", t.Type, t.Value) +} + +func (t *TunnelEncapSubTLVUnknown) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type EncapSubTLVType `json:"type"` + Value []byte `json:"value"` + }{ + Type: t.Type, + Value: t.Value, + }) +} + +func NewTunnelEncapSubTLVUnknown(typ EncapSubTLVType, value []byte) *TunnelEncapSubTLVUnknown { + return &TunnelEncapSubTLVUnknown{ + TunnelEncapSubTLV: TunnelEncapSubTLV{ + Type: typ, + }, + Value: value, + } +} + +type TunnelEncapSubTLVEncapsulation struct { + TunnelEncapSubTLV + Key uint32 // this represent both SessionID for L2TPv3 case and GRE-key for GRE case (RFC5512 4.) + Cookie []byte +} + +func (t *TunnelEncapSubTLVEncapsulation) DecodeFromBytes(data []byte) error { + value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data) + if err != nil { + return err + } + if t.Length < 4 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVEncapsulation bytes available") + } + t.Key = binary.BigEndian.Uint32(value[0:4]) + t.Cookie = value[4:] + return nil +} + +func (t *TunnelEncapSubTLVEncapsulation) Serialize() ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, t.Key) + buf = append(buf, t.Cookie...) + return t.TunnelEncapSubTLV.Serialize(buf) +} + +func (t *TunnelEncapSubTLVEncapsulation) String() string { + return fmt.Sprintf("{Key: %d, Cookie: %x}", t.Key, t.Cookie) +} + +func (t *TunnelEncapSubTLVEncapsulation) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type EncapSubTLVType `json:"type"` + Key uint32 `json:"key"` + Cookie []byte `json:"cookie"` + }{ + Type: t.Type, + Key: t.Key, + Cookie: t.Cookie, + }) +} + +func NewTunnelEncapSubTLVEncapsulation(key uint32, cookie []byte) *TunnelEncapSubTLVEncapsulation { + return &TunnelEncapSubTLVEncapsulation{ + TunnelEncapSubTLV: TunnelEncapSubTLV{ + Type: ENCAP_SUBTLV_TYPE_ENCAPSULATION, + }, + Key: key, + Cookie: cookie, + } +} + +type TunnelEncapSubTLVProtocol struct { + TunnelEncapSubTLV + Protocol uint16 +} + +func (t *TunnelEncapSubTLVProtocol) DecodeFromBytes(data []byte) error { + value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data) + if err != nil { + return err + } + if t.Length < 2 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVProtocol bytes available") + } + t.Protocol = binary.BigEndian.Uint16(value[0:2]) + return nil +} + +func (t *TunnelEncapSubTLVProtocol) Serialize() ([]byte, error) { + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, t.Protocol) + return t.TunnelEncapSubTLV.Serialize(buf) +} + +func (t *TunnelEncapSubTLVProtocol) String() string { + return fmt.Sprintf("{Protocol: %d}", t.Protocol) +} + +func (t *TunnelEncapSubTLVProtocol) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type EncapSubTLVType `json:"type"` + Protocol uint16 `json:"protocol"` + }{ + Type: t.Type, + Protocol: t.Protocol, + }) +} + +func NewTunnelEncapSubTLVProtocol(protocol uint16) *TunnelEncapSubTLVProtocol { + return &TunnelEncapSubTLVProtocol{ + TunnelEncapSubTLV: TunnelEncapSubTLV{ + Type: ENCAP_SUBTLV_TYPE_PROTOCOL, + }, + Protocol: protocol, + } +} + +type TunnelEncapSubTLVColor struct { + TunnelEncapSubTLV + Color uint32 +} + +func (t *TunnelEncapSubTLVColor) DecodeFromBytes(data []byte) error { + value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data) + if err != nil { + return err + } + if t.Length != 8 { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Invalid TunnelEncapSubTLVColor length") + } + t.Color = binary.BigEndian.Uint32(value[4:8]) + return nil +} + +func (t *TunnelEncapSubTLVColor) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE) + buf[1] = byte(EC_SUBTYPE_COLOR) + binary.BigEndian.PutUint32(buf[4:8], t.Color) + return t.TunnelEncapSubTLV.Serialize(buf) +} + +func (t *TunnelEncapSubTLVColor) String() string { + return fmt.Sprintf("{Color: %d}", t.Color) +} + +func (t *TunnelEncapSubTLVColor) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type EncapSubTLVType `json:"type"` + Color uint32 `json:"color"` + }{ + Type: t.Type, + Color: t.Color, + }) +} + +func NewTunnelEncapSubTLVColor(color uint32) *TunnelEncapSubTLVColor { + return &TunnelEncapSubTLVColor{ + TunnelEncapSubTLV: TunnelEncapSubTLV{ + Type: ENCAP_SUBTLV_TYPE_COLOR, + }, + Color: color, + } +} + +type TunnelEncapTLV struct { + Type TunnelType + Length uint16 + Value []TunnelEncapSubTLVInterface +} + +func (t *TunnelEncapTLV) Len() int { + var l int + for _, v := range t.Value { + l += v.Len() + } + return 4 + l // Type(2) + Length(2) + Value(variable) +} + +func (t *TunnelEncapTLV) DecodeFromBytes(data []byte) error { + t.Type = TunnelType(binary.BigEndian.Uint16(data[0:2])) + t.Length = binary.BigEndian.Uint16(data[2:4]) + data = data[4:] + if len(data) < int(t.Length) { + return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Not all TunnelEncapTLV bytes available")) + } + value := data[:t.Length] + for len(value) > 2 { + subType := EncapSubTLVType(value[0]) + var subTlv TunnelEncapSubTLVInterface + switch subType { + case ENCAP_SUBTLV_TYPE_ENCAPSULATION: + subTlv = &TunnelEncapSubTLVEncapsulation{} + case ENCAP_SUBTLV_TYPE_PROTOCOL: + subTlv = &TunnelEncapSubTLVProtocol{} + case ENCAP_SUBTLV_TYPE_COLOR: + subTlv = &TunnelEncapSubTLVColor{} + default: + subTlv = &TunnelEncapSubTLVUnknown{ + TunnelEncapSubTLV: TunnelEncapSubTLV{ + Type: subType, + }, + } + } + err := subTlv.DecodeFromBytes(value) + if err != nil { + return err + } + t.Value = append(t.Value, subTlv) + value = value[subTlv.Len():] + } + return nil +} + +func (p *TunnelEncapTLV) Serialize() ([]byte, error) { + buf := make([]byte, 4) + for _, t := range p.Value { + tBuf, err := t.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, tBuf...) + } + binary.BigEndian.PutUint16(buf, uint16(p.Type)) + binary.BigEndian.PutUint16(buf[2:], uint16(len(buf)-4)) + return buf, nil +} + +func (p *TunnelEncapTLV) String() string { + tlvList := make([]string, len(p.Value)) + for i, v := range p.Value { + tlvList[i] = v.String() + } + return fmt.Sprintf("{%s: %s}", p.Type, strings.Join(tlvList, ", ")) +} + +func (p *TunnelEncapTLV) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type TunnelType `json:"type"` + Value []TunnelEncapSubTLVInterface `json:"value"` + }{ + Type: p.Type, + Value: p.Value, + }) +} + +func NewTunnelEncapTLV(typ TunnelType, value []TunnelEncapSubTLVInterface) *TunnelEncapTLV { + return &TunnelEncapTLV{ + Type: typ, + Value: value, + } +} + +type PathAttributeTunnelEncap struct { + PathAttribute + Value []*TunnelEncapTLV +} + +func (p *PathAttributeTunnelEncap) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + for len(value) > 4 { + tlv := &TunnelEncapTLV{} + err = tlv.DecodeFromBytes(value) + if err != nil { + return err + } + p.Value = append(p.Value, tlv) + value = value[4+tlv.Length:] + } + return nil +} + +func (p *PathAttributeTunnelEncap) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 0) + for _, t := range p.Value { + bbuf, err := t.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + } + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributeTunnelEncap) String() string { + tlvList := make([]string, len(p.Value)) + for i, v := range p.Value { + tlvList[i] = v.String() + } + return fmt.Sprintf("{TunnelEncap: %s}", strings.Join(tlvList, ", ")) +} + +func (p *PathAttributeTunnelEncap) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + Value []*TunnelEncapTLV `json:"value"` + }{ + Type: p.Type, + Value: p.Value, + }) +} + +func NewPathAttributeTunnelEncap(value []*TunnelEncapTLV) *PathAttributeTunnelEncap { + var l int + for _, v := range value { + l += v.Len() + } + t := BGP_ATTR_TYPE_TUNNEL_ENCAP + return &PathAttributeTunnelEncap{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + Value: value, + } +} + +type PmsiTunnelIDInterface interface { + Len() int + Serialize() ([]byte, error) + String() string +} + +type DefaultPmsiTunnelID struct { + Value []byte +} + +func (i *DefaultPmsiTunnelID) Len() int { + return len(i.Value) +} + +func (i *DefaultPmsiTunnelID) Serialize() ([]byte, error) { + return i.Value, nil +} + +func (i *DefaultPmsiTunnelID) String() string { + return string(i.Value) +} + +func NewDefaultPmsiTunnelID(value []byte) *DefaultPmsiTunnelID { + return &DefaultPmsiTunnelID{ + Value: value, + } +} + +type IngressReplTunnelID struct { + Value net.IP +} + +func (i *IngressReplTunnelID) Len() int { + return len(i.Value) +} + +func (i *IngressReplTunnelID) Serialize() ([]byte, error) { + if i.Value.To4() != nil { + return []byte(i.Value.To4()), nil + } + return []byte(i.Value), nil +} + +func (i *IngressReplTunnelID) String() string { + return i.Value.String() +} + +func NewIngressReplTunnelID(value string) *IngressReplTunnelID { + ip := net.ParseIP(value) + if ip == nil { + return nil + } + return &IngressReplTunnelID{ + Value: ip, + } +} + +type PathAttributePmsiTunnel struct { + PathAttribute + IsLeafInfoRequired bool + TunnelType PmsiTunnelType + Label uint32 + TunnelID PmsiTunnelIDInterface +} + +func (p *PathAttributePmsiTunnel) DecodeFromBytes(data []byte, options ...*MarshallingOption) error { + value, err := p.PathAttribute.DecodeFromBytes(data, options...) + if err != nil { + return err + } + if p.Length < 5 { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + return NewMessageError(eCode, eSubCode, nil, "PMSI Tunnel length is incorrect") + } + + if (value[0] & 0x01) > 0 { + p.IsLeafInfoRequired = true + } + p.TunnelType = PmsiTunnelType(value[1]) + if p.Label, err = labelDecode(value[2:5]); err != nil { + return err + } + + switch p.TunnelType { + case PMSI_TUNNEL_TYPE_INGRESS_REPL: + p.TunnelID = &IngressReplTunnelID{net.IP(value[5:])} + default: + p.TunnelID = &DefaultPmsiTunnelID{value[5:]} + } + return nil +} + +func (p *PathAttributePmsiTunnel) Serialize(options ...*MarshallingOption) ([]byte, error) { + buf := make([]byte, 2) + if p.IsLeafInfoRequired { + buf[0] = 0x01 + } + buf[1] = byte(p.TunnelType) + tbuf, err := labelSerialize(p.Label) + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + tbuf, err = p.TunnelID.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, tbuf...) + return p.PathAttribute.Serialize(buf, options...) +} + +func (p *PathAttributePmsiTunnel) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString(fmt.Sprintf("{Pmsi: type: %s,", p.TunnelType)) + if p.IsLeafInfoRequired { + buf.WriteString(" leaf-info-required,") + } + buf.WriteString(fmt.Sprintf(" label: %d, tunnel-id: %s}", p.Label, p.TunnelID)) + return buf.String() +} + +func (p *PathAttributePmsiTunnel) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPAttrType `json:"type"` + IsLeafInfoRequired bool `json:"is-leaf-info-required"` + TunnelType uint8 `json:"tunnel-type"` + Label uint32 `json:"label"` + TunnelID string `json:"tunnel-id"` + }{ + Type: p.Type, + IsLeafInfoRequired: p.IsLeafInfoRequired, + TunnelType: uint8(p.TunnelType), + Label: p.Label, + TunnelID: p.TunnelID.String(), + }) +} + +func NewPathAttributePmsiTunnel(typ PmsiTunnelType, isLeafInfoRequired bool, label uint32, id PmsiTunnelIDInterface) *PathAttributePmsiTunnel { + if id == nil { + return nil + } + // Flags(1) + TunnelType(1) + Label(3) + TunnelID(variable) + l := 5 + id.Len() + t := BGP_ATTR_TYPE_PMSI_TUNNEL + return &PathAttributePmsiTunnel{ + PathAttribute: PathAttribute{ + Flags: getPathAttrFlags(t, l), + Type: t, + Length: uint16(l), + }, + IsLeafInfoRequired: isLeafInfoRequired, + TunnelType: typ, + Label: label, + TunnelID: id, + } +} + +func ParsePmsiTunnel(args []string) (*PathAttributePmsiTunnel, error) { + // Format: + // "" ["leaf-info-required"] "