Repository: containers/dnsname Branch: main Commit: bdc4ab85266a Files: 763 Total size: 12.7 MB Directory structure: gitextract_sf87iksg/ ├── .cirrus.yml ├── .github/ │ └── renovate.json5 ├── .gitignore ├── CODE-OF-CONDUCT.md ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── README_PODMAN.md ├── RELEASE_NOTES.md ├── SECURITY.md ├── ci/ │ └── Dockerfile ├── example/ │ └── foobar.conflist ├── go.mod ├── go.sum ├── hack/ │ ├── get_ci_vm.sh │ └── tree_status.sh ├── plugins/ │ └── meta/ │ └── dnsname/ │ ├── config.go │ ├── dnsname_suite_test.go │ ├── dnsname_test.go │ ├── files.go │ ├── files_test.go │ ├── main.go │ ├── result.go │ ├── service.go │ └── version.go └── vendor/ ├── github.com/ │ ├── alexflint/ │ │ └── go-filemutex/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── filemutex_flock.go │ │ └── filemutex_windows.go │ ├── containernetworking/ │ │ ├── cni/ │ │ │ ├── LICENSE │ │ │ └── pkg/ │ │ │ ├── skel/ │ │ │ │ └── skel.go │ │ │ ├── types/ │ │ │ │ ├── 020/ │ │ │ │ │ └── types.go │ │ │ │ ├── 040/ │ │ │ │ │ └── types.go │ │ │ │ ├── 100/ │ │ │ │ │ └── types.go │ │ │ │ ├── args.go │ │ │ │ ├── create/ │ │ │ │ │ └── create.go │ │ │ │ ├── internal/ │ │ │ │ │ ├── convert.go │ │ │ │ │ └── create.go │ │ │ │ └── types.go │ │ │ ├── utils/ │ │ │ │ └── utils.go │ │ │ └── version/ │ │ │ ├── conf.go │ │ │ ├── plugin.go │ │ │ ├── reconcile.go │ │ │ └── version.go │ │ └── plugins/ │ │ ├── LICENSE │ │ ├── pkg/ │ │ │ ├── ns/ │ │ │ │ ├── README.md │ │ │ │ └── ns_linux.go │ │ │ └── testutils/ │ │ │ ├── bad_reader.go │ │ │ ├── cmd.go │ │ │ ├── dns.go │ │ │ ├── netns_linux.go │ │ │ └── ping.go │ │ └── plugins/ │ │ └── ipam/ │ │ └── host-local/ │ │ └── backend/ │ │ ├── disk/ │ │ │ ├── backend.go │ │ │ └── lock.go │ │ └── store.go │ ├── coreos/ │ │ └── go-iptables/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ └── iptables/ │ │ ├── iptables.go │ │ └── lock.go │ ├── fsnotify/ │ │ └── fsnotify/ │ │ ├── .editorconfig │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fen.go │ │ ├── fsnotify.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── inotify.go │ │ ├── inotify_poller.go │ │ ├── kqueue.go │ │ ├── open_mode_bsd.go │ │ ├── open_mode_darwin.go │ │ └── windows.go │ ├── nxadm/ │ │ └── tail/ │ │ ├── .gitignore │ │ ├── CHANGES.md │ │ ├── Dockerfile │ │ ├── LICENSE │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ ├── ratelimiter/ │ │ │ ├── Licence │ │ │ ├── leakybucket.go │ │ │ ├── memory.go │ │ │ └── storage.go │ │ ├── tail.go │ │ ├── tail_posix.go │ │ ├── tail_windows.go │ │ ├── util/ │ │ │ └── util.go │ │ ├── watch/ │ │ │ ├── filechanges.go │ │ │ ├── inotify.go │ │ │ ├── inotify_tracker.go │ │ │ ├── polling.go │ │ │ └── watch.go │ │ └── winfile/ │ │ └── winfile.go │ ├── onsi/ │ │ ├── ginkgo/ │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── RELEASING.md │ │ │ ├── config/ │ │ │ │ └── config.go │ │ │ ├── formatter/ │ │ │ │ └── formatter.go │ │ │ ├── ginkgo_dsl.go │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── internal/ │ │ │ │ ├── codelocation/ │ │ │ │ │ └── code_location.go │ │ │ │ ├── containernode/ │ │ │ │ │ └── container_node.go │ │ │ │ ├── failer/ │ │ │ │ │ └── failer.go │ │ │ │ ├── global/ │ │ │ │ │ └── init.go │ │ │ │ ├── leafnodes/ │ │ │ │ │ ├── benchmarker.go │ │ │ │ │ ├── interfaces.go │ │ │ │ │ ├── it_node.go │ │ │ │ │ ├── measure_node.go │ │ │ │ │ ├── runner.go │ │ │ │ │ ├── setup_nodes.go │ │ │ │ │ ├── suite_nodes.go │ │ │ │ │ ├── synchronized_after_suite_node.go │ │ │ │ │ └── synchronized_before_suite_node.go │ │ │ │ ├── remote/ │ │ │ │ │ ├── aggregator.go │ │ │ │ │ ├── forwarding_reporter.go │ │ │ │ │ ├── output_interceptor.go │ │ │ │ │ ├── output_interceptor_unix.go │ │ │ │ │ ├── output_interceptor_win.go │ │ │ │ │ └── server.go │ │ │ │ ├── spec/ │ │ │ │ │ ├── spec.go │ │ │ │ │ └── specs.go │ │ │ │ ├── spec_iterator/ │ │ │ │ │ ├── index_computer.go │ │ │ │ │ ├── parallel_spec_iterator.go │ │ │ │ │ ├── serial_spec_iterator.go │ │ │ │ │ ├── sharded_parallel_spec_iterator.go │ │ │ │ │ └── spec_iterator.go │ │ │ │ ├── specrunner/ │ │ │ │ │ ├── random_id.go │ │ │ │ │ └── spec_runner.go │ │ │ │ ├── suite/ │ │ │ │ │ └── suite.go │ │ │ │ ├── testingtproxy/ │ │ │ │ │ └── testing_t_proxy.go │ │ │ │ └── writer/ │ │ │ │ ├── fake_writer.go │ │ │ │ └── writer.go │ │ │ ├── reporters/ │ │ │ │ ├── default_reporter.go │ │ │ │ ├── fake_reporter.go │ │ │ │ ├── junit_reporter.go │ │ │ │ ├── reporter.go │ │ │ │ ├── stenographer/ │ │ │ │ │ ├── console_logging.go │ │ │ │ │ ├── fake_stenographer.go │ │ │ │ │ ├── stenographer.go │ │ │ │ │ └── support/ │ │ │ │ │ ├── go-colorable/ │ │ │ │ │ │ ├── LICENSE │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── colorable_others.go │ │ │ │ │ │ ├── colorable_windows.go │ │ │ │ │ │ └── noncolorable.go │ │ │ │ │ └── go-isatty/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── isatty_appengine.go │ │ │ │ │ ├── isatty_bsd.go │ │ │ │ │ ├── isatty_linux.go │ │ │ │ │ ├── isatty_solaris.go │ │ │ │ │ └── isatty_windows.go │ │ │ │ └── teamcity_reporter.go │ │ │ └── types/ │ │ │ ├── code_location.go │ │ │ ├── deprecation_support.go │ │ │ ├── synchronization.go │ │ │ └── types.go │ │ └── gomega/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── Dockerfile │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── RELEASING.md │ │ ├── docker-compose.yaml │ │ ├── format/ │ │ │ └── format.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── gomega_dsl.go │ │ ├── internal/ │ │ │ ├── assertion.go │ │ │ ├── async_assertion.go │ │ │ ├── duration_bundle.go │ │ │ └── gomega.go │ │ ├── matchers/ │ │ │ ├── and.go │ │ │ ├── assignable_to_type_of_matcher.go │ │ │ ├── attributes_slice.go │ │ │ ├── be_a_directory.go │ │ │ ├── be_a_regular_file.go │ │ │ ├── be_an_existing_file.go │ │ │ ├── be_closed_matcher.go │ │ │ ├── be_element_of_matcher.go │ │ │ ├── be_empty_matcher.go │ │ │ ├── be_equivalent_to_matcher.go │ │ │ ├── be_false_matcher.go │ │ │ ├── be_identical_to.go │ │ │ ├── be_nil_matcher.go │ │ │ ├── be_numerically_matcher.go │ │ │ ├── be_sent_matcher.go │ │ │ ├── be_temporally_matcher.go │ │ │ ├── be_true_matcher.go │ │ │ ├── be_zero_matcher.go │ │ │ ├── consist_of.go │ │ │ ├── contain_element_matcher.go │ │ │ ├── contain_elements_matcher.go │ │ │ ├── contain_substring_matcher.go │ │ │ ├── equal_matcher.go │ │ │ ├── have_cap_matcher.go │ │ │ ├── have_field.go │ │ │ ├── have_http_body_matcher.go │ │ │ ├── have_http_header_with_value_matcher.go │ │ │ ├── have_http_status_matcher.go │ │ │ ├── have_key_matcher.go │ │ │ ├── have_key_with_value_matcher.go │ │ │ ├── have_len_matcher.go │ │ │ ├── have_occurred_matcher.go │ │ │ ├── have_prefix_matcher.go │ │ │ ├── have_suffix_matcher.go │ │ │ ├── match_error_matcher.go │ │ │ ├── match_json_matcher.go │ │ │ ├── match_regexp_matcher.go │ │ │ ├── match_xml_matcher.go │ │ │ ├── match_yaml_matcher.go │ │ │ ├── not.go │ │ │ ├── or.go │ │ │ ├── panic_matcher.go │ │ │ ├── receive_matcher.go │ │ │ ├── satisfy_matcher.go │ │ │ ├── semi_structured_data_support.go │ │ │ ├── succeed_matcher.go │ │ │ ├── support/ │ │ │ │ └── goraph/ │ │ │ │ ├── bipartitegraph/ │ │ │ │ │ ├── bipartitegraph.go │ │ │ │ │ └── bipartitegraphmatching.go │ │ │ │ ├── edge/ │ │ │ │ │ └── edge.go │ │ │ │ ├── node/ │ │ │ │ │ └── node.go │ │ │ │ └── util/ │ │ │ │ └── util.go │ │ │ ├── type_support.go │ │ │ └── with_transform.go │ │ ├── matchers.go │ │ └── types/ │ │ └── types.go │ ├── pkg/ │ │ └── errors/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── appveyor.yml │ │ ├── errors.go │ │ ├── go113.go │ │ └── stack.go │ └── vishvananda/ │ ├── netlink/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── addr.go │ │ ├── addr_linux.go │ │ ├── bpf_linux.go │ │ ├── bridge_linux.go │ │ ├── class.go │ │ ├── class_linux.go │ │ ├── conntrack_linux.go │ │ ├── conntrack_unspecified.go │ │ ├── devlink_linux.go │ │ ├── filter.go │ │ ├── filter_linux.go │ │ ├── fou.go │ │ ├── fou_linux.go │ │ ├── fou_unspecified.go │ │ ├── genetlink_linux.go │ │ ├── genetlink_unspecified.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── gtp_linux.go │ │ ├── handle_linux.go │ │ ├── handle_unspecified.go │ │ ├── inet_diag.go │ │ ├── ioctl_linux.go │ │ ├── ipset_linux.go │ │ ├── link.go │ │ ├── link_linux.go │ │ ├── link_tuntap_linux.go │ │ ├── neigh.go │ │ ├── neigh_linux.go │ │ ├── netlink.go │ │ ├── netlink_linux.go │ │ ├── netlink_unspecified.go │ │ ├── netns_linux.go │ │ ├── netns_unspecified.go │ │ ├── nl/ │ │ │ ├── addr_linux.go │ │ │ ├── bridge_linux.go │ │ │ ├── conntrack_linux.go │ │ │ ├── devlink_linux.go │ │ │ ├── genetlink_linux.go │ │ │ ├── ipset_linux.go │ │ │ ├── link_linux.go │ │ │ ├── lwt_linux.go │ │ │ ├── mpls_linux.go │ │ │ ├── nl_linux.go │ │ │ ├── nl_unspecified.go │ │ │ ├── parse_attr_linux.go │ │ │ ├── rdma_link_linux.go │ │ │ ├── route_linux.go │ │ │ ├── seg6_linux.go │ │ │ ├── seg6local_linux.go │ │ │ ├── syscall.go │ │ │ ├── tc_linux.go │ │ │ ├── xfrm_linux.go │ │ │ ├── xfrm_monitor_linux.go │ │ │ ├── xfrm_policy_linux.go │ │ │ └── xfrm_state_linux.go │ │ ├── order.go │ │ ├── protinfo.go │ │ ├── protinfo_linux.go │ │ ├── qdisc.go │ │ ├── qdisc_linux.go │ │ ├── rdma_link_linux.go │ │ ├── route.go │ │ ├── route_linux.go │ │ ├── route_unspecified.go │ │ ├── rule.go │ │ ├── rule_linux.go │ │ ├── socket.go │ │ ├── socket_linux.go │ │ ├── tcp.go │ │ ├── tcp_linux.go │ │ ├── xfrm.go │ │ ├── xfrm_monitor_linux.go │ │ ├── xfrm_policy.go │ │ ├── xfrm_policy_linux.go │ │ ├── xfrm_state.go │ │ └── xfrm_state_linux.go │ └── netns/ │ ├── LICENSE │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── netns.go │ ├── netns_linux.go │ └── netns_unspecified.go ├── golang.org/ │ └── x/ │ ├── net/ │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── html/ │ │ ├── atom/ │ │ │ ├── atom.go │ │ │ └── table.go │ │ ├── charset/ │ │ │ └── charset.go │ │ ├── const.go │ │ ├── doc.go │ │ ├── doctype.go │ │ ├── entity.go │ │ ├── escape.go │ │ ├── foreign.go │ │ ├── node.go │ │ ├── parse.go │ │ ├── render.go │ │ └── token.go │ ├── sys/ │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── internal/ │ │ │ └── unsafeheader/ │ │ │ └── unsafeheader.go │ │ ├── unix/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── affinity_linux.go │ │ │ ├── aliases.go │ │ │ ├── asm_aix_ppc64.s │ │ │ ├── asm_bsd_386.s │ │ │ ├── asm_bsd_amd64.s │ │ │ ├── asm_bsd_arm.s │ │ │ ├── asm_bsd_arm64.s │ │ │ ├── asm_bsd_riscv64.s │ │ │ ├── asm_linux_386.s │ │ │ ├── asm_linux_amd64.s │ │ │ ├── asm_linux_arm.s │ │ │ ├── asm_linux_arm64.s │ │ │ ├── asm_linux_loong64.s │ │ │ ├── asm_linux_mips64x.s │ │ │ ├── asm_linux_mipsx.s │ │ │ ├── asm_linux_ppc64x.s │ │ │ ├── asm_linux_riscv64.s │ │ │ ├── asm_linux_s390x.s │ │ │ ├── asm_openbsd_mips64.s │ │ │ ├── asm_solaris_amd64.s │ │ │ ├── asm_zos_s390x.s │ │ │ ├── bluetooth_linux.go │ │ │ ├── cap_freebsd.go │ │ │ ├── constants.go │ │ │ ├── dev_aix_ppc.go │ │ │ ├── dev_aix_ppc64.go │ │ │ ├── dev_darwin.go │ │ │ ├── dev_dragonfly.go │ │ │ ├── dev_freebsd.go │ │ │ ├── dev_linux.go │ │ │ ├── dev_netbsd.go │ │ │ ├── dev_openbsd.go │ │ │ ├── dev_zos.go │ │ │ ├── dirent.go │ │ │ ├── endian_big.go │ │ │ ├── endian_little.go │ │ │ ├── env_unix.go │ │ │ ├── epoll_zos.go │ │ │ ├── fcntl.go │ │ │ ├── fcntl_darwin.go │ │ │ ├── fcntl_linux_32bit.go │ │ │ ├── fdset.go │ │ │ ├── fstatfs_zos.go │ │ │ ├── gccgo.go │ │ │ ├── gccgo_c.c │ │ │ ├── gccgo_linux_amd64.go │ │ │ ├── ifreq_linux.go │ │ │ ├── ioctl.go │ │ │ ├── ioctl_linux.go │ │ │ ├── ioctl_zos.go │ │ │ ├── mkall.sh │ │ │ ├── mkerrors.sh │ │ │ ├── pagesize_unix.go │ │ │ ├── pledge_openbsd.go │ │ │ ├── ptrace_darwin.go │ │ │ ├── ptrace_ios.go │ │ │ ├── race.go │ │ │ ├── race0.go │ │ │ ├── readdirent_getdents.go │ │ │ ├── readdirent_getdirentries.go │ │ │ ├── sockcmsg_dragonfly.go │ │ │ ├── sockcmsg_linux.go │ │ │ ├── sockcmsg_unix.go │ │ │ ├── sockcmsg_unix_other.go │ │ │ ├── str.go │ │ │ ├── syscall.go │ │ │ ├── syscall_aix.go │ │ │ ├── syscall_aix_ppc.go │ │ │ ├── syscall_aix_ppc64.go │ │ │ ├── syscall_bsd.go │ │ │ ├── syscall_darwin.1_12.go │ │ │ ├── syscall_darwin.1_13.go │ │ │ ├── syscall_darwin.go │ │ │ ├── syscall_darwin_amd64.go │ │ │ ├── syscall_darwin_arm64.go │ │ │ ├── syscall_darwin_libSystem.go │ │ │ ├── syscall_dragonfly.go │ │ │ ├── syscall_dragonfly_amd64.go │ │ │ ├── syscall_freebsd.go │ │ │ ├── syscall_freebsd_386.go │ │ │ ├── syscall_freebsd_amd64.go │ │ │ ├── syscall_freebsd_arm.go │ │ │ ├── syscall_freebsd_arm64.go │ │ │ ├── syscall_freebsd_riscv64.go │ │ │ ├── syscall_illumos.go │ │ │ ├── syscall_linux.go │ │ │ ├── syscall_linux_386.go │ │ │ ├── syscall_linux_alarm.go │ │ │ ├── syscall_linux_amd64.go │ │ │ ├── syscall_linux_amd64_gc.go │ │ │ ├── syscall_linux_arm.go │ │ │ ├── syscall_linux_arm64.go │ │ │ ├── syscall_linux_gc.go │ │ │ ├── syscall_linux_gc_386.go │ │ │ ├── syscall_linux_gc_arm.go │ │ │ ├── syscall_linux_gccgo_386.go │ │ │ ├── syscall_linux_gccgo_arm.go │ │ │ ├── syscall_linux_loong64.go │ │ │ ├── syscall_linux_mips64x.go │ │ │ ├── syscall_linux_mipsx.go │ │ │ ├── syscall_linux_ppc.go │ │ │ ├── syscall_linux_ppc64x.go │ │ │ ├── syscall_linux_riscv64.go │ │ │ ├── syscall_linux_s390x.go │ │ │ ├── syscall_linux_sparc64.go │ │ │ ├── syscall_netbsd.go │ │ │ ├── syscall_netbsd_386.go │ │ │ ├── syscall_netbsd_amd64.go │ │ │ ├── syscall_netbsd_arm.go │ │ │ ├── syscall_netbsd_arm64.go │ │ │ ├── syscall_openbsd.go │ │ │ ├── syscall_openbsd_386.go │ │ │ ├── syscall_openbsd_amd64.go │ │ │ ├── syscall_openbsd_arm.go │ │ │ ├── syscall_openbsd_arm64.go │ │ │ ├── syscall_openbsd_mips64.go │ │ │ ├── syscall_solaris.go │ │ │ ├── syscall_solaris_amd64.go │ │ │ ├── syscall_unix.go │ │ │ ├── syscall_unix_gc.go │ │ │ ├── syscall_unix_gc_ppc64x.go │ │ │ ├── syscall_zos_s390x.go │ │ │ ├── sysvshm_linux.go │ │ │ ├── sysvshm_unix.go │ │ │ ├── sysvshm_unix_other.go │ │ │ ├── timestruct.go │ │ │ ├── unveil_openbsd.go │ │ │ ├── xattr_bsd.go │ │ │ ├── zerrors_aix_ppc.go │ │ │ ├── zerrors_aix_ppc64.go │ │ │ ├── zerrors_darwin_amd64.go │ │ │ ├── zerrors_darwin_arm64.go │ │ │ ├── zerrors_dragonfly_amd64.go │ │ │ ├── zerrors_freebsd_386.go │ │ │ ├── zerrors_freebsd_amd64.go │ │ │ ├── zerrors_freebsd_arm.go │ │ │ ├── zerrors_freebsd_arm64.go │ │ │ ├── zerrors_freebsd_riscv64.go │ │ │ ├── zerrors_linux.go │ │ │ ├── zerrors_linux_386.go │ │ │ ├── zerrors_linux_amd64.go │ │ │ ├── zerrors_linux_arm.go │ │ │ ├── zerrors_linux_arm64.go │ │ │ ├── zerrors_linux_loong64.go │ │ │ ├── zerrors_linux_mips.go │ │ │ ├── zerrors_linux_mips64.go │ │ │ ├── zerrors_linux_mips64le.go │ │ │ ├── zerrors_linux_mipsle.go │ │ │ ├── zerrors_linux_ppc.go │ │ │ ├── zerrors_linux_ppc64.go │ │ │ ├── zerrors_linux_ppc64le.go │ │ │ ├── zerrors_linux_riscv64.go │ │ │ ├── zerrors_linux_s390x.go │ │ │ ├── zerrors_linux_sparc64.go │ │ │ ├── zerrors_netbsd_386.go │ │ │ ├── zerrors_netbsd_amd64.go │ │ │ ├── zerrors_netbsd_arm.go │ │ │ ├── zerrors_netbsd_arm64.go │ │ │ ├── zerrors_openbsd_386.go │ │ │ ├── zerrors_openbsd_amd64.go │ │ │ ├── zerrors_openbsd_arm.go │ │ │ ├── zerrors_openbsd_arm64.go │ │ │ ├── zerrors_openbsd_mips64.go │ │ │ ├── zerrors_solaris_amd64.go │ │ │ ├── zerrors_zos_s390x.go │ │ │ ├── zptrace_armnn_linux.go │ │ │ ├── zptrace_linux_arm64.go │ │ │ ├── zptrace_mipsnn_linux.go │ │ │ ├── zptrace_mipsnnle_linux.go │ │ │ ├── zptrace_x86_linux.go │ │ │ ├── zsyscall_aix_ppc.go │ │ │ ├── zsyscall_aix_ppc64.go │ │ │ ├── zsyscall_aix_ppc64_gc.go │ │ │ ├── zsyscall_aix_ppc64_gccgo.go │ │ │ ├── zsyscall_darwin_amd64.1_13.go │ │ │ ├── zsyscall_darwin_amd64.1_13.s │ │ │ ├── zsyscall_darwin_amd64.go │ │ │ ├── zsyscall_darwin_amd64.s │ │ │ ├── zsyscall_darwin_arm64.1_13.go │ │ │ ├── zsyscall_darwin_arm64.1_13.s │ │ │ ├── zsyscall_darwin_arm64.go │ │ │ ├── zsyscall_darwin_arm64.s │ │ │ ├── zsyscall_dragonfly_amd64.go │ │ │ ├── zsyscall_freebsd_386.go │ │ │ ├── zsyscall_freebsd_amd64.go │ │ │ ├── zsyscall_freebsd_arm.go │ │ │ ├── zsyscall_freebsd_arm64.go │ │ │ ├── zsyscall_freebsd_riscv64.go │ │ │ ├── zsyscall_illumos_amd64.go │ │ │ ├── zsyscall_linux.go │ │ │ ├── zsyscall_linux_386.go │ │ │ ├── zsyscall_linux_amd64.go │ │ │ ├── zsyscall_linux_arm.go │ │ │ ├── zsyscall_linux_arm64.go │ │ │ ├── zsyscall_linux_loong64.go │ │ │ ├── zsyscall_linux_mips.go │ │ │ ├── zsyscall_linux_mips64.go │ │ │ ├── zsyscall_linux_mips64le.go │ │ │ ├── zsyscall_linux_mipsle.go │ │ │ ├── zsyscall_linux_ppc.go │ │ │ ├── zsyscall_linux_ppc64.go │ │ │ ├── zsyscall_linux_ppc64le.go │ │ │ ├── zsyscall_linux_riscv64.go │ │ │ ├── zsyscall_linux_s390x.go │ │ │ ├── zsyscall_linux_sparc64.go │ │ │ ├── zsyscall_netbsd_386.go │ │ │ ├── zsyscall_netbsd_amd64.go │ │ │ ├── zsyscall_netbsd_arm.go │ │ │ ├── zsyscall_netbsd_arm64.go │ │ │ ├── zsyscall_openbsd_386.go │ │ │ ├── zsyscall_openbsd_amd64.go │ │ │ ├── zsyscall_openbsd_arm.go │ │ │ ├── zsyscall_openbsd_arm64.go │ │ │ ├── zsyscall_openbsd_mips64.go │ │ │ ├── zsyscall_solaris_amd64.go │ │ │ ├── zsyscall_zos_s390x.go │ │ │ ├── zsysctl_openbsd_386.go │ │ │ ├── zsysctl_openbsd_amd64.go │ │ │ ├── zsysctl_openbsd_arm.go │ │ │ ├── zsysctl_openbsd_arm64.go │ │ │ ├── zsysctl_openbsd_mips64.go │ │ │ ├── zsysnum_darwin_amd64.go │ │ │ ├── zsysnum_darwin_arm64.go │ │ │ ├── zsysnum_dragonfly_amd64.go │ │ │ ├── zsysnum_freebsd_386.go │ │ │ ├── zsysnum_freebsd_amd64.go │ │ │ ├── zsysnum_freebsd_arm.go │ │ │ ├── zsysnum_freebsd_arm64.go │ │ │ ├── zsysnum_freebsd_riscv64.go │ │ │ ├── zsysnum_linux_386.go │ │ │ ├── zsysnum_linux_amd64.go │ │ │ ├── zsysnum_linux_arm.go │ │ │ ├── zsysnum_linux_arm64.go │ │ │ ├── zsysnum_linux_loong64.go │ │ │ ├── zsysnum_linux_mips.go │ │ │ ├── zsysnum_linux_mips64.go │ │ │ ├── zsysnum_linux_mips64le.go │ │ │ ├── zsysnum_linux_mipsle.go │ │ │ ├── zsysnum_linux_ppc.go │ │ │ ├── zsysnum_linux_ppc64.go │ │ │ ├── zsysnum_linux_ppc64le.go │ │ │ ├── zsysnum_linux_riscv64.go │ │ │ ├── zsysnum_linux_s390x.go │ │ │ ├── zsysnum_linux_sparc64.go │ │ │ ├── zsysnum_netbsd_386.go │ │ │ ├── zsysnum_netbsd_amd64.go │ │ │ ├── zsysnum_netbsd_arm.go │ │ │ ├── zsysnum_netbsd_arm64.go │ │ │ ├── zsysnum_openbsd_386.go │ │ │ ├── zsysnum_openbsd_amd64.go │ │ │ ├── zsysnum_openbsd_arm.go │ │ │ ├── zsysnum_openbsd_arm64.go │ │ │ ├── zsysnum_openbsd_mips64.go │ │ │ ├── zsysnum_zos_s390x.go │ │ │ ├── ztypes_aix_ppc.go │ │ │ ├── ztypes_aix_ppc64.go │ │ │ ├── ztypes_darwin_amd64.go │ │ │ ├── ztypes_darwin_arm64.go │ │ │ ├── ztypes_dragonfly_amd64.go │ │ │ ├── ztypes_freebsd_386.go │ │ │ ├── ztypes_freebsd_amd64.go │ │ │ ├── ztypes_freebsd_arm.go │ │ │ ├── ztypes_freebsd_arm64.go │ │ │ ├── ztypes_freebsd_riscv64.go │ │ │ ├── ztypes_illumos_amd64.go │ │ │ ├── ztypes_linux.go │ │ │ ├── ztypes_linux_386.go │ │ │ ├── ztypes_linux_amd64.go │ │ │ ├── ztypes_linux_arm.go │ │ │ ├── ztypes_linux_arm64.go │ │ │ ├── ztypes_linux_loong64.go │ │ │ ├── ztypes_linux_mips.go │ │ │ ├── ztypes_linux_mips64.go │ │ │ ├── ztypes_linux_mips64le.go │ │ │ ├── ztypes_linux_mipsle.go │ │ │ ├── ztypes_linux_ppc.go │ │ │ ├── ztypes_linux_ppc64.go │ │ │ ├── ztypes_linux_ppc64le.go │ │ │ ├── ztypes_linux_riscv64.go │ │ │ ├── ztypes_linux_s390x.go │ │ │ ├── ztypes_linux_sparc64.go │ │ │ ├── ztypes_netbsd_386.go │ │ │ ├── ztypes_netbsd_amd64.go │ │ │ ├── ztypes_netbsd_arm.go │ │ │ ├── ztypes_netbsd_arm64.go │ │ │ ├── ztypes_openbsd_386.go │ │ │ ├── ztypes_openbsd_amd64.go │ │ │ ├── ztypes_openbsd_arm.go │ │ │ ├── ztypes_openbsd_arm64.go │ │ │ ├── ztypes_openbsd_mips64.go │ │ │ ├── ztypes_solaris_amd64.go │ │ │ └── ztypes_zos_s390x.go │ │ └── windows/ │ │ ├── aliases.go │ │ ├── dll_windows.go │ │ ├── empty.s │ │ ├── env_windows.go │ │ ├── eventlog.go │ │ ├── exec_windows.go │ │ ├── memory_windows.go │ │ ├── mkerrors.bash │ │ ├── mkknownfolderids.bash │ │ ├── mksyscall.go │ │ ├── race.go │ │ ├── race0.go │ │ ├── security_windows.go │ │ ├── service.go │ │ ├── setupapi_windows.go │ │ ├── str.go │ │ ├── syscall.go │ │ ├── syscall_windows.go │ │ ├── types_windows.go │ │ ├── types_windows_386.go │ │ ├── types_windows_amd64.go │ │ ├── types_windows_arm.go │ │ ├── types_windows_arm64.go │ │ ├── zerrors_windows.go │ │ ├── zknownfolderids_windows.go │ │ └── zsyscall_windows.go │ └── text/ │ ├── AUTHORS │ ├── CONTRIBUTORS │ ├── LICENSE │ ├── PATENTS │ ├── encoding/ │ │ ├── charmap/ │ │ │ ├── charmap.go │ │ │ └── tables.go │ │ ├── encoding.go │ │ ├── htmlindex/ │ │ │ ├── htmlindex.go │ │ │ ├── map.go │ │ │ └── tables.go │ │ ├── internal/ │ │ │ ├── identifier/ │ │ │ │ ├── identifier.go │ │ │ │ └── mib.go │ │ │ └── internal.go │ │ ├── japanese/ │ │ │ ├── all.go │ │ │ ├── eucjp.go │ │ │ ├── iso2022jp.go │ │ │ ├── shiftjis.go │ │ │ └── tables.go │ │ ├── korean/ │ │ │ ├── euckr.go │ │ │ └── tables.go │ │ ├── simplifiedchinese/ │ │ │ ├── all.go │ │ │ ├── gbk.go │ │ │ ├── hzgb2312.go │ │ │ └── tables.go │ │ ├── traditionalchinese/ │ │ │ ├── big5.go │ │ │ └── tables.go │ │ └── unicode/ │ │ ├── override.go │ │ └── unicode.go │ ├── internal/ │ │ ├── language/ │ │ │ ├── common.go │ │ │ ├── compact/ │ │ │ │ ├── compact.go │ │ │ │ ├── language.go │ │ │ │ ├── parents.go │ │ │ │ ├── tables.go │ │ │ │ └── tags.go │ │ │ ├── compact.go │ │ │ ├── compose.go │ │ │ ├── coverage.go │ │ │ ├── language.go │ │ │ ├── lookup.go │ │ │ ├── match.go │ │ │ ├── parse.go │ │ │ ├── tables.go │ │ │ └── tags.go │ │ ├── tag/ │ │ │ └── tag.go │ │ └── utf8internal/ │ │ └── utf8internal.go │ ├── language/ │ │ ├── coverage.go │ │ ├── doc.go │ │ ├── go1_1.go │ │ ├── go1_2.go │ │ ├── language.go │ │ ├── match.go │ │ ├── parse.go │ │ ├── tables.go │ │ └── tags.go │ ├── runes/ │ │ ├── cond.go │ │ └── runes.go │ └── transform/ │ └── transform.go ├── gopkg.in/ │ ├── tomb.v1/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── tomb.go │ └── yaml.v2/ │ ├── .travis.yml │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── NOTICE │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── go.mod │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go └── modules.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .cirrus.yml ================================================ --- env: GOPATH: "/var/tmp/go" CIRRUS_WORKING_DIR: "${GOPATH}/src/github.com/containers/dnsname" GOSRC: "$CIRRUS_WORKING_DIR" CIRRUS_SHELL: "/bin/bash" IMAGE_PROJECT: "libpod-218412" HOME: "/root" # not set by default GOCACHE: "${GOPATH}/cache" # Make names more readable in github/cirrus-ci FEDORA_NAME: "fedora-38" DEBIAN_NAME: "debian-12" # VM Images are maintained in the automation_images repo. IMAGE_SUFFIX: "c20230517t144652z-f38f37d12" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}" # Must be defined true when testing w/in containers CONTAINER: "false" gcp_credentials: ENCRYPTED[5ad247acfd6cfca94554b973d61a98a7882dfafbfcc1deb5faee8634cb3f8cf29720c88c243586d2975dd40885279db6] # Default VM to use unless set or modified by task gce_instance: image_project: "${IMAGE_PROJECT}" zone: "us-central1-c" # Required by Cirrus for the time being cpu: 2 memory: "4Gb" disk: 200 # Required for performance reasons image_name: "${FEDORA_CACHE_IMAGE_NAME}" # Update metadata on VM images referenced by this repository state meta_task: # see bors.toml skip: $CIRRUS_BRANCH =~ ".*\.tmp" container: image: "quay.io/libpod/imgts:latest" cpu: 1 memory: 1 env: CONTAINER: true # Space-separated list of images used by this repository state IMGNAMES: |- ${FEDORA_CACHE_IMAGE_NAME} ${DEBIAN_CACHE_IMAGE_NAME} BUILDID: "${CIRRUS_BUILD_ID}" REPOREF: "${CIRRUS_REPO_NAME}" GCPJSON: ENCRYPTED[35f6dca7928a3b676c05e0e6a6ce7f4ca3d347803e23b8d5d57ea8a22973248e2808e8726b9463b00b57e576c5ff0331] GCPNAME: ENCRYPTED[f3890da8c780aaa352ae8d1e3c9fedffd0a233625c1b983e6754e609a92d5c814167ee53ef967896d66c73710c90465e] GCPPROJECT: ENCRYPTED[e82537cfb95d6121717be1f23078a18b14de224de82ed2f72c0a8f0adc77b5c7e8ad394d10714214bccb9f010d65ac29] CIRRUS_CLONE_DEPTH: 1 # source not used script: /usr/local/bin/entrypoint.sh validate_task: validate_script: - export PATH="$PATH:$GOPATH/bin" - make validate - make vendor - ./hack/tree_status.sh - make test_task: alias: test depends_on: - validate matrix: - name: $FEDORA_NAME gce_instance: image_name: ${FEDORA_CACHE_IMAGE_NAME} - name: $DEBIAN_NAME gce_instance: image_name: ${DEBIAN_CACHE_IMAGE_NAME} test_script: - export PATH="$PATH:$GOPATH/bin" - make - make test ================================================ FILE: .github/renovate.json5 ================================================ /* Renovate is a service similar to GitHub Dependabot, but with (fantastically) more configuration options. So many options in fact, if you're new I recommend glossing over this cheat-sheet prior to the official documentation: https://www.augmentedmind.de/2021/07/25/renovate-bot-cheat-sheet Configuration Update/Change Procedure: 1. Make changes 2. Manually validate changes (from repo-root): podman run -it \ -v ./.github/renovate.json5:/usr/src/app/renovate.json5:z \ docker.io/renovate/renovate:latest \ renovate-config-validator 3. Commit. Configuration Reference: https://docs.renovatebot.com/configuration-options/ Monitoring Dashboard: https://app.renovatebot.com/dashboard#github/containers Note: The Renovate bot will create/manage it's business on branches named 'renovate/*'. Otherwise, and by default, the only the copy of this file that matters is the one on the `main` branch. No other branches will be monitored or touched in any way. */ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", /************************************************* ****** Global/general configuration options ***** *************************************************/ // Re-use predefined sets of configuration options to DRY "extends": [ // https://github.com/containers/automation/blob/main/renovate/defaults.json5 "github>containers/automation//renovate/defaults.json5" ], // Permit automatic rebasing when base-branch changes by more than // one commit. "rebaseWhen": "behind-base-branch", /************************************************* *** Repository-specific configuration options *** *************************************************/ // Don't leave dep. update. PRs "hanging", assign them to people. "assignees": ["baude"], } ================================================ FILE: .gitignore ================================================ bin/* ================================================ FILE: CODE-OF-CONDUCT.md ================================================ ## The dnsname Project Community Code of Conduct The dnsname project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md). ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: Makefile ================================================ export GOPROXY=https://proxy.golang.org GO ?= go EPOCH_TEST_COMMIT ?= 394c06f491fe9f1c28a410e3b0b91916a5119406 DESTDIR ?= LIBEXECDIR ?= ${PREFIX}/libexec/cni PREFIX ?= /usr/local PROJECT := github.com/containers/dnsname FIRST_GOPATH := $(firstword $(subst :, ,$(GOPATH))) GOPKGDIR := $(FIRST_GOPATH)/src/$(PROJECT) GOPKGBASEDIR ?= $(shell dirname "$(GOPKGDIR)") SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z) COMMIT_NO ?= $(shell git rev-parse HEAD 2> /dev/null || true) GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),${COMMIT_NO}-dirty,${COMMIT_NO}) LDFLAGS_DNSNAME ?= -X main.gitCommit=$(GIT_COMMIT) GO_BUILD=$(GO) build # Go module support: set `-mod=vendor` to use the vendored sources ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true) GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor endif GOBIN := $(shell $(GO) env GOBIN) ifeq ($(GOBIN),) GOBIN := $(FIRST_GOPATH)/bin endif all: binaries validate: install.tools gofmt .gitvalidation lint gofmt: find . -name '*.go' ! -path './vendor/*' -exec gofmt -s -w {} \+ git diff --exit-code binaries: $(GO_BUILD) -ldflags '$(LDFLAGS_DNSNAME)' -o bin/dnsname github.com/containers/dnsname/plugins/meta/dnsname .PHONY: .gitvalidation .gitvalidation: GIT_CHECK_EXCLUDE="./vendor" $(GOBIN)/git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) .PHONY: install.tools install.tools: .install.gitvalidation .install.ginkgo .install.golangci-lint lint: .install.golangci-lint ./bin/golangci-lint run define go-get env GO111MODULE=off \ $(GO) get -u ${1} endef .install.ginkgo: if [ ! -x "$(GOBIN)/ginkgo" ]; then \ $(call go-get,github.com/onsi/ginkgo/ginkgo); \ fi .install.gitvalidation: if [ ! -x "$(GOBIN)/git-validation" ]; then \ $(call go-get,github.com/vbatts/git-validation); \ fi .install.golangci-lint: if [ ! -x "./bin/golangci-lint" ]; then \ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.51.1 ; \ fi install: install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(LIBEXECDIR) install ${SELINUXOPT} -m 755 bin/dnsname $(DESTDIR)$(LIBEXECDIR)/dnsname clean: rm -fr bin/ test: .install.ginkgo $(GO) test -v ./... vendor: export GO111MODULE=on \ $(GO) mod tidy && \ $(GO) mod vendor && \ $(GO) mod verify .PHONY: vendor-in-container vendor-in-container: podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:1.13 make vendor .PHONY: \ binaries \ test \ gofmt \ lint \ validate \ vendor ================================================ FILE: OWNERS ================================================ approvers: - mheon - baude - mrunalp - rhatdan - TomSweeneyRedHat - giuseppe - vrothberg - jwhonce reviewers: - mheon - baude - mrunalp - rhatdan - TomSweeneyRedHat - giuseppe - vrothberg - jwhonce ================================================ FILE: README.md ================================================ # dnsname plugin ## IMPORTANT As of 2023, this repository is no longer actively maintained. Our development efforts are happening in [netavark](https://github.com/containers/netavark/) and [aardvark-dns](https://github.com/containers/aardvark-dns/) that should provide a better alternative. ## Overview This plugin sets up the use of dnsmasq on a given CNI network so that Pods can resolve each other by name. When configured, the pod and its IP address are added to a network specific hosts file that dnsmasq reads in. Similarly, when a pod is removed from the network, it will remove the entry from the hosts file. Each CNI network will have its own dnsmasq instance. The *dnsname* plugin was specifically designed for the [Podman](https://github.com/containers/podman) container engine. Follow the [mini-tutorial](README_PODMAN.md) to use it with Podman. ## Usage The dnsname plugin can be enabled in the cni network configuration file. ``` { "cniVersion": "0.4.0", "name": "cni-bridge-network", "plugins": [ { "type": "bridge", "bridge": "cni0", ... } }, { "type": "dnsname", "domainName": "foobar.com", "capabilities": { "aliases": true } } ] } ``` ## DNSMasq configuration files The dnsmasq service and its configuration files are considered to be very fluid and are not meant to survive a system reboot. Therefore, files are stored in `/run/containers/cni/dnsname`, or under `$XDG_RUNTIME_DIR/containers/cni/dnsname` if `XDG_RUNTIME_DIR` is specified. The plugin knows to recreate the necessary files if it detects they are not present. ## DNSMasq default configuration Much like the implementation of DNSMasq for libvirt, this plugin will only set up dnsmasq to listen on the network interfaces associated with the CNI network. The DNSMasq services are not configured or managed by systemd but rather only by the plugin itself. ## Network aliases The dnsname plugin is capable of not only adding the container name for DNS resolution but also adding network aliases. These aliases are also added to the DNSMasq host file. ## Reporting issues If you are using dnsname code compiled directly from github, then reporting bugs and problem to the dnsname github issues tracker is appropriate. In the case that you are using code compiled and provided by a Linux distribution, you should file the problem with their appropriate bug tracker (bugzilla/trackpad). ================================================ FILE: README_PODMAN.md ================================================ # Using the dnsname plugin with Podman The *dnsname* plugin allows containers to resolve each other by name. The plugin adds each container's name to an instance of a dnsmasq server. The plugin is enabled through adding it to a network's CNI configuration. The containers will only be able to resolve each other if they are on the same CNI network. This tutorial assumes you already have Podman, containernetworking-plugins, and a golang development environment installed. ## Install dnsmasq Using your package manager, install the *dnsmasq* package. For Fedora, this would be: `sudo dnf install dnsmasq` ### AppArmor If your system uses AppArmor, it can prevent dnsmasq to open the necessary files. To fix this, add the following lines to `/etc/apparmor.d/local/usr.sbin.dnsmasq`: ``` # required by the dnsname plugin in podman /run/containers/cni/dnsname/*/dnsmasq.conf r, /run/containers/cni/dnsname/*/addnhosts r, /run/containers/cni/dnsname/*/pidfile rw, ``` Then reload the main dnsmasq profile: ``` sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.dnsmasq ``` ## Build and install 1. using git, clone the *github.com/containers/dnsname* repository. 2. make install PREFIX=/usr -- this will install the dnsname plugin into /usr/libexec/cni where your CNI plugins should already exist. ## Configure a CNI network for Podman 1. Create a new network using `podman network create`. For example, `podman network create foobar` will suffice. The following example [configuration file](example/foobar.conflist) shows a usable example for Podman. 2. (optional)+The configuration will be automatically enabled for newly created networks via `podman network create`. If you want to add this feature to an exisiting network add the needed lines to `/etc/cni/net.d/foobar.conflist` using your favorite editor. For example: ``` { "cniVersion": "0.4.0", "name": "foobar", "plugins": [ ... { "type": "dnsname", "domainName": "dns.podman", "capabilities": { "aliases": true } } ] } ``` ## Example: container name resolution In this test image, the nginx server will respond with *podman rulez* on an http request. **Note**: we use the --network foobar here. ```console sudo podman run -dt --name web --network foobar quay.io/libpod/alpine_nginx:latest 5139d65d22135e9ecab511559d863754550894a32285befd94dab231017048c2 sudo podman run -it --name client --network foobar quay.io/libpod/alpine_nginx:latest curl http://web.dns.podman/ podman rulez ``` ## Enabling name resolution on the default Podman network After making sure the *dnsplugin* is functioning properly, you can add name resolution to your default Podman network. This can be done two different ways: 1. Add the *dnsname* plugin as described in above to your default Podman network. This default network is usually `/etc/cni/net.d/87-podman-bridge.conflist`. ================================================ FILE: RELEASE_NOTES.md ================================================ # Release Notes ## 1.3.1 - Fixed an issue where an incorrect use of `LDFLAGS` made `dnsname` unable to build in some packaging systems. ## 1.3.0 - Fixed a bug where errors when removing a container from `dnsname` could cause CNI to fail to clean up iptables rules for the container. - Fixed a bug where `dnsname` would never remove unused configuration files for networks that no longer had containers present. - If errors occur when running `dnsmasq`, the full error message is now displayed for debugging. - The version number of `dnsname` is now displayed correctly. ## 1.2.0 - DNS Search domains required to use the `dnsname` plugin are now returned in the CNI response. ## 1.1.1 - Fixed a bug where network aliases support was nonfunctional. ## 1.1.0 - Added support for network aliases - multiple names for the same container. ## 1.0.0 - Initial release ================================================ FILE: SECURITY.md ================================================ ## Security and Disclosure Information Policy for the dnsname Project The dnsname Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects. ================================================ FILE: ci/Dockerfile ================================================ FROM registry.fedoraproject.org/fedora:latest RUN dnf update -y && dnf install -y golang make findutils && dnf -y clean all ================================================ FILE: example/foobar.conflist ================================================ { "cniVersion": "0.4.0", "name": "foobar", "plugins": [ { "type": "bridge", "bridge": "cni-podman1", "isGateway": true, "ipMasq": true, "hairpinMode": true, "ipam": { "type": "host-local", "routes": [ { "dst": "0.0.0.0/0" } ], "ranges": [ [ { "subnet": "10.89.0.0/24" } ] ] } }, { "type": "portmap", "capabilities": { "portMappings": true } }, { "type": "firewall", "backend": "" }, { "type": "tuning" }, { "type": "dnsname", "domainName": "dns.podman", "capabilities": { "aliases": true } } ] } ================================================ FILE: go.mod ================================================ module github.com/containers/dnsname go 1.13 require ( github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v0.9.1 github.com/coreos/go-iptables v0.6.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.2 github.com/vishvananda/netlink v1.1.1-0.20210916161339-2c39f3491956 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 ) ================================================ FILE: go.sum ================================================ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUqU3jMrZiTuW0zkYeKlKDAFD+DG20IoO421/Y= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20210916161339-2c39f3491956 h1:2tjYQpM+MZB25iPeE20lI/PrUNrbN66Bd+P27TkVaxU= github.com/vishvananda/netlink v1.1.1-0.20210916161339-2c39f3491956/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: hack/get_ci_vm.sh ================================================ #!/usr/bin/env bash # # For help and usage information, simply execute the script w/o any arguments. # # This script is intended to be run by Red Hat dnsname developers who need # to debug problems specifically related to Cirrus-CI automated testing. # It requires that you have been granted prior access to create VMs in # google-cloud. For non-Red Hat contributors, VMs are available as-needed, # with supervision upon request. set -e SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}") SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH") REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../") # Help detect if we were called by get_ci_vm container GET_CI_VM="${GET_CI_VM:-0}" in_get_ci_vm() { if ((GET_CI_VM==0)); then echo "Error: $1 is not intended for use in this context" exit 2 fi } # get_ci_vm APIv1 container entrypoint calls into this script # to obtain required repo. specific configuration options. if [[ "$1" == "--config" ]]; then in_get_ci_vm "$1" cat < /dev/stderr echo 'PATH=$PATH:$GOPATH/bin' > /etc/ci_environment else # Create and access VM for specified Cirrus-CI task mkdir -p $HOME/.config/gcloud/ssh podman run -it --rm \ --tz=local \ -e NAME="$USER" \ -e SRCDIR=/src \ -e GCLOUD_ZONE="$GCLOUD_ZONE" \ -e DEBUG="${DEBUG:-0}" \ -v $REPO_DIRPATH:/src:O \ -v $HOME/.config/gcloud:/root/.config/gcloud:z \ -v $HOME/.config/gcloud/ssh:/root/.ssh:z \ quay.io/libpod/get_ci_vm:latest "$@" fi ================================================ FILE: hack/tree_status.sh ================================================ #!/bin/bash set -e SUGGESTION="${SUGGESTION:-sync the vendor.conf and commit all changes.}" STATUS=$(git status --porcelain) if [[ -z $STATUS ]] then echo "tree is clean" else echo "tree is dirty, please $SUGGESTION" echo "" echo "$STATUS" exit 1 fi ================================================ FILE: plugins/meta/dnsname/config.go ================================================ package main import ( "errors" "os" "path/filepath" "github.com/containernetworking/cni/pkg/types" ) const ( // confFileName is the name of the dns masq conf file confFileName = "dnsmasq.conf" // hostsFileName is the name of the addnhosts file hostsFileName = "addnhosts" // pidFileName is the file where the dnsmasq file is stored pidFileName = "pidfile" ) const dnsMasqTemplate = `## WARNING: THIS IS AN AUTOGENERATED FILE ## AND SHOULD NOT BE EDITED MANUALLY AS IT ## LIKELY TO AUTOMATICALLY BE REPLACED. strict-order local=/{{.Domain}}/ domain={{.Domain}} expand-hosts pid-file={{.PidFile}} except-interface=lo bind-dynamic no-hosts interface={{.NetworkInterface}} addn-hosts={{.AddOnHostsFile}}` var ( // ErrBinaryNotFound means that the dnsmasq binary was not found ErrBinaryNotFound = errors.New("unable to locate dnsmasq in path") // ErrNoIPAddressFound means that CNI was unable to resolve an IP address in the CNI configuration ErrNoIPAddressFound = errors.New("no ip address was found in the network") ) // DNSNameConf represents the cni config with the domain name attribute type DNSNameConf struct { types.NetConf DomainName string `json:"domainName"` RuntimeConfig struct { // The capability arg Aliases map[string][]string `json:"aliases"` } `json:"runtimeConfig,omitempty"` } // dnsNameFile describes the plugin's attributes type dnsNameFile struct { AddOnHostsFile string Binary string ConfigFile string Domain string NetworkInterface string PidFile string } // dnsNameConfPath tells where we store the conf, pid, and hosts files func dnsNameConfPath() string { xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") if xdgRuntimeDir != "" { return filepath.Join(xdgRuntimeDir, "containers/cni/dnsname") } return "/run/containers/cni/dnsname" } ================================================ FILE: plugins/meta/dnsname/dnsname_suite_test.go ================================================ // Copyright 2019 dnsname authors // Copyright 2017 CNI authors // // 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 main import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "testing" ) func TestTuning(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "plugins/meta/dnsname") } ================================================ FILE: plugins/meta/dnsname/dnsname_test.go ================================================ package main import ( "io/ioutil" "os" "path/filepath" "reflect" "syscall" "time" "github.com/containernetworking/cni/pkg/skel" current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/vishvananda/netlink" ) func cleanup(d dnsNameFile) error { _ = d.stop() return os.RemoveAll(filepath.Dir(d.PidFile)) } var _ = Describe("dnsname tests", func() { var originalNS, targetNS ns.NetNS const IFNAME string = "dummy0" fullConf := []byte(`{ "cniVersion": "0.4.0", "name": "test", "type": "dnsname", "domainName": "foobar.io", "prevResult": { "cniVersion": "0.4.0", "interfaces": [ { "name": "dummy0", "mac": "a6:a7:ca:6b:34:2e" }, { "name": "vetha0a83b38", "mac": "9a:45:bd:b0:2d:dd" }, { "name": "eth0", "mac": "ea:63:0e:63:3e:86", "sandbox": "/var/run/netns/baude" } ], "ips": [ { "version": "4", "interface": 2, "address": "10.88.8.5/24", "gateway": "10.88.8.1" } ], "routes": [ { "dst": "0.0.0.0/0" } ] } }`) BeforeEach(func() { // Create a new NetNS so we don't modify the host var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() err = netlink.LinkAdd(&netlink.Dummy{ LinkAttrs: netlink.LinkAttrs{ Name: IFNAME, }, }) Expect(err).NotTo(HaveOccurred()) _, err = netlink.LinkByName(IFNAME) Expect(err).NotTo(HaveOccurred()) return nil }) Expect(err).NotTo(HaveOccurred()) targetNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) }) AfterEach(func() { Expect(originalNS.Close()).To(Succeed()) Expect(targetNS.Close()).To(Succeed()) }) It("dnsname add", func() { args := &skel.CmdArgs{ ContainerID: "dummy", Netns: targetNS.Path(), IfName: IFNAME, StdinData: fullConf, } err := originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) result, err := current.GetResult(r) Expect(err).NotTo(HaveOccurred()) // check that the dns search domain is set correctly Expect(result.DNS.Search).To(Equal([]string{"foobar.io"})) // Check that all configuration files are created files, err := ioutil.ReadDir("/run/containers/cni/dnsname/test") Expect(err).To(BeNil()) expectedFileNames := []string{"addnhosts", "dnsmasq.conf", "lock", "pidfile"} var resultingFileNames []string for _, f := range files { resultingFileNames = append(resultingFileNames, f.Name()) } Expect(reflect.DeepEqual(expectedFileNames, resultingFileNames)).To(BeTrue()) d, err := newDNSMasqFile("foobar.io", "dummy0", "test") Expect(err).To(BeNil()) // Check that the dns masq instance is running pid, err := d.getProcess() Expect(err).To(BeNil()) // Send it a signal 0; if alive, error will be nil err = pid.Signal(syscall.Signal(0)) Expect(err).To(BeNil()) // Stop the dnsmasq instance and clean up files in the filesystem Expect(cleanup(d)).To(BeNil()) return nil }) Expect(err).NotTo(HaveOccurred()) }) It("dnsname del", func() { var ( dnsDead bool counter int ) args := &skel.CmdArgs{ ContainerID: "dummy", Netns: targetNS.Path(), IfName: IFNAME, StdinData: fullConf, } err := originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) _, err = current.GetResult(r) Expect(err).NotTo(HaveOccurred()) d, err := newDNSMasqFile("foobar.io", "dummy0", "test") Expect(err).To(BeNil()) pid, err := d.getProcess() Expect(err).To(BeNil()) err = pid.Signal(syscall.Signal(0)) Expect(err).To(BeNil()) err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error { return cmdDel(args) }) Expect(err).To(BeNil()) // Ensure the dnsmasq instance has been stopped on del // It sometimes takes time for the dnsmasq pid to be killed // check every .5 second for maximum of 10 tries for { err = pid.Signal(syscall.Signal(0)) if err != nil { dnsDead = true break } if counter == 10 { break } counter++ time.Sleep(500 * time.Millisecond) } Expect(dnsDead).To(BeTrue()) // defer cleanup is case it does not work automatically defer func() { _ = cleanup(d) }() // Check that the configuration directory is deleted _, err = ioutil.ReadDir("/run/containers/cni/dnsname/test") Expect(os.IsNotExist(err)).To(BeTrue()) return nil }) Expect(err).NotTo(HaveOccurred()) }) }) ================================================ FILE: plugins/meta/dnsname/files.go ================================================ package main import ( "bufio" "bytes" "fmt" "io/ioutil" "net" "os" "strings" "text/template" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk" "github.com/coreos/go-iptables/iptables" "github.com/sirupsen/logrus" ) // dnsNameLock embeds the CNI disk lock so we can hang methods from it type dnsNameLock struct { lock *disk.FileLock } // release unlocks and closes the disk lock. func (m *dnsNameLock) release() error { if err := m.lock.Unlock(); err != nil { return err } return m.lock.Close() } // acquire locks the disk lock. func (m *dnsNameLock) acquire() error { return m.lock.Lock() } // getLock returns a dnsNameLock synchronizing the configuration directory for // the domain. func getLock(path string) (*dnsNameLock, error) { l, err := disk.NewFileLock(path) if err != nil { return nil, err } return &dnsNameLock{l}, nil } // checkFromDNSMasqConfFile ensures that the dnsmasq conf file for // the network interface exists or it creates it func checkForDNSMasqConfFile(conf dnsNameFile) error { if _, err := os.Stat(conf.ConfigFile); err == nil { // the file already exists, we can proceed return err } newConfig, err := generateDNSMasqConfig(conf) if err != nil { return err } ip, err := iptables.New() if err != nil { return err } args := []string{"-i", conf.NetworkInterface, "-p", "udp", "-m", "udp", "--dport", "53", "-j", "ACCEPT"} exists, err := ip.Exists("filter", "INPUT", args...) if err != nil { return err } if !exists { if err := ip.Insert("filter", "INPUT", 1, args...); err != nil { return err } } // Generate the template and compile it. return ioutil.WriteFile(conf.ConfigFile, newConfig, 0700) } // generateDNSMasqConfig fills out the configuration file template for the dnsmasq service func generateDNSMasqConfig(config dnsNameFile) ([]byte, error) { var buf bytes.Buffer templ, err := template.New("dnsmasq-conf-file").Parse(dnsMasqTemplate) if err != nil { return nil, err } if err := templ.Execute(&buf, config); err != nil { return nil, err } buf.WriteByte('\n') return buf.Bytes(), nil } // appendToFile appends a new entry to the dnsmasqs hosts file func appendToFile(path, podname string, aliases []string, ips []*net.IPNet) error { f, err := openFile(path) if err != nil { return err } defer func() { if err := f.Close(); err != nil { logrus.Errorf("failed to close file %q: %v", path, err) } }() for _, ip := range ips { entry := fmt.Sprintf("%s\t%s", ip.IP.String(), podname) for _, alias := range aliases { entry += fmt.Sprintf("\t%s", alias) } entry += "\n" if _, err = f.WriteString(entry); err != nil { return err } logrus.Debugf("appended %s: %s", path, entry) } return nil } // removeLineFromFile removes a given entry from the dnsmasq host file func removeFromFile(path, podname string) (bool, error) { var ( keepers []string found bool ) shouldHUP := false backup := fmt.Sprintf("%s.old", path) if err := os.Rename(path, backup); err != nil { return shouldHUP, err } f, err := os.Open(backup) if err != nil { // if the open fails here, we need to revert things renameFile(backup, path) return shouldHUP, err } defer func() { if err := f.Close(); err != nil { logrus.Errorf("unable to close %q: %v", backup, err) } }() oldFile := bufio.NewScanner(f) // Iterate the old file for oldFile.Scan() { fields := strings.Fields(oldFile.Text()) // if the IP of the entry and the given IP dont match, it should // go into the new file if len(fields) > 1 && fields[1] != podname { keepers = append(keepers, fmt.Sprintf("%s\n", oldFile.Text())) continue } found = true } if !found { // We never found a matching record; non-fatal logrus.Debugf("a record for %s was never found in %s", podname, path) } fileLength, err := writeFile(path, keepers) if err != nil { renameFile(backup, path) return shouldHUP, err } if fileLength > 0 { shouldHUP = true } if err := os.Remove(backup); err != nil { logrus.Errorf("unable to delete '%s': %q", backup, err) } return shouldHUP, nil } // renameFile renames a file to backup func renameFile(oldpath, newpath string) { if renameError := os.Rename(oldpath, newpath); renameError != nil { logrus.Errorf("unable to restore %q to %q: %v", oldpath, newpath, renameError) } } // writeFile writes a []string to the given path and returns the number // of lines in the file func writeFile(path string, content []string) (int, error) { var counter int f, err := openFile(path) if err != nil { return 0, err } defer func() { if err := f.Close(); err != nil { logrus.Errorf("unable to close %q: %v", path, err) } }() for _, line := range content { if _, err := f.WriteString(line); err != nil { return 0, err } counter++ } return counter, nil } // openFile opens a file for reading func openFile(path string) (*os.File, error) { return os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) } ================================================ FILE: plugins/meta/dnsname/files_test.go ================================================ package main import ( "reflect" "testing" ) func Test_generateDNSMasqConfig(t *testing.T) { testResult := `## WARNING: THIS IS AN AUTOGENERATED FILE ## AND SHOULD NOT BE EDITED MANUALLY AS IT ## LIKELY TO AUTOMATICALLY BE REPLACED. strict-order local=/foobar.org/ domain=foobar.org expand-hosts pid-file=/run/containers/cni/dnsname/cni0/pidfile except-interface=lo bind-dynamic no-hosts interface=cni0 addn-hosts=/run/containers/cni/dnsname/cni0/addnhosts ` testConfig := dnsNameFile{ AddOnHostsFile: makePath("cni0", hostsFileName), Binary: "/usr/bin/foo", ConfigFile: makePath("cni0", confFileName), Domain: "foobar.org", NetworkInterface: "cni0", PidFile: makePath("cni0", pidFileName), } type args struct { config dnsNameFile } tests := []struct { name string args args want []byte wantErr bool }{ {"pass", args{testConfig}, []byte(testResult), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := generateDNSMasqConfig(tt.args.config) if (err != nil) != tt.wantErr { t.Errorf("generateDNSMasqConfig() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("generateDNSMasqConfig() got = '%v', want '%v'", string(got), string(tt.want)) } }) } } ================================================ FILE: plugins/meta/dnsname/main.go ================================================ // Copyright 2019 dnsname authors // Copyright 2017 CNI authors // // 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. // This is a post-setup plugin that establishes port forwarding - using iptables, // from the host's network interface(s) to a pod's network interface. // // It is intended to be used as a chained CNI plugin, and determines the container // IP from the previous result. If the result includes an IPv6 address, it will // also be configured. (IPTables will not forward cross-family). // // This has one notable limitation: it does not perform any kind of reservation // of the actual host port. If there is a service on the host, it will have all // its traffic captured by the container. If another container also claims a given // port, it will capture the traffic - it is last-write-wins. package main import ( "encoding/json" "io/ioutil" "os" "os/exec" "path/filepath" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) func cmdAdd(args *skel.CmdArgs) error { if err := findDNSMasq(); err != nil { return ErrBinaryNotFound } netConf, result, podname, err := parseConfig(args.StdinData, args.Args) if err != nil { return errors.Wrap(err, "failed to parse config") } if netConf.PrevResult == nil { return errors.Errorf("must be called as chained plugin") } ips, err := getIPs(result) if err != nil { return err } dnsNameConf, err := newDNSMasqFile(netConf.DomainName, result.Interfaces[0].Name, netConf.Name) if err != nil { return err } domainBaseDir := filepath.Dir(dnsNameConf.PidFile) // Check if the configuration file directory exists, else make it if _, err := os.Stat(domainBaseDir); os.IsNotExist(err) { if makeDirErr := os.MkdirAll(domainBaseDir, 0700); makeDirErr != nil { return makeDirErr } } // we use the configuration directory for our locking mechanism but read/write and hup lock, err := getLock(domainBaseDir) if err != nil { return err } if err := lock.acquire(); err != nil { return err } defer func() { if err := lock.release(); err != nil { logrus.Errorf("unable to release lock for %q: %v", dnsNameConf.AddOnHostsFile, err) } }() if err := checkForDNSMasqConfFile(dnsNameConf); err != nil { return err } aliases := netConf.RuntimeConfig.Aliases[netConf.Name] if err := appendToFile(dnsNameConf.AddOnHostsFile, podname, aliases, ips); err != nil { return err } // Now we need to HUP if err := dnsNameConf.hup(); err != nil { return err } nameservers, err := getInterfaceAddresses(dnsNameConf) if err != nil { return err } // keep anything that was passed in already nameservers = append(nameservers, result.DNS.Nameservers...) result.DNS.Nameservers = nameservers // add dns search domain result.DNS.Search = append(result.DNS.Search, netConf.DomainName) // Pass through the previous result return types.PrintResult(result, netConf.CNIVersion) } // Do not return an error, otherwise cni will stop // and not invoke the following plugins del command. func cmdDel(args *skel.CmdArgs) error { if err := findDNSMasq(); err != nil { logrus.Error(ErrBinaryNotFound) return nil } netConf, result, podname, err := parseConfig(args.StdinData, args.Args) if err != nil { logrus.Error(errors.Wrap(err, "failed to parse config")) return nil } else if result == nil { return nil } dnsNameConf, err := newDNSMasqFile(netConf.DomainName, result.Interfaces[0].Name, netConf.Name) if err != nil { logrus.Error(err) return nil } domainBaseDir := filepath.Dir(dnsNameConf.PidFile) lock, err := getLock(domainBaseDir) if err != nil { logrus.Error(err) return nil } if err := lock.acquire(); err != nil { logrus.Error(err) return nil } defer func() { // if the lock isn't given up by another process if err := lock.release(); err != nil { logrus.Errorf("unable to release lock for %q: %v", domainBaseDir, err) } }() shouldHUP, err := removeFromFile(filepath.Join(domainBaseDir, hostsFileName), podname) if err != nil { logrus.Error(err) return nil } if !shouldHUP { // if there are no hosts, we should just stop the dnsmasq instance to not take // system resources err = dnsNameConf.stop() if err != nil { logrus.Error(err) return nil } // remove the config directory err = os.RemoveAll(domainBaseDir) if err != nil { logrus.Error(err) } return nil } // Now we need to HUP err = dnsNameConf.hup() if err != nil { logrus.Error(err) } return nil } func main() { skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, getVersion()) } func cmdCheck(args *skel.CmdArgs) error { var ( conffiles []string ) if err := findDNSMasq(); err != nil { return ErrBinaryNotFound } netConf, result, _, err := parseConfig(args.StdinData, args.Args) if err != nil { return errors.Wrap(err, "failed to parse config") } // Ensure we have previous result. if result == nil { return errors.Errorf("Required prevResult missing") } dnsNameConf, err := newDNSMasqFile(netConf.DomainName, result.Interfaces[0].Name, netConf.Name) if err != nil { return err } domainBaseDir := filepath.Dir(dnsNameConf.PidFile) lock, err := getLock(domainBaseDir) if err != nil { return err } if err := lock.acquire(); err != nil { return err } defer func() { // if the lock isn't given up by another process if err := lock.release(); err != nil { logrus.Errorf("unable to release lock for %q: %v", domainBaseDir, err) } }() pid, err := dnsNameConf.getProcess() if err != nil { return err } // Ensure the dnsmasq instance is running if !isRunning(pid) { return errors.Errorf("dnsmasq instance not running") } // Above will make sure the pidfile exists files, err := ioutil.ReadDir(dnsNameConfPath()) if err != nil { return err } for _, f := range files { conffiles = append(conffiles, f.Name()) } if !stringInSlice("addnhosts", conffiles) { return errors.Errorf("addnhost file missing from configuration") } if !stringInSlice("dnsmasq.conf", conffiles) { return errors.Errorf("dnsmasq.conf file missing from configuration") } return nil } // stringInSlice is simple util to check for the presence of a string // in a string slice func stringInSlice(s string, slice []string) bool { for _, sl := range slice { if s == sl { return true } } return false } type podname struct { types.CommonArgs K8S_POD_NAME types.UnmarshallableString `json:"podname,omitempty"` } // parseConfig parses the supplied configuration (and prevResult) from stdin. func parseConfig(stdin []byte, args string) (*DNSNameConf, *current.Result, string, error) { conf := DNSNameConf{} if err := json.Unmarshal(stdin, &conf); err != nil { return nil, nil, "", errors.Wrap(err, "failed to parse network configuration") } // Parse previous result. var result *current.Result if conf.RawPrevResult != nil { var err error if err = version.ParsePrevResult(&conf.NetConf); err != nil { return nil, nil, "", errors.Wrap(err, "could not parse prevResult") } result, err = current.NewResultFromResult(conf.PrevResult) if err != nil { return nil, nil, "", errors.Wrap(err, "could not convert result to current version") } } e := podname{} if err := types.LoadArgs(args, &e); err != nil { return nil, nil, "", err } return &conf, result, string(e.K8S_POD_NAME), nil } func findDNSMasq() error { _, err := exec.LookPath("dnsmasq") return err } ================================================ FILE: plugins/meta/dnsname/result.go ================================================ package main import ( "net" current "github.com/containernetworking/cni/pkg/types/100" "github.com/pkg/errors" ) // getIPs iterates a result and returns all the IP addresses // associated with it func getIPs(r *current.Result) ([]*net.IPNet, error) { var ( ips []*net.IPNet ) if len(r.IPs) < 1 { return nil, ErrNoIPAddressFound } if len(r.IPs) == 1 { return append(ips, &r.IPs[0].Address), nil } for _, ip := range r.IPs { if ip.Address.IP != nil && ip.Interface != nil { if isInterfaceIndexSandox(*ip.Interface, r) { ips = append(ips, &ip.Address) } else { return nil, errors.Errorf("unable to check if interface has a sandbox due to index being out of range") } } } if len(ips) < 1 { return nil, ErrNoIPAddressFound } return ips, nil } // isInterfaceIndexSandox determines if the given interface index has the sandbox // attribute and the value is greater than 0 func isInterfaceIndexSandox(idx int, r *current.Result) bool { if idx >= 0 && idx < len(r.Interfaces) { return len(r.Interfaces[idx].Sandbox) > 0 } return false } // getInterfaceAddresses gets all globalunicast IP addresses for a given // interface func getInterfaceAddresses(nameConf dnsNameFile) ([]string, error) { var nameservers []string nic, err := net.InterfaceByName(nameConf.NetworkInterface) if err != nil { return nil, err } addrs, err := nic.Addrs() if err != nil { return nil, err } for _, addr := range addrs { ip, _, err := net.ParseCIDR(addr.String()) if err != nil { return nil, err } if ip.IsGlobalUnicast() { nameservers = append(nameservers, ip.String()) } } return nameservers, nil } ================================================ FILE: plugins/meta/dnsname/service.go ================================================ package main import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "strings" "syscall" "github.com/pkg/errors" "golang.org/x/sys/unix" ) // newDNSMasqFile creates a new instance of a dnsNameFile func newDNSMasqFile(domainName, networkInterface, networkName string) (dnsNameFile, error) { dnsMasqBinary, err := exec.LookPath("dnsmasq") if err != nil { return dnsNameFile{}, errors.Errorf("the dnsmasq cni plugin requires the dnsmasq binary be in PATH") } masqConf := dnsNameFile{ ConfigFile: makePath(networkName, confFileName), Domain: domainName, PidFile: makePath(networkName, pidFileName), NetworkInterface: networkInterface, AddOnHostsFile: makePath(networkName, hostsFileName), Binary: dnsMasqBinary, } return masqConf, nil } // hup sends a sighup to a running dnsmasq to reload its hosts file. if // there is no instance of the dnsmasq, then it simply starts it. func (d dnsNameFile) hup() error { // First check for pidfile; if it does not exist, we just // start the service if _, err := os.Stat(d.PidFile); os.IsNotExist(err) { return d.start() } pid, err := d.getProcess() if err != nil { return err } if !isRunning(pid) { return d.start() } return pid.Signal(unix.SIGHUP) } // isRunning sends a signal 0 to the pid to determine if it // responds or not func isRunning(pid *os.Process) bool { if err := pid.Signal(syscall.Signal(0)); err != nil { return false } return true } // start starts the dnsmasq instance. func (d dnsNameFile) start() error { args := []string{ "-u", "root", fmt.Sprintf("--conf-file=%s", d.ConfigFile), } cmd := exec.Command(d.Binary, args...) if b, err := cmd.CombinedOutput(); err != nil { return errors.Wrapf(err, "dnsname error: dnsmasq failed with %q", b) } return nil } // stop stops the dnsmasq instance. func (d dnsNameFile) stop() error { pid, err := d.getProcess() if err != nil { return err } return pid.Kill() } // getProcess reads the PID for the dnsmasq instance and returns an // *os.Process. Returns an error if the PID does not exist. func (d dnsNameFile) getProcess() (*os.Process, error) { pidFileContents, err := ioutil.ReadFile(d.PidFile) if err != nil { return nil, err } pid, err := strconv.Atoi(strings.TrimSpace(string(pidFileContents))) if err != nil { return nil, err } return os.FindProcess(pid) } // makePath formats a path name given a domain and suffix func makePath(networkName, fileName string) string { // the generic path for where conf, host, pid files are kept is: // /run/containers/cni/dnsmasq// return filepath.Join(dnsNameConfPath(), networkName, fileName) } ================================================ FILE: plugins/meta/dnsname/version.go ================================================ package main import "fmt" // overwritten at build time var gitCommit = "unknown" const dnsnameVersion = "1.4.0-dev" func getVersion() string { return fmt.Sprintf(`CNI dnsname plugin version: %s commit: %s`, dnsnameVersion, gitCommit) } ================================================ FILE: vendor/github.com/alexflint/go-filemutex/LICENSE ================================================ The MIT License Copyright (c) 2010-2017 Alex Flint. 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. ================================================ FILE: vendor/github.com/alexflint/go-filemutex/README.md ================================================ # FileMutex FileMutex is similar to `sync.RWMutex`, but also synchronizes across processes. On Linux, OSX, and other POSIX systems it uses the flock system call. On windows it uses the LockFileEx and UnlockFileEx system calls. ```go import ( "log" "github.com/alexflint/go-filemutex" ) func main() { m, err := filemutex.New("/tmp/foo.lock") if err != nil { log.Fatalln("Directory did not exist or file could not created") } m.Lock() // Will block until lock can be acquired // Code here is protected by the mutex m.Unlock() } ``` ### Installation go get github.com/alexflint/go-filemutex Forked from https://github.com/golang/build/tree/master/cmd/builder/filemutex_*.go ================================================ FILE: vendor/github.com/alexflint/go-filemutex/filemutex_flock.go ================================================ // 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 dragonfly freebsd linux netbsd openbsd package filemutex import ( "syscall" ) const ( mkdirPerm = 0750 ) // FileMutex is similar to sync.RWMutex, but also synchronizes across processes. // This implementation is based on flock syscall. type FileMutex struct { fd int } func New(filename string) (*FileMutex, error) { fd, err := syscall.Open(filename, syscall.O_CREAT|syscall.O_RDONLY, mkdirPerm) if err != nil { return nil, err } return &FileMutex{fd: fd}, nil } func (m *FileMutex) Lock() error { if err := syscall.Flock(m.fd, syscall.LOCK_EX); err != nil { return err } return nil } func (m *FileMutex) Unlock() error { if err := syscall.Flock(m.fd, syscall.LOCK_UN); err != nil { return err } return nil } func (m *FileMutex) RLock() error { if err := syscall.Flock(m.fd, syscall.LOCK_SH); err != nil { return err } return nil } func (m *FileMutex) RUnlock() error { if err := syscall.Flock(m.fd, syscall.LOCK_UN); err != nil { return err } return nil } // Close does an Unlock() combined with closing and unlinking the associated // lock file. You should create a New() FileMutex for every Lock() attempt if // using Close(). func (m *FileMutex) Close() error { if err := syscall.Flock(m.fd, syscall.LOCK_UN); err != nil { return err } return syscall.Close(m.fd) } ================================================ FILE: vendor/github.com/alexflint/go-filemutex/filemutex_windows.go ================================================ // 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. package filemutex import ( "syscall" "unsafe" ) var ( modkernel32 = syscall.NewLazyDLL("kernel32.dll") procLockFileEx = modkernel32.NewProc("LockFileEx") procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") ) const ( lockfileExclusiveLock = 2 ) func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol))) if r1 == 0 { if e1 != 0 { err = error(e1) } else { err = syscall.EINVAL } } return } func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0) if r1 == 0 { if e1 != 0 { err = error(e1) } else { err = syscall.EINVAL } } return } // FileMutex is similar to sync.RWMutex, but also synchronizes across processes. // This implementation is based on flock syscall. type FileMutex struct { fd syscall.Handle } func New(filename string) (*FileMutex, error) { fd, err := syscall.CreateFile(&(syscall.StringToUTF16(filename)[0]), syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0) if err != nil { return nil, err } return &FileMutex{fd: fd}, nil } func (m *FileMutex) Lock() error { var ol syscall.Overlapped if err := lockFileEx(m.fd, lockfileExclusiveLock, 0, 1, 0, &ol); err != nil { return err } return nil } func (m *FileMutex) Unlock() error { var ol syscall.Overlapped if err := unlockFileEx(m.fd, 0, 1, 0, &ol); err != nil { return err } return nil } func (m *FileMutex) RLock() error { var ol syscall.Overlapped if err := lockFileEx(m.fd, 0, 0, 1, 0, &ol); err != nil { return err } return nil } func (m *FileMutex) RUnlock() error { var ol syscall.Overlapped if err := unlockFileEx(m.fd, 0, 1, 0, &ol); err != nil { return err } return nil } // Close does an Unlock() combined with closing and unlinking the associated // lock file. You should create a New() FileMutex for every Lock() attempt if // using Close(). func (m *FileMutex) Close() error { var ol syscall.Overlapped if err := unlockFileEx(m.fd, 0, 1, 0, &ol); err != nil { return err } return syscall.Close(m.fd) } ================================================ FILE: vendor/github.com/containernetworking/cni/LICENSE ================================================ 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. ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/skel/skel.go ================================================ // Copyright 2014-2016 CNI authors // // 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 skel provides skeleton code for a CNI plugin. // In particular, it implements argument parsing and validation. package skel import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "log" "os" "strings" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/utils" "github.com/containernetworking/cni/pkg/version" ) // CmdArgs captures all the arguments passed in to the plugin // via both env vars and stdin type CmdArgs struct { ContainerID string Netns string IfName string Args string Path string StdinData []byte } type dispatcher struct { Getenv func(string) string Stdin io.Reader Stdout io.Writer Stderr io.Writer ConfVersionDecoder version.ConfigDecoder VersionReconciler version.Reconciler } type reqForCmdEntry map[string]bool func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) { var cmd, contID, netns, ifName, args, path string vars := []struct { name string val *string reqForCmd reqForCmdEntry }{ { "CNI_COMMAND", &cmd, reqForCmdEntry{ "ADD": true, "CHECK": true, "DEL": true, }, }, { "CNI_CONTAINERID", &contID, reqForCmdEntry{ "ADD": true, "CHECK": true, "DEL": true, }, }, { "CNI_NETNS", &netns, reqForCmdEntry{ "ADD": true, "CHECK": true, "DEL": false, }, }, { "CNI_IFNAME", &ifName, reqForCmdEntry{ "ADD": true, "CHECK": true, "DEL": true, }, }, { "CNI_ARGS", &args, reqForCmdEntry{ "ADD": false, "CHECK": false, "DEL": false, }, }, { "CNI_PATH", &path, reqForCmdEntry{ "ADD": true, "CHECK": true, "DEL": true, }, }, } argsMissing := make([]string, 0) for _, v := range vars { *v.val = t.Getenv(v.name) if *v.val == "" { if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { argsMissing = append(argsMissing, v.name) } } } if len(argsMissing) > 0 { joined := strings.Join(argsMissing, ",") return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "") } if cmd == "VERSION" { t.Stdin = bytes.NewReader(nil) } stdinData, err := ioutil.ReadAll(t.Stdin) if err != nil { return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "") } cmdArgs := &CmdArgs{ ContainerID: contID, Netns: netns, IfName: ifName, Args: args, Path: path, StdinData: stdinData, } return cmd, cmdArgs, nil } func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error { configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) if err != nil { return types.NewError(types.ErrDecodingFailure, err.Error(), "") } verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) if verErr != nil { return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details()) } if err = toCall(cmdArgs); err != nil { if e, ok := err.(*types.Error); ok { // don't wrap Error in Error return e } return types.NewError(types.ErrInternal, err.Error(), "") } return nil } func validateConfig(jsonBytes []byte) *types.Error { var conf struct { Name string `json:"name"` } if err := json.Unmarshal(jsonBytes, &conf); err != nil { return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "") } if conf.Name == "" { return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "") } if err := utils.ValidateNetworkName(conf.Name); err != nil { return err } return nil } func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { cmd, cmdArgs, err := t.getCmdArgsFromEnv() if err != nil { // Print the about string to stderr when no command is set if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" { _, _ = fmt.Fprintln(t.Stderr, about) _, _ = fmt.Fprintf(t.Stderr, "CNI protocol versions supported: %s\n", strings.Join(versionInfo.SupportedVersions(), ", ")) return nil } return err } if cmd != "VERSION" { if err = validateConfig(cmdArgs.StdinData); err != nil { return err } if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil { return err } if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil { return err } } switch cmd { case "ADD": err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) case "CHECK": configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) if err != nil { return types.NewError(types.ErrDecodingFailure, err.Error(), "") } if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { return types.NewError(types.ErrDecodingFailure, err.Error(), "") } else if !gtet { return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "") } for _, pluginVersion := range versionInfo.SupportedVersions() { gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) if err != nil { return types.NewError(types.ErrDecodingFailure, err.Error(), "") } else if gtet { if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { return err } return nil } } return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "") case "DEL": err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) case "VERSION": if err := versionInfo.Encode(t.Stdout); err != nil { return types.NewError(types.ErrIOFailure, err.Error(), "") } default: return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "") } return err } // PluginMainWithError is the core "main" for a plugin. It accepts // callback functions for add, check, and del CNI commands and returns an error. // // The caller must also specify what CNI spec versions the plugin supports. // // It is the responsibility of the caller to check for non-nil error return. // // For a plugin to comply with the CNI spec, it must print any error to stdout // as JSON and then exit with nonzero status code. // // To let this package automatically handle errors and call os.Exit(1) for you, // use PluginMain() instead. func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { return (&dispatcher{ Getenv: os.Getenv, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) } // PluginMain is the core "main" for a plugin which includes automatic error handling. // // The caller must also specify what CNI spec versions the plugin supports. // // The caller can specify an "about" string, which is printed on stderr // when no CNI_COMMAND is specified. The recommended output is "CNI plugin v" // // When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error // as JSON to stdout and call os.Exit(1). // // To have more control over error handling, use PluginMainWithError() instead. func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { if err := e.Print(); err != nil { log.Print("Error writing error JSON to stdout: ", err) } os.Exit(1) } } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/020/types.go ================================================ // Copyright 2016 CNI authors // // 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 types020 import ( "encoding/json" "fmt" "io" "net" "os" "github.com/containernetworking/cni/pkg/types" convert "github.com/containernetworking/cni/pkg/types/internal" ) const ImplementedSpecVersion string = "0.2.0" var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} // Register converters for all versions less than the implemented spec version func init() { convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010) convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010) // Creator convert.RegisterCreator(supportedVersions, NewResult) } // Compatibility types for CNI version 0.1.0 and 0.2.0 // NewResult creates a new Result object from JSON data. The JSON data // must be compatible with the CNI versions implemented by this type. func NewResult(data []byte) (types.Result, error) { result := &Result{} if err := json.Unmarshal(data, result); err != nil { return nil, err } for _, v := range supportedVersions { if result.CNIVersion == v { if result.CNIVersion == "" { result.CNIVersion = "0.1.0" } return result, nil } } return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", supportedVersions, result.CNIVersion) } // GetResult converts the given Result object to the ImplementedSpecVersion // and returns the concrete type or an error func GetResult(r types.Result) (*Result, error) { result020, err := convert.Convert(r, ImplementedSpecVersion) if err != nil { return nil, err } result, ok := result020.(*Result) if !ok { return nil, fmt.Errorf("failed to convert result") } return result, nil } func convertFrom010(from types.Result, toVersion string) (types.Result, error) { if toVersion != "0.2.0" { panic("only converts to version 0.2.0") } fromResult := from.(*Result) return &Result{ CNIVersion: ImplementedSpecVersion, IP4: fromResult.IP4.Copy(), IP6: fromResult.IP6.Copy(), DNS: *fromResult.DNS.Copy(), }, nil } func convertTo010(from types.Result, toVersion string) (types.Result, error) { if toVersion != "0.1.0" { panic("only converts to version 0.1.0") } fromResult := from.(*Result) return &Result{ CNIVersion: "0.1.0", IP4: fromResult.IP4.Copy(), IP6: fromResult.IP6.Copy(), DNS: *fromResult.DNS.Copy(), }, nil } // Result is what gets returned from the plugin (via stdout) to the caller type Result struct { CNIVersion string `json:"cniVersion,omitempty"` IP4 *IPConfig `json:"ip4,omitempty"` IP6 *IPConfig `json:"ip6,omitempty"` DNS types.DNS `json:"dns,omitempty"` } func (r *Result) Version() string { return r.CNIVersion } func (r *Result) GetAsVersion(version string) (types.Result, error) { // If the creator of the result did not set the CNIVersion, assume it // should be the highest spec version implemented by this Result if r.CNIVersion == "" { r.CNIVersion = ImplementedSpecVersion } return convert.Convert(r, version) } func (r *Result) Print() error { return r.PrintTo(os.Stdout) } func (r *Result) PrintTo(writer io.Writer) error { data, err := json.MarshalIndent(r, "", " ") if err != nil { return err } _, err = writer.Write(data) return err } // IPConfig contains values necessary to configure an interface type IPConfig struct { IP net.IPNet Gateway net.IP Routes []types.Route } func (i *IPConfig) Copy() *IPConfig { if i == nil { return nil } var routes []types.Route for _, fromRoute := range i.Routes { routes = append(routes, *fromRoute.Copy()) } return &IPConfig{ IP: i.IP, Gateway: i.Gateway, Routes: routes, } } // net.IPNet is not JSON (un)marshallable so this duality is needed // for our custom IPNet type // JSON (un)marshallable types type ipConfig struct { IP types.IPNet `json:"ip"` Gateway net.IP `json:"gateway,omitempty"` Routes []types.Route `json:"routes,omitempty"` } func (c *IPConfig) MarshalJSON() ([]byte, error) { ipc := ipConfig{ IP: types.IPNet(c.IP), Gateway: c.Gateway, Routes: c.Routes, } return json.Marshal(ipc) } func (c *IPConfig) UnmarshalJSON(data []byte) error { ipc := ipConfig{} if err := json.Unmarshal(data, &ipc); err != nil { return err } c.IP = net.IPNet(ipc.IP) c.Gateway = ipc.Gateway c.Routes = ipc.Routes return nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/040/types.go ================================================ // Copyright 2016 CNI authors // // 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 types040 import ( "encoding/json" "fmt" "io" "net" "os" "github.com/containernetworking/cni/pkg/types" types020 "github.com/containernetworking/cni/pkg/types/020" convert "github.com/containernetworking/cni/pkg/types/internal" ) const ImplementedSpecVersion string = "0.4.0" var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} // Register converters for all versions less than the implemented spec version func init() { // Up-converters convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) convert.RegisterConverter("0.3.0", supportedVersions, convertInternal) convert.RegisterConverter("0.3.1", supportedVersions, convertInternal) // Down-converters convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal) convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x) convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x) convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x) // Creator convert.RegisterCreator(supportedVersions, NewResult) } func NewResult(data []byte) (types.Result, error) { result := &Result{} if err := json.Unmarshal(data, result); err != nil { return nil, err } for _, v := range supportedVersions { if result.CNIVersion == v { return result, nil } } return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", supportedVersions, result.CNIVersion) } func GetResult(r types.Result) (*Result, error) { resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) if err != nil { return nil, err } result, ok := resultCurrent.(*Result) if !ok { return nil, fmt.Errorf("failed to convert result") } return result, nil } func NewResultFromResult(result types.Result) (*Result, error) { newResult, err := convert.Convert(result, ImplementedSpecVersion) if err != nil { return nil, err } return newResult.(*Result), nil } // Result is what gets returned from the plugin (via stdout) to the caller type Result struct { CNIVersion string `json:"cniVersion,omitempty"` Interfaces []*Interface `json:"interfaces,omitempty"` IPs []*IPConfig `json:"ips,omitempty"` Routes []*types.Route `json:"routes,omitempty"` DNS types.DNS `json:"dns,omitempty"` } func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig { return &IPConfig{ Version: ipVersion, Address: from.IP, Gateway: from.Gateway, } } func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { fromResult := from.(*types020.Result) toResult := &Result{ CNIVersion: toVersion, DNS: *fromResult.DNS.Copy(), Routes: []*types.Route{}, } if fromResult.IP4 != nil { toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4")) for _, fromRoute := range fromResult.IP4.Routes { toResult.Routes = append(toResult.Routes, fromRoute.Copy()) } } if fromResult.IP6 != nil { toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6")) for _, fromRoute := range fromResult.IP6.Routes { toResult.Routes = append(toResult.Routes, fromRoute.Copy()) } } return toResult, nil } func convertInternal(from types.Result, toVersion string) (types.Result, error) { fromResult := from.(*Result) toResult := &Result{ CNIVersion: toVersion, DNS: *fromResult.DNS.Copy(), Routes: []*types.Route{}, } for _, fromIntf := range fromResult.Interfaces { toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy()) } for _, fromIPC := range fromResult.IPs { toResult.IPs = append(toResult.IPs, fromIPC.Copy()) } for _, fromRoute := range fromResult.Routes { toResult.Routes = append(toResult.Routes, fromRoute.Copy()) } return toResult, nil } func convertTo02x(from types.Result, toVersion string) (types.Result, error) { fromResult := from.(*Result) toResult := &types020.Result{ CNIVersion: toVersion, DNS: *fromResult.DNS.Copy(), } for _, fromIP := range fromResult.IPs { // Only convert the first IP address of each version as 0.2.0 // and earlier cannot handle multiple IP addresses if fromIP.Version == "4" && toResult.IP4 == nil { toResult.IP4 = &types020.IPConfig{ IP: fromIP.Address, Gateway: fromIP.Gateway, } } else if fromIP.Version == "6" && toResult.IP6 == nil { toResult.IP6 = &types020.IPConfig{ IP: fromIP.Address, Gateway: fromIP.Gateway, } } if toResult.IP4 != nil && toResult.IP6 != nil { break } } for _, fromRoute := range fromResult.Routes { is4 := fromRoute.Dst.IP.To4() != nil if is4 && toResult.IP4 != nil { toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{ Dst: fromRoute.Dst, GW: fromRoute.GW, }) } else if !is4 && toResult.IP6 != nil { toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{ Dst: fromRoute.Dst, GW: fromRoute.GW, }) } } // 0.2.0 and earlier require at least one IP address in the Result if toResult.IP4 == nil && toResult.IP6 == nil { return nil, fmt.Errorf("cannot convert: no valid IP addresses") } return toResult, nil } func (r *Result) Version() string { return r.CNIVersion } func (r *Result) GetAsVersion(version string) (types.Result, error) { // If the creator of the result did not set the CNIVersion, assume it // should be the highest spec version implemented by this Result if r.CNIVersion == "" { r.CNIVersion = ImplementedSpecVersion } return convert.Convert(r, version) } func (r *Result) Print() error { return r.PrintTo(os.Stdout) } func (r *Result) PrintTo(writer io.Writer) error { data, err := json.MarshalIndent(r, "", " ") if err != nil { return err } _, err = writer.Write(data) return err } // Interface contains values about the created interfaces type Interface struct { Name string `json:"name"` Mac string `json:"mac,omitempty"` Sandbox string `json:"sandbox,omitempty"` } func (i *Interface) String() string { return fmt.Sprintf("%+v", *i) } func (i *Interface) Copy() *Interface { if i == nil { return nil } newIntf := *i return &newIntf } // Int returns a pointer to the int value passed in. Used to // set the IPConfig.Interface field. func Int(v int) *int { return &v } // IPConfig contains values necessary to configure an IP address on an interface type IPConfig struct { // IP version, either "4" or "6" Version string // Index into Result structs Interfaces list Interface *int Address net.IPNet Gateway net.IP } func (i *IPConfig) String() string { return fmt.Sprintf("%+v", *i) } func (i *IPConfig) Copy() *IPConfig { if i == nil { return nil } ipc := &IPConfig{ Version: i.Version, Address: i.Address, Gateway: i.Gateway, } if i.Interface != nil { intf := *i.Interface ipc.Interface = &intf } return ipc } // JSON (un)marshallable types type ipConfig struct { Version string `json:"version"` Interface *int `json:"interface,omitempty"` Address types.IPNet `json:"address"` Gateway net.IP `json:"gateway,omitempty"` } func (c *IPConfig) MarshalJSON() ([]byte, error) { ipc := ipConfig{ Version: c.Version, Interface: c.Interface, Address: types.IPNet(c.Address), Gateway: c.Gateway, } return json.Marshal(ipc) } func (c *IPConfig) UnmarshalJSON(data []byte) error { ipc := ipConfig{} if err := json.Unmarshal(data, &ipc); err != nil { return err } c.Version = ipc.Version c.Interface = ipc.Interface c.Address = net.IPNet(ipc.Address) c.Gateway = ipc.Gateway return nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/100/types.go ================================================ // Copyright 2016 CNI authors // // 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 types100 import ( "encoding/json" "fmt" "io" "net" "os" "github.com/containernetworking/cni/pkg/types" types040 "github.com/containernetworking/cni/pkg/types/040" convert "github.com/containernetworking/cni/pkg/types/internal" ) const ImplementedSpecVersion string = "1.0.0" var supportedVersions = []string{ImplementedSpecVersion} // Register converters for all versions less than the implemented spec version func init() { // Up-converters convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x) convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x) convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x) // Down-converters convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x) // Creator convert.RegisterCreator(supportedVersions, NewResult) } func NewResult(data []byte) (types.Result, error) { result := &Result{} if err := json.Unmarshal(data, result); err != nil { return nil, err } for _, v := range supportedVersions { if result.CNIVersion == v { return result, nil } } return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", supportedVersions, result.CNIVersion) } func GetResult(r types.Result) (*Result, error) { resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) if err != nil { return nil, err } result, ok := resultCurrent.(*Result) if !ok { return nil, fmt.Errorf("failed to convert result") } return result, nil } func NewResultFromResult(result types.Result) (*Result, error) { newResult, err := convert.Convert(result, ImplementedSpecVersion) if err != nil { return nil, err } return newResult.(*Result), nil } // Result is what gets returned from the plugin (via stdout) to the caller type Result struct { CNIVersion string `json:"cniVersion,omitempty"` Interfaces []*Interface `json:"interfaces,omitempty"` IPs []*IPConfig `json:"ips,omitempty"` Routes []*types.Route `json:"routes,omitempty"` DNS types.DNS `json:"dns,omitempty"` } func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { result040, err := convert.Convert(from, "0.4.0") if err != nil { return nil, err } result100, err := convertFrom04x(result040, ImplementedSpecVersion) if err != nil { return nil, err } return result100, nil } func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig { to := &IPConfig{ Address: from.Address, Gateway: from.Gateway, } if from.Interface != nil { intf := *from.Interface to.Interface = &intf } return to } func convertInterfaceFrom040(from *types040.Interface) *Interface { return &Interface{ Name: from.Name, Mac: from.Mac, Sandbox: from.Sandbox, } } func convertFrom04x(from types.Result, toVersion string) (types.Result, error) { fromResult := from.(*types040.Result) toResult := &Result{ CNIVersion: toVersion, DNS: *fromResult.DNS.Copy(), Routes: []*types.Route{}, } for _, fromIntf := range fromResult.Interfaces { toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf)) } for _, fromIPC := range fromResult.IPs { toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC)) } for _, fromRoute := range fromResult.Routes { toResult.Routes = append(toResult.Routes, fromRoute.Copy()) } return toResult, nil } func convertIPConfigTo040(from *IPConfig) *types040.IPConfig { version := "6" if from.Address.IP.To4() != nil { version = "4" } to := &types040.IPConfig{ Version: version, Address: from.Address, Gateway: from.Gateway, } if from.Interface != nil { intf := *from.Interface to.Interface = &intf } return to } func convertInterfaceTo040(from *Interface) *types040.Interface { return &types040.Interface{ Name: from.Name, Mac: from.Mac, Sandbox: from.Sandbox, } } func convertTo04x(from types.Result, toVersion string) (types.Result, error) { fromResult := from.(*Result) toResult := &types040.Result{ CNIVersion: toVersion, DNS: *fromResult.DNS.Copy(), Routes: []*types.Route{}, } for _, fromIntf := range fromResult.Interfaces { toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf)) } for _, fromIPC := range fromResult.IPs { toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC)) } for _, fromRoute := range fromResult.Routes { toResult.Routes = append(toResult.Routes, fromRoute.Copy()) } return toResult, nil } func convertTo02x(from types.Result, toVersion string) (types.Result, error) { // First convert to 0.4.0 result040, err := convertTo04x(from, "0.4.0") if err != nil { return nil, err } result02x, err := convert.Convert(result040, toVersion) if err != nil { return nil, err } return result02x, nil } func (r *Result) Version() string { return r.CNIVersion } func (r *Result) GetAsVersion(version string) (types.Result, error) { // If the creator of the result did not set the CNIVersion, assume it // should be the highest spec version implemented by this Result if r.CNIVersion == "" { r.CNIVersion = ImplementedSpecVersion } return convert.Convert(r, version) } func (r *Result) Print() error { return r.PrintTo(os.Stdout) } func (r *Result) PrintTo(writer io.Writer) error { data, err := json.MarshalIndent(r, "", " ") if err != nil { return err } _, err = writer.Write(data) return err } // Interface contains values about the created interfaces type Interface struct { Name string `json:"name"` Mac string `json:"mac,omitempty"` Sandbox string `json:"sandbox,omitempty"` } func (i *Interface) String() string { return fmt.Sprintf("%+v", *i) } func (i *Interface) Copy() *Interface { if i == nil { return nil } newIntf := *i return &newIntf } // Int returns a pointer to the int value passed in. Used to // set the IPConfig.Interface field. func Int(v int) *int { return &v } // IPConfig contains values necessary to configure an IP address on an interface type IPConfig struct { // Index into Result structs Interfaces list Interface *int Address net.IPNet Gateway net.IP } func (i *IPConfig) String() string { return fmt.Sprintf("%+v", *i) } func (i *IPConfig) Copy() *IPConfig { if i == nil { return nil } ipc := &IPConfig{ Address: i.Address, Gateway: i.Gateway, } if i.Interface != nil { intf := *i.Interface ipc.Interface = &intf } return ipc } // JSON (un)marshallable types type ipConfig struct { Interface *int `json:"interface,omitempty"` Address types.IPNet `json:"address"` Gateway net.IP `json:"gateway,omitempty"` } func (c *IPConfig) MarshalJSON() ([]byte, error) { ipc := ipConfig{ Interface: c.Interface, Address: types.IPNet(c.Address), Gateway: c.Gateway, } return json.Marshal(ipc) } func (c *IPConfig) UnmarshalJSON(data []byte) error { ipc := ipConfig{} if err := json.Unmarshal(data, &ipc); err != nil { return err } c.Interface = ipc.Interface c.Address = net.IPNet(ipc.Address) c.Gateway = ipc.Gateway return nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/args.go ================================================ // Copyright 2015 CNI authors // // 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 types import ( "encoding" "fmt" "reflect" "strings" ) // UnmarshallableBool typedef for builtin bool // because builtin type's methods can't be declared type UnmarshallableBool bool // UnmarshalText implements the encoding.TextUnmarshaler interface. // Returns boolean true if the string is "1" or "[Tt]rue" // Returns boolean false if the string is "0" or "[Ff]alse" func (b *UnmarshallableBool) UnmarshalText(data []byte) error { s := strings.ToLower(string(data)) switch s { case "1", "true": *b = true case "0", "false": *b = false default: return fmt.Errorf("boolean unmarshal error: invalid input %s", s) } return nil } // UnmarshallableString typedef for builtin string type UnmarshallableString string // UnmarshalText implements the encoding.TextUnmarshaler interface. // Returns the string func (s *UnmarshallableString) UnmarshalText(data []byte) error { *s = UnmarshallableString(data) return nil } // CommonArgs contains the IgnoreUnknown argument // and must be embedded by all Arg structs type CommonArgs struct { IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` } // GetKeyField is a helper function to receive Values // Values that represent a pointer to a struct func GetKeyField(keyString string, v reflect.Value) reflect.Value { return v.Elem().FieldByName(keyString) } // UnmarshalableArgsError is used to indicate error unmarshalling args // from the args-string in the form "K=V;K2=V2;..." type UnmarshalableArgsError struct { error } // LoadArgs parses args from a string in the form "K=V;K2=V2;..." func LoadArgs(args string, container interface{}) error { if args == "" { return nil } containerValue := reflect.ValueOf(container) pairs := strings.Split(args, ";") unknownArgs := []string{} for _, pair := range pairs { kv := strings.Split(pair, "=") if len(kv) != 2 { return fmt.Errorf("ARGS: invalid pair %q", pair) } keyString := kv[0] valueString := kv[1] keyField := GetKeyField(keyString, containerValue) if !keyField.IsValid() { unknownArgs = append(unknownArgs, pair) continue } var keyFieldInterface interface{} switch { case keyField.Kind() == reflect.Ptr: keyField.Set(reflect.New(keyField.Type().Elem())) keyFieldInterface = keyField.Interface() case keyField.CanAddr() && keyField.Addr().CanInterface(): keyFieldInterface = keyField.Addr().Interface() default: return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)} } u, ok := keyFieldInterface.(encoding.TextUnmarshaler) if !ok { return UnmarshalableArgsError{fmt.Errorf( "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", keyString, reflect.TypeOf(keyFieldInterface))} } err := u.UnmarshalText([]byte(valueString)) if err != nil { return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err) } } isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() if len(unknownArgs) > 0 && !isIgnoreUnknown { return fmt.Errorf("ARGS: unknown args %q", unknownArgs) } return nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/create/create.go ================================================ // Copyright 2016 CNI authors // // 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 create import ( "encoding/json" "fmt" "github.com/containernetworking/cni/pkg/types" convert "github.com/containernetworking/cni/pkg/types/internal" ) // DecodeVersion returns the CNI version from CNI configuration or result JSON, // or an error if the operation could not be performed. func DecodeVersion(jsonBytes []byte) (string, error) { var conf struct { CNIVersion string `json:"cniVersion"` } err := json.Unmarshal(jsonBytes, &conf) if err != nil { return "", fmt.Errorf("decoding version from network config: %w", err) } if conf.CNIVersion == "" { return "0.1.0", nil } return conf.CNIVersion, nil } // Create creates a CNI Result using the given JSON with the expected // version, or an error if the creation could not be performed func Create(version string, bytes []byte) (types.Result, error) { return convert.Create(version, bytes) } // CreateFromBytes creates a CNI Result from the given JSON, automatically // detecting the CNI spec version of the result. An error is returned if the // operation could not be performed. func CreateFromBytes(bytes []byte) (types.Result, error) { version, err := DecodeVersion(bytes) if err != nil { return nil, err } return convert.Create(version, bytes) } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go ================================================ // Copyright 2016 CNI authors // // 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 convert import ( "fmt" "github.com/containernetworking/cni/pkg/types" ) // ConvertFn should convert from the given arbitrary Result type into a // Result implementing CNI specification version passed in toVersion. // The function is guaranteed to be passed a Result type matching the // fromVersion it was registered with, and is guaranteed to be // passed a toVersion matching one of the toVersions it was registered with. type ConvertFn func(from types.Result, toVersion string) (types.Result, error) type converter struct { // fromVersion is the CNI Result spec version that convertFn accepts fromVersion string // toVersions is a list of versions that convertFn can convert to toVersions []string convertFn ConvertFn } var converters []*converter func findConverter(fromVersion, toVersion string) *converter { for _, c := range converters { if c.fromVersion == fromVersion { for _, v := range c.toVersions { if v == toVersion { return c } } } } return nil } // Convert converts a CNI Result to the requested CNI specification version, // or returns an error if the conversion could not be performed or failed func Convert(from types.Result, toVersion string) (types.Result, error) { if toVersion == "" { toVersion = "0.1.0" } fromVersion := from.Version() // Shortcut for same version if fromVersion == toVersion { return from, nil } // Otherwise find the right converter c := findConverter(fromVersion, toVersion) if c == nil { return nil, fmt.Errorf("no converter for CNI result version %s to %s", fromVersion, toVersion) } return c.convertFn(from, toVersion) } // RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED // EXCEPT FROM CNI ITSELF. func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) { // Make sure there is no converter already registered for these // from and to versions for _, v := range toVersions { if findConverter(fromVersion, v) != nil { panic(fmt.Sprintf("converter already registered for %s to %s", fromVersion, v)) } } converters = append(converters, &converter{ fromVersion: fromVersion, toVersions: toVersions, convertFn: convertFn, }) } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/internal/create.go ================================================ // Copyright 2016 CNI authors // // 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 convert import ( "fmt" "github.com/containernetworking/cni/pkg/types" ) type ResultFactoryFunc func([]byte) (types.Result, error) type creator struct { // CNI Result spec versions that createFn can create a Result for versions []string createFn ResultFactoryFunc } var creators []*creator func findCreator(version string) *creator { for _, c := range creators { for _, v := range c.versions { if v == version { return c } } } return nil } // Create creates a CNI Result using the given JSON, or an error if the creation // could not be performed func Create(version string, bytes []byte) (types.Result, error) { if c := findCreator(version); c != nil { return c.createFn(bytes) } return nil, fmt.Errorf("unsupported CNI result version %q", version) } // RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED // EXCEPT FROM CNI ITSELF. func RegisterCreator(versions []string, createFn ResultFactoryFunc) { // Make sure there is no creator already registered for these versions for _, v := range versions { if findCreator(v) != nil { panic(fmt.Sprintf("creator already registered for %s", v)) } } creators = append(creators, &creator{ versions: versions, createFn: createFn, }) } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/types/types.go ================================================ // Copyright 2015 CNI authors // // 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 types import ( "encoding/json" "fmt" "io" "net" "os" ) // like net.IPNet but adds JSON marshalling and unmarshalling type IPNet net.IPNet // ParseCIDR takes a string like "10.2.3.1/24" and // return IPNet with "10.2.3.1" and /24 mask func ParseCIDR(s string) (*net.IPNet, error) { ip, ipn, err := net.ParseCIDR(s) if err != nil { return nil, err } ipn.IP = ip return ipn, nil } func (n IPNet) MarshalJSON() ([]byte, error) { return json.Marshal((*net.IPNet)(&n).String()) } func (n *IPNet) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } tmp, err := ParseCIDR(s) if err != nil { return err } *n = IPNet(*tmp) return nil } // NetConf describes a network. type NetConf struct { CNIVersion string `json:"cniVersion,omitempty"` Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` Capabilities map[string]bool `json:"capabilities,omitempty"` IPAM IPAM `json:"ipam,omitempty"` DNS DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` PrevResult Result `json:"-"` } type IPAM struct { Type string `json:"type,omitempty"` } // NetConfList describes an ordered list of networks. type NetConfList struct { CNIVersion string `json:"cniVersion,omitempty"` Name string `json:"name,omitempty"` DisableCheck bool `json:"disableCheck,omitempty"` Plugins []*NetConf `json:"plugins,omitempty"` } // Result is an interface that provides the result of plugin execution type Result interface { // The highest CNI specification result version the result supports // without having to convert Version() string // Returns the result converted into the requested CNI specification // result version, or an error if conversion failed GetAsVersion(version string) (Result, error) // Prints the result in JSON format to stdout Print() error // Prints the result in JSON format to provided writer PrintTo(writer io.Writer) error } func PrintResult(result Result, version string) error { newResult, err := result.GetAsVersion(version) if err != nil { return err } return newResult.Print() } // DNS contains values interesting for DNS resolvers type DNS struct { Nameservers []string `json:"nameservers,omitempty"` Domain string `json:"domain,omitempty"` Search []string `json:"search,omitempty"` Options []string `json:"options,omitempty"` } func (d *DNS) Copy() *DNS { if d == nil { return nil } to := &DNS{Domain: d.Domain} for _, ns := range d.Nameservers { to.Nameservers = append(to.Nameservers, ns) } for _, s := range d.Search { to.Search = append(to.Search, s) } for _, o := range d.Options { to.Options = append(to.Options, o) } return to } type Route struct { Dst net.IPNet GW net.IP } func (r *Route) String() string { return fmt.Sprintf("%+v", *r) } func (r *Route) Copy() *Route { if r == nil { return nil } return &Route{ Dst: r.Dst, GW: r.GW, } } // Well known error codes // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes const ( ErrUnknown uint = iota // 0 ErrIncompatibleCNIVersion // 1 ErrUnsupportedField // 2 ErrUnknownContainer // 3 ErrInvalidEnvironmentVariables // 4 ErrIOFailure // 5 ErrDecodingFailure // 6 ErrInvalidNetworkConfig // 7 ErrTryAgainLater uint = 11 ErrInternal uint = 999 ) type Error struct { Code uint `json:"code"` Msg string `json:"msg"` Details string `json:"details,omitempty"` } func NewError(code uint, msg, details string) *Error { return &Error{ Code: code, Msg: msg, Details: details, } } func (e *Error) Error() string { details := "" if e.Details != "" { details = fmt.Sprintf("; %v", e.Details) } return fmt.Sprintf("%v%v", e.Msg, details) } func (e *Error) Print() error { return prettyPrint(e) } // net.IPNet is not JSON (un)marshallable so this duality is needed // for our custom IPNet type // JSON (un)marshallable types type route struct { Dst IPNet `json:"dst"` GW net.IP `json:"gw,omitempty"` } func (r *Route) UnmarshalJSON(data []byte) error { rt := route{} if err := json.Unmarshal(data, &rt); err != nil { return err } r.Dst = net.IPNet(rt.Dst) r.GW = rt.GW return nil } func (r Route) MarshalJSON() ([]byte, error) { rt := route{ Dst: IPNet(r.Dst), GW: r.GW, } return json.Marshal(rt) } func prettyPrint(obj interface{}) error { data, err := json.MarshalIndent(obj, "", " ") if err != nil { return err } _, err = os.Stdout.Write(data) return err } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/utils/utils.go ================================================ // Copyright 2019 CNI authors // // 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 utils import ( "bytes" "fmt" "regexp" "unicode" "github.com/containernetworking/cni/pkg/types" ) const ( // cniValidNameChars is the regexp used to validate valid characters in // containerID and networkName cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]` // maxInterfaceNameLength is the length max of a valid interface name maxInterfaceNameLength = 15 ) var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`) // ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters func ValidateContainerID(containerID string) *types.Error { if containerID == "" { return types.NewError(types.ErrUnknownContainer, "missing containerID", "") } if !cniReg.MatchString(containerID) { return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID) } return nil } // ValidateNetworkName will validate that the supplied networkName does not contain invalid characters func ValidateNetworkName(networkName string) *types.Error { if networkName == "" { return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "") } if !cniReg.MatchString(networkName) { return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName) } return nil } // ValidateInterfaceName will validate the interface name based on the three rules below // 1. The name must not be empty // 2. The name must be less than 16 characters // 3. The name must not be "." or ".." // 3. The name must not contain / or : or any whitespace characters // ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024 func ValidateInterfaceName(ifName string) *types.Error { if len(ifName) == 0 { return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "") } if len(ifName) > maxInterfaceNameLength { return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1)) } if ifName == "." || ifName == ".." { return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "") } for _, r := range bytes.Runes([]byte(ifName)) { if r == '/' || r == ':' || unicode.IsSpace(r) { return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "") } } return nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/version/conf.go ================================================ // Copyright 2016 CNI authors // // 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 version import ( "github.com/containernetworking/cni/pkg/types/create" ) // ConfigDecoder can decode the CNI version available in network config data type ConfigDecoder struct{} func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { return create.DecodeVersion(jsonBytes) } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/version/plugin.go ================================================ // Copyright 2016 CNI authors // // 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 version import ( "encoding/json" "fmt" "io" "strconv" "strings" ) // PluginInfo reports information about CNI versioning type PluginInfo interface { // SupportedVersions returns one or more CNI spec versions that the plugin // supports. If input is provided in one of these versions, then the plugin // promises to use the same CNI version in its response SupportedVersions() []string // Encode writes this CNI version information as JSON to the given Writer Encode(io.Writer) error } type pluginInfo struct { CNIVersion_ string `json:"cniVersion"` SupportedVersions_ []string `json:"supportedVersions,omitempty"` } // pluginInfo implements the PluginInfo interface var _ PluginInfo = &pluginInfo{} func (p *pluginInfo) Encode(w io.Writer) error { return json.NewEncoder(w).Encode(p) } func (p *pluginInfo) SupportedVersions() []string { return p.SupportedVersions_ } // PluginSupports returns a new PluginInfo that will report the given versions // as supported func PluginSupports(supportedVersions ...string) PluginInfo { if len(supportedVersions) < 1 { panic("programmer error: you must support at least one version") } return &pluginInfo{ CNIVersion_: Current(), SupportedVersions_: supportedVersions, } } // PluginDecoder can decode the response returned by a plugin's VERSION command type PluginDecoder struct{} func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { var info pluginInfo err := json.Unmarshal(jsonBytes, &info) if err != nil { return nil, fmt.Errorf("decoding version info: %w", err) } if info.CNIVersion_ == "" { return nil, fmt.Errorf("decoding version info: missing field cniVersion") } if len(info.SupportedVersions_) == 0 { if info.CNIVersion_ == "0.2.0" { return PluginSupports("0.1.0", "0.2.0"), nil } return nil, fmt.Errorf("decoding version info: missing field supportedVersions") } return &info, nil } // ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, // minor, and micro numbers or returns an error func ParseVersion(version string) (int, int, int, error) { var major, minor, micro int if version == "" { // special case: no version declared == v0.1.0 return 0, 1, 0, nil } parts := strings.Split(version, ".") if len(parts) >= 4 { return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) } major, err := strconv.Atoi(parts[0]) if err != nil { return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %w", parts[0], err) } if len(parts) >= 2 { minor, err = strconv.Atoi(parts[1]) if err != nil { return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %w", parts[1], err) } } if len(parts) >= 3 { micro, err = strconv.Atoi(parts[2]) if err != nil { return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %w", parts[2], err) } } return major, minor, micro, nil } // GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro // numbers, and compares them to determine whether the first version is greater // than or equal to the second func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { firstMajor, firstMinor, firstMicro, err := ParseVersion(version) if err != nil { return false, err } secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) if err != nil { return false, err } if firstMajor > secondMajor { return true, nil } else if firstMajor == secondMajor { if firstMinor > secondMinor { return true, nil } else if firstMinor == secondMinor && firstMicro >= secondMicro { return true, nil } } return false, nil } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/version/reconcile.go ================================================ // Copyright 2016 CNI authors // // 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 version import "fmt" type ErrorIncompatible struct { Config string Supported []string } func (e *ErrorIncompatible) Details() string { return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) } func (e *ErrorIncompatible) Error() string { return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) } type Reconciler struct{} func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) } func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { for _, supportedVersion := range supportedVersions { if configVersion == supportedVersion { return nil } } return &ErrorIncompatible{ Config: configVersion, Supported: supportedVersions, } } ================================================ FILE: vendor/github.com/containernetworking/cni/pkg/version/version.go ================================================ // Copyright 2016 CNI authors // // 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 version import ( "encoding/json" "fmt" "github.com/containernetworking/cni/pkg/types" types100 "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/types/create" ) // Current reports the version of the CNI spec implemented by this library func Current() string { return types100.ImplementedSpecVersion } // Legacy PluginInfo describes a plugin that is backwards compatible with the // CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 // library ought to work correctly with a plugin that reports support for // Legacy versions. // // Any future CNI spec versions which meet this definition should be added to // this list. var Legacy = PluginSupports("0.1.0", "0.2.0") var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0") // VersionsFrom returns a list of versions starting from min, inclusive func VersionsStartingFrom(min string) PluginInfo { out := []string{} // cheat, just assume ordered ok := false for _, v := range All.SupportedVersions() { if !ok && v == min { ok = true } if ok { out = append(out, v) } } return PluginSupports(out...) } // Finds a Result object matching the requested version (if any) and asks // that object to parse the plugin result, returning an error if parsing failed. func NewResult(version string, resultBytes []byte) (types.Result, error) { return create.Create(version, resultBytes) } // ParsePrevResult parses a prevResult in a NetConf structure and sets // the NetConf's PrevResult member to the parsed Result object. func ParsePrevResult(conf *types.NetConf) error { if conf.RawPrevResult == nil { return nil } // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the // result version must match the config version, if the Result's version // is empty, inject the config version. if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" { conf.RawPrevResult["CNIVersion"] = conf.CNIVersion } resultBytes, err := json.Marshal(conf.RawPrevResult) if err != nil { return fmt.Errorf("could not serialize prevResult: %w", err) } conf.RawPrevResult = nil conf.PrevResult, err = create.Create(conf.CNIVersion, resultBytes) if err != nil { return fmt.Errorf("could not parse prevResult: %w", err) } return nil } ================================================ FILE: vendor/github.com/containernetworking/plugins/LICENSE ================================================ 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. ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/ns/README.md ================================================ ### Namespaces, Threads, and Go On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code. ### Namespace Switching Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads. Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in. For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly. ### Do() The Recommended Thing The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: ```go err = targetNs.Do(func(hostNs ns.NetNS) error { dummy := &netlink.Dummy{ LinkAttrs: netlink.LinkAttrs{ Name: "dummy0", }, } return netlink.LinkAdd(dummy) }) ``` Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled. In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater. ### Creating network namespaces Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration. ### Further Reading - https://github.com/golang/go/wiki/LockOSThread - http://morsmachine.dk/go-scheduler - https://github.com/containernetworking/cni/issues/262 - https://golang.org/pkg/runtime/ - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go ================================================ // Copyright 2015-2017 CNI authors // // 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 ns import ( "fmt" "os" "runtime" "sync" "syscall" "golang.org/x/sys/unix" ) // Returns an object representing the current OS thread's network namespace func GetCurrentNS() (NetNS, error) { // Lock the thread in case other goroutine executes in it and changes its // network namespace after getCurrentThreadNetNSPath(), otherwise it might // return an unexpected network namespace. runtime.LockOSThread() defer runtime.UnlockOSThread() return GetNS(getCurrentThreadNetNSPath()) } func getCurrentThreadNetNSPath() string { // /proc/self/ns/net returns the namespace of the main thread, not // of whatever thread this goroutine is running on. Make sure we // use the thread's net namespace since the thread is switching around return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) } func (ns *netNS) Close() error { if err := ns.errorIfClosed(); err != nil { return err } if err := ns.file.Close(); err != nil { return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err) } ns.closed = true return nil } func (ns *netNS) Set() error { if err := ns.errorIfClosed(); err != nil { return err } if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil { return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err) } return nil } type NetNS interface { // Executes the passed closure in this object's network namespace, // attempting to restore the original namespace before returning. // However, since each OS thread can have a different network namespace, // and Go's thread scheduling is highly variable, callers cannot // guarantee any specific namespace is set unless operations that // require that namespace are wrapped with Do(). Also, no code called // from Do() should call runtime.UnlockOSThread(), or the risk // of executing code in an incorrect namespace will be greater. See // https://github.com/golang/go/wiki/LockOSThread for further details. Do(toRun func(NetNS) error) error // Sets the current network namespace to this object's network namespace. // Note that since Go's thread scheduling is highly variable, callers // cannot guarantee the requested namespace will be the current namespace // after this function is called; to ensure this wrap operations that // require the namespace with Do() instead. Set() error // Returns the filesystem path representing this object's network namespace Path() string // Returns a file descriptor representing this object's network namespace Fd() uintptr // Cleans up this instance of the network namespace; if this instance // is the last user the namespace will be destroyed Close() error } type netNS struct { file *os.File closed bool } // netNS implements the NetNS interface var _ NetNS = &netNS{} const ( // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h NSFS_MAGIC = 0x6e736673 PROCFS_MAGIC = 0x9fa0 ) type NSPathNotExistErr struct{ msg string } func (e NSPathNotExistErr) Error() string { return e.msg } type NSPathNotNSErr struct{ msg string } func (e NSPathNotNSErr) Error() string { return e.msg } func IsNSorErr(nspath string) error { stat := syscall.Statfs_t{} if err := syscall.Statfs(nspath, &stat); err != nil { if os.IsNotExist(err) { err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)} } else { err = fmt.Errorf("failed to Statfs %q: %v", nspath, err) } return err } switch stat.Type { case PROCFS_MAGIC, NSFS_MAGIC: return nil default: return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)} } } // Returns an object representing the namespace referred to by @path func GetNS(nspath string) (NetNS, error) { err := IsNSorErr(nspath) if err != nil { return nil, err } fd, err := os.Open(nspath) if err != nil { return nil, err } return &netNS{file: fd}, nil } func (ns *netNS) Path() string { return ns.file.Name() } func (ns *netNS) Fd() uintptr { return ns.file.Fd() } func (ns *netNS) errorIfClosed() error { if ns.closed { return fmt.Errorf("%q has already been closed", ns.file.Name()) } return nil } func (ns *netNS) Do(toRun func(NetNS) error) error { if err := ns.errorIfClosed(); err != nil { return err } containedCall := func(hostNS NetNS) error { threadNS, err := GetCurrentNS() if err != nil { return fmt.Errorf("failed to open current netns: %v", err) } defer threadNS.Close() // switch to target namespace if err = ns.Set(); err != nil { return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) } defer func() { err := threadNS.Set() // switch back if err == nil { // Unlock the current thread only when we successfully switched back // to the original namespace; otherwise leave the thread locked which // will force the runtime to scrap the current thread, that is maybe // not as optimal but at least always safe to do. runtime.UnlockOSThread() } }() return toRun(hostNS) } // save a handle to current network namespace hostNS, err := GetCurrentNS() if err != nil { return fmt.Errorf("Failed to open current namespace: %v", err) } defer hostNS.Close() var wg sync.WaitGroup wg.Add(1) // Start the callback in a new green thread so that if we later fail // to switch the namespace back to the original one, we can safely // leave the thread locked to die without a risk of the current thread // left lingering with incorrect namespace. var innerError error go func() { defer wg.Done() runtime.LockOSThread() innerError = containedCall(hostNS) }() wg.Wait() return innerError } // WithNetNSPath executes the passed closure under the given network // namespace, restoring the original namespace afterwards. func WithNetNSPath(nspath string, toRun func(NetNS) error) error { ns, err := GetNS(nspath) if err != nil { return err } defer ns.Close() return ns.Do(toRun) } ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/testutils/bad_reader.go ================================================ // Copyright 2016 CNI authors // // 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 testutils import "errors" // BadReader is an io.Reader which always errors type BadReader struct { Error error } func (r *BadReader) Read(buffer []byte) (int, error) { if r.Error != nil { return 0, r.Error } return 0, errors.New("banana") } func (r *BadReader) Close() error { return nil } ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/testutils/cmd.go ================================================ // Copyright 2016 CNI authors // // 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 testutils import ( "io/ioutil" "os" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/version" ) func envCleanup() { os.Unsetenv("CNI_COMMAND") os.Unsetenv("CNI_PATH") os.Unsetenv("CNI_NETNS") os.Unsetenv("CNI_IFNAME") os.Unsetenv("CNI_CONTAINERID") } func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) { os.Setenv("CNI_COMMAND", "ADD") os.Setenv("CNI_PATH", os.Getenv("PATH")) os.Setenv("CNI_NETNS", cniNetns) os.Setenv("CNI_IFNAME", cniIfname) os.Setenv("CNI_CONTAINERID", cniContainerID) defer envCleanup() // Redirect stdout to capture plugin result oldStdout := os.Stdout r, w, err := os.Pipe() if err != nil { return nil, nil, err } os.Stdout = w err = f() w.Close() var out []byte if err == nil { out, err = ioutil.ReadAll(r) } os.Stdout = oldStdout // Return errors after restoring stdout so Ginkgo will correctly // emit verbose error information on stdout if err != nil { return nil, nil, err } // Plugin must return result in same version as specified in netconf versionDecoder := &version.ConfigDecoder{} confVersion, err := versionDecoder.Decode(conf) if err != nil { return nil, nil, err } result, err := version.NewResult(confVersion, out) if err != nil { return nil, nil, err } return result, out, nil } func CmdAddWithArgs(args *skel.CmdArgs, f func() error) (types.Result, []byte, error) { return CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) } func CmdCheck(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) error { os.Setenv("CNI_COMMAND", "CHECK") os.Setenv("CNI_PATH", os.Getenv("PATH")) os.Setenv("CNI_NETNS", cniNetns) os.Setenv("CNI_IFNAME", cniIfname) os.Setenv("CNI_CONTAINERID", cniContainerID) defer envCleanup() return f() } func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error { return CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) } func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error { os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_PATH", os.Getenv("PATH")) os.Setenv("CNI_NETNS", cniNetns) os.Setenv("CNI_IFNAME", cniIfname) os.Setenv("CNI_CONTAINERID", cniContainerID) defer envCleanup() return f() } func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error { return CmdDel(args.Netns, args.ContainerID, args.IfName, f) } ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/testutils/dns.go ================================================ // Copyright 2019 CNI authors // // 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 testutils import ( "fmt" "io/ioutil" "os" "strings" "github.com/containernetworking/cni/pkg/types" ) // TmpResolvConf will create a temporary file and write the provided DNS settings to // it in the resolv.conf format. It returns the path of the created temporary file or // an error if any occurs while creating/writing the file. It is the caller's // responsibility to remove the file. func TmpResolvConf(dnsConf types.DNS) (string, error) { f, err := ioutil.TempFile("", "cni_test_resolv.conf") if err != nil { return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err) } defer f.Close() path := f.Name() defer func() { if err != nil { os.RemoveAll(path) } }() // see "man 5 resolv.conf" for the format of resolv.conf var resolvConfLines []string for _, nameserver := range dnsConf.Nameservers { resolvConfLines = append(resolvConfLines, fmt.Sprintf("nameserver %s", nameserver)) } resolvConfLines = append(resolvConfLines, fmt.Sprintf("domain %s", dnsConf.Domain)) resolvConfLines = append(resolvConfLines, fmt.Sprintf("search %s", strings.Join(dnsConf.Search, " "))) resolvConfLines = append(resolvConfLines, fmt.Sprintf("options %s", strings.Join(dnsConf.Options, " "))) resolvConf := strings.Join(resolvConfLines, "\n") _, err = f.Write([]byte(resolvConf)) if err != nil { return "", fmt.Errorf("failed to write temp resolv.conf for CNI test: %v", err) } return path, err } ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/testutils/netns_linux.go ================================================ // Copyright 2018 CNI authors // // 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 testutils import ( "crypto/rand" "fmt" "os" "path" "runtime" "strings" "sync" "syscall" "github.com/containernetworking/plugins/pkg/ns" "golang.org/x/sys/unix" ) func getNsRunDir() string { xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") /// If XDG_RUNTIME_DIR is set, check if the current user owns /var/run. If // the owner is different, we are most likely running in a user namespace. // In that case use $XDG_RUNTIME_DIR/netns as runtime dir. if xdgRuntimeDir != "" { if s, err := os.Stat("/var/run"); err == nil { st, ok := s.Sys().(*syscall.Stat_t) if ok && int(st.Uid) != os.Geteuid() { return path.Join(xdgRuntimeDir, "netns") } } } return "/var/run/netns" } // Creates a new persistent (bind-mounted) network namespace and returns an object // representing that namespace, without switching to it. func NewNS() (ns.NetNS, error) { nsRunDir := getNsRunDir() b := make([]byte, 16) _, err := rand.Reader.Read(b) if err != nil { return nil, fmt.Errorf("failed to generate random netns name: %v", err) } // Create the directory for mounting network namespaces // This needs to be a shared mountpoint in case it is mounted in to // other namespaces (containers) err = os.MkdirAll(nsRunDir, 0755) if err != nil { return nil, err } // Remount the namespace directory shared. This will fail if it is not // already a mountpoint, so bind-mount it on to itself to "upgrade" it // to a mountpoint. err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") if err != nil { if err != unix.EINVAL { return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) } // Recursively remount /var/run/netns on itself. The recursive flag is // so that any existing netns bindmounts are carried over. err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { return nil, fmt.Errorf("mount --rbind %s %s failed: %q", nsRunDir, nsRunDir, err) } // Now we can make it shared err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") if err != nil { return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) } } nsName := fmt.Sprintf("cnitest-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) // create an empty file at the mount point nsPath := path.Join(nsRunDir, nsName) mountPointFd, err := os.Create(nsPath) if err != nil { return nil, err } mountPointFd.Close() // Ensure the mount point is cleaned up on errors; if the namespace // was successfully mounted this will have no effect because the file // is in-use defer os.RemoveAll(nsPath) var wg sync.WaitGroup wg.Add(1) // do namespace work in a dedicated goroutine, so that we can safely // Lock/Unlock OSThread without upsetting the lock/unlock state of // the caller of this function go (func() { defer wg.Done() runtime.LockOSThread() // Don't unlock. By not unlocking, golang will kill the OS thread when the // goroutine is done (for go1.10+) var origNS ns.NetNS origNS, err = ns.GetNS(getCurrentThreadNetNSPath()) if err != nil { return } defer origNS.Close() // create a new netns on the current thread err = unix.Unshare(unix.CLONE_NEWNET) if err != nil { return } // Put this thread back to the orig ns, since it might get reused (pre go1.10) defer origNS.Set() // bind mount the netns from the current thread (from /proc) onto the // mount point. This causes the namespace to persist, even when there // are no threads in the ns. err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "") if err != nil { err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err) } })() wg.Wait() if err != nil { return nil, fmt.Errorf("failed to create namespace: %v", err) } return ns.GetNS(nsPath) } // UnmountNS unmounts the NS held by the netns object func UnmountNS(ns ns.NetNS) error { nsPath := ns.Path() // Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) if strings.HasPrefix(nsPath, getNsRunDir()) { if err := unix.Unmount(nsPath, 0); err != nil { return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err) } if err := os.Remove(nsPath); err != nil { return fmt.Errorf("failed to remove ns path %s: %v", nsPath, err) } } return nil } // getCurrentThreadNetNSPath copied from pkg/ns func getCurrentThreadNetNSPath() string { // /proc/self/ns/net returns the namespace of the main thread, not // of whatever thread this goroutine is running on. Make sure we // use the thread's net namespace since the thread is switching around return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) } ================================================ FILE: vendor/github.com/containernetworking/plugins/pkg/testutils/ping.go ================================================ // Copyright 2017 CNI authors // // 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 testutils import ( "bytes" "fmt" "os/exec" "strconv" "syscall" ) // Ping shells out to the `ping` command. Returns nil if successful. func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error { args := []string{ "-c", "1", "-W", strconv.Itoa(timeoutSec), "-I", saddr, daddr, } bin := "ping" if isV6 { bin = "ping6" } cmd := exec.Command(bin, args...) var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Run(); err != nil { switch e := err.(type) { case *exec.ExitError: return fmt.Errorf("%v exit status %d: %s", args, e.Sys().(syscall.WaitStatus).ExitStatus(), stderr.String()) default: return err } } return nil } ================================================ FILE: vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk/backend.go ================================================ // Copyright 2015 CNI authors // // 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 disk import ( "io/ioutil" "net" "os" "path/filepath" "runtime" "strings" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" ) const lastIPFilePrefix = "last_reserved_ip." const LineBreak = "\r\n" var defaultDataDir = "/var/lib/cni/networks" // Store is a simple disk-backed store that creates one file per IP // address in a given directory. The contents of the file are the container ID. type Store struct { *FileLock dataDir string } // Store implements the Store interface var _ backend.Store = &Store{} func New(network, dataDir string) (*Store, error) { if dataDir == "" { dataDir = defaultDataDir } dir := filepath.Join(dataDir, network) if err := os.MkdirAll(dir, 0755); err != nil { return nil, err } lk, err := NewFileLock(dir) if err != nil { return nil, err } return &Store{lk, dir}, nil } func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) { fname := GetEscapedPath(s.dataDir, ip.String()) f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0644) if os.IsExist(err) { return false, nil } if err != nil { return false, err } if _, err := f.WriteString(strings.TrimSpace(id) + LineBreak + ifname); err != nil { f.Close() os.Remove(f.Name()) return false, err } if err := f.Close(); err != nil { os.Remove(f.Name()) return false, err } // store the reserved ip in lastIPFile ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID) err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644) if err != nil { return false, err } return true, nil } // LastReservedIP returns the last reserved IP if exists func (s *Store) LastReservedIP(rangeID string) (net.IP, error) { ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID) data, err := ioutil.ReadFile(ipfile) if err != nil { return nil, err } return net.ParseIP(string(data)), nil } func (s *Store) Release(ip net.IP) error { return os.Remove(GetEscapedPath(s.dataDir, ip.String())) } func (s *Store) FindByKey(id string, ifname string, match string) (bool, error) { found := false err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() { return nil } data, err := ioutil.ReadFile(path) if err != nil { return nil } if strings.TrimSpace(string(data)) == match { found = true } return nil }) return found, err } func (s *Store) FindByID(id string, ifname string) bool { s.Lock() defer s.Unlock() found := false match := strings.TrimSpace(id) + LineBreak + ifname found, err := s.FindByKey(id, ifname, match) // Match anything created by this id if !found && err == nil { match := strings.TrimSpace(id) found, err = s.FindByKey(id, ifname, match) } return found } func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, error) { found := false err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() { return nil } data, err := ioutil.ReadFile(path) if err != nil { return nil } if strings.TrimSpace(string(data)) == match { if err := os.Remove(path); err != nil { return nil } found = true } return nil }) return found, err } // N.B. This function eats errors to be tolerant and // release as much as possible func (s *Store) ReleaseByID(id string, ifname string) error { found := false match := strings.TrimSpace(id) + LineBreak + ifname found, err := s.ReleaseByKey(id, ifname, match) // For backwards compatibility, look for files written by a previous version if !found && err == nil { match := strings.TrimSpace(id) found, err = s.ReleaseByKey(id, ifname, match) } return err } // GetByID returns the IPs which have been allocated to the specific ID func (s *Store) GetByID(id string, ifname string) []net.IP { var ips []net.IP match := strings.TrimSpace(id) + LineBreak + ifname // matchOld for backwards compatibility matchOld := strings.TrimSpace(id) // walk through all ips in this network to get the ones which belong to a specific ID _ = filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() { return nil } data, err := ioutil.ReadFile(path) if err != nil { return nil } if strings.TrimSpace(string(data)) == match || strings.TrimSpace(string(data)) == matchOld { _, ipString := filepath.Split(path) if ip := net.ParseIP(ipString); ip != nil { ips = append(ips, ip) } } return nil }) return ips } func GetEscapedPath(dataDir string, fname string) string { if runtime.GOOS == "windows" { fname = strings.Replace(fname, ":", "_", -1) } return filepath.Join(dataDir, fname) } ================================================ FILE: vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk/lock.go ================================================ // Copyright 2015 CNI authors // // 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 disk import ( "github.com/alexflint/go-filemutex" "os" "path" ) // FileLock wraps os.File to be used as a lock using flock type FileLock struct { f *filemutex.FileMutex } // NewFileLock opens file/dir at path and returns unlocked FileLock object func NewFileLock(lockPath string) (*FileLock, error) { fi, err := os.Stat(lockPath) if err != nil { return nil, err } if fi.IsDir() { lockPath = path.Join(lockPath, "lock") } f, err := filemutex.New(lockPath) if err != nil { return nil, err } return &FileLock{f}, nil } func (l *FileLock) Close() error { return l.f.Close() } // Lock acquires an exclusive lock func (l *FileLock) Lock() error { return l.f.Lock() } // Unlock releases the lock func (l *FileLock) Unlock() error { return l.f.Unlock() } ================================================ FILE: vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go ================================================ // Copyright 2015 CNI authors // // 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 backend import "net" type Store interface { Lock() error Unlock() error Close() error Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) LastReservedIP(rangeID string) (net.IP, error) Release(ip net.IP) error ReleaseByID(id string, ifname string) error GetByID(id string, ifname string) []net.IP } ================================================ FILE: vendor/github.com/coreos/go-iptables/LICENSE ================================================ 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. ================================================ FILE: vendor/github.com/coreos/go-iptables/NOTICE ================================================ CoreOS Project Copyright 2018 CoreOS, Inc This product includes software developed at CoreOS, Inc. (http://www.coreos.com/). ================================================ FILE: vendor/github.com/coreos/go-iptables/iptables/iptables.go ================================================ // Copyright 2015 CoreOS, Inc. // // 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 iptables import ( "bytes" "fmt" "io" "net" "os/exec" "regexp" "strconv" "strings" "syscall" ) // Adds the output of stderr to exec.ExitError type Error struct { exec.ExitError cmd exec.Cmd msg string exitStatus *int //for overriding } func (e *Error) ExitStatus() int { if e.exitStatus != nil { return *e.exitStatus } return e.Sys().(syscall.WaitStatus).ExitStatus() } func (e *Error) Error() string { return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg) } // IsNotExist returns true if the error is due to the chain or rule not existing func (e *Error) IsNotExist() bool { if e.ExitStatus() != 1 { return false } msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n" msgNoChainExist := "No chain/target/match by that name.\n" return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) } // Protocol to differentiate between IPv4 and IPv6 type Protocol byte const ( ProtocolIPv4 Protocol = iota ProtocolIPv6 ) type IPTables struct { path string proto Protocol hasCheck bool hasWait bool waitSupportSecond bool hasRandomFully bool v1 int v2 int v3 int mode string // the underlying iptables operating mode, e.g. nf_tables timeout int // time to wait for the iptables lock, default waits forever } // Stat represents a structured statistic entry. type Stat struct { Packets uint64 `json:"pkts"` Bytes uint64 `json:"bytes"` Target string `json:"target"` Protocol string `json:"prot"` Opt string `json:"opt"` Input string `json:"in"` Output string `json:"out"` Source *net.IPNet `json:"source"` Destination *net.IPNet `json:"destination"` Options string `json:"options"` } type option func(*IPTables) func IPFamily(proto Protocol) option { return func(ipt *IPTables) { ipt.proto = proto } } func Timeout(timeout int) option { return func(ipt *IPTables) { ipt.timeout = timeout } } // New creates a new IPTables configured with the options passed as parameter. // For backwards compatibility, by default always uses IPv4 and timeout 0. // i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing // the IPFamily and Timeout options as follow: // ip6t := New(IPFamily(ProtocolIPv6), Timeout(5)) func New(opts ...option) (*IPTables, error) { ipt := &IPTables{ proto: ProtocolIPv4, timeout: 0, } for _, opt := range opts { opt(ipt) } path, err := exec.LookPath(getIptablesCommand(ipt.proto)) if err != nil { return nil, err } ipt.path = path vstring, err := getIptablesVersionString(path) if err != nil { return nil, fmt.Errorf("could not get iptables version: %v", err) } v1, v2, v3, mode, err := extractIptablesVersion(vstring) if err != nil { return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) } ipt.v1 = v1 ipt.v2 = v2 ipt.v3 = v3 ipt.mode = mode checkPresent, waitPresent, waitSupportSecond, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) ipt.hasCheck = checkPresent ipt.hasWait = waitPresent ipt.waitSupportSecond = waitSupportSecond ipt.hasRandomFully = randomFullyPresent return ipt, nil } // New creates a new IPTables for the given proto. // The proto will determine which command is used, either "iptables" or "ip6tables". func NewWithProtocol(proto Protocol) (*IPTables, error) { return New(IPFamily(proto), Timeout(0)) } // Proto returns the protocol used by this IPTables. func (ipt *IPTables) Proto() Protocol { return ipt.proto } // Exists checks if given rulespec in specified table/chain exists func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) { if !ipt.hasCheck { return ipt.existsForOldIptables(table, chain, rulespec) } cmd := append([]string{"-t", table, "-C", chain}, rulespec...) err := ipt.run(cmd...) eerr, eok := err.(*Error) switch { case err == nil: return true, nil case eok && eerr.ExitStatus() == 1: return false, nil default: return false, err } } // Insert inserts rulespec to specified table/chain (in specified pos) func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error { cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...) return ipt.run(cmd...) } // Append appends rulespec to specified table/chain func (ipt *IPTables) Append(table, chain string, rulespec ...string) error { cmd := append([]string{"-t", table, "-A", chain}, rulespec...) return ipt.run(cmd...) } // AppendUnique acts like Append except that it won't add a duplicate func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error { exists, err := ipt.Exists(table, chain, rulespec...) if err != nil { return err } if !exists { return ipt.Append(table, chain, rulespec...) } return nil } // Delete removes rulespec in specified table/chain func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error { cmd := append([]string{"-t", table, "-D", chain}, rulespec...) return ipt.run(cmd...) } func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error { exists, err := ipt.Exists(table, chain, rulespec...) if err == nil && exists { err = ipt.Delete(table, chain, rulespec...) } return err } // List rules in specified table/chain func (ipt *IPTables) List(table, chain string) ([]string, error) { args := []string{"-t", table, "-S", chain} return ipt.executeList(args) } // List rules (with counters) in specified table/chain func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) { args := []string{"-t", table, "-v", "-S", chain} return ipt.executeList(args) } // ListChains returns a slice containing the name of each chain in the specified table. func (ipt *IPTables) ListChains(table string) ([]string, error) { args := []string{"-t", table, "-S"} result, err := ipt.executeList(args) if err != nil { return nil, err } // Iterate over rules to find all default (-P) and user-specified (-N) chains. // Chains definition always come before rules. // Format is the following: // -P OUTPUT ACCEPT // -N Custom var chains []string for _, val := range result { if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") { chains = append(chains, strings.Fields(val)[1]) } else { break } } return chains, nil } // '-S' is fine with non existing rule index as long as the chain exists // therefore pass index 1 to reduce overhead for large chains func (ipt *IPTables) ChainExists(table, chain string) (bool, error) { err := ipt.run("-t", table, "-S", chain, "1") eerr, eok := err.(*Error) switch { case err == nil: return true, nil case eok && eerr.ExitStatus() == 1: return false, nil default: return false, err } } // Stats lists rules including the byte and packet counts func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} lines, err := ipt.executeList(args) if err != nil { return nil, err } appendSubnet := func(addr string) string { if strings.IndexByte(addr, byte('/')) < 0 { if strings.IndexByte(addr, '.') < 0 { return addr + "/128" } return addr + "/32" } return addr } ipv6 := ipt.proto == ProtocolIPv6 rows := [][]string{} for i, line := range lines { // Skip over chain name and field header if i < 2 { continue } // Fields: // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options line = strings.TrimSpace(line) fields := strings.Fields(line) // The ip6tables verbose output cannot be naively split due to the default "opt" // field containing 2 single spaces. if ipv6 { // Check if field 6 is "opt" or "source" address dest := fields[6] ip, _, _ := net.ParseCIDR(dest) if ip == nil { ip = net.ParseIP(dest) } // If we detected a CIDR or IP, the "opt" field is empty.. insert it. if ip != nil { f := []string{} f = append(f, fields[:4]...) f = append(f, " ") // Empty "opt" field for ip6tables f = append(f, fields[4:]...) fields = f } } // Adjust "source" and "destination" to include netmask, to match regular // List output fields[7] = appendSubnet(fields[7]) fields[8] = appendSubnet(fields[8]) // Combine "options" fields 9... into a single space-delimited field. options := fields[9:] fields = fields[:9] fields = append(fields, strings.Join(options, " ")) rows = append(rows, fields) } return rows, nil } // ParseStat parses a single statistic row into a Stat struct. The input should // be a string slice that is returned from calling the Stat method. func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) { // For forward-compatibility, expect at least 10 fields in the stat if len(stat) < 10 { return parsed, fmt.Errorf("stat contained fewer fields than expected") } // Convert the fields that are not plain strings parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64) if err != nil { return parsed, fmt.Errorf(err.Error(), "could not parse packets") } parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64) if err != nil { return parsed, fmt.Errorf(err.Error(), "could not parse bytes") } _, parsed.Source, err = net.ParseCIDR(stat[7]) if err != nil { return parsed, fmt.Errorf(err.Error(), "could not parse source") } _, parsed.Destination, err = net.ParseCIDR(stat[8]) if err != nil { return parsed, fmt.Errorf(err.Error(), "could not parse destination") } // Put the fields that are strings parsed.Target = stat[2] parsed.Protocol = stat[3] parsed.Opt = stat[4] parsed.Input = stat[5] parsed.Output = stat[6] parsed.Options = stat[9] return parsed, nil } // StructuredStats returns statistics as structured data which may be further // parsed and marshaled. func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) { rawStats, err := ipt.Stats(table, chain) if err != nil { return nil, err } structStats := []Stat{} for _, rawStat := range rawStats { stat, err := ipt.ParseStat(rawStat) if err != nil { return nil, err } structStats = append(structStats, stat) } return structStats, nil } func (ipt *IPTables) executeList(args []string) ([]string, error) { var stdout bytes.Buffer if err := ipt.runWithOutput(args, &stdout); err != nil { return nil, err } rules := strings.Split(stdout.String(), "\n") // strip trailing newline if len(rules) > 0 && rules[len(rules)-1] == "" { rules = rules[:len(rules)-1] } for i, rule := range rules { rules[i] = filterRuleOutput(rule) } return rules, nil } // NewChain creates a new chain in the specified table. // If the chain already exists, it will result in an error. func (ipt *IPTables) NewChain(table, chain string) error { return ipt.run("-t", table, "-N", chain) } const existsErr = 1 // ClearChain flushed (deletes all rules) in the specified table/chain. // If the chain does not exist, a new one will be created func (ipt *IPTables) ClearChain(table, chain string) error { err := ipt.NewChain(table, chain) eerr, eok := err.(*Error) switch { case err == nil: return nil case eok && eerr.ExitStatus() == existsErr: // chain already exists. Flush (clear) it. return ipt.run("-t", table, "-F", chain) default: return err } } // RenameChain renames the old chain to the new one. func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error { return ipt.run("-t", table, "-E", oldChain, newChain) } // DeleteChain deletes the chain in the specified table. // The chain must be empty func (ipt *IPTables) DeleteChain(table, chain string) error { return ipt.run("-t", table, "-X", chain) } func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error { exists, err := ipt.ChainExists(table, chain) if err != nil || !exists { return err } err = ipt.run("-t", table, "-F", chain) if err == nil { err = ipt.run("-t", table, "-X", chain) } return err } func (ipt *IPTables) ClearAll() error { return ipt.run("-F") } func (ipt *IPTables) DeleteAll() error { return ipt.run("-X") } // ChangePolicy changes policy on chain to target func (ipt *IPTables) ChangePolicy(table, chain, target string) error { return ipt.run("-t", table, "-P", chain, target) } // Check if the underlying iptables command supports the --random-fully flag func (ipt *IPTables) HasRandomFully() bool { return ipt.hasRandomFully } // Return version components of the underlying iptables command func (ipt *IPTables) GetIptablesVersion() (int, int, int) { return ipt.v1, ipt.v2, ipt.v3 } // run runs an iptables command with the given arguments, ignoring // any stdout output func (ipt *IPTables) run(args ...string) error { return ipt.runWithOutput(args, nil) } // runWithOutput runs an iptables command with the given arguments, // writing any stdout output to the given writer func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { args = append([]string{ipt.path}, args...) if ipt.hasWait { args = append(args, "--wait") if ipt.timeout != 0 && ipt.waitSupportSecond { args = append(args, strconv.Itoa(ipt.timeout)) } } else { fmu, err := newXtablesFileLock() if err != nil { return err } ul, err := fmu.tryLock() if err != nil { syscall.Close(fmu.fd) return err } defer ul.Unlock() } var stderr bytes.Buffer cmd := exec.Cmd{ Path: ipt.path, Args: args, Stdout: stdout, Stderr: &stderr, } if err := cmd.Run(); err != nil { switch e := err.(type) { case *exec.ExitError: return &Error{*e, cmd, stderr.String(), nil} default: return err } } return nil } // getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables". func getIptablesCommand(proto Protocol) string { if proto == ProtocolIPv6 { return "ip6tables" } else { return "iptables" } } // Checks if iptables has the "-C" and "--wait" flag func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool, bool) { return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesWaitSupportSecond(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) } // getIptablesVersion returns the first three components of the iptables version // and the operating mode (e.g. nf_tables or legacy) // e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) func extractIptablesVersion(str string) (int, int, int, string, error) { versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) result := versionMatcher.FindStringSubmatch(str) if result == nil { return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) } v1, err := strconv.Atoi(result[1]) if err != nil { return 0, 0, 0, "", err } v2, err := strconv.Atoi(result[2]) if err != nil { return 0, 0, 0, "", err } v3, err := strconv.Atoi(result[3]) if err != nil { return 0, 0, 0, "", err } mode := "legacy" if result[4] != "" { mode = result[4] } return v1, v2, v3, mode, nil } // Runs "iptables --version" to get the version string func getIptablesVersionString(path string) (string, error) { cmd := exec.Command(path, "--version") var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { return "", err } return out.String(), nil } // Checks if an iptables version is after 1.4.11, when --check was added func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool { if v1 > 1 { return true } if v1 == 1 && v2 > 4 { return true } if v1 == 1 && v2 == 4 && v3 >= 11 { return true } return false } // Checks if an iptables version is after 1.4.20, when --wait was added func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { if v1 > 1 { return true } if v1 == 1 && v2 > 4 { return true } if v1 == 1 && v2 == 4 && v3 >= 20 { return true } return false } //Checks if an iptablse version is after 1.6.0, when --wait support second func iptablesWaitSupportSecond(v1 int, v2 int, v3 int) bool { if v1 > 1 { return true } if v1 == 1 && v2 >= 6 { return true } return false } // Checks if an iptables version is after 1.6.2, when --random-fully was added func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { if v1 > 1 { return true } if v1 == 1 && v2 > 6 { return true } if v1 == 1 && v2 == 6 && v3 >= 2 { return true } return false } // Checks if a rule specification exists for a table func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") args := []string{"-t", table, "-S"} var stdout bytes.Buffer err := ipt.runWithOutput(args, &stdout) if err != nil { return false, err } return strings.Contains(stdout.String(), rs), nil } // counterRegex is the regex used to detect nftables counter format var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) // filterRuleOutput works around some inconsistencies in output. // For example, when iptables is in legacy vs. nftables mode, it produces // different results. func filterRuleOutput(rule string) string { out := rule // work around an output difference in nftables mode where counters // are output in iptables-save format, rather than iptables -S format // The string begins with "[0:0]" // // Fixes #49 if groups := counterRegex.FindStringSubmatch(out); groups != nil { // drop the brackets out = out[len(groups[0]):] out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) } return out } ================================================ FILE: vendor/github.com/coreos/go-iptables/iptables/lock.go ================================================ // Copyright 2015 CoreOS, Inc. // // 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 iptables import ( "os" "sync" "syscall" ) const ( // In earlier versions of iptables, the xtables lock was implemented // via a Unix socket, but now flock is used via this lockfile: // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 // Note the LSB-conforming "/run" directory does not exist on old // distributions, so assume "/var" is symlinked xtablesLockFilePath = "/var/run/xtables.lock" defaultFilePerm = 0600 ) type Unlocker interface { Unlock() error } type nopUnlocker struct{} func (_ nopUnlocker) Unlock() error { return nil } type fileLock struct { // mu is used to protect against concurrent invocations from within this process mu sync.Mutex fd int } // tryLock takes an exclusive lock on the xtables lock file without blocking. // This is best-effort only: if the exclusive lock would block (i.e. because // another process already holds it), no error is returned. Otherwise, any // error encountered during the locking operation is returned. // The returned Unlocker should be used to release the lock when the caller is // done invoking iptables commands. func (l *fileLock) tryLock() (Unlocker, error) { l.mu.Lock() err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) switch err { case syscall.EWOULDBLOCK: l.mu.Unlock() return nopUnlocker{}, nil case nil: return l, nil default: l.mu.Unlock() return nil, err } } // Unlock closes the underlying file, which implicitly unlocks it as well. It // also unlocks the associated mutex. func (l *fileLock) Unlock() error { defer l.mu.Unlock() return syscall.Close(l.fd) } // newXtablesFileLock opens a new lock on the xtables lockfile without // acquiring the lock func newXtablesFileLock() (*fileLock, error) { fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) if err != nil { return nil, err } return &fileLock{fd: fd}, nil } ================================================ FILE: vendor/github.com/fsnotify/fsnotify/.editorconfig ================================================ root = true [*.go] indent_style = tab indent_size = 4 insert_final_newline = true [*.{yml,yaml}] indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: vendor/github.com/fsnotify/fsnotify/.gitattributes ================================================ go.sum linguist-generated ================================================ FILE: vendor/github.com/fsnotify/fsnotify/.gitignore ================================================ # 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 ================================================ FILE: vendor/github.com/fsnotify/fsnotify/.travis.yml ================================================ sudo: false language: go go: - "stable" - "1.11.x" - "1.10.x" - "1.9.x" matrix: include: - go: "stable" env: GOLINT=true allow_failures: - go: tip fast_finish: true before_install: - if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi script: - go test --race ./... after_script: - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi - go vet ./... os: - linux - osx - windows notifications: email: false ================================================ FILE: vendor/github.com/fsnotify/fsnotify/AUTHORS ================================================ # 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 铁哥 ================================================ FILE: vendor/github.com/fsnotify/fsnotify/CHANGELOG.md ================================================ # 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 ================================================ FILE: vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md ================================================ # 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 ================================================ FILE: vendor/github.com/fsnotify/fsnotify/LICENSE ================================================ Copyright (c) 2012 The Go Authors. All rights reserved. Copyright (c) 2012-2019 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. ================================================ FILE: vendor/github.com/fsnotify/fsnotify/README.md ================================================ # 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://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | | 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+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) | | 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`. ## Usage ```go package main import ( "log" "github.com/fsnotify/fsnotify" ) func main() { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() done := make(chan bool) go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } log.Println("event:", event) if event.Op&fsnotify.Write == fsnotify.Write { log.Println("modified file:", event.Name) } case err, ok := <-watcher.Errors: if !ok { return } log.Println("error:", err) } } }() err = watcher.Add("/tmp/foo") if err != nil { log.Fatal(err) } <-done } ``` ## 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. **Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. [#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) ================================================ FILE: vendor/github.com/fsnotify/fsnotify/fen.go ================================================ // 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 } ================================================ FILE: vendor/github.com/fsnotify/fsnotify/fsnotify.go ================================================ // 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") ) ================================================ FILE: vendor/github.com/fsnotify/fsnotify/go.mod ================================================ module github.com/fsnotify/fsnotify go 1.13 require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 ================================================ FILE: vendor/github.com/fsnotify/fsnotify/go.sum ================================================ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ================================================ FILE: vendor/github.com/fsnotify/fsnotify/inotify.go ================================================ // 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 } ================================================ FILE: vendor/github.com/fsnotify/fsnotify/inotify_poller.go ================================================ // 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(unix.EPOLL_CLOEXEC) 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|unix.O_CLOEXEC) 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) } } ================================================ FILE: vendor/github.com/fsnotify/fsnotify/kqueue.go ================================================ // 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()) } ================================================ FILE: vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go ================================================ // 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 | unix.O_CLOEXEC ================================================ FILE: vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go ================================================ // 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 | unix.O_CLOEXEC ================================================ FILE: vendor/github.com/fsnotify/fsnotify/windows.go ================================================ // 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 } ================================================ FILE: vendor/github.com/nxadm/tail/.gitignore ================================================ .idea/ .test/ examples/_* ================================================ FILE: vendor/github.com/nxadm/tail/CHANGES.md ================================================ # Version v1.4.7-v1.4.8 * Documentation updates. * Small linter cleanups. * Added example in test. # Version v1.4.6 * Document the usage of Cleanup when re-reading a file (thanks to @lesovsky) for issue #18. * Add example directories with example and tests for issues. # Version v1.4.4-v1.4.5 * Fix of checksum problem because of forced tag. No changes to the code. # Version v1.4.1 * Incorporated PR 162 by by Mohammed902: "Simplify non-Windows build tag". # Version v1.4.0 * Incorporated PR 9 by mschneider82: "Added seekinfo to Tail". # Version v1.3.1 * Incorporated PR 7: "Fix deadlock when stopping on non-empty file/buffer", fixes upstream issue 93. # Version v1.3.0 * Incorporated changes of unmerged upstream PR 149 by mezzi: "added line num to Line struct". # Version v1.2.1 * Incorporated changes of unmerged upstream PR 128 by jadekler: "Compile-able code in readme". * Incorporated changes of unmerged upstream PR 130 by fgeller: "small change to comment wording". * Incorporated changes of unmerged upstream PR 133 by sm3142: "removed spurious newlines from log messages". # Version v1.2.0 * Incorporated changes of unmerged upstream PR 126 by Code-Hex: "Solved the problem for never return the last line if it's not followed by a newline". * Incorporated changes of unmerged upstream PR 131 by StoicPerlman: "Remove deprecated os.SEEK consts". The changes bumped the minimal supported Go release to 1.9. # Version v1.1.0 * migration to go modules. * release of master branch of the dormant upstream, because it contains fixes and improvement no present in the tagged release. ================================================ FILE: vendor/github.com/nxadm/tail/Dockerfile ================================================ FROM golang RUN mkdir -p $GOPATH/src/github.com/nxadm/tail/ ADD . $GOPATH/src/github.com/nxadm/tail/ # expecting to fetch dependencies successfully. RUN go get -v github.com/nxadm/tail # expecting to run the test successfully. RUN go test -v github.com/nxadm/tail # expecting to install successfully RUN go install -v github.com/nxadm/tail RUN go install -v github.com/nxadm/tail/cmd/gotail RUN $GOPATH/bin/gotail -h || true ENV PATH $GOPATH/bin:$PATH CMD ["gotail"] ================================================ FILE: vendor/github.com/nxadm/tail/LICENSE ================================================ # The MIT License (MIT) # © Copyright 2015 Hewlett Packard Enterprise Development LP Copyright (c) 2014 ActiveState 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. ================================================ FILE: vendor/github.com/nxadm/tail/README.md ================================================ ![ci](https://github.com/nxadm/tail/workflows/ci/badge.svg)[![Go Reference](https://pkg.go.dev/badge/github.com/nxadm/tail.svg)](https://pkg.go.dev/github.com/nxadm/tail) # tail functionality in Go nxadm/tail provides a Go library that emulates the features of the BSD `tail` program. The library comes with full support for truncation/move detection as it is designed to work with log rotation tools. The library works on all operating systems supported by Go, including POSIX systems like Linux and *BSD, and MS Windows. Go 1.9 is the oldest compiler release supported. A simple example: ```Go // Create a tail t, err := tail.TailFile( "/var/log/nginx.log", tail.Config{Follow: true, ReOpen: true}) if err != nil { panic(err) } // Print the text of each received line for line := range t.Lines { fmt.Println(line.Text) } ``` See [API documentation](https://pkg.go.dev/github.com/nxadm/tail). ## Installing go get github.com/nxadm/tail/... ## History This project is an active, drop-in replacement for the [abandoned](https://en.wikipedia.org/wiki/HPE_Helion) Go tail library at [hpcloud](https://github.com/hpcloud/tail). Next to [addressing open issues/PRs of the original project](https://github.com/nxadm/tail/issues/6), nxadm/tail continues the development by keeping up to date with the Go toolchain (e.g. go modules) and dependencies, completing the documentation, adding features and fixing bugs. ## Examples Examples, e.g. used to debug an issue, are kept in the [examples directory](/examples). ================================================ FILE: vendor/github.com/nxadm/tail/go.mod ================================================ module github.com/nxadm/tail go 1.13 require ( github.com/fsnotify/fsnotify v1.4.9 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ) ================================================ FILE: vendor/github.com/nxadm/tail/go.sum ================================================ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= ================================================ FILE: vendor/github.com/nxadm/tail/ratelimiter/Licence ================================================ Copyright (C) 2013 99designs 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. ================================================ FILE: vendor/github.com/nxadm/tail/ratelimiter/leakybucket.go ================================================ // Package ratelimiter implements the Leaky Bucket ratelimiting algorithm with memcached and in-memory backends. package ratelimiter import ( "time" ) type LeakyBucket struct { Size uint16 Fill float64 LeakInterval time.Duration // time.Duration for 1 unit of size to leak Lastupdate time.Time Now func() time.Time } func NewLeakyBucket(size uint16, leakInterval time.Duration) *LeakyBucket { bucket := LeakyBucket{ Size: size, Fill: 0, LeakInterval: leakInterval, Now: time.Now, Lastupdate: time.Now(), } return &bucket } func (b *LeakyBucket) updateFill() { now := b.Now() if b.Fill > 0 { elapsed := now.Sub(b.Lastupdate) b.Fill -= float64(elapsed) / float64(b.LeakInterval) if b.Fill < 0 { b.Fill = 0 } } b.Lastupdate = now } func (b *LeakyBucket) Pour(amount uint16) bool { b.updateFill() var newfill float64 = b.Fill + float64(amount) if newfill > float64(b.Size) { return false } b.Fill = newfill return true } // The time at which this bucket will be completely drained func (b *LeakyBucket) DrainedAt() time.Time { return b.Lastupdate.Add(time.Duration(b.Fill * float64(b.LeakInterval))) } // The duration until this bucket is completely drained func (b *LeakyBucket) TimeToDrain() time.Duration { return b.DrainedAt().Sub(b.Now()) } func (b *LeakyBucket) TimeSinceLastUpdate() time.Duration { return b.Now().Sub(b.Lastupdate) } type LeakyBucketSer struct { Size uint16 Fill float64 LeakInterval time.Duration // time.Duration for 1 unit of size to leak Lastupdate time.Time } func (b *LeakyBucket) Serialise() *LeakyBucketSer { bucket := LeakyBucketSer{ Size: b.Size, Fill: b.Fill, LeakInterval: b.LeakInterval, Lastupdate: b.Lastupdate, } return &bucket } func (b *LeakyBucketSer) DeSerialise() *LeakyBucket { bucket := LeakyBucket{ Size: b.Size, Fill: b.Fill, LeakInterval: b.LeakInterval, Lastupdate: b.Lastupdate, Now: time.Now, } return &bucket } ================================================ FILE: vendor/github.com/nxadm/tail/ratelimiter/memory.go ================================================ package ratelimiter import ( "errors" "time" ) const ( GC_SIZE int = 100 GC_PERIOD time.Duration = 60 * time.Second ) type Memory struct { store map[string]LeakyBucket lastGCCollected time.Time } func NewMemory() *Memory { m := new(Memory) m.store = make(map[string]LeakyBucket) m.lastGCCollected = time.Now() return m } func (m *Memory) GetBucketFor(key string) (*LeakyBucket, error) { bucket, ok := m.store[key] if !ok { return nil, errors.New("miss") } return &bucket, nil } func (m *Memory) SetBucketFor(key string, bucket LeakyBucket) error { if len(m.store) > GC_SIZE { m.GarbageCollect() } m.store[key] = bucket return nil } func (m *Memory) GarbageCollect() { now := time.Now() // rate limit GC to once per minute if now.Unix() >= m.lastGCCollected.Add(GC_PERIOD).Unix() { for key, bucket := range m.store { // if the bucket is drained, then GC if bucket.DrainedAt().Unix() < now.Unix() { delete(m.store, key) } } m.lastGCCollected = now } } ================================================ FILE: vendor/github.com/nxadm/tail/ratelimiter/storage.go ================================================ package ratelimiter type Storage interface { GetBucketFor(string) (*LeakyBucket, error) SetBucketFor(string, LeakyBucket) error } ================================================ FILE: vendor/github.com/nxadm/tail/tail.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. //nxadm/tail provides a Go library that emulates the features of the BSD `tail` //program. The library comes with full support for truncation/move detection as //it is designed to work with log rotation tools. The library works on all //operating systems supported by Go, including POSIX systems like Linux and //*BSD, and MS Windows. Go 1.9 is the oldest compiler release supported. package tail import ( "bufio" "errors" "fmt" "io" "io/ioutil" "log" "os" "strings" "sync" "time" "github.com/nxadm/tail/ratelimiter" "github.com/nxadm/tail/util" "github.com/nxadm/tail/watch" "gopkg.in/tomb.v1" ) var ( // ErrStop is returned when the tail of a file has been marked to be stopped. ErrStop = errors.New("tail should now stop") ) type Line struct { Text string // The contents of the file Num int // The line number SeekInfo SeekInfo // SeekInfo Time time.Time // Present time Err error // Error from tail } // Deprecated: this function is no longer used internally and it has little of no // use in the API. As such, it will be removed from the API in a future major // release. // // NewLine returns a * pointer to a Line struct. func NewLine(text string, lineNum int) *Line { return &Line{text, lineNum, SeekInfo{}, time.Now(), nil} } // SeekInfo represents arguments to io.Seek. See: https://golang.org/pkg/io/#SectionReader.Seek type SeekInfo struct { Offset int64 Whence int } type logger interface { Fatal(v ...interface{}) Fatalf(format string, v ...interface{}) Fatalln(v ...interface{}) Panic(v ...interface{}) Panicf(format string, v ...interface{}) Panicln(v ...interface{}) Print(v ...interface{}) Printf(format string, v ...interface{}) Println(v ...interface{}) } // Config is used to specify how a file must be tailed. type Config struct { // File-specifc Location *SeekInfo // Tail from this location. If nil, start at the beginning of the file ReOpen bool // Reopen recreated files (tail -F) MustExist bool // Fail early if the file does not exist Poll bool // Poll for file changes instead of using the default inotify Pipe bool // The file is a named pipe (mkfifo) // Generic IO Follow bool // Continue looking for new lines (tail -f) MaxLineSize int // If non-zero, split longer lines into multiple lines // Optionally, use a ratelimiter (e.g. created by the ratelimiter/NewLeakyBucket function) RateLimiter *ratelimiter.LeakyBucket // Optionally use a Logger. When nil, the Logger is set to tail.DefaultLogger. // To disable logging, set it to tail.DiscardingLogger Logger logger } type Tail struct { Filename string // The filename Lines chan *Line // A consumable channel of *Line Config // Tail.Configuration file *os.File reader *bufio.Reader lineNum int watcher watch.FileWatcher changes *watch.FileChanges tomb.Tomb // provides: Done, Kill, Dying lk sync.Mutex } var ( // DefaultLogger logs to os.Stderr and it is used when Config.Logger == nil DefaultLogger = log.New(os.Stderr, "", log.LstdFlags) // DiscardingLogger can be used to disable logging output DiscardingLogger = log.New(ioutil.Discard, "", 0) ) // TailFile begins tailing the file. And returns a pointer to a Tail struct // and an error. An output stream is made available via the Tail.Lines // channel (e.g. to be looped and printed). To handle errors during tailing, // after finishing reading from the Lines channel, invoke the `Wait` or `Err` // method on the returned *Tail. func TailFile(filename string, config Config) (*Tail, error) { if config.ReOpen && !config.Follow { util.Fatal("cannot set ReOpen without Follow.") } t := &Tail{ Filename: filename, Lines: make(chan *Line), Config: config, } // when Logger was not specified in config, use default logger if t.Logger == nil { t.Logger = DefaultLogger } if t.Poll { t.watcher = watch.NewPollingFileWatcher(filename) } else { t.watcher = watch.NewInotifyFileWatcher(filename) } if t.MustExist { var err error t.file, err = OpenFile(t.Filename) if err != nil { return nil, err } } go t.tailFileSync() return t, nil } // Tell returns the file's current position, like stdio's ftell() and an error. // Beware that this value may not be completely accurate because one line from // the chan(tail.Lines) may have been read already. func (tail *Tail) Tell() (offset int64, err error) { if tail.file == nil { return } offset, err = tail.file.Seek(0, io.SeekCurrent) if err != nil { return } tail.lk.Lock() defer tail.lk.Unlock() if tail.reader == nil { return } offset -= int64(tail.reader.Buffered()) return } // Stop stops the tailing activity. func (tail *Tail) Stop() error { tail.Kill(nil) return tail.Wait() } // StopAtEOF stops tailing as soon as the end of the file is reached. The function // returns an error, func (tail *Tail) StopAtEOF() error { tail.Kill(errStopAtEOF) return tail.Wait() } var errStopAtEOF = errors.New("tail: stop at eof") func (tail *Tail) close() { close(tail.Lines) tail.closeFile() } func (tail *Tail) closeFile() { if tail.file != nil { tail.file.Close() tail.file = nil } } func (tail *Tail) reopen() error { tail.closeFile() tail.lineNum = 0 for { var err error tail.file, err = OpenFile(tail.Filename) if err != nil { if os.IsNotExist(err) { tail.Logger.Printf("Waiting for %s to appear...", tail.Filename) if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil { if err == tomb.ErrDying { return err } return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err) } continue } return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err) } break } return nil } func (tail *Tail) readLine() (string, error) { tail.lk.Lock() line, err := tail.reader.ReadString('\n') tail.lk.Unlock() if err != nil { // Note ReadString "returns the data read before the error" in // case of an error, including EOF, so we return it as is. The // caller is expected to process it if err is EOF. return line, err } line = strings.TrimRight(line, "\n") return line, err } func (tail *Tail) tailFileSync() { defer tail.Done() defer tail.close() if !tail.MustExist { // deferred first open. err := tail.reopen() if err != nil { if err != tomb.ErrDying { tail.Kill(err) } return } } // Seek to requested location on first open of the file. if tail.Location != nil { _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence) if err != nil { tail.Killf("Seek error on %s: %s", tail.Filename, err) return } } tail.openReader() // Read line by line. for { // do not seek in named pipes if !tail.Pipe { // grab the position in case we need to back up in the event of a half-line if _, err := tail.Tell(); err != nil { tail.Kill(err) return } } line, err := tail.readLine() // Process `line` even if err is EOF. if err == nil { cooloff := !tail.sendLine(line) if cooloff { // Wait a second before seeking till the end of // file when rate limit is reached. msg := ("Too much log activity; waiting a second before resuming tailing") offset, _ := tail.Tell() tail.Lines <- &Line{msg, tail.lineNum, SeekInfo{Offset: offset}, time.Now(), errors.New(msg)} select { case <-time.After(time.Second): case <-tail.Dying(): return } if err := tail.seekEnd(); err != nil { tail.Kill(err) return } } } else if err == io.EOF { if !tail.Follow { if line != "" { tail.sendLine(line) } return } if tail.Follow && line != "" { tail.sendLine(line) if err := tail.seekEnd(); err != nil { tail.Kill(err) return } } // When EOF is reached, wait for more data to become // available. Wait strategy is based on the `tail.watcher` // implementation (inotify or polling). err := tail.waitForChanges() if err != nil { if err != ErrStop { tail.Kill(err) } return } } else { // non-EOF error tail.Killf("Error reading %s: %s", tail.Filename, err) return } select { case <-tail.Dying(): if tail.Err() == errStopAtEOF { continue } return default: } } } // waitForChanges waits until the file has been appended, deleted, // moved or truncated. When moved or deleted - the file will be // reopened if ReOpen is true. Truncated files are always reopened. func (tail *Tail) waitForChanges() error { if tail.changes == nil { pos, err := tail.file.Seek(0, io.SeekCurrent) if err != nil { return err } tail.changes, err = tail.watcher.ChangeEvents(&tail.Tomb, pos) if err != nil { return err } } select { case <-tail.changes.Modified: return nil case <-tail.changes.Deleted: tail.changes = nil if tail.ReOpen { // XXX: we must not log from a library. tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename) if err := tail.reopen(); err != nil { return err } tail.Logger.Printf("Successfully reopened %s", tail.Filename) tail.openReader() return nil } tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename) return ErrStop case <-tail.changes.Truncated: // Always reopen truncated files (Follow is true) tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename) if err := tail.reopen(); err != nil { return err } tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename) tail.openReader() return nil case <-tail.Dying(): return ErrStop } } func (tail *Tail) openReader() { tail.lk.Lock() if tail.MaxLineSize > 0 { // add 2 to account for newline characters tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2) } else { tail.reader = bufio.NewReader(tail.file) } tail.lk.Unlock() } func (tail *Tail) seekEnd() error { return tail.seekTo(SeekInfo{Offset: 0, Whence: io.SeekEnd}) } func (tail *Tail) seekTo(pos SeekInfo) error { _, err := tail.file.Seek(pos.Offset, pos.Whence) if err != nil { return fmt.Errorf("Seek error on %s: %s", tail.Filename, err) } // Reset the read buffer whenever the file is re-seek'ed tail.reader.Reset(tail.file) return nil } // sendLine sends the line(s) to Lines channel, splitting longer lines // if necessary. Return false if rate limit is reached. func (tail *Tail) sendLine(line string) bool { now := time.Now() lines := []string{line} // Split longer lines if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize { lines = util.PartitionString(line, tail.MaxLineSize) } for _, line := range lines { tail.lineNum++ offset, _ := tail.Tell() select { case tail.Lines <- &Line{line, tail.lineNum, SeekInfo{Offset: offset}, now, nil}: case <-tail.Dying(): return true } } if tail.Config.RateLimiter != nil { ok := tail.Config.RateLimiter.Pour(uint16(len(lines))) if !ok { tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.", tail.Filename) return false } } return true } // Cleanup removes inotify watches added by the tail package. This function is // meant to be invoked from a process's exit handler. Linux kernel may not // automatically remove inotify watches after the process exits. // If you plan to re-read a file, don't call Cleanup in between. func (tail *Tail) Cleanup() { watch.Cleanup(tail.Filename) } ================================================ FILE: vendor/github.com/nxadm/tail/tail_posix.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // +build !windows package tail import ( "os" ) // Deprecated: this function is only useful internally and, as such, // it will be removed from the API in a future major release. // // OpenFile proxies a os.Open call for a file so it can be correctly tailed // on POSIX and non-POSIX OSes like MS Windows. func OpenFile(name string) (file *os.File, err error) { return os.Open(name) } ================================================ FILE: vendor/github.com/nxadm/tail/tail_windows.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // +build windows package tail import ( "os" "github.com/nxadm/tail/winfile" ) // Deprecated: this function is only useful internally and, as such, // it will be removed from the API in a future major release. // // OpenFile proxies a os.Open call for a file so it can be correctly tailed // on POSIX and non-POSIX OSes like MS Windows. func OpenFile(name string) (file *os.File, err error) { return winfile.OpenFile(name, os.O_RDONLY, 0) } ================================================ FILE: vendor/github.com/nxadm/tail/util/util.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. package util import ( "fmt" "log" "os" "runtime/debug" ) type Logger struct { *log.Logger } var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)} // fatal is like panic except it displays only the current goroutine's stack. func Fatal(format string, v ...interface{}) { // https://github.com/nxadm/log/blob/master/log.go#L45 LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack())) os.Exit(1) } // partitionString partitions the string into chunks of given size, // with the last chunk of variable size. func PartitionString(s string, chunkSize int) []string { if chunkSize <= 0 { panic("invalid chunkSize") } length := len(s) chunks := 1 + length/chunkSize start := 0 end := chunkSize parts := make([]string, 0, chunks) for { if end > length { end = length } parts = append(parts, s[start:end]) if end == length { break } start, end = end, end+chunkSize } return parts } ================================================ FILE: vendor/github.com/nxadm/tail/watch/filechanges.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail package watch type FileChanges struct { Modified chan bool // Channel to get notified of modifications Truncated chan bool // Channel to get notified of truncations Deleted chan bool // Channel to get notified of deletions/renames } func NewFileChanges() *FileChanges { return &FileChanges{ make(chan bool, 1), make(chan bool, 1), make(chan bool, 1)} } func (fc *FileChanges) NotifyModified() { sendOnlyIfEmpty(fc.Modified) } func (fc *FileChanges) NotifyTruncated() { sendOnlyIfEmpty(fc.Truncated) } func (fc *FileChanges) NotifyDeleted() { sendOnlyIfEmpty(fc.Deleted) } // sendOnlyIfEmpty sends on a bool channel only if the channel has no // backlog to be read by other goroutines. This concurrency pattern // can be used to notify other goroutines if and only if they are // looking for it (i.e., subsequent notifications can be compressed // into one). func sendOnlyIfEmpty(ch chan bool) { select { case ch <- true: default: } } ================================================ FILE: vendor/github.com/nxadm/tail/watch/inotify.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. package watch import ( "fmt" "os" "path/filepath" "github.com/nxadm/tail/util" "github.com/fsnotify/fsnotify" "gopkg.in/tomb.v1" ) // InotifyFileWatcher uses inotify to monitor file changes. type InotifyFileWatcher struct { Filename string Size int64 } func NewInotifyFileWatcher(filename string) *InotifyFileWatcher { fw := &InotifyFileWatcher{filepath.Clean(filename), 0} return fw } func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error { err := WatchCreate(fw.Filename) if err != nil { return err } defer RemoveWatchCreate(fw.Filename) // Do a real check now as the file might have been created before // calling `WatchFlags` above. if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) { // file exists, or stat returned an error. return err } events := Events(fw.Filename) for { select { case evt, ok := <-events: if !ok { return fmt.Errorf("inotify watcher has been closed") } evtName, err := filepath.Abs(evt.Name) if err != nil { return err } fwFilename, err := filepath.Abs(fw.Filename) if err != nil { return err } if evtName == fwFilename { return nil } case <-t.Dying(): return tomb.ErrDying } } panic("unreachable") } func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { err := Watch(fw.Filename) if err != nil { return nil, err } changes := NewFileChanges() fw.Size = pos go func() { events := Events(fw.Filename) for { prevSize := fw.Size var evt fsnotify.Event var ok bool select { case evt, ok = <-events: if !ok { RemoveWatch(fw.Filename) return } case <-t.Dying(): RemoveWatch(fw.Filename) return } switch { case evt.Op&fsnotify.Remove == fsnotify.Remove: fallthrough case evt.Op&fsnotify.Rename == fsnotify.Rename: RemoveWatch(fw.Filename) changes.NotifyDeleted() return //With an open fd, unlink(fd) - inotify returns IN_ATTRIB (==fsnotify.Chmod) case evt.Op&fsnotify.Chmod == fsnotify.Chmod: fallthrough case evt.Op&fsnotify.Write == fsnotify.Write: fi, err := os.Stat(fw.Filename) if err != nil { if os.IsNotExist(err) { RemoveWatch(fw.Filename) changes.NotifyDeleted() return } // XXX: report this error back to the user util.Fatal("Failed to stat file %v: %v", fw.Filename, err) } fw.Size = fi.Size() if prevSize > 0 && prevSize > fw.Size { changes.NotifyTruncated() } else { changes.NotifyModified() } prevSize = fw.Size } } }() return changes, nil } ================================================ FILE: vendor/github.com/nxadm/tail/watch/inotify_tracker.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. package watch import ( "log" "os" "path/filepath" "sync" "syscall" "github.com/nxadm/tail/util" "github.com/fsnotify/fsnotify" ) type InotifyTracker struct { mux sync.Mutex watcher *fsnotify.Watcher chans map[string]chan fsnotify.Event done map[string]chan bool watchNums map[string]int watch chan *watchInfo remove chan *watchInfo error chan error } type watchInfo struct { op fsnotify.Op fname string } func (this *watchInfo) isCreate() bool { return this.op == fsnotify.Create } var ( // globally shared InotifyTracker; ensures only one fsnotify.Watcher is used shared *InotifyTracker // these are used to ensure the shared InotifyTracker is run exactly once once = sync.Once{} goRun = func() { shared = &InotifyTracker{ mux: sync.Mutex{}, chans: make(map[string]chan fsnotify.Event), done: make(map[string]chan bool), watchNums: make(map[string]int), watch: make(chan *watchInfo), remove: make(chan *watchInfo), error: make(chan error), } go shared.run() } logger = log.New(os.Stderr, "", log.LstdFlags) ) // Watch signals the run goroutine to begin watching the input filename func Watch(fname string) error { return watch(&watchInfo{ fname: fname, }) } // Watch create signals the run goroutine to begin watching the input filename // if call the WatchCreate function, don't call the Cleanup, call the RemoveWatchCreate func WatchCreate(fname string) error { return watch(&watchInfo{ op: fsnotify.Create, fname: fname, }) } func watch(winfo *watchInfo) error { // start running the shared InotifyTracker if not already running once.Do(goRun) winfo.fname = filepath.Clean(winfo.fname) shared.watch <- winfo return <-shared.error } // RemoveWatch signals the run goroutine to remove the watch for the input filename func RemoveWatch(fname string) error { return remove(&watchInfo{ fname: fname, }) } // RemoveWatch create signals the run goroutine to remove the watch for the input filename func RemoveWatchCreate(fname string) error { return remove(&watchInfo{ op: fsnotify.Create, fname: fname, }) } func remove(winfo *watchInfo) error { // start running the shared InotifyTracker if not already running once.Do(goRun) winfo.fname = filepath.Clean(winfo.fname) shared.mux.Lock() done := shared.done[winfo.fname] if done != nil { delete(shared.done, winfo.fname) close(done) } shared.mux.Unlock() shared.remove <- winfo return <-shared.error } // Events returns a channel to which FileEvents corresponding to the input filename // will be sent. This channel will be closed when removeWatch is called on this // filename. func Events(fname string) <-chan fsnotify.Event { shared.mux.Lock() defer shared.mux.Unlock() return shared.chans[fname] } // Cleanup removes the watch for the input filename if necessary. func Cleanup(fname string) error { return RemoveWatch(fname) } // watchFlags calls fsnotify.WatchFlags for the input filename and flags, creating // a new Watcher if the previous Watcher was closed. func (shared *InotifyTracker) addWatch(winfo *watchInfo) error { shared.mux.Lock() defer shared.mux.Unlock() if shared.chans[winfo.fname] == nil { shared.chans[winfo.fname] = make(chan fsnotify.Event) } if shared.done[winfo.fname] == nil { shared.done[winfo.fname] = make(chan bool) } fname := winfo.fname if winfo.isCreate() { // Watch for new files to be created in the parent directory. fname = filepath.Dir(fname) } var err error // already in inotify watch if shared.watchNums[fname] == 0 { err = shared.watcher.Add(fname) } if err == nil { shared.watchNums[fname]++ } return err } // removeWatch calls fsnotify.RemoveWatch for the input filename and closes the // corresponding events channel. func (shared *InotifyTracker) removeWatch(winfo *watchInfo) error { shared.mux.Lock() ch := shared.chans[winfo.fname] if ch != nil { delete(shared.chans, winfo.fname) close(ch) } fname := winfo.fname if winfo.isCreate() { // Watch for new files to be created in the parent directory. fname = filepath.Dir(fname) } shared.watchNums[fname]-- watchNum := shared.watchNums[fname] if watchNum == 0 { delete(shared.watchNums, fname) } shared.mux.Unlock() var err error // If we were the last ones to watch this file, unsubscribe from inotify. // This needs to happen after releasing the lock because fsnotify waits // synchronously for the kernel to acknowledge the removal of the watch // for this file, which causes us to deadlock if we still held the lock. if watchNum == 0 { err = shared.watcher.Remove(fname) } return err } // sendEvent sends the input event to the appropriate Tail. func (shared *InotifyTracker) sendEvent(event fsnotify.Event) { name := filepath.Clean(event.Name) shared.mux.Lock() ch := shared.chans[name] done := shared.done[name] shared.mux.Unlock() if ch != nil && done != nil { select { case ch <- event: case <-done: } } } // run starts the goroutine in which the shared struct reads events from its // Watcher's Event channel and sends the events to the appropriate Tail. func (shared *InotifyTracker) run() { watcher, err := fsnotify.NewWatcher() if err != nil { util.Fatal("failed to create Watcher") } shared.watcher = watcher for { select { case winfo := <-shared.watch: shared.error <- shared.addWatch(winfo) case winfo := <-shared.remove: shared.error <- shared.removeWatch(winfo) case event, open := <-shared.watcher.Events: if !open { return } shared.sendEvent(event) case err, open := <-shared.watcher.Errors: if !open { return } else if err != nil { sysErr, ok := err.(*os.SyscallError) if !ok || sysErr.Err != syscall.EINTR { logger.Printf("Error in Watcher Error channel: %s", err) } } } } } ================================================ FILE: vendor/github.com/nxadm/tail/watch/polling.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. package watch import ( "os" "runtime" "time" "github.com/nxadm/tail/util" "gopkg.in/tomb.v1" ) // PollingFileWatcher polls the file for changes. type PollingFileWatcher struct { Filename string Size int64 } func NewPollingFileWatcher(filename string) *PollingFileWatcher { fw := &PollingFileWatcher{filename, 0} return fw } var POLL_DURATION time.Duration func (fw *PollingFileWatcher) BlockUntilExists(t *tomb.Tomb) error { for { if _, err := os.Stat(fw.Filename); err == nil { return nil } else if !os.IsNotExist(err) { return err } select { case <-time.After(POLL_DURATION): continue case <-t.Dying(): return tomb.ErrDying } } panic("unreachable") } func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { origFi, err := os.Stat(fw.Filename) if err != nil { return nil, err } changes := NewFileChanges() var prevModTime time.Time // XXX: use tomb.Tomb to cleanly manage these goroutines. replace // the fatal (below) with tomb's Kill. fw.Size = pos go func() { prevSize := fw.Size for { select { case <-t.Dying(): return default: } time.Sleep(POLL_DURATION) fi, err := os.Stat(fw.Filename) if err != nil { // Windows cannot delete a file if a handle is still open (tail keeps one open) // so it gives access denied to anything trying to read it until all handles are released. if os.IsNotExist(err) || (runtime.GOOS == "windows" && os.IsPermission(err)) { // File does not exist (has been deleted). changes.NotifyDeleted() return } // XXX: report this error back to the user util.Fatal("Failed to stat file %v: %v", fw.Filename, err) } // File got moved/renamed? if !os.SameFile(origFi, fi) { changes.NotifyDeleted() return } // File got truncated? fw.Size = fi.Size() if prevSize > 0 && prevSize > fw.Size { changes.NotifyTruncated() prevSize = fw.Size continue } // File got bigger? if prevSize > 0 && prevSize < fw.Size { changes.NotifyModified() prevSize = fw.Size continue } prevSize = fw.Size // File was appended to (changed)? modTime := fi.ModTime() if modTime != prevModTime { prevModTime = modTime changes.NotifyModified() } } }() return changes, nil } func init() { POLL_DURATION = 250 * time.Millisecond } ================================================ FILE: vendor/github.com/nxadm/tail/watch/watch.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // Copyright (c) 2015 HPE Software Inc. All rights reserved. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. package watch import "gopkg.in/tomb.v1" // FileWatcher monitors file-level events. type FileWatcher interface { // BlockUntilExists blocks until the file comes into existence. BlockUntilExists(*tomb.Tomb) error // ChangeEvents reports on changes to a file, be it modification, // deletion, renames or truncations. Returned FileChanges group of // channels will be closed, thus become unusable, after a deletion // or truncation event. // In order to properly report truncations, ChangeEvents requires // the caller to pass their current offset in the file. ChangeEvents(*tomb.Tomb, int64) (*FileChanges, error) } ================================================ FILE: vendor/github.com/nxadm/tail/winfile/winfile.go ================================================ // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail // +build windows package winfile import ( "os" "syscall" "unsafe" ) // issue also described here //https://codereview.appspot.com/8203043/ // https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L218 func Open(path string, mode int, perm uint32) (fd syscall.Handle, err error) { if len(path) == 0 { return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND } pathp, err := syscall.UTF16PtrFromString(path) if err != nil { return syscall.InvalidHandle, err } var access uint32 switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { case syscall.O_RDONLY: access = syscall.GENERIC_READ case syscall.O_WRONLY: access = syscall.GENERIC_WRITE case syscall.O_RDWR: access = syscall.GENERIC_READ | syscall.GENERIC_WRITE } if mode&syscall.O_CREAT != 0 { access |= syscall.GENERIC_WRITE } if mode&syscall.O_APPEND != 0 { access &^= syscall.GENERIC_WRITE access |= syscall.FILE_APPEND_DATA } sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE) var sa *syscall.SecurityAttributes if mode&syscall.O_CLOEXEC == 0 { sa = makeInheritSa() } var createmode uint32 switch { case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): createmode = syscall.CREATE_NEW case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): createmode = syscall.CREATE_ALWAYS case mode&syscall.O_CREAT == syscall.O_CREAT: createmode = syscall.OPEN_ALWAYS case mode&syscall.O_TRUNC == syscall.O_TRUNC: createmode = syscall.TRUNCATE_EXISTING default: createmode = syscall.OPEN_EXISTING } h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0) return h, e } // https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L211 func makeInheritSa() *syscall.SecurityAttributes { var sa syscall.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 return &sa } // https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_windows.go#L133 func OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) { r, e := Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) if e != nil { return nil, e } return os.NewFile(uintptr(r), name), nil } // https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_posix.go#L61 func syscallMode(i os.FileMode) (o uint32) { o |= uint32(i.Perm()) if i&os.ModeSetuid != 0 { o |= syscall.S_ISUID } if i&os.ModeSetgid != 0 { o |= syscall.S_ISGID } if i&os.ModeSticky != 0 { o |= syscall.S_ISVTX } // No mapping for Go's ModeTemporary (plan9 only). return } ================================================ FILE: vendor/github.com/onsi/ginkgo/.gitignore ================================================ .DS_Store TODO tmp/**/* *.coverprofile .vscode .idea/ *.log ================================================ FILE: vendor/github.com/onsi/ginkgo/.travis.yml ================================================ language: go go: - tip - 1.16.x - 1.15.x cache: directories: - $GOPATH/pkg/mod # allow internal package imports, necessary for forked repositories go_import_path: github.com/onsi/ginkgo install: - GO111MODULE="off" go get -v -t ./... - GO111MODULE="off" go get golang.org/x/tools/cmd/cover - GO111MODULE="off" go get github.com/onsi/gomega - GO111MODULE="off" go install github.com/onsi/ginkgo/ginkgo - export PATH=$GOPATH/bin:$PATH script: - GO111MODULE="on" go mod tidy && git diff --exit-code go.mod go.sum - go vet - ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace ================================================ FILE: vendor/github.com/onsi/ginkgo/CHANGELOG.md ================================================ ## 1.16.5 Ginkgo 2.0 now has a Release Candidate. 1.16.5 advertises the existence of the RC. 1.16.5 deprecates GinkgoParallelNode in favor of GinkgoParallelProcess You can silence the RC advertisement by setting an `ACK_GINKG_RC=true` environment variable or creating a file in your home directory called `.ack-ginkgo-rc` ## 1.16.4 ### Fixes 1.16.4 retracts 1.16.3. There are no code changes. The 1.16.3 tag was associated with the wrong commit and an attempt to change it after-the-fact has proven problematic. 1.16.4 retracts 1.16.3 in Ginkgo's go.mod and creates a new, correctly tagged, release. ## 1.16.3 ### Features - Measure is now deprecated and emits a deprecation warning. ## 1.16.2 ### Fixes - Deprecations can be suppressed by setting an `ACK_GINKGO_DEPRECATIONS=` environment variable. ## 1.16.1 ### Fixes - Supress --stream deprecation warning on windows (#793) ## 1.16.0 ### Features - Advertise Ginkgo 2.0. Introduce deprecations. [9ef1913] - Update README.md to advertise that Ginkgo 2.0 is coming. - Backport the 2.0 DeprecationTracker and start alerting users about upcoming deprecations. - Add slim-sprig template functions to bootstrap/generate (#775) [9162b86] ### Fixes - Fix accidental reference to 1488 (#784) [9fb7fe4] ## 1.15.2 ### Fixes - ignore blank `-focus` and `-skip` flags (#780) [e90a4a0] ## 1.15.1 ### Fixes - reporters/junit: Use `system-out` element instead of `passed` (#769) [9eda305] ## 1.15.0 ### Features - Adds 'outline' command to print the outline of specs/containers in a file (#754) [071c369] [6803cc3] [935b538] [06744e8] [0c40583] - Add support for using template to generate tests (#752) [efb9e69] - Add a Chinese Doc #755 (#756) [5207632] - cli: allow multiple -focus and -skip flags (#736) [9a782fb] ### Fixes - Add _internal to filename of tests created with internal flag (#751) [43c12da] ## 1.14.2 ### Fixes - correct handling windows backslash in import path (#721) [97f3d51] - Add additional methods to GinkgoT() to improve compatibility with the testing.TB interface [b5fe44d] ## 1.14.1 ### Fixes - Discard exported method declaration when running ginkgo bootstrap (#558) [f4b0240] ## 1.14.0 ### Features - Defer running top-level container nodes until RunSpecs is called [d44dedf] - [Document Ginkgo lifecycle](http://onsi.github.io/ginkgo/#understanding-ginkgos-lifecycle) - Add `extensions/globals` package (#692) [3295c8f] - this can be helpful in contexts where you are test-driving your test-generation code (see [#692](https://github.com/onsi/ginkgo/pull/692)) - Print Skip reason in JUnit reporter if one was provided [820dfab] ## 1.13.0 ### Features - Add a version of table.Entry that allows dumping the entry parameters. (#689) [21eaef2] ### Fixes - Ensure integration tests pass in an environment sans GOPATH [606fba2] - Add books package (#568) [fc0e44e] - doc(readme): installation via "tools package" (#677) [83bb20e] - Solve the undefined: unix.Dup2 compile error on mips64le (#680) [0624f75] - Import package without dot (#687) [6321024] - Fix integration tests to stop require GOPATH (#686) [a912ec5] ## 1.12.3 ### Fixes - Print correct code location of failing table test (#666) [c6d7afb] ## 1.12.2 ### Fixes - Update dependencies [ea4a036] ## 1.12.1 ### Fixes - Make unfocus ("blur") much faster (#674) [8b18061] - Fix typo (#673) [7fdcbe8] - Test against 1.14 and remove 1.12 [d5c2ad6] - Test if a coverprofile content is empty before checking its latest character (#670) [14d9fa2] - replace tail package with maintained one. this fixes go get errors (#667) [4ba33d4] - improve ginkgo performance - makes progress on #644 [a14f98e] - fix convert integration tests [1f8ba69] - fix typo succesful -> successful (#663) [1ea49cf] - Fix invalid link (#658) [b886136] - convert utility : Include comments from source (#657) [1077c6d] - Explain what BDD means [d79e7fb] - skip race detector test on unsupported platform (#642) [f8ab89d] - Use Dup2 from golang.org/x/sys/unix instead of syscallDup (#638) [5d53c55] - Fix missing newline in combined coverage file (#641) [6a07ea2] - check if a spec is run before returning SpecSummary (#645) [8850000] ## 1.12.0 ### Features - Add module definition (#630) [78916ab] ## 1.11.0 ### Features - Add syscall for riscv64 architecture [f66e896] - teamcity reporter: output location of test failure as well as test definition (#626) [9869142] - teamcity reporter: output newline after every service message (#625) [3cfa02d] - Add support for go module when running `generate` command (#578) [9c89e3f] ## 1.10.3 ### Fixes - Set go_import_path in travis.yml to allow internal packages in forks (#607) [3b721db] - Add integration test [d90e0dc] - Fix coverage files combining [e5dde8c] - A new CLI option: -ginkgo.reportFile (#601) [034fd25] ## 1.10.2 ### Fixes - speed up table entry generateIt() (#609) [5049dc5] - Fix. Write errors to stderr instead of stdout (#610) [7bb3091] ## 1.10.1 ### Fixes - stack backtrace: fix skipping (#600) [2a4c0bd] ## 1.10.0 ### Fixes - stack backtrace: fix alignment and skipping [66915d6] - fix typo in documentation [8f97b93] ## 1.9.0 ### Features - Option to print output into report, when tests have passed [0545415] ### Fixes - Fixed typos in comments [0ecbc58] - gofmt code [a7f8bfb] - Simplify code [7454d00] - Simplify concatenation, incrementation and function assignment [4825557] - Avoid unnecessary conversions [9d9403c] - JUnit: include more detailed information about panic [19cca4b] - Print help to stdout when the user asks for help [4cb7441] ## 1.8.0 ### New Features - allow config of the vet flag for `go test` (#562) [3cd45fa] - Support projects using go modules [d56ee76] ### Fixes and Minor Improvements - chore(godoc): fixes typos in Measurement funcs [dbaca8e] - Optimize focus to avoid allocations [f493786] - Ensure generated test file names are underscored [505cc35] ## 1.7.0 ### New Features - Add JustAfterEach (#484) [0d4f080] ### Fixes - Correctly round suite time in junit reporter [2445fc1] - Avoid using -i argument to go test for Golang 1.10+ [46bbc26] ## 1.6.0 ### New Features - add --debug flag to emit node output to files (#499) [39febac] ### Fixes - fix: for `go vet` to pass [69338ec] - docs: fix for contributing instructions [7004cb1] - consolidate and streamline contribution docs (#494) [d848015] - Make generated Junit file compatable with "Maven Surefire" (#488) [e51bee6] - all: gofmt [000d317] - Increase eventually timeout to 30s [c73579c] - Clarify asynchronous test behaviour [294d8f4] - Travis badge should only show master [26d2143] ## 1.5.0 5/10/2018 ### New Features - Supports go v1.10 (#443, #446, #451) [e873237, 468e89e, e37dbfe, a37f4c0, c0b857d, bca5260, 4177ca8] - Add a When() synonym for Context() (#386) [747514b, 7484dad, 7354a07, dd826c8] - Re-add noisySkippings flag [652e15c] - Allow coverage to be displayed for focused specs (#367) [11459a8] - Handle -outputdir flag (#364) [228e3a8] - Handle -coverprofile flag (#355) [43392d5] ### Fixes - When using custom reporters register the custom reporters *before* the default reporter. This allows users to see the output of any print statements in their customer reporters. (#365) [8382b23] - When running a test and calculating the coverage using the `-coverprofile` and `-outputdir` flags, Ginkgo fails with an error if the directory does not exist. This is due to an [issue in go 1.10](https://github.com/golang/go/issues/24588) (#446) [b36a6e0] - `unfocus` command ignores vendor folder (#459) [e5e551c, c556e43, a3b6351, 9a820dd] - Ignore packages whose tests are all ignored by go (#456) [7430ca7, 6d8be98] - Increase the threshold when checking time measuments (#455) [2f714bf, 68f622c] - Fix race condition in coverage tests (#423) [a5a8ff7, ab9c08b] - Add an extra new line after reporting spec run completion for test2json [874520d] - added name name field to junit reported testsuite [ae61c63] - Do not set the run time of a spec when the dryRun flag is used (#438) [457e2d9, ba8e856] - Process FWhen and FSpecify when unfocusing (#434) [9008c7b, ee65bd, df87dfe] - Synchronise the access to the state of specs to avoid race conditions (#430) [7d481bc, ae6829d] - Added Duration on GinkgoTestDescription (#383) [5f49dad, 528417e, 0747408, 329d7ed] - Fix Ginkgo stack trace on failure for Specify (#415) [b977ede, 65ca40e, 6c46eb8] - Update README with Go 1.6+, Golang -> Go (#409) [17f6b97, bc14b66, 20d1598] - Use fmt.Errorf instead of errors.New(fmt.Sprintf (#401) [a299f56, 44e2eaa] - Imports in generated code should follow conventions (#398) [0bec0b0, e8536d8] - Prevent data race error when Recording a benchmark value from multiple go routines (#390) [c0c4881, 7a241e9] - Replace GOPATH in Environment [4b883f0] ## 1.4.0 7/16/2017 - `ginkgo` now provides a hint if you accidentally forget to run `ginkgo bootstrap` to generate a `*_suite_test.go` file that actually invokes the Ginkgo test runner. [#345](https://github.com/onsi/ginkgo/pull/345) - thanks to improvements in `go test -c` `ginkgo` no longer needs to fix Go's compilation output to ensure compilation errors are expressed relative to the CWD. [#357] - `ginkgo watch -watchRegExp=...` allows you to specify a custom regular expression to watch. Only files matching the regular expression are watched for changes (the default is `\.go$`) [#356] - `ginkgo` now always emits compilation output. Previously, only failed compilation output was printed out. [#277] - `ginkgo -requireSuite` now fails the test run if there are `*_test.go` files but `go test` fails to detect any tests. Typically this means you forgot to run `ginkgo bootstrap` to generate a suite file. [#344] - `ginkgo -timeout=DURATION` allows you to adjust the timeout for the entire test suite (default is 24 hours) [#248] ## 1.3.0 3/28/2017 Improvements: - Significantly improved parallel test distribution. Now instead of pre-sharding test cases across workers (which can result in idle workers and poor test performance) Ginkgo uses a shared queue to keep all workers busy until all tests are complete. This improves test-time performance and consistency. - `Skip(message)` can be used to skip the current test. - Added `extensions/table` - a Ginkgo DSL for [Table Driven Tests](http://onsi.github.io/ginkgo/#table-driven-tests) - Add `GinkgoRandomSeed()` - shorthand for `config.GinkgoConfig.RandomSeed` - Support for retrying flaky tests with `--flakeAttempts` - `ginkgo ./...` now recurses as you'd expect - Added `Specify` a synonym for `It` - Support colorise on Windows - Broader support for various go compilation flags in the `ginkgo` CLI Bug Fixes: - Ginkgo tests now fail when you `panic(nil)` (#167) ## 1.2.0 5/31/2015 Improvements - `ginkgo -coverpkg` calls down to `go test -coverpkg` (#160) - `ginkgo -afterSuiteHook COMMAND` invokes the passed-in `COMMAND` after a test suite completes (#152) - Relaxed requirement for Go 1.4+. `ginkgo` now works with Go v1.3+ (#166) ## 1.2.0-beta Ginkgo now requires Go 1.4+ Improvements: - Call reporters in reverse order when announcing spec completion -- allows custom reporters to emit output before the default reporter does. - Improved focus behavior. Now, this: ```golang FDescribe("Some describe", func() { It("A", func() {}) FIt("B", func() {}) }) ``` will run `B` but *not* `A`. This tends to be a common usage pattern when in the thick of writing and debugging tests. - When `SIGINT` is received, Ginkgo will emit the contents of the `GinkgoWriter` before running the `AfterSuite`. Useful for debugging stuck tests. - When `--progress` is set, Ginkgo will write test progress (in particular, Ginkgo will say when it is about to run a BeforeEach, AfterEach, It, etc...) to the `GinkgoWriter`. This is useful for debugging stuck tests and tests that generate many logs. - Improved output when an error occurs in a setup or teardown block. - When `--dryRun` is set, Ginkgo will walk the spec tree and emit to its reporter *without* actually running anything. Best paired with `-v` to understand which specs will run in which order. - Add `By` to help document long `It`s. `By` simply writes to the `GinkgoWriter`. - Add support for precompiled tests: - `ginkgo build ` will now compile the package, producing a file named `package.test` - The compiled `package.test` file can be run directly. This runs the tests in series. - To run precompiled tests in parallel, you can run: `ginkgo -p package.test` - Support `bootstrap`ping and `generate`ing [Agouti](http://agouti.org) specs. - `ginkgo generate` and `ginkgo bootstrap` now honor the package name already defined in a given directory - The `ginkgo` CLI ignores `SIGQUIT`. Prevents its stack dump from interlacing with the underlying test suite's stack dump. - The `ginkgo` CLI now compiles tests into a temporary directory instead of the package directory. This necessitates upgrading to Go v1.4+. - `ginkgo -notify` now works on Linux Bug Fixes: - If --skipPackages is used and all packages are skipped, Ginkgo should exit 0. - Fix tempfile leak when running in parallel - Fix incorrect failure message when a panic occurs during a parallel test run - Fixed an issue where a pending test within a focused context (or a focused test within a pending context) would skip all other tests. - Be more consistent about handling SIGTERM as well as SIGINT - When interupted while concurrently compiling test suites in the background, Ginkgo now cleans up the compiled artifacts. - Fixed a long standing bug where `ginkgo -p` would hang if a process spawned by one of the Ginkgo parallel nodes does not exit. (Hooray!) ## 1.1.0 (8/2/2014) No changes, just dropping the beta. ## 1.1.0-beta (7/22/2014) New Features: - `ginkgo watch` now monitors packages *and their dependencies* for changes. The depth of the dependency tree can be modified with the `-depth` flag. - Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, even when they pass. This allows CI systems to detect accidental commits of focused test suites. - `ginkgo -p` runs the testsuite in parallel with an auto-detected number of nodes. - `ginkgo -tags=TAG_LIST` passes a list of tags down to the `go build` command. - `ginkgo --failFast` aborts the test suite after the first failure. - `ginkgo generate file_1 file_2` can take multiple file arguments. - Ginkgo now summarizes any spec failures that occurred at the end of the test run. - `ginkgo --randomizeSuites` will run tests *suites* in random order using the generated/passed-in seed. Improvements: - `ginkgo -skipPackage` now takes a comma-separated list of strings. If the *relative path* to a package matches one of the entries in the comma-separated list, that package is skipped. - `ginkgo --untilItFails` no longer recompiles between attempts. - Ginkgo now panics when a runnable node (`It`, `BeforeEach`, `JustBeforeEach`, `AfterEach`, `Measure`) is nested within another runnable node. This is always a mistake. Any test suites that panic because of this change should be fixed. Bug Fixes: - `ginkgo boostrap` and `ginkgo generate` no longer fail when dealing with `hyphen-separated-packages`. - parallel specs are now better distributed across nodes - fixed a crashing bug where (for example) distributing 11 tests across 7 nodes would panic ## 1.0.0 (5/24/2014) New Features: - Add `GinkgoParallelNode()` - shorthand for `config.GinkgoConfig.ParallelNode` Improvements: - When compilation fails, the compilation output is rewritten to present a correct *relative* path. Allows ⌘-clicking in iTerm open the file in your text editor. - `--untilItFails` and `ginkgo watch` now generate new random seeds between test runs, unless a particular random seed is specified. Bug Fixes: - `-cover` now generates a correctly combined coverprofile when running with in parallel with multiple `-node`s. - Print out the contents of the `GinkgoWriter` when `BeforeSuite` or `AfterSuite` fail. - Fix all remaining race conditions in Ginkgo's test suite. ## 1.0.0-beta (4/14/2014) Breaking changes: - `thirdparty/gomocktestreporter` is gone. Use `GinkgoT()` instead - Modified the Reporter interface - `watch` is now a subcommand, not a flag. DSL changes: - `BeforeSuite` and `AfterSuite` for setting up and tearing down test suites. - `AfterSuite` is triggered on interrupt (`^C`) as well as exit. - `SynchronizedBeforeSuite` and `SynchronizedAfterSuite` for setting up and tearing down singleton resources across parallel nodes. CLI changes: - `watch` is now a subcommand, not a flag - `--nodot` flag can be passed to `ginkgo generate` and `ginkgo bootstrap` to avoid dot imports. This explicitly imports all exported identifiers in Ginkgo and Gomega. Refreshing this list can be done by running `ginkgo nodot` - Additional arguments can be passed to specs. Pass them after the `--` separator - `--skipPackage` flag takes a regexp and ignores any packages with package names passing said regexp. - `--trace` flag prints out full stack traces when errors occur, not just the line at which the error occurs. Misc: - Start using semantic versioning - Start maintaining changelog Major refactor: - Pull out Ginkgo's internal to `internal` - Rename `example` everywhere to `spec` - Much more! ================================================ FILE: vendor/github.com/onsi/ginkgo/CONTRIBUTING.md ================================================ # Contributing to Ginkgo Your contributions to Ginkgo are essential for its long-term maintenance and improvement. - Please **open an issue first** - describe what problem you are trying to solve and give the community a forum for input and feedback ahead of investing time in writing code! - Ensure adequate test coverage: - When adding to the Ginkgo library, add unit and/or integration tests (under the `integration` folder). - When adding to the Ginkgo CLI, note that there are very few unit tests. Please add an integration test. - Update the documentation. Ginko uses `godoc` comments and documentation on the `gh-pages` branch. If relevant, please submit a docs PR to that branch alongside your code PR. Thanks for supporting Ginkgo! ## Setup Fork the repo, then: ``` go get github.com/onsi/ginkgo go get github.com/onsi/gomega/... cd $GOPATH/src/github.com/onsi/ginkgo git remote add fork git@github.com:/ginkgo.git ginkgo -r -p # ensure tests are green go vet ./... # ensure linter is happy ``` ## Making the PR - go to a new branch `git checkout -b my-feature` - make your changes - run tests and linter again (see above) - `git push fork` - open PR 🎉 ================================================ FILE: vendor/github.com/onsi/ginkgo/LICENSE ================================================ Copyright (c) 2013-2014 Onsi Fakhouri 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. ================================================ FILE: vendor/github.com/onsi/ginkgo/README.md ================================================ ![Ginkgo: A Go BDD Testing Framework](https://onsi.github.io/ginkgo/images/ginkgo.png) [![test](https://github.com/onsi/ginkgo/workflows/test/badge.svg?branch=master)](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster) Jump to the [docs](https://onsi.github.io/ginkgo/) | [中文文档](https://ke-chain.github.io/ginkgodoc) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)! If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW). # Ginkgo 2.0 Release Candidate is available! An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [ver2](https://github.com/onsi/ginkgo/tree/ver2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion. As described in the [changelog](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711). Please start exploring and using the V2 release! To get started follow the [Using the Release Candidate](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta) directions in the migration guide. ## TLDR Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests. It is typically (and optionally) paired with the [Gomega](https://github.com/onsi/gomega) matcher library. ```go Describe("the strings package", func() { Context("strings.Contains()", func() { When("the string contains the substring in the middle", func() { It("returns `true`", func() { Expect(strings.Contains("Ginkgo is awesome", "is")).To(BeTrue()) }) }) }) }) ``` ## Feature List - Ginkgo uses Go's `testing` package and can live alongside your existing `testing` tests. It's easy to [bootstrap](https://onsi.github.io/ginkgo/#bootstrapping-a-suite) and start writing your [first tests](https://onsi.github.io/ginkgo/#adding-specs-to-a-suite) - Ginkgo allows you to write tests in Go using expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style: - Nestable [`Describe`, `Context` and `When` container blocks](https://onsi.github.io/ginkgo/#organizing-specs-with-containers-describe-and-context) - [`BeforeEach` and `AfterEach` blocks](https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach) for setup and teardown - [`It` and `Specify` blocks](https://onsi.github.io/ginkgo/#individual-specs-it) that hold your assertions - [`JustBeforeEach` blocks](https://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach) that separate creation from configuration (also known as the subject action pattern). - [`BeforeSuite` and `AfterSuite` blocks](https://onsi.github.io/ginkgo/#global-setup-and-teardown-beforesuite-and-aftersuite) to prep for and cleanup after a suite. - A comprehensive test runner that lets you: - Mark specs as [pending](https://onsi.github.io/ginkgo/#pending-specs) - [Focus](https://onsi.github.io/ginkgo/#focused-specs) individual specs, and groups of specs, either programmatically or on the command line - Run your tests in [random order](https://onsi.github.io/ginkgo/#spec-permutation), and then reuse random seeds to replicate the same order. - Break up your test suite into parallel processes for straightforward [test parallelization](https://onsi.github.io/ginkgo/#parallel-specs) - `ginkgo`: a command line interface with plenty of handy command line arguments for [running your tests](https://onsi.github.io/ginkgo/#running-tests) and [generating](https://onsi.github.io/ginkgo/#generators) test files. Here are a few choice examples: - `ginkgo -nodes=N` runs your tests in `N` parallel processes and print out coherent output in realtime - `ginkgo -cover` runs your tests using Go's code coverage tool - `ginkgo convert` converts an XUnit-style `testing` package to a Ginkgo-style package - `ginkgo -focus="REGEXP"` and `ginkgo -skip="REGEXP"` allow you to specify a subset of tests to run via regular expression - `ginkgo -r` runs all tests suites under the current directory - `ginkgo -v` prints out identifying information for each tests just before it runs And much more: run `ginkgo help` for details! The `ginkgo` CLI is convenient, but purely optional -- Ginkgo works just fine with `go test` - `ginkgo watch` [watches](https://onsi.github.io/ginkgo/#watching-for-changes) packages *and their dependencies* for changes, then reruns tests. Run tests immediately as you develop! - Built-in support for testing [asynchronicity](https://onsi.github.io/ginkgo/#asynchronous-tests) - Built-in support for [benchmarking](https://onsi.github.io/ginkgo/#benchmark-tests) your code. Control the number of benchmark samples as you gather runtimes and other, arbitrary, bits of numerical information about your code. - [Completions for Sublime Text](https://github.com/onsi/ginkgo-sublime-completions): just use [Package Control](https://sublime.wbond.net/) to install `Ginkgo Completions`. - [Completions for VSCode](https://github.com/onsi/vscode-ginkgo): just use VSCode's extension installer to install `vscode-ginkgo`. - [Ginkgo tools for VSCode](https://marketplace.visualstudio.com/items?itemName=joselitofilho.ginkgotestexplorer): just use VSCode's extension installer to install `ginkgoTestExplorer`. - Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](https://onsi.github.io/ginkgo/#third-party-integrations) for details. - A modular architecture that lets you easily: - Write [custom reporters](https://onsi.github.io/ginkgo/#writing-custom-reporters) (for example, Ginkgo comes with a [JUnit XML reporter](https://onsi.github.io/ginkgo/#generating-junit-xml-output) and a TeamCity reporter). - [Adapt an existing matcher library (or write your own!)](https://onsi.github.io/ginkgo/#using-other-matcher-libraries) to work with Ginkgo ## [Gomega](https://github.com/onsi/gomega): Ginkgo's Preferred Matcher Library Ginkgo is best paired with Gomega. Learn more about Gomega [here](https://onsi.github.io/gomega/) ## [Agouti](https://github.com/sclevine/agouti): A Go Acceptance Testing Framework Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](https://agouti.org) ## Getting Started You'll need the Go command-line tools. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed. ### Global installation To install the Ginkgo command line interface: ```bash go get -u github.com/onsi/ginkgo/ginkgo ``` Note that this will install it to `$GOBIN`, which will need to be in the `$PATH` (or equivalent). Run `go help install` for more information. ### Go module ["tools package"](https://github.com/golang/go/issues/25922): Create (or update) a file called `tools/tools.go` with the following contents: ```go // +build tools package tools import ( _ "github.com/onsi/ginkgo/ginkgo" ) // This file imports packages that are used when running go generate, or used // during the development process but not otherwise depended on by built code. ``` The Ginkgo command can then be run via `go run github.com/onsi/ginkgo/ginkgo`. This approach allows the version of Ginkgo to be maintained under source control for reproducible results, and is well suited to automated test pipelines. ### Bootstrapping ```bash cd path/to/package/you/want/to/test ginkgo bootstrap # set up a new ginkgo suite ginkgo generate # will create a sample test file. edit this file and add your tests then... go test # to run your tests ginkgo # also runs your tests ``` ## I'm new to Go: What are my testing options? Of course, I heartily recommend [Ginkgo](https://github.com/onsi/ginkgo) and [Gomega](https://github.com/onsi/gomega). Both packages are seeing heavy, daily, production use on a number of projects and boast a mature and comprehensive feature-set. With that said, it's great to know what your options are :) ### What Go gives you out of the box Testing is a first class citizen in Go, however Go's built-in testing primitives are somewhat limited: The [testing](https://golang.org/pkg/testing) package provides basic XUnit style tests and no assertion library. ### Matcher libraries for Go's XUnit style tests A number of matcher libraries have been written to augment Go's built-in XUnit style tests. Here are two that have gained traction: - [testify](https://github.com/stretchr/testify) - [gocheck](https://labix.org/gocheck) You can also use Ginkgo's matcher library [Gomega](https://github.com/onsi/gomega) in [XUnit style tests](https://onsi.github.io/gomega/#using-gomega-with-golangs-xunitstyle-tests) ### BDD style testing frameworks There are a handful of BDD-style testing frameworks written for Go. Here are a few: - [Ginkgo](https://github.com/onsi/ginkgo) ;) - [GoConvey](https://github.com/smartystreets/goconvey) - [Goblin](https://github.com/franela/goblin) - [Mao](https://github.com/azer/mao) - [Zen](https://github.com/pranavraja/zen) Finally, @shageman has [put together](https://github.com/shageman/gotestit) a comprehensive comparison of Go testing libraries. Go explore! ## License Ginkgo is MIT-Licensed ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) ================================================ FILE: vendor/github.com/onsi/ginkgo/RELEASING.md ================================================ A Ginkgo release is a tagged git sha and a GitHub release. To cut a release: 1. Ensure CHANGELOG.md is up to date. - Use `git log --pretty=format:'- %s [%h]' HEAD...vX.X.X` to list all the commits since the last release - Categorize the changes into - Breaking Changes (requires a major version) - New Features (minor version) - Fixes (fix version) - Maintenance (which in general should not be mentioned in `CHANGELOG.md` as they have no user impact) 1. Update `VERSION` in `config/config.go` 1. Commit, push, and release: ``` git commit -m "vM.m.p" git push gh release create "vM.m.p" git fetch --tags origin master ``` ================================================ FILE: vendor/github.com/onsi/ginkgo/config/config.go ================================================ /* Ginkgo accepts a number of configuration options. These are documented [here](http://onsi.github.io/ginkgo/#the-ginkgo-cli) You can also learn more via ginkgo help or (I kid you not): go test -asdf */ package config import ( "flag" "time" "fmt" ) const VERSION = "1.16.5" type GinkgoConfigType struct { RandomSeed int64 RandomizeAllSpecs bool RegexScansFilePath bool FocusStrings []string SkipStrings []string SkipMeasurements bool FailOnPending bool FailFast bool FlakeAttempts int EmitSpecProgress bool DryRun bool DebugParallel bool ParallelNode int ParallelTotal int SyncHost string StreamHost string } var GinkgoConfig = GinkgoConfigType{} type DefaultReporterConfigType struct { NoColor bool SlowSpecThreshold float64 NoisyPendings bool NoisySkippings bool Succinct bool Verbose bool FullTrace bool ReportPassed bool ReportFile string } var DefaultReporterConfig = DefaultReporterConfigType{} func processPrefix(prefix string) string { if prefix != "" { prefix += "." } return prefix } type flagFunc func(string) func (f flagFunc) String() string { return "" } func (f flagFunc) Set(s string) error { f(s); return nil } func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { prefix = processPrefix(prefix) flagSet.Int64Var(&(GinkgoConfig.RandomSeed), prefix+"seed", time.Now().Unix(), "The seed used to randomize the spec suite.") flagSet.BoolVar(&(GinkgoConfig.RandomizeAllSpecs), prefix+"randomizeAllSpecs", false, "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe, Context and When groups.") flagSet.BoolVar(&(GinkgoConfig.SkipMeasurements), prefix+"skipMeasurements", false, "If set, ginkgo will skip any measurement specs.") flagSet.BoolVar(&(GinkgoConfig.FailOnPending), prefix+"failOnPending", false, "If set, ginkgo will mark the test suite as failed if any specs are pending.") flagSet.BoolVar(&(GinkgoConfig.FailFast), prefix+"failFast", false, "If set, ginkgo will stop running a test suite after a failure occurs.") flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.") flagSet.Var(flagFunc(flagFocus), prefix+"focus", "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed.") flagSet.Var(flagFunc(flagSkip), prefix+"skip", "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed.") flagSet.BoolVar(&(GinkgoConfig.RegexScansFilePath), prefix+"regexScansFilePath", false, "If set, ginkgo regex matching also will look at the file path (code location).") flagSet.IntVar(&(GinkgoConfig.FlakeAttempts), prefix+"flakeAttempts", 1, "Make up to this many attempts to run each spec. Please note that if any of the attempts succeed, the suite will not be failed. But any failures will still be recorded.") flagSet.BoolVar(&(GinkgoConfig.EmitSpecProgress), prefix+"progress", false, "If set, ginkgo will emit progress information as each spec runs to the GinkgoWriter.") flagSet.BoolVar(&(GinkgoConfig.DebugParallel), prefix+"debug", false, "If set, ginkgo will emit node output to files when running in parallel.") if includeParallelFlags { flagSet.IntVar(&(GinkgoConfig.ParallelNode), prefix+"parallel.node", 1, "This worker node's (one-indexed) node number. For running specs in parallel.") flagSet.IntVar(&(GinkgoConfig.ParallelTotal), prefix+"parallel.total", 1, "The total number of worker nodes. For running specs in parallel.") flagSet.StringVar(&(GinkgoConfig.SyncHost), prefix+"parallel.synchost", "", "The address for the server that will synchronize the running nodes.") flagSet.StringVar(&(GinkgoConfig.StreamHost), prefix+"parallel.streamhost", "", "The address for the server that the running nodes should stream data to.") } flagSet.BoolVar(&(DefaultReporterConfig.NoColor), prefix+"noColor", false, "If set, suppress color output in default reporter.") flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter.") flagSet.BoolVar(&(DefaultReporterConfig.NoisyPendings), prefix+"noisyPendings", true, "If set, default reporter will shout about pending tests.") flagSet.BoolVar(&(DefaultReporterConfig.NoisySkippings), prefix+"noisySkippings", true, "If set, default reporter will shout about skipping tests.") flagSet.BoolVar(&(DefaultReporterConfig.Verbose), prefix+"v", false, "If set, default reporter print out all specs as they begin.") flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report") flagSet.BoolVar(&(DefaultReporterConfig.FullTrace), prefix+"trace", false, "If set, default reporter prints out the full stack trace when a failure occurs") flagSet.BoolVar(&(DefaultReporterConfig.ReportPassed), prefix+"reportPassed", false, "If set, default reporter prints out captured output of passed tests.") flagSet.StringVar(&(DefaultReporterConfig.ReportFile), prefix+"reportFile", "", "Override the default reporter output file path.") } func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultReporterConfigType) []string { prefix = processPrefix(prefix) result := make([]string, 0) if ginkgo.RandomSeed > 0 { result = append(result, fmt.Sprintf("--%sseed=%d", prefix, ginkgo.RandomSeed)) } if ginkgo.RandomizeAllSpecs { result = append(result, fmt.Sprintf("--%srandomizeAllSpecs", prefix)) } if ginkgo.SkipMeasurements { result = append(result, fmt.Sprintf("--%sskipMeasurements", prefix)) } if ginkgo.FailOnPending { result = append(result, fmt.Sprintf("--%sfailOnPending", prefix)) } if ginkgo.FailFast { result = append(result, fmt.Sprintf("--%sfailFast", prefix)) } if ginkgo.DryRun { result = append(result, fmt.Sprintf("--%sdryRun", prefix)) } for _, s := range ginkgo.FocusStrings { result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, s)) } for _, s := range ginkgo.SkipStrings { result = append(result, fmt.Sprintf("--%sskip=%s", prefix, s)) } if ginkgo.FlakeAttempts > 1 { result = append(result, fmt.Sprintf("--%sflakeAttempts=%d", prefix, ginkgo.FlakeAttempts)) } if ginkgo.EmitSpecProgress { result = append(result, fmt.Sprintf("--%sprogress", prefix)) } if ginkgo.DebugParallel { result = append(result, fmt.Sprintf("--%sdebug", prefix)) } if ginkgo.ParallelNode != 0 { result = append(result, fmt.Sprintf("--%sparallel.node=%d", prefix, ginkgo.ParallelNode)) } if ginkgo.ParallelTotal != 0 { result = append(result, fmt.Sprintf("--%sparallel.total=%d", prefix, ginkgo.ParallelTotal)) } if ginkgo.StreamHost != "" { result = append(result, fmt.Sprintf("--%sparallel.streamhost=%s", prefix, ginkgo.StreamHost)) } if ginkgo.SyncHost != "" { result = append(result, fmt.Sprintf("--%sparallel.synchost=%s", prefix, ginkgo.SyncHost)) } if ginkgo.RegexScansFilePath { result = append(result, fmt.Sprintf("--%sregexScansFilePath", prefix)) } if reporter.NoColor { result = append(result, fmt.Sprintf("--%snoColor", prefix)) } if reporter.SlowSpecThreshold > 0 { result = append(result, fmt.Sprintf("--%sslowSpecThreshold=%.5f", prefix, reporter.SlowSpecThreshold)) } if !reporter.NoisyPendings { result = append(result, fmt.Sprintf("--%snoisyPendings=false", prefix)) } if !reporter.NoisySkippings { result = append(result, fmt.Sprintf("--%snoisySkippings=false", prefix)) } if reporter.Verbose { result = append(result, fmt.Sprintf("--%sv", prefix)) } if reporter.Succinct { result = append(result, fmt.Sprintf("--%ssuccinct", prefix)) } if reporter.FullTrace { result = append(result, fmt.Sprintf("--%strace", prefix)) } if reporter.ReportPassed { result = append(result, fmt.Sprintf("--%sreportPassed", prefix)) } if reporter.ReportFile != "" { result = append(result, fmt.Sprintf("--%sreportFile=%s", prefix, reporter.ReportFile)) } return result } // flagFocus implements the -focus flag. func flagFocus(arg string) { if arg != "" { GinkgoConfig.FocusStrings = append(GinkgoConfig.FocusStrings, arg) } } // flagSkip implements the -skip flag. func flagSkip(arg string) { if arg != "" { GinkgoConfig.SkipStrings = append(GinkgoConfig.SkipStrings, arg) } } ================================================ FILE: vendor/github.com/onsi/ginkgo/formatter/formatter.go ================================================ package formatter import ( "fmt" "regexp" "strings" ) const COLS = 80 type ColorMode uint8 const ( ColorModeNone ColorMode = iota ColorModeTerminal ColorModePassthrough ) var SingletonFormatter = New(ColorModeTerminal) func F(format string, args ...interface{}) string { return SingletonFormatter.F(format, args...) } func Fi(indentation uint, format string, args ...interface{}) string { return SingletonFormatter.Fi(indentation, format, args...) } func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { return SingletonFormatter.Fiw(indentation, maxWidth, format, args...) } type Formatter struct { ColorMode ColorMode colors map[string]string styleRe *regexp.Regexp preserveColorStylingTags bool } func NewWithNoColorBool(noColor bool) Formatter { if noColor { return New(ColorModeNone) } return New(ColorModeTerminal) } func New(colorMode ColorMode) Formatter { f := Formatter{ ColorMode: colorMode, colors: map[string]string{ "/": "\x1b[0m", "bold": "\x1b[1m", "underline": "\x1b[4m", "red": "\x1b[38;5;9m", "orange": "\x1b[38;5;214m", "coral": "\x1b[38;5;204m", "magenta": "\x1b[38;5;13m", "green": "\x1b[38;5;10m", "dark-green": "\x1b[38;5;28m", "yellow": "\x1b[38;5;11m", "light-yellow": "\x1b[38;5;228m", "cyan": "\x1b[38;5;14m", "gray": "\x1b[38;5;243m", "light-gray": "\x1b[38;5;246m", "blue": "\x1b[38;5;12m", }, } colors := []string{} for color := range f.colors { colors = append(colors, color) } f.styleRe = regexp.MustCompile("{{(" + strings.Join(colors, "|") + ")}}") return f } func (f Formatter) F(format string, args ...interface{}) string { return f.Fi(0, format, args...) } func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string { return f.Fiw(indentation, 0, format, args...) } func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { out := fmt.Sprintf(f.style(format), args...) if indentation == 0 && maxWidth == 0 { return out } lines := strings.Split(out, "\n") if maxWidth != 0 { outLines := []string{} maxWidth = maxWidth - indentation*2 for _, line := range lines { if f.length(line) <= maxWidth { outLines = append(outLines, line) continue } outWords := []string{} length := uint(0) words := strings.Split(line, " ") for _, word := range words { wordLength := f.length(word) if length+wordLength <= maxWidth { length += wordLength outWords = append(outWords, word) continue } outLines = append(outLines, strings.Join(outWords, " ")) outWords = []string{word} length = wordLength } if len(outWords) > 0 { outLines = append(outLines, strings.Join(outWords, " ")) } } lines = outLines } if indentation == 0 { return strings.Join(lines, "\n") } padding := strings.Repeat(" ", int(indentation)) for i := range lines { if lines[i] != "" { lines[i] = padding + lines[i] } } return strings.Join(lines, "\n") } func (f Formatter) length(styled string) uint { n := uint(0) inStyle := false for _, b := range styled { if inStyle { if b == 'm' { inStyle = false } continue } if b == '\x1b' { inStyle = true continue } n += 1 } return n } func (f Formatter) CycleJoin(elements []string, joiner string, cycle []string) string { if len(elements) == 0 { return "" } n := len(cycle) out := "" for i, text := range elements { out += cycle[i%n] + text if i < len(elements)-1 { out += joiner } } out += "{{/}}" return f.style(out) } func (f Formatter) style(s string) string { switch f.ColorMode { case ColorModeNone: return f.styleRe.ReplaceAllString(s, "") case ColorModePassthrough: return s case ColorModeTerminal: return f.styleRe.ReplaceAllStringFunc(s, func(match string) string { if out, ok := f.colors[strings.Trim(match, "{}")]; ok { return out } return match }) } return "" } ================================================ FILE: vendor/github.com/onsi/ginkgo/ginkgo_dsl.go ================================================ /* Ginkgo is a BDD-style testing framework for Golang The godoc documentation describes Ginkgo's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo/ Ginkgo's preferred matcher library is [Gomega](http://github.com/onsi/gomega) Ginkgo on Github: http://github.com/onsi/ginkgo Ginkgo is MIT-Licensed */ package ginkgo import ( "flag" "fmt" "io" "net/http" "os" "reflect" "strings" "time" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/internal/codelocation" "github.com/onsi/ginkgo/internal/global" "github.com/onsi/ginkgo/internal/remote" "github.com/onsi/ginkgo/internal/testingtproxy" "github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/reporters/stenographer" colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable" "github.com/onsi/ginkgo/types" ) var deprecationTracker = types.NewDeprecationTracker() const GINKGO_VERSION = config.VERSION const GINKGO_PANIC = ` Your test failed. Ginkgo panics to prevent subsequent assertions from running. Normally Ginkgo rescues this panic so you shouldn't see it. But, if you make an assertion in a goroutine, Ginkgo can't capture the panic. To circumvent this, you should call defer GinkgoRecover() at the top of the goroutine that caused this panic. ` func init() { config.Flags(flag.CommandLine, "ginkgo", true) GinkgoWriter = writer.New(os.Stdout) } //GinkgoWriter implements an io.Writer //When running in verbose mode any writes to GinkgoWriter will be immediately printed //to stdout. Otherwise, GinkgoWriter will buffer any writes produced during the current test and flush them to screen //only if the current test fails. var GinkgoWriter io.Writer //The interface by which Ginkgo receives *testing.T type GinkgoTestingT interface { Fail() } //GinkgoRandomSeed returns the seed used to randomize spec execution order. It is //useful for seeding your own pseudorandom number generators (PRNGs) to ensure //consistent executions from run to run, where your tests contain variability (for //example, when selecting random test data). func GinkgoRandomSeed() int64 { return config.GinkgoConfig.RandomSeed } //GinkgoParallelNode is deprecated, use GinkgoParallelProcess instead func GinkgoParallelNode() int { deprecationTracker.TrackDeprecation(types.Deprecations.ParallelNode(), codelocation.New(1)) return GinkgoParallelProcess() } //GinkgoParallelProcess returns the parallel process number for the current ginkgo process //The process number is 1-indexed func GinkgoParallelProcess() int { return config.GinkgoConfig.ParallelNode } //Some matcher libraries or legacy codebases require a *testing.T //GinkgoT implements an interface analogous to *testing.T and can be used if //the library in question accepts *testing.T through an interface // // For example, with testify: // assert.Equal(GinkgoT(), 123, 123, "they should be equal") // // Or with gomock: // gomock.NewController(GinkgoT()) // // GinkgoT() takes an optional offset argument that can be used to get the // correct line number associated with the failure. func GinkgoT(optionalOffset ...int) GinkgoTInterface { offset := 3 if len(optionalOffset) > 0 { offset = optionalOffset[0] } failedFunc := func() bool { return CurrentGinkgoTestDescription().Failed } nameFunc := func() string { return CurrentGinkgoTestDescription().FullTestText } return testingtproxy.New(GinkgoWriter, Fail, Skip, failedFunc, nameFunc, offset) } //The interface returned by GinkgoT(). This covers most of the methods //in the testing package's T. type GinkgoTInterface interface { Cleanup(func()) Setenv(key, value string) Error(args ...interface{}) Errorf(format string, args ...interface{}) Fail() FailNow() Failed() bool Fatal(args ...interface{}) Fatalf(format string, args ...interface{}) Helper() Log(args ...interface{}) Logf(format string, args ...interface{}) Name() string Parallel() Skip(args ...interface{}) SkipNow() Skipf(format string, args ...interface{}) Skipped() bool TempDir() string } //Custom Ginkgo test reporters must implement the Reporter interface. // //The custom reporter is passed in a SuiteSummary when the suite begins and ends, //and a SpecSummary just before a spec begins and just after a spec ends type Reporter reporters.Reporter //Asynchronous specs are given a channel of the Done type. You must close or write to the channel //to tell Ginkgo that your async test is done. type Done chan<- interface{} //GinkgoTestDescription represents the information about the current running test returned by CurrentGinkgoTestDescription // FullTestText: a concatenation of ComponentTexts and the TestText // ComponentTexts: a list of all texts for the Describes & Contexts leading up to the current test // TestText: the text in the actual It or Measure node // IsMeasurement: true if the current test is a measurement // FileName: the name of the file containing the current test // LineNumber: the line number for the current test // Failed: if the current test has failed, this will be true (useful in an AfterEach) type GinkgoTestDescription struct { FullTestText string ComponentTexts []string TestText string IsMeasurement bool FileName string LineNumber int Failed bool Duration time.Duration } //CurrentGinkgoTestDescripton returns information about the current running test. func CurrentGinkgoTestDescription() GinkgoTestDescription { summary, ok := global.Suite.CurrentRunningSpecSummary() if !ok { return GinkgoTestDescription{} } subjectCodeLocation := summary.ComponentCodeLocations[len(summary.ComponentCodeLocations)-1] return GinkgoTestDescription{ ComponentTexts: summary.ComponentTexts[1:], FullTestText: strings.Join(summary.ComponentTexts[1:], " "), TestText: summary.ComponentTexts[len(summary.ComponentTexts)-1], IsMeasurement: summary.IsMeasurement, FileName: subjectCodeLocation.FileName, LineNumber: subjectCodeLocation.LineNumber, Failed: summary.HasFailureState(), Duration: summary.RunTime, } } //Measurement tests receive a Benchmarker. // //You use the Time() function to time how long the passed in body function takes to run //You use the RecordValue() function to track arbitrary numerical measurements. //The RecordValueWithPrecision() function can be used alternatively to provide the unit //and resolution of the numeric measurement. //The optional info argument is passed to the test reporter and can be used to // provide the measurement data to a custom reporter with context. // //See http://onsi.github.io/ginkgo/#benchmark_tests for more details type Benchmarker interface { Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration) RecordValue(name string, value float64, info ...interface{}) RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{}) } //RunSpecs is the entry point for the Ginkgo test runner. //You must call this within a Golang testing TestX(t *testing.T) function. // //To bootstrap a test suite you can use the Ginkgo CLI: // // ginkgo bootstrap func RunSpecs(t GinkgoTestingT, description string) bool { specReporters := []Reporter{buildDefaultReporter()} if config.DefaultReporterConfig.ReportFile != "" { reportFile := config.DefaultReporterConfig.ReportFile specReporters[0] = reporters.NewJUnitReporter(reportFile) specReporters = append(specReporters, buildDefaultReporter()) } return runSpecsWithCustomReporters(t, description, specReporters) } //To run your tests with Ginkgo's default reporter and your custom reporter(s), replace //RunSpecs() with this method. func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool { deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter()) specReporters = append(specReporters, buildDefaultReporter()) return runSpecsWithCustomReporters(t, description, specReporters) } //To run your tests with your custom reporter(s) (and *not* Ginkgo's default reporter), replace //RunSpecs() with this method. Note that parallel tests will not work correctly without the default reporter func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool { deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter()) return runSpecsWithCustomReporters(t, description, specReporters) } func runSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool { writer := GinkgoWriter.(*writer.Writer) writer.SetStream(config.DefaultReporterConfig.Verbose) reporters := make([]reporters.Reporter, len(specReporters)) for i, reporter := range specReporters { reporters[i] = reporter } passed, hasFocusedTests := global.Suite.Run(t, description, reporters, writer, config.GinkgoConfig) if deprecationTracker.DidTrackDeprecations() { fmt.Fprintln(colorable.NewColorableStderr(), deprecationTracker.DeprecationsReport()) } if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { fmt.Println("PASS | FOCUSED") os.Exit(types.GINKGO_FOCUS_EXIT_CODE) } return passed } func buildDefaultReporter() Reporter { remoteReportingServer := config.GinkgoConfig.StreamHost if remoteReportingServer == "" { stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1, colorable.NewColorableStdout()) return reporters.NewDefaultReporter(config.DefaultReporterConfig, stenographer) } else { debugFile := "" if config.GinkgoConfig.DebugParallel { debugFile = fmt.Sprintf("ginkgo-node-%d.log", config.GinkgoConfig.ParallelNode) } return remote.NewForwardingReporter(config.DefaultReporterConfig, remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor(), GinkgoWriter.(*writer.Writer), debugFile) } } //Skip notifies Ginkgo that the current spec was skipped. func Skip(message string, callerSkip ...int) { skip := 0 if len(callerSkip) > 0 { skip = callerSkip[0] } global.Failer.Skip(message, codelocation.New(skip+1)) panic(GINKGO_PANIC) } //Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.) func Fail(message string, callerSkip ...int) { skip := 0 if len(callerSkip) > 0 { skip = callerSkip[0] } global.Failer.Fail(message, codelocation.New(skip+1)) panic(GINKGO_PANIC) } //GinkgoRecover should be deferred at the top of any spawned goroutine that (may) call `Fail` //Since Gomega assertions call fail, you should throw a `defer GinkgoRecover()` at the top of any goroutine that //calls out to Gomega // //Here's why: Ginkgo's `Fail` method records the failure and then panics to prevent //further assertions from running. This panic must be recovered. Ginkgo does this for you //if the panic originates in a Ginkgo node (an It, BeforeEach, etc...) // //Unfortunately, if a panic originates on a goroutine *launched* from one of these nodes there's no //way for Ginkgo to rescue the panic. To do this, you must remember to `defer GinkgoRecover()` at the top of such a goroutine. func GinkgoRecover() { e := recover() if e != nil { global.Failer.Panic(codelocation.New(1), e) } } //Describe blocks allow you to organize your specs. A Describe block can contain any number of //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. // //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally //equivalent. The difference is purely semantic -- you typically Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func Describe(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FDescribe func FDescribe(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PDescribe func PDescribe(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XDescribe func XDescribe(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //Context blocks allow you to organize your specs. A Context block can contain any number of //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. // //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func Context(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FContext func FContext(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PContext func PContext(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XContext func XContext(text string, body func()) bool { global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //When blocks allow you to organize your specs. A When block can contain any number of //BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. // //In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func When(text string, body func()) bool { global.Suite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FWhen func FWhen(text string, body func()) bool { global.Suite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PWhen func PWhen(text string, body func()) bool { global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XWhen func XWhen(text string, body func()) bool { global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) return true } //It blocks contain your test code and assertions. You cannot nest any other Ginkgo blocks //within an It block. // //Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a //function that accepts a Done channel. When you do this, you can also provide an optional timeout. func It(text string, body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) return true } //You can focus individual Its using FIt func FIt(text string, body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) return true } //You can mark Its as pending using PIt func PIt(text string, _ ...interface{}) bool { global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Its as pending using XIt func XIt(text string, _ ...interface{}) bool { global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //Specify blocks are aliases for It blocks and allow for more natural wording in situations //which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks //which apply to It blocks. func Specify(text string, body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) return true } //You can focus individual Specifys using FSpecify func FSpecify(text string, body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) return true } //You can mark Specifys as pending using PSpecify func PSpecify(text string, is ...interface{}) bool { global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Specifys as pending using XSpecify func XSpecify(text string, is ...interface{}) bool { global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //By allows you to better document large Its. // //Generally you should try to keep your Its short and to the point. This is not always possible, however, //especially in the context of integration tests that capture a particular workflow. // //By allows you to document such flows. By must be called within a runnable node (It, BeforeEach, Measure, etc...) //By will simply log the passed in text to the GinkgoWriter. If By is handed a function it will immediately run the function. func By(text string, callbacks ...func()) { preamble := "\x1b[1mSTEP\x1b[0m" if config.DefaultReporterConfig.NoColor { preamble = "STEP" } fmt.Fprintln(GinkgoWriter, preamble+": "+text) if len(callbacks) == 1 { callbacks[0]() } if len(callbacks) > 1 { panic("just one callback per By, please") } } //Measure blocks run the passed in body function repeatedly (determined by the samples argument) //and accumulate metrics provided to the Benchmarker by the body function. // //The body function must have the signature: // func(b Benchmarker) func Measure(text string, body interface{}, samples int) bool { deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1)) global.Suite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples) return true } //You can focus individual Measures using FMeasure func FMeasure(text string, body interface{}, samples int) bool { deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1)) global.Suite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) return true } //You can mark Measurements as pending using PMeasure func PMeasure(text string, _ ...interface{}) bool { deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1)) global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Measurements as pending using XMeasure func XMeasure(text string, _ ...interface{}) bool { deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1)) global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) return true } //BeforeSuite blocks are run just once before any specs are run. When running in parallel, each //parallel node process will call BeforeSuite. // //BeforeSuite blocks can be made asynchronous by providing a body function that accepts a Done channel // //You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. func BeforeSuite(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } //AfterSuite blocks are *always* run after all the specs regardless of whether specs have passed or failed. //Moreover, if Ginkgo receives an interrupt signal (^C) it will attempt to run the AfterSuite before exiting. // //When running in parallel, each parallel node process will call AfterSuite. // //AfterSuite blocks can be made asynchronous by providing a body function that accepts a Done channel // //You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level. func AfterSuite(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } //SynchronizedBeforeSuite blocks are primarily meant to solve the problem of setting up singleton external resources shared across //nodes when running tests in parallel. For example, say you have a shared database that you can only start one instance of that //must be used in your tests. When running in parallel, only one node should set up the database and all other nodes should wait //until that node is done before running. // //SynchronizedBeforeSuite accomplishes this by taking *two* function arguments. The first is only run on parallel node #1. The second is //run on all nodes, but *only* after the first function completes successfully. Ginkgo also makes it possible to send data from the first function (on Node 1) //to the second function (on all the other nodes). // //The functions have the following signatures. The first function (which only runs on node 1) has the signature: // // func() []byte // //or, to run asynchronously: // // func(done Done) []byte // //The byte array returned by the first function is then passed to the second function, which has the signature: // // func(data []byte) // //or, to run asynchronously: // // func(data []byte, done Done) // //Here's a simple pseudo-code example that starts a shared database on Node 1 and shares the database's address with the other nodes: // // var dbClient db.Client // var dbRunner db.Runner // // var _ = SynchronizedBeforeSuite(func() []byte { // dbRunner = db.NewRunner() // err := dbRunner.Start() // Ω(err).ShouldNot(HaveOccurred()) // return []byte(dbRunner.URL) // }, func(data []byte) { // dbClient = db.NewClient() // err := dbClient.Connect(string(data)) // Ω(err).ShouldNot(HaveOccurred()) // }) func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool { global.Suite.SetSynchronizedBeforeSuiteNode( node1Body, allNodesBody, codelocation.New(1), parseTimeout(timeout...), ) return true } //SynchronizedAfterSuite blocks complement the SynchronizedBeforeSuite blocks in solving the problem of setting up //external singleton resources shared across nodes when running tests in parallel. // //SynchronizedAfterSuite accomplishes this by taking *two* function arguments. The first runs on all nodes. The second runs only on parallel node #1 //and *only* after all other nodes have finished and exited. This ensures that node 1, and any resources it is running, remain alive until //all other nodes are finished. // //Both functions have the same signature: either func() or func(done Done) to run asynchronously. // //Here's a pseudo-code example that complements that given in SynchronizedBeforeSuite. Here, SynchronizedAfterSuite is used to tear down the shared database //only after all nodes have finished: // // var _ = SynchronizedAfterSuite(func() { // dbClient.Cleanup() // }, func() { // dbRunner.Stop() // }) func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool { global.Suite.SetSynchronizedAfterSuiteNode( allNodesBody, node1Body, codelocation.New(1), parseTimeout(timeout...), ) return true } //BeforeEach blocks are run before It blocks. When multiple BeforeEach blocks are defined in nested //Describe and Context blocks the outermost BeforeEach blocks are run first. // //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func BeforeEach(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } //JustBeforeEach blocks are run before It blocks but *after* all BeforeEach blocks. For more details, //read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_) // //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func JustBeforeEach(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } //JustAfterEach blocks are run after It blocks but *before* all AfterEach blocks. For more details, //read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_) // //Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func JustAfterEach(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } //AfterEach blocks are run after It blocks. When multiple AfterEach blocks are defined in nested //Describe and Context blocks the innermost AfterEach blocks are run first. // //Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func AfterEach(body interface{}, timeout ...float64) bool { validateBodyFunc(body, codelocation.New(1)) global.Suite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } func validateBodyFunc(body interface{}, cl types.CodeLocation) { t := reflect.TypeOf(body) if t.Kind() != reflect.Func { return } if t.NumOut() > 0 { return } if t.NumIn() == 0 { return } if t.In(0) == reflect.TypeOf(make(Done)) { deprecationTracker.TrackDeprecation(types.Deprecations.Async(), cl) } } func parseTimeout(timeout ...float64) time.Duration { if len(timeout) == 0 { return global.DefaultTimeout } else { return time.Duration(timeout[0] * float64(time.Second)) } } ================================================ FILE: vendor/github.com/onsi/ginkgo/go.mod ================================================ module github.com/onsi/ginkgo go 1.16 require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 github.com/nxadm/tail v1.4.8 github.com/onsi/gomega v1.10.1 golang.org/x/sys v0.0.0-20210112080510-489259a85091 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e ) retract v1.16.3 // git tag accidentally associated with incorrect git commit ================================================ FILE: vendor/github.com/onsi/ginkgo/go.sum ================================================ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/codelocation/code_location.go ================================================ package codelocation import ( "regexp" "runtime" "runtime/debug" "strings" "github.com/onsi/ginkgo/types" ) func New(skip int) types.CodeLocation { _, file, line, _ := runtime.Caller(skip + 1) stackTrace := PruneStack(string(debug.Stack()), skip+1) return types.CodeLocation{FileName: file, LineNumber: line, FullStackTrace: stackTrace} } // PruneStack removes references to functions that are internal to Ginkgo // and the Go runtime from a stack string and a certain number of stack entries // at the beginning of the stack. The stack string has the format // as returned by runtime/debug.Stack. The leading goroutine information is // optional and always removed if present. Beware that runtime/debug.Stack // adds itself as first entry, so typically skip must be >= 1 to remove that // entry. func PruneStack(fullStackTrace string, skip int) string { stack := strings.Split(fullStackTrace, "\n") // Ensure that the even entries are the method names and the // the odd entries the source code information. if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") { // Ignore "goroutine 29 [running]:" line. stack = stack[1:] } // The "+1" is for skipping over the initial entry, which is // runtime/debug.Stack() itself. if len(stack) > 2*(skip+1) { stack = stack[2*(skip+1):] } prunedStack := []string{} re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`) for i := 0; i < len(stack)/2; i++ { // We filter out based on the source code file name. if !re.Match([]byte(stack[i*2+1])) { prunedStack = append(prunedStack, stack[i*2]) prunedStack = append(prunedStack, stack[i*2+1]) } } return strings.Join(prunedStack, "\n") } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/containernode/container_node.go ================================================ package containernode import ( "math/rand" "sort" "github.com/onsi/ginkgo/internal/leafnodes" "github.com/onsi/ginkgo/types" ) type subjectOrContainerNode struct { containerNode *ContainerNode subjectNode leafnodes.SubjectNode } func (n subjectOrContainerNode) text() string { if n.containerNode != nil { return n.containerNode.Text() } else { return n.subjectNode.Text() } } type CollatedNodes struct { Containers []*ContainerNode Subject leafnodes.SubjectNode } type ContainerNode struct { text string flag types.FlagType codeLocation types.CodeLocation setupNodes []leafnodes.BasicNode subjectAndContainerNodes []subjectOrContainerNode } func New(text string, flag types.FlagType, codeLocation types.CodeLocation) *ContainerNode { return &ContainerNode{ text: text, flag: flag, codeLocation: codeLocation, } } func (container *ContainerNode) Shuffle(r *rand.Rand) { sort.Sort(container) permutation := r.Perm(len(container.subjectAndContainerNodes)) shuffledNodes := make([]subjectOrContainerNode, len(container.subjectAndContainerNodes)) for i, j := range permutation { shuffledNodes[i] = container.subjectAndContainerNodes[j] } container.subjectAndContainerNodes = shuffledNodes } func (node *ContainerNode) BackPropagateProgrammaticFocus() bool { if node.flag == types.FlagTypePending { return false } shouldUnfocus := false for _, subjectOrContainerNode := range node.subjectAndContainerNodes { if subjectOrContainerNode.containerNode != nil { shouldUnfocus = subjectOrContainerNode.containerNode.BackPropagateProgrammaticFocus() || shouldUnfocus } else { shouldUnfocus = (subjectOrContainerNode.subjectNode.Flag() == types.FlagTypeFocused) || shouldUnfocus } } if shouldUnfocus { if node.flag == types.FlagTypeFocused { node.flag = types.FlagTypeNone } return true } return node.flag == types.FlagTypeFocused } func (node *ContainerNode) Collate() []CollatedNodes { return node.collate([]*ContainerNode{}) } func (node *ContainerNode) collate(enclosingContainers []*ContainerNode) []CollatedNodes { collated := make([]CollatedNodes, 0) containers := make([]*ContainerNode, len(enclosingContainers)) copy(containers, enclosingContainers) containers = append(containers, node) for _, subjectOrContainer := range node.subjectAndContainerNodes { if subjectOrContainer.containerNode != nil { collated = append(collated, subjectOrContainer.containerNode.collate(containers)...) } else { collated = append(collated, CollatedNodes{ Containers: containers, Subject: subjectOrContainer.subjectNode, }) } } return collated } func (node *ContainerNode) PushContainerNode(container *ContainerNode) { node.subjectAndContainerNodes = append(node.subjectAndContainerNodes, subjectOrContainerNode{containerNode: container}) } func (node *ContainerNode) PushSubjectNode(subject leafnodes.SubjectNode) { node.subjectAndContainerNodes = append(node.subjectAndContainerNodes, subjectOrContainerNode{subjectNode: subject}) } func (node *ContainerNode) PushSetupNode(setupNode leafnodes.BasicNode) { node.setupNodes = append(node.setupNodes, setupNode) } func (node *ContainerNode) SetupNodesOfType(nodeType types.SpecComponentType) []leafnodes.BasicNode { nodes := []leafnodes.BasicNode{} for _, setupNode := range node.setupNodes { if setupNode.Type() == nodeType { nodes = append(nodes, setupNode) } } return nodes } func (node *ContainerNode) Text() string { return node.text } func (node *ContainerNode) CodeLocation() types.CodeLocation { return node.codeLocation } func (node *ContainerNode) Flag() types.FlagType { return node.flag } //sort.Interface func (node *ContainerNode) Len() int { return len(node.subjectAndContainerNodes) } func (node *ContainerNode) Less(i, j int) bool { return node.subjectAndContainerNodes[i].text() < node.subjectAndContainerNodes[j].text() } func (node *ContainerNode) Swap(i, j int) { node.subjectAndContainerNodes[i], node.subjectAndContainerNodes[j] = node.subjectAndContainerNodes[j], node.subjectAndContainerNodes[i] } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/failer/failer.go ================================================ package failer import ( "fmt" "sync" "github.com/onsi/ginkgo/types" ) type Failer struct { lock *sync.Mutex failure types.SpecFailure state types.SpecState } func New() *Failer { return &Failer{ lock: &sync.Mutex{}, state: types.SpecStatePassed, } } func (f *Failer) Panic(location types.CodeLocation, forwardedPanic interface{}) { f.lock.Lock() defer f.lock.Unlock() if f.state == types.SpecStatePassed { f.state = types.SpecStatePanicked f.failure = types.SpecFailure{ Message: "Test Panicked", Location: location, ForwardedPanic: fmt.Sprintf("%v", forwardedPanic), } } } func (f *Failer) Timeout(location types.CodeLocation) { f.lock.Lock() defer f.lock.Unlock() if f.state == types.SpecStatePassed { f.state = types.SpecStateTimedOut f.failure = types.SpecFailure{ Message: "Timed out", Location: location, } } } func (f *Failer) Fail(message string, location types.CodeLocation) { f.lock.Lock() defer f.lock.Unlock() if f.state == types.SpecStatePassed { f.state = types.SpecStateFailed f.failure = types.SpecFailure{ Message: message, Location: location, } } } func (f *Failer) Drain(componentType types.SpecComponentType, componentIndex int, componentCodeLocation types.CodeLocation) (types.SpecFailure, types.SpecState) { f.lock.Lock() defer f.lock.Unlock() failure := f.failure outcome := f.state if outcome != types.SpecStatePassed { failure.ComponentType = componentType failure.ComponentIndex = componentIndex failure.ComponentCodeLocation = componentCodeLocation } f.state = types.SpecStatePassed f.failure = types.SpecFailure{} return failure, outcome } func (f *Failer) Skip(message string, location types.CodeLocation) { f.lock.Lock() defer f.lock.Unlock() if f.state == types.SpecStatePassed { f.state = types.SpecStateSkipped f.failure = types.SpecFailure{ Message: message, Location: location, } } } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/global/init.go ================================================ package global import ( "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/internal/suite" ) const DefaultTimeout = time.Duration(1 * time.Second) var Suite *suite.Suite var Failer *failer.Failer func init() { InitializeGlobals() } func InitializeGlobals() { Failer = failer.New() Suite = suite.New(Failer) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/benchmarker.go ================================================ package leafnodes import ( "math" "time" "sync" "github.com/onsi/ginkgo/types" ) type benchmarker struct { mu sync.Mutex measurements map[string]*types.SpecMeasurement orderCounter int } func newBenchmarker() *benchmarker { return &benchmarker{ measurements: make(map[string]*types.SpecMeasurement), } } func (b *benchmarker) Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration) { t := time.Now() body() elapsedTime = time.Since(t) b.mu.Lock() defer b.mu.Unlock() measurement := b.getMeasurement(name, "Fastest Time", "Slowest Time", "Average Time", "s", 3, info...) measurement.Results = append(measurement.Results, elapsedTime.Seconds()) return } func (b *benchmarker) RecordValue(name string, value float64, info ...interface{}) { b.mu.Lock() measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", "", 3, info...) defer b.mu.Unlock() measurement.Results = append(measurement.Results, value) } func (b *benchmarker) RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{}) { b.mu.Lock() measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", units, precision, info...) defer b.mu.Unlock() measurement.Results = append(measurement.Results, value) } func (b *benchmarker) getMeasurement(name string, smallestLabel string, largestLabel string, averageLabel string, units string, precision int, info ...interface{}) *types.SpecMeasurement { measurement, ok := b.measurements[name] if !ok { var computedInfo interface{} computedInfo = nil if len(info) > 0 { computedInfo = info[0] } measurement = &types.SpecMeasurement{ Name: name, Info: computedInfo, Order: b.orderCounter, SmallestLabel: smallestLabel, LargestLabel: largestLabel, AverageLabel: averageLabel, Units: units, Precision: precision, Results: make([]float64, 0), } b.measurements[name] = measurement b.orderCounter++ } return measurement } func (b *benchmarker) measurementsReport() map[string]*types.SpecMeasurement { b.mu.Lock() defer b.mu.Unlock() for _, measurement := range b.measurements { measurement.Smallest = math.MaxFloat64 measurement.Largest = -math.MaxFloat64 sum := float64(0) sumOfSquares := float64(0) for _, result := range measurement.Results { if result > measurement.Largest { measurement.Largest = result } if result < measurement.Smallest { measurement.Smallest = result } sum += result sumOfSquares += result * result } n := float64(len(measurement.Results)) measurement.Average = sum / n measurement.StdDeviation = math.Sqrt(sumOfSquares/n - (sum/n)*(sum/n)) } return b.measurements } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/interfaces.go ================================================ package leafnodes import ( "github.com/onsi/ginkgo/types" ) type BasicNode interface { Type() types.SpecComponentType Run() (types.SpecState, types.SpecFailure) CodeLocation() types.CodeLocation } type SubjectNode interface { BasicNode Text() string Flag() types.FlagType Samples() int } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/it_node.go ================================================ package leafnodes import ( "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type ItNode struct { runner *runner flag types.FlagType text string } func NewItNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *ItNode { return &ItNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeIt, componentIndex), flag: flag, text: text, } } func (node *ItNode) Run() (outcome types.SpecState, failure types.SpecFailure) { return node.runner.run() } func (node *ItNode) Type() types.SpecComponentType { return types.SpecComponentTypeIt } func (node *ItNode) Text() string { return node.text } func (node *ItNode) Flag() types.FlagType { return node.flag } func (node *ItNode) CodeLocation() types.CodeLocation { return node.runner.codeLocation } func (node *ItNode) Samples() int { return 1 } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/measure_node.go ================================================ package leafnodes import ( "reflect" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type MeasureNode struct { runner *runner text string flag types.FlagType samples int benchmarker *benchmarker } func NewMeasureNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, samples int, failer *failer.Failer, componentIndex int) *MeasureNode { benchmarker := newBenchmarker() wrappedBody := func() { reflect.ValueOf(body).Call([]reflect.Value{reflect.ValueOf(benchmarker)}) } return &MeasureNode{ runner: newRunner(wrappedBody, codeLocation, 0, failer, types.SpecComponentTypeMeasure, componentIndex), text: text, flag: flag, samples: samples, benchmarker: benchmarker, } } func (node *MeasureNode) Run() (outcome types.SpecState, failure types.SpecFailure) { return node.runner.run() } func (node *MeasureNode) MeasurementsReport() map[string]*types.SpecMeasurement { return node.benchmarker.measurementsReport() } func (node *MeasureNode) Type() types.SpecComponentType { return types.SpecComponentTypeMeasure } func (node *MeasureNode) Text() string { return node.text } func (node *MeasureNode) Flag() types.FlagType { return node.flag } func (node *MeasureNode) CodeLocation() types.CodeLocation { return node.runner.codeLocation } func (node *MeasureNode) Samples() int { return node.samples } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go ================================================ package leafnodes import ( "fmt" "reflect" "time" "github.com/onsi/ginkgo/internal/codelocation" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type runner struct { isAsync bool asyncFunc func(chan<- interface{}) syncFunc func() codeLocation types.CodeLocation timeoutThreshold time.Duration nodeType types.SpecComponentType componentIndex int failer *failer.Failer } func newRunner(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, nodeType types.SpecComponentType, componentIndex int) *runner { bodyType := reflect.TypeOf(body) if bodyType.Kind() != reflect.Func { panic(fmt.Sprintf("Expected a function but got something else at %v", codeLocation)) } runner := &runner{ codeLocation: codeLocation, timeoutThreshold: timeout, failer: failer, nodeType: nodeType, componentIndex: componentIndex, } switch bodyType.NumIn() { case 0: runner.syncFunc = body.(func()) return runner case 1: if !(bodyType.In(0).Kind() == reflect.Chan && bodyType.In(0).Elem().Kind() == reflect.Interface) { panic(fmt.Sprintf("Must pass a Done channel to function at %v", codeLocation)) } wrappedBody := func(done chan<- interface{}) { bodyValue := reflect.ValueOf(body) bodyValue.Call([]reflect.Value{reflect.ValueOf(done)}) } runner.isAsync = true runner.asyncFunc = wrappedBody return runner } panic(fmt.Sprintf("Too many arguments to function at %v", codeLocation)) } func (r *runner) run() (outcome types.SpecState, failure types.SpecFailure) { if r.isAsync { return r.runAsync() } else { return r.runSync() } } func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) { done := make(chan interface{}, 1) go func() { finished := false defer func() { if e := recover(); e != nil || !finished { r.failer.Panic(codelocation.New(2), e) select { case <-done: break default: close(done) } } }() r.asyncFunc(done) finished = true }() // If this goroutine gets no CPU time before the select block, // the <-done case may complete even if the test took longer than the timeoutThreshold. // This can cause flaky behaviour, but we haven't seen it in the wild. select { case <-done: case <-time.After(r.timeoutThreshold): r.failer.Timeout(r.codeLocation) } failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation) return } func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) { finished := false defer func() { if e := recover(); e != nil || !finished { r.failer.Panic(codelocation.New(2), e) } failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation) }() r.syncFunc() finished = true return } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/setup_nodes.go ================================================ package leafnodes import ( "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type SetupNode struct { runner *runner } func (node *SetupNode) Run() (outcome types.SpecState, failure types.SpecFailure) { return node.runner.run() } func (node *SetupNode) Type() types.SpecComponentType { return node.runner.nodeType } func (node *SetupNode) CodeLocation() types.CodeLocation { return node.runner.codeLocation } func NewBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *SetupNode { return &SetupNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeBeforeEach, componentIndex), } } func NewAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *SetupNode { return &SetupNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeAfterEach, componentIndex), } } func NewJustBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *SetupNode { return &SetupNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeJustBeforeEach, componentIndex), } } func NewJustAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *SetupNode { return &SetupNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeJustAfterEach, componentIndex), } } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/suite_nodes.go ================================================ package leafnodes import ( "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type SuiteNode interface { Run(parallelNode int, parallelTotal int, syncHost string) bool Passed() bool Summary() *types.SetupSummary } type simpleSuiteNode struct { runner *runner outcome types.SpecState failure types.SpecFailure runTime time.Duration } func (node *simpleSuiteNode) Run(parallelNode int, parallelTotal int, syncHost string) bool { t := time.Now() node.outcome, node.failure = node.runner.run() node.runTime = time.Since(t) return node.outcome == types.SpecStatePassed } func (node *simpleSuiteNode) Passed() bool { return node.outcome == types.SpecStatePassed } func (node *simpleSuiteNode) Summary() *types.SetupSummary { return &types.SetupSummary{ ComponentType: node.runner.nodeType, CodeLocation: node.runner.codeLocation, State: node.outcome, RunTime: node.runTime, Failure: node.failure, } } func NewBeforeSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer) SuiteNode { return &simpleSuiteNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeBeforeSuite, 0), } } func NewAfterSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer) SuiteNode { return &simpleSuiteNode{ runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeAfterSuite, 0), } } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/synchronized_after_suite_node.go ================================================ package leafnodes import ( "encoding/json" "io/ioutil" "net/http" "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type synchronizedAfterSuiteNode struct { runnerA *runner runnerB *runner outcome types.SpecState failure types.SpecFailure runTime time.Duration } func NewSynchronizedAfterSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer) SuiteNode { return &synchronizedAfterSuiteNode{ runnerA: newRunner(bodyA, codeLocation, timeout, failer, types.SpecComponentTypeAfterSuite, 0), runnerB: newRunner(bodyB, codeLocation, timeout, failer, types.SpecComponentTypeAfterSuite, 0), } } func (node *synchronizedAfterSuiteNode) Run(parallelNode int, parallelTotal int, syncHost string) bool { node.outcome, node.failure = node.runnerA.run() if parallelNode == 1 { if parallelTotal > 1 { node.waitUntilOtherNodesAreDone(syncHost) } outcome, failure := node.runnerB.run() if node.outcome == types.SpecStatePassed { node.outcome, node.failure = outcome, failure } } return node.outcome == types.SpecStatePassed } func (node *synchronizedAfterSuiteNode) Passed() bool { return node.outcome == types.SpecStatePassed } func (node *synchronizedAfterSuiteNode) Summary() *types.SetupSummary { return &types.SetupSummary{ ComponentType: node.runnerA.nodeType, CodeLocation: node.runnerA.codeLocation, State: node.outcome, RunTime: node.runTime, Failure: node.failure, } } func (node *synchronizedAfterSuiteNode) waitUntilOtherNodesAreDone(syncHost string) { for { if node.canRun(syncHost) { return } time.Sleep(50 * time.Millisecond) } } func (node *synchronizedAfterSuiteNode) canRun(syncHost string) bool { resp, err := http.Get(syncHost + "/RemoteAfterSuiteData") if err != nil || resp.StatusCode != http.StatusOK { return false } body, err := ioutil.ReadAll(resp.Body) if err != nil { return false } resp.Body.Close() afterSuiteData := types.RemoteAfterSuiteData{} err = json.Unmarshal(body, &afterSuiteData) if err != nil { return false } return afterSuiteData.CanRun } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/leafnodes/synchronized_before_suite_node.go ================================================ package leafnodes import ( "bytes" "encoding/json" "io/ioutil" "net/http" "reflect" "time" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/types" ) type synchronizedBeforeSuiteNode struct { runnerA *runner runnerB *runner data []byte outcome types.SpecState failure types.SpecFailure runTime time.Duration } func NewSynchronizedBeforeSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer) SuiteNode { node := &synchronizedBeforeSuiteNode{} node.runnerA = newRunner(node.wrapA(bodyA), codeLocation, timeout, failer, types.SpecComponentTypeBeforeSuite, 0) node.runnerB = newRunner(node.wrapB(bodyB), codeLocation, timeout, failer, types.SpecComponentTypeBeforeSuite, 0) return node } func (node *synchronizedBeforeSuiteNode) Run(parallelNode int, parallelTotal int, syncHost string) bool { t := time.Now() defer func() { node.runTime = time.Since(t) }() if parallelNode == 1 { node.outcome, node.failure = node.runA(parallelTotal, syncHost) } else { node.outcome, node.failure = node.waitForA(syncHost) } if node.outcome != types.SpecStatePassed { return false } node.outcome, node.failure = node.runnerB.run() return node.outcome == types.SpecStatePassed } func (node *synchronizedBeforeSuiteNode) runA(parallelTotal int, syncHost string) (types.SpecState, types.SpecFailure) { outcome, failure := node.runnerA.run() if parallelTotal > 1 { state := types.RemoteBeforeSuiteStatePassed if outcome != types.SpecStatePassed { state = types.RemoteBeforeSuiteStateFailed } json := (types.RemoteBeforeSuiteData{ Data: node.data, State: state, }).ToJSON() http.Post(syncHost+"/BeforeSuiteState", "application/json", bytes.NewBuffer(json)) } return outcome, failure } func (node *synchronizedBeforeSuiteNode) waitForA(syncHost string) (types.SpecState, types.SpecFailure) { failure := func(message string) types.SpecFailure { return types.SpecFailure{ Message: message, Location: node.runnerA.codeLocation, ComponentType: node.runnerA.nodeType, ComponentIndex: node.runnerA.componentIndex, ComponentCodeLocation: node.runnerA.codeLocation, } } for { resp, err := http.Get(syncHost + "/BeforeSuiteState") if err != nil || resp.StatusCode != http.StatusOK { return types.SpecStateFailed, failure("Failed to fetch BeforeSuite state") } body, err := ioutil.ReadAll(resp.Body) if err != nil { return types.SpecStateFailed, failure("Failed to read BeforeSuite state") } resp.Body.Close() beforeSuiteData := types.RemoteBeforeSuiteData{} err = json.Unmarshal(body, &beforeSuiteData) if err != nil { return types.SpecStateFailed, failure("Failed to decode BeforeSuite state") } switch beforeSuiteData.State { case types.RemoteBeforeSuiteStatePassed: node.data = beforeSuiteData.Data return types.SpecStatePassed, types.SpecFailure{} case types.RemoteBeforeSuiteStateFailed: return types.SpecStateFailed, failure("BeforeSuite on Node 1 failed") case types.RemoteBeforeSuiteStateDisappeared: return types.SpecStateFailed, failure("Node 1 disappeared before completing BeforeSuite") } time.Sleep(50 * time.Millisecond) } } func (node *synchronizedBeforeSuiteNode) Passed() bool { return node.outcome == types.SpecStatePassed } func (node *synchronizedBeforeSuiteNode) Summary() *types.SetupSummary { return &types.SetupSummary{ ComponentType: node.runnerA.nodeType, CodeLocation: node.runnerA.codeLocation, State: node.outcome, RunTime: node.runTime, Failure: node.failure, } } func (node *synchronizedBeforeSuiteNode) wrapA(bodyA interface{}) interface{} { typeA := reflect.TypeOf(bodyA) if typeA.Kind() != reflect.Func { panic("SynchronizedBeforeSuite expects a function as its first argument") } takesNothing := typeA.NumIn() == 0 takesADoneChannel := typeA.NumIn() == 1 && typeA.In(0).Kind() == reflect.Chan && typeA.In(0).Elem().Kind() == reflect.Interface returnsBytes := typeA.NumOut() == 1 && typeA.Out(0).Kind() == reflect.Slice && typeA.Out(0).Elem().Kind() == reflect.Uint8 if !((takesNothing || takesADoneChannel) && returnsBytes) { panic("SynchronizedBeforeSuite's first argument should be a function that returns []byte and either takes no arguments or takes a Done channel.") } if takesADoneChannel { return func(done chan<- interface{}) { out := reflect.ValueOf(bodyA).Call([]reflect.Value{reflect.ValueOf(done)}) node.data = out[0].Interface().([]byte) } } return func() { out := reflect.ValueOf(bodyA).Call([]reflect.Value{}) node.data = out[0].Interface().([]byte) } } func (node *synchronizedBeforeSuiteNode) wrapB(bodyB interface{}) interface{} { typeB := reflect.TypeOf(bodyB) if typeB.Kind() != reflect.Func { panic("SynchronizedBeforeSuite expects a function as its second argument") } returnsNothing := typeB.NumOut() == 0 takesBytesOnly := typeB.NumIn() == 1 && typeB.In(0).Kind() == reflect.Slice && typeB.In(0).Elem().Kind() == reflect.Uint8 takesBytesAndDone := typeB.NumIn() == 2 && typeB.In(0).Kind() == reflect.Slice && typeB.In(0).Elem().Kind() == reflect.Uint8 && typeB.In(1).Kind() == reflect.Chan && typeB.In(1).Elem().Kind() == reflect.Interface if !((takesBytesOnly || takesBytesAndDone) && returnsNothing) { panic("SynchronizedBeforeSuite's second argument should be a function that returns nothing and either takes []byte or ([]byte, Done)") } if takesBytesAndDone { return func(done chan<- interface{}) { reflect.ValueOf(bodyB).Call([]reflect.Value{reflect.ValueOf(node.data), reflect.ValueOf(done)}) } } return func() { reflect.ValueOf(bodyB).Call([]reflect.Value{reflect.ValueOf(node.data)}) } } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/aggregator.go ================================================ /* Aggregator is a reporter used by the Ginkgo CLI to aggregate and present parallel test output coherently as tests complete. You shouldn't need to use this in your code. To run tests in parallel: ginkgo -nodes=N where N is the number of nodes you desire. */ package remote import ( "time" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/reporters/stenographer" "github.com/onsi/ginkgo/types" ) type configAndSuite struct { config config.GinkgoConfigType summary *types.SuiteSummary } type Aggregator struct { nodeCount int config config.DefaultReporterConfigType stenographer stenographer.Stenographer result chan bool suiteBeginnings chan configAndSuite aggregatedSuiteBeginnings []configAndSuite beforeSuites chan *types.SetupSummary aggregatedBeforeSuites []*types.SetupSummary afterSuites chan *types.SetupSummary aggregatedAfterSuites []*types.SetupSummary specCompletions chan *types.SpecSummary completedSpecs []*types.SpecSummary suiteEndings chan *types.SuiteSummary aggregatedSuiteEndings []*types.SuiteSummary specs []*types.SpecSummary startTime time.Time } func NewAggregator(nodeCount int, result chan bool, config config.DefaultReporterConfigType, stenographer stenographer.Stenographer) *Aggregator { aggregator := &Aggregator{ nodeCount: nodeCount, result: result, config: config, stenographer: stenographer, suiteBeginnings: make(chan configAndSuite), beforeSuites: make(chan *types.SetupSummary), afterSuites: make(chan *types.SetupSummary), specCompletions: make(chan *types.SpecSummary), suiteEndings: make(chan *types.SuiteSummary), } go aggregator.mux() return aggregator } func (aggregator *Aggregator) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { aggregator.suiteBeginnings <- configAndSuite{config, summary} } func (aggregator *Aggregator) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { aggregator.beforeSuites <- setupSummary } func (aggregator *Aggregator) AfterSuiteDidRun(setupSummary *types.SetupSummary) { aggregator.afterSuites <- setupSummary } func (aggregator *Aggregator) SpecWillRun(specSummary *types.SpecSummary) { //noop } func (aggregator *Aggregator) SpecDidComplete(specSummary *types.SpecSummary) { aggregator.specCompletions <- specSummary } func (aggregator *Aggregator) SpecSuiteDidEnd(summary *types.SuiteSummary) { aggregator.suiteEndings <- summary } func (aggregator *Aggregator) mux() { loop: for { select { case configAndSuite := <-aggregator.suiteBeginnings: aggregator.registerSuiteBeginning(configAndSuite) case setupSummary := <-aggregator.beforeSuites: aggregator.registerBeforeSuite(setupSummary) case setupSummary := <-aggregator.afterSuites: aggregator.registerAfterSuite(setupSummary) case specSummary := <-aggregator.specCompletions: aggregator.registerSpecCompletion(specSummary) case suite := <-aggregator.suiteEndings: finished, passed := aggregator.registerSuiteEnding(suite) if finished { aggregator.result <- passed break loop } } } } func (aggregator *Aggregator) registerSuiteBeginning(configAndSuite configAndSuite) { aggregator.aggregatedSuiteBeginnings = append(aggregator.aggregatedSuiteBeginnings, configAndSuite) if len(aggregator.aggregatedSuiteBeginnings) == 1 { aggregator.startTime = time.Now() } if len(aggregator.aggregatedSuiteBeginnings) != aggregator.nodeCount { return } aggregator.stenographer.AnnounceSuite(configAndSuite.summary.SuiteDescription, configAndSuite.config.RandomSeed, configAndSuite.config.RandomizeAllSpecs, aggregator.config.Succinct) totalNumberOfSpecs := 0 if len(aggregator.aggregatedSuiteBeginnings) > 0 { totalNumberOfSpecs = configAndSuite.summary.NumberOfSpecsBeforeParallelization } aggregator.stenographer.AnnounceTotalNumberOfSpecs(totalNumberOfSpecs, aggregator.config.Succinct) aggregator.stenographer.AnnounceAggregatedParallelRun(aggregator.nodeCount, aggregator.config.Succinct) aggregator.flushCompletedSpecs() } func (aggregator *Aggregator) registerBeforeSuite(setupSummary *types.SetupSummary) { aggregator.aggregatedBeforeSuites = append(aggregator.aggregatedBeforeSuites, setupSummary) aggregator.flushCompletedSpecs() } func (aggregator *Aggregator) registerAfterSuite(setupSummary *types.SetupSummary) { aggregator.aggregatedAfterSuites = append(aggregator.aggregatedAfterSuites, setupSummary) aggregator.flushCompletedSpecs() } func (aggregator *Aggregator) registerSpecCompletion(specSummary *types.SpecSummary) { aggregator.completedSpecs = append(aggregator.completedSpecs, specSummary) aggregator.specs = append(aggregator.specs, specSummary) aggregator.flushCompletedSpecs() } func (aggregator *Aggregator) flushCompletedSpecs() { if len(aggregator.aggregatedSuiteBeginnings) != aggregator.nodeCount { return } for _, setupSummary := range aggregator.aggregatedBeforeSuites { aggregator.announceBeforeSuite(setupSummary) } for _, specSummary := range aggregator.completedSpecs { aggregator.announceSpec(specSummary) } for _, setupSummary := range aggregator.aggregatedAfterSuites { aggregator.announceAfterSuite(setupSummary) } aggregator.aggregatedBeforeSuites = []*types.SetupSummary{} aggregator.completedSpecs = []*types.SpecSummary{} aggregator.aggregatedAfterSuites = []*types.SetupSummary{} } func (aggregator *Aggregator) announceBeforeSuite(setupSummary *types.SetupSummary) { aggregator.stenographer.AnnounceCapturedOutput(setupSummary.CapturedOutput) if setupSummary.State != types.SpecStatePassed { aggregator.stenographer.AnnounceBeforeSuiteFailure(setupSummary, aggregator.config.Succinct, aggregator.config.FullTrace) } } func (aggregator *Aggregator) announceAfterSuite(setupSummary *types.SetupSummary) { aggregator.stenographer.AnnounceCapturedOutput(setupSummary.CapturedOutput) if setupSummary.State != types.SpecStatePassed { aggregator.stenographer.AnnounceAfterSuiteFailure(setupSummary, aggregator.config.Succinct, aggregator.config.FullTrace) } } func (aggregator *Aggregator) announceSpec(specSummary *types.SpecSummary) { if aggregator.config.Verbose && specSummary.State != types.SpecStatePending && specSummary.State != types.SpecStateSkipped { aggregator.stenographer.AnnounceSpecWillRun(specSummary) } aggregator.stenographer.AnnounceCapturedOutput(specSummary.CapturedOutput) switch specSummary.State { case types.SpecStatePassed: if specSummary.IsMeasurement { aggregator.stenographer.AnnounceSuccessfulMeasurement(specSummary, aggregator.config.Succinct) } else if specSummary.RunTime.Seconds() >= aggregator.config.SlowSpecThreshold { aggregator.stenographer.AnnounceSuccessfulSlowSpec(specSummary, aggregator.config.Succinct) } else { aggregator.stenographer.AnnounceSuccessfulSpec(specSummary) } case types.SpecStatePending: aggregator.stenographer.AnnouncePendingSpec(specSummary, aggregator.config.NoisyPendings && !aggregator.config.Succinct) case types.SpecStateSkipped: aggregator.stenographer.AnnounceSkippedSpec(specSummary, aggregator.config.Succinct || !aggregator.config.NoisySkippings, aggregator.config.FullTrace) case types.SpecStateTimedOut: aggregator.stenographer.AnnounceSpecTimedOut(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace) case types.SpecStatePanicked: aggregator.stenographer.AnnounceSpecPanicked(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace) case types.SpecStateFailed: aggregator.stenographer.AnnounceSpecFailed(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace) } } func (aggregator *Aggregator) registerSuiteEnding(suite *types.SuiteSummary) (finished bool, passed bool) { aggregator.aggregatedSuiteEndings = append(aggregator.aggregatedSuiteEndings, suite) if len(aggregator.aggregatedSuiteEndings) < aggregator.nodeCount { return false, false } aggregatedSuiteSummary := &types.SuiteSummary{} aggregatedSuiteSummary.SuiteSucceeded = true for _, suiteSummary := range aggregator.aggregatedSuiteEndings { if !suiteSummary.SuiteSucceeded { aggregatedSuiteSummary.SuiteSucceeded = false } aggregatedSuiteSummary.NumberOfSpecsThatWillBeRun += suiteSummary.NumberOfSpecsThatWillBeRun aggregatedSuiteSummary.NumberOfTotalSpecs += suiteSummary.NumberOfTotalSpecs aggregatedSuiteSummary.NumberOfPassedSpecs += suiteSummary.NumberOfPassedSpecs aggregatedSuiteSummary.NumberOfFailedSpecs += suiteSummary.NumberOfFailedSpecs aggregatedSuiteSummary.NumberOfPendingSpecs += suiteSummary.NumberOfPendingSpecs aggregatedSuiteSummary.NumberOfSkippedSpecs += suiteSummary.NumberOfSkippedSpecs aggregatedSuiteSummary.NumberOfFlakedSpecs += suiteSummary.NumberOfFlakedSpecs } aggregatedSuiteSummary.RunTime = time.Since(aggregator.startTime) aggregator.stenographer.SummarizeFailures(aggregator.specs) aggregator.stenographer.AnnounceSpecRunCompletion(aggregatedSuiteSummary, aggregator.config.Succinct) return true, aggregatedSuiteSummary.SuiteSucceeded } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/forwarding_reporter.go ================================================ package remote import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os" "github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/reporters/stenographer" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/types" ) //An interface to net/http's client to allow the injection of fakes under test type Poster interface { Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error) } /* The ForwardingReporter is a Ginkgo reporter that forwards information to a Ginkgo remote server. When streaming parallel test output, this repoter is automatically installed by Ginkgo. This is accomplished by passing in the GINKGO_REMOTE_REPORTING_SERVER environment variable to `go test`, the Ginkgo test runner detects this environment variable (which should contain the host of the server) and automatically installs a ForwardingReporter in place of Ginkgo's DefaultReporter. */ type ForwardingReporter struct { serverHost string poster Poster outputInterceptor OutputInterceptor debugMode bool debugFile *os.File nestedReporter *reporters.DefaultReporter } func NewForwardingReporter(config config.DefaultReporterConfigType, serverHost string, poster Poster, outputInterceptor OutputInterceptor, ginkgoWriter *writer.Writer, debugFile string) *ForwardingReporter { reporter := &ForwardingReporter{ serverHost: serverHost, poster: poster, outputInterceptor: outputInterceptor, } if debugFile != "" { var err error reporter.debugMode = true reporter.debugFile, err = os.Create(debugFile) if err != nil { fmt.Println(err.Error()) os.Exit(1) } if !config.Verbose { //if verbose is true then the GinkgoWriter emits to stdout. Don't _also_ redirect GinkgoWriter output as that will result in duplication. ginkgoWriter.AndRedirectTo(reporter.debugFile) } outputInterceptor.StreamTo(reporter.debugFile) //This is not working stenographer := stenographer.New(false, true, reporter.debugFile) config.Succinct = false config.Verbose = true config.FullTrace = true reporter.nestedReporter = reporters.NewDefaultReporter(config, stenographer) } return reporter } func (reporter *ForwardingReporter) post(path string, data interface{}) { encoded, _ := json.Marshal(data) buffer := bytes.NewBuffer(encoded) reporter.poster.Post(reporter.serverHost+path, "application/json", buffer) } func (reporter *ForwardingReporter) SpecSuiteWillBegin(conf config.GinkgoConfigType, summary *types.SuiteSummary) { data := struct { Config config.GinkgoConfigType `json:"config"` Summary *types.SuiteSummary `json:"suite-summary"` }{ conf, summary, } reporter.outputInterceptor.StartInterceptingOutput() if reporter.debugMode { reporter.nestedReporter.SpecSuiteWillBegin(conf, summary) reporter.debugFile.Sync() } reporter.post("/SpecSuiteWillBegin", data) } func (reporter *ForwardingReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput() reporter.outputInterceptor.StartInterceptingOutput() setupSummary.CapturedOutput = output if reporter.debugMode { reporter.nestedReporter.BeforeSuiteDidRun(setupSummary) reporter.debugFile.Sync() } reporter.post("/BeforeSuiteDidRun", setupSummary) } func (reporter *ForwardingReporter) SpecWillRun(specSummary *types.SpecSummary) { if reporter.debugMode { reporter.nestedReporter.SpecWillRun(specSummary) reporter.debugFile.Sync() } reporter.post("/SpecWillRun", specSummary) } func (reporter *ForwardingReporter) SpecDidComplete(specSummary *types.SpecSummary) { output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput() reporter.outputInterceptor.StartInterceptingOutput() specSummary.CapturedOutput = output if reporter.debugMode { reporter.nestedReporter.SpecDidComplete(specSummary) reporter.debugFile.Sync() } reporter.post("/SpecDidComplete", specSummary) } func (reporter *ForwardingReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput() reporter.outputInterceptor.StartInterceptingOutput() setupSummary.CapturedOutput = output if reporter.debugMode { reporter.nestedReporter.AfterSuiteDidRun(setupSummary) reporter.debugFile.Sync() } reporter.post("/AfterSuiteDidRun", setupSummary) } func (reporter *ForwardingReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { reporter.outputInterceptor.StopInterceptingAndReturnOutput() if reporter.debugMode { reporter.nestedReporter.SpecSuiteDidEnd(summary) reporter.debugFile.Sync() } reporter.post("/SpecSuiteDidEnd", summary) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor.go ================================================ package remote import "os" /* The OutputInterceptor is used by the ForwardingReporter to intercept and capture all stdin and stderr output during a test run. */ type OutputInterceptor interface { StartInterceptingOutput() error StopInterceptingAndReturnOutput() (string, error) StreamTo(*os.File) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_unix.go ================================================ // +build freebsd openbsd netbsd dragonfly darwin linux solaris package remote import ( "errors" "io/ioutil" "os" "github.com/nxadm/tail" "golang.org/x/sys/unix" ) func NewOutputInterceptor() OutputInterceptor { return &outputInterceptor{} } type outputInterceptor struct { redirectFile *os.File streamTarget *os.File intercepting bool tailer *tail.Tail doneTailing chan bool } func (interceptor *outputInterceptor) StartInterceptingOutput() error { if interceptor.intercepting { return errors.New("Already intercepting output!") } interceptor.intercepting = true var err error interceptor.redirectFile, err = ioutil.TempFile("", "ginkgo-output") if err != nil { return err } // This might call Dup3 if the dup2 syscall is not available, e.g. on // linux/arm64 or linux/riscv64 unix.Dup2(int(interceptor.redirectFile.Fd()), 1) unix.Dup2(int(interceptor.redirectFile.Fd()), 2) if interceptor.streamTarget != nil { interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true}) interceptor.doneTailing = make(chan bool) go func() { for line := range interceptor.tailer.Lines { interceptor.streamTarget.Write([]byte(line.Text + "\n")) } close(interceptor.doneTailing) }() } return nil } func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string, error) { if !interceptor.intercepting { return "", errors.New("Not intercepting output!") } interceptor.redirectFile.Close() output, err := ioutil.ReadFile(interceptor.redirectFile.Name()) os.Remove(interceptor.redirectFile.Name()) interceptor.intercepting = false if interceptor.streamTarget != nil { interceptor.tailer.Stop() interceptor.tailer.Cleanup() <-interceptor.doneTailing interceptor.streamTarget.Sync() } return string(output), err } func (interceptor *outputInterceptor) StreamTo(out *os.File) { interceptor.streamTarget = out } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_win.go ================================================ // +build windows package remote import ( "errors" "os" ) func NewOutputInterceptor() OutputInterceptor { return &outputInterceptor{} } type outputInterceptor struct { intercepting bool } func (interceptor *outputInterceptor) StartInterceptingOutput() error { if interceptor.intercepting { return errors.New("Already intercepting output!") } interceptor.intercepting = true // not working on windows... return nil } func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string, error) { // not working on windows... interceptor.intercepting = false return "", nil } func (interceptor *outputInterceptor) StreamTo(*os.File) {} ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/remote/server.go ================================================ /* The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners. This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser). */ package remote import ( "encoding/json" "io/ioutil" "net" "net/http" "sync" "github.com/onsi/ginkgo/internal/spec_iterator" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/types" ) /* Server spins up on an automatically selected port and listens for communication from the forwarding reporter. It then forwards that communication to attached reporters. */ type Server struct { listener net.Listener reporters []reporters.Reporter alives []func() bool lock *sync.Mutex beforeSuiteData types.RemoteBeforeSuiteData parallelTotal int counter int } //Create a new server, automatically selecting a port func NewServer(parallelTotal int) (*Server, error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, err } return &Server{ listener: listener, lock: &sync.Mutex{}, alives: make([]func() bool, parallelTotal), beforeSuiteData: types.RemoteBeforeSuiteData{Data: nil, State: types.RemoteBeforeSuiteStatePending}, parallelTotal: parallelTotal, }, nil } //Start the server. You don't need to `go s.Start()`, just `s.Start()` func (server *Server) Start() { httpServer := &http.Server{} mux := http.NewServeMux() httpServer.Handler = mux //streaming endpoints mux.HandleFunc("/SpecSuiteWillBegin", server.specSuiteWillBegin) mux.HandleFunc("/BeforeSuiteDidRun", server.beforeSuiteDidRun) mux.HandleFunc("/AfterSuiteDidRun", server.afterSuiteDidRun) mux.HandleFunc("/SpecWillRun", server.specWillRun) mux.HandleFunc("/SpecDidComplete", server.specDidComplete) mux.HandleFunc("/SpecSuiteDidEnd", server.specSuiteDidEnd) //synchronization endpoints mux.HandleFunc("/BeforeSuiteState", server.handleBeforeSuiteState) mux.HandleFunc("/RemoteAfterSuiteData", server.handleRemoteAfterSuiteData) mux.HandleFunc("/counter", server.handleCounter) mux.HandleFunc("/has-counter", server.handleHasCounter) //for backward compatibility go httpServer.Serve(server.listener) } //Stop the server func (server *Server) Close() { server.listener.Close() } //The address the server can be reached it. Pass this into the `ForwardingReporter`. func (server *Server) Address() string { return "http://" + server.listener.Addr().String() } // // Streaming Endpoints // //The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters` func (server *Server) readAll(request *http.Request) []byte { defer request.Body.Close() body, _ := ioutil.ReadAll(request.Body) return body } func (server *Server) RegisterReporters(reporters ...reporters.Reporter) { server.reporters = reporters } func (server *Server) specSuiteWillBegin(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var data struct { Config config.GinkgoConfigType `json:"config"` Summary *types.SuiteSummary `json:"suite-summary"` } json.Unmarshal(body, &data) for _, reporter := range server.reporters { reporter.SpecSuiteWillBegin(data.Config, data.Summary) } } func (server *Server) beforeSuiteDidRun(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var setupSummary *types.SetupSummary json.Unmarshal(body, &setupSummary) for _, reporter := range server.reporters { reporter.BeforeSuiteDidRun(setupSummary) } } func (server *Server) afterSuiteDidRun(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var setupSummary *types.SetupSummary json.Unmarshal(body, &setupSummary) for _, reporter := range server.reporters { reporter.AfterSuiteDidRun(setupSummary) } } func (server *Server) specWillRun(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var specSummary *types.SpecSummary json.Unmarshal(body, &specSummary) for _, reporter := range server.reporters { reporter.SpecWillRun(specSummary) } } func (server *Server) specDidComplete(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var specSummary *types.SpecSummary json.Unmarshal(body, &specSummary) for _, reporter := range server.reporters { reporter.SpecDidComplete(specSummary) } } func (server *Server) specSuiteDidEnd(writer http.ResponseWriter, request *http.Request) { body := server.readAll(request) var suiteSummary *types.SuiteSummary json.Unmarshal(body, &suiteSummary) for _, reporter := range server.reporters { reporter.SpecSuiteDidEnd(suiteSummary) } } // // Synchronization Endpoints // func (server *Server) RegisterAlive(node int, alive func() bool) { server.lock.Lock() defer server.lock.Unlock() server.alives[node-1] = alive } func (server *Server) nodeIsAlive(node int) bool { server.lock.Lock() defer server.lock.Unlock() alive := server.alives[node-1] if alive == nil { return true } return alive() } func (server *Server) handleBeforeSuiteState(writer http.ResponseWriter, request *http.Request) { if request.Method == "POST" { dec := json.NewDecoder(request.Body) dec.Decode(&(server.beforeSuiteData)) } else { beforeSuiteData := server.beforeSuiteData if beforeSuiteData.State == types.RemoteBeforeSuiteStatePending && !server.nodeIsAlive(1) { beforeSuiteData.State = types.RemoteBeforeSuiteStateDisappeared } enc := json.NewEncoder(writer) enc.Encode(beforeSuiteData) } } func (server *Server) handleRemoteAfterSuiteData(writer http.ResponseWriter, request *http.Request) { afterSuiteData := types.RemoteAfterSuiteData{ CanRun: true, } for i := 2; i <= server.parallelTotal; i++ { afterSuiteData.CanRun = afterSuiteData.CanRun && !server.nodeIsAlive(i) } enc := json.NewEncoder(writer) enc.Encode(afterSuiteData) } func (server *Server) handleCounter(writer http.ResponseWriter, request *http.Request) { c := spec_iterator.Counter{} server.lock.Lock() c.Index = server.counter server.counter++ server.lock.Unlock() json.NewEncoder(writer).Encode(c) } func (server *Server) handleHasCounter(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("")) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec/spec.go ================================================ package spec import ( "fmt" "io" "time" "sync" "github.com/onsi/ginkgo/internal/containernode" "github.com/onsi/ginkgo/internal/leafnodes" "github.com/onsi/ginkgo/types" ) type Spec struct { subject leafnodes.SubjectNode focused bool announceProgress bool containers []*containernode.ContainerNode state types.SpecState runTime time.Duration startTime time.Time failure types.SpecFailure previousFailures bool stateMutex *sync.Mutex } func New(subject leafnodes.SubjectNode, containers []*containernode.ContainerNode, announceProgress bool) *Spec { spec := &Spec{ subject: subject, containers: containers, focused: subject.Flag() == types.FlagTypeFocused, announceProgress: announceProgress, stateMutex: &sync.Mutex{}, } spec.processFlag(subject.Flag()) for i := len(containers) - 1; i >= 0; i-- { spec.processFlag(containers[i].Flag()) } return spec } func (spec *Spec) processFlag(flag types.FlagType) { if flag == types.FlagTypeFocused { spec.focused = true } else if flag == types.FlagTypePending { spec.setState(types.SpecStatePending) } } func (spec *Spec) Skip() { spec.setState(types.SpecStateSkipped) } func (spec *Spec) Failed() bool { return spec.getState() == types.SpecStateFailed || spec.getState() == types.SpecStatePanicked || spec.getState() == types.SpecStateTimedOut } func (spec *Spec) Passed() bool { return spec.getState() == types.SpecStatePassed } func (spec *Spec) Flaked() bool { return spec.getState() == types.SpecStatePassed && spec.previousFailures } func (spec *Spec) Pending() bool { return spec.getState() == types.SpecStatePending } func (spec *Spec) Skipped() bool { return spec.getState() == types.SpecStateSkipped } func (spec *Spec) Focused() bool { return spec.focused } func (spec *Spec) IsMeasurement() bool { return spec.subject.Type() == types.SpecComponentTypeMeasure } func (spec *Spec) Summary(suiteID string) *types.SpecSummary { componentTexts := make([]string, len(spec.containers)+1) componentCodeLocations := make([]types.CodeLocation, len(spec.containers)+1) for i, container := range spec.containers { componentTexts[i] = container.Text() componentCodeLocations[i] = container.CodeLocation() } componentTexts[len(spec.containers)] = spec.subject.Text() componentCodeLocations[len(spec.containers)] = spec.subject.CodeLocation() runTime := spec.runTime if runTime == 0 && !spec.startTime.IsZero() { runTime = time.Since(spec.startTime) } return &types.SpecSummary{ IsMeasurement: spec.IsMeasurement(), NumberOfSamples: spec.subject.Samples(), ComponentTexts: componentTexts, ComponentCodeLocations: componentCodeLocations, State: spec.getState(), RunTime: runTime, Failure: spec.failure, Measurements: spec.measurementsReport(), SuiteID: suiteID, } } func (spec *Spec) ConcatenatedString() string { s := "" for _, container := range spec.containers { s += container.Text() + " " } return s + spec.subject.Text() } func (spec *Spec) Run(writer io.Writer) { if spec.getState() == types.SpecStateFailed { spec.previousFailures = true } spec.startTime = time.Now() defer func() { spec.runTime = time.Since(spec.startTime) }() for sample := 0; sample < spec.subject.Samples(); sample++ { spec.runSample(sample, writer) if spec.getState() != types.SpecStatePassed { return } } } func (spec *Spec) getState() types.SpecState { spec.stateMutex.Lock() defer spec.stateMutex.Unlock() return spec.state } func (spec *Spec) setState(state types.SpecState) { spec.stateMutex.Lock() defer spec.stateMutex.Unlock() spec.state = state } func (spec *Spec) runSample(sample int, writer io.Writer) { spec.setState(types.SpecStatePassed) spec.failure = types.SpecFailure{} innerMostContainerIndexToUnwind := -1 defer func() { for i := innerMostContainerIndexToUnwind; i >= 0; i-- { container := spec.containers[i] for _, justAfterEach := range container.SetupNodesOfType(types.SpecComponentTypeJustAfterEach) { spec.announceSetupNode(writer, "JustAfterEach", container, justAfterEach) justAfterEachState, justAfterEachFailure := justAfterEach.Run() if justAfterEachState != types.SpecStatePassed && spec.state == types.SpecStatePassed { spec.state = justAfterEachState spec.failure = justAfterEachFailure } } } for i := innerMostContainerIndexToUnwind; i >= 0; i-- { container := spec.containers[i] for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) { spec.announceSetupNode(writer, "AfterEach", container, afterEach) afterEachState, afterEachFailure := afterEach.Run() if afterEachState != types.SpecStatePassed && spec.getState() == types.SpecStatePassed { spec.setState(afterEachState) spec.failure = afterEachFailure } } } }() for i, container := range spec.containers { innerMostContainerIndexToUnwind = i for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) { spec.announceSetupNode(writer, "BeforeEach", container, beforeEach) s, f := beforeEach.Run() spec.failure = f spec.setState(s) if spec.getState() != types.SpecStatePassed { return } } } for _, container := range spec.containers { for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) { spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach) s, f := justBeforeEach.Run() spec.failure = f spec.setState(s) if spec.getState() != types.SpecStatePassed { return } } } spec.announceSubject(writer, spec.subject) s, f := spec.subject.Run() spec.failure = f spec.setState(s) } func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) { if spec.announceProgress { s := fmt.Sprintf("[%s] %s\n %s\n", nodeType, container.Text(), setupNode.CodeLocation().String()) writer.Write([]byte(s)) } } func (spec *Spec) announceSubject(writer io.Writer, subject leafnodes.SubjectNode) { if spec.announceProgress { nodeType := "" switch subject.Type() { case types.SpecComponentTypeIt: nodeType = "It" case types.SpecComponentTypeMeasure: nodeType = "Measure" } s := fmt.Sprintf("[%s] %s\n %s\n", nodeType, subject.Text(), subject.CodeLocation().String()) writer.Write([]byte(s)) } } func (spec *Spec) measurementsReport() map[string]*types.SpecMeasurement { if !spec.IsMeasurement() || spec.Failed() { return map[string]*types.SpecMeasurement{} } return spec.subject.(*leafnodes.MeasureNode).MeasurementsReport() } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec/specs.go ================================================ package spec import ( "math/rand" "regexp" "sort" "strings" ) type Specs struct { specs []*Spec names []string hasProgrammaticFocus bool RegexScansFilePath bool } func NewSpecs(specs []*Spec) *Specs { names := make([]string, len(specs)) for i, spec := range specs { names[i] = spec.ConcatenatedString() } return &Specs{ specs: specs, names: names, } } func (e *Specs) Specs() []*Spec { return e.specs } func (e *Specs) HasProgrammaticFocus() bool { return e.hasProgrammaticFocus } func (e *Specs) Shuffle(r *rand.Rand) { sort.Sort(e) permutation := r.Perm(len(e.specs)) shuffledSpecs := make([]*Spec, len(e.specs)) names := make([]string, len(e.specs)) for i, j := range permutation { shuffledSpecs[i] = e.specs[j] names[i] = e.names[j] } e.specs = shuffledSpecs e.names = names } func (e *Specs) ApplyFocus(description string, focus, skip []string) { if len(focus)+len(skip) == 0 { e.applyProgrammaticFocus() } else { e.applyRegExpFocusAndSkip(description, focus, skip) } } func (e *Specs) applyProgrammaticFocus() { e.hasProgrammaticFocus = false for _, spec := range e.specs { if spec.Focused() && !spec.Pending() { e.hasProgrammaticFocus = true break } } if e.hasProgrammaticFocus { for _, spec := range e.specs { if !spec.Focused() { spec.Skip() } } } } // toMatch returns a byte[] to be used by regex matchers. When adding new behaviours to the matching function, // this is the place which we append to. func (e *Specs) toMatch(description string, i int) []byte { if i > len(e.names) { return nil } if e.RegexScansFilePath { return []byte( description + " " + e.names[i] + " " + e.specs[i].subject.CodeLocation().FileName) } else { return []byte( description + " " + e.names[i]) } } func (e *Specs) applyRegExpFocusAndSkip(description string, focus, skip []string) { var focusFilter, skipFilter *regexp.Regexp if len(focus) > 0 { focusFilter = regexp.MustCompile(strings.Join(focus, "|")) } if len(skip) > 0 { skipFilter = regexp.MustCompile(strings.Join(skip, "|")) } for i, spec := range e.specs { matchesFocus := true matchesSkip := false toMatch := e.toMatch(description, i) if focusFilter != nil { matchesFocus = focusFilter.Match(toMatch) } if skipFilter != nil { matchesSkip = skipFilter.Match(toMatch) } if !matchesFocus || matchesSkip { spec.Skip() } } } func (e *Specs) SkipMeasurements() { for _, spec := range e.specs { if spec.IsMeasurement() { spec.Skip() } } } //sort.Interface func (e *Specs) Len() int { return len(e.specs) } func (e *Specs) Less(i, j int) bool { return e.names[i] < e.names[j] } func (e *Specs) Swap(i, j int) { e.names[i], e.names[j] = e.names[j], e.names[i] e.specs[i], e.specs[j] = e.specs[j], e.specs[i] } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec_iterator/index_computer.go ================================================ package spec_iterator func ParallelizedIndexRange(length int, parallelTotal int, parallelNode int) (startIndex int, count int) { if length == 0 { return 0, 0 } // We have more nodes than tests. Trivial case. if parallelTotal >= length { if parallelNode > length { return 0, 0 } else { return parallelNode - 1, 1 } } // This is the minimum amount of tests that a node will be required to run minTestsPerNode := length / parallelTotal // This is the maximum amount of tests that a node will be required to run // The algorithm guarantees that this would be equal to at least the minimum amount // and at most one more maxTestsPerNode := minTestsPerNode if length%parallelTotal != 0 { maxTestsPerNode++ } // Number of nodes that will have to run the maximum amount of tests per node numMaxLoadNodes := length % parallelTotal // Number of nodes that precede the current node and will have to run the maximum amount of tests per node var numPrecedingMaxLoadNodes int if parallelNode > numMaxLoadNodes { numPrecedingMaxLoadNodes = numMaxLoadNodes } else { numPrecedingMaxLoadNodes = parallelNode - 1 } // Number of nodes that precede the current node and will have to run the minimum amount of tests per node var numPrecedingMinLoadNodes int if parallelNode <= numMaxLoadNodes { numPrecedingMinLoadNodes = 0 } else { numPrecedingMinLoadNodes = parallelNode - numMaxLoadNodes - 1 } // Evaluate the test start index and number of tests to run startIndex = numPrecedingMaxLoadNodes*maxTestsPerNode + numPrecedingMinLoadNodes*minTestsPerNode if parallelNode > numMaxLoadNodes { count = minTestsPerNode } else { count = maxTestsPerNode } return } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec_iterator/parallel_spec_iterator.go ================================================ package spec_iterator import ( "encoding/json" "fmt" "net/http" "github.com/onsi/ginkgo/internal/spec" ) type ParallelIterator struct { specs []*spec.Spec host string client *http.Client } func NewParallelIterator(specs []*spec.Spec, host string) *ParallelIterator { return &ParallelIterator{ specs: specs, host: host, client: &http.Client{}, } } func (s *ParallelIterator) Next() (*spec.Spec, error) { resp, err := s.client.Get(s.host + "/counter") if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode) } var counter Counter err = json.NewDecoder(resp.Body).Decode(&counter) if err != nil { return nil, err } if counter.Index >= len(s.specs) { return nil, ErrClosed } return s.specs[counter.Index], nil } func (s *ParallelIterator) NumberOfSpecsPriorToIteration() int { return len(s.specs) } func (s *ParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) { return -1, false } func (s *ParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) { return -1, false } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec_iterator/serial_spec_iterator.go ================================================ package spec_iterator import ( "github.com/onsi/ginkgo/internal/spec" ) type SerialIterator struct { specs []*spec.Spec index int } func NewSerialIterator(specs []*spec.Spec) *SerialIterator { return &SerialIterator{ specs: specs, index: 0, } } func (s *SerialIterator) Next() (*spec.Spec, error) { if s.index >= len(s.specs) { return nil, ErrClosed } spec := s.specs[s.index] s.index += 1 return spec, nil } func (s *SerialIterator) NumberOfSpecsPriorToIteration() int { return len(s.specs) } func (s *SerialIterator) NumberOfSpecsToProcessIfKnown() (int, bool) { return len(s.specs), true } func (s *SerialIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) { count := 0 for _, s := range s.specs { if !s.Skipped() && !s.Pending() { count += 1 } } return count, true } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec_iterator/sharded_parallel_spec_iterator.go ================================================ package spec_iterator import "github.com/onsi/ginkgo/internal/spec" type ShardedParallelIterator struct { specs []*spec.Spec index int maxIndex int } func NewShardedParallelIterator(specs []*spec.Spec, total int, node int) *ShardedParallelIterator { startIndex, count := ParallelizedIndexRange(len(specs), total, node) return &ShardedParallelIterator{ specs: specs, index: startIndex, maxIndex: startIndex + count, } } func (s *ShardedParallelIterator) Next() (*spec.Spec, error) { if s.index >= s.maxIndex { return nil, ErrClosed } spec := s.specs[s.index] s.index += 1 return spec, nil } func (s *ShardedParallelIterator) NumberOfSpecsPriorToIteration() int { return len(s.specs) } func (s *ShardedParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) { return s.maxIndex - s.index, true } func (s *ShardedParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) { count := 0 for i := s.index; i < s.maxIndex; i += 1 { if !s.specs[i].Skipped() && !s.specs[i].Pending() { count += 1 } } return count, true } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/spec_iterator/spec_iterator.go ================================================ package spec_iterator import ( "errors" "github.com/onsi/ginkgo/internal/spec" ) var ErrClosed = errors.New("no more specs to run") type SpecIterator interface { Next() (*spec.Spec, error) NumberOfSpecsPriorToIteration() int NumberOfSpecsToProcessIfKnown() (int, bool) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) } type Counter struct { Index int `json:"index"` } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/specrunner/random_id.go ================================================ package specrunner import ( "crypto/rand" "fmt" ) func randomID() string { b := make([]byte, 8) _, err := rand.Read(b) if err != nil { return "" } return fmt.Sprintf("%x-%x-%x-%x", b[0:2], b[2:4], b[4:6], b[6:8]) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go ================================================ package specrunner import ( "fmt" "os" "os/signal" "sync" "syscall" "github.com/onsi/ginkgo/internal/spec_iterator" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/internal/leafnodes" "github.com/onsi/ginkgo/internal/spec" Writer "github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/types" "time" ) type SpecRunner struct { description string beforeSuiteNode leafnodes.SuiteNode iterator spec_iterator.SpecIterator afterSuiteNode leafnodes.SuiteNode reporters []reporters.Reporter startTime time.Time suiteID string runningSpec *spec.Spec writer Writer.WriterInterface config config.GinkgoConfigType interrupted bool processedSpecs []*spec.Spec lock *sync.Mutex } func New(description string, beforeSuiteNode leafnodes.SuiteNode, iterator spec_iterator.SpecIterator, afterSuiteNode leafnodes.SuiteNode, reporters []reporters.Reporter, writer Writer.WriterInterface, config config.GinkgoConfigType) *SpecRunner { return &SpecRunner{ description: description, beforeSuiteNode: beforeSuiteNode, iterator: iterator, afterSuiteNode: afterSuiteNode, reporters: reporters, writer: writer, config: config, suiteID: randomID(), lock: &sync.Mutex{}, } } func (runner *SpecRunner) Run() bool { if runner.config.DryRun { runner.performDryRun() return true } runner.reportSuiteWillBegin() signalRegistered := make(chan struct{}) go runner.registerForInterrupts(signalRegistered) <-signalRegistered suitePassed := runner.runBeforeSuite() if suitePassed { suitePassed = runner.runSpecs() } runner.blockForeverIfInterrupted() suitePassed = runner.runAfterSuite() && suitePassed runner.reportSuiteDidEnd(suitePassed) return suitePassed } func (runner *SpecRunner) performDryRun() { runner.reportSuiteWillBegin() if runner.beforeSuiteNode != nil { summary := runner.beforeSuiteNode.Summary() summary.State = types.SpecStatePassed runner.reportBeforeSuite(summary) } for { spec, err := runner.iterator.Next() if err == spec_iterator.ErrClosed { break } if err != nil { fmt.Println("failed to iterate over tests:\n" + err.Error()) break } runner.processedSpecs = append(runner.processedSpecs, spec) summary := spec.Summary(runner.suiteID) runner.reportSpecWillRun(summary) if summary.State == types.SpecStateInvalid { summary.State = types.SpecStatePassed } runner.reportSpecDidComplete(summary, false) } if runner.afterSuiteNode != nil { summary := runner.afterSuiteNode.Summary() summary.State = types.SpecStatePassed runner.reportAfterSuite(summary) } runner.reportSuiteDidEnd(true) } func (runner *SpecRunner) runBeforeSuite() bool { if runner.beforeSuiteNode == nil || runner.wasInterrupted() { return true } runner.writer.Truncate() conf := runner.config passed := runner.beforeSuiteNode.Run(conf.ParallelNode, conf.ParallelTotal, conf.SyncHost) if !passed { runner.writer.DumpOut() } runner.reportBeforeSuite(runner.beforeSuiteNode.Summary()) return passed } func (runner *SpecRunner) runAfterSuite() bool { if runner.afterSuiteNode == nil { return true } runner.writer.Truncate() conf := runner.config passed := runner.afterSuiteNode.Run(conf.ParallelNode, conf.ParallelTotal, conf.SyncHost) if !passed { runner.writer.DumpOut() } runner.reportAfterSuite(runner.afterSuiteNode.Summary()) return passed } func (runner *SpecRunner) runSpecs() bool { suiteFailed := false skipRemainingSpecs := false for { spec, err := runner.iterator.Next() if err == spec_iterator.ErrClosed { break } if err != nil { fmt.Println("failed to iterate over tests:\n" + err.Error()) suiteFailed = true break } runner.processedSpecs = append(runner.processedSpecs, spec) if runner.wasInterrupted() { break } if skipRemainingSpecs { spec.Skip() } if !spec.Skipped() && !spec.Pending() { if passed := runner.runSpec(spec); !passed { suiteFailed = true } } else if spec.Pending() && runner.config.FailOnPending { runner.reportSpecWillRun(spec.Summary(runner.suiteID)) suiteFailed = true runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed()) } else { runner.reportSpecWillRun(spec.Summary(runner.suiteID)) runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed()) } if spec.Failed() && runner.config.FailFast { skipRemainingSpecs = true } } return !suiteFailed } func (runner *SpecRunner) runSpec(spec *spec.Spec) (passed bool) { maxAttempts := 1 if runner.config.FlakeAttempts > 0 { // uninitialized configs count as 1 maxAttempts = runner.config.FlakeAttempts } for i := 0; i < maxAttempts; i++ { runner.reportSpecWillRun(spec.Summary(runner.suiteID)) runner.runningSpec = spec spec.Run(runner.writer) runner.runningSpec = nil runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed()) if !spec.Failed() { return true } } return false } func (runner *SpecRunner) CurrentSpecSummary() (*types.SpecSummary, bool) { if runner.runningSpec == nil { return nil, false } return runner.runningSpec.Summary(runner.suiteID), true } func (runner *SpecRunner) registerForInterrupts(signalRegistered chan struct{}) { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) close(signalRegistered) <-c signal.Stop(c) runner.markInterrupted() go runner.registerForHardInterrupts() runner.writer.DumpOutWithHeader(` Received interrupt. Emitting contents of GinkgoWriter... --------------------------------------------------------- `) if runner.afterSuiteNode != nil { fmt.Fprint(os.Stderr, ` --------------------------------------------------------- Received interrupt. Running AfterSuite... ^C again to terminate immediately `) runner.runAfterSuite() } runner.reportSuiteDidEnd(false) os.Exit(1) } func (runner *SpecRunner) registerForHardInterrupts() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c fmt.Fprintln(os.Stderr, "\nReceived second interrupt. Shutting down.") os.Exit(1) } func (runner *SpecRunner) blockForeverIfInterrupted() { runner.lock.Lock() interrupted := runner.interrupted runner.lock.Unlock() if interrupted { select {} } } func (runner *SpecRunner) markInterrupted() { runner.lock.Lock() defer runner.lock.Unlock() runner.interrupted = true } func (runner *SpecRunner) wasInterrupted() bool { runner.lock.Lock() defer runner.lock.Unlock() return runner.interrupted } func (runner *SpecRunner) reportSuiteWillBegin() { runner.startTime = time.Now() summary := runner.suiteWillBeginSummary() for _, reporter := range runner.reporters { reporter.SpecSuiteWillBegin(runner.config, summary) } } func (runner *SpecRunner) reportBeforeSuite(summary *types.SetupSummary) { for _, reporter := range runner.reporters { reporter.BeforeSuiteDidRun(summary) } } func (runner *SpecRunner) reportAfterSuite(summary *types.SetupSummary) { for _, reporter := range runner.reporters { reporter.AfterSuiteDidRun(summary) } } func (runner *SpecRunner) reportSpecWillRun(summary *types.SpecSummary) { runner.writer.Truncate() for _, reporter := range runner.reporters { reporter.SpecWillRun(summary) } } func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, failed bool) { if len(summary.CapturedOutput) == 0 { summary.CapturedOutput = string(runner.writer.Bytes()) } for i := len(runner.reporters) - 1; i >= 1; i-- { runner.reporters[i].SpecDidComplete(summary) } if failed { runner.writer.DumpOut() } runner.reporters[0].SpecDidComplete(summary) } func (runner *SpecRunner) reportSuiteDidEnd(success bool) { summary := runner.suiteDidEndSummary(success) summary.RunTime = time.Since(runner.startTime) for _, reporter := range runner.reporters { reporter.SpecSuiteDidEnd(summary) } } func (runner *SpecRunner) countSpecsThatRanSatisfying(filter func(ex *spec.Spec) bool) (count int) { count = 0 for _, spec := range runner.processedSpecs { if filter(spec) { count++ } } return count } func (runner *SpecRunner) suiteDidEndSummary(success bool) *types.SuiteSummary { numberOfSpecsThatWillBeRun := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return !ex.Skipped() && !ex.Pending() }) numberOfPendingSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return ex.Pending() }) numberOfSkippedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return ex.Skipped() }) numberOfPassedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return ex.Passed() }) numberOfFlakedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return ex.Flaked() }) numberOfFailedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool { return ex.Failed() }) if runner.beforeSuiteNode != nil && !runner.beforeSuiteNode.Passed() && !runner.config.DryRun { var known bool numberOfSpecsThatWillBeRun, known = runner.iterator.NumberOfSpecsThatWillBeRunIfKnown() if !known { numberOfSpecsThatWillBeRun = runner.iterator.NumberOfSpecsPriorToIteration() } numberOfFailedSpecs = numberOfSpecsThatWillBeRun } return &types.SuiteSummary{ SuiteDescription: runner.description, SuiteSucceeded: success, SuiteID: runner.suiteID, NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(), NumberOfTotalSpecs: len(runner.processedSpecs), NumberOfSpecsThatWillBeRun: numberOfSpecsThatWillBeRun, NumberOfPendingSpecs: numberOfPendingSpecs, NumberOfSkippedSpecs: numberOfSkippedSpecs, NumberOfPassedSpecs: numberOfPassedSpecs, NumberOfFailedSpecs: numberOfFailedSpecs, NumberOfFlakedSpecs: numberOfFlakedSpecs, } } func (runner *SpecRunner) suiteWillBeginSummary() *types.SuiteSummary { numTotal, known := runner.iterator.NumberOfSpecsToProcessIfKnown() if !known { numTotal = -1 } numToRun, known := runner.iterator.NumberOfSpecsThatWillBeRunIfKnown() if !known { numToRun = -1 } return &types.SuiteSummary{ SuiteDescription: runner.description, SuiteID: runner.suiteID, NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(), NumberOfTotalSpecs: numTotal, NumberOfSpecsThatWillBeRun: numToRun, NumberOfPendingSpecs: -1, NumberOfSkippedSpecs: -1, NumberOfPassedSpecs: -1, NumberOfFailedSpecs: -1, NumberOfFlakedSpecs: -1, } } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/suite/suite.go ================================================ package suite import ( "math/rand" "net/http" "time" "github.com/onsi/ginkgo/internal/spec_iterator" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/internal/containernode" "github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/internal/leafnodes" "github.com/onsi/ginkgo/internal/spec" "github.com/onsi/ginkgo/internal/specrunner" "github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/types" ) type ginkgoTestingT interface { Fail() } type deferredContainerNode struct { text string body func() flag types.FlagType codeLocation types.CodeLocation } type Suite struct { topLevelContainer *containernode.ContainerNode currentContainer *containernode.ContainerNode deferredContainerNodes []deferredContainerNode containerIndex int beforeSuiteNode leafnodes.SuiteNode afterSuiteNode leafnodes.SuiteNode runner *specrunner.SpecRunner failer *failer.Failer running bool expandTopLevelNodes bool } func New(failer *failer.Failer) *Suite { topLevelContainer := containernode.New("[Top Level]", types.FlagTypeNone, types.CodeLocation{}) return &Suite{ topLevelContainer: topLevelContainer, currentContainer: topLevelContainer, failer: failer, containerIndex: 1, deferredContainerNodes: []deferredContainerNode{}, } } func (suite *Suite) Run(t ginkgoTestingT, description string, reporters []reporters.Reporter, writer writer.WriterInterface, config config.GinkgoConfigType) (bool, bool) { if config.ParallelTotal < 1 { panic("ginkgo.parallel.total must be >= 1") } if config.ParallelNode > config.ParallelTotal || config.ParallelNode < 1 { panic("ginkgo.parallel.node is one-indexed and must be <= ginkgo.parallel.total") } suite.expandTopLevelNodes = true for _, deferredNode := range suite.deferredContainerNodes { suite.PushContainerNode(deferredNode.text, deferredNode.body, deferredNode.flag, deferredNode.codeLocation) } r := rand.New(rand.NewSource(config.RandomSeed)) suite.topLevelContainer.Shuffle(r) iterator, hasProgrammaticFocus := suite.generateSpecsIterator(description, config) suite.runner = specrunner.New(description, suite.beforeSuiteNode, iterator, suite.afterSuiteNode, reporters, writer, config) suite.running = true success := suite.runner.Run() if !success { t.Fail() } return success, hasProgrammaticFocus } func (suite *Suite) generateSpecsIterator(description string, config config.GinkgoConfigType) (spec_iterator.SpecIterator, bool) { specsSlice := []*spec.Spec{} suite.topLevelContainer.BackPropagateProgrammaticFocus() for _, collatedNodes := range suite.topLevelContainer.Collate() { specsSlice = append(specsSlice, spec.New(collatedNodes.Subject, collatedNodes.Containers, config.EmitSpecProgress)) } specs := spec.NewSpecs(specsSlice) specs.RegexScansFilePath = config.RegexScansFilePath if config.RandomizeAllSpecs { specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed))) } specs.ApplyFocus(description, config.FocusStrings, config.SkipStrings) if config.SkipMeasurements { specs.SkipMeasurements() } var iterator spec_iterator.SpecIterator if config.ParallelTotal > 1 { iterator = spec_iterator.NewParallelIterator(specs.Specs(), config.SyncHost) resp, err := http.Get(config.SyncHost + "/has-counter") if err != nil || resp.StatusCode != http.StatusOK { iterator = spec_iterator.NewShardedParallelIterator(specs.Specs(), config.ParallelTotal, config.ParallelNode) } } else { iterator = spec_iterator.NewSerialIterator(specs.Specs()) } return iterator, specs.HasProgrammaticFocus() } func (suite *Suite) CurrentRunningSpecSummary() (*types.SpecSummary, bool) { if !suite.running { return nil, false } return suite.runner.CurrentSpecSummary() } func (suite *Suite) SetBeforeSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.beforeSuiteNode != nil { panic("You may only call BeforeSuite once!") } suite.beforeSuiteNode = leafnodes.NewBeforeSuiteNode(body, codeLocation, timeout, suite.failer) } func (suite *Suite) SetAfterSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.afterSuiteNode != nil { panic("You may only call AfterSuite once!") } suite.afterSuiteNode = leafnodes.NewAfterSuiteNode(body, codeLocation, timeout, suite.failer) } func (suite *Suite) SetSynchronizedBeforeSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.beforeSuiteNode != nil { panic("You may only call BeforeSuite once!") } suite.beforeSuiteNode = leafnodes.NewSynchronizedBeforeSuiteNode(bodyA, bodyB, codeLocation, timeout, suite.failer) } func (suite *Suite) SetSynchronizedAfterSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.afterSuiteNode != nil { panic("You may only call AfterSuite once!") } suite.afterSuiteNode = leafnodes.NewSynchronizedAfterSuiteNode(bodyA, bodyB, codeLocation, timeout, suite.failer) } func (suite *Suite) PushContainerNode(text string, body func(), flag types.FlagType, codeLocation types.CodeLocation) { /* We defer walking the container nodes (which immediately evaluates the `body` function) until `RunSpecs` is called. We do this by storing off the deferred container nodes. Then, when `RunSpecs` is called we actually go through and add the container nodes to the test structure. This allows us to defer calling all the `body` functions until _after_ the top level functions have been walked, _after_ func init()s have been called, and _after_ `go test` has called `flag.Parse()`. This allows users to load up configuration information in the `TestX` go test hook just before `RunSpecs` is invoked and solves issues like #693 and makes the lifecycle easier to reason about. */ if !suite.expandTopLevelNodes { suite.deferredContainerNodes = append(suite.deferredContainerNodes, deferredContainerNode{text, body, flag, codeLocation}) return } container := containernode.New(text, flag, codeLocation) suite.currentContainer.PushContainerNode(container) previousContainer := suite.currentContainer suite.currentContainer = container suite.containerIndex++ body() suite.containerIndex-- suite.currentContainer = previousContainer } func (suite *Suite) PushItNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, timeout time.Duration) { if suite.running { suite.failer.Fail("You may only call It from within a Describe, Context or When", codeLocation) } suite.currentContainer.PushSubjectNode(leafnodes.NewItNode(text, body, flag, codeLocation, timeout, suite.failer, suite.containerIndex)) } func (suite *Suite) PushMeasureNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, samples int) { if suite.running { suite.failer.Fail("You may only call Measure from within a Describe, Context or When", codeLocation) } suite.currentContainer.PushSubjectNode(leafnodes.NewMeasureNode(text, body, flag, codeLocation, samples, suite.failer, suite.containerIndex)) } func (suite *Suite) PushBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.running { suite.failer.Fail("You may only call BeforeEach from within a Describe, Context or When", codeLocation) } suite.currentContainer.PushSetupNode(leafnodes.NewBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex)) } func (suite *Suite) PushJustBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.running { suite.failer.Fail("You may only call JustBeforeEach from within a Describe, Context or When", codeLocation) } suite.currentContainer.PushSetupNode(leafnodes.NewJustBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex)) } func (suite *Suite) PushJustAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.running { suite.failer.Fail("You may only call JustAfterEach from within a Describe or Context", codeLocation) } suite.currentContainer.PushSetupNode(leafnodes.NewJustAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex)) } func (suite *Suite) PushAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) { if suite.running { suite.failer.Fail("You may only call AfterEach from within a Describe, Context or When", codeLocation) } suite.currentContainer.PushSetupNode(leafnodes.NewAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex)) } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go ================================================ package testingtproxy import ( "fmt" "io" ) type failFunc func(message string, callerSkip ...int) type skipFunc func(message string, callerSkip ...int) type failedFunc func() bool type nameFunc func() string func New(writer io.Writer, fail failFunc, skip skipFunc, failed failedFunc, name nameFunc, offset int) *ginkgoTestingTProxy { return &ginkgoTestingTProxy{ fail: fail, offset: offset, writer: writer, skip: skip, failed: failed, name: name, } } type ginkgoTestingTProxy struct { fail failFunc skip skipFunc failed failedFunc name nameFunc offset int writer io.Writer } func (t *ginkgoTestingTProxy) Cleanup(func()) { // No-op } func (t *ginkgoTestingTProxy) Setenv(kev, value string) { fmt.Println("Setenv is a noop for Ginkgo at the moment but will be implemented in V2") // No-op until Cleanup is implemented } func (t *ginkgoTestingTProxy) Error(args ...interface{}) { t.fail(fmt.Sprintln(args...), t.offset) } func (t *ginkgoTestingTProxy) Errorf(format string, args ...interface{}) { t.fail(fmt.Sprintf(format, args...), t.offset) } func (t *ginkgoTestingTProxy) Fail() { t.fail("failed", t.offset) } func (t *ginkgoTestingTProxy) FailNow() { t.fail("failed", t.offset) } func (t *ginkgoTestingTProxy) Failed() bool { return t.failed() } func (t *ginkgoTestingTProxy) Fatal(args ...interface{}) { t.fail(fmt.Sprintln(args...), t.offset) } func (t *ginkgoTestingTProxy) Fatalf(format string, args ...interface{}) { t.fail(fmt.Sprintf(format, args...), t.offset) } func (t *ginkgoTestingTProxy) Helper() { // No-op } func (t *ginkgoTestingTProxy) Log(args ...interface{}) { fmt.Fprintln(t.writer, args...) } func (t *ginkgoTestingTProxy) Logf(format string, args ...interface{}) { t.Log(fmt.Sprintf(format, args...)) } func (t *ginkgoTestingTProxy) Name() string { return t.name() } func (t *ginkgoTestingTProxy) Parallel() { // No-op } func (t *ginkgoTestingTProxy) Skip(args ...interface{}) { t.skip(fmt.Sprintln(args...), t.offset) } func (t *ginkgoTestingTProxy) SkipNow() { t.skip("skip", t.offset) } func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) { t.skip(fmt.Sprintf(format, args...), t.offset) } func (t *ginkgoTestingTProxy) Skipped() bool { return false } func (t *ginkgoTestingTProxy) TempDir() string { // No-op return "" } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/writer/fake_writer.go ================================================ package writer type FakeGinkgoWriter struct { EventStream []string } func NewFake() *FakeGinkgoWriter { return &FakeGinkgoWriter{ EventStream: []string{}, } } func (writer *FakeGinkgoWriter) AddEvent(event string) { writer.EventStream = append(writer.EventStream, event) } func (writer *FakeGinkgoWriter) Truncate() { writer.EventStream = append(writer.EventStream, "TRUNCATE") } func (writer *FakeGinkgoWriter) DumpOut() { writer.EventStream = append(writer.EventStream, "DUMP") } func (writer *FakeGinkgoWriter) DumpOutWithHeader(header string) { writer.EventStream = append(writer.EventStream, "DUMP_WITH_HEADER: "+header) } func (writer *FakeGinkgoWriter) Bytes() []byte { writer.EventStream = append(writer.EventStream, "BYTES") return nil } func (writer *FakeGinkgoWriter) Write(data []byte) (n int, err error) { return 0, nil } ================================================ FILE: vendor/github.com/onsi/ginkgo/internal/writer/writer.go ================================================ package writer import ( "bytes" "io" "sync" ) type WriterInterface interface { io.Writer Truncate() DumpOut() DumpOutWithHeader(header string) Bytes() []byte } type Writer struct { buffer *bytes.Buffer outWriter io.Writer lock *sync.Mutex stream bool redirector io.Writer } func New(outWriter io.Writer) *Writer { return &Writer{ buffer: &bytes.Buffer{}, lock: &sync.Mutex{}, outWriter: outWriter, stream: true, } } func (w *Writer) AndRedirectTo(writer io.Writer) { w.redirector = writer } func (w *Writer) SetStream(stream bool) { w.lock.Lock() defer w.lock.Unlock() w.stream = stream } func (w *Writer) Write(b []byte) (n int, err error) { w.lock.Lock() defer w.lock.Unlock() n, err = w.buffer.Write(b) if w.redirector != nil { w.redirector.Write(b) } if w.stream { return w.outWriter.Write(b) } return n, err } func (w *Writer) Truncate() { w.lock.Lock() defer w.lock.Unlock() w.buffer.Reset() } func (w *Writer) DumpOut() { w.lock.Lock() defer w.lock.Unlock() if !w.stream { w.buffer.WriteTo(w.outWriter) } } func (w *Writer) Bytes() []byte { w.lock.Lock() defer w.lock.Unlock() b := w.buffer.Bytes() copied := make([]byte, len(b)) copy(copied, b) return copied } func (w *Writer) DumpOutWithHeader(header string) { w.lock.Lock() defer w.lock.Unlock() if !w.stream && w.buffer.Len() > 0 { w.outWriter.Write([]byte(header)) w.buffer.WriteTo(w.outWriter) } } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/default_reporter.go ================================================ /* Ginkgo's Default Reporter A number of command line flags are available to tweak Ginkgo's default output. These are documented [here](http://onsi.github.io/ginkgo/#running_tests) */ package reporters import ( "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/reporters/stenographer" "github.com/onsi/ginkgo/types" ) type DefaultReporter struct { config config.DefaultReporterConfigType stenographer stenographer.Stenographer specSummaries []*types.SpecSummary } func NewDefaultReporter(config config.DefaultReporterConfigType, stenographer stenographer.Stenographer) *DefaultReporter { return &DefaultReporter{ config: config, stenographer: stenographer, } } func (reporter *DefaultReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { reporter.stenographer.AnnounceSuite(summary.SuiteDescription, config.RandomSeed, config.RandomizeAllSpecs, reporter.config.Succinct) if config.ParallelTotal > 1 { reporter.stenographer.AnnounceParallelRun(config.ParallelNode, config.ParallelTotal, reporter.config.Succinct) } else { reporter.stenographer.AnnounceNumberOfSpecs(summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, reporter.config.Succinct) } } func (reporter *DefaultReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { if setupSummary.State != types.SpecStatePassed { reporter.stenographer.AnnounceBeforeSuiteFailure(setupSummary, reporter.config.Succinct, reporter.config.FullTrace) } } func (reporter *DefaultReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { if setupSummary.State != types.SpecStatePassed { reporter.stenographer.AnnounceAfterSuiteFailure(setupSummary, reporter.config.Succinct, reporter.config.FullTrace) } } func (reporter *DefaultReporter) SpecWillRun(specSummary *types.SpecSummary) { if reporter.config.Verbose && !reporter.config.Succinct && specSummary.State != types.SpecStatePending && specSummary.State != types.SpecStateSkipped { reporter.stenographer.AnnounceSpecWillRun(specSummary) } } func (reporter *DefaultReporter) SpecDidComplete(specSummary *types.SpecSummary) { switch specSummary.State { case types.SpecStatePassed: if specSummary.IsMeasurement { reporter.stenographer.AnnounceSuccessfulMeasurement(specSummary, reporter.config.Succinct) } else if specSummary.RunTime.Seconds() >= reporter.config.SlowSpecThreshold { reporter.stenographer.AnnounceSuccessfulSlowSpec(specSummary, reporter.config.Succinct) } else { reporter.stenographer.AnnounceSuccessfulSpec(specSummary) if reporter.config.ReportPassed { reporter.stenographer.AnnounceCapturedOutput(specSummary.CapturedOutput) } } case types.SpecStatePending: reporter.stenographer.AnnouncePendingSpec(specSummary, reporter.config.NoisyPendings && !reporter.config.Succinct) case types.SpecStateSkipped: reporter.stenographer.AnnounceSkippedSpec(specSummary, reporter.config.Succinct || !reporter.config.NoisySkippings, reporter.config.FullTrace) case types.SpecStateTimedOut: reporter.stenographer.AnnounceSpecTimedOut(specSummary, reporter.config.Succinct, reporter.config.FullTrace) case types.SpecStatePanicked: reporter.stenographer.AnnounceSpecPanicked(specSummary, reporter.config.Succinct, reporter.config.FullTrace) case types.SpecStateFailed: reporter.stenographer.AnnounceSpecFailed(specSummary, reporter.config.Succinct, reporter.config.FullTrace) } reporter.specSummaries = append(reporter.specSummaries, specSummary) } func (reporter *DefaultReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { reporter.stenographer.SummarizeFailures(reporter.specSummaries) reporter.stenographer.AnnounceSpecRunCompletion(summary, reporter.config.Succinct) } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/fake_reporter.go ================================================ package reporters import ( "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/types" ) //FakeReporter is useful for testing purposes type FakeReporter struct { Config config.GinkgoConfigType BeginSummary *types.SuiteSummary BeforeSuiteSummary *types.SetupSummary SpecWillRunSummaries []*types.SpecSummary SpecSummaries []*types.SpecSummary AfterSuiteSummary *types.SetupSummary EndSummary *types.SuiteSummary SpecWillRunStub func(specSummary *types.SpecSummary) SpecDidCompleteStub func(specSummary *types.SpecSummary) } func NewFakeReporter() *FakeReporter { return &FakeReporter{ SpecWillRunSummaries: make([]*types.SpecSummary, 0), SpecSummaries: make([]*types.SpecSummary, 0), } } func (fakeR *FakeReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { fakeR.Config = config fakeR.BeginSummary = summary } func (fakeR *FakeReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { fakeR.BeforeSuiteSummary = setupSummary } func (fakeR *FakeReporter) SpecWillRun(specSummary *types.SpecSummary) { if fakeR.SpecWillRunStub != nil { fakeR.SpecWillRunStub(specSummary) } fakeR.SpecWillRunSummaries = append(fakeR.SpecWillRunSummaries, specSummary) } func (fakeR *FakeReporter) SpecDidComplete(specSummary *types.SpecSummary) { if fakeR.SpecDidCompleteStub != nil { fakeR.SpecDidCompleteStub(specSummary) } fakeR.SpecSummaries = append(fakeR.SpecSummaries, specSummary) } func (fakeR *FakeReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { fakeR.AfterSuiteSummary = setupSummary } func (fakeR *FakeReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { fakeR.EndSummary = summary } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/junit_reporter.go ================================================ /* JUnit XML Reporter for Ginkgo For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output */ package reporters import ( "encoding/xml" "fmt" "math" "os" "path/filepath" "strings" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/types" ) type JUnitTestSuite struct { XMLName xml.Name `xml:"testsuite"` TestCases []JUnitTestCase `xml:"testcase"` Name string `xml:"name,attr"` Tests int `xml:"tests,attr"` Failures int `xml:"failures,attr"` Errors int `xml:"errors,attr"` Time float64 `xml:"time,attr"` } type JUnitTestCase struct { Name string `xml:"name,attr"` ClassName string `xml:"classname,attr"` FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"` Skipped *JUnitSkipped `xml:"skipped,omitempty"` Time float64 `xml:"time,attr"` SystemOut string `xml:"system-out,omitempty"` } type JUnitFailureMessage struct { Type string `xml:"type,attr"` Message string `xml:",chardata"` } type JUnitSkipped struct { Message string `xml:",chardata"` } type JUnitReporter struct { suite JUnitTestSuite filename string testSuiteName string ReporterConfig config.DefaultReporterConfigType } //NewJUnitReporter creates a new JUnit XML reporter. The XML will be stored in the passed in filename. func NewJUnitReporter(filename string) *JUnitReporter { return &JUnitReporter{ filename: filename, } } func (reporter *JUnitReporter) SpecSuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) { reporter.suite = JUnitTestSuite{ Name: summary.SuiteDescription, TestCases: []JUnitTestCase{}, } reporter.testSuiteName = summary.SuiteDescription reporter.ReporterConfig = config.DefaultReporterConfig } func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) { } func (reporter *JUnitReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { reporter.handleSetupSummary("BeforeSuite", setupSummary) } func (reporter *JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { reporter.handleSetupSummary("AfterSuite", setupSummary) } func failureMessage(failure types.SpecFailure) string { return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String()) } func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) { if setupSummary.State != types.SpecStatePassed { testCase := JUnitTestCase{ Name: name, ClassName: reporter.testSuiteName, } testCase.FailureMessage = &JUnitFailureMessage{ Type: reporter.failureTypeForState(setupSummary.State), Message: failureMessage(setupSummary.Failure), } testCase.SystemOut = setupSummary.CapturedOutput testCase.Time = setupSummary.RunTime.Seconds() reporter.suite.TestCases = append(reporter.suite.TestCases, testCase) } } func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) { testCase := JUnitTestCase{ Name: strings.Join(specSummary.ComponentTexts[1:], " "), ClassName: reporter.testSuiteName, } if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed { testCase.SystemOut = specSummary.CapturedOutput } if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { testCase.FailureMessage = &JUnitFailureMessage{ Type: reporter.failureTypeForState(specSummary.State), Message: failureMessage(specSummary.Failure), } if specSummary.State == types.SpecStatePanicked { testCase.FailureMessage.Message += fmt.Sprintf("\n\nPanic: %s\n\nFull stack:\n%s", specSummary.Failure.ForwardedPanic, specSummary.Failure.Location.FullStackTrace) } testCase.SystemOut = specSummary.CapturedOutput } if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending { testCase.Skipped = &JUnitSkipped{} if specSummary.Failure.Message != "" { testCase.Skipped.Message = failureMessage(specSummary.Failure) } } testCase.Time = specSummary.RunTime.Seconds() reporter.suite.TestCases = append(reporter.suite.TestCases, testCase) } func (reporter *JUnitReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun reporter.suite.Time = math.Trunc(summary.RunTime.Seconds()*1000) / 1000 reporter.suite.Failures = summary.NumberOfFailedSpecs reporter.suite.Errors = 0 if reporter.ReporterConfig.ReportFile != "" { reporter.filename = reporter.ReporterConfig.ReportFile fmt.Printf("\nJUnit path was configured: %s\n", reporter.filename) } filePath, _ := filepath.Abs(reporter.filename) dirPath := filepath.Dir(filePath) err := os.MkdirAll(dirPath, os.ModePerm) if err != nil { fmt.Printf("\nFailed to create JUnit directory: %s\n\t%s", filePath, err.Error()) } file, err := os.Create(filePath) if err != nil { fmt.Fprintf(os.Stderr, "Failed to create JUnit report file: %s\n\t%s", filePath, err.Error()) } defer file.Close() file.WriteString(xml.Header) encoder := xml.NewEncoder(file) encoder.Indent(" ", " ") err = encoder.Encode(reporter.suite) if err == nil { fmt.Fprintf(os.Stdout, "\nJUnit report was created: %s\n", filePath) } else { fmt.Fprintf(os.Stderr,"\nFailed to generate JUnit report data:\n\t%s", err.Error()) } } func (reporter *JUnitReporter) failureTypeForState(state types.SpecState) string { switch state { case types.SpecStateFailed: return "Failure" case types.SpecStateTimedOut: return "Timeout" case types.SpecStatePanicked: return "Panic" default: return "" } } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/reporter.go ================================================ package reporters import ( "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/types" ) type Reporter interface { SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) BeforeSuiteDidRun(setupSummary *types.SetupSummary) SpecWillRun(specSummary *types.SpecSummary) SpecDidComplete(specSummary *types.SpecSummary) AfterSuiteDidRun(setupSummary *types.SetupSummary) SpecSuiteDidEnd(summary *types.SuiteSummary) } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/console_logging.go ================================================ package stenographer import ( "fmt" "strings" ) func (s *consoleStenographer) colorize(colorCode string, format string, args ...interface{}) string { var out string if len(args) > 0 { out = fmt.Sprintf(format, args...) } else { out = format } if s.color { return fmt.Sprintf("%s%s%s", colorCode, out, defaultStyle) } else { return out } } func (s *consoleStenographer) printBanner(text string, bannerCharacter string) { fmt.Fprintln(s.w, text) fmt.Fprintln(s.w, strings.Repeat(bannerCharacter, len(text))) } func (s *consoleStenographer) printNewLine() { fmt.Fprintln(s.w, "") } func (s *consoleStenographer) printDelimiter() { fmt.Fprintln(s.w, s.colorize(grayColor, "%s", strings.Repeat("-", 30))) } func (s *consoleStenographer) print(indentation int, format string, args ...interface{}) { fmt.Fprint(s.w, s.indent(indentation, format, args...)) } func (s *consoleStenographer) println(indentation int, format string, args ...interface{}) { fmt.Fprintln(s.w, s.indent(indentation, format, args...)) } func (s *consoleStenographer) indent(indentation int, format string, args ...interface{}) string { var text string if len(args) > 0 { text = fmt.Sprintf(format, args...) } else { text = format } stringArray := strings.Split(text, "\n") padding := "" if indentation >= 0 { padding = strings.Repeat(" ", indentation) } for i, s := range stringArray { stringArray[i] = fmt.Sprintf("%s%s", padding, s) } return strings.Join(stringArray, "\n") } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/fake_stenographer.go ================================================ package stenographer import ( "sync" "github.com/onsi/ginkgo/types" ) func NewFakeStenographerCall(method string, args ...interface{}) FakeStenographerCall { return FakeStenographerCall{ Method: method, Args: args, } } type FakeStenographer struct { calls []FakeStenographerCall lock *sync.Mutex } type FakeStenographerCall struct { Method string Args []interface{} } func NewFakeStenographer() *FakeStenographer { stenographer := &FakeStenographer{ lock: &sync.Mutex{}, } stenographer.Reset() return stenographer } func (stenographer *FakeStenographer) Calls() []FakeStenographerCall { stenographer.lock.Lock() defer stenographer.lock.Unlock() return stenographer.calls } func (stenographer *FakeStenographer) Reset() { stenographer.lock.Lock() defer stenographer.lock.Unlock() stenographer.calls = make([]FakeStenographerCall, 0) } func (stenographer *FakeStenographer) CallsTo(method string) []FakeStenographerCall { stenographer.lock.Lock() defer stenographer.lock.Unlock() results := make([]FakeStenographerCall, 0) for _, call := range stenographer.calls { if call.Method == method { results = append(results, call) } } return results } func (stenographer *FakeStenographer) registerCall(method string, args ...interface{}) { stenographer.lock.Lock() defer stenographer.lock.Unlock() stenographer.calls = append(stenographer.calls, NewFakeStenographerCall(method, args...)) } func (stenographer *FakeStenographer) AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool) { stenographer.registerCall("AnnounceSuite", description, randomSeed, randomizingAll, succinct) } func (stenographer *FakeStenographer) AnnounceAggregatedParallelRun(nodes int, succinct bool) { stenographer.registerCall("AnnounceAggregatedParallelRun", nodes, succinct) } func (stenographer *FakeStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) { stenographer.registerCall("AnnounceParallelRun", node, nodes, succinct) } func (stenographer *FakeStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) { stenographer.registerCall("AnnounceNumberOfSpecs", specsToRun, total, succinct) } func (stenographer *FakeStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) { stenographer.registerCall("AnnounceTotalNumberOfSpecs", total, succinct) } func (stenographer *FakeStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) { stenographer.registerCall("AnnounceSpecRunCompletion", summary, succinct) } func (stenographer *FakeStenographer) AnnounceSpecWillRun(spec *types.SpecSummary) { stenographer.registerCall("AnnounceSpecWillRun", spec) } func (stenographer *FakeStenographer) AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceBeforeSuiteFailure", summary, succinct, fullTrace) } func (stenographer *FakeStenographer) AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceAfterSuiteFailure", summary, succinct, fullTrace) } func (stenographer *FakeStenographer) AnnounceCapturedOutput(output string) { stenographer.registerCall("AnnounceCapturedOutput", output) } func (stenographer *FakeStenographer) AnnounceSuccessfulSpec(spec *types.SpecSummary) { stenographer.registerCall("AnnounceSuccessfulSpec", spec) } func (stenographer *FakeStenographer) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) { stenographer.registerCall("AnnounceSuccessfulSlowSpec", spec, succinct) } func (stenographer *FakeStenographer) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) { stenographer.registerCall("AnnounceSuccessfulMeasurement", spec, succinct) } func (stenographer *FakeStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) { stenographer.registerCall("AnnouncePendingSpec", spec, noisy) } func (stenographer *FakeStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceSkippedSpec", spec, succinct, fullTrace) } func (stenographer *FakeStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceSpecTimedOut", spec, succinct, fullTrace) } func (stenographer *FakeStenographer) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceSpecPanicked", spec, succinct, fullTrace) } func (stenographer *FakeStenographer) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) { stenographer.registerCall("AnnounceSpecFailed", spec, succinct, fullTrace) } func (stenographer *FakeStenographer) SummarizeFailures(summaries []*types.SpecSummary) { stenographer.registerCall("SummarizeFailures", summaries) } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go ================================================ /* The stenographer is used by Ginkgo's reporters to generate output. Move along, nothing to see here. */ package stenographer import ( "fmt" "io" "runtime" "strings" "github.com/onsi/ginkgo/types" ) const defaultStyle = "\x1b[0m" const boldStyle = "\x1b[1m" const redColor = "\x1b[91m" const greenColor = "\x1b[32m" const yellowColor = "\x1b[33m" const cyanColor = "\x1b[36m" const grayColor = "\x1b[90m" const lightGrayColor = "\x1b[37m" type cursorStateType int const ( cursorStateTop cursorStateType = iota cursorStateStreaming cursorStateMidBlock cursorStateEndBlock ) type Stenographer interface { AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool) AnnounceAggregatedParallelRun(nodes int, succinct bool) AnnounceParallelRun(node int, nodes int, succinct bool) AnnounceTotalNumberOfSpecs(total int, succinct bool) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) AnnounceSpecWillRun(spec *types.SpecSummary) AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) AnnounceCapturedOutput(output string) AnnounceSuccessfulSpec(spec *types.SpecSummary) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) SummarizeFailures(summaries []*types.SpecSummary) } func New(color bool, enableFlakes bool, writer io.Writer) Stenographer { denoter := "•" if runtime.GOOS == "windows" { denoter = "+" } return &consoleStenographer{ color: color, denoter: denoter, cursorState: cursorStateTop, enableFlakes: enableFlakes, w: writer, } } type consoleStenographer struct { color bool denoter string cursorState cursorStateType enableFlakes bool w io.Writer } var alternatingColors = []string{defaultStyle, grayColor} func (s *consoleStenographer) AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool) { if succinct { s.print(0, "[%d] %s ", randomSeed, s.colorize(boldStyle, description)) return } s.printBanner(fmt.Sprintf("Running Suite: %s", description), "=") s.print(0, "Random Seed: %s", s.colorize(boldStyle, "%d", randomSeed)) if randomizingAll { s.print(0, " - Will randomize all specs") } s.printNewLine() } func (s *consoleStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) { if succinct { s.print(0, "- node #%d ", node) return } s.println(0, "Parallel test node %s/%s.", s.colorize(boldStyle, "%d", node), s.colorize(boldStyle, "%d", nodes), ) s.printNewLine() } func (s *consoleStenographer) AnnounceAggregatedParallelRun(nodes int, succinct bool) { if succinct { s.print(0, "- %d nodes ", nodes) return } s.println(0, "Running in parallel across %s nodes", s.colorize(boldStyle, "%d", nodes), ) s.printNewLine() } func (s *consoleStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) { if succinct { s.print(0, "- %d/%d specs ", specsToRun, total) s.stream() return } s.println(0, "Will run %s of %s specs", s.colorize(boldStyle, "%d", specsToRun), s.colorize(boldStyle, "%d", total), ) s.printNewLine() } func (s *consoleStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) { if succinct { s.print(0, "- %d specs ", total) s.stream() return } s.println(0, "Will run %s specs", s.colorize(boldStyle, "%d", total), ) s.printNewLine() } func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) { if succinct && summary.SuiteSucceeded { s.print(0, " %s %s ", s.colorize(greenColor, "SUCCESS!"), summary.RunTime) return } s.printNewLine() color := greenColor if !summary.SuiteSucceeded { color = redColor } s.println(0, s.colorize(boldStyle+color, "Ran %d of %d Specs in %.3f seconds", summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, summary.RunTime.Seconds())) status := "" if summary.SuiteSucceeded { status = s.colorize(boldStyle+greenColor, "SUCCESS!") } else { status = s.colorize(boldStyle+redColor, "FAIL!") } flakes := "" if s.enableFlakes { flakes = " | " + s.colorize(yellowColor+boldStyle, "%d Flaked", summary.NumberOfFlakedSpecs) } s.print(0, "%s -- %s | %s | %s | %s\n", status, s.colorize(greenColor+boldStyle, "%d Passed", summary.NumberOfPassedSpecs), s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs)+flakes, s.colorize(yellowColor+boldStyle, "%d Pending", summary.NumberOfPendingSpecs), s.colorize(cyanColor+boldStyle, "%d Skipped", summary.NumberOfSkippedSpecs), ) } func (s *consoleStenographer) AnnounceSpecWillRun(spec *types.SpecSummary) { s.startBlock() for i, text := range spec.ComponentTexts[1 : len(spec.ComponentTexts)-1] { s.print(0, s.colorize(alternatingColors[i%2], text)+" ") } indentation := 0 if len(spec.ComponentTexts) > 2 { indentation = 1 s.printNewLine() } index := len(spec.ComponentTexts) - 1 s.print(indentation, s.colorize(boldStyle, spec.ComponentTexts[index])) s.printNewLine() s.print(indentation, s.colorize(lightGrayColor, spec.ComponentCodeLocations[index].String())) s.printNewLine() s.midBlock() } func (s *consoleStenographer) AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) { s.announceSetupFailure("BeforeSuite", summary, succinct, fullTrace) } func (s *consoleStenographer) AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) { s.announceSetupFailure("AfterSuite", summary, succinct, fullTrace) } func (s *consoleStenographer) announceSetupFailure(name string, summary *types.SetupSummary, succinct bool, fullTrace bool) { s.startBlock() var message string switch summary.State { case types.SpecStateFailed: message = "Failure" case types.SpecStatePanicked: message = "Panic" case types.SpecStateTimedOut: message = "Timeout" } s.println(0, s.colorize(redColor+boldStyle, "%s [%.3f seconds]", message, summary.RunTime.Seconds())) indentation := s.printCodeLocationBlock([]string{name}, []types.CodeLocation{summary.CodeLocation}, summary.ComponentType, 0, summary.State, true) s.printNewLine() s.printFailure(indentation, summary.State, summary.Failure, fullTrace) s.endBlock() } func (s *consoleStenographer) AnnounceCapturedOutput(output string) { if output == "" { return } s.startBlock() s.println(0, output) s.midBlock() } func (s *consoleStenographer) AnnounceSuccessfulSpec(spec *types.SpecSummary) { s.print(0, s.colorize(greenColor, s.denoter)) s.stream() } func (s *consoleStenographer) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) { s.printBlockWithMessage( s.colorize(greenColor, "%s [SLOW TEST:%.3f seconds]", s.denoter, spec.RunTime.Seconds()), "", spec, succinct, ) } func (s *consoleStenographer) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) { s.printBlockWithMessage( s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter), s.measurementReport(spec, succinct), spec, succinct, ) } func (s *consoleStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) { if noisy { s.printBlockWithMessage( s.colorize(yellowColor, "P [PENDING]"), "", spec, false, ) } else { s.print(0, s.colorize(yellowColor, "P")) s.stream() } } func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) { // Skips at runtime will have a non-empty spec.Failure. All others should be succinct. if succinct || spec.Failure == (types.SpecFailure{}) { s.print(0, s.colorize(cyanColor, "S")) s.stream() } else { s.startBlock() s.println(0, s.colorize(cyanColor+boldStyle, "S [SKIPPING]%s [%.3f seconds]", s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds())) indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct) s.printNewLine() s.printSkip(indentation, spec.Failure) s.endBlock() } } func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { s.printSpecFailure(fmt.Sprintf("%s... Timeout", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) { s.printSpecFailure(fmt.Sprintf("%s! Panic", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) { s.printSpecFailure(fmt.Sprintf("%s Failure", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary) { failingSpecs := []*types.SpecSummary{} for _, summary := range summaries { if summary.HasFailureState() { failingSpecs = append(failingSpecs, summary) } } if len(failingSpecs) == 0 { return } s.printNewLine() s.printNewLine() plural := "s" if len(failingSpecs) == 1 { plural = "" } s.println(0, s.colorize(redColor+boldStyle, "Summarizing %d Failure%s:", len(failingSpecs), plural)) for _, summary := range failingSpecs { s.printNewLine() if summary.HasFailureState() { if summary.TimedOut() { s.print(0, s.colorize(redColor+boldStyle, "[Timeout...] ")) } else if summary.Panicked() { s.print(0, s.colorize(redColor+boldStyle, "[Panic!] ")) } else if summary.Failed() { s.print(0, s.colorize(redColor+boldStyle, "[Fail] ")) } s.printSpecContext(summary.ComponentTexts, summary.ComponentCodeLocations, summary.Failure.ComponentType, summary.Failure.ComponentIndex, summary.State, true) s.printNewLine() s.println(0, s.colorize(lightGrayColor, summary.Failure.Location.String())) } } } func (s *consoleStenographer) startBlock() { if s.cursorState == cursorStateStreaming { s.printNewLine() s.printDelimiter() } else if s.cursorState == cursorStateMidBlock { s.printNewLine() } } func (s *consoleStenographer) midBlock() { s.cursorState = cursorStateMidBlock } func (s *consoleStenographer) endBlock() { s.printDelimiter() s.cursorState = cursorStateEndBlock } func (s *consoleStenographer) stream() { s.cursorState = cursorStateStreaming } func (s *consoleStenographer) printBlockWithMessage(header string, message string, spec *types.SpecSummary, succinct bool) { s.startBlock() s.println(0, header) indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, types.SpecComponentTypeInvalid, 0, spec.State, succinct) if message != "" { s.printNewLine() s.println(indentation, message) } s.endBlock() } func (s *consoleStenographer) printSpecFailure(message string, spec *types.SpecSummary, succinct bool, fullTrace bool) { s.startBlock() s.println(0, s.colorize(redColor+boldStyle, "%s%s [%.3f seconds]", message, s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds())) indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct) s.printNewLine() s.printFailure(indentation, spec.State, spec.Failure, fullTrace) s.endBlock() } func (s *consoleStenographer) failureContext(failedComponentType types.SpecComponentType) string { switch failedComponentType { case types.SpecComponentTypeBeforeSuite: return " in Suite Setup (BeforeSuite)" case types.SpecComponentTypeAfterSuite: return " in Suite Teardown (AfterSuite)" case types.SpecComponentTypeBeforeEach: return " in Spec Setup (BeforeEach)" case types.SpecComponentTypeJustBeforeEach: return " in Spec Setup (JustBeforeEach)" case types.SpecComponentTypeAfterEach: return " in Spec Teardown (AfterEach)" } return "" } func (s *consoleStenographer) printSkip(indentation int, spec types.SpecFailure) { s.println(indentation, s.colorize(cyanColor, spec.Message)) s.printNewLine() s.println(indentation, spec.Location.String()) } func (s *consoleStenographer) printFailure(indentation int, state types.SpecState, failure types.SpecFailure, fullTrace bool) { if state == types.SpecStatePanicked { s.println(indentation, s.colorize(redColor+boldStyle, failure.Message)) s.println(indentation, s.colorize(redColor, failure.ForwardedPanic)) s.println(indentation, failure.Location.String()) s.printNewLine() s.println(indentation, s.colorize(redColor, "Full Stack Trace")) s.println(indentation, failure.Location.FullStackTrace) } else { s.println(indentation, s.colorize(redColor, failure.Message)) s.printNewLine() s.println(indentation, failure.Location.String()) if fullTrace { s.printNewLine() s.println(indentation, s.colorize(redColor, "Full Stack Trace")) s.println(indentation, failure.Location.FullStackTrace) } } } func (s *consoleStenographer) printSpecContext(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int { startIndex := 1 indentation := 0 if len(componentTexts) == 1 { startIndex = 0 } for i := startIndex; i < len(componentTexts); i++ { if (state.IsFailure() || state == types.SpecStateSkipped) && i == failedComponentIndex { color := redColor if state == types.SpecStateSkipped { color = cyanColor } blockType := "" switch failedComponentType { case types.SpecComponentTypeBeforeSuite: blockType = "BeforeSuite" case types.SpecComponentTypeAfterSuite: blockType = "AfterSuite" case types.SpecComponentTypeBeforeEach: blockType = "BeforeEach" case types.SpecComponentTypeJustBeforeEach: blockType = "JustBeforeEach" case types.SpecComponentTypeAfterEach: blockType = "AfterEach" case types.SpecComponentTypeIt: blockType = "It" case types.SpecComponentTypeMeasure: blockType = "Measurement" } if succinct { s.print(0, s.colorize(color+boldStyle, "[%s] %s ", blockType, componentTexts[i])) } else { s.println(indentation, s.colorize(color+boldStyle, "%s [%s]", componentTexts[i], blockType)) s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i])) } } else { if succinct { s.print(0, s.colorize(alternatingColors[i%2], "%s ", componentTexts[i])) } else { s.println(indentation, componentTexts[i]) s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i])) } } indentation++ } return indentation } func (s *consoleStenographer) printCodeLocationBlock(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int { indentation := s.printSpecContext(componentTexts, componentCodeLocations, failedComponentType, failedComponentIndex, state, succinct) if succinct { if len(componentTexts) > 0 { s.printNewLine() s.print(0, s.colorize(lightGrayColor, "%s", componentCodeLocations[len(componentCodeLocations)-1])) } s.printNewLine() indentation = 1 } else { indentation-- } return indentation } func (s *consoleStenographer) orderedMeasurementKeys(measurements map[string]*types.SpecMeasurement) []string { orderedKeys := make([]string, len(measurements)) for key, measurement := range measurements { orderedKeys[measurement.Order] = key } return orderedKeys } func (s *consoleStenographer) measurementReport(spec *types.SpecSummary, succinct bool) string { if len(spec.Measurements) == 0 { return "Found no measurements" } message := []string{} orderedKeys := s.orderedMeasurementKeys(spec.Measurements) if succinct { message = append(message, fmt.Sprintf("%s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples))) for _, key := range orderedKeys { measurement := spec.Measurements[key] message = append(message, fmt.Sprintf(" %s - %s: %s%s, %s: %s%s ± %s%s, %s: %s%s", s.colorize(boldStyle, "%s", measurement.Name), measurement.SmallestLabel, s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest), measurement.Units, measurement.AverageLabel, s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average), measurement.Units, s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation), measurement.Units, measurement.LargestLabel, s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest), measurement.Units, )) } } else { message = append(message, fmt.Sprintf("Ran %s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples))) for _, key := range orderedKeys { measurement := spec.Measurements[key] info := "" if measurement.Info != nil { message = append(message, fmt.Sprintf("%v", measurement.Info)) } message = append(message, fmt.Sprintf("%s:\n%s %s: %s%s\n %s: %s%s\n %s: %s%s ± %s%s", s.colorize(boldStyle, "%s", measurement.Name), info, measurement.SmallestLabel, s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest), measurement.Units, measurement.LargestLabel, s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest), measurement.Units, measurement.AverageLabel, s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average), measurement.Units, s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation), measurement.Units, )) } } return strings.Join(message, "\n") } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Yasuhiro Matsumoto 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. ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable/README.md ================================================ # go-colorable Colorable writer for windows. For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) This package is possible to handle escape sequence for ansi color on windows. ## Too Bad! ![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) ## So Good! ![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) ## Usage ```go logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) logrus.SetOutput(colorable.NewColorableStdout()) logrus.Info("succeeded") logrus.Warn("not correct") logrus.Error("something error") logrus.Fatal("panic") ``` You can compile above code on non-windows OSs. ## Installation ``` $ go get github.com/mattn/go-colorable ``` # License MIT # Author Yasuhiro Matsumoto (a.k.a mattn) ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable/colorable_others.go ================================================ // +build !windows package colorable import ( "io" "os" ) func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") } return file } func NewColorableStdout() io.Writer { return os.Stdout } func NewColorableStderr() io.Writer { return os.Stderr } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable/colorable_windows.go ================================================ package colorable import ( "bytes" "fmt" "io" "math" "os" "strconv" "strings" "syscall" "unsafe" "github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty" ) const ( foregroundBlue = 0x1 foregroundGreen = 0x2 foregroundRed = 0x4 foregroundIntensity = 0x8 foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) backgroundBlue = 0x10 backgroundGreen = 0x20 backgroundRed = 0x40 backgroundIntensity = 0x80 backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) ) type wchar uint16 type short int16 type dword uint32 type word uint16 type coord struct { x short y short } type smallRect struct { left short top short right short bottom short } type consoleScreenBufferInfo struct { size coord cursorPosition coord attributes word window smallRect maximumWindowSize coord } var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") ) type Writer struct { out io.Writer handle syscall.Handle lastbuf bytes.Buffer oldattr word } func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") } if isatty.IsTerminal(file.Fd()) { var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) return &Writer{out: file, handle: handle, oldattr: csbi.attributes} } else { return file } } func NewColorableStdout() io.Writer { return NewColorable(os.Stdout) } func NewColorableStderr() io.Writer { return NewColorable(os.Stderr) } var color256 = map[int]int{ 0: 0x000000, 1: 0x800000, 2: 0x008000, 3: 0x808000, 4: 0x000080, 5: 0x800080, 6: 0x008080, 7: 0xc0c0c0, 8: 0x808080, 9: 0xff0000, 10: 0x00ff00, 11: 0xffff00, 12: 0x0000ff, 13: 0xff00ff, 14: 0x00ffff, 15: 0xffffff, 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, } func (w *Writer) Write(data []byte) (n int, err error) { var csbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) er := bytes.NewBuffer(data) loop: for { r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) if r1 == 0 { break loop } c1, _, err := er.ReadRune() if err != nil { break loop } if c1 != 0x1b { fmt.Fprint(w.out, string(c1)) continue } c2, _, err := er.ReadRune() if err != nil { w.lastbuf.WriteRune(c1) break loop } if c2 != 0x5b { w.lastbuf.WriteRune(c1) w.lastbuf.WriteRune(c2) continue } var buf bytes.Buffer var m rune for { c, _, err := er.ReadRune() if err != nil { w.lastbuf.WriteRune(c1) w.lastbuf.WriteRune(c2) w.lastbuf.Write(buf.Bytes()) break loop } if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { m = c break } buf.Write([]byte(string(c))) } var csbi consoleScreenBufferInfo switch m { case 'A': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.y -= short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'B': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'C': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x -= short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'D': n, err = strconv.Atoi(buf.String()) if err != nil { continue } if n, err = strconv.Atoi(buf.String()); err == nil { var csbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x += short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) } case 'E': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = 0 csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'F': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = 0 csbi.cursorPosition.y -= short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'G': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = short(n) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'H': token := strings.Split(buf.String(), ";") if len(token) != 2 { continue } n1, err := strconv.Atoi(token[0]) if err != nil { continue } n2, err := strconv.Atoi(token[1]) if err != nil { continue } csbi.cursorPosition.x = short(n2) csbi.cursorPosition.x = short(n1) procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'J': n, err := strconv.Atoi(buf.String()) if err != nil { continue } var cursor coord switch n { case 0: cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} case 1: cursor = coord{x: csbi.window.left, y: csbi.window.top} case 2: cursor = coord{x: csbi.window.left, y: csbi.window.top} } var count, written dword count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'K': n, err := strconv.Atoi(buf.String()) if err != nil { continue } var cursor coord switch n { case 0: cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} case 1: cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} case 2: cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} } var count, written dword count = dword(csbi.size.x - csbi.cursorPosition.x) procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'm': attr := csbi.attributes cs := buf.String() if cs == "" { procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) continue } token := strings.Split(cs, ";") for i := 0; i < len(token); i += 1 { ns := token[i] if n, err = strconv.Atoi(ns); err == nil { switch { case n == 0 || n == 100: attr = w.oldattr case 1 <= n && n <= 5: attr |= foregroundIntensity case n == 7: attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) case 22 == n || n == 25 || n == 25: attr |= foregroundIntensity case n == 27: attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) case 30 <= n && n <= 37: attr = (attr & backgroundMask) if (n-30)&1 != 0 { attr |= foregroundRed } if (n-30)&2 != 0 { attr |= foregroundGreen } if (n-30)&4 != 0 { attr |= foregroundBlue } case n == 38: // set foreground color. if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { if n256, err := strconv.Atoi(token[i+2]); err == nil { if n256foreAttr == nil { n256setup() } attr &= backgroundMask attr |= n256foreAttr[n256] i += 2 } } else { attr = attr & (w.oldattr & backgroundMask) } case n == 39: // reset foreground color. attr &= backgroundMask attr |= w.oldattr & foregroundMask case 40 <= n && n <= 47: attr = (attr & foregroundMask) if (n-40)&1 != 0 { attr |= backgroundRed } if (n-40)&2 != 0 { attr |= backgroundGreen } if (n-40)&4 != 0 { attr |= backgroundBlue } case n == 48: // set background color. if i < len(token)-2 && token[i+1] == "5" { if n256, err := strconv.Atoi(token[i+2]); err == nil { if n256backAttr == nil { n256setup() } attr &= foregroundMask attr |= n256backAttr[n256] i += 2 } } else { attr = attr & (w.oldattr & foregroundMask) } case n == 49: // reset foreground color. attr &= foregroundMask attr |= w.oldattr & backgroundMask case 90 <= n && n <= 97: attr = (attr & backgroundMask) attr |= foregroundIntensity if (n-90)&1 != 0 { attr |= foregroundRed } if (n-90)&2 != 0 { attr |= foregroundGreen } if (n-90)&4 != 0 { attr |= foregroundBlue } case 100 <= n && n <= 107: attr = (attr & foregroundMask) attr |= backgroundIntensity if (n-100)&1 != 0 { attr |= backgroundRed } if (n-100)&2 != 0 { attr |= backgroundGreen } if (n-100)&4 != 0 { attr |= backgroundBlue } } procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) } } } } return len(data) - w.lastbuf.Len(), nil } type consoleColor struct { rgb int red bool green bool blue bool intensity bool } func (c consoleColor) foregroundAttr() (attr word) { if c.red { attr |= foregroundRed } if c.green { attr |= foregroundGreen } if c.blue { attr |= foregroundBlue } if c.intensity { attr |= foregroundIntensity } return } func (c consoleColor) backgroundAttr() (attr word) { if c.red { attr |= backgroundRed } if c.green { attr |= backgroundGreen } if c.blue { attr |= backgroundBlue } if c.intensity { attr |= backgroundIntensity } return } var color16 = []consoleColor{ consoleColor{0x000000, false, false, false, false}, consoleColor{0x000080, false, false, true, false}, consoleColor{0x008000, false, true, false, false}, consoleColor{0x008080, false, true, true, false}, consoleColor{0x800000, true, false, false, false}, consoleColor{0x800080, true, false, true, false}, consoleColor{0x808000, true, true, false, false}, consoleColor{0xc0c0c0, true, true, true, false}, consoleColor{0x808080, false, false, false, true}, consoleColor{0x0000ff, false, false, true, true}, consoleColor{0x00ff00, false, true, false, true}, consoleColor{0x00ffff, false, true, true, true}, consoleColor{0xff0000, true, false, false, true}, consoleColor{0xff00ff, true, false, true, true}, consoleColor{0xffff00, true, true, false, true}, consoleColor{0xffffff, true, true, true, true}, } type hsv struct { h, s, v float32 } func (a hsv) dist(b hsv) float32 { dh := a.h - b.h switch { case dh > 0.5: dh = 1 - dh case dh < -0.5: dh = -1 - dh } ds := a.s - b.s dv := a.v - b.v return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) } func toHSV(rgb int) hsv { r, g, b := float32((rgb&0xFF0000)>>16)/256.0, float32((rgb&0x00FF00)>>8)/256.0, float32(rgb&0x0000FF)/256.0 min, max := minmax3f(r, g, b) h := max - min if h > 0 { if max == r { h = (g - b) / h if h < 0 { h += 6 } } else if max == g { h = 2 + (b-r)/h } else { h = 4 + (r-g)/h } } h /= 6.0 s := max - min if max != 0 { s /= max } v := max return hsv{h: h, s: s, v: v} } type hsvTable []hsv func toHSVTable(rgbTable []consoleColor) hsvTable { t := make(hsvTable, len(rgbTable)) for i, c := range rgbTable { t[i] = toHSV(c.rgb) } return t } func (t hsvTable) find(rgb int) consoleColor { hsv := toHSV(rgb) n := 7 l := float32(5.0) for i, p := range t { d := hsv.dist(p) if d < l { l, n = d, i } } return color16[n] } func minmax3f(a, b, c float32) (min, max float32) { if a < b { if b < c { return a, c } else if a < c { return a, b } else { return c, b } } else { if a < c { return b, c } else if b < c { return b, a } else { return c, a } } } var n256foreAttr []word var n256backAttr []word func n256setup() { n256foreAttr = make([]word, 256) n256backAttr = make([]word, 256) t := toHSVTable(color16) for i, rgb := range color256 { c := t.find(rgb) n256foreAttr[i] = c.foregroundAttr() n256backAttr[i] = c.backgroundAttr() } } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable/noncolorable.go ================================================ package colorable import ( "bytes" "fmt" "io" ) type NonColorable struct { out io.Writer lastbuf bytes.Buffer } func NewNonColorable(w io.Writer) io.Writer { return &NonColorable{out: w} } func (w *NonColorable) Write(data []byte) (n int, err error) { er := bytes.NewBuffer(data) loop: for { c1, _, err := er.ReadRune() if err != nil { break loop } if c1 != 0x1b { fmt.Fprint(w.out, string(c1)) continue } c2, _, err := er.ReadRune() if err != nil { w.lastbuf.WriteRune(c1) break loop } if c2 != 0x5b { w.lastbuf.WriteRune(c1) w.lastbuf.WriteRune(c2) continue } var buf bytes.Buffer for { c, _, err := er.ReadRune() if err != nil { w.lastbuf.WriteRune(c1) w.lastbuf.WriteRune(c2) w.lastbuf.Write(buf.Bytes()) break loop } if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { break } buf.Write([]byte(string(c))) } } return len(data) - w.lastbuf.Len(), nil } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/LICENSE ================================================ Copyright (c) Yasuhiro MATSUMOTO MIT License (Expat) 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. ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/README.md ================================================ # go-isatty isatty for golang ## Usage ```go package main import ( "fmt" "github.com/mattn/go-isatty" "os" ) func main() { if isatty.IsTerminal(os.Stdout.Fd()) { fmt.Println("Is Terminal") } else { fmt.Println("Is Not Terminal") } } ``` ## Installation ``` $ go get github.com/mattn/go-isatty ``` # License MIT # Author Yasuhiro Matsumoto (a.k.a mattn) ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/doc.go ================================================ // Package isatty implements interface to isatty package isatty ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/isatty_appengine.go ================================================ // +build appengine package isatty // IsTerminal returns true if the file descriptor is terminal which // is always false on on appengine classic which is a sandboxed PaaS. func IsTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/isatty_bsd.go ================================================ // +build darwin freebsd openbsd netbsd // +build !appengine package isatty import ( "syscall" "unsafe" ) const ioctlReadTermios = syscall.TIOCGETA // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { var termios syscall.Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/isatty_linux.go ================================================ // +build linux // +build !appengine package isatty import ( "syscall" "unsafe" ) const ioctlReadTermios = syscall.TCGETS // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { var termios syscall.Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/isatty_solaris.go ================================================ // +build solaris // +build !appengine package isatty import ( "golang.org/x/sys/unix" ) // IsTerminal returns true if the given file descriptor is a terminal. // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c func IsTerminal(fd uintptr) bool { var termio unix.Termio err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) return err == nil } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty/isatty_windows.go ================================================ // +build windows // +build !appengine package isatty import ( "syscall" "unsafe" ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") var procGetConsoleMode = kernel32.NewProc("GetConsoleMode") // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { var st uint32 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) return r != 0 && e == 0 } ================================================ FILE: vendor/github.com/onsi/ginkgo/reporters/teamcity_reporter.go ================================================ /* TeamCity Reporter for Ginkgo Makes use of TeamCity's support for Service Messages http://confluence.jetbrains.com/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests */ package reporters import ( "fmt" "io" "strings" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/types" ) const ( messageId = "##teamcity" ) type TeamCityReporter struct { writer io.Writer testSuiteName string ReporterConfig config.DefaultReporterConfigType } func NewTeamCityReporter(writer io.Writer) *TeamCityReporter { return &TeamCityReporter{ writer: writer, } } func (reporter *TeamCityReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { reporter.testSuiteName = escape(summary.SuiteDescription) fmt.Fprintf(reporter.writer, "%s[testSuiteStarted name='%s']\n", messageId, reporter.testSuiteName) } func (reporter *TeamCityReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { reporter.handleSetupSummary("BeforeSuite", setupSummary) } func (reporter *TeamCityReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { reporter.handleSetupSummary("AfterSuite", setupSummary) } func (reporter *TeamCityReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) { if setupSummary.State != types.SpecStatePassed { testName := escape(name) fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']\n", messageId, testName) message := reporter.failureMessage(setupSummary.Failure) details := reporter.failureDetails(setupSummary.Failure) fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']\n", messageId, testName, message, details) durationInMilliseconds := setupSummary.RunTime.Seconds() * 1000 fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']\n", messageId, testName, durationInMilliseconds) } } func (reporter *TeamCityReporter) SpecWillRun(specSummary *types.SpecSummary) { testName := escape(strings.Join(specSummary.ComponentTexts[1:], " ")) fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']\n", messageId, testName) } func (reporter *TeamCityReporter) SpecDidComplete(specSummary *types.SpecSummary) { testName := escape(strings.Join(specSummary.ComponentTexts[1:], " ")) if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed { details := escape(specSummary.CapturedOutput) fmt.Fprintf(reporter.writer, "%s[testPassed name='%s' details='%s']\n", messageId, testName, details) } if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { message := reporter.failureMessage(specSummary.Failure) details := reporter.failureDetails(specSummary.Failure) fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']\n", messageId, testName, message, details) } if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending { fmt.Fprintf(reporter.writer, "%s[testIgnored name='%s']\n", messageId, testName) } durationInMilliseconds := specSummary.RunTime.Seconds() * 1000 fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']\n", messageId, testName, durationInMilliseconds) } func (reporter *TeamCityReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { fmt.Fprintf(reporter.writer, "%s[testSuiteFinished name='%s']\n", messageId, reporter.testSuiteName) } func (reporter *TeamCityReporter) failureMessage(failure types.SpecFailure) string { return escape(failure.ComponentCodeLocation.String()) } func (reporter *TeamCityReporter) failureDetails(failure types.SpecFailure) string { return escape(fmt.Sprintf("%s\n%s", failure.Message, failure.Location.String())) } func escape(output string) string { output = strings.Replace(output, "|", "||", -1) output = strings.Replace(output, "'", "|'", -1) output = strings.Replace(output, "\n", "|n", -1) output = strings.Replace(output, "\r", "|r", -1) output = strings.Replace(output, "[", "|[", -1) output = strings.Replace(output, "]", "|]", -1) return output } ================================================ FILE: vendor/github.com/onsi/ginkgo/types/code_location.go ================================================ package types import ( "fmt" ) type CodeLocation struct { FileName string LineNumber int FullStackTrace string } func (codeLocation CodeLocation) String() string { return fmt.Sprintf("%s:%d", codeLocation.FileName, codeLocation.LineNumber) } ================================================ FILE: vendor/github.com/onsi/ginkgo/types/deprecation_support.go ================================================ package types import ( "os" "strconv" "strings" "unicode" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/formatter" ) type Deprecation struct { Message string DocLink string Version string } type deprecations struct{} var Deprecations = deprecations{} func (d deprecations) CustomReporter() Deprecation { return Deprecation{ Message: "You are using a custom reporter. Support for custom reporters will likely be removed in V2. Most users were using them to generate junit or teamcity reports and this functionality will be merged into the core reporter. In addition, Ginkgo 2.0 will support emitting a JSON-formatted report that users can then manipulate to generate custom reports.\n\n{{red}}{{bold}}If this change will be impactful to you please leave a comment on {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}", DocLink: "removed-custom-reporters", Version: "1.16.0", } } func (d deprecations) V1Reporter() Deprecation { return Deprecation{ Message: "You are using a V1 Ginkgo Reporter. Please update your custom reporter to the new V2 Reporter interface.", DocLink: "changed-reporter-interface", Version: "1.16.0", } } func (d deprecations) Async() Deprecation { return Deprecation{ Message: "You are passing a Done channel to a test node to test asynchronous behavior. This is deprecated in Ginkgo V2. Your test will run synchronously and the timeout will be ignored.", DocLink: "removed-async-testing", Version: "1.16.0", } } func (d deprecations) Measure() Deprecation { return Deprecation{ Message: "Measure is deprecated and will be removed in Ginkgo V2. Please migrate to gomega/gmeasure.", DocLink: "removed-measure", Version: "1.16.3", } } func (d deprecations) ParallelNode() Deprecation { return Deprecation{ Message: "GinkgoParallelNode is deprecated and will be removed in Ginkgo V2. Please use GinkgoParallelProcess instead.", DocLink: "renamed-ginkgoparallelnode", Version: "1.16.5", } } func (d deprecations) Convert() Deprecation { return Deprecation{ Message: "The convert command is deprecated in Ginkgo V2", DocLink: "removed-ginkgo-convert", Version: "1.16.0", } } func (d deprecations) Blur() Deprecation { return Deprecation{ Message: "The blur command is deprecated in Ginkgo V2. Use 'ginkgo unfocus' instead.", Version: "1.16.0", } } type DeprecationTracker struct { deprecations map[Deprecation][]CodeLocation } func NewDeprecationTracker() *DeprecationTracker { return &DeprecationTracker{ deprecations: map[Deprecation][]CodeLocation{}, } } func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) { ackVersion := os.Getenv("ACK_GINKGO_DEPRECATIONS") if deprecation.Version != "" && ackVersion != "" { ack := ParseSemVer(ackVersion) version := ParseSemVer(deprecation.Version) if ack.GreaterThanOrEqualTo(version) { return } } if len(cl) == 1 { d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0]) } else { d.deprecations[deprecation] = []CodeLocation{} } } func (d *DeprecationTracker) DidTrackDeprecations() bool { return len(d.deprecations) > 0 } func (d *DeprecationTracker) DeprecationsReport() string { out := formatter.F("\n{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") out += formatter.F("{{light-yellow}}============================================={{/}}\n") out += formatter.F("{{bold}}{{green}}Ginkgo 2.0{{/}} is under active development and will introduce several new features, improvements, and a small handful of breaking changes.\n") out += formatter.F("A release candidate for 2.0 is now available and 2.0 should GA in Fall 2021. {{bold}}Please give the RC a try and send us feedback!{{/}}\n") out += formatter.F(" - To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md{{/}}\n") out += formatter.F(" - For instructions on using the Release Candidate visit {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta{{/}}\n") out += formatter.F(" - To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") for deprecation, locations := range d.deprecations { out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n") if deprecation.DocLink != "" { out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink) } for _, location := range locations { out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location) } } out += formatter.F("\n{{gray}}To silence deprecations that can be silenced set the following environment variable:{{/}}\n") out += formatter.Fi(1, "{{gray}}ACK_GINKGO_DEPRECATIONS=%s{{/}}\n", config.VERSION) return out } type SemVer struct { Major int Minor int Patch int } func (s SemVer) GreaterThanOrEqualTo(o SemVer) bool { return (s.Major > o.Major) || (s.Major == o.Major && s.Minor > o.Minor) || (s.Major == o.Major && s.Minor == o.Minor && s.Patch >= o.Patch) } func ParseSemVer(semver string) SemVer { out := SemVer{} semver = strings.TrimFunc(semver, func(r rune) bool { return !(unicode.IsNumber(r) || r == '.') }) components := strings.Split(semver, ".") if len(components) > 0 { out.Major, _ = strconv.Atoi(components[0]) } if len(components) > 1 { out.Minor, _ = strconv.Atoi(components[1]) } if len(components) > 2 { out.Patch, _ = strconv.Atoi(components[2]) } return out } ================================================ FILE: vendor/github.com/onsi/ginkgo/types/synchronization.go ================================================ package types import ( "encoding/json" ) type RemoteBeforeSuiteState int const ( RemoteBeforeSuiteStateInvalid RemoteBeforeSuiteState = iota RemoteBeforeSuiteStatePending RemoteBeforeSuiteStatePassed RemoteBeforeSuiteStateFailed RemoteBeforeSuiteStateDisappeared ) type RemoteBeforeSuiteData struct { Data []byte State RemoteBeforeSuiteState } func (r RemoteBeforeSuiteData) ToJSON() []byte { data, _ := json.Marshal(r) return data } type RemoteAfterSuiteData struct { CanRun bool } ================================================ FILE: vendor/github.com/onsi/ginkgo/types/types.go ================================================ package types import ( "strconv" "time" ) const GINKGO_FOCUS_EXIT_CODE = 197 /* SuiteSummary represents the a summary of the test suite and is passed to both Reporter.SpecSuiteWillBegin Reporter.SpecSuiteDidEnd this is unfortunate as these two methods should receive different objects. When running in parallel each node does not deterministically know how many specs it will end up running. Unfortunately making such a change would break backward compatibility. Until Ginkgo 2.0 comes out we will continue to reuse this struct but populate unknown fields with -1. */ type SuiteSummary struct { SuiteDescription string SuiteSucceeded bool SuiteID string NumberOfSpecsBeforeParallelization int NumberOfTotalSpecs int NumberOfSpecsThatWillBeRun int NumberOfPendingSpecs int NumberOfSkippedSpecs int NumberOfPassedSpecs int NumberOfFailedSpecs int // Flaked specs are those that failed initially, but then passed on a // subsequent try. NumberOfFlakedSpecs int RunTime time.Duration } type SpecSummary struct { ComponentTexts []string ComponentCodeLocations []CodeLocation State SpecState RunTime time.Duration Failure SpecFailure IsMeasurement bool NumberOfSamples int Measurements map[string]*SpecMeasurement CapturedOutput string SuiteID string } func (s SpecSummary) HasFailureState() bool { return s.State.IsFailure() } func (s SpecSummary) TimedOut() bool { return s.State == SpecStateTimedOut } func (s SpecSummary) Panicked() bool { return s.State == SpecStatePanicked } func (s SpecSummary) Failed() bool { return s.State == SpecStateFailed } func (s SpecSummary) Passed() bool { return s.State == SpecStatePassed } func (s SpecSummary) Skipped() bool { return s.State == SpecStateSkipped } func (s SpecSummary) Pending() bool { return s.State == SpecStatePending } type SetupSummary struct { ComponentType SpecComponentType CodeLocation CodeLocation State SpecState RunTime time.Duration Failure SpecFailure CapturedOutput string SuiteID string } type SpecFailure struct { Message string Location CodeLocation ForwardedPanic string ComponentIndex int ComponentType SpecComponentType ComponentCodeLocation CodeLocation } type SpecMeasurement struct { Name string Info interface{} Order int Results []float64 Smallest float64 Largest float64 Average float64 StdDeviation float64 SmallestLabel string LargestLabel string AverageLabel string Units string Precision int } func (s SpecMeasurement) PrecisionFmt() string { if s.Precision == 0 { return "%f" } str := strconv.Itoa(s.Precision) return "%." + str + "f" } type SpecState uint const ( SpecStateInvalid SpecState = iota SpecStatePending SpecStateSkipped SpecStatePassed SpecStateFailed SpecStatePanicked SpecStateTimedOut ) func (state SpecState) IsFailure() bool { return state == SpecStateTimedOut || state == SpecStatePanicked || state == SpecStateFailed } type SpecComponentType uint const ( SpecComponentTypeInvalid SpecComponentType = iota SpecComponentTypeContainer SpecComponentTypeBeforeSuite SpecComponentTypeAfterSuite SpecComponentTypeBeforeEach SpecComponentTypeJustBeforeEach SpecComponentTypeJustAfterEach SpecComponentTypeAfterEach SpecComponentTypeIt SpecComponentTypeMeasure ) type FlagType uint const ( FlagTypeNone FlagType = iota FlagTypeFocused FlagTypePending ) ================================================ FILE: vendor/github.com/onsi/gomega/.gitignore ================================================ .DS_Store *.test . .idea gomega.iml ================================================ FILE: vendor/github.com/onsi/gomega/.travis.yml ================================================ language: go arch: - amd64 - ppc64le go: - gotip - 1.16.x - 1.15.x env: - GO111MODULE=on install: skip script: - go mod tidy && git diff --exit-code go.mod go.sum - make test ================================================ FILE: vendor/github.com/onsi/gomega/CHANGELOG.md ================================================ ## 1.17.0 ### Features - Add HaveField matcher [3a26311] - add Error() assertions on the final error value of multi-return values (#480) [2f96943] - separate out offsets and timeouts (#478) [18a4723] - fix transformation error reporting (#479) [e001fab] - allow transform functions to report errors (#472) [bf93408] ### Fixes Stop using deprecated ioutil package (#467) [07f405d] ## 1.16.0 ### Features - feat: HaveHTTPStatus multiple expected values (#465) [aa69f1b] - feat: HaveHTTPHeaderWithValue() matcher (#463) [dd83a96] - feat: HaveHTTPBody matcher (#462) [504e1f2] - feat: formatter for HTTP responses (#461) [e5b3157] ## 1.15.0 ### Fixes The previous version (1.14.0) introduced a change to allow `Eventually` and `Consistently` to support functions that make assertions. This was accomplished by overriding the global fail handler when running the callbacks passed to `Eventually/Consistently` in order to capture any resulting errors. Issue #457 uncovered a flaw with this approach: when multiple `Eventually`s are running concurrently they race when overriding the singleton global fail handler. 1.15.0 resolves this by requiring users who want to make assertions in `Eventually/Consistently` call backs to explicitly pass in a function that takes a `Gomega` as an argument. The passed-in `Gomega` instance can be used to make assertions. Any failures will cause `Eventually` to retry the callback. This cleaner interface avoids the issue of swapping out globals but comes at the cost of changing the contract introduced in v1.14.0. As such 1.15.0 introduces a breaking change with respect to 1.14.0 - however we expect that adoption of this feature in 1.14.0 remains limited. In addition, 1.15.0 cleans up some of Gomega's internals. Most users shouldn't notice any differences stemming from the refactoring that was made. ## 1.14.0 ### Features - gmeasure.SamplingConfig now suppers a MinSamplingInterval [e94dbca] - Eventually and Consistently support functions that make assertions [2f04e6e] - Eventually and Consistently now allow their passed-in functions to make assertions. These assertions must pass or the function is considered to have failed and is retried. - Eventually and Consistently can now take functions with no return values. These implicitly return nil if they contain no failed assertion. Otherwise they return an error wrapping the first assertion failure. This allows these functions to be used with the Succeed() matcher. - Introduce InterceptGomegaFailure - an analogue to InterceptGomegaFailures - that captures the first assertion failure and halts execution in its passed-in callback. ### Fixes - Call Verify GHTTPWithGomega receiver funcs (#454) [496e6fd] - Build a binary with an expected name (#446) [7356360] ## 1.13.0 ### Features - gmeasure provides BETA support for benchmarking (#447) [8f2dfbf] - Set consistently and eventually defaults on init (#443) [12eb778] ## 1.12.0 ### Features - Add Satisfy() matcher (#437) [c548f31] - tweak truncation message [3360b8c] - Add format.GomegaStringer (#427) [cc80b6f] - Add Clear() method to gbytes.Buffer [c3c0920] ### Fixes - Fix error message in BeNumericallyMatcher (#432) [09c074a] - Bump github.com/onsi/ginkgo from 1.12.1 to 1.16.2 (#442) [e5f6ea0] - Bump github.com/golang/protobuf from 1.4.3 to 1.5.2 (#431) [adae3bf] - Bump golang.org/x/net (#441) [3275b35] ## 1.11.0 ### Features - feature: add index to gstruct element func (#419) [334e00d] - feat(gexec) Add CompileTest functions. Close #410 (#411) [47c613f] ### Fixes - Check more carefully for nils in WithTransform (#423) [3c60a15] - fix: typo in Makefile [b82522a] - Allow WithTransform function to accept a nil value (#422) [b75d2f2] - fix: print value type for interface{} containers (#409) [f08e2dc] - fix(BeElementOf): consistently flatten expected values [1fa9468] ## 1.10.5 ### Fixes - fix: collections matchers should display type of expectation (#408) [6b4eb5a] - fix(ContainElements): consistently flatten expected values [073b880] - fix(ConsistOf): consistently flatten expected values [7266efe] ## 1.10.4 ### Fixes - update golang net library to more recent version without vulnerability (#406) [817a8b9] - Correct spelling: alloted -> allotted (#403) [0bae715] - fix a panic in MessageWithDiff with long message (#402) [ea06b9b] ## 1.10.3 ### Fixes - updates golang/x/net to fix vulnerability detected by snyk (#394) [c479356] ## 1.10.2 ### Fixes - Add ExpectWithOffset, EventuallyWithOffset and ConsistentlyWithOffset to WithT (#391) [990941a] ## 1.10.1 ### Fixes - Update dependencies (#389) [9f5eecd] ## 1.10.0 ### Features - Add HaveHTTPStatusMatcher (#378) [f335c94] - Changed matcher for content-type in VerifyJSONRepresenting (#377) [6024f5b] - Make ghttp usable with x-unit style tests (#376) [c0be499] - Implement PanicWith matcher (#381) [f8032b4] ## 1.9.0 ### Features - Add ContainElements matcher (#370) [2f57380] - Output missing and extra elements in ConsistOf failure message [a31eda7] - Document method LargestMatching [7c5a280] ## 1.8.1 ### Fixes - Fix unexpected MatchError() behaviour (#375) [8ae7b2f] ## 1.8.0 ### Features - Allow optional description to be lazily evaluated function (#364) [bf64010] - Support wrapped errors (#359) [0a981cb] ## 1.7.1 ### Fixes - Bump go-yaml version to cover fixed ddos heuristic (#362) [95e431e] ## 1.7.0 ### Features - export format property variables (#347) [642e5ba] ### Fixes - minor fix in the documentation of ExpectWithOffset (#358) [beea727] ## 1.6.0 ### Features - Display special chars on error [41e1b26] - Add BeElementOf matcher [6a48b48] ### Fixes - Remove duplication in XML matcher tests [cc1a6cb] - Remove unnecessary conversions (#357) [7bf756a] - Fixed import order (#353) [2e3b965] - Added missing error handling in test (#355) [c98d3eb] - Simplify code (#356) [0001ed9] - Simplify code (#354) [0d9100e] - Fixed typos (#352) [3f647c4] - Add failure message tests to BeElementOf matcher [efe19c3] - Update go-testcov untested sections [37ee382] - Mark all uncovered files so go-testcov ./... works [53b150e] - Reenable gotip in travis [5c249dc] - Fix the typo of comment (#345) [f0e010e] - Optimize contain_element_matcher [abeb93d] ## 1.5.0 ### Features - Added MatchKeys matchers [8b909fc] ### Fixes and Minor Improvements - Add type aliases to remove stuttering [03b0461] - Don't run session_test.go on windows (#324) [5533ce8] ## 1.4.3 ### Fixes: - ensure file name and line numbers are correctly reported for XUnit [6fff58f] - Fixed matcher for content-type (#305) [69d9b43] ## 1.4.2 ### Fixes: - Add go.mod and go.sum files to define the gomega go module [f3de367, a085d30] - Work around go vet issue with Go v1.11 (#300) [40dd6ad] - Better output when using with go XUnit-style tests, fixes #255 (#297) [29a4b97] - Fix MatchJSON fail to parse json.RawMessage (#298) [ae19f1b] - show threshold in failure message of BeNumericallyMatcher (#293) [4bbecc8] ## 1.4.1 ### Fixes: - Update documentation formatting and examples (#289) [9be8410] - allow 'Receive' matcher to be used with concrete types (#286) [41673fd] - Fix data race in ghttp server (#283) [7ac6b01] - Travis badge should only show master [cc102ab] ## 1.4.0 ### Features - Make string pretty diff user configurable (#273) [eb112ce, 649b44d] ### Fixes - Use httputil.DumpRequest to pretty-print unhandled requests (#278) [a4ff0fc, b7d1a52] - fix typo floa32 > float32 (#272) [041ae3b, 6e33911] - Fix link to documentation on adding your own matchers (#270) [bb2c830, fcebc62] - Use setters and getters to avoid race condition (#262) [13057c3, a9c79f1] - Avoid sending a signal if the process is not alive (#259) [b8043e5, 4fc1762] - Improve message from AssignableToTypeOf when expected value is nil (#281) [9c1fb20] ## 1.3.0 Improvements: - The `Equal` matcher matches byte slices more performantly. - Improved how `MatchError` matches error strings. - `MatchXML` ignores the order of xml node attributes. - Improve support for XUnit style golang tests. ([#254](https://github.com/onsi/gomega/issues/254)) Bug Fixes: - Diff generation now handles multi-byte sequences correctly. - Multiple goroutines can now call `gexec.Build` concurrently. ## 1.2.0 Improvements: - Added `BeSent` which attempts to send a value down a channel and fails if the attempt blocks. Can be paired with `Eventually` to safely send a value down a channel with a timeout. - `Ω`, `Expect`, `Eventually`, and `Consistently` now immediately `panic` if there is no registered fail handler. This is always a mistake that can hide failing tests. - `Receive()` no longer errors when passed a closed channel, it's perfectly fine to attempt to read from a closed channel so Ω(c).Should(Receive()) always fails and Ω(c).ShoudlNot(Receive()) always passes with a closed channel. - Added `HavePrefix` and `HaveSuffix` matchers. - `ghttp` can now handle concurrent requests. - Added `Succeed` which allows one to write `Ω(MyFunction()).Should(Succeed())`. - Improved `ghttp`'s behavior around failing assertions and panics: - If a registered handler makes a failing assertion `ghttp` will return `500`. - If a registered handler panics, `ghttp` will return `500` *and* fail the test. This is new behavior that may cause existing code to break. This code is almost certainly incorrect and creating a false positive. - `ghttp` servers can take an `io.Writer`. `ghttp` will write a line to the writer when each request arrives. - Added `WithTransform` matcher to allow munging input data before feeding into the relevant matcher - Added boolean `And`, `Or`, and `Not` matchers to allow creating composite matchers - Added `gbytes.TimeoutCloser`, `gbytes.TimeoutReader`, and `gbytes.TimeoutWriter` - these are convenience wrappers that timeout if the underlying Closer/Reader/Writer does not return within the alloted time. - Added `gbytes.BufferReader` - this constructs a `gbytes.Buffer` that asynchronously reads the passed-in `io.Reader` into its buffer. Bug Fixes: - gexec: `session.Wait` now uses `EventuallyWithOffset` to get the right line number in the failure. - `ContainElement` no longer bails if a passed-in matcher errors. ## 1.0 (8/2/2014) No changes. Dropping "beta" from the version number. ## 1.0.0-beta (7/8/2014) Breaking Changes: - Changed OmegaMatcher interface. Instead of having `Match` return failure messages, two new methods `FailureMessage` and `NegatedFailureMessage` are called instead. - Moved and renamed OmegaFailHandler to types.GomegaFailHandler and OmegaMatcher to types.GomegaMatcher. Any references to OmegaMatcher in any custom matchers will need to be changed to point to types.GomegaMatcher New Test-Support Features: - `ghttp`: supports testing http clients - Provides a flexible fake http server - Provides a collection of chainable http handlers that perform assertions. - `gbytes`: supports making ordered assertions against streams of data - Provides a `gbytes.Buffer` - Provides a `Say` matcher to perform ordered assertions against output data - `gexec`: supports testing external processes - Provides support for building Go binaries - Wraps and starts `exec.Cmd` commands - Makes it easy to assert against stdout and stderr - Makes it easy to send signals and wait for processes to exit - Provides an `Exit` matcher to assert against exit code. DSL Changes: - `Eventually` and `Consistently` can accept `time.Duration` interval and polling inputs. - The default timeouts for `Eventually` and `Consistently` are now configurable. New Matchers: - `ConsistOf`: order-independent assertion against the elements of an array/slice or keys of a map. - `BeTemporally`: like `BeNumerically` but for `time.Time` - `HaveKeyWithValue`: asserts a map has a given key with the given value. Updated Matchers: - `Receive` matcher can take a matcher as an argument and passes only if the channel under test receives an objet that satisfies the passed-in matcher. - Matchers that implement `MatchMayChangeInTheFuture(actual interface{}) bool` can inform `Eventually` and/or `Consistently` when a match has no chance of changing status in the future. For example, `Receive` returns `false` when a channel is closed. Misc: - Start using semantic versioning - Start maintaining changelog Major refactor: - Pull out Gomega's internal to `internal` ================================================ FILE: vendor/github.com/onsi/gomega/CONTRIBUTING.md ================================================ # Contributing to Gomega Your contributions to Gomega are essential for its long-term maintenance and improvement. To make a contribution: - Please **open an issue first** - describe what problem you are trying to solve and give the community a forum for input and feedback ahead of investing time in writing code! - Ensure adequate test coverage: - Make sure to add appropriate unit tests - Please run all tests locally (`ginkgo -r -p`) and make sure they go green before submitting the PR - Please run following linter locally `go vet ./...` and make sure output does not contain any warnings - Update the documentation. In addition to standard `godoc` comments Gomega has extensive documentation on the `gh-pages` branch. If relevant, please submit a docs PR to that branch alongside your code PR. If you're a committer, check out RELEASING.md to learn how to cut a release. Thanks for supporting Gomega! ================================================ FILE: vendor/github.com/onsi/gomega/Dockerfile ================================================ FROM golang:1.15 ================================================ FILE: vendor/github.com/onsi/gomega/LICENSE ================================================ Copyright (c) 2013-2014 Onsi Fakhouri 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. ================================================ FILE: vendor/github.com/onsi/gomega/Makefile ================================================ ###### Help ################################################################### .DEFAULT_GOAL = help .PHONY: help help: ## list Makefile targets @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' ###### Targets ################################################################ test: version download fmt vet ginkgo ## Runs all build, static analysis, and test steps download: ## Download dependencies go mod download vet: ## Run static code analysis go vet ./... ginkgo: ## Run tests using Ginkgo go run github.com/onsi/ginkgo/ginkgo -p -r --randomizeAllSpecs --failOnPending --randomizeSuites --race fmt: ## Checks that the code is formatted correcty @@if [ -n "$$(gofmt -s -e -l -d .)" ]; then \ echo "gofmt check failed: run 'gofmt -s -e -l -w .'"; \ exit 1; \ fi docker_test: ## Run tests in a container via docker-compose docker-compose build test && docker-compose run --rm test make test version: ## Display the version of Go @@go version ================================================ FILE: vendor/github.com/onsi/gomega/README.md ================================================ ![Gomega: Ginkgo's Preferred Matcher Library](http://onsi.github.io/gomega/images/gomega.png) [![test](https://github.com/onsi/gomega/actions/workflows/test.yml/badge.svg)](https://github.com/onsi/gomega/actions/workflows/test.yml) Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega, including a list of [all available matchers](http://onsi.github.io/gomega/#provided-matchers). If you have a question, comment, bug report, feature request, etc. please open a GitHub issue. ## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/) ## Community Matchers A collection of community matchers is available on the [wiki](https://github.com/onsi/gomega/wiki). ## License Gomega is MIT-Licensed The `ConsistOf` matcher uses [goraph](https://github.com/amitkgupta/goraph) which is embedded in the source to simplify distribution. goraph has an MIT license. ================================================ FILE: vendor/github.com/onsi/gomega/RELEASING.md ================================================ A Gomega release is a tagged sha and a GitHub release. To cut a release: 1. Ensure CHANGELOG.md is up to date. - Use `git log --pretty=format:'- %s [%h]' HEAD...vX.X.X` to list all the commits since the last release - Categorize the changes into - Breaking Changes (requires a major version) - New Features (minor version) - Fixes (fix version) - Maintenance (which in general should not be mentioned in `CHANGELOG.md` as they have no user impact) 2. Update GOMEGA_VERSION in `gomega_dsl.go` 3. Push a commit with the version number as the commit message (e.g. `v1.3.0`) 4. Create a new [GitHub release](https://help.github.com/articles/creating-releases/) with the version number as the tag (e.g. `v1.3.0`). List the key changes in the release notes. ================================================ FILE: vendor/github.com/onsi/gomega/docker-compose.yaml ================================================ version: '3.0' services: test: build: dockerfile: Dockerfile context: . working_dir: /app volumes: - ${PWD}:/app ================================================ FILE: vendor/github.com/onsi/gomega/format/format.go ================================================ /* Gomega's format package pretty-prints objects. It explores input objects recursively and generates formatted, indented output with type information. */ // untested sections: 4 package format import ( "context" "fmt" "reflect" "strconv" "strings" "time" ) // Use MaxDepth to set the maximum recursion depth when printing deeply nested objects var MaxDepth = uint(10) // MaxLength of the string representation of an object. // If MaxLength is set to 0, the Object will not be truncated. var MaxLength = 4000 /* By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output. Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead. Note that GoString and String don't always have all the information you need to understand why a test failed! */ var UseStringerRepresentation = false /* Print the content of context objects. By default it will be suppressed. Set PrintContextObjects = true to enable printing of the context internals. */ var PrintContextObjects = false // TruncatedDiff choose if we should display a truncated pretty diff or not var TruncatedDiff = true // TruncateThreshold (default 50) specifies the maximum length string to print in string comparison assertion error // messages. var TruncateThreshold uint = 50 // CharactersAroundMismatchToInclude (default 5) specifies how many contextual characters should be printed before and // after the first diff location in a truncated string assertion error message. var CharactersAroundMismatchToInclude uint = 5 var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() var timeType = reflect.TypeOf(time.Time{}) //The default indentation string emitted by the format package var Indent = " " var longFormThreshold = 20 // GomegaStringer allows for custom formating of objects for gomega. type GomegaStringer interface { // GomegaString will be used to custom format an object. // It does not follow UseStringerRepresentation value and will always be called regardless. // It also ignores the MaxLength value. GomegaString() string } /* Generates a formatted matcher success/failure message of the form: Expected If expected is omitted, then the message looks like: Expected */ func Message(actual interface{}, message string, expected ...interface{}) string { if len(expected) == 0 { return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message) } return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1)) } /* Generates a nicely formatted matcher success / failure message Much like Message(...), but it attempts to pretty print diffs in strings Expected : "...aaaaabaaaaa..." to equal | : "...aaaaazaaaaa..." */ func MessageWithDiff(actual, message, expected string) string { if TruncatedDiff && len(actual) >= int(TruncateThreshold) && len(expected) >= int(TruncateThreshold) { diffPoint := findFirstMismatch(actual, expected) formattedActual := truncateAndFormat(actual, diffPoint) formattedExpected := truncateAndFormat(expected, diffPoint) spacesBeforeFormattedMismatch := findFirstMismatch(formattedActual, formattedExpected) tabLength := 4 spaceFromMessageToActual := tabLength + len(": ") - len(message) paddingCount := spaceFromMessageToActual + spacesBeforeFormattedMismatch if paddingCount < 0 { return Message(formattedActual, message, formattedExpected) } padding := strings.Repeat(" ", paddingCount) + "|" return Message(formattedActual, message+padding, formattedExpected) } actual = escapedWithGoSyntax(actual) expected = escapedWithGoSyntax(expected) return Message(actual, message, expected) } func escapedWithGoSyntax(str string) string { withQuotes := fmt.Sprintf("%q", str) return withQuotes[1 : len(withQuotes)-1] } func truncateAndFormat(str string, index int) string { leftPadding := `...` rightPadding := `...` start := index - int(CharactersAroundMismatchToInclude) if start < 0 { start = 0 leftPadding = "" } // slice index must include the mis-matched character lengthOfMismatchedCharacter := 1 end := index + int(CharactersAroundMismatchToInclude) + lengthOfMismatchedCharacter if end > len(str) { end = len(str) rightPadding = "" } return fmt.Sprintf("\"%s\"", leftPadding+str[start:end]+rightPadding) } func findFirstMismatch(a, b string) int { aSlice := strings.Split(a, "") bSlice := strings.Split(b, "") for index, str := range aSlice { if index > len(bSlice)-1 { return index } if str != bSlice[index] { return index } } if len(b) > len(a) { return len(a) + 1 } return 0 } const truncateHelpText = ` Gomega truncated this representation as it exceeds 'format.MaxLength'. Consider having the object provide a custom 'GomegaStringer' representation or adjust the parameters in Gomega's 'format' package. Learn more here: https://onsi.github.io/gomega/#adjusting-output ` func truncateLongStrings(s string) string { if MaxLength > 0 && len(s) > MaxLength { var sb strings.Builder for i, r := range s { if i < MaxLength { sb.WriteRune(r) continue } break } sb.WriteString("...\n") sb.WriteString(truncateHelpText) return sb.String() } return s } /* Pretty prints the passed in object at the passed in indentation level. Object recurses into deeply nested objects emitting pretty-printed representations of their components. Modify format.MaxDepth to control how deep the recursion is allowed to go Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of recursing into the object. Set PrintContextObjects to true to print the content of objects implementing context.Context */ func Object(object interface{}, indentation uint) string { indent := strings.Repeat(Indent, int(indentation)) value := reflect.ValueOf(object) return fmt.Sprintf("%s<%s>: %s", indent, formatType(value), formatValue(value, indentation)) } /* IndentString takes a string and indents each line by the specified amount. */ func IndentString(s string, indentation uint) string { components := strings.Split(s, "\n") result := "" indent := strings.Repeat(Indent, int(indentation)) for i, component := range components { result += indent + component if i < len(components)-1 { result += "\n" } } return result } func formatType(v reflect.Value) string { switch v.Kind() { case reflect.Invalid: return "nil" case reflect.Chan: return fmt.Sprintf("%s | len:%d, cap:%d", v.Type(), v.Len(), v.Cap()) case reflect.Ptr: return fmt.Sprintf("%s | 0x%x", v.Type(), v.Pointer()) case reflect.Slice: return fmt.Sprintf("%s | len:%d, cap:%d", v.Type(), v.Len(), v.Cap()) case reflect.Map: return fmt.Sprintf("%s | len:%d", v.Type(), v.Len()) default: return fmt.Sprintf("%s", v.Type()) } } func formatValue(value reflect.Value, indentation uint) string { if indentation > MaxDepth { return "..." } if isNilValue(value) { return "nil" } if value.CanInterface() { obj := value.Interface() // GomegaStringer will take precedence to other representations and disregards UseStringerRepresentation if x, ok := obj.(GomegaStringer); ok { // do not truncate a user-defined GoMegaString() value return x.GomegaString() } if UseStringerRepresentation { switch x := obj.(type) { case fmt.GoStringer: return truncateLongStrings(x.GoString()) case fmt.Stringer: return truncateLongStrings(x.String()) } } } if !PrintContextObjects { if value.Type().Implements(contextType) && indentation > 1 { return "" } } switch value.Kind() { case reflect.Bool: return fmt.Sprintf("%v", value.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return fmt.Sprintf("%v", value.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return fmt.Sprintf("%v", value.Uint()) case reflect.Uintptr: return fmt.Sprintf("0x%x", value.Uint()) case reflect.Float32, reflect.Float64: return fmt.Sprintf("%v", value.Float()) case reflect.Complex64, reflect.Complex128: return fmt.Sprintf("%v", value.Complex()) case reflect.Chan: return fmt.Sprintf("0x%x", value.Pointer()) case reflect.Func: return fmt.Sprintf("0x%x", value.Pointer()) case reflect.Ptr: return formatValue(value.Elem(), indentation) case reflect.Slice: return truncateLongStrings(formatSlice(value, indentation)) case reflect.String: return truncateLongStrings(formatString(value.String(), indentation)) case reflect.Array: return truncateLongStrings(formatSlice(value, indentation)) case reflect.Map: return truncateLongStrings(formatMap(value, indentation)) case reflect.Struct: if value.Type() == timeType && value.CanInterface() { t, _ := value.Interface().(time.Time) return t.Format(time.RFC3339Nano) } return truncateLongStrings(formatStruct(value, indentation)) case reflect.Interface: return formatInterface(value, indentation) default: if value.CanInterface() { return truncateLongStrings(fmt.Sprintf("%#v", value.Interface())) } return truncateLongStrings(fmt.Sprintf("%#v", value)) } } func formatString(object interface{}, indentation uint) string { if indentation == 1 { s := fmt.Sprintf("%s", object) components := strings.Split(s, "\n") result := "" for i, component := range components { if i == 0 { result += component } else { result += Indent + component } if i < len(components)-1 { result += "\n" } } return result } else { return fmt.Sprintf("%q", object) } } func formatSlice(v reflect.Value, indentation uint) string { if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())) { return formatString(v.Bytes(), indentation) } l := v.Len() result := make([]string, l) longest := 0 for i := 0; i < l; i++ { result[i] = formatValue(v.Index(i), indentation+1) if len(result[i]) > longest { longest = len(result[i]) } } if longest > longFormThreshold { indenter := strings.Repeat(Indent, int(indentation)) return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) } return fmt.Sprintf("[%s]", strings.Join(result, ", ")) } func formatMap(v reflect.Value, indentation uint) string { l := v.Len() result := make([]string, l) longest := 0 for i, key := range v.MapKeys() { value := v.MapIndex(key) result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1), formatValue(value, indentation+1)) if len(result[i]) > longest { longest = len(result[i]) } } if longest > longFormThreshold { indenter := strings.Repeat(Indent, int(indentation)) return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) } return fmt.Sprintf("{%s}", strings.Join(result, ", ")) } func formatStruct(v reflect.Value, indentation uint) string { t := v.Type() l := v.NumField() result := []string{} longest := 0 for i := 0; i < l; i++ { structField := t.Field(i) fieldEntry := v.Field(i) representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1)) result = append(result, representation) if len(representation) > longest { longest = len(representation) } } if longest > longFormThreshold { indenter := strings.Repeat(Indent, int(indentation)) return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter) } return fmt.Sprintf("{%s}", strings.Join(result, ", ")) } func formatInterface(v reflect.Value, indentation uint) string { return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation)) } func isNilValue(a reflect.Value) bool { switch a.Kind() { case reflect.Invalid: return true case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return a.IsNil() } return false } /* Returns true when the string is entirely made of printable runes, false otherwise. */ func isPrintableString(str string) bool { for _, runeValue := range str { if !strconv.IsPrint(runeValue) { return false } } return true } ================================================ FILE: vendor/github.com/onsi/gomega/go.mod ================================================ module github.com/onsi/gomega go 1.16 require ( github.com/golang/protobuf v1.5.2 github.com/onsi/ginkgo v1.16.4 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 gopkg.in/yaml.v2 v2.4.0 ) ================================================ FILE: vendor/github.com/onsi/gomega/go.sum ================================================ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= ================================================ FILE: vendor/github.com/onsi/gomega/gomega_dsl.go ================================================ /* Gomega is the Ginkgo BDD-style testing framework's preferred matcher library. The godoc documentation describes Gomega's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/gomega/ Gomega on Github: http://github.com/onsi/gomega Learn more about Ginkgo online: http://onsi.github.io/ginkgo Ginkgo on Github: http://github.com/onsi/ginkgo Gomega is MIT-Licensed */ package gomega import ( "errors" "fmt" "time" "github.com/onsi/gomega/internal" "github.com/onsi/gomega/types" ) const GOMEGA_VERSION = "1.17.0" const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT(). Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations. ` // Gomega describes the essential Gomega DSL. This interface allows libraries // to abstract between the standard package-level function implementations // and alternatives like *WithT. // // The types in the top-level DSL have gotten a bit messy due to earlier depracations that avoid stuttering // and due to an accidental use of a concrete type (*WithT) in an earlier release. // // As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object // however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant) // is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure // that declarations of *WithT in existing code are not broken by the upgrade to 1.15. type Gomega = types.Gomega // DefaultGomega supplies the standard package-level implementation var Default = Gomega(internal.NewGomega(internal.FetchDefaultDurationBundle())) // NewGomega returns an instance of Gomega wired into the passed-in fail handler. // You generally don't need to use this when using Ginkgo - RegisterFailHandler will wire up the global gomega // However creating a NewGomega with a custom fail handler can be useful in contexts where you want to use Gomega's // rich ecosystem of matchers without causing a test to fail. For example, to aggregate a series of potential failures // or for use in a non-test setting. func NewGomega(fail types.GomegaFailHandler) Gomega { return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithFailHandler(fail) } // WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage // Gomega's rich ecosystem of matchers in standard `testing` test suites. // // Use `NewWithT` to instantiate a `WithT` // // As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object // however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant) // is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure // that declarations of *WithT in existing code are not broken by the upgrade to 1.15. type WithT = internal.Gomega // GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter. type GomegaWithT = WithT // NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with // Gomega's rich ecosystem of matchers in standard `testing` test suits. // // func TestFarmHasCow(t *testing.T) { // g := gomega.NewWithT(t) // // f := farm.New([]string{"Cow", "Horse"}) // g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") // } func NewWithT(t types.GomegaTestingT) *WithT { return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithT(t) } // NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter. var NewGomegaWithT = NewWithT // RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails // the fail handler passed into RegisterFailHandler is called. func RegisterFailHandler(fail types.GomegaFailHandler) { Default.(*internal.Gomega).ConfigureWithFailHandler(fail) } // RegisterFailHandlerWithT is deprecated and will be removed in a future release. // users should use RegisterFailHandler, or RegisterTestingT func RegisterFailHandlerWithT(_ types.GomegaTestingT, fail types.GomegaFailHandler) { fmt.Println("RegisterFailHandlerWithT is deprecated. Please use RegisterFailHandler or RegisterTestingT instead.") Default.(*internal.Gomega).ConfigureWithFailHandler(fail) } // RegisterTestingT connects Gomega to Golang's XUnit style // Testing.T tests. It is now deprecated and you should use NewWithT() instead to get a fresh instance of Gomega for each test. func RegisterTestingT(t types.GomegaTestingT) { Default.(*internal.Gomega).ConfigureWithT(t) } // InterceptGomegaFailures runs a given callback and returns an array of // failure messages generated by any Gomega assertions within the callback. // Exeuction continues after the first failure allowing users to collect all failures // in the callback. // // This is most useful when testing custom matchers, but can also be used to check // on a value using a Gomega assertion without causing a test failure. func InterceptGomegaFailures(f func()) []string { originalHandler := Default.(*internal.Gomega).Fail failures := []string{} Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) { failures = append(failures, message) } defer func() { Default.(*internal.Gomega).Fail = originalHandler }() f() return failures } // InterceptGomegaFailure runs a given callback and returns the first // failure message generated by any Gomega assertions within the callback, wrapped in an error. // // The callback ceases execution as soon as the first failed assertion occurs, however Gomega // does not register a failure with the FailHandler registered via RegisterFailHandler - it is up // to the user to decide what to do with the returned error func InterceptGomegaFailure(f func()) (err error) { originalHandler := Default.(*internal.Gomega).Fail Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) { err = errors.New(message) panic("stop execution") } defer func() { Default.(*internal.Gomega).Fail = originalHandler if e := recover(); e != nil { if err == nil { panic(e) } } }() f() return err } func ensureDefaultGomegaIsConfigured() { if !Default.(*internal.Gomega).IsConfigured() { panic(nilGomegaPanic) } } // Ω wraps an actual value allowing assertions to be made on it: // Ω("foo").Should(Equal("foo")) // // If Ω is passed more than one argument it will pass the *first* argument to the matcher. // All subsequent arguments will be required to be nil/zero. // // This is convenient if you want to make an assertion on a method/function that returns // a value and an error - a common patter in Go. // // For example, given a function with signature: // func MyAmazingThing() (int, error) // // Then: // Ω(MyAmazingThing()).Should(Equal(3)) // Will succeed only if `MyAmazingThing()` returns `(3, nil)` // // Ω and Expect are identical func Ω(actual interface{}, extra ...interface{}) Assertion { ensureDefaultGomegaIsConfigured() return Default.Ω(actual, extra...) } // Expect wraps an actual value allowing assertions to be made on it: // Expect("foo").To(Equal("foo")) // // If Expect is passed more than one argument it will pass the *first* argument to the matcher. // All subsequent arguments will be required to be nil/zero. // // This is convenient if you want to make an assertion on a method/function that returns // a value and an error - a common patter in Go. // // For example, given a function with signature: // func MyAmazingThing() (int, error) // // Then: // Expect(MyAmazingThing()).Should(Equal(3)) // Will succeed only if `MyAmazingThing()` returns `(3, nil)` // // Expect and Ω are identical func Expect(actual interface{}, extra ...interface{}) Assertion { ensureDefaultGomegaIsConfigured() return Default.Expect(actual, extra...) } // ExpectWithOffset wraps an actual value allowing assertions to be made on it: // ExpectWithOffset(1, "foo").To(Equal("foo")) // // Unlike `Expect` and `Ω`, `ExpectWithOffset` takes an additional integer argument // that is used to modify the call-stack offset when computing line numbers. It is // the same as `Expect(...).WithOffset`. // // This is most useful in helper functions that make assertions. If you want Gomega's // error message to refer to the calling line in the test (as opposed to the line in the helper function) // set the first argument of `ExpectWithOffset` appropriately. func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion { ensureDefaultGomegaIsConfigured() return Default.ExpectWithOffset(offset, actual, extra...) } /* Eventually enables making assertions on asynchronous behavior. Eventually checks that an assertion *eventually* passes. Eventually blocks when called and attempts an assertion periodically until it passes or a timeout occurs. Both the timeout and polling interval are configurable as optional arguments. The first optional argument is the timeout (which defaults to 1s), the second is the polling interval (which defaults to 10ms). Both intervals can be specified as time.Duration, parsable duration strings or floats/integers (in which case they are interpreted as seconds). Eventually works with any Gomega compatible matcher and supports making assertions against three categories of actual value: **Category 1: Making Eventually assertions on values** There are several examples of values that can change over time. These can be passed in to Eventually and will be passed to the matcher repeatedly until a match occurs. For example: c := make(chan bool) go DoStuff(c) Eventually(c, "50ms").Should(BeClosed()) will poll the channel repeatedly until it is closed. In this example `Eventually` will block until either the specified timeout of 50ms has elapsed or the channel is closed, whichever comes first. Several Gomega libraries allow you to use Eventually in this way. For example, the gomega/gexec package allows you to block until a *gexec.Session exits successfuly via: Eventually(session).Should(gexec.Exit(0)) And the gomega/gbytes package allows you to monitor a streaming *gbytes.Buffer until a given string is seen: Eventually(buffer).Should(gbytes.Say("hello there")) In these examples, both `session` and `buffer` are designed to be thread-safe when polled by the `Exit` and `Say` matchers. This is not true in general of most raw values, so while it is tempting to do something like: // THIS IS NOT THREAD-SAFE var s *string go mutateStringEventually(s) Eventually(s).Should(Equal("I've changed")) this will trigger Go's race detector as the goroutine polling via Eventually will race over the value of s with the goroutine mutating the string. For cases like this you can use channels or introduce your own locking around s by passing Eventually a function. **Category 2: Make Eventually assertions on functions** Eventually can be passed functions that **take no arguments** and **return at least one value**. When configured this way, Eventually will poll the function repeatedly and pass the first returned value to the matcher. For example: Eventually(func() int { return client.FetchCount() }).Should(BeNumerically(">=", 17)) will repeatedly poll client.FetchCount until the BeNumerically matcher is satisfied. (Note that this example could have been written as Eventually(client.FetchCount).Should(BeNumerically(">=", 17))) If multple values are returned by the function, Eventually will pass the first value to the matcher and require that all others are zero-valued. This allows you to pass Eventually a function that returns a value and an error - a common patternin Go. For example, consider a method that returns a value and an error: func FetchFromDB() (string, error) Then Eventually(FetchFromDB).Should(Equal("got it")) will pass only if and when the returned error is nil *and* the returned string satisfies the matcher. It is important to note that the function passed into Eventually is invoked *synchronously* when polled. Eventually does not (in fact, it cannot) kill the function if it takes longer to return than Eventually's configured timeout. You should design your functions with this in mind. **Category 3: Making assertions _in_ the function passed into Eventually** When testing complex systems it can be valuable to assert that a _set_ of assertions passes Eventually. Eventually supports this by accepting functions that take a single Gomega argument and return zero or more values. Here's an example that makes some asssertions and returns a value and error: Eventually(func(g Gomega) (Widget, error) { ids, err := client.FetchIDs() g.Expect(err).NotTo(HaveOccurred()) g.Expect(ids).To(ContainElement(1138)) return client.FetchWidget(1138) }).Should(Equal(expectedWidget)) will pass only if all the assertions in the polled function pass and the return value satisfied the matcher. Eventually also supports a special case polling function that takes a single Gomega argument and returns no values. Eventually assumes such a function is making assertions and is designed to work with the Succeed matcher to validate that all assertions have passed. For example: Eventually(func(g Gomega) { model, err := client.Find(1138) g.Expect(err).NotTo(HaveOccurred()) g.Expect(model.Reticulate()).To(Succeed()) g.Expect(model.IsReticulated()).To(BeTrue()) g.Expect(model.Save()).To(Succeed()) }).Should(Succeed()) will rerun the function until all assertions pass. `Eventually` specifying a timeout interval (and an optional polling interval) are the same as `Eventually(...).WithTimeout` or `Eventually(...).WithTimeout(...).WithPolling`. */ func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() return Default.Eventually(actual, intervals...) } // EventuallyWithOffset operates like Eventually but takes an additional // initial argument to indicate an offset in the call stack. This is useful when building helper // functions that contain matchers. To learn more, read about `ExpectWithOffset`. // // `EventuallyWithOffset` is the same as `Eventually(...).WithOffset`. // // `EventuallyWithOffset` specifying a timeout interval (and an optional polling interval) are // the same as `Eventually(...).WithOffset(...).WithTimeout` or // `Eventually(...).WithOffset(...).WithTimeout(...).WithPolling`. func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() return Default.EventuallyWithOffset(offset, actual, intervals...) } /* Consistently, like Eventually, enables making assertions on asynchronous behavior. Consistently blocks when called for a specified duration. During that duration Consistently repeatedly polls its matcher and ensures that it is satisfied. If the matcher is consistently satisfied, then Consistently will pass. Otherwise Consistently will fail. Both the total waiting duration and the polling interval are configurable as optional arguments. The first optional arugment is the duration that Consistently will run for (defaults to 100ms), and the second argument is the polling interval (defaults to 10ms). As with Eventually, these intervals can be passed in as time.Duration, parsable duration strings or an integer or float number of seconds. Consistently accepts the same three categories of actual as Eventually, check the Eventually docs to learn more. Consistently is useful in cases where you want to assert that something *does not happen* for a period of time. For example, you may want to assert that a goroutine does *not* send data down a channel. In this case you could write: Consistently(channel, "200ms").ShouldNot(Receive()) This will block for 200 milliseconds and repeatedly check the channel and ensure nothing has been received. */ func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() return Default.Consistently(actual, intervals...) } // ConsistentlyWithOffset operates like Consistently but takes an additional // initial argument to indicate an offset in the call stack. This is useful when building helper // functions that contain matchers. To learn more, read about `ExpectWithOffset`. // // `ConsistentlyWithOffset` is the same as `Consistently(...).WithOffset` and // optional `WithTimeout` and `WithPolling`. func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { ensureDefaultGomegaIsConfigured() return Default.ConsistentlyWithOffset(offset, actual, intervals...) } // SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses. func SetDefaultEventuallyTimeout(t time.Duration) { Default.SetDefaultEventuallyTimeout(t) } // SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually. func SetDefaultEventuallyPollingInterval(t time.Duration) { Default.SetDefaultEventuallyPollingInterval(t) } // SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long. func SetDefaultConsistentlyDuration(t time.Duration) { Default.SetDefaultConsistentlyDuration(t) } // SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently. func SetDefaultConsistentlyPollingInterval(t time.Duration) { Default.SetDefaultConsistentlyPollingInterval(t) } // AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against // the matcher passed to the Should and ShouldNot methods. // // Both Should and ShouldNot take a variadic optionalDescription argument. // This argument allows you to make your failure messages more descriptive. // If a single argument of type `func() string` is passed, this function will be lazily evaluated if a failure occurs // and the returned string is used to annotate the failure message. // Otherwise, this argument is passed on to fmt.Sprintf() and then used to annotate the failure message. // // Both Should and ShouldNot return a boolean that is true if the assertion passed and false if it failed. // // Example: // // Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.") // Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." }) type AsyncAssertion = types.AsyncAssertion // GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter. type GomegaAsyncAssertion = types.AsyncAssertion // Assertion is returned by Ω and Expect and compares the actual value to the matcher // passed to the Should/ShouldNot and To/ToNot/NotTo methods. // // Typically Should/ShouldNot are used with Ω and To/ToNot/NotTo are used with Expect // though this is not enforced. // // All methods take a variadic optionalDescription argument. // This argument allows you to make your failure messages more descriptive. // If a single argument of type `func() string` is passed, this function will be lazily evaluated if a failure occurs // and the returned string is used to annotate the failure message. // Otherwise, this argument is passed on to fmt.Sprintf() and then used to annotate the failure message. // // All methods return a bool that is true if the assertion passed and false if it failed. // // Example: // // Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm) type Assertion = types.Assertion // GomegaAssertion is deprecated in favor of Assertion, which does not stutter. type GomegaAssertion = types.Assertion // OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it type OmegaMatcher = types.GomegaMatcher ================================================ FILE: vendor/github.com/onsi/gomega/internal/assertion.go ================================================ package internal import ( "fmt" "reflect" "github.com/onsi/gomega/types" ) type Assertion struct { actuals []interface{} // actual value plus all extra values actualIndex int // value to pass to the matcher vet vetinari // the vet to call before calling Gomega matcher offset int g *Gomega } // ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right. type vetinari func(assertion *Assertion, optionalDescription ...interface{}) bool func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion { return &Assertion{ actuals: append([]interface{}{actualInput}, extra...), actualIndex: 0, vet: (*Assertion).vetActuals, offset: offset, g: g, } } func (assertion *Assertion) WithOffset(offset int) types.Assertion { assertion.offset = offset return assertion } func (assertion *Assertion) Error() types.Assertion { return &Assertion{ actuals: assertion.actuals, actualIndex: len(assertion.actuals) - 1, vet: (*Assertion).vetError, offset: assertion.offset, g: assertion.g, } } func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) } func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) } func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) } func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) } func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) } func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { switch len(optionalDescription) { case 0: return "" case 1: if describe, ok := optionalDescription[0].(func() string); ok { return describe() + "\n" } } return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" } func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { actualInput := assertion.actuals[assertion.actualIndex] matches, err := matcher.Match(actualInput) assertion.g.THelper() if err != nil { description := assertion.buildDescription(optionalDescription...) assertion.g.Fail(description+err.Error(), 2+assertion.offset) return false } if matches != desiredMatch { var message string if desiredMatch { message = matcher.FailureMessage(actualInput) } else { message = matcher.NegatedFailureMessage(actualInput) } description := assertion.buildDescription(optionalDescription...) assertion.g.Fail(description+message, 2+assertion.offset) return false } return true } // vetActuals vets the actual values, with the (optional) exception of a // specific value, such as the first value in case non-error assertions, or the // last value in case of Error()-based assertions. func (assertion *Assertion) vetActuals(optionalDescription ...interface{}) bool { success, message := vetActuals(assertion.actuals, assertion.actualIndex) if success { return true } description := assertion.buildDescription(optionalDescription...) assertion.g.THelper() assertion.g.Fail(description+message, 2+assertion.offset) return false } // vetError vets the actual values, except for the final error value, in case // the final error value is non-zero. Otherwise, it doesn't vet the actual // values, as these are allowed to take on any values unless there is a non-zero // error value. func (assertion *Assertion) vetError(optionalDescription ...interface{}) bool { if err := assertion.actuals[assertion.actualIndex]; err != nil { // Go error result idiom: all other actual values must be zero values. return assertion.vetActuals(optionalDescription...) } return true } // vetActuals vets a slice of actual values, optionally skipping a particular // value slice element, such as the first or last value slice element. func vetActuals(actuals []interface{}, skipIndex int) (bool, string) { for i, actual := range actuals { if i == skipIndex { continue } if actual != nil { zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() if !reflect.DeepEqual(zeroValue, actual) { message := fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual) return false, message } } } return true, "" } ================================================ FILE: vendor/github.com/onsi/gomega/internal/async_assertion.go ================================================ package internal import ( "errors" "fmt" "reflect" "runtime" "time" "github.com/onsi/gomega/types" ) type AsyncAssertionType uint const ( AsyncAssertionTypeEventually AsyncAssertionType = iota AsyncAssertionTypeConsistently ) type AsyncAssertion struct { asyncType AsyncAssertionType actualIsFunc bool actualValue interface{} actualFunc func() ([]reflect.Value, error) timeoutInterval time.Duration pollingInterval time.Duration offset int g *Gomega } func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { out := &AsyncAssertion{ asyncType: asyncType, timeoutInterval: timeoutInterval, pollingInterval: pollingInterval, offset: offset, g: g, } switch actualType := reflect.TypeOf(actualInput); { case actualType.Kind() != reflect.Func: out.actualValue = actualInput case actualType.NumIn() == 0 && actualType.NumOut() > 0: out.actualIsFunc = true out.actualFunc = func() ([]reflect.Value, error) { return reflect.ValueOf(actualInput).Call([]reflect.Value{}), nil } case actualType.NumIn() == 1 && actualType.In(0).Implements(reflect.TypeOf((*types.Gomega)(nil)).Elem()): out.actualIsFunc = true out.actualFunc = func() (values []reflect.Value, err error) { var assertionFailure error assertionCapturingGomega := NewGomega(g.DurationBundle).ConfigureWithFailHandler(func(message string, callerSkip ...int) { skip := 0 if len(callerSkip) > 0 { skip = callerSkip[0] } _, file, line, _ := runtime.Caller(skip + 1) assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message) panic("stop execution") }) defer func() { if actualType.NumOut() == 0 { if assertionFailure == nil { values = []reflect.Value{reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())} } else { values = []reflect.Value{reflect.ValueOf(assertionFailure)} } } else { err = assertionFailure } if e := recover(); e != nil && assertionFailure == nil { panic(e) } }() values = reflect.ValueOf(actualInput).Call([]reflect.Value{reflect.ValueOf(assertionCapturingGomega)}) return } default: msg := fmt.Sprintf("The function passed to Gomega's async assertions should either take no arguments and return values, or take a single Gomega interface that it can use to make assertions within the body of the function. When taking a Gomega interface the function can optionally return values or return nothing. The function you passed takes %d arguments and returns %d values.", actualType.NumIn(), actualType.NumOut()) g.Fail(msg, offset+4) } return out } func (assertion *AsyncAssertion) WithOffset(offset int) types.AsyncAssertion { assertion.offset = offset return assertion } func (assertion *AsyncAssertion) WithTimeout(interval time.Duration) types.AsyncAssertion { assertion.timeoutInterval = interval return assertion } func (assertion *AsyncAssertion) WithPolling(interval time.Duration) types.AsyncAssertion { assertion.pollingInterval = interval return assertion } func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.match(matcher, true, optionalDescription...) } func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { assertion.g.THelper() return assertion.match(matcher, false, optionalDescription...) } func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string { switch len(optionalDescription) { case 0: return "" case 1: if describe, ok := optionalDescription[0].(func() string); ok { return describe() + "\n" } } return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" } func (assertion *AsyncAssertion) pollActual() (interface{}, error) { if !assertion.actualIsFunc { return assertion.actualValue, nil } values, err := assertion.actualFunc() if err != nil { return nil, err } extras := []interface{}{nil} for _, value := range values[1:] { extras = append(extras, value.Interface()) } success, message := vetActuals(extras, 0) if !success { return nil, errors.New(message) } return values[0].Interface(), nil } func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { if assertion.actualIsFunc { return true } return types.MatchMayChangeInTheFuture(matcher, value) } func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { timer := time.Now() timeout := time.After(assertion.timeoutInterval) var matches bool var err error mayChange := true value, err := assertion.pollActual() if err == nil { mayChange = assertion.matcherMayChange(matcher, value) matches, err = matcher.Match(value) } assertion.g.THelper() fail := func(preamble string) { errMsg := "" message := "" if err != nil { errMsg = "Error: " + err.Error() } else { if desiredMatch { message = matcher.FailureMessage(value) } else { message = matcher.NegatedFailureMessage(value) } } assertion.g.THelper() description := assertion.buildDescription(optionalDescription...) assertion.g.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) } if assertion.asyncType == AsyncAssertionTypeEventually { for { if err == nil && matches == desiredMatch { return true } if !mayChange { fail("No future change is possible. Bailing out early") return false } select { case <-time.After(assertion.pollingInterval): value, err = assertion.pollActual() if err == nil { mayChange = assertion.matcherMayChange(matcher, value) matches, err = matcher.Match(value) } case <-timeout: fail("Timed out") return false } } } else if assertion.asyncType == AsyncAssertionTypeConsistently { for { if !(err == nil && matches == desiredMatch) { fail("Failed") return false } if !mayChange { return true } select { case <-time.After(assertion.pollingInterval): value, err = assertion.pollActual() if err == nil { mayChange = assertion.matcherMayChange(matcher, value) matches, err = matcher.Match(value) } case <-timeout: return true } } } return false } ================================================ FILE: vendor/github.com/onsi/gomega/internal/duration_bundle.go ================================================ package internal import ( "fmt" "os" "reflect" "time" ) type DurationBundle struct { EventuallyTimeout time.Duration EventuallyPollingInterval time.Duration ConsistentlyDuration time.Duration ConsistentlyPollingInterval time.Duration } const ( EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT" EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL" ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION" ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL" ) func FetchDefaultDurationBundle() DurationBundle { return DurationBundle{ EventuallyTimeout: durationFromEnv(EventuallyTimeoutEnvVarName, time.Second), EventuallyPollingInterval: durationFromEnv(EventuallyPollingIntervalEnvVarName, 10*time.Millisecond), ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond), ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond), } } func durationFromEnv(key string, defaultDuration time.Duration) time.Duration { value := os.Getenv(key) if value == "" { return defaultDuration } duration, err := time.ParseDuration(value) if err != nil { panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", key, err)) } return duration } func toDuration(input interface{}) time.Duration { duration, ok := input.(time.Duration) if ok { return duration } value := reflect.ValueOf(input) kind := reflect.TypeOf(input).Kind() if reflect.Int <= kind && kind <= reflect.Int64 { return time.Duration(value.Int()) * time.Second } else if reflect.Uint <= kind && kind <= reflect.Uint64 { return time.Duration(value.Uint()) * time.Second } else if reflect.Float32 <= kind && kind <= reflect.Float64 { return time.Duration(value.Float() * float64(time.Second)) } else if reflect.String == kind { duration, err := time.ParseDuration(value.String()) if err != nil { panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input)) } return duration } panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input)) } ================================================ FILE: vendor/github.com/onsi/gomega/internal/gomega.go ================================================ package internal import ( "time" "github.com/onsi/gomega/types" ) type Gomega struct { Fail types.GomegaFailHandler THelper func() DurationBundle DurationBundle } func NewGomega(bundle DurationBundle) *Gomega { return &Gomega{ Fail: nil, THelper: nil, DurationBundle: bundle, } } func (g *Gomega) IsConfigured() bool { return g.Fail != nil && g.THelper != nil } func (g *Gomega) ConfigureWithFailHandler(fail types.GomegaFailHandler) *Gomega { g.Fail = fail g.THelper = func() {} return g } func (g *Gomega) ConfigureWithT(t types.GomegaTestingT) *Gomega { g.Fail = func(message string, _ ...int) { t.Helper() t.Fatalf("\n%s", message) } g.THelper = t.Helper return g } func (g *Gomega) Ω(actual interface{}, extra ...interface{}) types.Assertion { return g.ExpectWithOffset(0, actual, extra...) } func (g *Gomega) Expect(actual interface{}, extra ...interface{}) types.Assertion { return g.ExpectWithOffset(0, actual, extra...) } func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) types.Assertion { return NewAssertion(actual, g, offset, extra...) } func (g *Gomega) Eventually(actual interface{}, intervals ...interface{}) types.AsyncAssertion { return g.EventuallyWithOffset(0, actual, intervals...) } func (g *Gomega) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion { timeoutInterval := g.DurationBundle.EventuallyTimeout pollingInterval := g.DurationBundle.EventuallyPollingInterval if len(intervals) > 0 { timeoutInterval = toDuration(intervals[0]) } if len(intervals) > 1 { pollingInterval = toDuration(intervals[1]) } return NewAsyncAssertion(AsyncAssertionTypeEventually, actual, g, timeoutInterval, pollingInterval, offset) } func (g *Gomega) Consistently(actual interface{}, intervals ...interface{}) types.AsyncAssertion { return g.ConsistentlyWithOffset(0, actual, intervals...) } func (g *Gomega) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion { timeoutInterval := g.DurationBundle.ConsistentlyDuration pollingInterval := g.DurationBundle.ConsistentlyPollingInterval if len(intervals) > 0 { timeoutInterval = toDuration(intervals[0]) } if len(intervals) > 1 { pollingInterval = toDuration(intervals[1]) } return NewAsyncAssertion(AsyncAssertionTypeConsistently, actual, g, timeoutInterval, pollingInterval, offset) } func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) { g.DurationBundle.EventuallyTimeout = t } func (g *Gomega) SetDefaultEventuallyPollingInterval(t time.Duration) { g.DurationBundle.EventuallyPollingInterval = t } func (g *Gomega) SetDefaultConsistentlyDuration(t time.Duration) { g.DurationBundle.ConsistentlyDuration = t } func (g *Gomega) SetDefaultConsistentlyPollingInterval(t time.Duration) { g.DurationBundle.ConsistentlyPollingInterval = t } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/and.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" "github.com/onsi/gomega/types" ) type AndMatcher struct { Matchers []types.GomegaMatcher // state firstFailedMatcher types.GomegaMatcher } func (m *AndMatcher) Match(actual interface{}) (success bool, err error) { m.firstFailedMatcher = nil for _, matcher := range m.Matchers { success, err := matcher.Match(actual) if !success || err != nil { m.firstFailedMatcher = matcher return false, err } } return true, nil } func (m *AndMatcher) FailureMessage(actual interface{}) (message string) { return m.firstFailedMatcher.FailureMessage(actual) } func (m *AndMatcher) NegatedFailureMessage(actual interface{}) (message string) { // not the most beautiful list of matchers, but not bad either... return format.Message(actual, fmt.Sprintf("To not satisfy all of these matchers: %s", m.Matchers)) } func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { /* Example with 3 matchers: A, B, C Match evaluates them: T, F, => F So match is currently F, what should MatchMayChangeInTheFuture() return? Seems like it only depends on B, since currently B MUST change to allow the result to become T Match eval: T, T, T => T So match is currently T, what should MatchMayChangeInTheFuture() return? Seems to depend on ANY of them being able to change to F. */ if m.firstFailedMatcher == nil { // so all matchers succeeded.. Any one of them changing would change the result. for _, matcher := range m.Matchers { if types.MatchMayChangeInTheFuture(matcher, actual) { return true } } return false // none of were going to change } // one of the matchers failed.. it must be able to change in order to affect the result return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type AssignableToTypeOfMatcher struct { Expected interface{} } func (matcher *AssignableToTypeOfMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil && matcher.Expected == nil { return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") } else if matcher.Expected == nil { return false, fmt.Errorf("Refusing to compare type to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") } else if actual == nil { return false, nil } actualType := reflect.TypeOf(actual) expectedType := reflect.TypeOf(matcher.Expected) return actualType.AssignableTo(expectedType), nil } func (matcher *AssignableToTypeOfMatcher) FailureMessage(actual interface{}) string { return format.Message(actual, fmt.Sprintf("to be assignable to the type: %T", matcher.Expected)) } func (matcher *AssignableToTypeOfMatcher) NegatedFailureMessage(actual interface{}) string { return format.Message(actual, fmt.Sprintf("not to be assignable to the type: %T", matcher.Expected)) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/attributes_slice.go ================================================ package matchers import ( "encoding/xml" "strings" ) type attributesSlice []xml.Attr func (attrs attributesSlice) Len() int { return len(attrs) } func (attrs attributesSlice) Less(i, j int) bool { return strings.Compare(attrs[i].Name.Local, attrs[j].Name.Local) == -1 } func (attrs attributesSlice) Swap(i, j int) { attrs[i], attrs[j] = attrs[j], attrs[i] } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_a_directory.go ================================================ // untested sections: 5 package matchers import ( "fmt" "os" "github.com/onsi/gomega/format" ) type notADirectoryError struct { os.FileInfo } func (t notADirectoryError) Error() string { fileInfo := os.FileInfo(t) switch { case fileInfo.Mode().IsRegular(): return "file is a regular file" default: return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String()) } } type BeADirectoryMatcher struct { expected interface{} err error } func (matcher *BeADirectoryMatcher) Match(actual interface{}) (success bool, err error) { actualFilename, ok := actual.(string) if !ok { return false, fmt.Errorf("BeADirectoryMatcher matcher expects a file path") } fileInfo, err := os.Stat(actualFilename) if err != nil { matcher.err = err return false, nil } if !fileInfo.Mode().IsDir() { matcher.err = notADirectoryError{fileInfo} return false, nil } return true, nil } func (matcher *BeADirectoryMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("to be a directory: %s", matcher.err)) } func (matcher *BeADirectoryMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("not be a directory")) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_a_regular_file.go ================================================ // untested sections: 5 package matchers import ( "fmt" "os" "github.com/onsi/gomega/format" ) type notARegularFileError struct { os.FileInfo } func (t notARegularFileError) Error() string { fileInfo := os.FileInfo(t) switch { case fileInfo.IsDir(): return "file is a directory" default: return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String()) } } type BeARegularFileMatcher struct { expected interface{} err error } func (matcher *BeARegularFileMatcher) Match(actual interface{}) (success bool, err error) { actualFilename, ok := actual.(string) if !ok { return false, fmt.Errorf("BeARegularFileMatcher matcher expects a file path") } fileInfo, err := os.Stat(actualFilename) if err != nil { matcher.err = err return false, nil } if !fileInfo.Mode().IsRegular() { matcher.err = notARegularFileError{fileInfo} return false, nil } return true, nil } func (matcher *BeARegularFileMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("to be a regular file: %s", matcher.err)) } func (matcher *BeARegularFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("not be a regular file")) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_an_existing_file.go ================================================ // untested sections: 3 package matchers import ( "fmt" "os" "github.com/onsi/gomega/format" ) type BeAnExistingFileMatcher struct { expected interface{} } func (matcher *BeAnExistingFileMatcher) Match(actual interface{}) (success bool, err error) { actualFilename, ok := actual.(string) if !ok { return false, fmt.Errorf("BeAnExistingFileMatcher matcher expects a file path") } if _, err = os.Stat(actualFilename); err != nil { switch { case os.IsNotExist(err): return false, nil default: return false, err } } return true, nil } func (matcher *BeAnExistingFileMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("to exist")) } func (matcher *BeAnExistingFileMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("not to exist")) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_closed_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type BeClosedMatcher struct { } func (matcher *BeClosedMatcher) Match(actual interface{}) (success bool, err error) { if !isChan(actual) { return false, fmt.Errorf("BeClosed matcher expects a channel. Got:\n%s", format.Object(actual, 1)) } channelType := reflect.TypeOf(actual) channelValue := reflect.ValueOf(actual) if channelType.ChanDir() == reflect.SendDir { return false, fmt.Errorf("BeClosed matcher cannot determine if a send-only channel is closed or open. Got:\n%s", format.Object(actual, 1)) } winnerIndex, _, open := reflect.Select([]reflect.SelectCase{ {Dir: reflect.SelectRecv, Chan: channelValue}, {Dir: reflect.SelectDefault}, }) var closed bool if winnerIndex == 0 { closed = !open } else if winnerIndex == 1 { closed = false } return closed, nil } func (matcher *BeClosedMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be closed") } func (matcher *BeClosedMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be open") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_element_of_matcher.go ================================================ // untested sections: 1 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type BeElementOfMatcher struct { Elements []interface{} } func (matcher *BeElementOfMatcher) Match(actual interface{}) (success bool, err error) { if reflect.TypeOf(actual) == nil { return false, fmt.Errorf("BeElement matcher expects actual to be typed") } var lastError error for _, m := range flatten(matcher.Elements) { matcher := &EqualMatcher{Expected: m} success, err := matcher.Match(actual) if err != nil { lastError = err continue } if success { return true, nil } } return false, lastError } func (matcher *BeElementOfMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be an element of", presentable(matcher.Elements)) } func (matcher *BeElementOfMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be an element of", presentable(matcher.Elements)) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_empty_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type BeEmptyMatcher struct { } func (matcher *BeEmptyMatcher) Match(actual interface{}) (success bool, err error) { length, ok := lengthOf(actual) if !ok { return false, fmt.Errorf("BeEmpty matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) } return length == 0, nil } func (matcher *BeEmptyMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be empty") } func (matcher *BeEmptyMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be empty") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type BeEquivalentToMatcher struct { Expected interface{} } func (matcher *BeEquivalentToMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil && matcher.Expected == nil { return false, fmt.Errorf("Both actual and expected must not be nil.") } convertedActual := actual if actual != nil && matcher.Expected != nil && reflect.TypeOf(actual).ConvertibleTo(reflect.TypeOf(matcher.Expected)) { convertedActual = reflect.ValueOf(actual).Convert(reflect.TypeOf(matcher.Expected)).Interface() } return reflect.DeepEqual(convertedActual, matcher.Expected), nil } func (matcher *BeEquivalentToMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be equivalent to", matcher.Expected) } func (matcher *BeEquivalentToMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be equivalent to", matcher.Expected) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_false_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type BeFalseMatcher struct { } func (matcher *BeFalseMatcher) Match(actual interface{}) (success bool, err error) { if !isBool(actual) { return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) } return actual == false, nil } func (matcher *BeFalseMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be false") } func (matcher *BeFalseMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be false") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_identical_to.go ================================================ // untested sections: 2 package matchers import ( "fmt" "runtime" "github.com/onsi/gomega/format" ) type BeIdenticalToMatcher struct { Expected interface{} } func (matcher *BeIdenticalToMatcher) Match(actual interface{}) (success bool, matchErr error) { if actual == nil && matcher.Expected == nil { return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") } defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { success = false matchErr = nil } } }() return actual == matcher.Expected, nil } func (matcher *BeIdenticalToMatcher) FailureMessage(actual interface{}) string { return format.Message(actual, "to be identical to", matcher.Expected) } func (matcher *BeIdenticalToMatcher) NegatedFailureMessage(actual interface{}) string { return format.Message(actual, "not to be identical to", matcher.Expected) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_nil_matcher.go ================================================ // untested sections: 2 package matchers import "github.com/onsi/gomega/format" type BeNilMatcher struct { } func (matcher *BeNilMatcher) Match(actual interface{}) (success bool, err error) { return isNil(actual), nil } func (matcher *BeNilMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be nil") } func (matcher *BeNilMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be nil") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.go ================================================ // untested sections: 4 package matchers import ( "fmt" "math" "github.com/onsi/gomega/format" ) type BeNumericallyMatcher struct { Comparator string CompareTo []interface{} } func (matcher *BeNumericallyMatcher) FailureMessage(actual interface{}) (message string) { return matcher.FormatFailureMessage(actual, false) } func (matcher *BeNumericallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { return matcher.FormatFailureMessage(actual, true) } func (matcher *BeNumericallyMatcher) FormatFailureMessage(actual interface{}, negated bool) (message string) { if len(matcher.CompareTo) == 1 { message = fmt.Sprintf("to be %s", matcher.Comparator) } else { message = fmt.Sprintf("to be within %v of %s", matcher.CompareTo[1], matcher.Comparator) } if negated { message = "not " + message } return format.Message(actual, message, matcher.CompareTo[0]) } func (matcher *BeNumericallyMatcher) Match(actual interface{}) (success bool, err error) { if len(matcher.CompareTo) == 0 || len(matcher.CompareTo) > 2 { return false, fmt.Errorf("BeNumerically requires 1 or 2 CompareTo arguments. Got:\n%s", format.Object(matcher.CompareTo, 1)) } if !isNumber(actual) { return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(actual, 1)) } if !isNumber(matcher.CompareTo[0]) { return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1)) } if len(matcher.CompareTo) == 2 && !isNumber(matcher.CompareTo[1]) { return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[1], 1)) } switch matcher.Comparator { case "==", "~", ">", ">=", "<", "<=": default: return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) } if isFloat(actual) || isFloat(matcher.CompareTo[0]) { var secondOperand float64 = 1e-8 if len(matcher.CompareTo) == 2 { secondOperand = toFloat(matcher.CompareTo[1]) } success = matcher.matchFloats(toFloat(actual), toFloat(matcher.CompareTo[0]), secondOperand) } else if isInteger(actual) { var secondOperand int64 = 0 if len(matcher.CompareTo) == 2 { secondOperand = toInteger(matcher.CompareTo[1]) } success = matcher.matchIntegers(toInteger(actual), toInteger(matcher.CompareTo[0]), secondOperand) } else if isUnsignedInteger(actual) { var secondOperand uint64 = 0 if len(matcher.CompareTo) == 2 { secondOperand = toUnsignedInteger(matcher.CompareTo[1]) } success = matcher.matchUnsignedIntegers(toUnsignedInteger(actual), toUnsignedInteger(matcher.CompareTo[0]), secondOperand) } else { return false, fmt.Errorf("Failed to compare:\n%s\n%s:\n%s", format.Object(actual, 1), matcher.Comparator, format.Object(matcher.CompareTo[0], 1)) } return success, nil } func (matcher *BeNumericallyMatcher) matchIntegers(actual, compareTo, threshold int64) (success bool) { switch matcher.Comparator { case "==", "~": diff := actual - compareTo return -threshold <= diff && diff <= threshold case ">": return (actual > compareTo) case ">=": return (actual >= compareTo) case "<": return (actual < compareTo) case "<=": return (actual <= compareTo) } return false } func (matcher *BeNumericallyMatcher) matchUnsignedIntegers(actual, compareTo, threshold uint64) (success bool) { switch matcher.Comparator { case "==", "~": if actual < compareTo { actual, compareTo = compareTo, actual } return actual-compareTo <= threshold case ">": return (actual > compareTo) case ">=": return (actual >= compareTo) case "<": return (actual < compareTo) case "<=": return (actual <= compareTo) } return false } func (matcher *BeNumericallyMatcher) matchFloats(actual, compareTo, threshold float64) (success bool) { switch matcher.Comparator { case "~": return math.Abs(actual-compareTo) <= threshold case "==": return (actual == compareTo) case ">": return (actual > compareTo) case ">=": return (actual >= compareTo) case "<": return (actual < compareTo) case "<=": return (actual <= compareTo) } return false } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_sent_matcher.go ================================================ // untested sections: 3 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type BeSentMatcher struct { Arg interface{} channelClosed bool } func (matcher *BeSentMatcher) Match(actual interface{}) (success bool, err error) { if !isChan(actual) { return false, fmt.Errorf("BeSent expects a channel. Got:\n%s", format.Object(actual, 1)) } channelType := reflect.TypeOf(actual) channelValue := reflect.ValueOf(actual) if channelType.ChanDir() == reflect.RecvDir { return false, fmt.Errorf("BeSent matcher cannot be passed a receive-only channel. Got:\n%s", format.Object(actual, 1)) } argType := reflect.TypeOf(matcher.Arg) assignable := argType.AssignableTo(channelType.Elem()) if !assignable { return false, fmt.Errorf("Cannot pass:\n%s to the channel:\n%s\nThe types don't match.", format.Object(matcher.Arg, 1), format.Object(actual, 1)) } argValue := reflect.ValueOf(matcher.Arg) defer func() { if e := recover(); e != nil { success = false err = fmt.Errorf("Cannot send to a closed channel") matcher.channelClosed = true } }() winnerIndex, _, _ := reflect.Select([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: channelValue, Send: argValue}, {Dir: reflect.SelectDefault}, }) var didSend bool if winnerIndex == 0 { didSend = true } return didSend, nil } func (matcher *BeSentMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to send:", matcher.Arg) } func (matcher *BeSentMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to send:", matcher.Arg) } func (matcher *BeSentMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { if !isChan(actual) { return false } return !matcher.channelClosed } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.go ================================================ // untested sections: 3 package matchers import ( "fmt" "time" "github.com/onsi/gomega/format" ) type BeTemporallyMatcher struct { Comparator string CompareTo time.Time Threshold []time.Duration } func (matcher *BeTemporallyMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo) } func (matcher *BeTemporallyMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo) } func (matcher *BeTemporallyMatcher) Match(actual interface{}) (bool, error) { // predicate to test for time.Time type isTime := func(t interface{}) bool { _, ok := t.(time.Time) return ok } if !isTime(actual) { return false, fmt.Errorf("Expected a time.Time. Got:\n%s", format.Object(actual, 1)) } switch matcher.Comparator { case "==", "~", ">", ">=", "<", "<=": default: return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator) } var threshold = time.Millisecond if len(matcher.Threshold) == 1 { threshold = matcher.Threshold[0] } return matcher.matchTimes(actual.(time.Time), matcher.CompareTo, threshold), nil } func (matcher *BeTemporallyMatcher) matchTimes(actual, compareTo time.Time, threshold time.Duration) (success bool) { switch matcher.Comparator { case "==": return actual.Equal(compareTo) case "~": diff := actual.Sub(compareTo) return -threshold <= diff && diff <= threshold case ">": return actual.After(compareTo) case ">=": return !actual.Before(compareTo) case "<": return actual.Before(compareTo) case "<=": return !actual.After(compareTo) } return false } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_true_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type BeTrueMatcher struct { } func (matcher *BeTrueMatcher) Match(actual interface{}) (success bool, err error) { if !isBool(actual) { return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1)) } return actual.(bool), nil } func (matcher *BeTrueMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be true") } func (matcher *BeTrueMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be true") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/be_zero_matcher.go ================================================ package matchers import ( "reflect" "github.com/onsi/gomega/format" ) type BeZeroMatcher struct { } func (matcher *BeZeroMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil { return true, nil } zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() return reflect.DeepEqual(zeroValue, actual), nil } func (matcher *BeZeroMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to be zero-valued") } func (matcher *BeZeroMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to be zero-valued") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/consist_of.go ================================================ // untested sections: 3 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" ) type ConsistOfMatcher struct { Elements []interface{} missingElements []interface{} extraElements []interface{} } func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) { if !isArrayOrSlice(actual) && !isMap(actual) { return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) } matchers := matchers(matcher.Elements) values := valuesOf(actual) bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours) if err != nil { return false, err } edges := bipartiteGraph.LargestMatching() if len(edges) == len(values) && len(edges) == len(matchers) { return true, nil } var missingMatchers []interface{} matcher.extraElements, missingMatchers = bipartiteGraph.FreeLeftRight(edges) matcher.missingElements = equalMatchersToElements(missingMatchers) return false, nil } func neighbours(value, matcher interface{}) (bool, error) { match, err := matcher.(omegaMatcher).Match(value) return match && err == nil, nil } func equalMatchersToElements(matchers []interface{}) (elements []interface{}) { for _, matcher := range matchers { equalMatcher, ok := matcher.(*EqualMatcher) if ok { matcher = equalMatcher.Expected } elements = append(elements, matcher) } return } func flatten(elems []interface{}) []interface{} { if len(elems) != 1 || !isArrayOrSlice(elems[0]) { return elems } value := reflect.ValueOf(elems[0]) flattened := make([]interface{}, value.Len()) for i := 0; i < value.Len(); i++ { flattened[i] = value.Index(i).Interface() } return flattened } func matchers(expectedElems []interface{}) (matchers []interface{}) { for _, e := range flatten(expectedElems) { matcher, isMatcher := e.(omegaMatcher) if !isMatcher { matcher = &EqualMatcher{Expected: e} } matchers = append(matchers, matcher) } return } func presentable(elems []interface{}) interface{} { elems = flatten(elems) if len(elems) == 0 { return []interface{}{} } sv := reflect.ValueOf(elems) tt := sv.Index(0).Elem().Type() for i := 1; i < sv.Len(); i++ { if sv.Index(i).Elem().Type() != tt { return elems } } ss := reflect.MakeSlice(reflect.SliceOf(tt), sv.Len(), sv.Len()) for i := 0; i < sv.Len(); i++ { ss.Index(i).Set(sv.Index(i).Elem()) } return ss.Interface() } func valuesOf(actual interface{}) []interface{} { value := reflect.ValueOf(actual) values := []interface{}{} if isMap(actual) { keys := value.MapKeys() for i := 0; i < value.Len(); i++ { values = append(values, value.MapIndex(keys[i]).Interface()) } } else { for i := 0; i < value.Len(); i++ { values = append(values, value.Index(i).Interface()) } } return values } func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) { message = format.Message(actual, "to consist of", presentable(matcher.Elements)) message = appendMissingElements(message, matcher.missingElements) if len(matcher.extraElements) > 0 { message = fmt.Sprintf("%s\nthe extra elements were\n%s", message, format.Object(presentable(matcher.extraElements), 1)) } return } func appendMissingElements(message string, missingElements []interface{}) string { if len(missingElements) == 0 { return message } return fmt.Sprintf("%s\nthe missing elements were\n%s", message, format.Object(presentable(missingElements), 1)) } func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to consist of", presentable(matcher.Elements)) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/contain_element_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type ContainElementMatcher struct { Element interface{} } func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, err error) { if !isArrayOrSlice(actual) && !isMap(actual) { return false, fmt.Errorf("ContainElement matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) } elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher) if !elementIsMatcher { elemMatcher = &EqualMatcher{Expected: matcher.Element} } value := reflect.ValueOf(actual) var valueAt func(int) interface{} if isMap(actual) { keys := value.MapKeys() valueAt = func(i int) interface{} { return value.MapIndex(keys[i]).Interface() } } else { valueAt = func(i int) interface{} { return value.Index(i).Interface() } } var lastError error for i := 0; i < value.Len(); i++ { success, err := elemMatcher.Match(valueAt(i)) if err != nil { lastError = err continue } if success { return true, nil } } return false, lastError } func (matcher *ContainElementMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to contain element matching", matcher.Element) } func (matcher *ContainElementMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to contain element matching", matcher.Element) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/contain_elements_matcher.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" ) type ContainElementsMatcher struct { Elements []interface{} missingElements []interface{} } func (matcher *ContainElementsMatcher) Match(actual interface{}) (success bool, err error) { if !isArrayOrSlice(actual) && !isMap(actual) { return false, fmt.Errorf("ContainElements matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1)) } matchers := matchers(matcher.Elements) bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(valuesOf(actual), matchers, neighbours) if err != nil { return false, err } edges := bipartiteGraph.LargestMatching() if len(edges) == len(matchers) { return true, nil } _, missingMatchers := bipartiteGraph.FreeLeftRight(edges) matcher.missingElements = equalMatchersToElements(missingMatchers) return false, nil } func (matcher *ContainElementsMatcher) FailureMessage(actual interface{}) (message string) { message = format.Message(actual, "to contain elements", presentable(matcher.Elements)) return appendMissingElements(message, matcher.missingElements) } func (matcher *ContainElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to contain elements", presentable(matcher.Elements)) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "strings" "github.com/onsi/gomega/format" ) type ContainSubstringMatcher struct { Substr string Args []interface{} } func (matcher *ContainSubstringMatcher) Match(actual interface{}) (success bool, err error) { actualString, ok := toString(actual) if !ok { return false, fmt.Errorf("ContainSubstring matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) } return strings.Contains(actualString, matcher.stringToMatch()), nil } func (matcher *ContainSubstringMatcher) stringToMatch() string { stringToMatch := matcher.Substr if len(matcher.Args) > 0 { stringToMatch = fmt.Sprintf(matcher.Substr, matcher.Args...) } return stringToMatch } func (matcher *ContainSubstringMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to contain substring", matcher.stringToMatch()) } func (matcher *ContainSubstringMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to contain substring", matcher.stringToMatch()) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/equal_matcher.go ================================================ package matchers import ( "bytes" "fmt" "reflect" "github.com/onsi/gomega/format" ) type EqualMatcher struct { Expected interface{} } func (matcher *EqualMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil && matcher.Expected == nil { return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") } // Shortcut for byte slices. // Comparing long byte slices with reflect.DeepEqual is very slow, // so use bytes.Equal if actual and expected are both byte slices. if actualByteSlice, ok := actual.([]byte); ok { if expectedByteSlice, ok := matcher.Expected.([]byte); ok { return bytes.Equal(actualByteSlice, expectedByteSlice), nil } } return reflect.DeepEqual(actual, matcher.Expected), nil } func (matcher *EqualMatcher) FailureMessage(actual interface{}) (message string) { actualString, actualOK := actual.(string) expectedString, expectedOK := matcher.Expected.(string) if actualOK && expectedOK { return format.MessageWithDiff(actualString, "to equal", expectedString) } return format.Message(actual, "to equal", matcher.Expected) } func (matcher *EqualMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to equal", matcher.Expected) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_cap_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type HaveCapMatcher struct { Count int } func (matcher *HaveCapMatcher) Match(actual interface{}) (success bool, err error) { length, ok := capOf(actual) if !ok { return false, fmt.Errorf("HaveCap matcher expects a array/channel/slice. Got:\n%s", format.Object(actual, 1)) } return length == matcher.Count, nil } func (matcher *HaveCapMatcher) FailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\nto have capacity %d", format.Object(actual, 1), matcher.Count) } func (matcher *HaveCapMatcher) NegatedFailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\nnot to have capacity %d", format.Object(actual, 1), matcher.Count) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_field.go ================================================ package matchers import ( "fmt" "reflect" "strings" "github.com/onsi/gomega/format" ) func extractField(actual interface{}, field string) (interface{}, error) { fields := strings.SplitN(field, ".", 2) actualValue := reflect.ValueOf(actual) if actualValue.Kind() != reflect.Struct { return nil, fmt.Errorf("HaveField encountered:\n%s\nWhich is not a struct.", format.Object(actual, 1)) } var extractedValue reflect.Value if strings.HasSuffix(fields[0], "()") { extractedValue = actualValue.MethodByName(strings.TrimSuffix(fields[0], "()")) if extractedValue == (reflect.Value{}) { return nil, fmt.Errorf("HaveField could not find method named '%s' in struct of type %T.", fields[0], actual) } t := extractedValue.Type() if t.NumIn() != 0 || t.NumOut() != 1 { return nil, fmt.Errorf("HaveField found an invalid method named '%s' in struct of type %T.\nMethods must take no arguments and return exactly one value.", fields[0], actual) } extractedValue = extractedValue.Call([]reflect.Value{})[0] } else { extractedValue = actualValue.FieldByName(fields[0]) if extractedValue == (reflect.Value{}) { return nil, fmt.Errorf("HaveField could not find field named '%s' in struct:\n%s", fields[0], format.Object(actual, 1)) } } if len(fields) == 1 { return extractedValue.Interface(), nil } else { return extractField(extractedValue.Interface(), fields[1]) } } type HaveFieldMatcher struct { Field string Expected interface{} extractedField interface{} expectedMatcher omegaMatcher } func (matcher *HaveFieldMatcher) Match(actual interface{}) (success bool, err error) { matcher.extractedField, err = extractField(actual, matcher.Field) if err != nil { return false, err } var isMatcher bool matcher.expectedMatcher, isMatcher = matcher.Expected.(omegaMatcher) if !isMatcher { matcher.expectedMatcher = &EqualMatcher{Expected: matcher.Expected} } return matcher.expectedMatcher.Match(matcher.extractedField) } func (matcher *HaveFieldMatcher) FailureMessage(actual interface{}) (message string) { message = fmt.Sprintf("Value for field '%s' failed to satisfy matcher.\n", matcher.Field) message += matcher.expectedMatcher.FailureMessage(matcher.extractedField) return message } func (matcher *HaveFieldMatcher) NegatedFailureMessage(actual interface{}) (message string) { message = fmt.Sprintf("Value for field '%s' satisfied matcher, but should not have.\n", matcher.Field) message += matcher.expectedMatcher.NegatedFailureMessage(matcher.extractedField) return message } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_http_body_matcher.go ================================================ package matchers import ( "fmt" "io" "net/http" "net/http/httptest" "github.com/onsi/gomega/format" "github.com/onsi/gomega/types" ) type HaveHTTPBodyMatcher struct { Expected interface{} cachedBody []byte } func (matcher *HaveHTTPBodyMatcher) Match(actual interface{}) (bool, error) { body, err := matcher.body(actual) if err != nil { return false, err } switch e := matcher.Expected.(type) { case string: return (&EqualMatcher{Expected: e}).Match(string(body)) case []byte: return (&EqualMatcher{Expected: e}).Match(body) case types.GomegaMatcher: return e.Match(body) default: return false, fmt.Errorf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) } } func (matcher *HaveHTTPBodyMatcher) FailureMessage(actual interface{}) (message string) { body, err := matcher.body(actual) if err != nil { return fmt.Sprintf("failed to read body: %s", err) } switch e := matcher.Expected.(type) { case string: return (&EqualMatcher{Expected: e}).FailureMessage(string(body)) case []byte: return (&EqualMatcher{Expected: e}).FailureMessage(body) case types.GomegaMatcher: return e.FailureMessage(body) default: return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) } } func (matcher *HaveHTTPBodyMatcher) NegatedFailureMessage(actual interface{}) (message string) { body, err := matcher.body(actual) if err != nil { return fmt.Sprintf("failed to read body: %s", err) } switch e := matcher.Expected.(type) { case string: return (&EqualMatcher{Expected: e}).NegatedFailureMessage(string(body)) case []byte: return (&EqualMatcher{Expected: e}).NegatedFailureMessage(body) case types.GomegaMatcher: return e.NegatedFailureMessage(body) default: return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) } } // body returns the body. It is cached because once we read it in Match() // the Reader is closed and it is not readable again in FailureMessage() // or NegatedFailureMessage() func (matcher *HaveHTTPBodyMatcher) body(actual interface{}) ([]byte, error) { if matcher.cachedBody != nil { return matcher.cachedBody, nil } body := func(a *http.Response) ([]byte, error) { if a.Body != nil { defer a.Body.Close() var err error matcher.cachedBody, err = io.ReadAll(a.Body) if err != nil { return nil, fmt.Errorf("error reading response body: %w", err) } } return matcher.cachedBody, nil } switch a := actual.(type) { case *http.Response: return body(a) case *httptest.ResponseRecorder: return body(a.Result()) default: return nil, fmt.Errorf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_http_header_with_value_matcher.go ================================================ package matchers import ( "fmt" "net/http" "net/http/httptest" "github.com/onsi/gomega/format" "github.com/onsi/gomega/types" ) type HaveHTTPHeaderWithValueMatcher struct { Header string Value interface{} } func (matcher *HaveHTTPHeaderWithValueMatcher) Match(actual interface{}) (success bool, err error) { headerValue, err := matcher.extractHeader(actual) if err != nil { return false, err } headerMatcher, err := matcher.getSubMatcher() if err != nil { return false, err } return headerMatcher.Match(headerValue) } func (matcher *HaveHTTPHeaderWithValueMatcher) FailureMessage(actual interface{}) string { headerValue, err := matcher.extractHeader(actual) if err != nil { panic(err) // protected by Match() } headerMatcher, err := matcher.getSubMatcher() if err != nil { panic(err) // protected by Match() } diff := format.IndentString(headerMatcher.FailureMessage(headerValue), 1) return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff) } func (matcher *HaveHTTPHeaderWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) { headerValue, err := matcher.extractHeader(actual) if err != nil { panic(err) // protected by Match() } headerMatcher, err := matcher.getSubMatcher() if err != nil { panic(err) // protected by Match() } diff := format.IndentString(headerMatcher.NegatedFailureMessage(headerValue), 1) return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff) } func (matcher *HaveHTTPHeaderWithValueMatcher) getSubMatcher() (types.GomegaMatcher, error) { switch m := matcher.Value.(type) { case string: return &EqualMatcher{Expected: matcher.Value}, nil case types.GomegaMatcher: return m, nil default: return nil, fmt.Errorf("HaveHTTPHeaderWithValue matcher must be passed a string or a GomegaMatcher. Got:\n%s", format.Object(matcher.Value, 1)) } } func (matcher *HaveHTTPHeaderWithValueMatcher) extractHeader(actual interface{}) (string, error) { switch r := actual.(type) { case *http.Response: return r.Header.Get(matcher.Header), nil case *httptest.ResponseRecorder: return r.Result().Header.Get(matcher.Header), nil default: return "", fmt.Errorf("HaveHTTPHeaderWithValue matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.go ================================================ package matchers import ( "fmt" "io" "net/http" "net/http/httptest" "reflect" "strings" "github.com/onsi/gomega/format" ) type HaveHTTPStatusMatcher struct { Expected []interface{} } func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, err error) { var resp *http.Response switch a := actual.(type) { case *http.Response: resp = a case *httptest.ResponseRecorder: resp = a.Result() default: return false, fmt.Errorf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) } if len(matcher.Expected) == 0 { return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got nothing") } for _, expected := range matcher.Expected { switch e := expected.(type) { case int: if resp.StatusCode == e { return true, nil } case string: if resp.Status == e { return true, nil } default: return false, fmt.Errorf("HaveHTTPStatus matcher must be passed int or string types. Got:\n%s", format.Object(expected, 1)) } } return false, nil } func (matcher *HaveHTTPStatusMatcher) FailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "to have HTTP status", matcher.expectedString()) } func (matcher *HaveHTTPStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "not to have HTTP status", matcher.expectedString()) } func (matcher *HaveHTTPStatusMatcher) expectedString() string { var lines []string for _, expected := range matcher.Expected { lines = append(lines, format.Object(expected, 1)) } return strings.Join(lines, "\n") } func formatHttpResponse(input interface{}) string { var resp *http.Response switch r := input.(type) { case *http.Response: resp = r case *httptest.ResponseRecorder: resp = r.Result() default: return "cannot format invalid HTTP response" } body := "" if resp.Body != nil { defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { data = []byte("") } body = format.Object(string(data), 0) } var s strings.Builder s.WriteString(fmt.Sprintf("%s<%s>: {\n", format.Indent, reflect.TypeOf(input))) s.WriteString(fmt.Sprintf("%s%sStatus: %s\n", format.Indent, format.Indent, format.Object(resp.Status, 0))) s.WriteString(fmt.Sprintf("%s%sStatusCode: %s\n", format.Indent, format.Indent, format.Object(resp.StatusCode, 0))) s.WriteString(fmt.Sprintf("%s%sBody: %s\n", format.Indent, format.Indent, body)) s.WriteString(fmt.Sprintf("%s}", format.Indent)) return s.String() } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_key_matcher.go ================================================ // untested sections: 6 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type HaveKeyMatcher struct { Key interface{} } func (matcher *HaveKeyMatcher) Match(actual interface{}) (success bool, err error) { if !isMap(actual) { return false, fmt.Errorf("HaveKey matcher expects a map. Got:%s", format.Object(actual, 1)) } keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) if !keyIsMatcher { keyMatcher = &EqualMatcher{Expected: matcher.Key} } keys := reflect.ValueOf(actual).MapKeys() for i := 0; i < len(keys); i++ { success, err := keyMatcher.Match(keys[i].Interface()) if err != nil { return false, fmt.Errorf("HaveKey's key matcher failed with:\n%s%s", format.Indent, err.Error()) } if success { return true, nil } } return false, nil } func (matcher *HaveKeyMatcher) FailureMessage(actual interface{}) (message string) { switch matcher.Key.(type) { case omegaMatcher: return format.Message(actual, "to have key matching", matcher.Key) default: return format.Message(actual, "to have key", matcher.Key) } } func (matcher *HaveKeyMatcher) NegatedFailureMessage(actual interface{}) (message string) { switch matcher.Key.(type) { case omegaMatcher: return format.Message(actual, "not to have key matching", matcher.Key) default: return format.Message(actual, "not to have key", matcher.Key) } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go ================================================ // untested sections:10 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type HaveKeyWithValueMatcher struct { Key interface{} Value interface{} } func (matcher *HaveKeyWithValueMatcher) Match(actual interface{}) (success bool, err error) { if !isMap(actual) { return false, fmt.Errorf("HaveKeyWithValue matcher expects a map. Got:%s", format.Object(actual, 1)) } keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher) if !keyIsMatcher { keyMatcher = &EqualMatcher{Expected: matcher.Key} } valueMatcher, valueIsMatcher := matcher.Value.(omegaMatcher) if !valueIsMatcher { valueMatcher = &EqualMatcher{Expected: matcher.Value} } keys := reflect.ValueOf(actual).MapKeys() for i := 0; i < len(keys); i++ { success, err := keyMatcher.Match(keys[i].Interface()) if err != nil { return false, fmt.Errorf("HaveKeyWithValue's key matcher failed with:\n%s%s", format.Indent, err.Error()) } if success { actualValue := reflect.ValueOf(actual).MapIndex(keys[i]) success, err := valueMatcher.Match(actualValue.Interface()) if err != nil { return false, fmt.Errorf("HaveKeyWithValue's value matcher failed with:\n%s%s", format.Indent, err.Error()) } return success, nil } } return false, nil } func (matcher *HaveKeyWithValueMatcher) FailureMessage(actual interface{}) (message string) { str := "to have {key: value}" if _, ok := matcher.Key.(omegaMatcher); ok { str += " matching" } else if _, ok := matcher.Value.(omegaMatcher); ok { str += " matching" } expect := make(map[interface{}]interface{}, 1) expect[matcher.Key] = matcher.Value return format.Message(actual, str, expect) } func (matcher *HaveKeyWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) { kStr := "not to have key" if _, ok := matcher.Key.(omegaMatcher); ok { kStr = "not to have key matching" } vStr := "or that key's value not be" if _, ok := matcher.Value.(omegaMatcher); ok { vStr = "or to have that key's value not matching" } return format.Message(actual, kStr, matcher.Key, vStr, matcher.Value) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_len_matcher.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type HaveLenMatcher struct { Count int } func (matcher *HaveLenMatcher) Match(actual interface{}) (success bool, err error) { length, ok := lengthOf(actual) if !ok { return false, fmt.Errorf("HaveLen matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1)) } return length == matcher.Count, nil } func (matcher *HaveLenMatcher) FailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\nto have length %d", format.Object(actual, 1), matcher.Count) } func (matcher *HaveLenMatcher) NegatedFailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected\n%s\nnot to have length %d", format.Object(actual, 1), matcher.Count) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.go ================================================ // untested sections: 2 package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type HaveOccurredMatcher struct { } func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { // is purely nil? if actual == nil { return false, nil } // must be an 'error' type if !isError(actual) { return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } // must be non-nil (or a pointer to a non-nil) return !isNil(actual), nil } func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected an error to have occurred. Got:\n%s", format.Object(actual, 1)) } func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Unexpected error:\n%s\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1), "occurred") } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_prefix_matcher.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type HavePrefixMatcher struct { Prefix string Args []interface{} } func (matcher *HavePrefixMatcher) Match(actual interface{}) (success bool, err error) { actualString, ok := toString(actual) if !ok { return false, fmt.Errorf("HavePrefix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) } prefix := matcher.prefix() return len(actualString) >= len(prefix) && actualString[0:len(prefix)] == prefix, nil } func (matcher *HavePrefixMatcher) prefix() string { if len(matcher.Args) > 0 { return fmt.Sprintf(matcher.Prefix, matcher.Args...) } return matcher.Prefix } func (matcher *HavePrefixMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to have prefix", matcher.prefix()) } func (matcher *HavePrefixMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to have prefix", matcher.prefix()) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/have_suffix_matcher.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type HaveSuffixMatcher struct { Suffix string Args []interface{} } func (matcher *HaveSuffixMatcher) Match(actual interface{}) (success bool, err error) { actualString, ok := toString(actual) if !ok { return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) } suffix := matcher.suffix() return len(actualString) >= len(suffix) && actualString[len(actualString)-len(suffix):] == suffix, nil } func (matcher *HaveSuffixMatcher) suffix() string { if len(matcher.Args) > 0 { return fmt.Sprintf(matcher.Suffix, matcher.Args...) } return matcher.Suffix } func (matcher *HaveSuffixMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to have suffix", matcher.suffix()) } func (matcher *HaveSuffixMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to have suffix", matcher.suffix()) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/match_error_matcher.go ================================================ package matchers import ( "errors" "fmt" "reflect" "github.com/onsi/gomega/format" ) type MatchErrorMatcher struct { Expected interface{} } func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err error) { if isNil(actual) { return false, fmt.Errorf("Expected an error, got nil") } if !isError(actual) { return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) } actualErr := actual.(error) expected := matcher.Expected if isError(expected) { return reflect.DeepEqual(actualErr, expected) || errors.Is(actualErr, expected.(error)), nil } if isString(expected) { return actualErr.Error() == expected, nil } var subMatcher omegaMatcher var hasSubMatcher bool if expected != nil { subMatcher, hasSubMatcher = (expected).(omegaMatcher) if hasSubMatcher { return subMatcher.Match(actualErr.Error()) } } return false, fmt.Errorf( "MatchError must be passed an error, a string, or a Matcher that can match on strings. Got:\n%s", format.Object(expected, 1)) } func (matcher *MatchErrorMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to match error", matcher.Expected) } func (matcher *MatchErrorMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to match error", matcher.Expected) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/match_json_matcher.go ================================================ package matchers import ( "bytes" "encoding/json" "fmt" "github.com/onsi/gomega/format" ) type MatchJSONMatcher struct { JSONToMatch interface{} firstFailurePath []interface{} } func (matcher *MatchJSONMatcher) Match(actual interface{}) (success bool, err error) { actualString, expectedString, err := matcher.prettyPrint(actual) if err != nil { return false, err } var aval interface{} var eval interface{} // this is guarded by prettyPrint json.Unmarshal([]byte(actualString), &aval) json.Unmarshal([]byte(expectedString), &eval) var equal bool equal, matcher.firstFailurePath = deepEqual(aval, eval) return equal, nil } func (matcher *MatchJSONMatcher) FailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.prettyPrint(actual) return formattedMessage(format.Message(actualString, "to match JSON of", expectedString), matcher.firstFailurePath) } func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.prettyPrint(actual) return formattedMessage(format.Message(actualString, "not to match JSON of", expectedString), matcher.firstFailurePath) } func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) { actualString, ok := toString(actual) if !ok { return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1)) } expectedString, ok := toString(matcher.JSONToMatch) if !ok { return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.JSONToMatch, 1)) } abuf := new(bytes.Buffer) ebuf := new(bytes.Buffer) if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil { return "", "", fmt.Errorf("Actual '%s' should be valid JSON, but it is not.\nUnderlying error:%s", actualString, err) } if err := json.Indent(ebuf, []byte(expectedString), "", " "); err != nil { return "", "", fmt.Errorf("Expected '%s' should be valid JSON, but it is not.\nUnderlying error:%s", expectedString, err) } return abuf.String(), ebuf.String(), nil } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.go ================================================ package matchers import ( "fmt" "regexp" "github.com/onsi/gomega/format" ) type MatchRegexpMatcher struct { Regexp string Args []interface{} } func (matcher *MatchRegexpMatcher) Match(actual interface{}) (success bool, err error) { actualString, ok := toString(actual) if !ok { return false, fmt.Errorf("RegExp matcher requires a string or stringer.\nGot:%s", format.Object(actual, 1)) } match, err := regexp.Match(matcher.regexp(), []byte(actualString)) if err != nil { return false, fmt.Errorf("RegExp match failed to compile with error:\n\t%s", err.Error()) } return match, nil } func (matcher *MatchRegexpMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to match regular expression", matcher.regexp()) } func (matcher *MatchRegexpMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "not to match regular expression", matcher.regexp()) } func (matcher *MatchRegexpMatcher) regexp() string { re := matcher.Regexp if len(matcher.Args) > 0 { re = fmt.Sprintf(matcher.Regexp, matcher.Args...) } return re } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/match_xml_matcher.go ================================================ package matchers import ( "bytes" "encoding/xml" "errors" "fmt" "io" "reflect" "sort" "strings" "github.com/onsi/gomega/format" "golang.org/x/net/html/charset" ) type MatchXMLMatcher struct { XMLToMatch interface{} } func (matcher *MatchXMLMatcher) Match(actual interface{}) (success bool, err error) { actualString, expectedString, err := matcher.formattedPrint(actual) if err != nil { return false, err } aval, err := parseXmlContent(actualString) if err != nil { return false, fmt.Errorf("Actual '%s' should be valid XML, but it is not.\nUnderlying error:%s", actualString, err) } eval, err := parseXmlContent(expectedString) if err != nil { return false, fmt.Errorf("Expected '%s' should be valid XML, but it is not.\nUnderlying error:%s", expectedString, err) } return reflect.DeepEqual(aval, eval), nil } func (matcher *MatchXMLMatcher) FailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.formattedPrint(actual) return fmt.Sprintf("Expected\n%s\nto match XML of\n%s", actualString, expectedString) } func (matcher *MatchXMLMatcher) NegatedFailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.formattedPrint(actual) return fmt.Sprintf("Expected\n%s\nnot to match XML of\n%s", actualString, expectedString) } func (matcher *MatchXMLMatcher) formattedPrint(actual interface{}) (actualString, expectedString string, err error) { var ok bool actualString, ok = toString(actual) if !ok { return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1)) } expectedString, ok = toString(matcher.XMLToMatch) if !ok { return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.XMLToMatch, 1)) } return actualString, expectedString, nil } func parseXmlContent(content string) (*xmlNode, error) { allNodes := []*xmlNode{} dec := newXmlDecoder(strings.NewReader(content)) for { tok, err := dec.Token() if err != nil { if err == io.EOF { break } return nil, fmt.Errorf("failed to decode next token: %v", err) // untested section } lastNodeIndex := len(allNodes) - 1 var lastNode *xmlNode if len(allNodes) > 0 { lastNode = allNodes[lastNodeIndex] } else { lastNode = &xmlNode{} } switch tok := tok.(type) { case xml.StartElement: attrs := attributesSlice(tok.Attr) sort.Sort(attrs) allNodes = append(allNodes, &xmlNode{XMLName: tok.Name, XMLAttr: tok.Attr}) case xml.EndElement: if len(allNodes) > 1 { allNodes[lastNodeIndex-1].Nodes = append(allNodes[lastNodeIndex-1].Nodes, lastNode) allNodes = allNodes[:lastNodeIndex] } case xml.CharData: lastNode.Content = append(lastNode.Content, tok.Copy()...) case xml.Comment: lastNode.Comments = append(lastNode.Comments, tok.Copy()) // untested section case xml.ProcInst: lastNode.ProcInsts = append(lastNode.ProcInsts, tok.Copy()) } } if len(allNodes) == 0 { return nil, errors.New("found no nodes") } firstNode := allNodes[0] trimParentNodesContentSpaces(firstNode) return firstNode, nil } func newXmlDecoder(reader io.Reader) *xml.Decoder { dec := xml.NewDecoder(reader) dec.CharsetReader = charset.NewReaderLabel return dec } func trimParentNodesContentSpaces(node *xmlNode) { if len(node.Nodes) > 0 { node.Content = bytes.TrimSpace(node.Content) for _, childNode := range node.Nodes { trimParentNodesContentSpaces(childNode) } } } type xmlNode struct { XMLName xml.Name Comments []xml.Comment ProcInsts []xml.ProcInst XMLAttr []xml.Attr Content []byte Nodes []*xmlNode } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/match_yaml_matcher.go ================================================ package matchers import ( "fmt" "strings" "github.com/onsi/gomega/format" "gopkg.in/yaml.v2" ) type MatchYAMLMatcher struct { YAMLToMatch interface{} firstFailurePath []interface{} } func (matcher *MatchYAMLMatcher) Match(actual interface{}) (success bool, err error) { actualString, expectedString, err := matcher.toStrings(actual) if err != nil { return false, err } var aval interface{} var eval interface{} if err := yaml.Unmarshal([]byte(actualString), &aval); err != nil { return false, fmt.Errorf("Actual '%s' should be valid YAML, but it is not.\nUnderlying error:%s", actualString, err) } if err := yaml.Unmarshal([]byte(expectedString), &eval); err != nil { return false, fmt.Errorf("Expected '%s' should be valid YAML, but it is not.\nUnderlying error:%s", expectedString, err) } var equal bool equal, matcher.firstFailurePath = deepEqual(aval, eval) return equal, nil } func (matcher *MatchYAMLMatcher) FailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.toNormalisedStrings(actual) return formattedMessage(format.Message(actualString, "to match YAML of", expectedString), matcher.firstFailurePath) } func (matcher *MatchYAMLMatcher) NegatedFailureMessage(actual interface{}) (message string) { actualString, expectedString, _ := matcher.toNormalisedStrings(actual) return formattedMessage(format.Message(actualString, "not to match YAML of", expectedString), matcher.firstFailurePath) } func (matcher *MatchYAMLMatcher) toNormalisedStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) { actualString, expectedString, err := matcher.toStrings(actual) return normalise(actualString), normalise(expectedString), err } func normalise(input string) string { var val interface{} err := yaml.Unmarshal([]byte(input), &val) if err != nil { panic(err) // unreachable since Match already calls Unmarshal } output, err := yaml.Marshal(val) if err != nil { panic(err) // untested section, unreachable since we Unmarshal above } return strings.TrimSpace(string(output)) } func (matcher *MatchYAMLMatcher) toStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) { actualString, ok := toString(actual) if !ok { return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1)) } expectedString, ok := toString(matcher.YAMLToMatch) if !ok { return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.YAMLToMatch, 1)) } return actualString, expectedString, nil } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/not.go ================================================ package matchers import ( "github.com/onsi/gomega/types" ) type NotMatcher struct { Matcher types.GomegaMatcher } func (m *NotMatcher) Match(actual interface{}) (bool, error) { success, err := m.Matcher.Match(actual) if err != nil { return false, err } return !success, nil } func (m *NotMatcher) FailureMessage(actual interface{}) (message string) { return m.Matcher.NegatedFailureMessage(actual) // works beautifully } func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string) { return m.Matcher.FailureMessage(actual) // works beautifully } func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/or.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" "github.com/onsi/gomega/types" ) type OrMatcher struct { Matchers []types.GomegaMatcher // state firstSuccessfulMatcher types.GomegaMatcher } func (m *OrMatcher) Match(actual interface{}) (success bool, err error) { m.firstSuccessfulMatcher = nil for _, matcher := range m.Matchers { success, err := matcher.Match(actual) if err != nil { return false, err } if success { m.firstSuccessfulMatcher = matcher return true, nil } } return false, nil } func (m *OrMatcher) FailureMessage(actual interface{}) (message string) { // not the most beautiful list of matchers, but not bad either... return format.Message(actual, fmt.Sprintf("To satisfy at least one of these matchers: %s", m.Matchers)) } func (m *OrMatcher) NegatedFailureMessage(actual interface{}) (message string) { return m.firstSuccessfulMatcher.NegatedFailureMessage(actual) } func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { /* Example with 3 matchers: A, B, C Match evaluates them: F, T, => T So match is currently T, what should MatchMayChangeInTheFuture() return? Seems like it only depends on B, since currently B MUST change to allow the result to become F Match eval: F, F, F => F So match is currently F, what should MatchMayChangeInTheFuture() return? Seems to depend on ANY of them being able to change to T. */ if m.firstSuccessfulMatcher != nil { // one of the matchers succeeded.. it must be able to change in order to affect the result return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual) } else { // so all matchers failed.. Any one of them changing would change the result. for _, matcher := range m.Matchers { if types.MatchMayChangeInTheFuture(matcher, actual) { return true } } return false // none of were going to change } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/panic_matcher.go ================================================ package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type PanicMatcher struct { Expected interface{} object interface{} } func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error) { if actual == nil { return false, fmt.Errorf("PanicMatcher expects a non-nil actual.") } actualType := reflect.TypeOf(actual) if actualType.Kind() != reflect.Func { return false, fmt.Errorf("PanicMatcher expects a function. Got:\n%s", format.Object(actual, 1)) } if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) { return false, fmt.Errorf("PanicMatcher expects a function with no arguments and no return value. Got:\n%s", format.Object(actual, 1)) } success = false defer func() { if e := recover(); e != nil { matcher.object = e if matcher.Expected == nil { success = true return } valueMatcher, valueIsMatcher := matcher.Expected.(omegaMatcher) if !valueIsMatcher { valueMatcher = &EqualMatcher{Expected: matcher.Expected} } success, err = valueMatcher.Match(e) if err != nil { err = fmt.Errorf("PanicMatcher's value matcher failed with:\n%s%s", format.Indent, err.Error()) } } }() reflect.ValueOf(actual).Call([]reflect.Value{}) return } func (matcher *PanicMatcher) FailureMessage(actual interface{}) (message string) { if matcher.Expected == nil { // We wanted any panic to occur, but none did. return format.Message(actual, "to panic") } if matcher.object == nil { // We wanted a panic with a specific value to occur, but none did. switch matcher.Expected.(type) { case omegaMatcher: return format.Message(actual, "to panic with a value matching", matcher.Expected) default: return format.Message(actual, "to panic with", matcher.Expected) } } // We got a panic, but the value isn't what we expected. switch matcher.Expected.(type) { case omegaMatcher: return format.Message( actual, fmt.Sprintf( "to panic with a value matching\n%s\nbut panicked with\n%s", format.Object(matcher.Expected, 1), format.Object(matcher.object, 1), ), ) default: return format.Message( actual, fmt.Sprintf( "to panic with\n%s\nbut panicked with\n%s", format.Object(matcher.Expected, 1), format.Object(matcher.object, 1), ), ) } } func (matcher *PanicMatcher) NegatedFailureMessage(actual interface{}) (message string) { if matcher.Expected == nil { // We didn't want any panic to occur, but one did. return format.Message(actual, fmt.Sprintf("not to panic, but panicked with\n%s", format.Object(matcher.object, 1))) } // We wanted a to ensure a panic with a specific value did not occur, but it did. switch matcher.Expected.(type) { case omegaMatcher: return format.Message( actual, fmt.Sprintf( "not to panic with a value matching\n%s\nbut panicked with\n%s", format.Object(matcher.Expected, 1), format.Object(matcher.object, 1), ), ) default: return format.Message(actual, "not to panic with", matcher.Expected) } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/receive_matcher.go ================================================ // untested sections: 3 package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type ReceiveMatcher struct { Arg interface{} receivedValue reflect.Value channelClosed bool } func (matcher *ReceiveMatcher) Match(actual interface{}) (success bool, err error) { if !isChan(actual) { return false, fmt.Errorf("ReceiveMatcher expects a channel. Got:\n%s", format.Object(actual, 1)) } channelType := reflect.TypeOf(actual) channelValue := reflect.ValueOf(actual) if channelType.ChanDir() == reflect.SendDir { return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel. Got:\n%s", format.Object(actual, 1)) } var subMatcher omegaMatcher var hasSubMatcher bool if matcher.Arg != nil { subMatcher, hasSubMatcher = (matcher.Arg).(omegaMatcher) if !hasSubMatcher { argType := reflect.TypeOf(matcher.Arg) if argType.Kind() != reflect.Ptr { return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s\nYou need to pass a pointer!", format.Object(actual, 1), format.Object(matcher.Arg, 1)) } } } winnerIndex, value, open := reflect.Select([]reflect.SelectCase{ {Dir: reflect.SelectRecv, Chan: channelValue}, {Dir: reflect.SelectDefault}, }) var closed bool var didReceive bool if winnerIndex == 0 { closed = !open didReceive = open } matcher.channelClosed = closed if closed { return false, nil } if hasSubMatcher { if didReceive { matcher.receivedValue = value return subMatcher.Match(matcher.receivedValue.Interface()) } return false, nil } if didReceive { if matcher.Arg != nil { outValue := reflect.ValueOf(matcher.Arg) if value.Type().AssignableTo(outValue.Elem().Type()) { outValue.Elem().Set(value) return true, nil } if value.Type().Kind() == reflect.Interface && value.Elem().Type().AssignableTo(outValue.Elem().Type()) { outValue.Elem().Set(value.Elem()) return true, nil } else { return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nType:\n%s\nTo:\n%s", format.Object(actual, 1), format.Object(value.Interface(), 1), format.Object(matcher.Arg, 1)) } } return true, nil } return false, nil } func (matcher *ReceiveMatcher) FailureMessage(actual interface{}) (message string) { subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) closedAddendum := "" if matcher.channelClosed { closedAddendum = " The channel is closed." } if hasSubMatcher { if matcher.receivedValue.IsValid() { return subMatcher.FailureMessage(matcher.receivedValue.Interface()) } return "When passed a matcher, ReceiveMatcher's channel *must* receive something." } return format.Message(actual, "to receive something."+closedAddendum) } func (matcher *ReceiveMatcher) NegatedFailureMessage(actual interface{}) (message string) { subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher) closedAddendum := "" if matcher.channelClosed { closedAddendum = " The channel is closed." } if hasSubMatcher { if matcher.receivedValue.IsValid() { return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface()) } return "When passed a matcher, ReceiveMatcher's channel *must* receive something." } return format.Message(actual, "not to receive anything."+closedAddendum) } func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { if !isChan(actual) { return false } return !matcher.channelClosed } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/satisfy_matcher.go ================================================ package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/format" ) type SatisfyMatcher struct { Predicate interface{} // cached type predicateArgType reflect.Type } func NewSatisfyMatcher(predicate interface{}) *SatisfyMatcher { if predicate == nil { panic("predicate cannot be nil") } predicateType := reflect.TypeOf(predicate) if predicateType.Kind() != reflect.Func { panic("predicate must be a function") } if predicateType.NumIn() != 1 { panic("predicate must have 1 argument") } if predicateType.NumOut() != 1 || predicateType.Out(0).Kind() != reflect.Bool { panic("predicate must return bool") } return &SatisfyMatcher{ Predicate: predicate, predicateArgType: predicateType.In(0), } } func (m *SatisfyMatcher) Match(actual interface{}) (success bool, err error) { // prepare a parameter to pass to the predicate var param reflect.Value if actual != nil && reflect.TypeOf(actual).AssignableTo(m.predicateArgType) { // The dynamic type of actual is compatible with the predicate argument. param = reflect.ValueOf(actual) } else if actual == nil && m.predicateArgType.Kind() == reflect.Interface { // The dynamic type of actual is unknown, so there's no way to make its // reflect.Value. Create a nil of the predicate argument, which is known. param = reflect.Zero(m.predicateArgType) } else { return false, fmt.Errorf("predicate expects '%s' but we have '%T'", m.predicateArgType, actual) } // call the predicate with `actual` fn := reflect.ValueOf(m.Predicate) result := fn.Call([]reflect.Value{param}) return result[0].Bool(), nil } func (m *SatisfyMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to satisfy predicate", m.Predicate) } func (m *SatisfyMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "to not satisfy predicate", m.Predicate) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/semi_structured_data_support.go ================================================ // untested sections: 5 package matchers import ( "fmt" "reflect" "strings" ) func formattedMessage(comparisonMessage string, failurePath []interface{}) string { var diffMessage string if len(failurePath) == 0 { diffMessage = "" } else { diffMessage = fmt.Sprintf("\n\nfirst mismatched key: %s", formattedFailurePath(failurePath)) } return fmt.Sprintf("%s%s", comparisonMessage, diffMessage) } func formattedFailurePath(failurePath []interface{}) string { formattedPaths := []string{} for i := len(failurePath) - 1; i >= 0; i-- { switch p := failurePath[i].(type) { case int: formattedPaths = append(formattedPaths, fmt.Sprintf(`[%d]`, p)) default: if i != len(failurePath)-1 { formattedPaths = append(formattedPaths, ".") } formattedPaths = append(formattedPaths, fmt.Sprintf(`"%s"`, p)) } } return strings.Join(formattedPaths, "") } func deepEqual(a interface{}, b interface{}) (bool, []interface{}) { var errorPath []interface{} if reflect.TypeOf(a) != reflect.TypeOf(b) { return false, errorPath } switch a.(type) { case []interface{}: if len(a.([]interface{})) != len(b.([]interface{})) { return false, errorPath } for i, v := range a.([]interface{}) { elementEqual, keyPath := deepEqual(v, b.([]interface{})[i]) if !elementEqual { return false, append(keyPath, i) } } return true, errorPath case map[interface{}]interface{}: if len(a.(map[interface{}]interface{})) != len(b.(map[interface{}]interface{})) { return false, errorPath } for k, v1 := range a.(map[interface{}]interface{}) { v2, ok := b.(map[interface{}]interface{})[k] if !ok { return false, errorPath } elementEqual, keyPath := deepEqual(v1, v2) if !elementEqual { return false, append(keyPath, k) } } return true, errorPath case map[string]interface{}: if len(a.(map[string]interface{})) != len(b.(map[string]interface{})) { return false, errorPath } for k, v1 := range a.(map[string]interface{}) { v2, ok := b.(map[string]interface{})[k] if !ok { return false, errorPath } elementEqual, keyPath := deepEqual(v1, v2) if !elementEqual { return false, append(keyPath, k) } } return true, errorPath default: return a == b, errorPath } } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/succeed_matcher.go ================================================ package matchers import ( "fmt" "github.com/onsi/gomega/format" ) type SucceedMatcher struct { } func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) { // is purely nil? if actual == nil { return true, nil } // must be an 'error' type if !isError(actual) { return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } // must be nil (or a pointer to a nil) return isNil(actual), nil } func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) { return fmt.Sprintf("Expected success, but got an error:\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1)) } func (matcher *SucceedMatcher) NegatedFailureMessage(actual interface{}) (message string) { return "Expected failure, but got no error." } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.go ================================================ package bipartitegraph import "fmt" import . "github.com/onsi/gomega/matchers/support/goraph/node" import . "github.com/onsi/gomega/matchers/support/goraph/edge" type BipartiteGraph struct { Left NodeOrderedSet Right NodeOrderedSet Edges EdgeSet } func NewBipartiteGraph(leftValues, rightValues []interface{}, neighbours func(interface{}, interface{}) (bool, error)) (*BipartiteGraph, error) { left := NodeOrderedSet{} for i, v := range leftValues { left = append(left, Node{ID: i, Value: v}) } right := NodeOrderedSet{} for j, v := range rightValues { right = append(right, Node{ID: j + len(left), Value: v}) } edges := EdgeSet{} for i, leftValue := range leftValues { for j, rightValue := range rightValues { neighbours, err := neighbours(leftValue, rightValue) if err != nil { return nil, fmt.Errorf("error determining adjacency for %v and %v: %s", leftValue, rightValue, err.Error()) } if neighbours { edges = append(edges, Edge{Node1: left[i].ID, Node2: right[j].ID}) } } } return &BipartiteGraph{left, right, edges}, nil } // FreeLeftRight returns left node values and right node values // of the BipartiteGraph's nodes which are not part of the given edges. func (bg *BipartiteGraph) FreeLeftRight(edges EdgeSet) (leftValues, rightValues []interface{}) { for _, node := range bg.Left { if edges.Free(node) { leftValues = append(leftValues, node.Value) } } for _, node := range bg.Right { if edges.Free(node) { rightValues = append(rightValues, node.Value) } } return } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go ================================================ package bipartitegraph import ( . "github.com/onsi/gomega/matchers/support/goraph/edge" . "github.com/onsi/gomega/matchers/support/goraph/node" "github.com/onsi/gomega/matchers/support/goraph/util" ) // LargestMatching implements the Hopcroft–Karp algorithm taking as input a bipartite graph // and outputting a maximum cardinality matching, i.e. a set of as many edges as possible // with the property that no two edges share an endpoint. func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) { paths := bg.maximalDisjointSLAPCollection(matching) for len(paths) > 0 { for _, path := range paths { matching = matching.SymmetricDifference(path) } paths = bg.maximalDisjointSLAPCollection(matching) } return } func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) { guideLayers := bg.createSLAPGuideLayers(matching) if len(guideLayers) == 0 { return } used := make(map[int]bool) for _, u := range guideLayers[len(guideLayers)-1] { slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used) if found { for _, edge := range slap { used[edge.Node1] = true used[edge.Node2] = true } result = append(result, slap) } } return } func (bg *BipartiteGraph) findDisjointSLAP( start Node, matching EdgeSet, guideLayers []NodeOrderedSet, used map[int]bool, ) ([]Edge, bool) { return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used) } func (bg *BipartiteGraph) findDisjointSLAPHelper( currentNode Node, currentSLAP EdgeSet, currentLevel int, matching EdgeSet, guideLayers []NodeOrderedSet, used map[int]bool, ) (EdgeSet, bool) { used[currentNode.ID] = true if currentLevel == 0 { return currentSLAP, true } for _, nextNode := range guideLayers[currentLevel-1] { if used[nextNode.ID] { continue } edge, found := bg.Edges.FindByNodes(currentNode, nextNode) if !found { continue } if matching.Contains(edge) == util.Odd(currentLevel) { continue } currentSLAP = append(currentSLAP, edge) slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used) if found { return slap, true } currentSLAP = currentSLAP[:len(currentSLAP)-1] } used[currentNode.ID] = false return nil, false } func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) { used := make(map[int]bool) currentLayer := NodeOrderedSet{} for _, node := range bg.Left { if matching.Free(node) { used[node.ID] = true currentLayer = append(currentLayer, node) } } if len(currentLayer) == 0 { return []NodeOrderedSet{} } guideLayers = append(guideLayers, currentLayer) done := false for !done { lastLayer := currentLayer currentLayer = NodeOrderedSet{} if util.Odd(len(guideLayers)) { for _, leftNode := range lastLayer { for _, rightNode := range bg.Right { if used[rightNode.ID] { continue } edge, found := bg.Edges.FindByNodes(leftNode, rightNode) if !found || matching.Contains(edge) { continue } currentLayer = append(currentLayer, rightNode) used[rightNode.ID] = true if matching.Free(rightNode) { done = true } } } } else { for _, rightNode := range lastLayer { for _, leftNode := range bg.Left { if used[leftNode.ID] { continue } edge, found := bg.Edges.FindByNodes(leftNode, rightNode) if !found || !matching.Contains(edge) { continue } currentLayer = append(currentLayer, leftNode) used[leftNode.ID] = true } } } if len(currentLayer) == 0 { return []NodeOrderedSet{} } guideLayers = append(guideLayers, currentLayer) } return } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go ================================================ package edge import . "github.com/onsi/gomega/matchers/support/goraph/node" type Edge struct { Node1 int Node2 int } type EdgeSet []Edge func (ec EdgeSet) Free(node Node) bool { for _, e := range ec { if e.Node1 == node.ID || e.Node2 == node.ID { return false } } return true } func (ec EdgeSet) Contains(edge Edge) bool { for _, e := range ec { if e == edge { return true } } return false } func (ec EdgeSet) FindByNodes(node1, node2 Node) (Edge, bool) { for _, e := range ec { if (e.Node1 == node1.ID && e.Node2 == node2.ID) || (e.Node1 == node2.ID && e.Node2 == node1.ID) { return e, true } } return Edge{}, false } func (ec EdgeSet) SymmetricDifference(ec2 EdgeSet) EdgeSet { edgesToInclude := make(map[Edge]bool) for _, e := range ec { edgesToInclude[e] = true } for _, e := range ec2 { edgesToInclude[e] = !edgesToInclude[e] } result := EdgeSet{} for e, include := range edgesToInclude { if include { result = append(result, e) } } return result } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.go ================================================ package node type Node struct { ID int Value interface{} } type NodeOrderedSet []Node ================================================ FILE: vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.go ================================================ package util import "math" func Odd(n int) bool { return math.Mod(float64(n), 2.0) == 1.0 } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/type_support.go ================================================ /* Gomega matchers This package implements the Gomega matchers and does not typically need to be imported. See the docs for Gomega for documentation on the matchers http://onsi.github.io/gomega/ */ // untested sections: 11 package matchers import ( "encoding/json" "fmt" "reflect" ) type omegaMatcher interface { Match(actual interface{}) (success bool, err error) FailureMessage(actual interface{}) (message string) NegatedFailureMessage(actual interface{}) (message string) } func isBool(a interface{}) bool { return reflect.TypeOf(a).Kind() == reflect.Bool } func isNumber(a interface{}) bool { if a == nil { return false } kind := reflect.TypeOf(a).Kind() return reflect.Int <= kind && kind <= reflect.Float64 } func isInteger(a interface{}) bool { kind := reflect.TypeOf(a).Kind() return reflect.Int <= kind && kind <= reflect.Int64 } func isUnsignedInteger(a interface{}) bool { kind := reflect.TypeOf(a).Kind() return reflect.Uint <= kind && kind <= reflect.Uint64 } func isFloat(a interface{}) bool { kind := reflect.TypeOf(a).Kind() return reflect.Float32 <= kind && kind <= reflect.Float64 } func toInteger(a interface{}) int64 { if isInteger(a) { return reflect.ValueOf(a).Int() } else if isUnsignedInteger(a) { return int64(reflect.ValueOf(a).Uint()) } else if isFloat(a) { return int64(reflect.ValueOf(a).Float()) } panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) } func toUnsignedInteger(a interface{}) uint64 { if isInteger(a) { return uint64(reflect.ValueOf(a).Int()) } else if isUnsignedInteger(a) { return reflect.ValueOf(a).Uint() } else if isFloat(a) { return uint64(reflect.ValueOf(a).Float()) } panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) } func toFloat(a interface{}) float64 { if isInteger(a) { return float64(reflect.ValueOf(a).Int()) } else if isUnsignedInteger(a) { return float64(reflect.ValueOf(a).Uint()) } else if isFloat(a) { return reflect.ValueOf(a).Float() } panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a)) } func isError(a interface{}) bool { _, ok := a.(error) return ok } func isChan(a interface{}) bool { if isNil(a) { return false } return reflect.TypeOf(a).Kind() == reflect.Chan } func isMap(a interface{}) bool { if a == nil { return false } return reflect.TypeOf(a).Kind() == reflect.Map } func isArrayOrSlice(a interface{}) bool { if a == nil { return false } switch reflect.TypeOf(a).Kind() { case reflect.Array, reflect.Slice: return true default: return false } } func isString(a interface{}) bool { if a == nil { return false } return reflect.TypeOf(a).Kind() == reflect.String } func toString(a interface{}) (string, bool) { aString, isString := a.(string) if isString { return aString, true } aBytes, isBytes := a.([]byte) if isBytes { return string(aBytes), true } aStringer, isStringer := a.(fmt.Stringer) if isStringer { return aStringer.String(), true } aJSONRawMessage, isJSONRawMessage := a.(json.RawMessage) if isJSONRawMessage { return string(aJSONRawMessage), true } return "", false } func lengthOf(a interface{}) (int, bool) { if a == nil { return 0, false } switch reflect.TypeOf(a).Kind() { case reflect.Map, reflect.Array, reflect.String, reflect.Chan, reflect.Slice: return reflect.ValueOf(a).Len(), true default: return 0, false } } func capOf(a interface{}) (int, bool) { if a == nil { return 0, false } switch reflect.TypeOf(a).Kind() { case reflect.Array, reflect.Chan, reflect.Slice: return reflect.ValueOf(a).Cap(), true default: return 0, false } } func isNil(a interface{}) bool { if a == nil { return true } switch reflect.TypeOf(a).Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return reflect.ValueOf(a).IsNil() } return false } ================================================ FILE: vendor/github.com/onsi/gomega/matchers/with_transform.go ================================================ package matchers import ( "fmt" "reflect" "github.com/onsi/gomega/types" ) type WithTransformMatcher struct { // input Transform interface{} // must be a function of one parameter that returns one value and an optional error Matcher types.GomegaMatcher // cached value transformArgType reflect.Type // state transformedValue interface{} } // reflect.Type for error var errorT = reflect.TypeOf((*error)(nil)).Elem() func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher { if transform == nil { panic("transform function cannot be nil") } txType := reflect.TypeOf(transform) if txType.NumIn() != 1 { panic("transform function must have 1 argument") } if numout := txType.NumOut(); numout != 1 { if numout != 2 || !txType.Out(1).AssignableTo(errorT) { panic("transform function must either have 1 return value, or 1 return value plus 1 error value") } } return &WithTransformMatcher{ Transform: transform, Matcher: matcher, transformArgType: reflect.TypeOf(transform).In(0), } } func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) { // prepare a parameter to pass to the Transform function var param reflect.Value if actual != nil && reflect.TypeOf(actual).AssignableTo(m.transformArgType) { // The dynamic type of actual is compatible with the transform argument. param = reflect.ValueOf(actual) } else if actual == nil && m.transformArgType.Kind() == reflect.Interface { // The dynamic type of actual is unknown, so there's no way to make its // reflect.Value. Create a nil of the transform argument, which is known. param = reflect.Zero(m.transformArgType) } else { return false, fmt.Errorf("Transform function expects '%s' but we have '%T'", m.transformArgType, actual) } // call the Transform function with `actual` fn := reflect.ValueOf(m.Transform) result := fn.Call([]reflect.Value{param}) if len(result) == 2 { if !result[1].IsNil() { return false, fmt.Errorf("Transform function failed: %s", result[1].Interface().(error).Error()) } } m.transformedValue = result[0].Interface() // expect exactly one value return m.Matcher.Match(m.transformedValue) } func (m *WithTransformMatcher) FailureMessage(_ interface{}) (message string) { return m.Matcher.FailureMessage(m.transformedValue) } func (m *WithTransformMatcher) NegatedFailureMessage(_ interface{}) (message string) { return m.Matcher.NegatedFailureMessage(m.transformedValue) } func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool { // TODO: Maybe this should always just return true? (Only an issue for non-deterministic transformers.) // // Querying the next matcher is fine if the transformer always will return the same value. // But if the transformer is non-deterministic and returns a different value each time, then there // is no point in querying the next matcher, since it can only comment on the last transformed value. return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue) } ================================================ FILE: vendor/github.com/onsi/gomega/matchers.go ================================================ package gomega import ( "time" "github.com/onsi/gomega/matchers" "github.com/onsi/gomega/types" ) //Equal uses reflect.DeepEqual to compare actual with expected. Equal is strict about //types when performing comparisons. //It is an error for both actual and expected to be nil. Use BeNil() instead. func Equal(expected interface{}) types.GomegaMatcher { return &matchers.EqualMatcher{ Expected: expected, } } //BeEquivalentTo is more lax than Equal, allowing equality between different types. //This is done by converting actual to have the type of expected before //attempting equality with reflect.DeepEqual. //It is an error for actual and expected to be nil. Use BeNil() instead. func BeEquivalentTo(expected interface{}) types.GomegaMatcher { return &matchers.BeEquivalentToMatcher{ Expected: expected, } } //BeIdenticalTo uses the == operator to compare actual with expected. //BeIdenticalTo is strict about types when performing comparisons. //It is an error for both actual and expected to be nil. Use BeNil() instead. func BeIdenticalTo(expected interface{}) types.GomegaMatcher { return &matchers.BeIdenticalToMatcher{ Expected: expected, } } //BeNil succeeds if actual is nil func BeNil() types.GomegaMatcher { return &matchers.BeNilMatcher{} } //BeTrue succeeds if actual is true func BeTrue() types.GomegaMatcher { return &matchers.BeTrueMatcher{} } //BeFalse succeeds if actual is false func BeFalse() types.GomegaMatcher { return &matchers.BeFalseMatcher{} } //HaveOccurred succeeds if actual is a non-nil error //The typical Go error checking pattern looks like: // err := SomethingThatMightFail() // Expect(err).ShouldNot(HaveOccurred()) func HaveOccurred() types.GomegaMatcher { return &matchers.HaveOccurredMatcher{} } //Succeed passes if actual is a nil error //Succeed is intended to be used with functions that return a single error value. Instead of // err := SomethingThatMightFail() // Expect(err).ShouldNot(HaveOccurred()) // //You can write: // Expect(SomethingThatMightFail()).Should(Succeed()) // //It is a mistake to use Succeed with a function that has multiple return values. Gomega's Ω and Expect //functions automatically trigger failure if any return values after the first return value are non-zero/non-nil. //This means that Ω(MultiReturnFunc()).ShouldNot(Succeed()) can never pass. func Succeed() types.GomegaMatcher { return &matchers.SucceedMatcher{} } //MatchError succeeds if actual is a non-nil error that matches the passed in string/error. // //These are valid use-cases: // Expect(err).Should(MatchError("an error")) //asserts that err.Error() == "an error" // Expect(err).Should(MatchError(SomeError)) //asserts that err == SomeError (via reflect.DeepEqual) // //It is an error for err to be nil or an object that does not implement the Error interface func MatchError(expected interface{}) types.GomegaMatcher { return &matchers.MatchErrorMatcher{ Expected: expected, } } //BeClosed succeeds if actual is a closed channel. //It is an error to pass a non-channel to BeClosed, it is also an error to pass nil // //In order to check whether or not the channel is closed, Gomega must try to read from the channel //(even in the `ShouldNot(BeClosed())` case). You should keep this in mind if you wish to make subsequent assertions about //values coming down the channel. // //Also, if you are testing that a *buffered* channel is closed you must first read all values out of the channel before //asserting that it is closed (it is not possible to detect that a buffered-channel has been closed until all its buffered values are read). // //Finally, as a corollary: it is an error to check whether or not a send-only channel is closed. func BeClosed() types.GomegaMatcher { return &matchers.BeClosedMatcher{} } //Receive succeeds if there is a value to be received on actual. //Actual must be a channel (and cannot be a send-only channel) -- anything else is an error. // //Receive returns immediately and never blocks: // //- If there is nothing on the channel `c` then Expect(c).Should(Receive()) will fail and Ω(c).ShouldNot(Receive()) will pass. // //- If the channel `c` is closed then Expect(c).Should(Receive()) will fail and Ω(c).ShouldNot(Receive()) will pass. // //- If there is something on the channel `c` ready to be read, then Expect(c).Should(Receive()) will pass and Ω(c).ShouldNot(Receive()) will fail. // //If you have a go-routine running in the background that will write to channel `c` you can: // Eventually(c).Should(Receive()) // //This will timeout if nothing gets sent to `c` (you can modify the timeout interval as you normally do with `Eventually`) // //A similar use-case is to assert that no go-routine writes to a channel (for a period of time). You can do this with `Consistently`: // Consistently(c).ShouldNot(Receive()) // //You can pass `Receive` a matcher. If you do so, it will match the received object against the matcher. For example: // Expect(c).Should(Receive(Equal("foo"))) // //When given a matcher, `Receive` will always fail if there is nothing to be received on the channel. // //Passing Receive a matcher is especially useful when paired with Eventually: // // Eventually(c).Should(Receive(ContainSubstring("bar"))) // //will repeatedly attempt to pull values out of `c` until a value matching "bar" is received. // //Finally, if you want to have a reference to the value *sent* to the channel you can pass the `Receive` matcher a pointer to a variable of the appropriate type: // var myThing thing // Eventually(thingChan).Should(Receive(&myThing)) // Expect(myThing.Sprocket).Should(Equal("foo")) // Expect(myThing.IsValid()).Should(BeTrue()) func Receive(args ...interface{}) types.GomegaMatcher { var arg interface{} if len(args) > 0 { arg = args[0] } return &matchers.ReceiveMatcher{ Arg: arg, } } //BeSent succeeds if a value can be sent to actual. //Actual must be a channel (and cannot be a receive-only channel) that can sent the type of the value passed into BeSent -- anything else is an error. //In addition, actual must not be closed. // //BeSent never blocks: // //- If the channel `c` is not ready to receive then Expect(c).Should(BeSent("foo")) will fail immediately //- If the channel `c` is eventually ready to receive then Eventually(c).Should(BeSent("foo")) will succeed.. presuming the channel becomes ready to receive before Eventually's timeout //- If the channel `c` is closed then Expect(c).Should(BeSent("foo")) and Ω(c).ShouldNot(BeSent("foo")) will both fail immediately // //Of course, the value is actually sent to the channel. The point of `BeSent` is less to make an assertion about the availability of the channel (which is typically an implementation detail that your test should not be concerned with). //Rather, the point of `BeSent` is to make it possible to easily and expressively write tests that can timeout on blocked channel sends. func BeSent(arg interface{}) types.GomegaMatcher { return &matchers.BeSentMatcher{ Arg: arg, } } //MatchRegexp succeeds if actual is a string or stringer that matches the //passed-in regexp. Optional arguments can be provided to construct a regexp //via fmt.Sprintf(). func MatchRegexp(regexp string, args ...interface{}) types.GomegaMatcher { return &matchers.MatchRegexpMatcher{ Regexp: regexp, Args: args, } } //ContainSubstring succeeds if actual is a string or stringer that contains the //passed-in substring. Optional arguments can be provided to construct the substring //via fmt.Sprintf(). func ContainSubstring(substr string, args ...interface{}) types.GomegaMatcher { return &matchers.ContainSubstringMatcher{ Substr: substr, Args: args, } } //HavePrefix succeeds if actual is a string or stringer that contains the //passed-in string as a prefix. Optional arguments can be provided to construct //via fmt.Sprintf(). func HavePrefix(prefix string, args ...interface{}) types.GomegaMatcher { return &matchers.HavePrefixMatcher{ Prefix: prefix, Args: args, } } //HaveSuffix succeeds if actual is a string or stringer that contains the //passed-in string as a suffix. Optional arguments can be provided to construct //via fmt.Sprintf(). func HaveSuffix(suffix string, args ...interface{}) types.GomegaMatcher { return &matchers.HaveSuffixMatcher{ Suffix: suffix, Args: args, } } //MatchJSON succeeds if actual is a string or stringer of JSON that matches //the expected JSON. The JSONs are decoded and the resulting objects are compared via //reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter. func MatchJSON(json interface{}) types.GomegaMatcher { return &matchers.MatchJSONMatcher{ JSONToMatch: json, } } //MatchXML succeeds if actual is a string or stringer of XML that matches //the expected XML. The XMLs are decoded and the resulting objects are compared via //reflect.DeepEqual so things like whitespaces shouldn't matter. func MatchXML(xml interface{}) types.GomegaMatcher { return &matchers.MatchXMLMatcher{ XMLToMatch: xml, } } //MatchYAML succeeds if actual is a string or stringer of YAML that matches //the expected YAML. The YAML's are decoded and the resulting objects are compared via //reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter. func MatchYAML(yaml interface{}) types.GomegaMatcher { return &matchers.MatchYAMLMatcher{ YAMLToMatch: yaml, } } //BeEmpty succeeds if actual is empty. Actual must be of type string, array, map, chan, or slice. func BeEmpty() types.GomegaMatcher { return &matchers.BeEmptyMatcher{} } //HaveLen succeeds if actual has the passed-in length. Actual must be of type string, array, map, chan, or slice. func HaveLen(count int) types.GomegaMatcher { return &matchers.HaveLenMatcher{ Count: count, } } //HaveCap succeeds if actual has the passed-in capacity. Actual must be of type array, chan, or slice. func HaveCap(count int) types.GomegaMatcher { return &matchers.HaveCapMatcher{ Count: count, } } //BeZero succeeds if actual is the zero value for its type or if actual is nil. func BeZero() types.GomegaMatcher { return &matchers.BeZeroMatcher{} } //ContainElement succeeds if actual contains the passed in element. //By default ContainElement() uses Equal() to perform the match, however a //matcher can be passed in instead: // Expect([]string{"Foo", "FooBar"}).Should(ContainElement(ContainSubstring("Bar"))) // //Actual must be an array, slice or map. //For maps, ContainElement searches through the map's values. func ContainElement(element interface{}) types.GomegaMatcher { return &matchers.ContainElementMatcher{ Element: element, } } //BeElementOf succeeds if actual is contained in the passed in elements. //BeElementOf() always uses Equal() to perform the match. //When the passed in elements are comprised of a single element that is either an Array or Slice, BeElementOf() behaves //as the reverse of ContainElement() that operates with Equal() to perform the match. // Expect(2).Should(BeElementOf([]int{1, 2})) // Expect(2).Should(BeElementOf([2]int{1, 2})) //Otherwise, BeElementOf() provides a syntactic sugar for Or(Equal(_), Equal(_), ...): // Expect(2).Should(BeElementOf(1, 2)) // //Actual must be typed. func BeElementOf(elements ...interface{}) types.GomegaMatcher { return &matchers.BeElementOfMatcher{ Elements: elements, } } //ConsistOf succeeds if actual contains precisely the elements passed into the matcher. The ordering of the elements does not matter. //By default ConsistOf() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: // // Expect([]string{"Foo", "FooBar"}).Should(ConsistOf("FooBar", "Foo")) // Expect([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Bar"), "Foo")) // Expect([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Foo"), ContainSubstring("Foo"))) // //Actual must be an array, slice or map. For maps, ConsistOf matches against the map's values. // //You typically pass variadic arguments to ConsistOf (as in the examples above). However, if you need to pass in a slice you can provided that it //is the only element passed in to ConsistOf: // // Expect([]string{"Foo", "FooBar"}).Should(ConsistOf([]string{"FooBar", "Foo"})) // //Note that Go's type system does not allow you to write this as ConsistOf([]string{"FooBar", "Foo"}...) as []string and []interface{} are different types - hence the need for this special rule. func ConsistOf(elements ...interface{}) types.GomegaMatcher { return &matchers.ConsistOfMatcher{ Elements: elements, } } //ContainElements succeeds if actual contains the passed in elements. The ordering of the elements does not matter. //By default ContainElements() uses Equal() to match the elements, however custom matchers can be passed in instead. Here are some examples: // // Expect([]string{"Foo", "FooBar"}).Should(ContainElements("FooBar")) // Expect([]string{"Foo", "FooBar"}).Should(ContainElements(ContainSubstring("Bar"), "Foo")) // //Actual must be an array, slice or map. //For maps, ContainElements searches through the map's values. func ContainElements(elements ...interface{}) types.GomegaMatcher { return &matchers.ContainElementsMatcher{ Elements: elements, } } //HaveKey succeeds if actual is a map with the passed in key. //By default HaveKey uses Equal() to perform the match, however a //matcher can be passed in instead: // Expect(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKey(MatchRegexp(`.+Foo$`))) func HaveKey(key interface{}) types.GomegaMatcher { return &matchers.HaveKeyMatcher{ Key: key, } } //HaveKeyWithValue succeeds if actual is a map with the passed in key and value. //By default HaveKeyWithValue uses Equal() to perform the match, however a //matcher can be passed in instead: // Expect(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue("Foo", "Bar")) // Expect(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKeyWithValue(MatchRegexp(`.+Foo$`), "Bar")) func HaveKeyWithValue(key interface{}, value interface{}) types.GomegaMatcher { return &matchers.HaveKeyWithValueMatcher{ Key: key, Value: value, } } //HaveField succeeds if actual is a struct and the value at the passed in field //matches the passed in matcher. By default HaveField used Equal() to perform the match, //however a matcher can be passed in in stead. // //The field must be a string that resolves to the name of a field in the struct. Structs can be traversed //using the '.' delimiter. If the field ends with '()' a method named field is assumed to exist on the struct and is invoked. //Such methods must take no arguments and return a single value: // // type Book struct { // Title string // Author Person // } // type Person struct { // FirstName string // LastName string // DOB time.Time // } // Expect(book).To(HaveField("Title", "Les Miserables")) // Expect(book).To(HaveField("Title", ContainSubstring("Les")) // Expect(book).To(HaveField("Person.FirstName", Equal("Victor")) // Expect(book).To(HaveField("Person.DOB.Year()", BeNumerically("<", 1900)) func HaveField(field string, expected interface{}) types.GomegaMatcher { return &matchers.HaveFieldMatcher{ Field: field, Expected: expected, } } //BeNumerically performs numerical assertions in a type-agnostic way. //Actual and expected should be numbers, though the specific type of //number is irrelevant (float32, float64, uint8, etc...). // //There are six, self-explanatory, supported comparators: // Expect(1.0).Should(BeNumerically("==", 1)) // Expect(1.0).Should(BeNumerically("~", 0.999, 0.01)) // Expect(1.0).Should(BeNumerically(">", 0.9)) // Expect(1.0).Should(BeNumerically(">=", 1.0)) // Expect(1.0).Should(BeNumerically("<", 3)) // Expect(1.0).Should(BeNumerically("<=", 1.0)) func BeNumerically(comparator string, compareTo ...interface{}) types.GomegaMatcher { return &matchers.BeNumericallyMatcher{ Comparator: comparator, CompareTo: compareTo, } } //BeTemporally compares time.Time's like BeNumerically //Actual and expected must be time.Time. The comparators are the same as for BeNumerically // Expect(time.Now()).Should(BeTemporally(">", time.Time{})) // Expect(time.Now()).Should(BeTemporally("~", time.Now(), time.Second)) func BeTemporally(comparator string, compareTo time.Time, threshold ...time.Duration) types.GomegaMatcher { return &matchers.BeTemporallyMatcher{ Comparator: comparator, CompareTo: compareTo, Threshold: threshold, } } //BeAssignableToTypeOf succeeds if actual is assignable to the type of expected. //It will return an error when one of the values is nil. // Expect(0).Should(BeAssignableToTypeOf(0)) // Same values // Expect(5).Should(BeAssignableToTypeOf(-1)) // different values same type // Expect("foo").Should(BeAssignableToTypeOf("bar")) // different values same type // Expect(struct{ Foo string }{}).Should(BeAssignableToTypeOf(struct{ Foo string }{})) func BeAssignableToTypeOf(expected interface{}) types.GomegaMatcher { return &matchers.AssignableToTypeOfMatcher{ Expected: expected, } } //Panic succeeds if actual is a function that, when invoked, panics. //Actual must be a function that takes no arguments and returns no results. func Panic() types.GomegaMatcher { return &matchers.PanicMatcher{} } //PanicWith succeeds if actual is a function that, when invoked, panics with a specific value. //Actual must be a function that takes no arguments and returns no results. // //By default PanicWith uses Equal() to perform the match, however a //matcher can be passed in instead: // Expect(fn).Should(PanicWith(MatchRegexp(`.+Foo$`))) func PanicWith(expected interface{}) types.GomegaMatcher { return &matchers.PanicMatcher{Expected: expected} } //BeAnExistingFile succeeds if a file exists. //Actual must be a string representing the abs path to the file being checked. func BeAnExistingFile() types.GomegaMatcher { return &matchers.BeAnExistingFileMatcher{} } //BeARegularFile succeeds if a file exists and is a regular file. //Actual must be a string representing the abs path to the file being checked. func BeARegularFile() types.GomegaMatcher { return &matchers.BeARegularFileMatcher{} } //BeADirectory succeeds if a file exists and is a directory. //Actual must be a string representing the abs path to the file being checked. func BeADirectory() types.GomegaMatcher { return &matchers.BeADirectoryMatcher{} } //HaveHTTPStatus succeeds if the Status or StatusCode field of an HTTP response matches. //Actual must be either a *http.Response or *httptest.ResponseRecorder. //Expected must be either an int or a string. // Expect(resp).Should(HaveHTTPStatus(http.StatusOK)) // asserts that resp.StatusCode == 200 // Expect(resp).Should(HaveHTTPStatus("404 Not Found")) // asserts that resp.Status == "404 Not Found" // Expect(resp).Should(HaveHTTPStatus(http.StatusOK, http.StatusNoContent)) // asserts that resp.StatusCode == 200 || resp.StatusCode == 204 func HaveHTTPStatus(expected ...interface{}) types.GomegaMatcher { return &matchers.HaveHTTPStatusMatcher{Expected: expected} } // HaveHTTPHeaderWithValue succeeds if the header is found and the value matches. // Actual must be either a *http.Response or *httptest.ResponseRecorder. // Expected must be a string header name, followed by a header value which // can be a string, or another matcher. func HaveHTTPHeaderWithValue(header string, value interface{}) types.GomegaMatcher { return &matchers.HaveHTTPHeaderWithValueMatcher{ Header: header, Value: value, } } // HaveHTTPBody matches if the body matches. // Actual must be either a *http.Response or *httptest.ResponseRecorder. // Expected must be either a string, []byte, or other matcher func HaveHTTPBody(expected interface{}) types.GomegaMatcher { return &matchers.HaveHTTPBodyMatcher{Expected: expected} } //And succeeds only if all of the given matchers succeed. //The matchers are tried in order, and will fail-fast if one doesn't succeed. // Expect("hi").To(And(HaveLen(2), Equal("hi")) // //And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. func And(ms ...types.GomegaMatcher) types.GomegaMatcher { return &matchers.AndMatcher{Matchers: ms} } //SatisfyAll is an alias for And(). // Expect("hi").Should(SatisfyAll(HaveLen(2), Equal("hi"))) func SatisfyAll(matchers ...types.GomegaMatcher) types.GomegaMatcher { return And(matchers...) } //Or succeeds if any of the given matchers succeed. //The matchers are tried in order and will return immediately upon the first successful match. // Expect("hi").To(Or(HaveLen(3), HaveLen(2)) // //And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. func Or(ms ...types.GomegaMatcher) types.GomegaMatcher { return &matchers.OrMatcher{Matchers: ms} } //SatisfyAny is an alias for Or(). // Expect("hi").SatisfyAny(Or(HaveLen(3), HaveLen(2)) func SatisfyAny(matchers ...types.GomegaMatcher) types.GomegaMatcher { return Or(matchers...) } //Not negates the given matcher; it succeeds if the given matcher fails. // Expect(1).To(Not(Equal(2)) // //And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. func Not(matcher types.GomegaMatcher) types.GomegaMatcher { return &matchers.NotMatcher{Matcher: matcher} } //WithTransform applies the `transform` to the actual value and matches it against `matcher`. //The given transform must be either a function of one parameter that returns one value or a // function of one parameter that returns two values, where the second value must be of the // error type. // var plus1 = func(i int) int { return i + 1 } // Expect(1).To(WithTransform(plus1, Equal(2)) // // var failingplus1 = func(i int) (int, error) { return 42, "this does not compute" } // Expect(1).To(WithTransform(failingplus1, Equal(2))) // //And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. func WithTransform(transform interface{}, matcher types.GomegaMatcher) types.GomegaMatcher { return matchers.NewWithTransformMatcher(transform, matcher) } //Satisfy matches the actual value against the `predicate` function. //The given predicate must be a function of one paramter that returns bool. // var isEven = func(i int) bool { return i%2 == 0 } // Expect(2).To(Satisfy(isEven)) func Satisfy(predicate interface{}) types.GomegaMatcher { return matchers.NewSatisfyMatcher(predicate) } ================================================ FILE: vendor/github.com/onsi/gomega/types/types.go ================================================ package types import ( "time" ) type GomegaFailHandler func(message string, callerSkip ...int) //A simple *testing.T interface wrapper type GomegaTestingT interface { Helper() Fatalf(format string, args ...interface{}) } // Gomega represents an object that can perform synchronous and assynchronous assertions with Gomega matchers type Gomega interface { Ω(actual interface{}, extra ...interface{}) Assertion Expect(actual interface{}, extra ...interface{}) Assertion ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion SetDefaultEventuallyTimeout(time.Duration) SetDefaultEventuallyPollingInterval(time.Duration) SetDefaultConsistentlyDuration(time.Duration) SetDefaultConsistentlyPollingInterval(time.Duration) } //All Gomega matchers must implement the GomegaMatcher interface // //For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding-your-own-matchers type GomegaMatcher interface { Match(actual interface{}) (success bool, err error) FailureMessage(actual interface{}) (message string) NegatedFailureMessage(actual interface{}) (message string) } /* GomegaMatchers that also match the OracleMatcher interface can convey information about whether or not their result will change upon future attempts. This allows `Eventually` and `Consistently` to short circuit if success becomes impossible. For example, a process' exit code can never change. So, gexec's Exit matcher returns `true` for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore. */ type OracleMatcher interface { MatchMayChangeInTheFuture(actual interface{}) bool } func MatchMayChangeInTheFuture(matcher GomegaMatcher, value interface{}) bool { oracleMatcher, ok := matcher.(OracleMatcher) if !ok { return true } return oracleMatcher.MatchMayChangeInTheFuture(value) } // AsyncAssertions are returned by Eventually and Consistently and enable matchers to be polled repeatedly to ensure // they are eventually satisfied type AsyncAssertion interface { Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool WithOffset(offset int) AsyncAssertion WithTimeout(interval time.Duration) AsyncAssertion WithPolling(interval time.Duration) AsyncAssertion } // Assertions are returned by Ω and Expect and enable assertions against Gomega matchers type Assertion interface { Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool To(matcher GomegaMatcher, optionalDescription ...interface{}) bool ToNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool NotTo(matcher GomegaMatcher, optionalDescription ...interface{}) bool WithOffset(offset int) Assertion Error() Assertion } ================================================ FILE: vendor/github.com/pkg/errors/.gitignore ================================================ # 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 *.prof ================================================ FILE: vendor/github.com/pkg/errors/.travis.yml ================================================ language: go go_import_path: github.com/pkg/errors go: - 1.11.x - 1.12.x - 1.13.x - tip script: - make check ================================================ FILE: vendor/github.com/pkg/errors/LICENSE ================================================ Copyright (c) 2015, Dave Cheney 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. 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 HOLDER 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. ================================================ FILE: vendor/github.com/pkg/errors/Makefile ================================================ PKGS := github.com/pkg/errors SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) GO := go check: test vet gofmt misspell unconvert staticcheck ineffassign unparam test: $(GO) test $(PKGS) vet: | test $(GO) vet $(PKGS) staticcheck: $(GO) get honnef.co/go/tools/cmd/staticcheck staticcheck -checks all $(PKGS) misspell: $(GO) get github.com/client9/misspell/cmd/misspell misspell \ -locale GB \ -error \ *.md *.go unconvert: $(GO) get github.com/mdempsky/unconvert unconvert -v $(PKGS) ineffassign: $(GO) get github.com/gordonklaus/ineffassign find $(SRCDIRS) -name '*.go' | xargs ineffassign pedantic: check errcheck unparam: $(GO) get mvdan.cc/unparam unparam ./... errcheck: $(GO) get github.com/kisielk/errcheck errcheck $(PKGS) gofmt: @echo Checking code is gofmted @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" ================================================ FILE: vendor/github.com/pkg/errors/README.md ================================================ # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) Package errors provides simple error handling primitives. `go get github.com/pkg/errors` The traditional error handling idiom in Go is roughly akin to ```go if err != nil { return err } ``` which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. ## Adding context to an error The errors.Wrap function returns a new error that adds context to the original error. For example ```go _, err := ioutil.ReadAll(r) if err != nil { return errors.Wrap(err, "read failed") } ``` ## Retrieving the cause of an error Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. ```go type causer interface { Cause() error } ``` `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: ```go switch err := errors.Cause(err).(type) { case *MyError: // handle specifically default: // unknown error } ``` [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). ## Roadmap With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: - 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) - 1.0. Final release. ## Contributing Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. Before sending a PR, please discuss your change by raising an issue. ## License BSD-2-Clause ================================================ FILE: vendor/github.com/pkg/errors/appveyor.yml ================================================ version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\pkg\errors shallow_clone: true # for startup speed environment: GOPATH: C:\gopath platform: - x64 # http://www.appveyor.com/docs/installed-software install: # some helpful output for debugging builds - go version - go env # pre-installed MinGW at C:\MinGW is 32bit only # but MSYS2 at C:\msys64 has mingw64 - set PATH=C:\msys64\mingw64\bin;%PATH% - gcc --version - g++ --version build_script: - go install -v ./... test_script: - set PATH=C:\gopath\bin;%PATH% - go test -v ./... #artifacts: # - path: '%GOPATH%\bin\*.exe' deploy: off ================================================ FILE: vendor/github.com/pkg/errors/errors.go ================================================ // Package errors provides simple error handling primitives. // // The traditional error handling idiom in Go is roughly akin to // // if err != nil { // return err // } // // which when applied recursively up the call stack results in error reports // without context or debugging information. The errors package allows // programmers to add context to the failure path in their code in a way // that does not destroy the original value of the error. // // Adding context to an error // // The errors.Wrap function returns a new error that adds context to the // original error by recording a stack trace at the point Wrap is called, // together with the supplied message. For example // // _, err := ioutil.ReadAll(r) // if err != nil { // return errors.Wrap(err, "read failed") // } // // If additional control is required, the errors.WithStack and // errors.WithMessage functions destructure errors.Wrap into its component // operations: annotating an error with a stack trace and with a message, // respectively. // // Retrieving the cause of an error // // Using errors.Wrap constructs a stack of errors, adding context to the // preceding error. Depending on the nature of the error it may be necessary // to reverse the operation of errors.Wrap to retrieve the original error // for inspection. Any error value which implements this interface // // type causer interface { // Cause() error // } // // can be inspected by errors.Cause. errors.Cause will recursively retrieve // the topmost error that does not implement causer, which is assumed to be // the original cause. For example: // // switch err := errors.Cause(err).(type) { // case *MyError: // // handle specifically // default: // // unknown error // } // // Although the causer interface is not exported by this package, it is // considered a part of its stable public interface. // // Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can // be formatted by the fmt package. The following verbs are supported: // // %s print the error. If the error has a Cause it will be // printed recursively. // %v see %s // %+v extended format. Each Frame of the error's StackTrace will // be printed in detail. // // Retrieving the stack trace of an error or wrapper // // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // invoked. This information can be retrieved with the following interface: // // type stackTracer interface { // StackTrace() errors.StackTrace // } // // The returned errors.StackTrace type is defined as // // type StackTrace []Frame // // The Frame type represents a call site in the stack trace. Frame supports // the fmt.Formatter interface that can be used for printing information about // the stack trace of this error. For example: // // if err, ok := err.(stackTracer); ok { // for _, f := range err.StackTrace() { // fmt.Printf("%+s:%d\n", f, f) // } // } // // Although the stackTracer interface is not exported by this package, it is // considered a part of its stable public interface. // // See the documentation for Frame.Format for more details. package errors import ( "fmt" "io" ) // New returns an error with the supplied message. // New also records the stack trace at the point it was called. func New(message string) error { return &fundamental{ msg: message, stack: callers(), } } // Errorf formats according to a format specifier and returns the string // as a value that satisfies error. // Errorf also records the stack trace at the point it was called. func Errorf(format string, args ...interface{}) error { return &fundamental{ msg: fmt.Sprintf(format, args...), stack: callers(), } } // fundamental is an error that has a message and a stack, but no caller. type fundamental struct { msg string *stack } func (f *fundamental) Error() string { return f.msg } func (f *fundamental) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { io.WriteString(s, f.msg) f.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, f.msg) case 'q': fmt.Fprintf(s, "%q", f.msg) } } // WithStack annotates err with a stack trace at the point WithStack was called. // If err is nil, WithStack returns nil. func WithStack(err error) error { if err == nil { return nil } return &withStack{ err, callers(), } } type withStack struct { error *stack } func (w *withStack) Cause() error { return w.error } // Unwrap provides compatibility for Go 1.13 error chains. func (w *withStack) Unwrap() error { return w.error } func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { fmt.Fprintf(s, "%+v", w.Cause()) w.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, w.Error()) case 'q': fmt.Fprintf(s, "%q", w.Error()) } } // Wrap returns an error annotating err with a stack trace // at the point Wrap is called, and the supplied message. // If err is nil, Wrap returns nil. func Wrap(err error, message string) error { if err == nil { return nil } err = &withMessage{ cause: err, msg: message, } return &withStack{ err, callers(), } } // Wrapf returns an error annotating err with a stack trace // at the point Wrapf is called, and the format specifier. // If err is nil, Wrapf returns nil. func Wrapf(err error, format string, args ...interface{}) error { if err == nil { return nil } err = &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), } return &withStack{ err, callers(), } } // WithMessage annotates err with a new message. // If err is nil, WithMessage returns nil. func WithMessage(err error, message string) error { if err == nil { return nil } return &withMessage{ cause: err, msg: message, } } // WithMessagef annotates err with the format specifier. // If err is nil, WithMessagef returns nil. func WithMessagef(err error, format string, args ...interface{}) error { if err == nil { return nil } return &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), } } type withMessage struct { cause error msg string } func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Cause() error { return w.cause } // Unwrap provides compatibility for Go 1.13 error chains. func (w *withMessage) Unwrap() error { return w.cause } func (w *withMessage) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { fmt.Fprintf(s, "%+v\n", w.Cause()) io.WriteString(s, w.msg) return } fallthrough case 's', 'q': io.WriteString(s, w.Error()) } } // Cause returns the underlying cause of the error, if possible. // An error value has a cause if it implements the following // interface: // // type causer interface { // Cause() error // } // // If the error does not implement Cause, the original error will // be returned. If the error is nil, nil will be returned without further // investigation. func Cause(err error) error { type causer interface { Cause() error } for err != nil { cause, ok := err.(causer) if !ok { break } err = cause.Cause() } return err } ================================================ FILE: vendor/github.com/pkg/errors/go113.go ================================================ // +build go1.13 package errors import ( stderrors "errors" ) // Is reports whether any error in err's chain matches target. // // The chain consists of err itself followed by the sequence of errors obtained by // repeatedly calling Unwrap. // // An error is considered to match a target if it is equal to that target or if // it implements a method Is(error) bool such that Is(target) returns true. func Is(err, target error) bool { return stderrors.Is(err, target) } // As finds the first error in err's chain that matches target, and if so, sets // target to that error value and returns true. // // The chain consists of err itself followed by the sequence of errors obtained by // repeatedly calling Unwrap. // // An error matches target if the error's concrete value is assignable to the value // pointed to by target, or if the error has a method As(interface{}) bool such that // As(target) returns true. In the latter case, the As method is responsible for // setting target. // // As will panic if target is not a non-nil pointer to either a type that implements // error, or to any interface type. As returns false if err is nil. func As(err error, target interface{}) bool { return stderrors.As(err, target) } // Unwrap returns the result of calling the Unwrap method on err, if err's // type contains an Unwrap method returning error. // Otherwise, Unwrap returns nil. func Unwrap(err error) error { return stderrors.Unwrap(err) } ================================================ FILE: vendor/github.com/pkg/errors/stack.go ================================================ package errors import ( "fmt" "io" "path" "runtime" "strconv" "strings" ) // Frame represents a program counter inside a stack frame. // For historical reasons if Frame is interpreted as a uintptr // its value represents the program counter + 1. type Frame uintptr // pc returns the program counter for this frame; // multiple frames may have the same PC value. func (f Frame) pc() uintptr { return uintptr(f) - 1 } // file returns the full path to the file that contains the // function for this Frame's pc. func (f Frame) file() string { fn := runtime.FuncForPC(f.pc()) if fn == nil { return "unknown" } file, _ := fn.FileLine(f.pc()) return file } // line returns the line number of source code of the // function for this Frame's pc. func (f Frame) line() int { fn := runtime.FuncForPC(f.pc()) if fn == nil { return 0 } _, line := fn.FileLine(f.pc()) return line } // name returns the name of this function, if known. func (f Frame) name() string { fn := runtime.FuncForPC(f.pc()) if fn == nil { return "unknown" } return fn.Name() } // Format formats the frame according to the fmt.Formatter interface. // // %s source file // %d source line // %n function name // %v equivalent to %s:%d // // Format accepts flags that alter the printing of some verbs, as follows: // // %+s function name and path of source file relative to the compile time // GOPATH separated by \n\t (\n\t) // %+v equivalent to %+s:%d func (f Frame) Format(s fmt.State, verb rune) { switch verb { case 's': switch { case s.Flag('+'): io.WriteString(s, f.name()) io.WriteString(s, "\n\t") io.WriteString(s, f.file()) default: io.WriteString(s, path.Base(f.file())) } case 'd': io.WriteString(s, strconv.Itoa(f.line())) case 'n': io.WriteString(s, funcname(f.name())) case 'v': f.Format(s, 's') io.WriteString(s, ":") f.Format(s, 'd') } } // MarshalText formats a stacktrace Frame as a text string. The output is the // same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. func (f Frame) MarshalText() ([]byte, error) { name := f.name() if name == "unknown" { return []byte(name), nil } return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil } // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). type StackTrace []Frame // Format formats the stack of Frames according to the fmt.Formatter interface. // // %s lists source files for each Frame in the stack // %v lists the source file and line number for each Frame in the stack // // Format accepts flags that alter the printing of some verbs, as follows: // // %+v Prints filename, function, and line number for each Frame in the stack. func (st StackTrace) Format(s fmt.State, verb rune) { switch verb { case 'v': switch { case s.Flag('+'): for _, f := range st { io.WriteString(s, "\n") f.Format(s, verb) } case s.Flag('#'): fmt.Fprintf(s, "%#v", []Frame(st)) default: st.formatSlice(s, verb) } case 's': st.formatSlice(s, verb) } } // formatSlice will format this StackTrace into the given buffer as a slice of // Frame, only valid when called with '%s' or '%v'. func (st StackTrace) formatSlice(s fmt.State, verb rune) { io.WriteString(s, "[") for i, f := range st { if i > 0 { io.WriteString(s, " ") } f.Format(s, verb) } io.WriteString(s, "]") } // stack represents a stack of program counters. type stack []uintptr func (s *stack) Format(st fmt.State, verb rune) { switch verb { case 'v': switch { case st.Flag('+'): for _, pc := range *s { f := Frame(pc) fmt.Fprintf(st, "\n%+v", f) } } } } func (s *stack) StackTrace() StackTrace { f := make([]Frame, len(*s)) for i := 0; i < len(f); i++ { f[i] = Frame((*s)[i]) } return f } func callers() *stack { const depth = 32 var pcs [depth]uintptr n := runtime.Callers(3, pcs[:]) var st stack = pcs[0:n] return &st } // funcname removes the path prefix component of a function's name reported by func.Name(). func funcname(name string) string { i := strings.LastIndex(name, "/") name = name[i+1:] i = strings.Index(name, ".") return name[i+1:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/.gitignore ================================================ .idea/ ================================================ FILE: vendor/github.com/vishvananda/netlink/.travis.yml ================================================ language: go go: - "1.12.x" - "1.13.x" - "1.14.x" before_script: # make sure we keep path in tact when we sudo - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers # modprobe ip_gre or else the first gre device can't be deleted - sudo modprobe ip_gre # modprobe nf_conntrack for the conntrack testing - sudo modprobe nf_conntrack - sudo modprobe nf_conntrack_netlink - sudo modprobe nf_conntrack_ipv4 - sudo modprobe nf_conntrack_ipv6 - sudo modprobe sch_hfsc - sudo modprobe sch_sfq install: - go get -v -t ./... go_import_path: github.com/vishvananda/netlink ================================================ FILE: vendor/github.com/vishvananda/netlink/CHANGELOG.md ================================================ # Changelog ## 1.0.0 (2018-03-15) Initial release tagging ================================================ FILE: vendor/github.com/vishvananda/netlink/LICENSE ================================================ 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 Copyright 2014 Vishvananda Ishaya. Copyright 2014 Docker, Inc. 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: vendor/github.com/vishvananda/netlink/Makefile ================================================ DIRS := \ . \ nl DEPS = \ github.com/vishvananda/netns \ golang.org/x/sys/unix uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1))) testdirs = $(call uniq,$(foreach d,$(1),$(dir $(wildcard $(d)/*_test.go)))) goroot = $(addprefix ../../../,$(1)) unroot = $(subst ../../../,,$(1)) fmt = $(addprefix fmt-,$(1)) all: test $(call goroot,$(DEPS)): go get $(call unroot,$@) .PHONY: $(call testdirs,$(DIRS)) $(call testdirs,$(DIRS)): go test -test.exec sudo -test.parallel 4 -timeout 60s -test.v github.com/vishvananda/netlink/$@ $(call fmt,$(call testdirs,$(DIRS))): ! gofmt -l $(subst fmt-,,$@)/*.go | grep -q . .PHONY: fmt fmt: $(call fmt,$(call testdirs,$(DIRS))) test: fmt $(call goroot,$(DEPS)) $(call testdirs,$(DIRS)) ================================================ FILE: vendor/github.com/vishvananda/netlink/README.md ================================================ # netlink - netlink library for go # [![Build Status](https://app.travis-ci.com/vishvananda/netlink.svg?branch=master)](https://app.travis-ci.com/vishvananda/netlink.svg?branch=master) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink) The netlink package provides a simple netlink library for go. Netlink is the interface a user-space program in linux uses to communicate with the kernel. It can be used to add and remove interfaces, set ip addresses and routes, and configure ipsec. Netlink communication requires elevated privileges, so in most cases this code needs to be run as root. Since low-level netlink messages are inscrutable at best, the library attempts to provide an api that is loosely modeled on the CLI provided by iproute2. Actions like `ip link add` will be accomplished via a similarly named function like AddLink(). This library began its life as a fork of the netlink functionality in [docker/libcontainer](https://github.com/docker/libcontainer) but was heavily rewritten to improve testability, performance, and to add new functionality like ipsec xfrm handling. ## Local Build and Test ## You can use go get command: go get github.com/vishvananda/netlink Testing dependencies: go get github.com/vishvananda/netns Testing (requires root): sudo -E go test github.com/vishvananda/netlink ## Examples ## Add a new bridge and add eth1 into it: ```go package main import ( "fmt" "github.com/vishvananda/netlink" ) func main() { la := netlink.NewLinkAttrs() la.Name = "foo" mybridge := &netlink.Bridge{LinkAttrs: la} err := netlink.LinkAdd(mybridge) if err != nil { fmt.Printf("could not add %s: %v\n", la.Name, err) } eth1, _ := netlink.LinkByName("eth1") netlink.LinkSetMaster(eth1, mybridge) } ``` Note `NewLinkAttrs` constructor, it sets default values in structure. For now it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to `0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`. Add a new ip address to loopback: ```go package main import ( "github.com/vishvananda/netlink" ) func main() { lo, _ := netlink.LinkByName("lo") addr, _ := netlink.ParseAddr("169.254.169.254/32") netlink.AddrAdd(lo, addr) } ``` ## Future Work ## Many pieces of netlink are not yet fully supported in the high-level interface. Aspects of virtually all of the high-level objects don't exist. Many of the underlying primitives are there, so its a matter of putting the right fields into the high-level objects and making sure that they are serialized and deserialized correctly in the Add and List methods. There are also a few pieces of low level netlink functionality that still need to be implemented. Routing rules are not in place and some of the more advanced link types. Hopefully there is decent structure and testing in place to make these fairly straightforward to add. ================================================ FILE: vendor/github.com/vishvananda/netlink/addr.go ================================================ package netlink import ( "fmt" "net" "strings" ) // Addr represents an IP address from netlink. Netlink ip addresses // include a mask, so it stores the address as a net.IPNet. type Addr struct { *net.IPNet Label string Flags int Scope int Peer *net.IPNet Broadcast net.IP PreferedLft int ValidLft int LinkIndex int } // String returns $ip/$netmask $label func (a Addr) String() string { return strings.TrimSpace(fmt.Sprintf("%s %s", a.IPNet, a.Label)) } // ParseAddr parses the string representation of an address in the // form $ip/$netmask $label. The label portion is optional func ParseAddr(s string) (*Addr, error) { label := "" parts := strings.Split(s, " ") if len(parts) > 1 { s = parts[0] label = parts[1] } m, err := ParseIPNet(s) if err != nil { return nil, err } return &Addr{IPNet: m, Label: label}, nil } // Equal returns true if both Addrs have the same net.IPNet value. func (a Addr) Equal(x Addr) bool { sizea, _ := a.Mask.Size() sizeb, _ := x.Mask.Size() // ignore label for comparison return a.IP.Equal(x.IP) && sizea == sizeb } func (a Addr) PeerEqual(x Addr) bool { sizea, _ := a.Peer.Mask.Size() sizeb, _ := x.Peer.Mask.Size() // ignore label for comparison return a.Peer.IP.Equal(x.Peer.IP) && sizea == sizeb } ================================================ FILE: vendor/github.com/vishvananda/netlink/addr_linux.go ================================================ package netlink import ( "fmt" "net" "strings" "syscall" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) // AddrAdd will add an IP address to a link device. // // Equivalent to: `ip addr add $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func AddrAdd(link Link, addr *Addr) error { return pkgHandle.AddrAdd(link, addr) } // AddrAdd will add an IP address to a link device. // // Equivalent to: `ip addr add $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func (h *Handle) AddrAdd(link Link, addr *Addr) error { req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) return h.addrHandle(link, addr, req) } // AddrReplace will replace (or, if not present, add) an IP address on a link device. // // Equivalent to: `ip addr replace $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func AddrReplace(link Link, addr *Addr) error { return pkgHandle.AddrReplace(link, addr) } // AddrReplace will replace (or, if not present, add) an IP address on a link device. // // Equivalent to: `ip addr replace $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func (h *Handle) AddrReplace(link Link, addr *Addr) error { req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK) return h.addrHandle(link, addr, req) } // AddrDel will delete an IP address from a link device. // // Equivalent to: `ip addr del $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func AddrDel(link Link, addr *Addr) error { return pkgHandle.AddrDel(link, addr) } // AddrDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` // // If `addr` is an IPv4 address and the broadcast address is not given, it // will be automatically computed based on the IP mask if /30 or larger. func (h *Handle) AddrDel(link Link, addr *Addr) error { req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK) return h.addrHandle(link, addr, req) } func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { base := link.Attrs() if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { return fmt.Errorf("label must begin with interface name") } h.ensureIndex(base) family := nl.GetIPFamily(addr.IP) msg := nl.NewIfAddrmsg(family) msg.Index = uint32(base.Index) msg.Scope = uint8(addr.Scope) mask := addr.Mask if addr.Peer != nil { mask = addr.Peer.Mask } prefixlen, masklen := mask.Size() msg.Prefixlen = uint8(prefixlen) req.AddData(msg) var localAddrData []byte if family == FAMILY_V4 { localAddrData = addr.IP.To4() } else { localAddrData = addr.IP.To16() } localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData) req.AddData(localData) var peerAddrData []byte if addr.Peer != nil { if family == FAMILY_V4 { peerAddrData = addr.Peer.IP.To4() } else { peerAddrData = addr.Peer.IP.To16() } } else { peerAddrData = localAddrData } addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData) req.AddData(addressData) if addr.Flags != 0 { if addr.Flags <= 0xff { msg.IfAddrmsg.Flags = uint8(addr.Flags) } else { b := make([]byte, 4) native.PutUint32(b, uint32(addr.Flags)) flagsData := nl.NewRtAttr(unix.IFA_FLAGS, b) req.AddData(flagsData) } } if family == FAMILY_V4 { // Automatically set the broadcast address if it is unset and the // subnet is large enough to sensibly have one (/30 or larger). // See: RFC 3021 if addr.Broadcast == nil && prefixlen < 31 { calcBroadcast := make(net.IP, masklen/8) for i := range localAddrData { calcBroadcast[i] = localAddrData[i] | ^mask[i] } addr.Broadcast = calcBroadcast } if addr.Broadcast != nil { req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast)) } if addr.Label != "" { labelData := nl.NewRtAttr(unix.IFA_LABEL, nl.ZeroTerminated(addr.Label)) req.AddData(labelData) } } // 0 is the default value for these attributes. However, 0 means "expired", while the least-surprising default // value should be "forever". To compensate for that, only add the attributes if at least one of the values is // non-zero, which means the caller has explicitly set them if addr.ValidLft > 0 || addr.PreferedLft > 0 { cachedata := nl.IfaCacheInfo{unix.IfaCacheinfo{ Valid: uint32(addr.ValidLft), Prefered: uint32(addr.PreferedLft), }} req.AddData(nl.NewRtAttr(unix.IFA_CACHEINFO, cachedata.Serialize())) } _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // AddrList gets a list of IP addresses in the system. // Equivalent to: `ip addr show`. // The list can be filtered by link and ip family. func AddrList(link Link, family int) ([]Addr, error) { return pkgHandle.AddrList(link, family) } // AddrList gets a list of IP addresses in the system. // Equivalent to: `ip addr show`. // The list can be filtered by link and ip family. func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(family) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR) if err != nil { return nil, err } indexFilter := 0 if link != nil { base := link.Attrs() h.ensureIndex(base) indexFilter = base.Index } var res []Addr for _, m := range msgs { addr, msgFamily, err := parseAddr(m) if err != nil { return res, err } if link != nil && addr.LinkIndex != indexFilter { // Ignore messages from other interfaces continue } if family != FAMILY_ALL && msgFamily != family { continue } res = append(res, addr) } return res, nil } func parseAddr(m []byte) (addr Addr, family int, err error) { msg := nl.DeserializeIfAddrmsg(m) family = -1 addr.LinkIndex = -1 attrs, err1 := nl.ParseRouteAttr(m[msg.Len():]) if err1 != nil { err = err1 return } family = int(msg.Family) addr.LinkIndex = int(msg.Index) var local, dst *net.IPNet for _, attr := range attrs { switch attr.Attr.Type { case unix.IFA_ADDRESS: dst = &net.IPNet{ IP: attr.Value, Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), } case unix.IFA_LOCAL: // iproute2 manual: // If a peer address is specified, the local address // cannot have a prefix length. The network prefix is // associated with the peer rather than with the local // address. n := 8 * len(attr.Value) local = &net.IPNet{ IP: attr.Value, Mask: net.CIDRMask(n, n), } case unix.IFA_BROADCAST: addr.Broadcast = attr.Value case unix.IFA_LABEL: addr.Label = string(attr.Value[:len(attr.Value)-1]) case unix.IFA_FLAGS: addr.Flags = int(native.Uint32(attr.Value[0:4])) case unix.IFA_CACHEINFO: ci := nl.DeserializeIfaCacheInfo(attr.Value) addr.PreferedLft = int(ci.Prefered) addr.ValidLft = int(ci.Valid) } } // libnl addr.c comment: // IPv6 sends the local address as IFA_ADDRESS with no // IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS // with IFA_ADDRESS being the peer address if they differ // // But obviously, as there are IPv6 PtP addresses, too, // IFA_LOCAL should also be handled for IPv6. if local != nil { if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) { addr.IPNet = dst } else { addr.IPNet = local addr.Peer = dst } } else { addr.IPNet = dst } addr.Scope = int(msg.Scope) return } type AddrUpdate struct { LinkAddress net.IPNet LinkIndex int Flags int Scope int PreferedLft int ValidLft int NewAddr bool // true=added false=deleted } // AddrSubscribe takes a chan down which notifications will be sent // when addresses change. Close the 'done' chan to stop subscription. func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0) } // AddrSubscribeAt works like AddrSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0) } // AddrSubscribeOptions contains a set of options to use with // AddrSubscribeWithOptions. type AddrSubscribeOptions struct { Namespace *netns.NsHandle ErrorCallback func(error) ListExisting bool ReceiveBufferSize int } // AddrSubscribeWithOptions work like AddrSubscribe but enable to // provide additional options to modify the behavior. Currently, the // namespace can be provided as well as an error callback. func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error { if options.Namespace == nil { none := netns.None() options.Namespace = &none } return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting, options.ReceiveBufferSize) } func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool, rcvbuf int) error { s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR) if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } if rcvbuf != 0 { err = pkgHandle.SetSocketReceiveBufferSize(rcvbuf, false) if err != nil { return err } } if listExisting { req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(infmsg) if err := s.Send(req); err != nil { return err } } go func() { defer close(ch) for { msgs, from, err := s.Receive() if err != nil { if cberr != nil { cberr(fmt.Errorf("Receive failed: %v", err)) } return } if from.Pid != nl.PidKernel { if cberr != nil { cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) } continue } for _, m := range msgs { if m.Header.Type == unix.NLMSG_DONE { continue } if m.Header.Type == unix.NLMSG_ERROR { error := int32(native.Uint32(m.Data[0:4])) if error == 0 { continue } if cberr != nil { cberr(fmt.Errorf("error message: %v", syscall.Errno(-error))) } continue } msgType := m.Header.Type if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR { if cberr != nil { cberr(fmt.Errorf("bad message type: %d", msgType)) } continue } addr, _, err := parseAddr(m.Data) if err != nil { if cberr != nil { cberr(fmt.Errorf("could not parse address: %v", err)) } continue } ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: addr.LinkIndex, NewAddr: msgType == unix.RTM_NEWADDR, Flags: addr.Flags, Scope: addr.Scope, PreferedLft: addr.PreferedLft, ValidLft: addr.ValidLft} } } }() return nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/bpf_linux.go ================================================ package netlink import ( "unsafe" "golang.org/x/sys/unix" ) type BpfProgType uint32 const ( BPF_PROG_TYPE_UNSPEC BpfProgType = iota BPF_PROG_TYPE_SOCKET_FILTER BPF_PROG_TYPE_KPROBE BPF_PROG_TYPE_SCHED_CLS BPF_PROG_TYPE_SCHED_ACT BPF_PROG_TYPE_TRACEPOINT BPF_PROG_TYPE_XDP BPF_PROG_TYPE_PERF_EVENT BPF_PROG_TYPE_CGROUP_SKB BPF_PROG_TYPE_CGROUP_SOCK BPF_PROG_TYPE_LWT_IN BPF_PROG_TYPE_LWT_OUT BPF_PROG_TYPE_LWT_XMIT BPF_PROG_TYPE_SOCK_OPS BPF_PROG_TYPE_SK_SKB BPF_PROG_TYPE_CGROUP_DEVICE BPF_PROG_TYPE_SK_MSG BPF_PROG_TYPE_RAW_TRACEPOINT BPF_PROG_TYPE_CGROUP_SOCK_ADDR BPF_PROG_TYPE_LWT_SEG6LOCAL BPF_PROG_TYPE_LIRC_MODE2 BPF_PROG_TYPE_SK_REUSEPORT BPF_PROG_TYPE_FLOW_DISSECTOR BPF_PROG_TYPE_CGROUP_SYSCTL BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE BPF_PROG_TYPE_CGROUP_SOCKOPT BPF_PROG_TYPE_TRACING BPF_PROG_TYPE_STRUCT_OPS BPF_PROG_TYPE_EXT BPF_PROG_TYPE_LSM BPF_PROG_TYPE_SK_LOOKUP ) type BPFAttr struct { ProgType uint32 InsnCnt uint32 Insns uintptr License uintptr LogLevel uint32 LogSize uint32 LogBuf uintptr KernVersion uint32 } // loadSimpleBpf loads a trivial bpf program for testing purposes. func loadSimpleBpf(progType BpfProgType, ret uint32) (int, error) { insns := []uint64{ 0x00000000000000b7 | (uint64(ret) << 32), 0x0000000000000095, } license := []byte{'A', 'S', 'L', '2', '\x00'} attr := BPFAttr{ ProgType: uint32(progType), InsnCnt: uint32(len(insns)), Insns: uintptr(unsafe.Pointer(&insns[0])), License: uintptr(unsafe.Pointer(&license[0])), } fd, _, errno := unix.Syscall(unix.SYS_BPF, 5, /* bpf cmd */ uintptr(unsafe.Pointer(&attr)), unsafe.Sizeof(attr)) if errno != 0 { return 0, errno } return int(fd), nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/bridge_linux.go ================================================ package netlink import ( "fmt" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // BridgeVlanList gets a map of device id to bridge vlan infos. // Equivalent to: `bridge vlan show` func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) { return pkgHandle.BridgeVlanList() } // BridgeVlanList gets a map of device id to bridge vlan infos. // Equivalent to: `bridge vlan show` func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) { req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(unix.AF_BRIDGE) req.AddData(msg) req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN)))) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK) if err != nil { return nil, err } ret := make(map[int32][]*nl.BridgeVlanInfo) for _, m := range msgs { msg := nl.DeserializeIfInfomsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } for _, attr := range attrs { switch attr.Attr.Type { case unix.IFLA_AF_SPEC: //nested attr nestAttrs, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, fmt.Errorf("failed to parse nested attr %v", err) } for _, nestAttr := range nestAttrs { switch nestAttr.Attr.Type { case nl.IFLA_BRIDGE_VLAN_INFO: vlanInfo := nl.DeserializeBridgeVlanInfo(nestAttr.Value) ret[msg.Index] = append(ret[msg.Index], vlanInfo) } } } } } return ret, nil } // BridgeVlanAdd adds a new vlan filter entry // Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error { return pkgHandle.BridgeVlanAdd(link, vid, pvid, untagged, self, master) } // BridgeVlanAdd adds a new vlan filter entry // Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error { return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, pvid, untagged, self, master) } // BridgeVlanDel adds a new vlan filter entry // Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error { return pkgHandle.BridgeVlanDel(link, vid, pvid, untagged, self, master) } // BridgeVlanDel adds a new vlan filter entry // Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error { return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, pvid, untagged, self, master) } func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(cmd, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_BRIDGE) msg.Index = int32(base.Index) req.AddData(msg) br := nl.NewRtAttr(unix.IFLA_AF_SPEC, nil) var flags uint16 if self { flags |= nl.BRIDGE_FLAGS_SELF } if master { flags |= nl.BRIDGE_FLAGS_MASTER } if flags > 0 { br.AddRtAttr(nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags)) } vlanInfo := &nl.BridgeVlanInfo{Vid: vid} if pvid { vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID } if untagged { vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED } br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize()) req.AddData(br) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } ================================================ FILE: vendor/github.com/vishvananda/netlink/class.go ================================================ package netlink import ( "fmt" ) // Class interfaces for all classes type Class interface { Attrs() *ClassAttrs Type() string } // Generic networking statistics for netlink users. // This file contains "gnet_" prefixed structs and relevant functions. // See Documentation/networking/getn_stats.txt in Linux source code for more details. // GnetStatsBasic Ref: struct gnet_stats_basic { ... } type GnetStatsBasic struct { Bytes uint64 // number of seen bytes Packets uint32 // number of seen packets } // GnetStatsRateEst Ref: struct gnet_stats_rate_est { ... } type GnetStatsRateEst struct { Bps uint32 // current byte rate Pps uint32 // current packet rate } // GnetStatsRateEst64 Ref: struct gnet_stats_rate_est64 { ... } type GnetStatsRateEst64 struct { Bps uint64 // current byte rate Pps uint64 // current packet rate } // GnetStatsQueue Ref: struct gnet_stats_queue { ... } type GnetStatsQueue struct { Qlen uint32 // queue length Backlog uint32 // backlog size of queue Drops uint32 // number of dropped packets Requeues uint32 // number of requues Overlimits uint32 // number of enqueues over the limit } // ClassStatistics representation based on generic networking statistics for netlink. // See Documentation/networking/gen_stats.txt in Linux source code for more details. type ClassStatistics struct { Basic *GnetStatsBasic Queue *GnetStatsQueue RateEst *GnetStatsRateEst } // NewClassStatistics Construct a ClassStatistics struct which fields are all initialized by 0. func NewClassStatistics() *ClassStatistics { return &ClassStatistics{ Basic: &GnetStatsBasic{}, Queue: &GnetStatsQueue{}, RateEst: &GnetStatsRateEst{}, } } // ClassAttrs represents a netlink class. A filter is associated with a link, // has a handle and a parent. The root filter of a device should have a // parent == HANDLE_ROOT. type ClassAttrs struct { LinkIndex int Handle uint32 Parent uint32 Leaf uint32 Statistics *ClassStatistics } func (q ClassAttrs) String() string { return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf) } // HtbClassAttrs stores the attributes of HTB class type HtbClassAttrs struct { // TODO handle all attributes Rate uint64 Ceil uint64 Buffer uint32 Cbuffer uint32 Quantum uint32 Level uint32 Prio uint32 } func (q HtbClassAttrs) String() string { return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer) } // HtbClass represents an Htb class type HtbClass struct { ClassAttrs Rate uint64 Ceil uint64 Buffer uint32 Cbuffer uint32 Quantum uint32 Level uint32 Prio uint32 } func (q HtbClass) String() string { return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer) } // Attrs returns the class attributes func (q *HtbClass) Attrs() *ClassAttrs { return &q.ClassAttrs } // Type return the class type func (q *HtbClass) Type() string { return "htb" } // GenericClass classes represent types that are not currently understood // by this netlink library. type GenericClass struct { ClassAttrs ClassType string } // Attrs return the class attributes func (class *GenericClass) Attrs() *ClassAttrs { return &class.ClassAttrs } // Type return the class type func (class *GenericClass) Type() string { return class.ClassType } // ServiceCurve is a nondecreasing function of some time unit, returning the amount of service // (an allowed or allocated amount of bandwidth) at some specific point in time. The purpose of it // should be subconsciously obvious: if a class was allowed to transfer not less than the amount // specified by its service curve, then the service curve is not violated. type ServiceCurve struct { m1 uint32 d uint32 m2 uint32 } // Attrs return the parameters of the service curve func (c *ServiceCurve) Attrs() (uint32, uint32, uint32) { return c.m1, c.d, c.m2 } // Burst returns the burst rate (m1) of the curve func (c *ServiceCurve) Burst() uint32 { return c.m1 } // Delay return the delay (d) of the curve func (c *ServiceCurve) Delay() uint32 { return c.d } // Rate returns the rate (m2) of the curve func (c *ServiceCurve) Rate() uint32 { return c.m2 } // HfscClass is a representation of the HFSC class type HfscClass struct { ClassAttrs Rsc ServiceCurve Fsc ServiceCurve Usc ServiceCurve } // SetUsc sets the USC curve. The bandwidth (m1 and m2) is specified in bits and the delay in // seconds. func (hfsc *HfscClass) SetUsc(m1 uint32, d uint32, m2 uint32) { hfsc.Usc = ServiceCurve{m1: m1, d: d, m2: m2} } // SetFsc sets the Fsc curve. The bandwidth (m1 and m2) is specified in bits and the delay in // seconds. func (hfsc *HfscClass) SetFsc(m1 uint32, d uint32, m2 uint32) { hfsc.Fsc = ServiceCurve{m1: m1, d: d, m2: m2} } // SetRsc sets the Rsc curve. The bandwidth (m1 and m2) is specified in bits and the delay in // seconds. func (hfsc *HfscClass) SetRsc(m1 uint32, d uint32, m2 uint32) { hfsc.Rsc = ServiceCurve{m1: m1, d: d, m2: m2} } // SetSC implements the SC from the `tc` CLI. This function behaves the same as if one would set the // USC through the `tc` command-line tool. This means bandwidth (m1 and m2) is specified in bits and // the delay in ms. func (hfsc *HfscClass) SetSC(m1 uint32, d uint32, m2 uint32) { hfsc.SetRsc(m1, d, m2) hfsc.SetFsc(m1, d, m2) } // SetUL implements the UL from the `tc` CLI. This function behaves the same as if one would set the // USC through the `tc` command-line tool. This means bandwidth (m1 and m2) is specified in bits and // the delay in ms. func (hfsc *HfscClass) SetUL(m1 uint32, d uint32, m2 uint32) { hfsc.SetUsc(m1, d, m2) } // SetLS implements the LS from the `tc` CLI. This function behaves the same as if one would set the // USC through the `tc` command-line tool. This means bandwidth (m1 and m2) is specified in bits and // the delay in ms. func (hfsc *HfscClass) SetLS(m1 uint32, d uint32, m2 uint32) { hfsc.SetFsc(m1, d, m2) } // NewHfscClass returns a new HFSC struct with the set parameters func NewHfscClass(attrs ClassAttrs) *HfscClass { return &HfscClass{ ClassAttrs: attrs, Rsc: ServiceCurve{}, Fsc: ServiceCurve{}, Usc: ServiceCurve{}, } } // String() returns a string that contains the information and attributes of the HFSC class func (hfsc *HfscClass) String() string { return fmt.Sprintf( "{%s -- {RSC: {m1=%d d=%d m2=%d}} {FSC: {m1=%d d=%d m2=%d}} {USC: {m1=%d d=%d m2=%d}}}", hfsc.Attrs(), hfsc.Rsc.m1*8, hfsc.Rsc.d, hfsc.Rsc.m2*8, hfsc.Fsc.m1*8, hfsc.Fsc.d, hfsc.Fsc.m2*8, hfsc.Usc.m1*8, hfsc.Usc.d, hfsc.Usc.m2*8, ) } // Attrs return the Hfsc parameters func (hfsc *HfscClass) Attrs() *ClassAttrs { return &hfsc.ClassAttrs } // Type return the type of the class func (hfsc *HfscClass) Type() string { return "hfsc" } ================================================ FILE: vendor/github.com/vishvananda/netlink/class_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "encoding/hex" "errors" "fmt" "syscall" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // Internal tc_stats representation in Go struct. // This is for internal uses only to deserialize the payload of rtattr. // After the deserialization, this should be converted into the canonical stats // struct, ClassStatistics, in case of statistics of a class. // Ref: struct tc_stats { ... } type tcStats struct { Bytes uint64 // Number of enqueued bytes Packets uint32 // Number of enqueued packets Drops uint32 // Packets dropped because of lack of resources Overlimits uint32 // Number of throttle events when this flow goes out of allocated bandwidth Bps uint32 // Current flow byte rate Pps uint32 // Current flow packet rate Qlen uint32 Backlog uint32 } // NewHtbClass NOTE: function is in here because it uses other linux functions func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass { mtu := 1600 rate := cattrs.Rate / 8 ceil := cattrs.Ceil / 8 buffer := cattrs.Buffer cbuffer := cattrs.Cbuffer if ceil == 0 { ceil = rate } if buffer == 0 { buffer = uint32(float64(rate)/Hz() + float64(mtu)) } buffer = Xmittime(rate, buffer) if cbuffer == 0 { cbuffer = uint32(float64(ceil)/Hz() + float64(mtu)) } cbuffer = Xmittime(ceil, cbuffer) return &HtbClass{ ClassAttrs: attrs, Rate: rate, Ceil: ceil, Buffer: buffer, Cbuffer: cbuffer, Level: 0, Prio: cattrs.Prio, Quantum: cattrs.Quantum, } } // ClassDel will delete a class from the system. // Equivalent to: `tc class del $class` func ClassDel(class Class) error { return pkgHandle.ClassDel(class) } // ClassDel will delete a class from the system. // Equivalent to: `tc class del $class` func (h *Handle) ClassDel(class Class) error { return h.classModify(unix.RTM_DELTCLASS, 0, class) } // ClassChange will change a class in place // Equivalent to: `tc class change $class` // The parent and handle MUST NOT be changed. func ClassChange(class Class) error { return pkgHandle.ClassChange(class) } // ClassChange will change a class in place // Equivalent to: `tc class change $class` // The parent and handle MUST NOT be changed. func (h *Handle) ClassChange(class Class) error { return h.classModify(unix.RTM_NEWTCLASS, 0, class) } // ClassReplace will replace a class to the system. // quivalent to: `tc class replace $class` // The handle MAY be changed. // If a class already exist with this parent/handle pair, the class is changed. // If a class does not already exist with this parent/handle, a new class is created. func ClassReplace(class Class) error { return pkgHandle.ClassReplace(class) } // ClassReplace will replace a class to the system. // quivalent to: `tc class replace $class` // The handle MAY be changed. // If a class already exist with this parent/handle pair, the class is changed. // If a class does not already exist with this parent/handle, a new class is created. func (h *Handle) ClassReplace(class Class) error { return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class) } // ClassAdd will add a class to the system. // Equivalent to: `tc class add $class` func ClassAdd(class Class) error { return pkgHandle.ClassAdd(class) } // ClassAdd will add a class to the system. // Equivalent to: `tc class add $class` func (h *Handle) ClassAdd(class Class) error { return h.classModify( unix.RTM_NEWTCLASS, unix.NLM_F_CREATE|unix.NLM_F_EXCL, class, ) } func (h *Handle) classModify(cmd, flags int, class Class) error { req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK) base := class.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: int32(base.LinkIndex), Handle: base.Handle, Parent: base.Parent, } req.AddData(msg) if cmd != unix.RTM_DELTCLASS { if err := classPayload(req, class); err != nil { return err } } _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func classPayload(req *nl.NetlinkRequest, class Class) error { req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type()))) options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) switch class.Type() { case "htb": htb := class.(*HtbClass) opt := nl.TcHtbCopt{} opt.Buffer = htb.Buffer opt.Cbuffer = htb.Cbuffer opt.Quantum = htb.Quantum opt.Level = htb.Level opt.Prio = htb.Prio // TODO: Handle Debug properly. For now default to 0 /* Calculate {R,C}Tab and set Rate and Ceil */ cellLog := -1 ccellLog := -1 linklayer := nl.LINKLAYER_ETHERNET mtu := 1600 var rtab [256]uint32 var ctab [256]uint32 tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)} if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 { return errors.New("HTB: failed to calculate rate table") } opt.Rate = tcrate tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)} if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 { return errors.New("HTB: failed to calculate ceil rate table") } opt.Ceil = tcceil options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize()) options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab)) options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab)) if htb.Rate >= uint64(1<<32) { options.AddRtAttr(nl.TCA_HTB_RATE64, nl.Uint64Attr(htb.Rate)) } if htb.Ceil >= uint64(1<<32) { options.AddRtAttr(nl.TCA_HTB_CEIL64, nl.Uint64Attr(htb.Ceil)) } case "hfsc": hfsc := class.(*HfscClass) opt := nl.HfscCopt{} rm1, rd, rm2 := hfsc.Rsc.Attrs() opt.Rsc.Set(rm1/8, rd, rm2/8) fm1, fd, fm2 := hfsc.Fsc.Attrs() opt.Fsc.Set(fm1/8, fd, fm2/8) um1, ud, um2 := hfsc.Usc.Attrs() opt.Usc.Set(um1/8, ud, um2/8) nl.NewRtAttrChild(options, nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc)) nl.NewRtAttrChild(options, nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc)) nl.NewRtAttrChild(options, nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc)) } req.AddData(options) return nil } // ClassList gets a list of classes in the system. // Equivalent to: `tc class show`. // Generally returns nothing if link and parent are not specified. func ClassList(link Link, parent uint32) ([]Class, error) { return pkgHandle.ClassList(link, parent) } // ClassList gets a list of classes in the system. // Equivalent to: `tc class show`. // Generally returns nothing if link and parent are not specified. func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) { req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() h.ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS) if err != nil { return nil, err } var res []Class for _, m := range msgs { msg := nl.DeserializeTcMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := ClassAttrs{ LinkIndex: int(msg.Ifindex), Handle: msg.Handle, Parent: msg.Parent, Statistics: nil, } var class Class classType := "" for _, attr := range attrs { switch attr.Attr.Type { case nl.TCA_KIND: classType = string(attr.Value[:len(attr.Value)-1]) switch classType { case "htb": class = &HtbClass{} case "hfsc": class = &HfscClass{} default: class = &GenericClass{ClassType: classType} } case nl.TCA_OPTIONS: switch classType { case "htb": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } _, err = parseHtbClassData(class, data) if err != nil { return nil, err } case "hfsc": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } _, err = parseHfscClassData(class, data) if err != nil { return nil, err } } // For backward compatibility. case nl.TCA_STATS: base.Statistics, err = parseTcStats(attr.Value) if err != nil { return nil, err } case nl.TCA_STATS2: base.Statistics, err = parseTcStats2(attr.Value) if err != nil { return nil, err } } } *class.Attrs() = base res = append(res, class) } return res, nil } func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) { htb := class.(*HtbClass) detailed := false for _, datum := range data { switch datum.Attr.Type { case nl.TCA_HTB_PARMS: opt := nl.DeserializeTcHtbCopt(datum.Value) htb.Rate = uint64(opt.Rate.Rate) htb.Ceil = uint64(opt.Ceil.Rate) htb.Buffer = opt.Buffer htb.Cbuffer = opt.Cbuffer htb.Quantum = opt.Quantum htb.Level = opt.Level htb.Prio = opt.Prio case nl.TCA_HTB_RATE64: htb.Rate = native.Uint64(datum.Value[0:8]) case nl.TCA_HTB_CEIL64: htb.Ceil = native.Uint64(datum.Value[0:8]) } } return detailed, nil } func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) { hfsc := class.(*HfscClass) detailed := false for _, datum := range data { m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs() switch datum.Attr.Type { case nl.TCA_HFSC_RSC: hfsc.Rsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8} case nl.TCA_HFSC_FSC: hfsc.Fsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8} case nl.TCA_HFSC_USC: hfsc.Usc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8} } } return detailed, nil } func parseTcStats(data []byte) (*ClassStatistics, error) { buf := &bytes.Buffer{} buf.Write(data) tcStats := &tcStats{} if err := binary.Read(buf, native, tcStats); err != nil { return nil, err } stats := NewClassStatistics() stats.Basic.Bytes = tcStats.Bytes stats.Basic.Packets = tcStats.Packets stats.Queue.Qlen = tcStats.Qlen stats.Queue.Backlog = tcStats.Backlog stats.Queue.Drops = tcStats.Drops stats.Queue.Overlimits = tcStats.Overlimits stats.RateEst.Bps = tcStats.Bps stats.RateEst.Pps = tcStats.Pps return stats, nil } func parseGnetStats(data []byte, gnetStats interface{}) error { buf := &bytes.Buffer{} buf.Write(data) return binary.Read(buf, native, gnetStats) } func parseTcStats2(data []byte) (*ClassStatistics, error) { rtAttrs, err := nl.ParseRouteAttr(data) if err != nil { return nil, err } stats := NewClassStatistics() for _, datum := range rtAttrs { switch datum.Attr.Type { case nl.TCA_STATS_BASIC: if err := parseGnetStats(datum.Value, stats.Basic); err != nil { return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s", err, hex.Dump(datum.Value)) } case nl.TCA_STATS_QUEUE: if err := parseGnetStats(datum.Value, stats.Queue); err != nil { return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s", err, hex.Dump(datum.Value)) } case nl.TCA_STATS_RATE_EST: if err := parseGnetStats(datum.Value, stats.RateEst); err != nil { return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s", err, hex.Dump(datum.Value)) } } } return stats, nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/conntrack_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "errors" "fmt" "net" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // ConntrackTableType Conntrack table for the netlink operation type ConntrackTableType uint8 const ( // ConntrackTable Conntrack table // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK 1 ConntrackTable = 1 // ConntrackExpectTable Conntrack expect table // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2 ConntrackExpectTable = 2 ) const ( // backward compatibility with golang 1.6 which does not have io.SeekCurrent seekCurrent = 1 ) // InetFamily Family type type InetFamily uint8 // -L [table] [options] List conntrack or expectation table // -G [table] parameters Get conntrack or expectation // -I [table] parameters Create a conntrack or expectation // -U [table] parameters Update a conntrack // -E [table] [options] Show events // -C [table] Show counter // -S Show statistics // ConntrackTableList returns the flow list of a table of a specific family // conntrack -L [table] [options] List conntrack or expectation table func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { return pkgHandle.ConntrackTableList(table, family) } // ConntrackTableFlush flushes all the flows of a specified table // conntrack -F [table] Flush table // The flush operation applies to all the family types func ConntrackTableFlush(table ConntrackTableType) error { return pkgHandle.ConntrackTableFlush(table) } // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter // conntrack -D [table] parameters Delete conntrack or expectation func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) { return pkgHandle.ConntrackDeleteFilter(table, family, filter) } // ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed // conntrack -L [table] [options] List conntrack or expectation table func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { res, err := h.dumpConntrackTable(table, family) if err != nil { return nil, err } // Deserialize all the flows var result []*ConntrackFlow for _, dataRaw := range res { result = append(result, parseRawData(dataRaw)) } return result, nil } // ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed // conntrack -F [table] Flush table // The flush operation applies to all the family types func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error { req := h.newConntrackRequest(table, unix.AF_INET, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK) _, err := req.Execute(unix.NETLINK_NETFILTER, 0) return err } // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed // conntrack -D [table] parameters Delete conntrack or expectation func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) { res, err := h.dumpConntrackTable(table, family) if err != nil { return 0, err } var matched uint for _, dataRaw := range res { flow := parseRawData(dataRaw) if match := filter.MatchConntrackFlow(flow); match { req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, unix.NLM_F_ACK) // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already req2.AddRawData(dataRaw[4:]) req2.Execute(unix.NETLINK_NETFILTER, 0) matched++ } } return matched, nil } func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest { // Create the Netlink request object req := h.newNetlinkRequest((int(table)<<8)|operation, flags) // Add the netfilter header msg := &nl.Nfgenmsg{ NfgenFamily: uint8(family), Version: nl.NFNETLINK_V0, ResId: 0, } req.AddData(msg) return req } func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) { req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, unix.NLM_F_DUMP) return req.Execute(unix.NETLINK_NETFILTER, 0) } // The full conntrack flow structure is very complicated and can be found in the file: // http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h // For the time being, the structure below allows to parse and extract the base information of a flow type ipTuple struct { Bytes uint64 DstIP net.IP DstPort uint16 Packets uint64 Protocol uint8 SrcIP net.IP SrcPort uint16 } type ConntrackFlow struct { FamilyType uint8 Forward ipTuple Reverse ipTuple Mark uint32 } func (s *ConntrackFlow) String() string { // conntrack cmd output: // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0 return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=%d", nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol, s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes, s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes, s.Mark) } // This method parse the ip tuple structure // The message structure is the following: // // // // // func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 { for i := 0; i < 2; i++ { _, t, _, v := parseNfAttrTLV(reader) switch t { case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC: tpl.SrcIP = v case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST: tpl.DstIP = v } } // Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO reader.Seek(4, seekCurrent) _, t, _, v := parseNfAttrTLV(reader) if t == nl.CTA_PROTO_NUM { tpl.Protocol = uint8(v[0]) } // Skip some padding 3 bytes reader.Seek(3, seekCurrent) for i := 0; i < 2; i++ { _, t, _ := parseNfAttrTL(reader) switch t { case nl.CTA_PROTO_SRC_PORT: parseBERaw16(reader, &tpl.SrcPort) case nl.CTA_PROTO_DST_PORT: parseBERaw16(reader, &tpl.DstPort) } // Skip some padding 2 byte reader.Seek(2, seekCurrent) } return tpl.Protocol } func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) { isNested, attrType, len = parseNfAttrTL(r) value = make([]byte, len) binary.Read(r, binary.BigEndian, &value) return isNested, attrType, len, value } func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) { binary.Read(r, nl.NativeEndian(), &len) len -= nl.SizeofNfattr binary.Read(r, nl.NativeEndian(), &attrType) isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED attrType = attrType & (nl.NLA_F_NESTED - 1) return isNested, attrType, len } func parseBERaw16(r *bytes.Reader, v *uint16) { binary.Read(r, binary.BigEndian, v) } func parseBERaw32(r *bytes.Reader, v *uint32) { binary.Read(r, binary.BigEndian, v) } func parseBERaw64(r *bytes.Reader, v *uint64) { binary.Read(r, binary.BigEndian, v) } func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) { for i := 0; i < 2; i++ { switch _, t, _ := parseNfAttrTL(r); t { case nl.CTA_COUNTERS_BYTES: parseBERaw64(r, &bytes) case nl.CTA_COUNTERS_PACKETS: parseBERaw64(r, &packets) default: return } } return } func parseConnectionMark(r *bytes.Reader) (mark uint32) { parseBERaw32(r, &mark) return } func parseRawData(data []byte) *ConntrackFlow { s := &ConntrackFlow{} // First there is the Nfgenmsg header // consume only the family field reader := bytes.NewReader(data) binary.Read(reader, nl.NativeEndian(), &s.FamilyType) // skip rest of the Netfilter header reader.Seek(3, seekCurrent) // The message structure is the following: // 4 bytes // 4 bytes // flow information of the forward flow // 4 bytes // 4 bytes // flow information of the reverse flow for reader.Len() > 0 { if nested, t, l := parseNfAttrTL(reader); nested { switch t { case nl.CTA_TUPLE_ORIG: if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { parseIpTuple(reader, &s.Forward) } case nl.CTA_TUPLE_REPLY: if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { parseIpTuple(reader, &s.Reverse) } else { // Header not recognized skip it reader.Seek(int64(l), seekCurrent) } case nl.CTA_COUNTERS_ORIG: s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader) case nl.CTA_COUNTERS_REPLY: s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader) } } else { switch t { case nl.CTA_MARK: s.Mark = parseConnectionMark(reader) } } } return s } // Conntrack parameters and options: // -n, --src-nat ip source NAT ip // -g, --dst-nat ip destination NAT ip // -j, --any-nat ip source or destination NAT ip // -m, --mark mark Set mark // -c, --secmark secmark Set selinux secmark // -e, --event-mask eventmask Event mask, eg. NEW,DESTROY // -z, --zero Zero counters while listing // -o, --output type[,...] Output format, eg. xml // -l, --label label[,...] conntrack labels // Common parameters and options: // -s, --src, --orig-src ip Source address from original direction // -d, --dst, --orig-dst ip Destination address from original direction // -r, --reply-src ip Source address from reply direction // -q, --reply-dst ip Destination address from reply direction // -p, --protonum proto Layer 4 Protocol, eg. 'tcp' // -f, --family proto Layer 3 Protocol, eg. 'ipv6' // -t, --timeout timeout Set timeout // -u, --status status Set status, eg. ASSURED // -w, --zone value Set conntrack zone // --orig-zone value Set zone for original direction // --reply-zone value Set zone for reply direction // -b, --buffer-size Netlink socket buffer size // --mask-src ip Source mask address // --mask-dst ip Destination mask address // Layer 4 Protocol common parameters and options: // TCP, UDP, SCTP, UDPLite and DCCP // --sport, --orig-port-src port Source port in original direction // --dport, --orig-port-dst port Destination port in original direction // Filter types type ConntrackFilterType uint8 const ( ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction ConntrackOrigDstIP // -orig-dst ip Destination address from original direction ConntrackReplySrcIP // --reply-src ip Reply Source IP ConntrackReplyDstIP // --reply-dst ip Reply Destination IP ConntrackReplyAnyIP // Match source or destination reply IP ConntrackOrigSrcPort // --orig-port-src port Source port in original direction ConntrackOrigDstPort // --orig-port-dst port Destination port in original direction ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instead ConntrackReplyAnyIP ) type CustomConntrackFilter interface { // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches // the filter or false otherwise MatchConntrackFlow(flow *ConntrackFlow) bool } type ConntrackFilter struct { ipNetFilter map[ConntrackFilterType]*net.IPNet portFilter map[ConntrackFilterType]uint16 protoFilter uint8 } // AddIPNet adds a IP subnet to the conntrack filter func (f *ConntrackFilter) AddIPNet(tp ConntrackFilterType, ipNet *net.IPNet) error { if ipNet == nil { return fmt.Errorf("Filter attribute empty") } if f.ipNetFilter == nil { f.ipNetFilter = make(map[ConntrackFilterType]*net.IPNet) } if _, ok := f.ipNetFilter[tp]; ok { return errors.New("Filter attribute already present") } f.ipNetFilter[tp] = ipNet return nil } // AddIP adds an IP to the conntrack filter func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error { if ip == nil { return fmt.Errorf("Filter attribute empty") } return f.AddIPNet(tp, NewIPNet(ip)) } // AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error { switch f.protoFilter { // TCP, UDP, DCCP, SCTP, UDPLite case 6, 17, 33, 132, 136: default: return fmt.Errorf("Filter attribute not available without a valid Layer 4 protocol: %d", f.protoFilter) } if f.portFilter == nil { f.portFilter = make(map[ConntrackFilterType]uint16) } if _, ok := f.portFilter[tp]; ok { return errors.New("Filter attribute already present") } f.portFilter[tp] = port return nil } // AddProtocol adds the Layer 4 protocol to the conntrack filter func (f *ConntrackFilter) AddProtocol(proto uint8) error { if f.protoFilter != 0 { return errors.New("Filter attribute already present") } f.protoFilter = proto return nil } // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter // false otherwise func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool { if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 { // empty filter always not match return false } // -p, --protonum proto Layer 4 Protocol, eg. 'tcp' if f.protoFilter != 0 && flow.Forward.Protocol != f.protoFilter { // different Layer 4 protocol always not match return false } match := true // IP conntrack filter if len(f.ipNetFilter) > 0 { // -orig-src ip Source address from original direction if elem, found := f.ipNetFilter[ConntrackOrigSrcIP]; found { match = match && elem.Contains(flow.Forward.SrcIP) } // -orig-dst ip Destination address from original direction if elem, found := f.ipNetFilter[ConntrackOrigDstIP]; match && found { match = match && elem.Contains(flow.Forward.DstIP) } // -src-nat ip Source NAT ip if elem, found := f.ipNetFilter[ConntrackReplySrcIP]; match && found { match = match && elem.Contains(flow.Reverse.SrcIP) } // -dst-nat ip Destination NAT ip if elem, found := f.ipNetFilter[ConntrackReplyDstIP]; match && found { match = match && elem.Contains(flow.Reverse.DstIP) } // Match source or destination reply IP if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found { match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP)) } } // Layer 4 Port filter if len(f.portFilter) > 0 { // -orig-port-src port Source port from original direction if elem, found := f.portFilter[ConntrackOrigSrcPort]; match && found { match = match && elem == flow.Forward.SrcPort } // -orig-port-dst port Destination port from original direction if elem, found := f.portFilter[ConntrackOrigDstPort]; match && found { match = match && elem == flow.Forward.DstPort } } return match } var _ CustomConntrackFilter = (*ConntrackFilter)(nil) ================================================ FILE: vendor/github.com/vishvananda/netlink/conntrack_unspecified.go ================================================ // +build !linux package netlink // ConntrackTableType Conntrack table for the netlink operation type ConntrackTableType uint8 // InetFamily Family type type InetFamily uint8 // ConntrackFlow placeholder type ConntrackFlow struct{} // ConntrackFilter placeholder type ConntrackFilter struct{} // ConntrackTableList returns the flow list of a table of a specific family // conntrack -L [table] [options] List conntrack or expectation table func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { return nil, ErrNotImplemented } // ConntrackTableFlush flushes all the flows of a specified table // conntrack -F [table] Flush table // The flush operation applies to all the family types func ConntrackTableFlush(table ConntrackTableType) error { return ErrNotImplemented } // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter // conntrack -D [table] parameters Delete conntrack or expectation func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { return 0, ErrNotImplemented } // ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed // conntrack -L [table] [options] List conntrack or expectation table func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { return nil, ErrNotImplemented } // ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed // conntrack -F [table] Flush table // The flush operation applies to all the family types func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error { return ErrNotImplemented } // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed // conntrack -D [table] parameters Delete conntrack or expectation func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { return 0, ErrNotImplemented } ================================================ FILE: vendor/github.com/vishvananda/netlink/devlink_linux.go ================================================ package netlink import ( "fmt" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" "net" "syscall" ) // DevlinkDevEswitchAttr represents device's eswitch attributes type DevlinkDevEswitchAttr struct { Mode string InlineMode string EncapMode string } // DevlinkDevAttrs represents device attributes type DevlinkDevAttrs struct { Eswitch DevlinkDevEswitchAttr } // DevlinkDevice represents device and its attributes type DevlinkDevice struct { BusName string DeviceName string Attrs DevlinkDevAttrs } // DevlinkPortFn represents port function and its attributes type DevlinkPortFn struct { HwAddr net.HardwareAddr State uint8 OpState uint8 } // DevlinkPortFnSetAttrs represents attributes to set type DevlinkPortFnSetAttrs struct { FnAttrs DevlinkPortFn HwAddrValid bool StateValid bool } // DevlinkPort represents port and its attributes type DevlinkPort struct { BusName string DeviceName string PortIndex uint32 PortType uint16 NetdeviceName string NetdevIfIndex uint32 RdmaDeviceName string PortFlavour uint16 Fn *DevlinkPortFn } type DevLinkPortAddAttrs struct { Controller uint32 SfNumber uint32 PortIndex uint32 PfNumber uint16 SfNumberValid bool PortIndexValid bool ControllerValid bool } func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { devices := make([]*DevlinkDevice, 0, len(msgs)) for _, m := range msgs { attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) if err != nil { return nil, err } dev := &DevlinkDevice{} if err = dev.parseAttributes(attrs); err != nil { return nil, err } devices = append(devices, dev) } return devices, nil } func eswitchStringToMode(modeName string) (uint16, error) { if modeName == "legacy" { return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil } else if modeName == "switchdev" { return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil } else { return 0xffff, fmt.Errorf("invalid switchdev mode") } } func parseEswitchMode(mode uint16) string { var eswitchMode = map[uint16]string{ nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy", nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev", } if eswitchMode[mode] == "" { return "unknown" } else { return eswitchMode[mode] } } func parseEswitchInlineMode(inlinemode uint8) string { var eswitchInlineMode = map[uint8]string{ nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none", nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link", nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network", nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport", } if eswitchInlineMode[inlinemode] == "" { return "unknown" } else { return eswitchInlineMode[inlinemode] } } func parseEswitchEncapMode(encapmode uint8) string { var eswitchEncapMode = map[uint8]string{ nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable", nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable", } if eswitchEncapMode[encapmode] == "" { return "unknown" } else { return eswitchEncapMode[encapmode] } } func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { for _, a := range attrs { switch a.Attr.Type { case nl.DEVLINK_ATTR_BUS_NAME: d.BusName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_DEV_NAME: d.DeviceName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_ESWITCH_MODE: d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0])) case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE: d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0])) } } return nil } func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) { m := msgs[0] attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) if err != nil { return } dev.parseAttributes(attrs) } func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) { msg := &nl.Genlmsg{ Command: nl.DEVLINK_CMD_ESWITCH_GET, Version: nl.GENL_DEVLINK_VERSION, } req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) req.AddData(msg) b := make([]byte, len(dev.BusName)) copy(b, dev.BusName) data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) req.AddData(data) b = make([]byte, len(dev.DeviceName)) copy(b, dev.DeviceName) data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) req.AddData(data) msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return } dev.parseEswitchAttrs(msgs) } // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, // otherwise returns an error code. func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) { f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) if err != nil { return nil, err } msg := &nl.Genlmsg{ Command: nl.DEVLINK_CMD_GET, Version: nl.GENL_DEVLINK_VERSION, } req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return nil, err } devices, err := parseDevLinkDeviceList(msgs) if err != nil { return nil, err } for _, d := range devices { h.getEswitchAttrs(f, d) } return devices, nil } // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, // otherwise returns an error code. func DevLinkGetDeviceList() ([]*DevlinkDevice, error) { return pkgHandle.DevLinkGetDeviceList() } func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) { m := msgs[0] attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) if err != nil { return nil, err } dev := &DevlinkDevice{} if err = dev.parseAttributes(attrs); err != nil { return nil, err } return dev, nil } func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) { f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) if err != nil { return nil, nil, err } msg := &nl.Genlmsg{ Command: cmd, Version: nl.GENL_DEVLINK_VERSION, } req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) req.AddData(msg) b := make([]byte, len(bus)+1) copy(b, bus) data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) req.AddData(data) b = make([]byte, len(device)+1) copy(b, device) data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) req.AddData(data) return f, req, nil } // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, // otherwise returns an error code. func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device) if err != nil { return nil, err } respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return nil, err } dev, err := parseDevlinkDevice(respmsg) if err == nil { h.getEswitchAttrs(f, dev) } return dev, err } // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, // otherwise returns an error code. func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { return pkgHandle.DevLinkGetDeviceByName(Bus, Device) } // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or // returns an error code. // Equivalent to: `devlink dev eswitch set $dev mode switchdev` // Equivalent to: `devlink dev eswitch set $dev mode legacy` func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { mode, err := eswitchStringToMode(NewMode) if err != nil { return err } _, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName) if err != nil { return err } req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode))) _, err = req.Execute(unix.NETLINK_GENERIC, 0) return err } // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or // returns an error code. // Equivalent to: `devlink dev eswitch set $dev mode switchdev` // Equivalent to: `devlink dev eswitch set $dev mode legacy` func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode) } func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { for _, a := range attrs { switch a.Attr.Type { case nl.DEVLINK_ATTR_BUS_NAME: port.BusName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_DEV_NAME: port.DeviceName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_PORT_INDEX: port.PortIndex = native.Uint32(a.Value) case nl.DEVLINK_ATTR_PORT_TYPE: port.PortType = native.Uint16(a.Value) case nl.DEVLINK_ATTR_PORT_NETDEV_NAME: port.NetdeviceName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX: port.NetdevIfIndex = native.Uint32(a.Value) case nl.DEVLINK_ATTR_PORT_IBDEV_NAME: port.RdmaDeviceName = string(a.Value[:len(a.Value)-1]) case nl.DEVLINK_ATTR_PORT_FLAVOUR: port.PortFlavour = native.Uint16(a.Value) case nl.DEVLINK_ATTR_PORT_FUNCTION: port.Fn = &DevlinkPortFn{} for nested := range nl.ParseAttributes(a.Value) { switch nested.Type { case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR: port.Fn.HwAddr = nested.Value[:] case nl.DEVLINK_PORT_FN_ATTR_STATE: port.Fn.State = uint8(nested.Value[0]) case nl.DEVLINK_PORT_FN_ATTR_OPSTATE: port.Fn.OpState = uint8(nested.Value[0]) } } } } return nil } func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) { ports := make([]*DevlinkPort, 0, len(msgs)) for _, m := range msgs { attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) if err != nil { return nil, err } port := &DevlinkPort{} if err = port.parseAttributes(attrs); err != nil { return nil, err } ports = append(ports, port) } return ports, nil } // DevLinkGetPortList provides a pointer to devlink ports and nil error, // otherwise returns an error code. func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) { f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) if err != nil { return nil, err } msg := &nl.Genlmsg{ Command: nl.DEVLINK_CMD_PORT_GET, Version: nl.GENL_DEVLINK_VERSION, } req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return nil, err } ports, err := parseDevLinkAllPortList(msgs) if err != nil { return nil, err } return ports, nil } // DevLinkGetPortList provides a pointer to devlink ports and nil error, // otherwise returns an error code. func DevLinkGetAllPortList() ([]*DevlinkPort, error) { return pkgHandle.DevLinkGetAllPortList() } func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) { m := msgs[0] attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) if err != nil { return nil, err } port := &DevlinkPort{} if err = port.parseAttributes(attrs); err != nil { return nil, err } return port, nil } // DevLinkGetPortByIndexprovides a pointer to devlink device and nil error, // otherwise returns an error code. func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device) if err != nil { return nil, err } req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return nil, err } port, err := parseDevlinkPortMsg(respmsg) return port, err } // DevLinkGetPortByIndex provides a pointer to devlink portand nil error, // otherwise returns an error code. func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex) } // DevLinkPortAdd adds a devlink port and returns a port on success // otherwise returns nil port and an error code. func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device) if err != nil { return nil, err } req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour))) req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber))) if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid { req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber))) } if Attrs.PortIndexValid { req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex))) } if Attrs.ControllerValid { req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller))) } respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) if err != nil { return nil, err } port, err := parseDevlinkPortMsg(respmsg) return port, err } // DevLinkPortAdd adds a devlink port and returns a port on success // otherwise returns nil port and an error code. func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs) } // DevLinkPortDel deletes a devlink port and returns success or error code. func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device) if err != nil { return err } req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) _, err = req.Execute(unix.NETLINK_GENERIC, 0) return err } // DevLinkPortDel deletes a devlink port and returns success or error code. func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex) } // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. // It returns 0 on success or error code. func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device) if err != nil { return err } req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil) if FnAttrs.HwAddrValid == true { fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr)) } if FnAttrs.StateValid == true { fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State)) } req.AddData(fnAttr) _, err = req.Execute(unix.NETLINK_GENERIC, 0) return err } // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. // It returns 0 on success or error code. func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs) } ================================================ FILE: vendor/github.com/vishvananda/netlink/filter.go ================================================ package netlink import ( "fmt" "net" ) type Filter interface { Attrs() *FilterAttrs Type() string } // FilterAttrs represents a netlink filter. A filter is associated with a link, // has a handle and a parent. The root filter of a device should have a // parent == HANDLE_ROOT. type FilterAttrs struct { LinkIndex int Handle uint32 Parent uint32 Priority uint16 // lower is higher priority Protocol uint16 // unix.ETH_P_* } func (q FilterAttrs) String() string { return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol) } type TcAct int32 const ( TC_ACT_UNSPEC TcAct = -1 TC_ACT_OK TcAct = 0 TC_ACT_RECLASSIFY TcAct = 1 TC_ACT_SHOT TcAct = 2 TC_ACT_PIPE TcAct = 3 TC_ACT_STOLEN TcAct = 4 TC_ACT_QUEUED TcAct = 5 TC_ACT_REPEAT TcAct = 6 TC_ACT_REDIRECT TcAct = 7 TC_ACT_JUMP TcAct = 0x10000000 ) func (a TcAct) String() string { switch a { case TC_ACT_UNSPEC: return "unspec" case TC_ACT_OK: return "ok" case TC_ACT_RECLASSIFY: return "reclassify" case TC_ACT_SHOT: return "shot" case TC_ACT_PIPE: return "pipe" case TC_ACT_STOLEN: return "stolen" case TC_ACT_QUEUED: return "queued" case TC_ACT_REPEAT: return "repeat" case TC_ACT_REDIRECT: return "redirect" case TC_ACT_JUMP: return "jump" } return fmt.Sprintf("0x%x", int32(a)) } type TcPolAct int32 const ( TC_POLICE_UNSPEC TcPolAct = TcPolAct(TC_ACT_UNSPEC) TC_POLICE_OK TcPolAct = TcPolAct(TC_ACT_OK) TC_POLICE_RECLASSIFY TcPolAct = TcPolAct(TC_ACT_RECLASSIFY) TC_POLICE_SHOT TcPolAct = TcPolAct(TC_ACT_SHOT) TC_POLICE_PIPE TcPolAct = TcPolAct(TC_ACT_PIPE) ) func (a TcPolAct) String() string { switch a { case TC_POLICE_UNSPEC: return "unspec" case TC_POLICE_OK: return "ok" case TC_POLICE_RECLASSIFY: return "reclassify" case TC_POLICE_SHOT: return "shot" case TC_POLICE_PIPE: return "pipe" } return fmt.Sprintf("0x%x", int32(a)) } type ActionAttrs struct { Index int Capab int Action TcAct Refcnt int Bindcnt int } func (q ActionAttrs) String() string { return fmt.Sprintf("{Index: %d, Capab: %x, Action: %s, Refcnt: %d, Bindcnt: %d}", q.Index, q.Capab, q.Action.String(), q.Refcnt, q.Bindcnt) } // Action represents an action in any supported filter. type Action interface { Attrs() *ActionAttrs Type() string } type GenericAction struct { ActionAttrs } func (action *GenericAction) Type() string { return "generic" } func (action *GenericAction) Attrs() *ActionAttrs { return &action.ActionAttrs } type BpfAction struct { ActionAttrs Fd int Name string } func (action *BpfAction) Type() string { return "bpf" } func (action *BpfAction) Attrs() *ActionAttrs { return &action.ActionAttrs } type ConnmarkAction struct { ActionAttrs Zone uint16 } func (action *ConnmarkAction) Type() string { return "connmark" } func (action *ConnmarkAction) Attrs() *ActionAttrs { return &action.ActionAttrs } func NewConnmarkAction() *ConnmarkAction { return &ConnmarkAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_PIPE, }, } } type MirredAct uint8 func (a MirredAct) String() string { switch a { case TCA_EGRESS_REDIR: return "egress redir" case TCA_EGRESS_MIRROR: return "egress mirror" case TCA_INGRESS_REDIR: return "ingress redir" case TCA_INGRESS_MIRROR: return "ingress mirror" } return "unknown" } const ( TCA_EGRESS_REDIR MirredAct = 1 /* packet redirect to EGRESS*/ TCA_EGRESS_MIRROR MirredAct = 2 /* mirror packet to EGRESS */ TCA_INGRESS_REDIR MirredAct = 3 /* packet redirect to INGRESS*/ TCA_INGRESS_MIRROR MirredAct = 4 /* mirror packet to INGRESS */ ) type MirredAction struct { ActionAttrs MirredAction MirredAct Ifindex int } func (action *MirredAction) Type() string { return "mirred" } func (action *MirredAction) Attrs() *ActionAttrs { return &action.ActionAttrs } func NewMirredAction(redirIndex int) *MirredAction { return &MirredAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_STOLEN, }, MirredAction: TCA_EGRESS_REDIR, Ifindex: redirIndex, } } type TunnelKeyAct int8 const ( TCA_TUNNEL_KEY_SET TunnelKeyAct = 1 // set tunnel key TCA_TUNNEL_KEY_UNSET TunnelKeyAct = 2 // unset tunnel key ) type TunnelKeyAction struct { ActionAttrs Action TunnelKeyAct SrcAddr net.IP DstAddr net.IP KeyID uint32 DestPort uint16 } func (action *TunnelKeyAction) Type() string { return "tunnel_key" } func (action *TunnelKeyAction) Attrs() *ActionAttrs { return &action.ActionAttrs } func NewTunnelKeyAction() *TunnelKeyAction { return &TunnelKeyAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_PIPE, }, } } type SkbEditAction struct { ActionAttrs QueueMapping *uint16 PType *uint16 Priority *uint32 Mark *uint32 } func (action *SkbEditAction) Type() string { return "skbedit" } func (action *SkbEditAction) Attrs() *ActionAttrs { return &action.ActionAttrs } func NewSkbEditAction() *SkbEditAction { return &SkbEditAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_PIPE, }, } } // MatchAll filters match all packets type MatchAll struct { FilterAttrs ClassId uint32 Actions []Action } func (filter *MatchAll) Attrs() *FilterAttrs { return &filter.FilterAttrs } func (filter *MatchAll) Type() string { return "matchall" } type FilterFwAttrs struct { ClassId uint32 InDev string Mask uint32 Index uint32 Buffer uint32 Mtu uint32 Mpu uint16 Rate uint32 AvRate uint32 PeakRate uint32 Action TcPolAct Overhead uint16 LinkLayer int } type BpfFilter struct { FilterAttrs ClassId uint32 Fd int Name string DirectAction bool Id int Tag string } func (filter *BpfFilter) Type() string { return "bpf" } func (filter *BpfFilter) Attrs() *FilterAttrs { return &filter.FilterAttrs } // GenericFilter filters represent types that are not currently understood // by this netlink library. type GenericFilter struct { FilterAttrs FilterType string } func (filter *GenericFilter) Attrs() *FilterAttrs { return &filter.FilterAttrs } func (filter *GenericFilter) Type() string { return filter.FilterType } ================================================ FILE: vendor/github.com/vishvananda/netlink/filter_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "encoding/hex" "errors" "fmt" "syscall" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // Constants used in TcU32Sel.Flags. const ( TC_U32_TERMINAL = nl.TC_U32_TERMINAL TC_U32_OFFSET = nl.TC_U32_OFFSET TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET TC_U32_EAT = nl.TC_U32_EAT ) // Sel of the U32 filters that contains multiple TcU32Key. This is the type // alias and the frontend representation of nl.TcU32Sel. It is serialized into // canonical nl.TcU32Sel with the appropriate endianness. type TcU32Sel = nl.TcU32Sel // TcU32Key contained of Sel in the U32 filters. This is the type alias and the // frontend representation of nl.TcU32Key. It is serialized into chanonical // nl.TcU32Sel with the appropriate endianness. type TcU32Key = nl.TcU32Key // U32 filters on many packet related properties type U32 struct { FilterAttrs ClassId uint32 Divisor uint32 // Divisor MUST be power of 2. Hash uint32 Link uint32 RedirIndex int Sel *TcU32Sel Actions []Action } func (filter *U32) Attrs() *FilterAttrs { return &filter.FilterAttrs } func (filter *U32) Type() string { return "u32" } // Fw filter filters on firewall marks // NOTE: this is in filter_linux because it refers to nl.TcPolice which // is defined in nl/tc_linux.go type Fw struct { FilterAttrs ClassId uint32 // TODO remove nl type from interface Police nl.TcPolice InDev string // TODO Action Mask uint32 AvRate uint32 Rtab [256]uint32 Ptab [256]uint32 } func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { var rtab [256]uint32 var ptab [256]uint32 rcellLog := -1 pcellLog := -1 avrate := fattrs.AvRate / 8 police := nl.TcPolice{} police.Rate.Rate = fattrs.Rate / 8 police.PeakRate.Rate = fattrs.PeakRate / 8 buffer := fattrs.Buffer linklayer := nl.LINKLAYER_ETHERNET if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC { linklayer = fattrs.LinkLayer } police.Action = int32(fattrs.Action) if police.Rate.Rate != 0 { police.Rate.Mpu = fattrs.Mpu police.Rate.Overhead = fattrs.Overhead if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 { return nil, errors.New("TBF: failed to calculate rate table") } police.Burst = Xmittime(uint64(police.Rate.Rate), uint32(buffer)) } police.Mtu = fattrs.Mtu if police.PeakRate.Rate != 0 { police.PeakRate.Mpu = fattrs.Mpu police.PeakRate.Overhead = fattrs.Overhead if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 { return nil, errors.New("POLICE: failed to calculate peak rate table") } } return &Fw{ FilterAttrs: attrs, ClassId: fattrs.ClassId, InDev: fattrs.InDev, Mask: fattrs.Mask, Police: police, AvRate: avrate, Rtab: rtab, Ptab: ptab, }, nil } func (filter *Fw) Attrs() *FilterAttrs { return &filter.FilterAttrs } func (filter *Fw) Type() string { return "fw" } // FilterDel will delete a filter from the system. // Equivalent to: `tc filter del $filter` func FilterDel(filter Filter) error { return pkgHandle.FilterDel(filter) } // FilterDel will delete a filter from the system. // Equivalent to: `tc filter del $filter` func (h *Handle) FilterDel(filter Filter) error { req := h.newNetlinkRequest(unix.RTM_DELTFILTER, unix.NLM_F_ACK) base := filter.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: int32(base.LinkIndex), Handle: base.Handle, Parent: base.Parent, Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)), } req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // FilterAdd will add a filter to the system. // Equivalent to: `tc filter add $filter` func FilterAdd(filter Filter) error { return pkgHandle.FilterAdd(filter) } // FilterAdd will add a filter to the system. // Equivalent to: `tc filter add $filter` func (h *Handle) FilterAdd(filter Filter) error { return h.filterModify(filter, unix.NLM_F_CREATE|unix.NLM_F_EXCL) } // FilterReplace will replace a filter. // Equivalent to: `tc filter replace $filter` func FilterReplace(filter Filter) error { return pkgHandle.FilterReplace(filter) } // FilterReplace will replace a filter. // Equivalent to: `tc filter replace $filter` func (h *Handle) FilterReplace(filter Filter) error { return h.filterModify(filter, unix.NLM_F_CREATE) } func (h *Handle) filterModify(filter Filter, flags int) error { req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK) base := filter.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: int32(base.LinkIndex), Handle: base.Handle, Parent: base.Parent, Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)), } req.AddData(msg) req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type()))) options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) switch filter := filter.(type) { case *U32: sel := filter.Sel if sel == nil { // match all sel = &nl.TcU32Sel{ Nkeys: 1, Flags: nl.TC_U32_TERMINAL, } sel.Keys = append(sel.Keys, nl.TcU32Key{}) } if native != networkOrder { // Copy TcU32Sel. cSel := *sel keys := make([]nl.TcU32Key, cap(sel.Keys)) copy(keys, sel.Keys) cSel.Keys = keys sel = &cSel // Handle the endianness of attributes sel.Offmask = native.Uint16(htons(sel.Offmask)) sel.Hmask = native.Uint32(htonl(sel.Hmask)) for i, key := range sel.Keys { sel.Keys[i].Mask = native.Uint32(htonl(key.Mask)) sel.Keys[i].Val = native.Uint32(htonl(key.Val)) } } sel.Nkeys = uint8(len(sel.Keys)) options.AddRtAttr(nl.TCA_U32_SEL, sel.Serialize()) if filter.ClassId != 0 { options.AddRtAttr(nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId)) } if filter.Divisor != 0 { if (filter.Divisor-1)&filter.Divisor != 0 { return fmt.Errorf("illegal divisor %d. Must be a power of 2", filter.Divisor) } options.AddRtAttr(nl.TCA_U32_DIVISOR, nl.Uint32Attr(filter.Divisor)) } if filter.Hash != 0 { options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash)) } if filter.Link != 0 { options.AddRtAttr(nl.TCA_U32_LINK, nl.Uint32Attr(filter.Link)) } actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil) // backwards compatibility if filter.RedirIndex != 0 { filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...) } if err := EncodeActions(actionsAttr, filter.Actions); err != nil { return err } case *Fw: if filter.Mask != 0 { b := make([]byte, 4) native.PutUint32(b, filter.Mask) options.AddRtAttr(nl.TCA_FW_MASK, b) } if filter.InDev != "" { options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev)) } if (filter.Police != nl.TcPolice{}) { police := options.AddRtAttr(nl.TCA_FW_POLICE, nil) police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize()) if (filter.Police.Rate != nl.TcRateSpec{}) { payload := SerializeRtab(filter.Rtab) police.AddRtAttr(nl.TCA_POLICE_RATE, payload) } if (filter.Police.PeakRate != nl.TcRateSpec{}) { payload := SerializeRtab(filter.Ptab) police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload) } } if filter.ClassId != 0 { b := make([]byte, 4) native.PutUint32(b, filter.ClassId) options.AddRtAttr(nl.TCA_FW_CLASSID, b) } case *BpfFilter: var bpfFlags uint32 if filter.ClassId != 0 { options.AddRtAttr(nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId)) } if filter.Fd >= 0 { options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd)))) } if filter.Name != "" { options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name)) } if filter.DirectAction { bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT } options.AddRtAttr(nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags)) case *MatchAll: actionsAttr := options.AddRtAttr(nl.TCA_MATCHALL_ACT, nil) if err := EncodeActions(actionsAttr, filter.Actions); err != nil { return err } if filter.ClassId != 0 { options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId)) } } req.AddData(options) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // FilterList gets a list of filters in the system. // Equivalent to: `tc filter show`. // Generally returns nothing if link and parent are not specified. func FilterList(link Link, parent uint32) ([]Filter, error) { return pkgHandle.FilterList(link, parent) } // FilterList gets a list of filters in the system. // Equivalent to: `tc filter show`. // Generally returns nothing if link and parent are not specified. func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) { req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() h.ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER) if err != nil { return nil, err } var res []Filter for _, m := range msgs { msg := nl.DeserializeTcMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := FilterAttrs{ LinkIndex: int(msg.Ifindex), Handle: msg.Handle, Parent: msg.Parent, } base.Priority, base.Protocol = MajorMinor(msg.Info) base.Protocol = nl.Swap16(base.Protocol) var filter Filter filterType := "" detailed := false for _, attr := range attrs { switch attr.Attr.Type { case nl.TCA_KIND: filterType = string(attr.Value[:len(attr.Value)-1]) switch filterType { case "u32": filter = &U32{} case "fw": filter = &Fw{} case "bpf": filter = &BpfFilter{} case "matchall": filter = &MatchAll{} default: filter = &GenericFilter{FilterType: filterType} } case nl.TCA_OPTIONS: data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } switch filterType { case "u32": detailed, err = parseU32Data(filter, data) if err != nil { return nil, err } case "fw": detailed, err = parseFwData(filter, data) if err != nil { return nil, err } case "bpf": detailed, err = parseBpfData(filter, data) if err != nil { return nil, err } case "matchall": detailed, err = parseMatchAllData(filter, data) if err != nil { return nil, err } default: detailed = true } } } // only return the detailed version of the filter if detailed { *filter.Attrs() = base res = append(res, filter) } } return res, nil } func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) { tcgen.Index = uint32(attrs.Index) tcgen.Capab = uint32(attrs.Capab) tcgen.Action = int32(attrs.Action) tcgen.Refcnt = int32(attrs.Refcnt) tcgen.Bindcnt = int32(attrs.Bindcnt) } func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) { attrs.Index = int(tcgen.Index) attrs.Capab = int(tcgen.Capab) attrs.Action = TcAct(tcgen.Action) attrs.Refcnt = int(tcgen.Refcnt) attrs.Bindcnt = int(tcgen.Bindcnt) } func EncodeActions(attr *nl.RtAttr, actions []Action) error { tabIndex := int(nl.TCA_ACT_TAB) for _, action := range actions { switch action := action.(type) { default: return fmt.Errorf("unknown action type %s", action.Type()) case *MirredAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) mirred := nl.TcMirred{ Eaction: int32(action.MirredAction), Ifindex: uint32(action.Ifindex), } toTcGen(action.Attrs(), &mirred.TcGen) aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize()) case *TunnelKeyAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("tunnel_key")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) tun := nl.TcTunnelKey{ Action: int32(action.Action), } toTcGen(action.Attrs(), &tun.TcGen) aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_PARMS, tun.Serialize()) if action.Action == TCA_TUNNEL_KEY_SET { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_KEY_ID, htonl(action.KeyID)) if v4 := action.SrcAddr.To4(); v4 != nil { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC, v4[:]) } else if v6 := action.SrcAddr.To16(); v6 != nil { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, v6[:]) } else { return fmt.Errorf("invalid src addr %s for tunnel_key action", action.SrcAddr) } if v4 := action.DstAddr.To4(); v4 != nil { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_DST, v4[:]) } else if v6 := action.DstAddr.To16(); v6 != nil { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, v6[:]) } else { return fmt.Errorf("invalid dst addr %s for tunnel_key action", action.DstAddr) } if action.DestPort != 0 { aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_DST_PORT, htons(action.DestPort)) } } case *SkbEditAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("skbedit")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) skbedit := nl.TcSkbEdit{} toTcGen(action.Attrs(), &skbedit.TcGen) aopts.AddRtAttr(nl.TCA_SKBEDIT_PARMS, skbedit.Serialize()) if action.QueueMapping != nil { aopts.AddRtAttr(nl.TCA_SKBEDIT_QUEUE_MAPPING, nl.Uint16Attr(*action.QueueMapping)) } if action.Priority != nil { aopts.AddRtAttr(nl.TCA_SKBEDIT_PRIORITY, nl.Uint32Attr(*action.Priority)) } if action.PType != nil { aopts.AddRtAttr(nl.TCA_SKBEDIT_PTYPE, nl.Uint16Attr(*action.PType)) } if action.Mark != nil { aopts.AddRtAttr(nl.TCA_SKBEDIT_MARK, nl.Uint32Attr(*action.Mark)) } case *ConnmarkAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("connmark")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) connmark := nl.TcConnmark{ Zone: action.Zone, } toTcGen(action.Attrs(), &connmark.TcGen) aopts.AddRtAttr(nl.TCA_CONNMARK_PARMS, connmark.Serialize()) case *BpfAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) gen := nl.TcGen{} toTcGen(action.Attrs(), &gen) aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize()) aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd))) aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name)) case *GenericAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("gact")) aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) gen := nl.TcGen{} toTcGen(action.Attrs(), &gen) aopts.AddRtAttr(nl.TCA_GACT_PARMS, gen.Serialize()) } } return nil } func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { var actions []Action for _, table := range tables { var action Action var actionType string aattrs, err := nl.ParseRouteAttr(table.Value) if err != nil { return nil, err } nextattr: for _, aattr := range aattrs { switch aattr.Attr.Type { case nl.TCA_KIND: actionType = string(aattr.Value[:len(aattr.Value)-1]) // only parse if the action is mirred or bpf switch actionType { case "mirred": action = &MirredAction{} case "bpf": action = &BpfAction{} case "connmark": action = &ConnmarkAction{} case "gact": action = &GenericAction{} case "tunnel_key": action = &TunnelKeyAction{} case "skbedit": action = &SkbEditAction{} default: break nextattr } case nl.TCA_OPTIONS: adata, err := nl.ParseRouteAttr(aattr.Value) if err != nil { return nil, err } for _, adatum := range adata { switch actionType { case "mirred": switch adatum.Attr.Type { case nl.TCA_MIRRED_PARMS: mirred := *nl.DeserializeTcMirred(adatum.Value) action.(*MirredAction).ActionAttrs = ActionAttrs{} toAttrs(&mirred.TcGen, action.Attrs()) action.(*MirredAction).Ifindex = int(mirred.Ifindex) action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) } case "tunnel_key": switch adatum.Attr.Type { case nl.TCA_TUNNEL_KEY_PARMS: tun := *nl.DeserializeTunnelKey(adatum.Value) action.(*TunnelKeyAction).ActionAttrs = ActionAttrs{} toAttrs(&tun.TcGen, action.Attrs()) action.(*TunnelKeyAction).Action = TunnelKeyAct(tun.Action) case nl.TCA_TUNNEL_KEY_ENC_KEY_ID: action.(*TunnelKeyAction).KeyID = networkOrder.Uint32(adatum.Value[0:4]) case nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC: action.(*TunnelKeyAction).SrcAddr = adatum.Value[:] case nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, nl.TCA_TUNNEL_KEY_ENC_IPV4_DST: action.(*TunnelKeyAction).DstAddr = adatum.Value[:] case nl.TCA_TUNNEL_KEY_ENC_DST_PORT: action.(*TunnelKeyAction).DestPort = ntohs(adatum.Value) } case "skbedit": switch adatum.Attr.Type { case nl.TCA_SKBEDIT_PARMS: skbedit := *nl.DeserializeSkbEdit(adatum.Value) action.(*SkbEditAction).ActionAttrs = ActionAttrs{} toAttrs(&skbedit.TcGen, action.Attrs()) case nl.TCA_SKBEDIT_MARK: mark := native.Uint32(adatum.Value[0:4]) action.(*SkbEditAction).Mark = &mark case nl.TCA_SKBEDIT_PRIORITY: priority := native.Uint32(adatum.Value[0:4]) action.(*SkbEditAction).Priority = &priority case nl.TCA_SKBEDIT_PTYPE: ptype := native.Uint16(adatum.Value[0:2]) action.(*SkbEditAction).PType = &ptype case nl.TCA_SKBEDIT_QUEUE_MAPPING: mapping := native.Uint16(adatum.Value[0:2]) action.(*SkbEditAction).QueueMapping = &mapping } case "bpf": switch adatum.Attr.Type { case nl.TCA_ACT_BPF_PARMS: gen := *nl.DeserializeTcGen(adatum.Value) toAttrs(&gen, action.Attrs()) case nl.TCA_ACT_BPF_FD: action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) case nl.TCA_ACT_BPF_NAME: action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) } case "connmark": switch adatum.Attr.Type { case nl.TCA_CONNMARK_PARMS: connmark := *nl.DeserializeTcConnmark(adatum.Value) action.(*ConnmarkAction).ActionAttrs = ActionAttrs{} toAttrs(&connmark.TcGen, action.Attrs()) action.(*ConnmarkAction).Zone = connmark.Zone } case "gact": switch adatum.Attr.Type { case nl.TCA_GACT_PARMS: gen := *nl.DeserializeTcGen(adatum.Value) toAttrs(&gen, action.Attrs()) } } } } } actions = append(actions, action) } return actions, nil } func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { u32 := filter.(*U32) detailed := false for _, datum := range data { switch datum.Attr.Type { case nl.TCA_U32_SEL: detailed = true sel := nl.DeserializeTcU32Sel(datum.Value) u32.Sel = sel if native != networkOrder { // Handle the endianness of attributes u32.Sel.Offmask = native.Uint16(htons(sel.Offmask)) u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask)) for i, key := range u32.Sel.Keys { u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask)) u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val)) } } case nl.TCA_U32_ACT: tables, err := nl.ParseRouteAttr(datum.Value) if err != nil { return detailed, err } u32.Actions, err = parseActions(tables) if err != nil { return detailed, err } for _, action := range u32.Actions { if action, ok := action.(*MirredAction); ok { u32.RedirIndex = int(action.Ifindex) } } case nl.TCA_U32_CLASSID: u32.ClassId = native.Uint32(datum.Value) case nl.TCA_U32_DIVISOR: u32.Divisor = native.Uint32(datum.Value) case nl.TCA_U32_HASH: u32.Hash = native.Uint32(datum.Value) case nl.TCA_U32_LINK: u32.Link = native.Uint32(datum.Value) } } return detailed, nil } func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { fw := filter.(*Fw) detailed := true for _, datum := range data { switch datum.Attr.Type { case nl.TCA_FW_MASK: fw.Mask = native.Uint32(datum.Value[0:4]) case nl.TCA_FW_CLASSID: fw.ClassId = native.Uint32(datum.Value[0:4]) case nl.TCA_FW_INDEV: fw.InDev = string(datum.Value[:len(datum.Value)-1]) case nl.TCA_FW_POLICE: adata, _ := nl.ParseRouteAttr(datum.Value) for _, aattr := range adata { switch aattr.Attr.Type { case nl.TCA_POLICE_TBF: fw.Police = *nl.DeserializeTcPolice(aattr.Value) case nl.TCA_POLICE_RATE: fw.Rtab = DeserializeRtab(aattr.Value) case nl.TCA_POLICE_PEAKRATE: fw.Ptab = DeserializeRtab(aattr.Value) } } } } return detailed, nil } func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { bpf := filter.(*BpfFilter) detailed := true for _, datum := range data { switch datum.Attr.Type { case nl.TCA_BPF_FD: bpf.Fd = int(native.Uint32(datum.Value[0:4])) case nl.TCA_BPF_NAME: bpf.Name = string(datum.Value[:len(datum.Value)-1]) case nl.TCA_BPF_CLASSID: bpf.ClassId = native.Uint32(datum.Value[0:4]) case nl.TCA_BPF_FLAGS: flags := native.Uint32(datum.Value[0:4]) if (flags & nl.TCA_BPF_FLAG_ACT_DIRECT) != 0 { bpf.DirectAction = true } case nl.TCA_BPF_ID: bpf.Id = int(native.Uint32(datum.Value[0:4])) case nl.TCA_BPF_TAG: bpf.Tag = hex.EncodeToString(datum.Value[:len(datum.Value)-1]) } } return detailed, nil } func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { matchall := filter.(*MatchAll) detailed := true for _, datum := range data { switch datum.Attr.Type { case nl.TCA_MATCHALL_CLASSID: matchall.ClassId = native.Uint32(datum.Value[0:4]) case nl.TCA_MATCHALL_ACT: tables, err := nl.ParseRouteAttr(datum.Value) if err != nil { return detailed, err } matchall.Actions, err = parseActions(tables) if err != nil { return detailed, err } } } return detailed, nil } func AlignToAtm(size uint) uint { var linksize, cells int cells = int(size / nl.ATM_CELL_PAYLOAD) if (size % nl.ATM_CELL_PAYLOAD) > 0 { cells++ } linksize = cells * nl.ATM_CELL_SIZE return uint(linksize) } func AdjustSize(sz uint, mpu uint, linklayer int) uint { if sz < mpu { sz = mpu } switch linklayer { case nl.LINKLAYER_ATM: return AlignToAtm(sz) default: return sz } } func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int { bps := rate.Rate mpu := rate.Mpu var sz uint if mtu == 0 { mtu = 2047 } if cellLog < 0 { cellLog = 0 for (mtu >> uint(cellLog)) > 255 { cellLog++ } } for i := 0; i < 256; i++ { sz = AdjustSize(uint((i+1)<= nl.IPSET_ERR_PRIVATE { err = nl.IPSetError(uintptr(errno)) } } return } func ipsetUnserialize(msgs [][]byte) (result IPSetResult) { for _, msg := range msgs { result.unserialize(msg) } return result } func (result *IPSetResult) unserialize(msg []byte) { result.Nfgenmsg = nl.DeserializeNfgenmsg(msg) for attr := range nl.ParseAttributes(msg[4:]) { switch attr.Type { case nl.IPSET_ATTR_PROTOCOL: result.Protocol = attr.Value[0] case nl.IPSET_ATTR_SETNAME: result.SetName = nl.BytesToString(attr.Value) case nl.IPSET_ATTR_COMMENT: result.Comment = nl.BytesToString(attr.Value) case nl.IPSET_ATTR_TYPENAME: result.TypeName = nl.BytesToString(attr.Value) case nl.IPSET_ATTR_REVISION: result.Revision = attr.Value[0] case nl.IPSET_ATTR_FAMILY: result.Family = attr.Value[0] case nl.IPSET_ATTR_FLAGS: result.Flags = attr.Value[0] case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED: result.parseAttrData(attr.Value) case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED: result.parseAttrADT(attr.Value) case nl.IPSET_ATTR_PROTOCOL_MIN: result.ProtocolMinVersion = attr.Value[0] default: log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) } } } func (result *IPSetResult) parseAttrData(data []byte) { for attr := range nl.ParseAttributes(data) { switch attr.Type { case nl.IPSET_ATTR_HASHSIZE | nl.NLA_F_NET_BYTEORDER: result.HashSize = attr.Uint32() case nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER: result.MaxElements = attr.Uint32() case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER: val := attr.Uint32() result.Timeout = &val case nl.IPSET_ATTR_ELEMENTS | nl.NLA_F_NET_BYTEORDER: result.NumEntries = attr.Uint32() case nl.IPSET_ATTR_REFERENCES | nl.NLA_F_NET_BYTEORDER: result.References = attr.Uint32() case nl.IPSET_ATTR_MEMSIZE | nl.NLA_F_NET_BYTEORDER: result.SizeInMemory = attr.Uint32() case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER: result.CadtFlags = attr.Uint32() case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED: for nested := range nl.ParseAttributes(attr.Value) { switch nested.Type { case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER: result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value}) } } case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER: result.LineNo = attr.Uint32() case nl.IPSET_ATTR_COMMENT: result.Comment = nl.BytesToString(attr.Value) default: log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) } } } func (result *IPSetResult) parseAttrADT(data []byte) { for attr := range nl.ParseAttributes(data) { switch attr.Type { case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED: result.Entries = append(result.Entries, parseIPSetEntry(attr.Value)) default: log.Printf("unknown ADT attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) } } } func parseIPSetEntry(data []byte) (entry IPSetEntry) { for attr := range nl.ParseAttributes(data) { switch attr.Type { case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER: val := attr.Uint32() entry.Timeout = &val case nl.IPSET_ATTR_BYTES | nl.NLA_F_NET_BYTEORDER: val := attr.Uint64() entry.Bytes = &val case nl.IPSET_ATTR_PACKETS | nl.NLA_F_NET_BYTEORDER: val := attr.Uint64() entry.Packets = &val case nl.IPSET_ATTR_ETHER: entry.MAC = net.HardwareAddr(attr.Value) case nl.IPSET_ATTR_IP: entry.IP = net.IP(attr.Value) case nl.IPSET_ATTR_COMMENT: entry.Comment = nl.BytesToString(attr.Value) case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED: for attr := range nl.ParseAttributes(attr.Value) { switch attr.Type { case nl.IPSET_ATTR_IP: entry.IP = net.IP(attr.Value) default: log.Printf("unknown nested ADT attribute from kernel: %+v", attr) } } default: log.Printf("unknown ADT attribute from kernel: %+v", attr) } } return } ================================================ FILE: vendor/github.com/vishvananda/netlink/link.go ================================================ package netlink import ( "fmt" "net" "os" "strconv" ) // Link represents a link device from netlink. Shared link attributes // like name may be retrieved using the Attrs() method. Unique data // can be retrieved by casting the object to the proper type. type Link interface { Attrs() *LinkAttrs Type() string } type ( NsPid int NsFd int ) // LinkAttrs represents data shared by most link types type LinkAttrs struct { Index int MTU int TxQLen int // Transmit Queue Length Name string HardwareAddr net.HardwareAddr Flags net.Flags RawFlags uint32 ParentIndex int // index of the parent link device MasterIndex int // must be the index of a bridge Namespace interface{} // nil | NsPid | NsFd Alias string Statistics *LinkStatistics Promisc int Allmulti int Multi int Xdp *LinkXdp EncapType string Protinfo *Protinfo OperState LinkOperState PhysSwitchID int NetNsID int NumTxQueues int NumRxQueues int GSOMaxSize uint32 GSOMaxSegs uint32 Vfs []VfInfo // virtual functions available on link Group uint32 Slave LinkSlave } // LinkSlave represents a slave device. type LinkSlave interface { SlaveType() string } // VfInfo represents configuration of virtual function type VfInfo struct { ID int Mac net.HardwareAddr Vlan int Qos int TxRate int // IFLA_VF_TX_RATE Max TxRate Spoofchk bool LinkState uint32 MaxTxRate uint32 // IFLA_VF_RATE Max TxRate MinTxRate uint32 // IFLA_VF_RATE Min TxRate RxPackets uint64 TxPackets uint64 RxBytes uint64 TxBytes uint64 Multicast uint64 Broadcast uint64 RxDropped uint64 TxDropped uint64 RssQuery uint32 Trust uint32 } // LinkOperState represents the values of the IFLA_OPERSTATE link // attribute, which contains the RFC2863 state of the interface. type LinkOperState uint8 const ( OperUnknown = iota // Status can't be determined. OperNotPresent // Some component is missing. OperDown // Down. OperLowerLayerDown // Down due to state of lower layer. OperTesting // In some test mode. OperDormant // Not up but pending an external event. OperUp // Up, ready to send packets. ) func (s LinkOperState) String() string { switch s { case OperNotPresent: return "not-present" case OperDown: return "down" case OperLowerLayerDown: return "lower-layer-down" case OperTesting: return "testing" case OperDormant: return "dormant" case OperUp: return "up" default: return "unknown" } } // NewLinkAttrs returns LinkAttrs structure filled with default values func NewLinkAttrs() LinkAttrs { return LinkAttrs{ NetNsID: -1, TxQLen: -1, } } type LinkStatistics LinkStatistics64 /* Ref: struct rtnl_link_stats {...} */ type LinkStatistics32 struct { RxPackets uint32 TxPackets uint32 RxBytes uint32 TxBytes uint32 RxErrors uint32 TxErrors uint32 RxDropped uint32 TxDropped uint32 Multicast uint32 Collisions uint32 RxLengthErrors uint32 RxOverErrors uint32 RxCrcErrors uint32 RxFrameErrors uint32 RxFifoErrors uint32 RxMissedErrors uint32 TxAbortedErrors uint32 TxCarrierErrors uint32 TxFifoErrors uint32 TxHeartbeatErrors uint32 TxWindowErrors uint32 RxCompressed uint32 TxCompressed uint32 } func (s32 LinkStatistics32) to64() *LinkStatistics64 { return &LinkStatistics64{ RxPackets: uint64(s32.RxPackets), TxPackets: uint64(s32.TxPackets), RxBytes: uint64(s32.RxBytes), TxBytes: uint64(s32.TxBytes), RxErrors: uint64(s32.RxErrors), TxErrors: uint64(s32.TxErrors), RxDropped: uint64(s32.RxDropped), TxDropped: uint64(s32.TxDropped), Multicast: uint64(s32.Multicast), Collisions: uint64(s32.Collisions), RxLengthErrors: uint64(s32.RxLengthErrors), RxOverErrors: uint64(s32.RxOverErrors), RxCrcErrors: uint64(s32.RxCrcErrors), RxFrameErrors: uint64(s32.RxFrameErrors), RxFifoErrors: uint64(s32.RxFifoErrors), RxMissedErrors: uint64(s32.RxMissedErrors), TxAbortedErrors: uint64(s32.TxAbortedErrors), TxCarrierErrors: uint64(s32.TxCarrierErrors), TxFifoErrors: uint64(s32.TxFifoErrors), TxHeartbeatErrors: uint64(s32.TxHeartbeatErrors), TxWindowErrors: uint64(s32.TxWindowErrors), RxCompressed: uint64(s32.RxCompressed), TxCompressed: uint64(s32.TxCompressed), } } /* Ref: struct rtnl_link_stats64 {...} */ type LinkStatistics64 struct { RxPackets uint64 TxPackets uint64 RxBytes uint64 TxBytes uint64 RxErrors uint64 TxErrors uint64 RxDropped uint64 TxDropped uint64 Multicast uint64 Collisions uint64 RxLengthErrors uint64 RxOverErrors uint64 RxCrcErrors uint64 RxFrameErrors uint64 RxFifoErrors uint64 RxMissedErrors uint64 TxAbortedErrors uint64 TxCarrierErrors uint64 TxFifoErrors uint64 TxHeartbeatErrors uint64 TxWindowErrors uint64 RxCompressed uint64 TxCompressed uint64 } type LinkXdp struct { Fd int Attached bool AttachMode uint32 Flags uint32 ProgId uint32 } // Device links cannot be created via netlink. These links // are links created by udev like 'lo' and 'etho0' type Device struct { LinkAttrs } func (device *Device) Attrs() *LinkAttrs { return &device.LinkAttrs } func (device *Device) Type() string { return "device" } // Dummy links are dummy ethernet devices type Dummy struct { LinkAttrs } func (dummy *Dummy) Attrs() *LinkAttrs { return &dummy.LinkAttrs } func (dummy *Dummy) Type() string { return "dummy" } // Ifb links are advanced dummy devices for packet filtering type Ifb struct { LinkAttrs } func (ifb *Ifb) Attrs() *LinkAttrs { return &ifb.LinkAttrs } func (ifb *Ifb) Type() string { return "ifb" } // Bridge links are simple linux bridges type Bridge struct { LinkAttrs MulticastSnooping *bool AgeingTime *uint32 HelloTime *uint32 VlanFiltering *bool } func (bridge *Bridge) Attrs() *LinkAttrs { return &bridge.LinkAttrs } func (bridge *Bridge) Type() string { return "bridge" } // Vlan links have ParentIndex set in their Attrs() type Vlan struct { LinkAttrs VlanId int VlanProtocol VlanProtocol } func (vlan *Vlan) Attrs() *LinkAttrs { return &vlan.LinkAttrs } func (vlan *Vlan) Type() string { return "vlan" } type MacvlanMode uint16 const ( MACVLAN_MODE_DEFAULT MacvlanMode = iota MACVLAN_MODE_PRIVATE MACVLAN_MODE_VEPA MACVLAN_MODE_BRIDGE MACVLAN_MODE_PASSTHRU MACVLAN_MODE_SOURCE ) // Macvlan links have ParentIndex set in their Attrs() type Macvlan struct { LinkAttrs Mode MacvlanMode // MACAddrs is only populated for Macvlan SOURCE links MACAddrs []net.HardwareAddr } func (macvlan *Macvlan) Attrs() *LinkAttrs { return &macvlan.LinkAttrs } func (macvlan *Macvlan) Type() string { return "macvlan" } // Macvtap - macvtap is a virtual interfaces based on macvlan type Macvtap struct { Macvlan } func (macvtap Macvtap) Type() string { return "macvtap" } type TuntapMode uint16 type TuntapFlag uint16 // Tuntap links created via /dev/tun/tap, but can be destroyed via netlink type Tuntap struct { LinkAttrs Mode TuntapMode Flags TuntapFlag NonPersist bool Queues int Fds []*os.File Owner uint32 Group uint32 } func (tuntap *Tuntap) Attrs() *LinkAttrs { return &tuntap.LinkAttrs } func (tuntap *Tuntap) Type() string { return "tuntap" } // Veth devices must specify PeerName on create type Veth struct { LinkAttrs PeerName string // veth on create only PeerHardwareAddr net.HardwareAddr PeerNamespace interface{} } func (veth *Veth) Attrs() *LinkAttrs { return &veth.LinkAttrs } func (veth *Veth) Type() string { return "veth" } // Wireguard represent links of type "wireguard", see https://www.wireguard.com/ type Wireguard struct { LinkAttrs } func (wg *Wireguard) Attrs() *LinkAttrs { return &wg.LinkAttrs } func (wg *Wireguard) Type() string { return "wireguard" } // GenericLink links represent types that are not currently understood // by this netlink library. type GenericLink struct { LinkAttrs LinkType string } func (generic *GenericLink) Attrs() *LinkAttrs { return &generic.LinkAttrs } func (generic *GenericLink) Type() string { return generic.LinkType } type Vxlan struct { LinkAttrs VxlanId int VtepDevIndex int SrcAddr net.IP Group net.IP TTL int TOS int Learning bool Proxy bool RSC bool L2miss bool L3miss bool UDPCSum bool UDP6ZeroCSumTx bool UDP6ZeroCSumRx bool NoAge bool GBP bool FlowBased bool Age int Limit int Port int PortLow int PortHigh int } func (vxlan *Vxlan) Attrs() *LinkAttrs { return &vxlan.LinkAttrs } func (vxlan *Vxlan) Type() string { return "vxlan" } type IPVlanMode uint16 const ( IPVLAN_MODE_L2 IPVlanMode = iota IPVLAN_MODE_L3 IPVLAN_MODE_L3S IPVLAN_MODE_MAX ) type IPVlanFlag uint16 const ( IPVLAN_FLAG_BRIDGE IPVlanFlag = iota IPVLAN_FLAG_PRIVATE IPVLAN_FLAG_VEPA ) type IPVlan struct { LinkAttrs Mode IPVlanMode Flag IPVlanFlag } func (ipvlan *IPVlan) Attrs() *LinkAttrs { return &ipvlan.LinkAttrs } func (ipvlan *IPVlan) Type() string { return "ipvlan" } // IPVtap - IPVtap is a virtual interfaces based on ipvlan type IPVtap struct { IPVlan } func (ipvtap *IPVtap) Attrs() *LinkAttrs { return &ipvtap.LinkAttrs } func (ipvtap IPVtap) Type() string { return "ipvtap" } // VlanProtocol type type VlanProtocol int func (p VlanProtocol) String() string { s, ok := VlanProtocolToString[p] if !ok { return fmt.Sprintf("VlanProtocol(%d)", p) } return s } // StringToVlanProtocol returns vlan protocol, or unknown is the s is invalid. func StringToVlanProtocol(s string) VlanProtocol { mode, ok := StringToVlanProtocolMap[s] if !ok { return VLAN_PROTOCOL_UNKNOWN } return mode } // VlanProtocol possible values const ( VLAN_PROTOCOL_UNKNOWN VlanProtocol = 0 VLAN_PROTOCOL_8021Q VlanProtocol = 0x8100 VLAN_PROTOCOL_8021AD VlanProtocol = 0x88A8 ) var VlanProtocolToString = map[VlanProtocol]string{ VLAN_PROTOCOL_8021Q: "802.1q", VLAN_PROTOCOL_8021AD: "802.1ad", } var StringToVlanProtocolMap = map[string]VlanProtocol{ "802.1q": VLAN_PROTOCOL_8021Q, "802.1ad": VLAN_PROTOCOL_8021AD, } // BondMode type type BondMode int func (b BondMode) String() string { s, ok := bondModeToString[b] if !ok { return fmt.Sprintf("BondMode(%d)", b) } return s } // StringToBondMode returns bond mode, or unknown is the s is invalid. func StringToBondMode(s string) BondMode { mode, ok := StringToBondModeMap[s] if !ok { return BOND_MODE_UNKNOWN } return mode } // Possible BondMode const ( BOND_MODE_BALANCE_RR BondMode = iota BOND_MODE_ACTIVE_BACKUP BOND_MODE_BALANCE_XOR BOND_MODE_BROADCAST BOND_MODE_802_3AD BOND_MODE_BALANCE_TLB BOND_MODE_BALANCE_ALB BOND_MODE_UNKNOWN ) var bondModeToString = map[BondMode]string{ BOND_MODE_BALANCE_RR: "balance-rr", BOND_MODE_ACTIVE_BACKUP: "active-backup", BOND_MODE_BALANCE_XOR: "balance-xor", BOND_MODE_BROADCAST: "broadcast", BOND_MODE_802_3AD: "802.3ad", BOND_MODE_BALANCE_TLB: "balance-tlb", BOND_MODE_BALANCE_ALB: "balance-alb", } var StringToBondModeMap = map[string]BondMode{ "balance-rr": BOND_MODE_BALANCE_RR, "active-backup": BOND_MODE_ACTIVE_BACKUP, "balance-xor": BOND_MODE_BALANCE_XOR, "broadcast": BOND_MODE_BROADCAST, "802.3ad": BOND_MODE_802_3AD, "balance-tlb": BOND_MODE_BALANCE_TLB, "balance-alb": BOND_MODE_BALANCE_ALB, } // BondArpValidate type type BondArpValidate int // Possible BondArpValidate value const ( BOND_ARP_VALIDATE_NONE BondArpValidate = iota BOND_ARP_VALIDATE_ACTIVE BOND_ARP_VALIDATE_BACKUP BOND_ARP_VALIDATE_ALL ) var bondArpValidateToString = map[BondArpValidate]string{ BOND_ARP_VALIDATE_NONE: "none", BOND_ARP_VALIDATE_ACTIVE: "active", BOND_ARP_VALIDATE_BACKUP: "backup", BOND_ARP_VALIDATE_ALL: "none", } var StringToBondArpValidateMap = map[string]BondArpValidate{ "none": BOND_ARP_VALIDATE_NONE, "active": BOND_ARP_VALIDATE_ACTIVE, "backup": BOND_ARP_VALIDATE_BACKUP, "all": BOND_ARP_VALIDATE_ALL, } func (b BondArpValidate) String() string { s, ok := bondArpValidateToString[b] if !ok { return fmt.Sprintf("BondArpValidate(%d)", b) } return s } // BondPrimaryReselect type type BondPrimaryReselect int // Possible BondPrimaryReselect value const ( BOND_PRIMARY_RESELECT_ALWAYS BondPrimaryReselect = iota BOND_PRIMARY_RESELECT_BETTER BOND_PRIMARY_RESELECT_FAILURE ) var bondPrimaryReselectToString = map[BondPrimaryReselect]string{ BOND_PRIMARY_RESELECT_ALWAYS: "always", BOND_PRIMARY_RESELECT_BETTER: "better", BOND_PRIMARY_RESELECT_FAILURE: "failure", } var StringToBondPrimaryReselectMap = map[string]BondPrimaryReselect{ "always": BOND_PRIMARY_RESELECT_ALWAYS, "better": BOND_PRIMARY_RESELECT_BETTER, "failure": BOND_PRIMARY_RESELECT_FAILURE, } func (b BondPrimaryReselect) String() string { s, ok := bondPrimaryReselectToString[b] if !ok { return fmt.Sprintf("BondPrimaryReselect(%d)", b) } return s } // BondArpAllTargets type type BondArpAllTargets int // Possible BondArpAllTargets value const ( BOND_ARP_ALL_TARGETS_ANY BondArpAllTargets = iota BOND_ARP_ALL_TARGETS_ALL ) var bondArpAllTargetsToString = map[BondArpAllTargets]string{ BOND_ARP_ALL_TARGETS_ANY: "any", BOND_ARP_ALL_TARGETS_ALL: "all", } var StringToBondArpAllTargetsMap = map[string]BondArpAllTargets{ "any": BOND_ARP_ALL_TARGETS_ANY, "all": BOND_ARP_ALL_TARGETS_ALL, } func (b BondArpAllTargets) String() string { s, ok := bondArpAllTargetsToString[b] if !ok { return fmt.Sprintf("BondArpAllTargets(%d)", b) } return s } // BondFailOverMac type type BondFailOverMac int // Possible BondFailOverMac value const ( BOND_FAIL_OVER_MAC_NONE BondFailOverMac = iota BOND_FAIL_OVER_MAC_ACTIVE BOND_FAIL_OVER_MAC_FOLLOW ) var bondFailOverMacToString = map[BondFailOverMac]string{ BOND_FAIL_OVER_MAC_NONE: "none", BOND_FAIL_OVER_MAC_ACTIVE: "active", BOND_FAIL_OVER_MAC_FOLLOW: "follow", } var StringToBondFailOverMacMap = map[string]BondFailOverMac{ "none": BOND_FAIL_OVER_MAC_NONE, "active": BOND_FAIL_OVER_MAC_ACTIVE, "follow": BOND_FAIL_OVER_MAC_FOLLOW, } func (b BondFailOverMac) String() string { s, ok := bondFailOverMacToString[b] if !ok { return fmt.Sprintf("BondFailOverMac(%d)", b) } return s } // BondXmitHashPolicy type type BondXmitHashPolicy int func (b BondXmitHashPolicy) String() string { s, ok := bondXmitHashPolicyToString[b] if !ok { return fmt.Sprintf("XmitHashPolicy(%d)", b) } return s } // StringToBondXmitHashPolicy returns bond lacp arte, or unknown is the s is invalid. func StringToBondXmitHashPolicy(s string) BondXmitHashPolicy { lacp, ok := StringToBondXmitHashPolicyMap[s] if !ok { return BOND_XMIT_HASH_POLICY_UNKNOWN } return lacp } // Possible BondXmitHashPolicy value const ( BOND_XMIT_HASH_POLICY_LAYER2 BondXmitHashPolicy = iota BOND_XMIT_HASH_POLICY_LAYER3_4 BOND_XMIT_HASH_POLICY_LAYER2_3 BOND_XMIT_HASH_POLICY_ENCAP2_3 BOND_XMIT_HASH_POLICY_ENCAP3_4 BOND_XMIT_HASH_POLICY_UNKNOWN ) var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{ BOND_XMIT_HASH_POLICY_LAYER2: "layer2", BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4", BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3", BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3", BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4", } var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{ "layer2": BOND_XMIT_HASH_POLICY_LAYER2, "layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4, "layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3, "encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3, "encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4, } // BondLacpRate type type BondLacpRate int func (b BondLacpRate) String() string { s, ok := bondLacpRateToString[b] if !ok { return fmt.Sprintf("LacpRate(%d)", b) } return s } // StringToBondLacpRate returns bond lacp arte, or unknown is the s is invalid. func StringToBondLacpRate(s string) BondLacpRate { lacp, ok := StringToBondLacpRateMap[s] if !ok { return BOND_LACP_RATE_UNKNOWN } return lacp } // Possible BondLacpRate value const ( BOND_LACP_RATE_SLOW BondLacpRate = iota BOND_LACP_RATE_FAST BOND_LACP_RATE_UNKNOWN ) var bondLacpRateToString = map[BondLacpRate]string{ BOND_LACP_RATE_SLOW: "slow", BOND_LACP_RATE_FAST: "fast", } var StringToBondLacpRateMap = map[string]BondLacpRate{ "slow": BOND_LACP_RATE_SLOW, "fast": BOND_LACP_RATE_FAST, } // BondAdSelect type type BondAdSelect int // Possible BondAdSelect value const ( BOND_AD_SELECT_STABLE BondAdSelect = iota BOND_AD_SELECT_BANDWIDTH BOND_AD_SELECT_COUNT ) var bondAdSelectToString = map[BondAdSelect]string{ BOND_AD_SELECT_STABLE: "stable", BOND_AD_SELECT_BANDWIDTH: "bandwidth", BOND_AD_SELECT_COUNT: "count", } var StringToBondAdSelectMap = map[string]BondAdSelect{ "stable": BOND_AD_SELECT_STABLE, "bandwidth": BOND_AD_SELECT_BANDWIDTH, "count": BOND_AD_SELECT_COUNT, } func (b BondAdSelect) String() string { s, ok := bondAdSelectToString[b] if !ok { return fmt.Sprintf("BondAdSelect(%d)", b) } return s } // BondAdInfo represents ad info for bond type BondAdInfo struct { AggregatorId int NumPorts int ActorKey int PartnerKey int PartnerMac net.HardwareAddr } // Bond representation type Bond struct { LinkAttrs Mode BondMode ActiveSlave int Miimon int UpDelay int DownDelay int UseCarrier int ArpInterval int ArpIpTargets []net.IP ArpValidate BondArpValidate ArpAllTargets BondArpAllTargets Primary int PrimaryReselect BondPrimaryReselect FailOverMac BondFailOverMac XmitHashPolicy BondXmitHashPolicy ResendIgmp int NumPeerNotif int AllSlavesActive int MinLinks int LpInterval int PacketsPerSlave int LacpRate BondLacpRate AdSelect BondAdSelect // looking at iproute tool AdInfo can only be retrived. It can't be set. AdInfo *BondAdInfo AdActorSysPrio int AdUserPortKey int AdActorSystem net.HardwareAddr TlbDynamicLb int } func NewLinkBond(atr LinkAttrs) *Bond { return &Bond{ LinkAttrs: atr, Mode: -1, ActiveSlave: -1, Miimon: -1, UpDelay: -1, DownDelay: -1, UseCarrier: -1, ArpInterval: -1, ArpIpTargets: nil, ArpValidate: -1, ArpAllTargets: -1, Primary: -1, PrimaryReselect: -1, FailOverMac: -1, XmitHashPolicy: -1, ResendIgmp: -1, NumPeerNotif: -1, AllSlavesActive: -1, MinLinks: -1, LpInterval: -1, PacketsPerSlave: -1, LacpRate: -1, AdSelect: -1, AdActorSysPrio: -1, AdUserPortKey: -1, AdActorSystem: nil, TlbDynamicLb: -1, } } // Flag mask for bond options. Bond.Flagmask must be set to on for option to work. const ( BOND_MODE_MASK uint64 = 1 << (1 + iota) BOND_ACTIVE_SLAVE_MASK BOND_MIIMON_MASK BOND_UPDELAY_MASK BOND_DOWNDELAY_MASK BOND_USE_CARRIER_MASK BOND_ARP_INTERVAL_MASK BOND_ARP_VALIDATE_MASK BOND_ARP_ALL_TARGETS_MASK BOND_PRIMARY_MASK BOND_PRIMARY_RESELECT_MASK BOND_FAIL_OVER_MAC_MASK BOND_XMIT_HASH_POLICY_MASK BOND_RESEND_IGMP_MASK BOND_NUM_PEER_NOTIF_MASK BOND_ALL_SLAVES_ACTIVE_MASK BOND_MIN_LINKS_MASK BOND_LP_INTERVAL_MASK BOND_PACKETS_PER_SLAVE_MASK BOND_LACP_RATE_MASK BOND_AD_SELECT_MASK ) // Attrs implementation. func (bond *Bond) Attrs() *LinkAttrs { return &bond.LinkAttrs } // Type implementation fro Vxlan. func (bond *Bond) Type() string { return "bond" } // BondSlaveState represents the values of the IFLA_BOND_SLAVE_STATE bond slave // attribute, which contains the state of the bond slave. type BondSlaveState uint8 const ( //BondStateActive Link is active. BondStateActive BondSlaveState = iota //BondStateBackup Link is backup. BondStateBackup ) func (s BondSlaveState) String() string { switch s { case BondStateActive: return "ACTIVE" case BondStateBackup: return "BACKUP" default: return strconv.Itoa(int(s)) } } // BondSlaveMiiStatus represents the values of the IFLA_BOND_SLAVE_MII_STATUS bond slave // attribute, which contains the status of MII link monitoring type BondSlaveMiiStatus uint8 const ( //BondLinkUp link is up and running. BondLinkUp BondSlaveMiiStatus = iota //BondLinkFail link has just gone down. BondLinkFail //BondLinkDown link has been down for too long time. BondLinkDown //BondLinkBack link is going back. BondLinkBack ) func (s BondSlaveMiiStatus) String() string { switch s { case BondLinkUp: return "UP" case BondLinkFail: return "GOING_DOWN" case BondLinkDown: return "DOWN" case BondLinkBack: return "GOING_BACK" default: return strconv.Itoa(int(s)) } } type BondSlave struct { State BondSlaveState MiiStatus BondSlaveMiiStatus LinkFailureCount uint32 PermHardwareAddr net.HardwareAddr QueueId uint16 AggregatorId uint16 AdActorOperPortState uint8 AdPartnerOperPortState uint16 } func (b *BondSlave) SlaveType() string { return "bond" } type VrfSlave struct { Table uint32 } func (v *VrfSlave) SlaveType() string { return "vrf" } // Geneve devices must specify RemoteIP and ID (VNI) on create // https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223 type Geneve struct { LinkAttrs ID uint32 // vni Remote net.IP Ttl uint8 Tos uint8 Dport uint16 UdpCsum uint8 UdpZeroCsum6Tx uint8 UdpZeroCsum6Rx uint8 Link uint32 FlowBased bool } func (geneve *Geneve) Attrs() *LinkAttrs { return &geneve.LinkAttrs } func (geneve *Geneve) Type() string { return "geneve" } // Gretap devices must specify LocalIP and RemoteIP on create type Gretap struct { LinkAttrs IKey uint32 OKey uint32 EncapSport uint16 EncapDport uint16 Local net.IP Remote net.IP IFlags uint16 OFlags uint16 PMtuDisc uint8 Ttl uint8 Tos uint8 EncapType uint16 EncapFlags uint16 Link uint32 FlowBased bool } func (gretap *Gretap) Attrs() *LinkAttrs { return &gretap.LinkAttrs } func (gretap *Gretap) Type() string { if gretap.Local.To4() == nil { return "ip6gretap" } return "gretap" } type Iptun struct { LinkAttrs Ttl uint8 Tos uint8 PMtuDisc uint8 Link uint32 Local net.IP Remote net.IP EncapSport uint16 EncapDport uint16 EncapType uint16 EncapFlags uint16 FlowBased bool } func (iptun *Iptun) Attrs() *LinkAttrs { return &iptun.LinkAttrs } func (iptun *Iptun) Type() string { return "ipip" } type Ip6tnl struct { LinkAttrs Link uint32 Local net.IP Remote net.IP Ttl uint8 Tos uint8 Flags uint32 Proto uint8 FlowInfo uint32 EncapLimit uint8 EncapType uint16 EncapFlags uint16 EncapSport uint16 EncapDport uint16 } func (ip6tnl *Ip6tnl) Attrs() *LinkAttrs { return &ip6tnl.LinkAttrs } func (ip6tnl *Ip6tnl) Type() string { return "ip6tnl" } type Sittun struct { LinkAttrs Link uint32 Ttl uint8 Tos uint8 PMtuDisc uint8 Proto uint8 Local net.IP Remote net.IP EncapLimit uint8 EncapType uint16 EncapFlags uint16 EncapSport uint16 EncapDport uint16 } func (sittun *Sittun) Attrs() *LinkAttrs { return &sittun.LinkAttrs } func (sittun *Sittun) Type() string { return "sit" } type Vti struct { LinkAttrs IKey uint32 OKey uint32 Link uint32 Local net.IP Remote net.IP } func (vti *Vti) Attrs() *LinkAttrs { return &vti.LinkAttrs } func (vti *Vti) Type() string { if vti.Local.To4() == nil { return "vti6" } return "vti" } type Gretun struct { LinkAttrs Link uint32 IFlags uint16 OFlags uint16 IKey uint32 OKey uint32 Local net.IP Remote net.IP Ttl uint8 Tos uint8 PMtuDisc uint8 EncapType uint16 EncapFlags uint16 EncapSport uint16 EncapDport uint16 } func (gretun *Gretun) Attrs() *LinkAttrs { return &gretun.LinkAttrs } func (gretun *Gretun) Type() string { if gretun.Local.To4() == nil { return "ip6gre" } return "gre" } type Vrf struct { LinkAttrs Table uint32 } func (vrf *Vrf) Attrs() *LinkAttrs { return &vrf.LinkAttrs } func (vrf *Vrf) Type() string { return "vrf" } type GTP struct { LinkAttrs FD0 int FD1 int Role int PDPHashsize int } func (gtp *GTP) Attrs() *LinkAttrs { return >p.LinkAttrs } func (gtp *GTP) Type() string { return "gtp" } // Virtual XFRM Interfaces // Named "xfrmi" to prevent confusion with XFRM objects type Xfrmi struct { LinkAttrs Ifid uint32 } func (xfrm *Xfrmi) Attrs() *LinkAttrs { return &xfrm.LinkAttrs } func (xfrm *Xfrmi) Type() string { return "xfrm" } // IPoIB interface type IPoIBMode uint16 func (m *IPoIBMode) String() string { str, ok := iPoIBModeToString[*m] if !ok { return fmt.Sprintf("mode(%d)", *m) } return str } const ( IPOIB_MODE_DATAGRAM = iota IPOIB_MODE_CONNECTED ) var iPoIBModeToString = map[IPoIBMode]string{ IPOIB_MODE_DATAGRAM: "datagram", IPOIB_MODE_CONNECTED: "connected", } var StringToIPoIBMode = map[string]IPoIBMode{ "datagram": IPOIB_MODE_DATAGRAM, "connected": IPOIB_MODE_CONNECTED, } const ( CAN_STATE_ERROR_ACTIVE = iota CAN_STATE_ERROR_WARNING CAN_STATE_ERROR_PASSIVE CAN_STATE_BUS_OFF CAN_STATE_STOPPED CAN_STATE_SLEEPING ) type Can struct { LinkAttrs BitRate uint32 SamplePoint uint32 TimeQuanta uint32 PropagationSegment uint32 PhaseSegment1 uint32 PhaseSegment2 uint32 SyncJumpWidth uint32 BitRatePreScaler uint32 Name string TimeSegment1Min uint32 TimeSegment1Max uint32 TimeSegment2Min uint32 TimeSegment2Max uint32 SyncJumpWidthMax uint32 BitRatePreScalerMin uint32 BitRatePreScalerMax uint32 BitRatePreScalerInc uint32 ClockFrequency uint32 State uint32 Mask uint32 Flags uint32 TxError uint16 RxError uint16 RestartMs uint32 } func (can *Can) Attrs() *LinkAttrs { return &can.LinkAttrs } func (can *Can) Type() string { return "can" } type IPoIB struct { LinkAttrs Pkey uint16 Mode IPoIBMode Umcast uint16 } func (ipoib *IPoIB) Attrs() *LinkAttrs { return &ipoib.LinkAttrs } func (ipoib *IPoIB) Type() string { return "ipoib" } // iproute2 supported devices; // vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | // gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon | // bond_slave | ipvlan | xfrm // LinkNotFoundError wraps the various not found errors when // getting/reading links. This is intended for better error // handling by dependent code so that "not found error" can // be distinguished from other errors type LinkNotFoundError struct { error } ================================================ FILE: vendor/github.com/vishvananda/netlink/link_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "fmt" "io/ioutil" "net" "os" "strconv" "strings" "syscall" "unsafe" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) const ( SizeofLinkStats32 = 0x5c SizeofLinkStats64 = 0xb8 ) const ( TUNTAP_MODE_TUN TuntapMode = unix.IFF_TUN TUNTAP_MODE_TAP TuntapMode = unix.IFF_TAP TUNTAP_DEFAULTS TuntapFlag = unix.IFF_TUN_EXCL | unix.IFF_ONE_QUEUE TUNTAP_VNET_HDR TuntapFlag = unix.IFF_VNET_HDR TUNTAP_TUN_EXCL TuntapFlag = unix.IFF_TUN_EXCL TUNTAP_NO_PI TuntapFlag = unix.IFF_NO_PI TUNTAP_ONE_QUEUE TuntapFlag = unix.IFF_ONE_QUEUE TUNTAP_MULTI_QUEUE TuntapFlag = unix.IFF_MULTI_QUEUE TUNTAP_MULTI_QUEUE_DEFAULTS TuntapFlag = TUNTAP_MULTI_QUEUE | TUNTAP_NO_PI ) var StringToTuntapModeMap = map[string]TuntapMode{ "tun": TUNTAP_MODE_TUN, "tap": TUNTAP_MODE_TAP, } func (ttm TuntapMode) String() string { switch ttm { case TUNTAP_MODE_TUN: return "tun" case TUNTAP_MODE_TAP: return "tap" } return "unknown" } const ( VF_LINK_STATE_AUTO uint32 = 0 VF_LINK_STATE_ENABLE uint32 = 1 VF_LINK_STATE_DISABLE uint32 = 2 ) var lookupByDump = false var macvlanModes = [...]uint32{ 0, nl.MACVLAN_MODE_PRIVATE, nl.MACVLAN_MODE_VEPA, nl.MACVLAN_MODE_BRIDGE, nl.MACVLAN_MODE_PASSTHRU, nl.MACVLAN_MODE_SOURCE, } func ensureIndex(link *LinkAttrs) { if link != nil && link.Index == 0 { newlink, _ := LinkByName(link.Name) if newlink != nil { link.Index = newlink.Attrs().Index } } } func (h *Handle) ensureIndex(link *LinkAttrs) { if link != nil && link.Index == 0 { newlink, _ := h.LinkByName(link.Name) if newlink != nil { link.Index = newlink.Attrs().Index } } } func (h *Handle) LinkSetARPOff(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change |= unix.IFF_NOARP msg.Flags |= unix.IFF_NOARP msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func LinkSetARPOff(link Link) error { return pkgHandle.LinkSetARPOff(link) } func (h *Handle) LinkSetARPOn(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change |= unix.IFF_NOARP msg.Flags &= ^uint32(unix.IFF_NOARP) msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func LinkSetARPOn(link Link) error { return pkgHandle.LinkSetARPOn(link) } func (h *Handle) SetPromiscOn(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_PROMISC msg.Flags = unix.IFF_PROMISC msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetAllmulticastOn enables the reception of all hardware multicast packets for the link device. // Equivalent to: `ip link set $link allmulticast on` func LinkSetAllmulticastOn(link Link) error { return pkgHandle.LinkSetAllmulticastOn(link) } // LinkSetAllmulticastOn enables the reception of all hardware multicast packets for the link device. // Equivalent to: `ip link set $link allmulticast on` func (h *Handle) LinkSetAllmulticastOn(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_ALLMULTI msg.Flags = unix.IFF_ALLMULTI msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetAllmulticastOff disables the reception of all hardware multicast packets for the link device. // Equivalent to: `ip link set $link allmulticast off` func LinkSetAllmulticastOff(link Link) error { return pkgHandle.LinkSetAllmulticastOff(link) } // LinkSetAllmulticastOff disables the reception of all hardware multicast packets for the link device. // Equivalent to: `ip link set $link allmulticast off` func (h *Handle) LinkSetAllmulticastOff(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_ALLMULTI msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetMulticastOn enables the reception of multicast packets for the link device. // Equivalent to: `ip link set $link multicast on` func LinkSetMulticastOn(link Link) error { return pkgHandle.LinkSetMulticastOn(link) } // LinkSetMulticastOn enables the reception of multicast packets for the link device. // Equivalent to: `ip link set $link multicast on` func (h *Handle) LinkSetMulticastOn(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_MULTICAST msg.Flags = unix.IFF_MULTICAST msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetAllmulticastOff disables the reception of multicast packets for the link device. // Equivalent to: `ip link set $link multicast off` func LinkSetMulticastOff(link Link) error { return pkgHandle.LinkSetMulticastOff(link) } // LinkSetAllmulticastOff disables the reception of multicast packets for the link device. // Equivalent to: `ip link set $link multicast off` func (h *Handle) LinkSetMulticastOff(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_MULTICAST msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error { return pkgHandle.MacvlanMACAddrAdd(link, addr) } func (h *Handle) MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error { return h.macvlanMACAddrChange(link, []net.HardwareAddr{addr}, nl.MACVLAN_MACADDR_ADD) } func MacvlanMACAddrDel(link Link, addr net.HardwareAddr) error { return pkgHandle.MacvlanMACAddrDel(link, addr) } func (h *Handle) MacvlanMACAddrDel(link Link, addr net.HardwareAddr) error { return h.macvlanMACAddrChange(link, []net.HardwareAddr{addr}, nl.MACVLAN_MACADDR_DEL) } func MacvlanMACAddrFlush(link Link) error { return pkgHandle.MacvlanMACAddrFlush(link) } func (h *Handle) MacvlanMACAddrFlush(link Link) error { return h.macvlanMACAddrChange(link, nil, nl.MACVLAN_MACADDR_FLUSH) } func MacvlanMACAddrSet(link Link, addrs []net.HardwareAddr) error { return pkgHandle.MacvlanMACAddrSet(link, addrs) } func (h *Handle) MacvlanMACAddrSet(link Link, addrs []net.HardwareAddr) error { return h.macvlanMACAddrChange(link, addrs, nl.MACVLAN_MACADDR_SET) } func (h *Handle) macvlanMACAddrChange(link Link, addrs []net.HardwareAddr, mode uint32) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) linkInfo.AddRtAttr(nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) inner := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) // IFLA_MACVLAN_MACADDR_MODE = mode b := make([]byte, 4) native.PutUint32(b, mode) inner.AddRtAttr(nl.IFLA_MACVLAN_MACADDR_MODE, b) // populate message with MAC addrs, if necessary switch mode { case nl.MACVLAN_MACADDR_ADD, nl.MACVLAN_MACADDR_DEL: if len(addrs) == 1 { inner.AddRtAttr(nl.IFLA_MACVLAN_MACADDR, []byte(addrs[0])) } case nl.MACVLAN_MACADDR_SET: mad := inner.AddRtAttr(nl.IFLA_MACVLAN_MACADDR_DATA, nil) for _, addr := range addrs { mad.AddRtAttr(nl.IFLA_MACVLAN_MACADDR, []byte(addr)) } } req.AddData(linkInfo) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetMacvlanMode sets the mode of a macvlan or macvtap link device. // Note that passthrough mode cannot be set to and from and will fail. // Equivalent to: `ip link set $link type (macvlan|macvtap) mode $mode func LinkSetMacvlanMode(link Link, mode MacvlanMode) error { return pkgHandle.LinkSetMacvlanMode(link, mode) } // LinkSetMacvlanMode sets the mode of the macvlan or macvtap link device. // Note that passthrough mode cannot be set to and from and will fail. // Equivalent to: `ip link set $link type (macvlan|macvtap) mode $mode func (h *Handle) LinkSetMacvlanMode(link Link, mode MacvlanMode) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) linkInfo.AddRtAttr(nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[mode])) req.AddData(linkInfo) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func BridgeSetMcastSnoop(link Link, on bool) error { return pkgHandle.BridgeSetMcastSnoop(link, on) } func (h *Handle) BridgeSetMcastSnoop(link Link, on bool) error { bridge := link.(*Bridge) bridge.MulticastSnooping = &on return h.linkModify(bridge, unix.NLM_F_ACK) } func BridgeSetVlanFiltering(link Link, on bool) error { return pkgHandle.BridgeSetVlanFiltering(link, on) } func (h *Handle) BridgeSetVlanFiltering(link Link, on bool) error { bridge := link.(*Bridge) bridge.VlanFiltering = &on return h.linkModify(bridge, unix.NLM_F_ACK) } func SetPromiscOn(link Link) error { return pkgHandle.SetPromiscOn(link) } func (h *Handle) SetPromiscOff(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_PROMISC msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func SetPromiscOff(link Link) error { return pkgHandle.SetPromiscOff(link) } // LinkSetUp enables the link device. // Equivalent to: `ip link set $link up` func LinkSetUp(link Link) error { return pkgHandle.LinkSetUp(link) } // LinkSetUp enables the link device. // Equivalent to: `ip link set $link up` func (h *Handle) LinkSetUp(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_UP msg.Flags = unix.IFF_UP msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetDown disables link device. // Equivalent to: `ip link set $link down` func LinkSetDown(link Link) error { return pkgHandle.LinkSetDown(link) } // LinkSetDown disables link device. // Equivalent to: `ip link set $link down` func (h *Handle) LinkSetDown(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Change = unix.IFF_UP msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetMTU sets the mtu of the link device. // Equivalent to: `ip link set $link mtu $mtu` func LinkSetMTU(link Link, mtu int) error { return pkgHandle.LinkSetMTU(link, mtu) } // LinkSetMTU sets the mtu of the link device. // Equivalent to: `ip link set $link mtu $mtu` func (h *Handle) LinkSetMTU(link Link, mtu int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(mtu)) data := nl.NewRtAttr(unix.IFLA_MTU, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetName sets the name of the link device. // Equivalent to: `ip link set $link name $name` func LinkSetName(link Link, name string) error { return pkgHandle.LinkSetName(link, name) } // LinkSetName sets the name of the link device. // Equivalent to: `ip link set $link name $name` func (h *Handle) LinkSetName(link Link, name string) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_IFNAME, []byte(name)) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetAlias sets the alias of the link device. // Equivalent to: `ip link set dev $link alias $name` func LinkSetAlias(link Link, name string) error { return pkgHandle.LinkSetAlias(link, name) } // LinkSetAlias sets the alias of the link device. // Equivalent to: `ip link set dev $link alias $name` func (h *Handle) LinkSetAlias(link Link, name string) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_IFALIAS, []byte(name)) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetHardwareAddr sets the hardware address of the link device. // Equivalent to: `ip link set $link address $hwaddr` func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { return pkgHandle.LinkSetHardwareAddr(link, hwaddr) } // LinkSetHardwareAddr sets the hardware address of the link device. // Equivalent to: `ip link set $link address $hwaddr` func (h *Handle) LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_ADDRESS, []byte(hwaddr)) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfHardwareAddr sets the hardware address of a vf for the link. // Equivalent to: `ip link set $link vf $vf mac $hwaddr` func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { return pkgHandle.LinkSetVfHardwareAddr(link, vf, hwaddr) } // LinkSetVfHardwareAddr sets the hardware address of a vf for the link. // Equivalent to: `ip link set $link vf $vf mac $hwaddr` func (h *Handle) LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfMac{ Vf: uint32(vf), } copy(vfmsg.Mac[:], []byte(hwaddr)) info.AddRtAttr(nl.IFLA_VF_MAC, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfVlan sets the vlan of a vf for the link. // Equivalent to: `ip link set $link vf $vf vlan $vlan` func LinkSetVfVlan(link Link, vf, vlan int) error { return pkgHandle.LinkSetVfVlan(link, vf, vlan) } // LinkSetVfVlan sets the vlan of a vf for the link. // Equivalent to: `ip link set $link vf $vf vlan $vlan` func (h *Handle) LinkSetVfVlan(link Link, vf, vlan int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfVlan{ Vf: uint32(vf), Vlan: uint32(vlan), } info.AddRtAttr(nl.IFLA_VF_VLAN, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfVlanQos sets the vlan and qos priority of a vf for the link. // Equivalent to: `ip link set $link vf $vf vlan $vlan qos $qos` func LinkSetVfVlanQos(link Link, vf, vlan, qos int) error { return pkgHandle.LinkSetVfVlanQos(link, vf, vlan, qos) } // LinkSetVfVlanQos sets the vlan and qos priority of a vf for the link. // Equivalent to: `ip link set $link vf $vf vlan $vlan qos $qos` func (h *Handle) LinkSetVfVlanQos(link Link, vf, vlan, qos int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) vfmsg := nl.VfVlan{ Vf: uint32(vf), Vlan: uint32(vlan), Qos: uint32(qos), } nl.NewRtAttrChild(info, nl.IFLA_VF_VLAN, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfTxRate sets the tx rate of a vf for the link. // Equivalent to: `ip link set $link vf $vf rate $rate` func LinkSetVfTxRate(link Link, vf, rate int) error { return pkgHandle.LinkSetVfTxRate(link, vf, rate) } // LinkSetVfTxRate sets the tx rate of a vf for the link. // Equivalent to: `ip link set $link vf $vf rate $rate` func (h *Handle) LinkSetVfTxRate(link Link, vf, rate int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfTxRate{ Vf: uint32(vf), Rate: uint32(rate), } info.AddRtAttr(nl.IFLA_VF_TX_RATE, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfRate sets the min and max tx rate of a vf for the link. // Equivalent to: `ip link set $link vf $vf min_tx_rate $min_rate max_tx_rate $max_rate` func LinkSetVfRate(link Link, vf, minRate, maxRate int) error { return pkgHandle.LinkSetVfRate(link, vf, minRate, maxRate) } // LinkSetVfRate sets the min and max tx rate of a vf for the link. // Equivalent to: `ip link set $link vf $vf min_tx_rate $min_rate max_tx_rate $max_rate` func (h *Handle) LinkSetVfRate(link Link, vf, minRate, maxRate int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfRate{ Vf: uint32(vf), MinTxRate: uint32(minRate), MaxTxRate: uint32(maxRate), } info.AddRtAttr(nl.IFLA_VF_RATE, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfState enables/disables virtual link state on a vf. // Equivalent to: `ip link set $link vf $vf state $state` func LinkSetVfState(link Link, vf int, state uint32) error { return pkgHandle.LinkSetVfState(link, vf, state) } // LinkSetVfState enables/disables virtual link state on a vf. // Equivalent to: `ip link set $link vf $vf state $state` func (h *Handle) LinkSetVfState(link Link, vf int, state uint32) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfLinkState{ Vf: uint32(vf), LinkState: state, } info.AddRtAttr(nl.IFLA_VF_LINK_STATE, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfSpoofchk enables/disables spoof check on a vf for the link. // Equivalent to: `ip link set $link vf $vf spoofchk $check` func LinkSetVfSpoofchk(link Link, vf int, check bool) error { return pkgHandle.LinkSetVfSpoofchk(link, vf, check) } // LinkSetVfSpoofchk enables/disables spoof check on a vf for the link. // Equivalent to: `ip link set $link vf $vf spoofchk $check` func (h *Handle) LinkSetVfSpoofchk(link Link, vf int, check bool) error { var setting uint32 base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) if check { setting = 1 } vfmsg := nl.VfSpoofchk{ Vf: uint32(vf), Setting: setting, } info.AddRtAttr(nl.IFLA_VF_SPOOFCHK, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfTrust enables/disables trust state on a vf for the link. // Equivalent to: `ip link set $link vf $vf trust $state` func LinkSetVfTrust(link Link, vf int, state bool) error { return pkgHandle.LinkSetVfTrust(link, vf, state) } // LinkSetVfTrust enables/disables trust state on a vf for the link. // Equivalent to: `ip link set $link vf $vf trust $state` func (h *Handle) LinkSetVfTrust(link Link, vf int, state bool) error { var setting uint32 base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) if state { setting = 1 } vfmsg := nl.VfTrust{ Vf: uint32(vf), Setting: setting, } info.AddRtAttr(nl.IFLA_VF_TRUST, vfmsg.Serialize()) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetVfNodeGUID sets the node GUID of a vf for the link. // Equivalent to: `ip link set dev $link vf $vf node_guid $nodeguid` func LinkSetVfNodeGUID(link Link, vf int, nodeguid net.HardwareAddr) error { return pkgHandle.LinkSetVfGUID(link, vf, nodeguid, nl.IFLA_VF_IB_NODE_GUID) } // LinkSetVfPortGUID sets the port GUID of a vf for the link. // Equivalent to: `ip link set dev $link vf $vf port_guid $portguid` func LinkSetVfPortGUID(link Link, vf int, portguid net.HardwareAddr) error { return pkgHandle.LinkSetVfGUID(link, vf, portguid, nl.IFLA_VF_IB_PORT_GUID) } // LinkSetVfGUID sets the node or port GUID of a vf for the link. func (h *Handle) LinkSetVfGUID(link Link, vf int, vfGuid net.HardwareAddr, guidType int) error { var err error var guid uint64 buf := bytes.NewBuffer(vfGuid) err = binary.Read(buf, binary.BigEndian, &guid) if err != nil { return err } base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil) vfmsg := nl.VfGUID{ Vf: uint32(vf), GUID: guid, } info.AddRtAttr(guidType, vfmsg.Serialize()) req.AddData(data) _, err = req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetMaster sets the master of the link device. // Equivalent to: `ip link set $link master $master` func LinkSetMaster(link Link, master Link) error { return pkgHandle.LinkSetMaster(link, master) } // LinkSetMaster sets the master of the link device. // Equivalent to: `ip link set $link master $master` func (h *Handle) LinkSetMaster(link Link, master Link) error { index := 0 if master != nil { masterBase := master.Attrs() h.ensureIndex(masterBase) index = masterBase.Index } if index <= 0 { return fmt.Errorf("Device does not exist") } return h.LinkSetMasterByIndex(link, index) } // LinkSetNoMaster removes the master of the link device. // Equivalent to: `ip link set $link nomaster` func LinkSetNoMaster(link Link) error { return pkgHandle.LinkSetNoMaster(link) } // LinkSetNoMaster removes the master of the link device. // Equivalent to: `ip link set $link nomaster` func (h *Handle) LinkSetNoMaster(link Link) error { return h.LinkSetMasterByIndex(link, 0) } // LinkSetMasterByIndex sets the master of the link device. // Equivalent to: `ip link set $link master $master` func LinkSetMasterByIndex(link Link, masterIndex int) error { return pkgHandle.LinkSetMasterByIndex(link, masterIndex) } // LinkSetMasterByIndex sets the master of the link device. // Equivalent to: `ip link set $link master $master` func (h *Handle) LinkSetMasterByIndex(link Link, masterIndex int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(masterIndex)) data := nl.NewRtAttr(unix.IFLA_MASTER, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetNsPid puts the device into a new network namespace. The // pid must be a pid of a running process. // Equivalent to: `ip link set $link netns $pid` func LinkSetNsPid(link Link, nspid int) error { return pkgHandle.LinkSetNsPid(link, nspid) } // LinkSetNsPid puts the device into a new network namespace. The // pid must be a pid of a running process. // Equivalent to: `ip link set $link netns $pid` func (h *Handle) LinkSetNsPid(link Link, nspid int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(nspid)) data := nl.NewRtAttr(unix.IFLA_NET_NS_PID, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetNsFd puts the device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `ip link set $link netns $ns` func LinkSetNsFd(link Link, fd int) error { return pkgHandle.LinkSetNsFd(link, fd) } // LinkSetNsFd puts the device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `ip link set $link netns $ns` func (h *Handle) LinkSetNsFd(link Link, fd int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(fd)) data := nl.NewRtAttr(unix.IFLA_NET_NS_FD, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetXdpFd adds a bpf function to the driver. The fd must be a bpf // program loaded with bpf(type=BPF_PROG_TYPE_XDP) func LinkSetXdpFd(link Link, fd int) error { return LinkSetXdpFdWithFlags(link, fd, 0) } // LinkSetXdpFdWithFlags adds a bpf function to the driver with the given // options. The fd must be a bpf program loaded with bpf(type=BPF_PROG_TYPE_XDP) func LinkSetXdpFdWithFlags(link Link, fd, flags int) error { base := link.Attrs() ensureIndex(base) req := nl.NewNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) addXdpAttrs(&LinkXdp{Fd: fd, Flags: uint32(flags)}, req) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func boolAttr(val bool) []byte { var v uint8 if val { v = 1 } return nl.Uint8Attr(v) } type vxlanPortRange struct { Lo, Hi uint16 } func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if vxlan.FlowBased { vxlan.VxlanId = 0 } data.AddRtAttr(nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId))) if vxlan.VtepDevIndex != 0 { data.AddRtAttr(nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex))) } if vxlan.SrcAddr != nil { ip := vxlan.SrcAddr.To4() if ip != nil { data.AddRtAttr(nl.IFLA_VXLAN_LOCAL, []byte(ip)) } else { ip = vxlan.SrcAddr.To16() if ip != nil { data.AddRtAttr(nl.IFLA_VXLAN_LOCAL6, []byte(ip)) } } } if vxlan.Group != nil { group := vxlan.Group.To4() if group != nil { data.AddRtAttr(nl.IFLA_VXLAN_GROUP, []byte(group)) } else { group = vxlan.Group.To16() if group != nil { data.AddRtAttr(nl.IFLA_VXLAN_GROUP6, []byte(group)) } } } data.AddRtAttr(nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL))) data.AddRtAttr(nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS))) data.AddRtAttr(nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning)) data.AddRtAttr(nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy)) data.AddRtAttr(nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC)) data.AddRtAttr(nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss)) data.AddRtAttr(nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss)) data.AddRtAttr(nl.IFLA_VXLAN_UDP_ZERO_CSUM6_TX, boolAttr(vxlan.UDP6ZeroCSumTx)) data.AddRtAttr(nl.IFLA_VXLAN_UDP_ZERO_CSUM6_RX, boolAttr(vxlan.UDP6ZeroCSumRx)) if vxlan.UDPCSum { data.AddRtAttr(nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum)) } if vxlan.GBP { data.AddRtAttr(nl.IFLA_VXLAN_GBP, []byte{}) } if vxlan.FlowBased { data.AddRtAttr(nl.IFLA_VXLAN_FLOWBASED, boolAttr(vxlan.FlowBased)) } if vxlan.NoAge { data.AddRtAttr(nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) } else if vxlan.Age > 0 { data.AddRtAttr(nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age))) } if vxlan.Limit > 0 { data.AddRtAttr(nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit))) } if vxlan.Port > 0 { data.AddRtAttr(nl.IFLA_VXLAN_PORT, htons(uint16(vxlan.Port))) } if vxlan.PortLow > 0 || vxlan.PortHigh > 0 { pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)} buf := new(bytes.Buffer) binary.Write(buf, binary.BigEndian, &pr) data.AddRtAttr(nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes()) } } func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if bond.Mode >= 0 { data.AddRtAttr(nl.IFLA_BOND_MODE, nl.Uint8Attr(uint8(bond.Mode))) } if bond.ActiveSlave >= 0 { data.AddRtAttr(nl.IFLA_BOND_ACTIVE_SLAVE, nl.Uint32Attr(uint32(bond.ActiveSlave))) } if bond.Miimon >= 0 { data.AddRtAttr(nl.IFLA_BOND_MIIMON, nl.Uint32Attr(uint32(bond.Miimon))) } if bond.UpDelay >= 0 { data.AddRtAttr(nl.IFLA_BOND_UPDELAY, nl.Uint32Attr(uint32(bond.UpDelay))) } if bond.DownDelay >= 0 { data.AddRtAttr(nl.IFLA_BOND_DOWNDELAY, nl.Uint32Attr(uint32(bond.DownDelay))) } if bond.UseCarrier >= 0 { data.AddRtAttr(nl.IFLA_BOND_USE_CARRIER, nl.Uint8Attr(uint8(bond.UseCarrier))) } if bond.ArpInterval >= 0 { data.AddRtAttr(nl.IFLA_BOND_ARP_INTERVAL, nl.Uint32Attr(uint32(bond.ArpInterval))) } if bond.ArpIpTargets != nil { msg := data.AddRtAttr(nl.IFLA_BOND_ARP_IP_TARGET, nil) for i := range bond.ArpIpTargets { ip := bond.ArpIpTargets[i].To4() if ip != nil { msg.AddRtAttr(i, []byte(ip)) continue } ip = bond.ArpIpTargets[i].To16() if ip != nil { msg.AddRtAttr(i, []byte(ip)) } } } if bond.ArpValidate >= 0 { data.AddRtAttr(nl.IFLA_BOND_ARP_VALIDATE, nl.Uint32Attr(uint32(bond.ArpValidate))) } if bond.ArpAllTargets >= 0 { data.AddRtAttr(nl.IFLA_BOND_ARP_ALL_TARGETS, nl.Uint32Attr(uint32(bond.ArpAllTargets))) } if bond.Primary >= 0 { data.AddRtAttr(nl.IFLA_BOND_PRIMARY, nl.Uint32Attr(uint32(bond.Primary))) } if bond.PrimaryReselect >= 0 { data.AddRtAttr(nl.IFLA_BOND_PRIMARY_RESELECT, nl.Uint8Attr(uint8(bond.PrimaryReselect))) } if bond.FailOverMac >= 0 { data.AddRtAttr(nl.IFLA_BOND_FAIL_OVER_MAC, nl.Uint8Attr(uint8(bond.FailOverMac))) } if bond.XmitHashPolicy >= 0 { data.AddRtAttr(nl.IFLA_BOND_XMIT_HASH_POLICY, nl.Uint8Attr(uint8(bond.XmitHashPolicy))) } if bond.ResendIgmp >= 0 { data.AddRtAttr(nl.IFLA_BOND_RESEND_IGMP, nl.Uint32Attr(uint32(bond.ResendIgmp))) } if bond.NumPeerNotif >= 0 { data.AddRtAttr(nl.IFLA_BOND_NUM_PEER_NOTIF, nl.Uint8Attr(uint8(bond.NumPeerNotif))) } if bond.AllSlavesActive >= 0 { data.AddRtAttr(nl.IFLA_BOND_ALL_SLAVES_ACTIVE, nl.Uint8Attr(uint8(bond.AllSlavesActive))) } if bond.MinLinks >= 0 { data.AddRtAttr(nl.IFLA_BOND_MIN_LINKS, nl.Uint32Attr(uint32(bond.MinLinks))) } if bond.LpInterval >= 0 { data.AddRtAttr(nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval))) } if bond.PacketsPerSlave >= 0 { data.AddRtAttr(nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PacketsPerSlave))) } if bond.LacpRate >= 0 { data.AddRtAttr(nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate))) } if bond.AdSelect >= 0 { data.AddRtAttr(nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect))) } if bond.AdActorSysPrio >= 0 { data.AddRtAttr(nl.IFLA_BOND_AD_ACTOR_SYS_PRIO, nl.Uint16Attr(uint16(bond.AdActorSysPrio))) } if bond.AdUserPortKey >= 0 { data.AddRtAttr(nl.IFLA_BOND_AD_USER_PORT_KEY, nl.Uint16Attr(uint16(bond.AdUserPortKey))) } if bond.AdActorSystem != nil { data.AddRtAttr(nl.IFLA_BOND_AD_ACTOR_SYSTEM, []byte(bond.AdActorSystem)) } if bond.TlbDynamicLb >= 0 { data.AddRtAttr(nl.IFLA_BOND_TLB_DYNAMIC_LB, nl.Uint8Attr(uint8(bond.TlbDynamicLb))) } } func cleanupFds(fds []*os.File) { for _, f := range fds { f.Close() } } // LinkAdd adds a new link device. The type and features of the device // are taken from the parameters in the link object. // Equivalent to: `ip link add $link` func LinkAdd(link Link) error { return pkgHandle.LinkAdd(link) } // LinkAdd adds a new link device. The type and features of the device // are taken from the parameters in the link object. // Equivalent to: `ip link add $link` func (h *Handle) LinkAdd(link Link) error { return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) } func (h *Handle) LinkModify(link Link) error { return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK) } func (h *Handle) linkModify(link Link, flags int) error { // TODO: support extra data for macvlan base := link.Attrs() // if tuntap, then the name can be empty, OS will provide a name tuntap, isTuntap := link.(*Tuntap) if base.Name == "" && !isTuntap { return fmt.Errorf("LinkAttrs.Name cannot be empty") } if isTuntap { if tuntap.Mode < unix.IFF_TUN || tuntap.Mode > unix.IFF_TAP { return fmt.Errorf("Tuntap.Mode %v unknown", tuntap.Mode) } queues := tuntap.Queues var fds []*os.File var req ifReq copy(req.Name[:15], base.Name) req.Flags = uint16(tuntap.Flags) if queues == 0 { //Legacy compatibility queues = 1 if tuntap.Flags == 0 { req.Flags = uint16(TUNTAP_DEFAULTS) } } else { // For best peformance set Flags to TUNTAP_MULTI_QUEUE_DEFAULTS | TUNTAP_VNET_HDR // when a) KVM has support for this ABI and // b) the value of the flag is queryable using the TUNGETIFF ioctl if tuntap.Flags == 0 { req.Flags = uint16(TUNTAP_MULTI_QUEUE_DEFAULTS) } } req.Flags |= uint16(tuntap.Mode) const TUN = "/dev/net/tun" for i := 0; i < queues; i++ { localReq := req fd, err := unix.Open(TUN, os.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { cleanupFds(fds) return err } _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.TUNSETIFF), uintptr(unsafe.Pointer(&localReq))) if errno != 0 { // close the new fd unix.Close(fd) // and the already opened ones cleanupFds(fds) return fmt.Errorf("Tuntap IOCTL TUNSETIFF failed [%d], errno %v", i, errno) } _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNSETOWNER, uintptr(tuntap.Owner)) if errno != 0 { cleanupFds(fds) return fmt.Errorf("Tuntap IOCTL TUNSETOWNER failed [%d], errno %v", i, errno) } _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNSETGROUP, uintptr(tuntap.Group)) if errno != 0 { cleanupFds(fds) return fmt.Errorf("Tuntap IOCTL TUNSETGROUP failed [%d], errno %v", i, errno) } // Set the tun device to non-blocking before use. The below comment // taken from: // // https://github.com/mistsys/tuntap/commit/161418c25003bbee77d085a34af64d189df62bea // // Note there is a complication because in go, if a device node is // opened, go sets it to use nonblocking I/O. However a /dev/net/tun // doesn't work with epoll until after the TUNSETIFF ioctl has been // done. So we open the unix fd directly, do the ioctl, then put the // fd in nonblocking mode, an then finally wrap it in a os.File, // which will see the nonblocking mode and add the fd to the // pollable set, so later on when we Read() from it blocked the // calling thread in the kernel. // // See // https://github.com/golang/go/issues/30426 // which got exposed in go 1.13 by the fix to // https://github.com/golang/go/issues/30624 err = unix.SetNonblock(fd, true) if err != nil { cleanupFds(fds) return fmt.Errorf("Tuntap set to non-blocking failed [%d], err %v", i, err) } // create the file from the file descriptor and store it file := os.NewFile(uintptr(fd), TUN) fds = append(fds, file) // 1) we only care for the name of the first tap in the multi queue set // 2) if the original name was empty, the localReq has now the actual name // // In addition: // This ensures that the link name is always identical to what the kernel returns. // Not only in case of an empty name, but also when using name templates. // e.g. when the provided name is "tap%d", the kernel replaces %d with the next available number. if i == 0 { link.Attrs().Name = strings.Trim(string(localReq.Name[:]), "\x00") } } // only persist interface if NonPersist is NOT set if !tuntap.NonPersist { _, _, errno := unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 1) if errno != 0 { cleanupFds(fds) return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno) } } h.ensureIndex(base) // can't set master during create, so set it afterwards if base.MasterIndex != 0 { // TODO: verify MasterIndex is actually a bridge? err := h.LinkSetMasterByIndex(link, base.MasterIndex) if err != nil { // un-persist (e.g. allow the interface to be removed) the tuntap // should not hurt if not set prior, condition might be not needed if !tuntap.NonPersist { _, _, _ = unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 0) } cleanupFds(fds) return err } } if tuntap.Queues == 0 { cleanupFds(fds) } else { tuntap.Fds = fds } return nil } req := h.newNetlinkRequest(unix.RTM_NEWLINK, flags) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) // TODO: make it shorter if base.Flags&net.FlagUp != 0 { msg.Change = unix.IFF_UP msg.Flags = unix.IFF_UP } if base.Flags&net.FlagBroadcast != 0 { msg.Change |= unix.IFF_BROADCAST msg.Flags |= unix.IFF_BROADCAST } if base.Flags&net.FlagLoopback != 0 { msg.Change |= unix.IFF_LOOPBACK msg.Flags |= unix.IFF_LOOPBACK } if base.Flags&net.FlagPointToPoint != 0 { msg.Change |= unix.IFF_POINTOPOINT msg.Flags |= unix.IFF_POINTOPOINT } if base.Flags&net.FlagMulticast != 0 { msg.Change |= unix.IFF_MULTICAST msg.Flags |= unix.IFF_MULTICAST } if base.Index != 0 { msg.Index = int32(base.Index) } req.AddData(msg) if base.ParentIndex != 0 { b := make([]byte, 4) native.PutUint32(b, uint32(base.ParentIndex)) data := nl.NewRtAttr(unix.IFLA_LINK, b) req.AddData(data) } else if link.Type() == "ipvlan" || link.Type() == "ipoib" { return fmt.Errorf("Can't create %s link without ParentIndex", link.Type()) } nameData := nl.NewRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(base.Name)) req.AddData(nameData) if base.Alias != "" { alias := nl.NewRtAttr(unix.IFLA_IFALIAS, []byte(base.Alias)) req.AddData(alias) } if base.MTU > 0 { mtu := nl.NewRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) req.AddData(mtu) } if base.TxQLen >= 0 { qlen := nl.NewRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) req.AddData(qlen) } if base.HardwareAddr != nil { hwaddr := nl.NewRtAttr(unix.IFLA_ADDRESS, []byte(base.HardwareAddr)) req.AddData(hwaddr) } if base.NumTxQueues > 0 { txqueues := nl.NewRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(uint32(base.NumTxQueues))) req.AddData(txqueues) } if base.NumRxQueues > 0 { rxqueues := nl.NewRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(uint32(base.NumRxQueues))) req.AddData(rxqueues) } if base.GSOMaxSegs > 0 { gsoAttr := nl.NewRtAttr(unix.IFLA_GSO_MAX_SEGS, nl.Uint32Attr(base.GSOMaxSegs)) req.AddData(gsoAttr) } if base.GSOMaxSize > 0 { gsoAttr := nl.NewRtAttr(unix.IFLA_GSO_MAX_SIZE, nl.Uint32Attr(base.GSOMaxSize)) req.AddData(gsoAttr) } if base.Group > 0 { groupAttr := nl.NewRtAttr(unix.IFLA_GROUP, nl.Uint32Attr(base.Group)) req.AddData(groupAttr) } if base.Namespace != nil { var attr *nl.RtAttr switch ns := base.Namespace.(type) { case NsPid: val := nl.Uint32Attr(uint32(ns)) attr = nl.NewRtAttr(unix.IFLA_NET_NS_PID, val) case NsFd: val := nl.Uint32Attr(uint32(ns)) attr = nl.NewRtAttr(unix.IFLA_NET_NS_FD, val) } req.AddData(attr) } if base.Xdp != nil { addXdpAttrs(base.Xdp, req) } linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) linkInfo.AddRtAttr(nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) switch link := link.(type) { case *Vlan: b := make([]byte, 2) native.PutUint16(b, uint16(link.VlanId)) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_VLAN_ID, b) if link.VlanProtocol != VLAN_PROTOCOL_UNKNOWN { data.AddRtAttr(nl.IFLA_VLAN_PROTOCOL, htons(uint16(link.VlanProtocol))) } case *Veth: data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) peer := data.AddRtAttr(nl.VETH_INFO_PEER, nil) nl.NewIfInfomsgChild(peer, unix.AF_UNSPEC) peer.AddRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName)) if base.TxQLen >= 0 { peer.AddRtAttr(unix.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) } if base.NumTxQueues > 0 { peer.AddRtAttr(unix.IFLA_NUM_TX_QUEUES, nl.Uint32Attr(uint32(base.NumTxQueues))) } if base.NumRxQueues > 0 { peer.AddRtAttr(unix.IFLA_NUM_RX_QUEUES, nl.Uint32Attr(uint32(base.NumRxQueues))) } if base.MTU > 0 { peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) } if link.PeerHardwareAddr != nil { peer.AddRtAttr(unix.IFLA_ADDRESS, []byte(link.PeerHardwareAddr)) } if link.PeerNamespace != nil { switch ns := link.PeerNamespace.(type) { case NsPid: val := nl.Uint32Attr(uint32(ns)) peer.AddRtAttr(unix.IFLA_NET_NS_PID, val) case NsFd: val := nl.Uint32Attr(uint32(ns)) peer.AddRtAttr(unix.IFLA_NET_NS_FD, val) } } case *Vxlan: addVxlanAttrs(link, linkInfo) case *Bond: addBondAttrs(link, linkInfo) case *IPVlan: data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode))) data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag))) case *IPVtap: data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode))) data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag))) case *Macvlan: if link.Mode != MACVLAN_MODE_DEFAULT { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[link.Mode])) } case *Macvtap: if link.Mode != MACVLAN_MODE_DEFAULT { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[link.Mode])) } case *Geneve: addGeneveAttrs(link, linkInfo) case *Gretap: addGretapAttrs(link, linkInfo) case *Iptun: addIptunAttrs(link, linkInfo) case *Ip6tnl: addIp6tnlAttrs(link, linkInfo) case *Sittun: addSittunAttrs(link, linkInfo) case *Gretun: addGretunAttrs(link, linkInfo) case *Vti: addVtiAttrs(link, linkInfo) case *Vrf: addVrfAttrs(link, linkInfo) case *Bridge: addBridgeAttrs(link, linkInfo) case *GTP: addGTPAttrs(link, linkInfo) case *Xfrmi: addXfrmiAttrs(link, linkInfo) case *IPoIB: addIPoIBAttrs(link, linkInfo) } req.AddData(linkInfo) _, err := req.Execute(unix.NETLINK_ROUTE, 0) if err != nil { return err } h.ensureIndex(base) // can't set master during create, so set it afterwards if base.MasterIndex != 0 { // TODO: verify MasterIndex is actually a bridge? return h.LinkSetMasterByIndex(link, base.MasterIndex) } return nil } // LinkDel deletes link device. Either Index or Name must be set in // the link object for it to be deleted. The other values are ignored. // Equivalent to: `ip link del $link` func LinkDel(link Link) error { return pkgHandle.LinkDel(link) } // LinkDel deletes link device. Either Index or Name must be set in // the link object for it to be deleted. The other values are ignored. // Equivalent to: `ip link del $link` func (h *Handle) LinkDel(link Link) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_DELLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func (h *Handle) linkByNameDump(name string) (Link, error) { links, err := h.LinkList() if err != nil { return nil, err } for _, link := range links { if link.Attrs().Name == name { return link, nil } } return nil, LinkNotFoundError{fmt.Errorf("Link %s not found", name)} } func (h *Handle) linkByAliasDump(alias string) (Link, error) { links, err := h.LinkList() if err != nil { return nil, err } for _, link := range links { if link.Attrs().Alias == alias { return link, nil } } return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)} } // LinkByName finds a link by name and returns a pointer to the object. func LinkByName(name string) (Link, error) { return pkgHandle.LinkByName(name) } // LinkByName finds a link by name and returns a pointer to the object. func (h *Handle) LinkByName(name string) (Link, error) { if h.lookupByDump { return h.linkByNameDump(name) } req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(msg) attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF)) req.AddData(attr) nameData := nl.NewRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(name)) req.AddData(nameData) link, err := execGetLink(req) if err == unix.EINVAL { // older kernels don't support looking up via IFLA_IFNAME // so fall back to dumping all links h.lookupByDump = true return h.linkByNameDump(name) } return link, err } // LinkByAlias finds a link by its alias and returns a pointer to the object. // If there are multiple links with the alias it returns the first one func LinkByAlias(alias string) (Link, error) { return pkgHandle.LinkByAlias(alias) } // LinkByAlias finds a link by its alias and returns a pointer to the object. // If there are multiple links with the alias it returns the first one func (h *Handle) LinkByAlias(alias string) (Link, error) { if h.lookupByDump { return h.linkByAliasDump(alias) } req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(msg) attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF)) req.AddData(attr) nameData := nl.NewRtAttr(unix.IFLA_IFALIAS, nl.ZeroTerminated(alias)) req.AddData(nameData) link, err := execGetLink(req) if err == unix.EINVAL { // older kernels don't support looking up via IFLA_IFALIAS // so fall back to dumping all links h.lookupByDump = true return h.linkByAliasDump(alias) } return link, err } // LinkByIndex finds a link by index and returns a pointer to the object. func LinkByIndex(index int) (Link, error) { return pkgHandle.LinkByIndex(index) } // LinkByIndex finds a link by index and returns a pointer to the object. func (h *Handle) LinkByIndex(index int) (Link, error) { req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(index) req.AddData(msg) attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF)) req.AddData(attr) return execGetLink(req) } func execGetLink(req *nl.NetlinkRequest) (Link, error) { msgs, err := req.Execute(unix.NETLINK_ROUTE, 0) if err != nil { if errno, ok := err.(syscall.Errno); ok { if errno == unix.ENODEV { return nil, LinkNotFoundError{fmt.Errorf("Link not found")} } } return nil, err } switch { case len(msgs) == 0: return nil, LinkNotFoundError{fmt.Errorf("Link not found")} case len(msgs) == 1: return LinkDeserialize(nil, msgs[0]) default: return nil, fmt.Errorf("More than one link found") } } // LinkDeserialize deserializes a raw message received from netlink into // a link object. func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { msg := nl.DeserializeIfInfomsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := NewLinkAttrs() base.Index = int(msg.Index) base.RawFlags = msg.Flags base.Flags = linkFlags(msg.Flags) base.EncapType = msg.EncapType() if msg.Flags&unix.IFF_PROMISC != 0 { base.Promisc = 1 } if msg.Flags&unix.IFF_ALLMULTI != 0 { base.Allmulti = 1 } if msg.Flags&unix.IFF_MULTICAST != 0 { base.Multi = 1 } var ( link Link stats32 *LinkStatistics32 stats64 *LinkStatistics64 linkType string linkSlave LinkSlave slaveType string ) for _, attr := range attrs { switch attr.Attr.Type { case unix.IFLA_LINKINFO: infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } for _, info := range infos { switch info.Attr.Type { case nl.IFLA_INFO_KIND: linkType = string(info.Value[:len(info.Value)-1]) switch linkType { case "dummy": link = &Dummy{} case "ifb": link = &Ifb{} case "bridge": link = &Bridge{} case "vlan": link = &Vlan{} case "veth": link = &Veth{} case "wireguard": link = &Wireguard{} case "vxlan": link = &Vxlan{} case "bond": link = &Bond{} case "ipvlan": link = &IPVlan{} case "ipvtap": link = &IPVtap{} case "macvlan": link = &Macvlan{} case "macvtap": link = &Macvtap{} case "geneve": link = &Geneve{} case "gretap": link = &Gretap{} case "ip6gretap": link = &Gretap{} case "ipip": link = &Iptun{} case "ip6tnl": link = &Ip6tnl{} case "sit": link = &Sittun{} case "gre": link = &Gretun{} case "ip6gre": link = &Gretun{} case "vti", "vti6": link = &Vti{} case "vrf": link = &Vrf{} case "gtp": link = >P{} case "xfrm": link = &Xfrmi{} case "tun": link = &Tuntap{} case "ipoib": link = &IPoIB{} case "can": link = &Can{} default: link = &GenericLink{LinkType: linkType} } case nl.IFLA_INFO_DATA: data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } switch linkType { case "vlan": parseVlanData(link, data) case "vxlan": parseVxlanData(link, data) case "bond": parseBondData(link, data) case "ipvlan": parseIPVlanData(link, data) case "ipvtap": parseIPVtapData(link, data) case "macvlan": parseMacvlanData(link, data) case "macvtap": parseMacvtapData(link, data) case "geneve": parseGeneveData(link, data) case "gretap": parseGretapData(link, data) case "ip6gretap": parseGretapData(link, data) case "ipip": parseIptunData(link, data) case "ip6tnl": parseIp6tnlData(link, data) case "sit": parseSittunData(link, data) case "gre": parseGretunData(link, data) case "ip6gre": parseGretunData(link, data) case "vti", "vti6": parseVtiData(link, data) case "vrf": parseVrfData(link, data) case "bridge": parseBridgeData(link, data) case "gtp": parseGTPData(link, data) case "xfrm": parseXfrmiData(link, data) case "tun": parseTuntapData(link, data) case "ipoib": parseIPoIBData(link, data) case "can": parseCanData(link, data) } case nl.IFLA_INFO_SLAVE_KIND: slaveType = string(info.Value[:len(info.Value)-1]) switch slaveType { case "bond": linkSlave = &BondSlave{} case "vrf": linkSlave = &VrfSlave{} } case nl.IFLA_INFO_SLAVE_DATA: switch slaveType { case "bond": data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } parseBondSlaveData(linkSlave, data) case "vrf": data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } parseVrfSlaveData(linkSlave, data) } } } case unix.IFLA_ADDRESS: var nonzero bool for _, b := range attr.Value { if b != 0 { nonzero = true } } if nonzero { base.HardwareAddr = attr.Value[:] } case unix.IFLA_IFNAME: base.Name = string(attr.Value[:len(attr.Value)-1]) case unix.IFLA_MTU: base.MTU = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_LINK: base.ParentIndex = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_MASTER: base.MasterIndex = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_TXQLEN: base.TxQLen = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_IFALIAS: base.Alias = string(attr.Value[:len(attr.Value)-1]) case unix.IFLA_STATS: stats32 = new(LinkStatistics32) if err := binary.Read(bytes.NewBuffer(attr.Value[:]), nl.NativeEndian(), stats32); err != nil { return nil, err } case unix.IFLA_STATS64: stats64 = new(LinkStatistics64) if err := binary.Read(bytes.NewBuffer(attr.Value[:]), nl.NativeEndian(), stats64); err != nil { return nil, err } case unix.IFLA_XDP: xdp, err := parseLinkXdp(attr.Value[:]) if err != nil { return nil, err } base.Xdp = xdp case unix.IFLA_PROTINFO | unix.NLA_F_NESTED: if hdr != nil && hdr.Type == unix.RTM_NEWLINK && msg.Family == unix.AF_BRIDGE { attrs, err := nl.ParseRouteAttr(attr.Value[:]) if err != nil { return nil, err } protinfo := parseProtinfo(attrs) base.Protinfo = &protinfo } case unix.IFLA_OPERSTATE: base.OperState = LinkOperState(uint8(attr.Value[0])) case unix.IFLA_PHYS_SWITCH_ID: base.PhysSwitchID = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_LINK_NETNSID: base.NetNsID = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_GSO_MAX_SIZE: base.GSOMaxSize = native.Uint32(attr.Value[0:4]) case unix.IFLA_GSO_MAX_SEGS: base.GSOMaxSegs = native.Uint32(attr.Value[0:4]) case unix.IFLA_VFINFO_LIST: data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } vfs, err := parseVfInfoList(data) if err != nil { return nil, err } base.Vfs = vfs case unix.IFLA_NUM_TX_QUEUES: base.NumTxQueues = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_NUM_RX_QUEUES: base.NumRxQueues = int(native.Uint32(attr.Value[0:4])) case unix.IFLA_GROUP: base.Group = native.Uint32(attr.Value[0:4]) } } if stats64 != nil { base.Statistics = (*LinkStatistics)(stats64) } else if stats32 != nil { base.Statistics = (*LinkStatistics)(stats32.to64()) } // Links that don't have IFLA_INFO_KIND are hardware devices if link == nil { link = &Device{} } *link.Attrs() = base link.Attrs().Slave = linkSlave // If the tuntap attributes are not updated by netlink due to // an older driver, use sysfs if link != nil && linkType == "tun" { tuntap := link.(*Tuntap) if tuntap.Mode == 0 { ifname := tuntap.Attrs().Name if flags, err := readSysPropAsInt64(ifname, "tun_flags"); err == nil { if flags&unix.IFF_TUN != 0 { tuntap.Mode = unix.IFF_TUN } else if flags&unix.IFF_TAP != 0 { tuntap.Mode = unix.IFF_TAP } tuntap.NonPersist = false if flags&unix.IFF_PERSIST == 0 { tuntap.NonPersist = true } } // The sysfs interface for owner/group returns -1 for root user, instead of returning 0. // So explicitly check for negative value, before assigning the owner uid/gid. if owner, err := readSysPropAsInt64(ifname, "owner"); err == nil && owner > 0 { tuntap.Owner = uint32(owner) } if group, err := readSysPropAsInt64(ifname, "group"); err == nil && group > 0 { tuntap.Group = uint32(group) } } } return link, nil } func readSysPropAsInt64(ifname, prop string) (int64, error) { fname := fmt.Sprintf("/sys/class/net/%s/%s", ifname, prop) contents, err := ioutil.ReadFile(fname) if err != nil { return 0, err } num, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 0, 64) if err == nil { return num, nil } return 0, err } // LinkList gets a list of link devices. // Equivalent to: `ip link show` func LinkList() ([]Link, error) { return pkgHandle.LinkList() } // LinkList gets a list of link devices. // Equivalent to: `ip link show` func (h *Handle) LinkList() ([]Link, error) { // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need // to get the message ourselves to parse link type. req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(msg) attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF)) req.AddData(attr) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK) if err != nil { return nil, err } var res []Link for _, m := range msgs { link, err := LinkDeserialize(nil, m) if err != nil { return nil, err } res = append(res, link) } return res, nil } // LinkUpdate is used to pass information back from LinkSubscribe() type LinkUpdate struct { nl.IfInfomsg Header unix.NlMsghdr Link } // LinkSubscribe takes a chan down which notifications will be sent // when links change. Close the 'done' chan to stop subscription. func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { return linkSubscribeAt(netns.None(), netns.None(), ch, done, nil, false) } // LinkSubscribeAt works like LinkSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { return linkSubscribeAt(ns, netns.None(), ch, done, nil, false) } // LinkSubscribeOptions contains a set of options to use with // LinkSubscribeWithOptions. type LinkSubscribeOptions struct { Namespace *netns.NsHandle ErrorCallback func(error) ListExisting bool } // LinkSubscribeWithOptions work like LinkSubscribe but enable to // provide additional options to modify the behavior. Currently, the // namespace can be provided as well as an error callback. func LinkSubscribeWithOptions(ch chan<- LinkUpdate, done <-chan struct{}, options LinkSubscribeOptions) error { if options.Namespace == nil { none := netns.None() options.Namespace = &none } return linkSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting) } func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error { s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_LINK) if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } if listExisting { req := pkgHandle.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(msg) if err := s.Send(req); err != nil { return err } } go func() { defer close(ch) for { msgs, from, err := s.Receive() if err != nil { if cberr != nil { cberr(fmt.Errorf("Receive failed: %v", err)) } return } if from.Pid != nl.PidKernel { if cberr != nil { cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) } continue } for _, m := range msgs { if m.Header.Type == unix.NLMSG_DONE { continue } if m.Header.Type == unix.NLMSG_ERROR { error := int32(native.Uint32(m.Data[0:4])) if error == 0 { continue } if cberr != nil { cberr(fmt.Errorf("error message: %v", syscall.Errno(-error))) } continue } ifmsg := nl.DeserializeIfInfomsg(m.Data) header := unix.NlMsghdr(m.Header) link, err := LinkDeserialize(&header, m.Data) if err != nil { if cberr != nil { cberr(err) } continue } ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link} } } }() return nil } func LinkSetHairpin(link Link, mode bool) error { return pkgHandle.LinkSetHairpin(link, mode) } func (h *Handle) LinkSetHairpin(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE) } func LinkSetGuard(link Link, mode bool) error { return pkgHandle.LinkSetGuard(link, mode) } func (h *Handle) LinkSetGuard(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD) } func LinkSetFastLeave(link Link, mode bool) error { return pkgHandle.LinkSetFastLeave(link, mode) } func (h *Handle) LinkSetFastLeave(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE) } func LinkSetLearning(link Link, mode bool) error { return pkgHandle.LinkSetLearning(link, mode) } func (h *Handle) LinkSetLearning(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING) } func LinkSetRootBlock(link Link, mode bool) error { return pkgHandle.LinkSetRootBlock(link, mode) } func (h *Handle) LinkSetRootBlock(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT) } func LinkSetFlood(link Link, mode bool) error { return pkgHandle.LinkSetFlood(link, mode) } func (h *Handle) LinkSetFlood(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD) } func LinkSetBrProxyArp(link Link, mode bool) error { return pkgHandle.LinkSetBrProxyArp(link, mode) } func (h *Handle) LinkSetBrProxyArp(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP) } func LinkSetBrProxyArpWiFi(link Link, mode bool) error { return pkgHandle.LinkSetBrProxyArpWiFi(link, mode) } func (h *Handle) LinkSetBrProxyArpWiFi(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP_WIFI) } func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_BRIDGE) msg.Index = int32(base.Index) req.AddData(msg) br := nl.NewRtAttr(unix.IFLA_PROTINFO|unix.NLA_F_NESTED, nil) br.AddRtAttr(attr, boolToByte(mode)) req.AddData(br) _, err := req.Execute(unix.NETLINK_ROUTE, 0) if err != nil { return err } return nil } // LinkSetTxQLen sets the transaction queue length for the link. // Equivalent to: `ip link set $link txqlen $qlen` func LinkSetTxQLen(link Link, qlen int) error { return pkgHandle.LinkSetTxQLen(link, qlen) } // LinkSetTxQLen sets the transaction queue length for the link. // Equivalent to: `ip link set $link txqlen $qlen` func (h *Handle) LinkSetTxQLen(link Link, qlen int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(qlen)) data := nl.NewRtAttr(unix.IFLA_TXQLEN, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetGroup sets the link group id which can be used to perform mass actions // with iproute2 as well use it as a reference in nft filters. // Equivalent to: `ip link set $link group $id` func LinkSetGroup(link Link, group int) error { return pkgHandle.LinkSetGroup(link, group) } // LinkSetGroup sets the link group id which can be used to perform mass actions // with iproute2 as well use it as a reference in nft filters. // Equivalent to: `ip link set $link group $id` func (h *Handle) LinkSetGroup(link Link, group int) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) b := make([]byte, 4) native.PutUint32(b, uint32(group)) data := nl.NewRtAttr(unix.IFLA_GROUP, b) req.AddData(data) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) { vlan := link.(*Vlan) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_VLAN_ID: vlan.VlanId = int(native.Uint16(datum.Value[0:2])) case nl.IFLA_VLAN_PROTOCOL: vlan.VlanProtocol = VlanProtocol(int(ntohs(datum.Value[0:2]))) } } } func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { vxlan := link.(*Vxlan) for _, datum := range data { // NOTE(vish): Apparently some messages can be sent with no value. // We special case GBP here to not change existing // functionality. It appears that GBP sends a datum.Value // of null. if len(datum.Value) == 0 && datum.Attr.Type != nl.IFLA_VXLAN_GBP { continue } switch datum.Attr.Type { case nl.IFLA_VXLAN_ID: vxlan.VxlanId = int(native.Uint32(datum.Value[0:4])) case nl.IFLA_VXLAN_LINK: vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4])) case nl.IFLA_VXLAN_LOCAL: vxlan.SrcAddr = net.IP(datum.Value[0:4]) case nl.IFLA_VXLAN_LOCAL6: vxlan.SrcAddr = net.IP(datum.Value[0:16]) case nl.IFLA_VXLAN_GROUP: vxlan.Group = net.IP(datum.Value[0:4]) case nl.IFLA_VXLAN_GROUP6: vxlan.Group = net.IP(datum.Value[0:16]) case nl.IFLA_VXLAN_TTL: vxlan.TTL = int(datum.Value[0]) case nl.IFLA_VXLAN_TOS: vxlan.TOS = int(datum.Value[0]) case nl.IFLA_VXLAN_LEARNING: vxlan.Learning = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_PROXY: vxlan.Proxy = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_RSC: vxlan.RSC = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_L2MISS: vxlan.L2miss = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_L3MISS: vxlan.L3miss = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_UDP_CSUM: vxlan.UDPCSum = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_UDP_ZERO_CSUM6_TX: vxlan.UDP6ZeroCSumTx = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_UDP_ZERO_CSUM6_RX: vxlan.UDP6ZeroCSumRx = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_GBP: vxlan.GBP = true case nl.IFLA_VXLAN_FLOWBASED: vxlan.FlowBased = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_AGEING: vxlan.Age = int(native.Uint32(datum.Value[0:4])) vxlan.NoAge = vxlan.Age == 0 case nl.IFLA_VXLAN_LIMIT: vxlan.Limit = int(native.Uint32(datum.Value[0:4])) case nl.IFLA_VXLAN_PORT: vxlan.Port = int(ntohs(datum.Value[0:2])) case nl.IFLA_VXLAN_PORT_RANGE: buf := bytes.NewBuffer(datum.Value[0:4]) var pr vxlanPortRange if binary.Read(buf, binary.BigEndian, &pr) != nil { vxlan.PortLow = int(pr.Lo) vxlan.PortHigh = int(pr.Hi) } } } } func parseBondData(link Link, data []syscall.NetlinkRouteAttr) { bond := link.(*Bond) for i := range data { switch data[i].Attr.Type { case nl.IFLA_BOND_MODE: bond.Mode = BondMode(data[i].Value[0]) case nl.IFLA_BOND_ACTIVE_SLAVE: bond.ActiveSlave = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_MIIMON: bond.Miimon = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_UPDELAY: bond.UpDelay = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_DOWNDELAY: bond.DownDelay = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_USE_CARRIER: bond.UseCarrier = int(data[i].Value[0]) case nl.IFLA_BOND_ARP_INTERVAL: bond.ArpInterval = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_ARP_IP_TARGET: bond.ArpIpTargets = parseBondArpIpTargets(data[i].Value) case nl.IFLA_BOND_ARP_VALIDATE: bond.ArpValidate = BondArpValidate(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_ARP_ALL_TARGETS: bond.ArpAllTargets = BondArpAllTargets(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_PRIMARY: bond.Primary = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_PRIMARY_RESELECT: bond.PrimaryReselect = BondPrimaryReselect(data[i].Value[0]) case nl.IFLA_BOND_FAIL_OVER_MAC: bond.FailOverMac = BondFailOverMac(data[i].Value[0]) case nl.IFLA_BOND_XMIT_HASH_POLICY: bond.XmitHashPolicy = BondXmitHashPolicy(data[i].Value[0]) case nl.IFLA_BOND_RESEND_IGMP: bond.ResendIgmp = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_NUM_PEER_NOTIF: bond.NumPeerNotif = int(data[i].Value[0]) case nl.IFLA_BOND_ALL_SLAVES_ACTIVE: bond.AllSlavesActive = int(data[i].Value[0]) case nl.IFLA_BOND_MIN_LINKS: bond.MinLinks = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_LP_INTERVAL: bond.LpInterval = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_PACKETS_PER_SLAVE: bond.PacketsPerSlave = int(native.Uint32(data[i].Value[0:4])) case nl.IFLA_BOND_AD_LACP_RATE: bond.LacpRate = BondLacpRate(data[i].Value[0]) case nl.IFLA_BOND_AD_SELECT: bond.AdSelect = BondAdSelect(data[i].Value[0]) case nl.IFLA_BOND_AD_INFO: // TODO: implement case nl.IFLA_BOND_AD_ACTOR_SYS_PRIO: bond.AdActorSysPrio = int(native.Uint16(data[i].Value[0:2])) case nl.IFLA_BOND_AD_USER_PORT_KEY: bond.AdUserPortKey = int(native.Uint16(data[i].Value[0:2])) case nl.IFLA_BOND_AD_ACTOR_SYSTEM: bond.AdActorSystem = net.HardwareAddr(data[i].Value[0:6]) case nl.IFLA_BOND_TLB_DYNAMIC_LB: bond.TlbDynamicLb = int(data[i].Value[0]) } } } func parseBondArpIpTargets(value []byte) []net.IP { data, err := nl.ParseRouteAttr(value) if err != nil { return nil } targets := []net.IP{} for i := range data { target := net.IP(data[i].Value) if ip := target.To4(); ip != nil { targets = append(targets, ip) continue } if ip := target.To16(); ip != nil { targets = append(targets, ip) } } return targets } func addBondSlaveAttrs(bondSlave *BondSlave, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_SLAVE_DATA, nil) data.AddRtAttr(nl.IFLA_BOND_SLAVE_STATE, nl.Uint8Attr(uint8(bondSlave.State))) data.AddRtAttr(nl.IFLA_BOND_SLAVE_MII_STATUS, nl.Uint8Attr(uint8(bondSlave.MiiStatus))) data.AddRtAttr(nl.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, nl.Uint32Attr(bondSlave.LinkFailureCount)) data.AddRtAttr(nl.IFLA_BOND_SLAVE_QUEUE_ID, nl.Uint16Attr(bondSlave.QueueId)) data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, nl.Uint16Attr(bondSlave.AggregatorId)) data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, nl.Uint8Attr(bondSlave.AdActorOperPortState)) data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, nl.Uint16Attr(bondSlave.AdPartnerOperPortState)) if mac := bondSlave.PermHardwareAddr; mac != nil { data.AddRtAttr(nl.IFLA_BOND_SLAVE_PERM_HWADDR, []byte(mac)) } } func parseBondSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) { bondSlave := slave.(*BondSlave) for i := range data { switch data[i].Attr.Type { case nl.IFLA_BOND_SLAVE_STATE: bondSlave.State = BondSlaveState(data[i].Value[0]) case nl.IFLA_BOND_SLAVE_MII_STATUS: bondSlave.MiiStatus = BondSlaveMiiStatus(data[i].Value[0]) case nl.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT: bondSlave.LinkFailureCount = native.Uint32(data[i].Value[0:4]) case nl.IFLA_BOND_SLAVE_PERM_HWADDR: bondSlave.PermHardwareAddr = net.HardwareAddr(data[i].Value[0:6]) case nl.IFLA_BOND_SLAVE_QUEUE_ID: bondSlave.QueueId = native.Uint16(data[i].Value[0:2]) case nl.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID: bondSlave.AggregatorId = native.Uint16(data[i].Value[0:2]) case nl.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE: bondSlave.AdActorOperPortState = uint8(data[i].Value[0]) case nl.IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE: bondSlave.AdPartnerOperPortState = native.Uint16(data[i].Value[0:2]) } } } func parseVrfSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) { vrfSlave := slave.(*VrfSlave) for i := range data { switch data[i].Attr.Type { case nl.IFLA_BOND_SLAVE_STATE: vrfSlave.Table = native.Uint32(data[i].Value[0:4]) } } } func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { ipv := link.(*IPVlan) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_IPVLAN_MODE: ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4])) case nl.IFLA_IPVLAN_FLAG: ipv.Flag = IPVlanFlag(native.Uint32(datum.Value[0:4])) } } } func parseIPVtapData(link Link, data []syscall.NetlinkRouteAttr) { ipv := link.(*IPVtap) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_IPVLAN_MODE: ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4])) case nl.IFLA_IPVLAN_FLAG: ipv.Flag = IPVlanFlag(native.Uint32(datum.Value[0:4])) } } } func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) { macv := link.(*Macvtap) parseMacvlanData(&macv.Macvlan, data) } func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) { macv := link.(*Macvlan) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_MACVLAN_MODE: switch native.Uint32(datum.Value[0:4]) { case nl.MACVLAN_MODE_PRIVATE: macv.Mode = MACVLAN_MODE_PRIVATE case nl.MACVLAN_MODE_VEPA: macv.Mode = MACVLAN_MODE_VEPA case nl.MACVLAN_MODE_BRIDGE: macv.Mode = MACVLAN_MODE_BRIDGE case nl.MACVLAN_MODE_PASSTHRU: macv.Mode = MACVLAN_MODE_PASSTHRU case nl.MACVLAN_MODE_SOURCE: macv.Mode = MACVLAN_MODE_SOURCE } case nl.IFLA_MACVLAN_MACADDR_COUNT: macv.MACAddrs = make([]net.HardwareAddr, 0, int(native.Uint32(datum.Value[0:4]))) case nl.IFLA_MACVLAN_MACADDR_DATA: macs, err := nl.ParseRouteAttr(datum.Value[:]) if err != nil { panic(fmt.Sprintf("failed to ParseRouteAttr for IFLA_MACVLAN_MACADDR_DATA: %v", err)) } for _, macDatum := range macs { macv.MACAddrs = append(macv.MACAddrs, net.HardwareAddr(macDatum.Value[0:6])) } } } } // copied from pkg/net_linux.go func linkFlags(rawFlags uint32) net.Flags { var f net.Flags if rawFlags&unix.IFF_UP != 0 { f |= net.FlagUp } if rawFlags&unix.IFF_BROADCAST != 0 { f |= net.FlagBroadcast } if rawFlags&unix.IFF_LOOPBACK != 0 { f |= net.FlagLoopback } if rawFlags&unix.IFF_POINTOPOINT != 0 { f |= net.FlagPointToPoint } if rawFlags&unix.IFF_MULTICAST != 0 { f |= net.FlagMulticast } return f } func addGeneveAttrs(geneve *Geneve, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if geneve.FlowBased { // In flow based mode, no other attributes need to be configured linkInfo.AddRtAttr(nl.IFLA_GENEVE_COLLECT_METADATA, boolAttr(geneve.FlowBased)) return } if ip := geneve.Remote; ip != nil { if ip4 := ip.To4(); ip4 != nil { data.AddRtAttr(nl.IFLA_GENEVE_REMOTE, ip.To4()) } else { data.AddRtAttr(nl.IFLA_GENEVE_REMOTE6, []byte(ip)) } } if geneve.ID != 0 { data.AddRtAttr(nl.IFLA_GENEVE_ID, nl.Uint32Attr(geneve.ID)) } if geneve.Dport != 0 { data.AddRtAttr(nl.IFLA_GENEVE_PORT, htons(geneve.Dport)) } if geneve.Ttl != 0 { data.AddRtAttr(nl.IFLA_GENEVE_TTL, nl.Uint8Attr(geneve.Ttl)) } if geneve.Tos != 0 { data.AddRtAttr(nl.IFLA_GENEVE_TOS, nl.Uint8Attr(geneve.Tos)) } } func parseGeneveData(link Link, data []syscall.NetlinkRouteAttr) { geneve := link.(*Geneve) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_GENEVE_ID: geneve.ID = native.Uint32(datum.Value[0:4]) case nl.IFLA_GENEVE_REMOTE, nl.IFLA_GENEVE_REMOTE6: geneve.Remote = datum.Value case nl.IFLA_GENEVE_PORT: geneve.Dport = ntohs(datum.Value[0:2]) case nl.IFLA_GENEVE_TTL: geneve.Ttl = uint8(datum.Value[0]) case nl.IFLA_GENEVE_TOS: geneve.Tos = uint8(datum.Value[0]) } } } func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if gretap.FlowBased { // In flow based mode, no other attributes need to be configured data.AddRtAttr(nl.IFLA_GRE_COLLECT_METADATA, boolAttr(gretap.FlowBased)) return } if ip := gretap.Local; ip != nil { if ip.To4() != nil { ip = ip.To4() } data.AddRtAttr(nl.IFLA_GRE_LOCAL, []byte(ip)) } if ip := gretap.Remote; ip != nil { if ip.To4() != nil { ip = ip.To4() } data.AddRtAttr(nl.IFLA_GRE_REMOTE, []byte(ip)) } if gretap.IKey != 0 { data.AddRtAttr(nl.IFLA_GRE_IKEY, htonl(gretap.IKey)) gretap.IFlags |= uint16(nl.GRE_KEY) } if gretap.OKey != 0 { data.AddRtAttr(nl.IFLA_GRE_OKEY, htonl(gretap.OKey)) gretap.OFlags |= uint16(nl.GRE_KEY) } data.AddRtAttr(nl.IFLA_GRE_IFLAGS, htons(gretap.IFlags)) data.AddRtAttr(nl.IFLA_GRE_OFLAGS, htons(gretap.OFlags)) if gretap.Link != 0 { data.AddRtAttr(nl.IFLA_GRE_LINK, nl.Uint32Attr(gretap.Link)) } data.AddRtAttr(nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(gretap.PMtuDisc)) data.AddRtAttr(nl.IFLA_GRE_TTL, nl.Uint8Attr(gretap.Ttl)) data.AddRtAttr(nl.IFLA_GRE_TOS, nl.Uint8Attr(gretap.Tos)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(gretap.EncapType)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(gretap.EncapFlags)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_SPORT, htons(gretap.EncapSport)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_DPORT, htons(gretap.EncapDport)) } func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) { gre := link.(*Gretap) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_GRE_OKEY: gre.IKey = ntohl(datum.Value[0:4]) case nl.IFLA_GRE_IKEY: gre.OKey = ntohl(datum.Value[0:4]) case nl.IFLA_GRE_LOCAL: gre.Local = net.IP(datum.Value) case nl.IFLA_GRE_REMOTE: gre.Remote = net.IP(datum.Value) case nl.IFLA_GRE_ENCAP_SPORT: gre.EncapSport = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_DPORT: gre.EncapDport = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_IFLAGS: gre.IFlags = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_OFLAGS: gre.OFlags = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_TTL: gre.Ttl = uint8(datum.Value[0]) case nl.IFLA_GRE_TOS: gre.Tos = uint8(datum.Value[0]) case nl.IFLA_GRE_PMTUDISC: gre.PMtuDisc = uint8(datum.Value[0]) case nl.IFLA_GRE_ENCAP_TYPE: gre.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_FLAGS: gre.EncapFlags = native.Uint16(datum.Value[0:2]) case nl.IFLA_GRE_COLLECT_METADATA: gre.FlowBased = true } } } func addGretunAttrs(gre *Gretun, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if ip := gre.Local; ip != nil { if ip.To4() != nil { ip = ip.To4() } data.AddRtAttr(nl.IFLA_GRE_LOCAL, []byte(ip)) } if ip := gre.Remote; ip != nil { if ip.To4() != nil { ip = ip.To4() } data.AddRtAttr(nl.IFLA_GRE_REMOTE, []byte(ip)) } if gre.IKey != 0 { data.AddRtAttr(nl.IFLA_GRE_IKEY, htonl(gre.IKey)) gre.IFlags |= uint16(nl.GRE_KEY) } if gre.OKey != 0 { data.AddRtAttr(nl.IFLA_GRE_OKEY, htonl(gre.OKey)) gre.OFlags |= uint16(nl.GRE_KEY) } data.AddRtAttr(nl.IFLA_GRE_IFLAGS, htons(gre.IFlags)) data.AddRtAttr(nl.IFLA_GRE_OFLAGS, htons(gre.OFlags)) if gre.Link != 0 { data.AddRtAttr(nl.IFLA_GRE_LINK, nl.Uint32Attr(gre.Link)) } data.AddRtAttr(nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(gre.PMtuDisc)) data.AddRtAttr(nl.IFLA_GRE_TTL, nl.Uint8Attr(gre.Ttl)) data.AddRtAttr(nl.IFLA_GRE_TOS, nl.Uint8Attr(gre.Tos)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(gre.EncapType)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(gre.EncapFlags)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_SPORT, htons(gre.EncapSport)) data.AddRtAttr(nl.IFLA_GRE_ENCAP_DPORT, htons(gre.EncapDport)) } func parseGretunData(link Link, data []syscall.NetlinkRouteAttr) { gre := link.(*Gretun) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_GRE_IKEY: gre.IKey = ntohl(datum.Value[0:4]) case nl.IFLA_GRE_OKEY: gre.OKey = ntohl(datum.Value[0:4]) case nl.IFLA_GRE_LOCAL: gre.Local = net.IP(datum.Value) case nl.IFLA_GRE_REMOTE: gre.Remote = net.IP(datum.Value) case nl.IFLA_GRE_IFLAGS: gre.IFlags = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_OFLAGS: gre.OFlags = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_TTL: gre.Ttl = uint8(datum.Value[0]) case nl.IFLA_GRE_TOS: gre.Tos = uint8(datum.Value[0]) case nl.IFLA_GRE_PMTUDISC: gre.PMtuDisc = uint8(datum.Value[0]) case nl.IFLA_GRE_ENCAP_TYPE: gre.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_FLAGS: gre.EncapFlags = native.Uint16(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_SPORT: gre.EncapSport = ntohs(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_DPORT: gre.EncapDport = ntohs(datum.Value[0:2]) } } } func addXdpAttrs(xdp *LinkXdp, req *nl.NetlinkRequest) { attrs := nl.NewRtAttr(unix.IFLA_XDP|unix.NLA_F_NESTED, nil) b := make([]byte, 4) native.PutUint32(b, uint32(xdp.Fd)) attrs.AddRtAttr(nl.IFLA_XDP_FD, b) if xdp.Flags != 0 { b := make([]byte, 4) native.PutUint32(b, xdp.Flags) attrs.AddRtAttr(nl.IFLA_XDP_FLAGS, b) } req.AddData(attrs) } func parseLinkXdp(data []byte) (*LinkXdp, error) { attrs, err := nl.ParseRouteAttr(data) if err != nil { return nil, err } xdp := &LinkXdp{} for _, attr := range attrs { switch attr.Attr.Type { case nl.IFLA_XDP_FD: xdp.Fd = int(native.Uint32(attr.Value[0:4])) case nl.IFLA_XDP_ATTACHED: xdp.AttachMode = uint32(attr.Value[0]) xdp.Attached = xdp.AttachMode != 0 case nl.IFLA_XDP_FLAGS: xdp.Flags = native.Uint32(attr.Value[0:4]) case nl.IFLA_XDP_PROG_ID: xdp.ProgId = native.Uint32(attr.Value[0:4]) } } return xdp, nil } func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) { if iptun.FlowBased { // In flow based mode, no other attributes need to be configured linkInfo.AddRtAttr(nl.IFLA_IPTUN_COLLECT_METADATA, boolAttr(iptun.FlowBased)) return } data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) ip := iptun.Local.To4() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_LOCAL, []byte(ip)) } ip = iptun.Remote.To4() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_REMOTE, []byte(ip)) } if iptun.Link != 0 { data.AddRtAttr(nl.IFLA_IPTUN_LINK, nl.Uint32Attr(iptun.Link)) } data.AddRtAttr(nl.IFLA_IPTUN_PMTUDISC, nl.Uint8Attr(iptun.PMtuDisc)) data.AddRtAttr(nl.IFLA_IPTUN_TTL, nl.Uint8Attr(iptun.Ttl)) data.AddRtAttr(nl.IFLA_IPTUN_TOS, nl.Uint8Attr(iptun.Tos)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_TYPE, nl.Uint16Attr(iptun.EncapType)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_FLAGS, nl.Uint16Attr(iptun.EncapFlags)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_SPORT, htons(iptun.EncapSport)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_DPORT, htons(iptun.EncapDport)) } func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) { iptun := link.(*Iptun) for _, datum := range data { // NOTE: same with vxlan, ip tunnel may also has null datum.Value if len(datum.Value) == 0 { continue } switch datum.Attr.Type { case nl.IFLA_IPTUN_LOCAL: iptun.Local = net.IP(datum.Value[0:4]) case nl.IFLA_IPTUN_REMOTE: iptun.Remote = net.IP(datum.Value[0:4]) case nl.IFLA_IPTUN_TTL: iptun.Ttl = uint8(datum.Value[0]) case nl.IFLA_IPTUN_TOS: iptun.Tos = uint8(datum.Value[0]) case nl.IFLA_IPTUN_PMTUDISC: iptun.PMtuDisc = uint8(datum.Value[0]) case nl.IFLA_IPTUN_ENCAP_SPORT: iptun.EncapSport = ntohs(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_DPORT: iptun.EncapDport = ntohs(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_TYPE: iptun.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_FLAGS: iptun.EncapFlags = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_COLLECT_METADATA: iptun.FlowBased = true } } } func addIp6tnlAttrs(ip6tnl *Ip6tnl, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if ip6tnl.Link != 0 { data.AddRtAttr(nl.IFLA_IPTUN_LINK, nl.Uint32Attr(ip6tnl.Link)) } ip := ip6tnl.Local.To16() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_LOCAL, []byte(ip)) } ip = ip6tnl.Remote.To16() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_REMOTE, []byte(ip)) } data.AddRtAttr(nl.IFLA_IPTUN_TTL, nl.Uint8Attr(ip6tnl.Ttl)) data.AddRtAttr(nl.IFLA_IPTUN_TOS, nl.Uint8Attr(ip6tnl.Tos)) data.AddRtAttr(nl.IFLA_IPTUN_FLAGS, nl.Uint32Attr(ip6tnl.Flags)) data.AddRtAttr(nl.IFLA_IPTUN_PROTO, nl.Uint8Attr(ip6tnl.Proto)) data.AddRtAttr(nl.IFLA_IPTUN_FLOWINFO, nl.Uint32Attr(ip6tnl.FlowInfo)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_LIMIT, nl.Uint8Attr(ip6tnl.EncapLimit)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_TYPE, nl.Uint16Attr(ip6tnl.EncapType)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_FLAGS, nl.Uint16Attr(ip6tnl.EncapFlags)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_SPORT, htons(ip6tnl.EncapSport)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_DPORT, htons(ip6tnl.EncapDport)) } func parseIp6tnlData(link Link, data []syscall.NetlinkRouteAttr) { ip6tnl := link.(*Ip6tnl) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_IPTUN_LOCAL: ip6tnl.Local = net.IP(datum.Value[:16]) case nl.IFLA_IPTUN_REMOTE: ip6tnl.Remote = net.IP(datum.Value[:16]) case nl.IFLA_IPTUN_TTL: ip6tnl.Ttl = datum.Value[0] case nl.IFLA_IPTUN_TOS: ip6tnl.Tos = datum.Value[0] case nl.IFLA_IPTUN_FLAGS: ip6tnl.Flags = native.Uint32(datum.Value[:4]) case nl.IFLA_IPTUN_PROTO: ip6tnl.Proto = datum.Value[0] case nl.IFLA_IPTUN_FLOWINFO: ip6tnl.FlowInfo = native.Uint32(datum.Value[:4]) case nl.IFLA_IPTUN_ENCAP_LIMIT: ip6tnl.EncapLimit = datum.Value[0] case nl.IFLA_IPTUN_ENCAP_TYPE: ip6tnl.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_FLAGS: ip6tnl.EncapFlags = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_SPORT: ip6tnl.EncapSport = ntohs(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_DPORT: ip6tnl.EncapDport = ntohs(datum.Value[0:2]) } } } func addSittunAttrs(sittun *Sittun, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if sittun.Link != 0 { data.AddRtAttr(nl.IFLA_IPTUN_LINK, nl.Uint32Attr(sittun.Link)) } ip := sittun.Local.To4() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_LOCAL, []byte(ip)) } ip = sittun.Remote.To4() if ip != nil { data.AddRtAttr(nl.IFLA_IPTUN_REMOTE, []byte(ip)) } if sittun.Ttl > 0 { // Would otherwise fail on 3.10 kernel data.AddRtAttr(nl.IFLA_IPTUN_TTL, nl.Uint8Attr(sittun.Ttl)) } data.AddRtAttr(nl.IFLA_IPTUN_PROTO, nl.Uint8Attr(sittun.Proto)) data.AddRtAttr(nl.IFLA_IPTUN_TOS, nl.Uint8Attr(sittun.Tos)) data.AddRtAttr(nl.IFLA_IPTUN_PMTUDISC, nl.Uint8Attr(sittun.PMtuDisc)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_LIMIT, nl.Uint8Attr(sittun.EncapLimit)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_TYPE, nl.Uint16Attr(sittun.EncapType)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_FLAGS, nl.Uint16Attr(sittun.EncapFlags)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_SPORT, htons(sittun.EncapSport)) data.AddRtAttr(nl.IFLA_IPTUN_ENCAP_DPORT, htons(sittun.EncapDport)) } func parseSittunData(link Link, data []syscall.NetlinkRouteAttr) { sittun := link.(*Sittun) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_IPTUN_LOCAL: sittun.Local = net.IP(datum.Value[0:4]) case nl.IFLA_IPTUN_REMOTE: sittun.Remote = net.IP(datum.Value[0:4]) case nl.IFLA_IPTUN_TTL: sittun.Ttl = datum.Value[0] case nl.IFLA_IPTUN_TOS: sittun.Tos = datum.Value[0] case nl.IFLA_IPTUN_PMTUDISC: sittun.PMtuDisc = datum.Value[0] case nl.IFLA_IPTUN_PROTO: sittun.Proto = datum.Value[0] case nl.IFLA_IPTUN_ENCAP_TYPE: sittun.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_FLAGS: sittun.EncapFlags = native.Uint16(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_SPORT: sittun.EncapSport = ntohs(datum.Value[0:2]) case nl.IFLA_IPTUN_ENCAP_DPORT: sittun.EncapDport = ntohs(datum.Value[0:2]) } } } func addVtiAttrs(vti *Vti, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) family := FAMILY_V4 if vti.Local.To4() == nil { family = FAMILY_V6 } var ip net.IP if family == FAMILY_V4 { ip = vti.Local.To4() } else { ip = vti.Local } if ip != nil { data.AddRtAttr(nl.IFLA_VTI_LOCAL, []byte(ip)) } if family == FAMILY_V4 { ip = vti.Remote.To4() } else { ip = vti.Remote } if ip != nil { data.AddRtAttr(nl.IFLA_VTI_REMOTE, []byte(ip)) } if vti.Link != 0 { data.AddRtAttr(nl.IFLA_VTI_LINK, nl.Uint32Attr(vti.Link)) } data.AddRtAttr(nl.IFLA_VTI_IKEY, htonl(vti.IKey)) data.AddRtAttr(nl.IFLA_VTI_OKEY, htonl(vti.OKey)) } func parseVtiData(link Link, data []syscall.NetlinkRouteAttr) { vti := link.(*Vti) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_VTI_LOCAL: vti.Local = net.IP(datum.Value) case nl.IFLA_VTI_REMOTE: vti.Remote = net.IP(datum.Value) case nl.IFLA_VTI_IKEY: vti.IKey = ntohl(datum.Value[0:4]) case nl.IFLA_VTI_OKEY: vti.OKey = ntohl(datum.Value[0:4]) } } } func addVrfAttrs(vrf *Vrf, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) b := make([]byte, 4) native.PutUint32(b, uint32(vrf.Table)) data.AddRtAttr(nl.IFLA_VRF_TABLE, b) } func parseVrfData(link Link, data []syscall.NetlinkRouteAttr) { vrf := link.(*Vrf) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_VRF_TABLE: vrf.Table = native.Uint32(datum.Value[0:4]) } } } func addBridgeAttrs(bridge *Bridge, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) if bridge.MulticastSnooping != nil { data.AddRtAttr(nl.IFLA_BR_MCAST_SNOOPING, boolToByte(*bridge.MulticastSnooping)) } if bridge.AgeingTime != nil { data.AddRtAttr(nl.IFLA_BR_AGEING_TIME, nl.Uint32Attr(*bridge.AgeingTime)) } if bridge.HelloTime != nil { data.AddRtAttr(nl.IFLA_BR_HELLO_TIME, nl.Uint32Attr(*bridge.HelloTime)) } if bridge.VlanFiltering != nil { data.AddRtAttr(nl.IFLA_BR_VLAN_FILTERING, boolToByte(*bridge.VlanFiltering)) } } func parseBridgeData(bridge Link, data []syscall.NetlinkRouteAttr) { br := bridge.(*Bridge) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_BR_AGEING_TIME: ageingTime := native.Uint32(datum.Value[0:4]) br.AgeingTime = &ageingTime case nl.IFLA_BR_HELLO_TIME: helloTime := native.Uint32(datum.Value[0:4]) br.HelloTime = &helloTime case nl.IFLA_BR_MCAST_SNOOPING: mcastSnooping := datum.Value[0] == 1 br.MulticastSnooping = &mcastSnooping case nl.IFLA_BR_VLAN_FILTERING: vlanFiltering := datum.Value[0] == 1 br.VlanFiltering = &vlanFiltering } } } func addGTPAttrs(gtp *GTP, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_GTP_FD0, nl.Uint32Attr(uint32(gtp.FD0))) data.AddRtAttr(nl.IFLA_GTP_FD1, nl.Uint32Attr(uint32(gtp.FD1))) data.AddRtAttr(nl.IFLA_GTP_PDP_HASHSIZE, nl.Uint32Attr(131072)) if gtp.Role != nl.GTP_ROLE_GGSN { data.AddRtAttr(nl.IFLA_GTP_ROLE, nl.Uint32Attr(uint32(gtp.Role))) } } func parseGTPData(link Link, data []syscall.NetlinkRouteAttr) { gtp := link.(*GTP) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_GTP_FD0: gtp.FD0 = int(native.Uint32(datum.Value)) case nl.IFLA_GTP_FD1: gtp.FD1 = int(native.Uint32(datum.Value)) case nl.IFLA_GTP_PDP_HASHSIZE: gtp.PDPHashsize = int(native.Uint32(datum.Value)) case nl.IFLA_GTP_ROLE: gtp.Role = int(native.Uint32(datum.Value)) } } } func parseVfInfoList(data []syscall.NetlinkRouteAttr) ([]VfInfo, error) { var vfs []VfInfo for i, element := range data { if element.Attr.Type != nl.IFLA_VF_INFO { return nil, fmt.Errorf("Incorrect element type in vf info list: %d", element.Attr.Type) } vfAttrs, err := nl.ParseRouteAttr(element.Value) if err != nil { return nil, err } vfs = append(vfs, parseVfInfo(vfAttrs, i)) } return vfs, nil } func parseVfInfo(data []syscall.NetlinkRouteAttr, id int) VfInfo { vf := VfInfo{ID: id} for _, element := range data { switch element.Attr.Type { case nl.IFLA_VF_MAC: mac := nl.DeserializeVfMac(element.Value[:]) vf.Mac = mac.Mac[:6] case nl.IFLA_VF_VLAN: vl := nl.DeserializeVfVlan(element.Value[:]) vf.Vlan = int(vl.Vlan) vf.Qos = int(vl.Qos) case nl.IFLA_VF_TX_RATE: txr := nl.DeserializeVfTxRate(element.Value[:]) vf.TxRate = int(txr.Rate) case nl.IFLA_VF_SPOOFCHK: sp := nl.DeserializeVfSpoofchk(element.Value[:]) vf.Spoofchk = sp.Setting != 0 case nl.IFLA_VF_LINK_STATE: ls := nl.DeserializeVfLinkState(element.Value[:]) vf.LinkState = ls.LinkState case nl.IFLA_VF_RATE: vfr := nl.DeserializeVfRate(element.Value[:]) vf.MaxTxRate = vfr.MaxTxRate vf.MinTxRate = vfr.MinTxRate case nl.IFLA_VF_STATS: vfstats := nl.DeserializeVfStats(element.Value[:]) vf.RxPackets = vfstats.RxPackets vf.TxPackets = vfstats.TxPackets vf.RxBytes = vfstats.RxBytes vf.TxBytes = vfstats.TxBytes vf.Multicast = vfstats.Multicast vf.Broadcast = vfstats.Broadcast vf.RxDropped = vfstats.RxDropped vf.TxDropped = vfstats.TxDropped case nl.IFLA_VF_RSS_QUERY_EN: result := nl.DeserializeVfRssQueryEn(element.Value) vf.RssQuery = result.Setting case nl.IFLA_VF_TRUST: result := nl.DeserializeVfTrust(element.Value) vf.Trust = result.Setting } } return vf } func addXfrmiAttrs(xfrmi *Xfrmi, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_XFRM_LINK, nl.Uint32Attr(uint32(xfrmi.ParentIndex))) data.AddRtAttr(nl.IFLA_XFRM_IF_ID, nl.Uint32Attr(xfrmi.Ifid)) } func parseXfrmiData(link Link, data []syscall.NetlinkRouteAttr) { xfrmi := link.(*Xfrmi) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_XFRM_LINK: xfrmi.ParentIndex = int(native.Uint32(datum.Value)) case nl.IFLA_XFRM_IF_ID: xfrmi.Ifid = native.Uint32(datum.Value) } } } // LinkSetBondSlave add slave to bond link via ioctl interface. func LinkSetBondSlave(link Link, master *Bond) error { fd, err := getSocketUDP() if err != nil { return err } defer syscall.Close(fd) ifreq := newIocltSlaveReq(link.Attrs().Name, master.Attrs().Name) _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), unix.SIOCBONDENSLAVE, uintptr(unsafe.Pointer(ifreq))) if errno != 0 { return fmt.Errorf("Failed to enslave %q to %q, errno=%v", link.Attrs().Name, master.Attrs().Name, errno) } return nil } // LinkSetBondSlaveQueueId modify bond slave queue-id. func (h *Handle) LinkSetBondSlaveQueueId(link Link, queueId uint16) error { base := link.Attrs() h.ensureIndex(base) req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg.Index = int32(base.Index) req.AddData(msg) linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_SLAVE_DATA, nil) data.AddRtAttr(nl.IFLA_BOND_SLAVE_QUEUE_ID, nl.Uint16Attr(queueId)) req.AddData(linkInfo) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // LinkSetBondSlaveQueueId modify bond slave queue-id. func LinkSetBondSlaveQueueId(link Link, queueId uint16) error { return pkgHandle.LinkSetBondSlaveQueueId(link, queueId) } func vethStatsSerialize(stats ethtoolStats) ([]byte, error) { statsSize := int(unsafe.Sizeof(stats)) + int(stats.nStats)*int(unsafe.Sizeof(uint64(0))) b := make([]byte, 0, statsSize) buf := bytes.NewBuffer(b) err := binary.Write(buf, nl.NativeEndian(), stats) return buf.Bytes()[:statsSize], err } type vethEthtoolStats struct { Cmd uint32 NStats uint32 Peer uint64 // Newer kernels have XDP stats in here, but we only care // to extract the peer ifindex here. } func vethStatsDeserialize(b []byte) (vethEthtoolStats, error) { var stats = vethEthtoolStats{} err := binary.Read(bytes.NewReader(b), nl.NativeEndian(), &stats) return stats, err } // VethPeerIndex get veth peer index. func VethPeerIndex(link *Veth) (int, error) { fd, err := getSocketUDP() if err != nil { return -1, err } defer syscall.Close(fd) ifreq, sSet := newIocltStringSetReq(link.Name) _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), SIOCETHTOOL, uintptr(unsafe.Pointer(ifreq))) if errno != 0 { return -1, fmt.Errorf("SIOCETHTOOL request for %q failed, errno=%v", link.Attrs().Name, errno) } stats := ethtoolStats{ cmd: ETHTOOL_GSTATS, nStats: sSet.data[0], } buffer, err := vethStatsSerialize(stats) if err != nil { return -1, err } ifreq.Data = uintptr(unsafe.Pointer(&buffer[0])) _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), SIOCETHTOOL, uintptr(unsafe.Pointer(ifreq))) if errno != 0 { return -1, fmt.Errorf("SIOCETHTOOL request for %q failed, errno=%v", link.Attrs().Name, errno) } vstats, err := vethStatsDeserialize(buffer) if err != nil { return -1, err } return int(vstats.Peer), nil } func parseTuntapData(link Link, data []syscall.NetlinkRouteAttr) { tuntap := link.(*Tuntap) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_TUN_OWNER: tuntap.Owner = native.Uint32(datum.Value) case nl.IFLA_TUN_GROUP: tuntap.Group = native.Uint32(datum.Value) case nl.IFLA_TUN_TYPE: tuntap.Mode = TuntapMode(uint8(datum.Value[0])) case nl.IFLA_TUN_PERSIST: tuntap.NonPersist = false if uint8(datum.Value[0]) == 0 { tuntap.NonPersist = true } } } } func parseIPoIBData(link Link, data []syscall.NetlinkRouteAttr) { ipoib := link.(*IPoIB) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_IPOIB_PKEY: ipoib.Pkey = uint16(native.Uint16(datum.Value)) case nl.IFLA_IPOIB_MODE: ipoib.Mode = IPoIBMode(native.Uint16(datum.Value)) case nl.IFLA_IPOIB_UMCAST: ipoib.Umcast = uint16(native.Uint16(datum.Value)) } } } func parseCanData(link Link, data []syscall.NetlinkRouteAttr) { can := link.(*Can) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_CAN_BITTIMING: can.BitRate = native.Uint32(datum.Value) can.SamplePoint = native.Uint32(datum.Value[4:]) can.TimeQuanta = native.Uint32(datum.Value[8:]) can.PropagationSegment = native.Uint32(datum.Value[12:]) can.PhaseSegment1 = native.Uint32(datum.Value[16:]) can.PhaseSegment2 = native.Uint32(datum.Value[20:]) can.SyncJumpWidth = native.Uint32(datum.Value[24:]) can.BitRatePreScaler = native.Uint32(datum.Value[28:]) case nl.IFLA_CAN_BITTIMING_CONST: can.Name = string(datum.Value[:16]) can.TimeSegment1Min = native.Uint32(datum.Value[16:]) can.TimeSegment1Max = native.Uint32(datum.Value[20:]) can.TimeSegment2Min = native.Uint32(datum.Value[24:]) can.TimeSegment2Max = native.Uint32(datum.Value[28:]) can.SyncJumpWidthMax = native.Uint32(datum.Value[32:]) can.BitRatePreScalerMin = native.Uint32(datum.Value[36:]) can.BitRatePreScalerMax = native.Uint32(datum.Value[40:]) can.BitRatePreScalerInc = native.Uint32(datum.Value[44:]) case nl.IFLA_CAN_CLOCK: can.ClockFrequency = native.Uint32(datum.Value) case nl.IFLA_CAN_STATE: can.State = native.Uint32(datum.Value) case nl.IFLA_CAN_CTRLMODE: can.Mask = native.Uint32(datum.Value) can.Flags = native.Uint32(datum.Value[4:]) case nl.IFLA_CAN_BERR_COUNTER: can.TxError = native.Uint16(datum.Value) can.RxError = native.Uint16(datum.Value[2:]) case nl.IFLA_CAN_RESTART_MS: can.RestartMs = native.Uint32(datum.Value) case nl.IFLA_CAN_DATA_BITTIMING_CONST: case nl.IFLA_CAN_RESTART: case nl.IFLA_CAN_DATA_BITTIMING: case nl.IFLA_CAN_TERMINATION: case nl.IFLA_CAN_TERMINATION_CONST: case nl.IFLA_CAN_BITRATE_CONST: case nl.IFLA_CAN_DATA_BITRATE_CONST: case nl.IFLA_CAN_BITRATE_MAX: } } } func addIPoIBAttrs(ipoib *IPoIB, linkInfo *nl.RtAttr) { data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data.AddRtAttr(nl.IFLA_IPOIB_PKEY, nl.Uint16Attr(uint16(ipoib.Pkey))) data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode))) data.AddRtAttr(nl.IFLA_IPOIB_UMCAST, nl.Uint16Attr(uint16(ipoib.Umcast))) } ================================================ FILE: vendor/github.com/vishvananda/netlink/link_tuntap_linux.go ================================================ package netlink // ideally golang.org/x/sys/unix would define IfReq but it only has // IFNAMSIZ, hence this minimalistic implementation const ( SizeOfIfReq = 40 IFNAMSIZ = 16 ) type ifReq struct { Name [IFNAMSIZ]byte Flags uint16 pad [SizeOfIfReq - IFNAMSIZ - 2]byte } ================================================ FILE: vendor/github.com/vishvananda/netlink/neigh.go ================================================ package netlink import ( "fmt" "net" ) // Neigh represents a link layer neighbor from netlink. type Neigh struct { LinkIndex int Family int State int Type int Flags int IP net.IP HardwareAddr net.HardwareAddr LLIPAddr net.IP //Used in the case of NHRP Vlan int VNI int MasterIndex int } // String returns $ip/$hwaddr $label func (neigh *Neigh) String() string { return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr) } // NeighUpdate is sent when a neighbor changes - type is RTM_NEWNEIGH or RTM_DELNEIGH. type NeighUpdate struct { Type uint16 Neigh } ================================================ FILE: vendor/github.com/vishvananda/netlink/neigh_linux.go ================================================ package netlink import ( "fmt" "net" "syscall" "unsafe" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) const ( NDA_UNSPEC = iota NDA_DST NDA_LLADDR NDA_CACHEINFO NDA_PROBES NDA_VLAN NDA_PORT NDA_VNI NDA_IFINDEX NDA_MASTER NDA_LINK_NETNSID NDA_SRC_VNI NDA_MAX = NDA_SRC_VNI ) // Neighbor Cache Entry States. const ( NUD_NONE = 0x00 NUD_INCOMPLETE = 0x01 NUD_REACHABLE = 0x02 NUD_STALE = 0x04 NUD_DELAY = 0x08 NUD_PROBE = 0x10 NUD_FAILED = 0x20 NUD_NOARP = 0x40 NUD_PERMANENT = 0x80 ) // Neighbor Flags const ( NTF_USE = 0x01 NTF_SELF = 0x02 NTF_MASTER = 0x04 NTF_PROXY = 0x08 NTF_EXT_LEARNED = 0x10 NTF_ROUTER = 0x80 ) // Ndmsg is for adding, removing or receiving information about a neighbor table entry type Ndmsg struct { Family uint8 Index uint32 State uint16 Flags uint8 Type uint8 } func deserializeNdmsg(b []byte) *Ndmsg { var dummy Ndmsg return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0])) } func (msg *Ndmsg) Serialize() []byte { return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:] } func (msg *Ndmsg) Len() int { return int(unsafe.Sizeof(*msg)) } // NeighAdd will add an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh add ....` func NeighAdd(neigh *Neigh) error { return pkgHandle.NeighAdd(neigh) } // NeighAdd will add an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh add ....` func (h *Handle) NeighAdd(neigh *Neigh) error { return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL) } // NeighSet will add or replace an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh replace....` func NeighSet(neigh *Neigh) error { return pkgHandle.NeighSet(neigh) } // NeighSet will add or replace an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh replace....` func (h *Handle) NeighSet(neigh *Neigh) error { return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE) } // NeighAppend will append an entry to FDB // Equivalent to: `bridge fdb append...` func NeighAppend(neigh *Neigh) error { return pkgHandle.NeighAppend(neigh) } // NeighAppend will append an entry to FDB // Equivalent to: `bridge fdb append...` func (h *Handle) NeighAppend(neigh *Neigh) error { return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND) } // NeighAppend will append an entry to FDB // Equivalent to: `bridge fdb append...` func neighAdd(neigh *Neigh, mode int) error { return pkgHandle.neighAdd(neigh, mode) } // NeighAppend will append an entry to FDB // Equivalent to: `bridge fdb append...` func (h *Handle) neighAdd(neigh *Neigh, mode int) error { req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK) return neighHandle(neigh, req) } // NeighDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` func NeighDel(neigh *Neigh) error { return pkgHandle.NeighDel(neigh) } // NeighDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` func (h *Handle) NeighDel(neigh *Neigh) error { req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK) return neighHandle(neigh, req) } func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { var family int if neigh.Family > 0 { family = neigh.Family } else { family = nl.GetIPFamily(neigh.IP) } msg := Ndmsg{ Family: uint8(family), Index: uint32(neigh.LinkIndex), State: uint16(neigh.State), Type: uint8(neigh.Type), Flags: uint8(neigh.Flags), } req.AddData(&msg) ipData := neigh.IP.To4() if ipData == nil { ipData = neigh.IP.To16() } dstData := nl.NewRtAttr(NDA_DST, ipData) req.AddData(dstData) if neigh.LLIPAddr != nil { llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4()) req.AddData(llIPData) } else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) req.AddData(hwData) } if neigh.Vlan != 0 { vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan))) req.AddData(vlanData) } if neigh.VNI != 0 { vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI))) req.AddData(vniData) } if neigh.MasterIndex != 0 { masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex))) req.AddData(masterData) } _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // NeighList returns a list of IP-MAC mappings in the system (ARP table). // Equivalent to: `ip neighbor show`. // The list can be filtered by link and ip family. func NeighList(linkIndex, family int) ([]Neigh, error) { return pkgHandle.NeighList(linkIndex, family) } // NeighProxyList returns a list of neighbor proxies in the system. // Equivalent to: `ip neighbor show proxy`. // The list can be filtered by link and ip family. func NeighProxyList(linkIndex, family int) ([]Neigh, error) { return pkgHandle.NeighProxyList(linkIndex, family) } // NeighList returns a list of IP-MAC mappings in the system (ARP table). // Equivalent to: `ip neighbor show`. // The list can be filtered by link and ip family. func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) { return h.NeighListExecute(Ndmsg{ Family: uint8(family), Index: uint32(linkIndex), }) } // NeighProxyList returns a list of neighbor proxies in the system. // Equivalent to: `ip neighbor show proxy`. // The list can be filtered by link, ip family. func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) { return h.NeighListExecute(Ndmsg{ Family: uint8(family), Index: uint32(linkIndex), Flags: NTF_PROXY, }) } // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state. func NeighListExecute(msg Ndmsg) ([]Neigh, error) { return pkgHandle.NeighListExecute(msg) } // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state. func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) { req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP) req.AddData(&msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH) if err != nil { return nil, err } var res []Neigh for _, m := range msgs { ndm := deserializeNdmsg(m) if msg.Index != 0 && ndm.Index != msg.Index { // Ignore messages from other interfaces continue } if msg.Family != 0 && ndm.Family != msg.Family { continue } if msg.State != 0 && ndm.State != msg.State { continue } if msg.Type != 0 && ndm.Type != msg.Type { continue } if msg.Flags != 0 && ndm.Flags != msg.Flags { continue } neigh, err := NeighDeserialize(m) if err != nil { continue } res = append(res, *neigh) } return res, nil } func NeighDeserialize(m []byte) (*Neigh, error) { msg := deserializeNdmsg(m) neigh := Neigh{ LinkIndex: int(msg.Index), Family: int(msg.Family), State: int(msg.State), Type: int(msg.Type), Flags: int(msg.Flags), } attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } for _, attr := range attrs { switch attr.Attr.Type { case NDA_DST: neigh.IP = net.IP(attr.Value) case NDA_LLADDR: // BUG: Is this a bug in the netlink library? // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) attrLen := attr.Attr.Len - unix.SizeofRtAttr if attrLen == 4 { neigh.LLIPAddr = net.IP(attr.Value) } else if attrLen == 16 { // Can be IPv6 or FireWire HWAddr link, err := LinkByIndex(neigh.LinkIndex) if err == nil && link.Attrs().EncapType == "tunnel6" { neigh.IP = net.IP(attr.Value) } else { neigh.HardwareAddr = net.HardwareAddr(attr.Value) } } else { neigh.HardwareAddr = net.HardwareAddr(attr.Value) } case NDA_VLAN: neigh.Vlan = int(native.Uint16(attr.Value[0:2])) case NDA_VNI: neigh.VNI = int(native.Uint32(attr.Value[0:4])) case NDA_MASTER: neigh.MasterIndex = int(native.Uint32(attr.Value[0:4])) } } return &neigh, nil } // NeighSubscribe takes a chan down which notifications will be sent // when neighbors are added or deleted. Close the 'done' chan to stop subscription. func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error { return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false) } // NeighSubscribeAt works like NeighSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error { return neighSubscribeAt(ns, netns.None(), ch, done, nil, false) } // NeighSubscribeOptions contains a set of options to use with // NeighSubscribeWithOptions. type NeighSubscribeOptions struct { Namespace *netns.NsHandle ErrorCallback func(error) ListExisting bool } // NeighSubscribeWithOptions work like NeighSubscribe but enable to // provide additional options to modify the behavior. Currently, the // namespace can be provided as well as an error callback. func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error { if options.Namespace == nil { none := netns.None() options.Namespace = &none } return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting) } func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error { s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH) makeRequest := func(family int) error { req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(family) req.AddData(infmsg) if err := s.Send(req); err != nil { return err } return nil } if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } if listExisting { if err := makeRequest(unix.AF_UNSPEC); err != nil { return err } // We have to wait for NLMSG_DONE before making AF_BRIDGE request } go func() { defer close(ch) for { msgs, from, err := s.Receive() if err != nil { if cberr != nil { cberr(err) } return } if from.Pid != nl.PidKernel { if cberr != nil { cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) } continue } for _, m := range msgs { if m.Header.Type == unix.NLMSG_DONE { if listExisting { // This will be called after handling AF_UNSPEC // list request, we have to wait for NLMSG_DONE // before making another request if err := makeRequest(unix.AF_BRIDGE); err != nil { if cberr != nil { cberr(err) } return } listExisting = false } continue } if m.Header.Type == unix.NLMSG_ERROR { error := int32(native.Uint32(m.Data[0:4])) if error == 0 { continue } if cberr != nil { cberr(syscall.Errno(-error)) } return } neigh, err := NeighDeserialize(m.Data) if err != nil { if cberr != nil { cberr(err) } return } ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh} } } }() return nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/netlink.go ================================================ // Package netlink provides a simple library for netlink. Netlink is // the interface a user-space program in linux uses to communicate with // the kernel. It can be used to add and remove interfaces, set up ip // addresses and routes, and confiugre ipsec. Netlink communication // requires elevated privileges, so in most cases this code needs to // be run as root. The low level primitives for netlink are contained // in the nl subpackage. This package attempts to provide a high-level // interface that is loosly modeled on the iproute2 cli. package netlink import ( "errors" "net" ) var ( // ErrNotImplemented is returned when a requested feature is not implemented. ErrNotImplemented = errors.New("not implemented") ) // ParseIPNet parses a string in ip/net format and returns a net.IPNet. // This is valuable because addresses in netlink are often IPNets and // ParseCIDR returns an IPNet with the IP part set to the base IP of the // range. func ParseIPNet(s string) (*net.IPNet, error) { ip, ipNet, err := net.ParseCIDR(s) if err != nil { return nil, err } ipNet.IP = ip return ipNet, nil } // NewIPNet generates an IPNet from an ip address using a netmask of 32 or 128. func NewIPNet(ip net.IP) *net.IPNet { if ip.To4() != nil { return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)} } return &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)} } ================================================ FILE: vendor/github.com/vishvananda/netlink/netlink_linux.go ================================================ package netlink import "github.com/vishvananda/netlink/nl" // Family type definitions const ( FAMILY_ALL = nl.FAMILY_ALL FAMILY_V4 = nl.FAMILY_V4 FAMILY_V6 = nl.FAMILY_V6 FAMILY_MPLS = nl.FAMILY_MPLS ) ================================================ FILE: vendor/github.com/vishvananda/netlink/netlink_unspecified.go ================================================ // +build !linux package netlink import "net" func LinkSetUp(link Link) error { return ErrNotImplemented } func LinkSetDown(link Link) error { return ErrNotImplemented } func LinkSetMTU(link Link, mtu int) error { return ErrNotImplemented } func LinkSetMaster(link Link, master Link) error { return ErrNotImplemented } func LinkSetNsPid(link Link, nspid int) error { return ErrNotImplemented } func LinkSetNsFd(link Link, fd int) error { return ErrNotImplemented } func LinkSetName(link Link, name string) error { return ErrNotImplemented } func LinkSetAlias(link Link, name string) error { return ErrNotImplemented } func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { return ErrNotImplemented } func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { return ErrNotImplemented } func LinkSetVfVlan(link Link, vf, vlan int) error { return ErrNotImplemented } func LinkSetVfVlanQos(link Link, vf, vlan, qos int) error { return ErrNotImplemented } func LinkSetVfTxRate(link Link, vf, rate int) error { return ErrNotImplemented } func LinkSetVfRate(link Link, vf, minRate, maxRate int) error { return ErrNotImplemented } func LinkSetNoMaster(link Link) error { return ErrNotImplemented } func LinkSetMasterByIndex(link Link, masterIndex int) error { return ErrNotImplemented } func LinkSetXdpFd(link Link, fd int) error { return ErrNotImplemented } func LinkSetXdpFdWithFlags(link Link, fd, flags int) error { return ErrNotImplemented } func LinkSetARPOff(link Link) error { return ErrNotImplemented } func LinkSetARPOn(link Link) error { return ErrNotImplemented } func LinkByName(name string) (Link, error) { return nil, ErrNotImplemented } func LinkByAlias(alias string) (Link, error) { return nil, ErrNotImplemented } func LinkByIndex(index int) (Link, error) { return nil, ErrNotImplemented } func LinkSetHairpin(link Link, mode bool) error { return ErrNotImplemented } func LinkSetGuard(link Link, mode bool) error { return ErrNotImplemented } func LinkSetFastLeave(link Link, mode bool) error { return ErrNotImplemented } func LinkSetLearning(link Link, mode bool) error { return ErrNotImplemented } func LinkSetRootBlock(link Link, mode bool) error { return ErrNotImplemented } func LinkSetFlood(link Link, mode bool) error { return ErrNotImplemented } func LinkSetTxQLen(link Link, qlen int) error { return ErrNotImplemented } func LinkAdd(link Link) error { return ErrNotImplemented } func LinkDel(link Link) error { return ErrNotImplemented } func SetHairpin(link Link, mode bool) error { return ErrNotImplemented } func SetGuard(link Link, mode bool) error { return ErrNotImplemented } func SetFastLeave(link Link, mode bool) error { return ErrNotImplemented } func SetLearning(link Link, mode bool) error { return ErrNotImplemented } func SetRootBlock(link Link, mode bool) error { return ErrNotImplemented } func SetFlood(link Link, mode bool) error { return ErrNotImplemented } func LinkList() ([]Link, error) { return nil, ErrNotImplemented } func AddrAdd(link Link, addr *Addr) error { return ErrNotImplemented } func AddrReplace(link Link, addr *Addr) error { return ErrNotImplemented } func AddrDel(link Link, addr *Addr) error { return ErrNotImplemented } func AddrList(link Link, family int) ([]Addr, error) { return nil, ErrNotImplemented } func RouteAdd(route *Route) error { return ErrNotImplemented } func RouteDel(route *Route) error { return ErrNotImplemented } func RouteList(link Link, family int) ([]Route, error) { return nil, ErrNotImplemented } func XfrmPolicyAdd(policy *XfrmPolicy) error { return ErrNotImplemented } func XfrmPolicyDel(policy *XfrmPolicy) error { return ErrNotImplemented } func XfrmPolicyList(family int) ([]XfrmPolicy, error) { return nil, ErrNotImplemented } func XfrmStateAdd(policy *XfrmState) error { return ErrNotImplemented } func XfrmStateDel(policy *XfrmState) error { return ErrNotImplemented } func XfrmStateList(family int) ([]XfrmState, error) { return nil, ErrNotImplemented } func NeighAdd(neigh *Neigh) error { return ErrNotImplemented } func NeighSet(neigh *Neigh) error { return ErrNotImplemented } func NeighAppend(neigh *Neigh) error { return ErrNotImplemented } func NeighDel(neigh *Neigh) error { return ErrNotImplemented } func NeighList(linkIndex, family int) ([]Neigh, error) { return nil, ErrNotImplemented } func NeighDeserialize(m []byte) (*Neigh, error) { return nil, ErrNotImplemented } func SocketGet(local, remote net.Addr) (*Socket, error) { return nil, ErrNotImplemented } ================================================ FILE: vendor/github.com/vishvananda/netlink/netns_linux.go ================================================ package netlink // Network namespace ID functions // // The kernel has a weird concept called the network namespace ID. // This is different from the file reference in proc (and any bind-mounted // namespaces, etc.) // // Instead, namespaces can be assigned a numeric ID at any time. Once set, // the ID is fixed. The ID can either be set manually by the user, or // automatically, triggered by certain kernel actions. The most common kernel // action that triggers namespace ID creation is moving one end of a veth pair // in to that namespace. import ( "fmt" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // These can be replaced by the values from sys/unix when it is next released. const ( _ = iota NETNSA_NSID NETNSA_PID NETNSA_FD ) // GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id). // Returns -1 if the namespace does not have an ID set. func (h *Handle) GetNetNsIdByPid(pid int) (int, error) { return h.getNetNsId(NETNSA_PID, uint32(pid)) } // GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id). // Returns -1 if the namespace does not have an ID set. func GetNetNsIdByPid(pid int) (int, error) { return pkgHandle.GetNetNsIdByPid(pid) } // SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id). // The ID can only be set for namespaces without an ID already set. func (h *Handle) SetNetNsIdByPid(pid, nsid int) error { return h.setNetNsId(NETNSA_PID, uint32(pid), uint32(nsid)) } // SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id). // The ID can only be set for namespaces without an ID already set. func SetNetNsIdByPid(pid, nsid int) error { return pkgHandle.SetNetNsIdByPid(pid, nsid) } // GetNetNsIdByFd looks up the network namespace ID for a given fd. // fd must be an open file descriptor to a namespace file. // Returns -1 if the namespace does not have an ID set. func (h *Handle) GetNetNsIdByFd(fd int) (int, error) { return h.getNetNsId(NETNSA_FD, uint32(fd)) } // GetNetNsIdByFd looks up the network namespace ID for a given fd. // fd must be an open file descriptor to a namespace file. // Returns -1 if the namespace does not have an ID set. func GetNetNsIdByFd(fd int) (int, error) { return pkgHandle.GetNetNsIdByFd(fd) } // SetNetNSIdByFd sets the ID of the network namespace for a given fd. // fd must be an open file descriptor to a namespace file. // The ID can only be set for namespaces without an ID already set. func (h *Handle) SetNetNsIdByFd(fd, nsid int) error { return h.setNetNsId(NETNSA_FD, uint32(fd), uint32(nsid)) } // SetNetNSIdByFd sets the ID of the network namespace for a given fd. // fd must be an open file descriptor to a namespace file. // The ID can only be set for namespaces without an ID already set. func SetNetNsIdByFd(fd, nsid int) error { return pkgHandle.SetNetNsIdByFd(fd, nsid) } // getNetNsId requests the netnsid for a given type-val pair // type should be either NETNSA_PID or NETNSA_FD func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) { req := h.newNetlinkRequest(unix.RTM_GETNSID, unix.NLM_F_REQUEST) rtgen := nl.NewRtGenMsg() req.AddData(rtgen) b := make([]byte, 4, 4) native.PutUint32(b, val) attr := nl.NewRtAttr(attrType, b) req.AddData(attr) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID) if err != nil { return 0, err } for _, m := range msgs { msg := nl.DeserializeRtGenMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return 0, err } for _, attr := range attrs { switch attr.Attr.Type { case NETNSA_NSID: return int(int32(native.Uint32(attr.Value))), nil } } } return 0, fmt.Errorf("unexpected empty result") } // setNetNsId sets the netnsid for a given type-val pair // type should be either NETNSA_PID or NETNSA_FD // The ID can only be set for namespaces without an ID already set func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error { req := h.newNetlinkRequest(unix.RTM_NEWNSID, unix.NLM_F_REQUEST|unix.NLM_F_ACK) rtgen := nl.NewRtGenMsg() req.AddData(rtgen) b := make([]byte, 4, 4) native.PutUint32(b, val) attr := nl.NewRtAttr(attrType, b) req.AddData(attr) b1 := make([]byte, 4, 4) native.PutUint32(b1, newnsid) attr1 := nl.NewRtAttr(NETNSA_NSID, b1) req.AddData(attr1) _, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID) return err } ================================================ FILE: vendor/github.com/vishvananda/netlink/netns_unspecified.go ================================================ // +build !linux package netlink func GetNetNsIdByPid(pid int) (int, error) { return 0, ErrNotImplemented } func SetNetNsIdByPid(pid, nsid int) error { return ErrNotImplemented } func GetNetNsIdByFd(fd int) (int, error) { return 0, ErrNotImplemented } func SetNetNsIdByFd(fd, nsid int) error { return ErrNotImplemented } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/addr_linux.go ================================================ package nl import ( "unsafe" "golang.org/x/sys/unix" ) type IfAddrmsg struct { unix.IfAddrmsg } func NewIfAddrmsg(family int) *IfAddrmsg { return &IfAddrmsg{ IfAddrmsg: unix.IfAddrmsg{ Family: uint8(family), }, } } // struct ifaddrmsg { // __u8 ifa_family; // __u8 ifa_prefixlen; /* The prefix length */ // __u8 ifa_flags; /* Flags */ // __u8 ifa_scope; /* Address scope */ // __u32 ifa_index; /* Link index */ // }; // type IfAddrmsg struct { // Family uint8 // Prefixlen uint8 // Flags uint8 // Scope uint8 // Index uint32 // } // SizeofIfAddrmsg = 0x8 func DeserializeIfAddrmsg(b []byte) *IfAddrmsg { return (*IfAddrmsg)(unsafe.Pointer(&b[0:unix.SizeofIfAddrmsg][0])) } func (msg *IfAddrmsg) Serialize() []byte { return (*(*[unix.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:] } func (msg *IfAddrmsg) Len() int { return unix.SizeofIfAddrmsg } // struct ifa_cacheinfo { // __u32 ifa_prefered; // __u32 ifa_valid; // __u32 cstamp; /* created timestamp, hundredths of seconds */ // __u32 tstamp; /* updated timestamp, hundredths of seconds */ // }; type IfaCacheInfo struct { unix.IfaCacheinfo } func (msg *IfaCacheInfo) Len() int { return unix.SizeofIfaCacheinfo } func DeserializeIfaCacheInfo(b []byte) *IfaCacheInfo { return (*IfaCacheInfo)(unsafe.Pointer(&b[0:unix.SizeofIfaCacheinfo][0])) } func (msg *IfaCacheInfo) Serialize() []byte { return (*(*[unix.SizeofIfaCacheinfo]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/bridge_linux.go ================================================ package nl import ( "fmt" "unsafe" ) const ( SizeofBridgeVlanInfo = 0x04 ) /* Bridge Flags */ const ( BRIDGE_FLAGS_MASTER = iota + 1 /* Bridge command to/from master */ BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */ ) /* Bridge management nested attributes * [IFLA_AF_SPEC] = { * [IFLA_BRIDGE_FLAGS] * [IFLA_BRIDGE_MODE] * [IFLA_BRIDGE_VLAN_INFO] * } */ const ( IFLA_BRIDGE_FLAGS = iota IFLA_BRIDGE_MODE IFLA_BRIDGE_VLAN_INFO ) const ( BRIDGE_VLAN_INFO_MASTER = 1 << iota BRIDGE_VLAN_INFO_PVID BRIDGE_VLAN_INFO_UNTAGGED BRIDGE_VLAN_INFO_RANGE_BEGIN BRIDGE_VLAN_INFO_RANGE_END ) // struct bridge_vlan_info { // __u16 flags; // __u16 vid; // }; type BridgeVlanInfo struct { Flags uint16 Vid uint16 } func (b *BridgeVlanInfo) Serialize() []byte { return (*(*[SizeofBridgeVlanInfo]byte)(unsafe.Pointer(b)))[:] } func DeserializeBridgeVlanInfo(b []byte) *BridgeVlanInfo { return (*BridgeVlanInfo)(unsafe.Pointer(&b[0:SizeofBridgeVlanInfo][0])) } func (b *BridgeVlanInfo) PortVID() bool { return b.Flags&BRIDGE_VLAN_INFO_PVID > 0 } func (b *BridgeVlanInfo) EngressUntag() bool { return b.Flags&BRIDGE_VLAN_INFO_UNTAGGED > 0 } func (b *BridgeVlanInfo) String() string { return fmt.Sprintf("%+v", *b) } /* New extended info filters for IFLA_EXT_MASK */ const ( RTEXT_FILTER_VF = 1 << iota RTEXT_FILTER_BRVLAN RTEXT_FILTER_BRVLAN_COMPRESSED ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go ================================================ package nl import "unsafe" // Track the message sizes for the correct serialization/deserialization const ( SizeofNfgenmsg = 4 SizeofNfattr = 4 SizeofNfConntrack = 376 SizeofNfctTupleHead = 52 ) var L4ProtoMap = map[uint8]string{ 6: "tcp", 17: "udp", } // All the following constants are coming from: // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h // enum cntl_msg_types { // IPCTNL_MSG_CT_NEW, // IPCTNL_MSG_CT_GET, // IPCTNL_MSG_CT_DELETE, // IPCTNL_MSG_CT_GET_CTRZERO, // IPCTNL_MSG_CT_GET_STATS_CPU, // IPCTNL_MSG_CT_GET_STATS, // IPCTNL_MSG_CT_GET_DYING, // IPCTNL_MSG_CT_GET_UNCONFIRMED, // // IPCTNL_MSG_MAX // }; const ( IPCTNL_MSG_CT_GET = 1 IPCTNL_MSG_CT_DELETE = 2 ) // #define NFNETLINK_V0 0 const ( NFNETLINK_V0 = 0 ) const ( NLA_F_NESTED uint16 = (1 << 15) // #define NLA_F_NESTED (1 << 15) NLA_F_NET_BYTEORDER uint16 = (1 << 14) // #define NLA_F_NESTED (1 << 14) NLA_TYPE_MASK = ^(NLA_F_NESTED | NLA_F_NET_BYTEORDER) ) // enum ctattr_type { // CTA_UNSPEC, // CTA_TUPLE_ORIG, // CTA_TUPLE_REPLY, // CTA_STATUS, // CTA_PROTOINFO, // CTA_HELP, // CTA_NAT_SRC, // #define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ // CTA_TIMEOUT, // CTA_MARK, // CTA_COUNTERS_ORIG, // CTA_COUNTERS_REPLY, // CTA_USE, // CTA_ID, // CTA_NAT_DST, // CTA_TUPLE_MASTER, // CTA_SEQ_ADJ_ORIG, // CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, // CTA_SEQ_ADJ_REPLY, // CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, // CTA_SECMARK, /* obsolete */ // CTA_ZONE, // CTA_SECCTX, // CTA_TIMESTAMP, // CTA_MARK_MASK, // CTA_LABELS, // CTA_LABELS_MASK, // __CTA_MAX // }; const ( CTA_TUPLE_ORIG = 1 CTA_TUPLE_REPLY = 2 CTA_STATUS = 3 CTA_PROTOINFO = 4 CTA_TIMEOUT = 7 CTA_MARK = 8 CTA_COUNTERS_ORIG = 9 CTA_COUNTERS_REPLY = 10 CTA_USE = 11 CTA_ID = 12 CTA_TIMESTAMP = 20 ) // enum ctattr_tuple { // CTA_TUPLE_UNSPEC, // CTA_TUPLE_IP, // CTA_TUPLE_PROTO, // CTA_TUPLE_ZONE, // __CTA_TUPLE_MAX // }; // #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) const ( CTA_TUPLE_IP = 1 CTA_TUPLE_PROTO = 2 ) // enum ctattr_ip { // CTA_IP_UNSPEC, // CTA_IP_V4_SRC, // CTA_IP_V4_DST, // CTA_IP_V6_SRC, // CTA_IP_V6_DST, // __CTA_IP_MAX // }; // #define CTA_IP_MAX (__CTA_IP_MAX - 1) const ( CTA_IP_V4_SRC = 1 CTA_IP_V4_DST = 2 CTA_IP_V6_SRC = 3 CTA_IP_V6_DST = 4 ) // enum ctattr_l4proto { // CTA_PROTO_UNSPEC, // CTA_PROTO_NUM, // CTA_PROTO_SRC_PORT, // CTA_PROTO_DST_PORT, // CTA_PROTO_ICMP_ID, // CTA_PROTO_ICMP_TYPE, // CTA_PROTO_ICMP_CODE, // CTA_PROTO_ICMPV6_ID, // CTA_PROTO_ICMPV6_TYPE, // CTA_PROTO_ICMPV6_CODE, // __CTA_PROTO_MAX // }; // #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) const ( CTA_PROTO_NUM = 1 CTA_PROTO_SRC_PORT = 2 CTA_PROTO_DST_PORT = 3 ) // enum ctattr_protoinfo { // CTA_PROTOINFO_UNSPEC, // CTA_PROTOINFO_TCP, // CTA_PROTOINFO_DCCP, // CTA_PROTOINFO_SCTP, // __CTA_PROTOINFO_MAX // }; // #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) const ( CTA_PROTOINFO_TCP = 1 ) // enum ctattr_protoinfo_tcp { // CTA_PROTOINFO_TCP_UNSPEC, // CTA_PROTOINFO_TCP_STATE, // CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, // CTA_PROTOINFO_TCP_WSCALE_REPLY, // CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, // CTA_PROTOINFO_TCP_FLAGS_REPLY, // __CTA_PROTOINFO_TCP_MAX // }; // #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) const ( CTA_PROTOINFO_TCP_STATE = 1 CTA_PROTOINFO_TCP_WSCALE_ORIGINAL = 2 CTA_PROTOINFO_TCP_WSCALE_REPLY = 3 CTA_PROTOINFO_TCP_FLAGS_ORIGINAL = 4 CTA_PROTOINFO_TCP_FLAGS_REPLY = 5 ) // enum ctattr_counters { // CTA_COUNTERS_UNSPEC, // CTA_COUNTERS_PACKETS, /* 64bit counters */ // CTA_COUNTERS_BYTES, /* 64bit counters */ // CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ // CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ // CTA_COUNTERS_PAD, // __CTA_COUNTERS_M // }; // #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) const ( CTA_COUNTERS_PACKETS = 1 CTA_COUNTERS_BYTES = 2 ) // enum CTA TIMESTAMP TLVs // CTA_TIMESTAMP_START /* 64bit value */ // CTA_TIMESTAMP_STOP /* 64bit value */ const ( CTA_TIMESTAMP_START = 1 CTA_TIMESTAMP_STOP = 2 ) // /* General form of address family dependent message. // */ // struct nfgenmsg { // __u8 nfgen_family; /* AF_xxx */ // __u8 version; /* nfnetlink version */ // __be16 res_id; /* resource id */ // }; type Nfgenmsg struct { NfgenFamily uint8 Version uint8 ResId uint16 // big endian } func (msg *Nfgenmsg) Len() int { return SizeofNfgenmsg } func DeserializeNfgenmsg(b []byte) *Nfgenmsg { return (*Nfgenmsg)(unsafe.Pointer(&b[0:SizeofNfgenmsg][0])) } func (msg *Nfgenmsg) Serialize() []byte { return (*(*[SizeofNfgenmsg]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/devlink_linux.go ================================================ package nl // All the following constants are coming from: // https://github.com/torvalds/linux/blob/master/include/uapi/linux/devlink.h const ( GENL_DEVLINK_VERSION = 1 GENL_DEVLINK_NAME = "devlink" ) const ( DEVLINK_CMD_GET = 1 DEVLINK_CMD_PORT_GET = 5 DEVLINK_CMD_PORT_SET = 6 DEVLINK_CMD_PORT_NEW = 7 DEVLINK_CMD_PORT_DEL = 8 DEVLINK_CMD_ESWITCH_GET = 29 DEVLINK_CMD_ESWITCH_SET = 30 ) const ( DEVLINK_ATTR_BUS_NAME = 1 DEVLINK_ATTR_DEV_NAME = 2 DEVLINK_ATTR_PORT_INDEX = 3 DEVLINK_ATTR_PORT_TYPE = 4 DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6 DEVLINK_ATTR_PORT_NETDEV_NAME = 7 DEVLINK_ATTR_PORT_IBDEV_NAME = 8 DEVLINK_ATTR_ESWITCH_MODE = 25 DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26 DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62 DEVLINK_ATTR_PORT_FLAVOUR = 77 DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 DEVLINK_ATTR_PORT_FUNCTION = 145 DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 ) const ( DEVLINK_ESWITCH_MODE_LEGACY = 0 DEVLINK_ESWITCH_MODE_SWITCHDEV = 1 ) const ( DEVLINK_ESWITCH_INLINE_MODE_NONE = 0 DEVLINK_ESWITCH_INLINE_MODE_LINK = 1 DEVLINK_ESWITCH_INLINE_MODE_NETWORK = 2 DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT = 3 ) const ( DEVLINK_ESWITCH_ENCAP_MODE_NONE = 0 DEVLINK_ESWITCH_ENCAP_MODE_BASIC = 1 ) const ( DEVLINK_PORT_FLAVOUR_PHYSICAL = 0 DEVLINK_PORT_FLAVOUR_CPU = 1 DEVLINK_PORT_FLAVOUR_DSA = 2 DEVLINK_PORT_FLAVOUR_PCI_PF = 3 DEVLINK_PORT_FLAVOUR_PCI_VF = 4 DEVLINK_PORT_FLAVOUR_VIRTUAL = 5 DEVLINK_PORT_FLAVOUR_UNUSED = 6 DEVLINK_PORT_FLAVOUR_PCI_SF = 7 ) const ( DEVLINK_PORT_TYPE_NOTSET = 0 DEVLINK_PORT_TYPE_AUTO = 1 DEVLINK_PORT_TYPE_ETH = 2 DEVLINK_PORT_TYPE_IB = 3 ) const ( DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR = 1 DEVLINK_PORT_FN_ATTR_STATE = 2 DEVLINK_PORT_FN_ATTR_OPSTATE = 3 ) const ( DEVLINK_PORT_FN_STATE_INACTIVE = 0 DEVLINK_PORT_FN_STATE_ACTIVE = 1 ) const ( DEVLINK_PORT_FN_OPSTATE_DETACHED = 0 DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1 ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/genetlink_linux.go ================================================ package nl import ( "unsafe" ) const SizeofGenlmsg = 4 const ( GENL_ID_CTRL = 0x10 GENL_CTRL_VERSION = 2 GENL_CTRL_NAME = "nlctrl" ) const ( GENL_CTRL_CMD_GETFAMILY = 3 ) const ( GENL_CTRL_ATTR_UNSPEC = iota GENL_CTRL_ATTR_FAMILY_ID GENL_CTRL_ATTR_FAMILY_NAME GENL_CTRL_ATTR_VERSION GENL_CTRL_ATTR_HDRSIZE GENL_CTRL_ATTR_MAXATTR GENL_CTRL_ATTR_OPS GENL_CTRL_ATTR_MCAST_GROUPS ) const ( GENL_CTRL_ATTR_OP_UNSPEC = iota GENL_CTRL_ATTR_OP_ID GENL_CTRL_ATTR_OP_FLAGS ) const ( GENL_ADMIN_PERM = 1 << iota GENL_CMD_CAP_DO GENL_CMD_CAP_DUMP GENL_CMD_CAP_HASPOL ) const ( GENL_CTRL_ATTR_MCAST_GRP_UNSPEC = iota GENL_CTRL_ATTR_MCAST_GRP_NAME GENL_CTRL_ATTR_MCAST_GRP_ID ) const ( GENL_GTP_VERSION = 0 GENL_GTP_NAME = "gtp" ) const ( GENL_GTP_CMD_NEWPDP = iota GENL_GTP_CMD_DELPDP GENL_GTP_CMD_GETPDP ) const ( GENL_GTP_ATTR_UNSPEC = iota GENL_GTP_ATTR_LINK GENL_GTP_ATTR_VERSION GENL_GTP_ATTR_TID GENL_GTP_ATTR_PEER_ADDRESS GENL_GTP_ATTR_MS_ADDRESS GENL_GTP_ATTR_FLOW GENL_GTP_ATTR_NET_NS_FD GENL_GTP_ATTR_I_TEI GENL_GTP_ATTR_O_TEI GENL_GTP_ATTR_PAD ) type Genlmsg struct { Command uint8 Version uint8 } func (msg *Genlmsg) Len() int { return SizeofGenlmsg } func DeserializeGenlmsg(b []byte) *Genlmsg { return (*Genlmsg)(unsafe.Pointer(&b[0:SizeofGenlmsg][0])) } func (msg *Genlmsg) Serialize() []byte { return (*(*[SizeofGenlmsg]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/ipset_linux.go ================================================ package nl import ( "strconv" "golang.org/x/sys/unix" ) const ( /* The protocol version */ IPSET_PROTOCOL = 6 /* The max length of strings including NUL: set and type identifiers */ IPSET_MAXNAMELEN = 32 /* The maximum permissible comment length we will accept over netlink */ IPSET_MAX_COMMENT_SIZE = 255 ) const ( _ = iota IPSET_CMD_PROTOCOL /* 1: Return protocol version */ IPSET_CMD_CREATE /* 2: Create a new (empty) set */ IPSET_CMD_DESTROY /* 3: Destroy a (empty) set */ IPSET_CMD_FLUSH /* 4: Remove all elements from a set */ IPSET_CMD_RENAME /* 5: Rename a set */ IPSET_CMD_SWAP /* 6: Swap two sets */ IPSET_CMD_LIST /* 7: List sets */ IPSET_CMD_SAVE /* 8: Save sets */ IPSET_CMD_ADD /* 9: Add an element to a set */ IPSET_CMD_DEL /* 10: Delete an element from a set */ IPSET_CMD_TEST /* 11: Test an element in a set */ IPSET_CMD_HEADER /* 12: Get set header data only */ IPSET_CMD_TYPE /* 13: Get set type */ ) /* Attributes at command level */ const ( _ = iota IPSET_ATTR_PROTOCOL /* 1: Protocol version */ IPSET_ATTR_SETNAME /* 2: Name of the set */ IPSET_ATTR_TYPENAME /* 3: Typename */ IPSET_ATTR_REVISION /* 4: Settype revision */ IPSET_ATTR_FAMILY /* 5: Settype family */ IPSET_ATTR_FLAGS /* 6: Flags at command level */ IPSET_ATTR_DATA /* 7: Nested attributes */ IPSET_ATTR_ADT /* 8: Multiple data containers */ IPSET_ATTR_LINENO /* 9: Restore lineno */ IPSET_ATTR_PROTOCOL_MIN /* 10: Minimal supported version number */ IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME /* Setname at rename/swap */ IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN /* type rev min */ ) /* CADT specific attributes */ const ( IPSET_ATTR_IP = 1 IPSET_ATTR_IP_FROM = 1 IPSET_ATTR_IP_TO = 2 IPSET_ATTR_CIDR = 3 IPSET_ATTR_PORT = 4 IPSET_ATTR_PORT_FROM = 4 IPSET_ATTR_PORT_TO = 5 IPSET_ATTR_TIMEOUT = 6 IPSET_ATTR_PROTO = 7 IPSET_ATTR_CADT_FLAGS = 8 IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO /* 9 */ IPSET_ATTR_MARK = 10 IPSET_ATTR_MARKMASK = 11 /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16 /* Create-only specific attributes */ IPSET_ATTR_GC = 3 + iota IPSET_ATTR_HASHSIZE IPSET_ATTR_MAXELEM IPSET_ATTR_NETMASK IPSET_ATTR_PROBES IPSET_ATTR_RESIZE IPSET_ATTR_SIZE /* Kernel-only */ IPSET_ATTR_ELEMENTS IPSET_ATTR_REFERENCES IPSET_ATTR_MEMSIZE SET_ATTR_CREATE_MAX ) /* ADT specific attributes */ const ( IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + iota + 1 IPSET_ATTR_NAME IPSET_ATTR_NAMEREF IPSET_ATTR_IP2 IPSET_ATTR_CIDR2 IPSET_ATTR_IP2_TO IPSET_ATTR_IFACE IPSET_ATTR_BYTES IPSET_ATTR_PACKETS IPSET_ATTR_COMMENT IPSET_ATTR_SKBMARK IPSET_ATTR_SKBPRIO IPSET_ATTR_SKBQUEUE ) /* Flags at CADT attribute level, upper half of cmdattrs */ const ( IPSET_FLAG_BIT_BEFORE = 0 IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE) IPSET_FLAG_BIT_PHYSDEV = 1 IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV) IPSET_FLAG_BIT_NOMATCH = 2 IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH) IPSET_FLAG_BIT_WITH_COUNTERS = 3 IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS) IPSET_FLAG_BIT_WITH_COMMENT = 4 IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT) IPSET_FLAG_BIT_WITH_FORCEADD = 5 IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD) IPSET_FLAG_BIT_WITH_SKBINFO = 6 IPSET_FLAG_WITH_SKBINFO = (1 << IPSET_FLAG_BIT_WITH_SKBINFO) IPSET_FLAG_CADT_MAX = 15 ) const ( IPSET_ERR_PRIVATE = 4096 + iota IPSET_ERR_PROTOCOL IPSET_ERR_FIND_TYPE IPSET_ERR_MAX_SETS IPSET_ERR_BUSY IPSET_ERR_EXIST_SETNAME2 IPSET_ERR_TYPE_MISMATCH IPSET_ERR_EXIST IPSET_ERR_INVALID_CIDR IPSET_ERR_INVALID_NETMASK IPSET_ERR_INVALID_FAMILY IPSET_ERR_TIMEOUT IPSET_ERR_REFERENCED IPSET_ERR_IPADDR_IPV4 IPSET_ERR_IPADDR_IPV6 IPSET_ERR_COUNTER IPSET_ERR_COMMENT IPSET_ERR_INVALID_MARKMASK IPSET_ERR_SKBINFO /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 4352 ) type IPSetError uintptr func (e IPSetError) Error() string { switch int(e) { case IPSET_ERR_PRIVATE: return "private" case IPSET_ERR_PROTOCOL: return "invalid protocol" case IPSET_ERR_FIND_TYPE: return "invalid type" case IPSET_ERR_MAX_SETS: return "max sets reached" case IPSET_ERR_BUSY: return "busy" case IPSET_ERR_EXIST_SETNAME2: return "exist_setname2" case IPSET_ERR_TYPE_MISMATCH: return "type mismatch" case IPSET_ERR_EXIST: return "exist" case IPSET_ERR_INVALID_CIDR: return "invalid cidr" case IPSET_ERR_INVALID_NETMASK: return "invalid netmask" case IPSET_ERR_INVALID_FAMILY: return "invalid family" case IPSET_ERR_TIMEOUT: return "timeout" case IPSET_ERR_REFERENCED: return "referenced" case IPSET_ERR_IPADDR_IPV4: return "invalid ipv4 address" case IPSET_ERR_IPADDR_IPV6: return "invalid ipv6 address" case IPSET_ERR_COUNTER: return "invalid counter" case IPSET_ERR_COMMENT: return "invalid comment" case IPSET_ERR_INVALID_MARKMASK: return "invalid markmask" case IPSET_ERR_SKBINFO: return "skbinfo" default: return "errno " + strconv.Itoa(int(e)) } } func GetIpsetFlags(cmd int) int { switch cmd { case IPSET_CMD_CREATE: return unix.NLM_F_REQUEST | unix.NLM_F_ACK | unix.NLM_F_CREATE case IPSET_CMD_DESTROY, IPSET_CMD_FLUSH, IPSET_CMD_RENAME, IPSET_CMD_SWAP, IPSET_CMD_TEST: return unix.NLM_F_REQUEST | unix.NLM_F_ACK case IPSET_CMD_LIST, IPSET_CMD_SAVE: return unix.NLM_F_REQUEST | unix.NLM_F_ACK | unix.NLM_F_ROOT | unix.NLM_F_MATCH | unix.NLM_F_DUMP case IPSET_CMD_ADD, IPSET_CMD_DEL: return unix.NLM_F_REQUEST | unix.NLM_F_ACK case IPSET_CMD_HEADER, IPSET_CMD_TYPE, IPSET_CMD_PROTOCOL: return unix.NLM_F_REQUEST default: return 0 } } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/link_linux.go ================================================ package nl import ( "bytes" "encoding/binary" "unsafe" ) const ( DEFAULT_CHANGE = 0xFFFFFFFF ) const ( IFLA_INFO_UNSPEC = iota IFLA_INFO_KIND IFLA_INFO_DATA IFLA_INFO_XSTATS IFLA_INFO_SLAVE_KIND IFLA_INFO_SLAVE_DATA IFLA_INFO_MAX = IFLA_INFO_SLAVE_DATA ) const ( IFLA_VLAN_UNSPEC = iota IFLA_VLAN_ID IFLA_VLAN_FLAGS IFLA_VLAN_EGRESS_QOS IFLA_VLAN_INGRESS_QOS IFLA_VLAN_PROTOCOL IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL ) const ( VETH_INFO_UNSPEC = iota VETH_INFO_PEER VETH_INFO_MAX = VETH_INFO_PEER ) const ( IFLA_VXLAN_UNSPEC = iota IFLA_VXLAN_ID IFLA_VXLAN_GROUP IFLA_VXLAN_LINK IFLA_VXLAN_LOCAL IFLA_VXLAN_TTL IFLA_VXLAN_TOS IFLA_VXLAN_LEARNING IFLA_VXLAN_AGEING IFLA_VXLAN_LIMIT IFLA_VXLAN_PORT_RANGE IFLA_VXLAN_PROXY IFLA_VXLAN_RSC IFLA_VXLAN_L2MISS IFLA_VXLAN_L3MISS IFLA_VXLAN_PORT IFLA_VXLAN_GROUP6 IFLA_VXLAN_LOCAL6 IFLA_VXLAN_UDP_CSUM IFLA_VXLAN_UDP_ZERO_CSUM6_TX IFLA_VXLAN_UDP_ZERO_CSUM6_RX IFLA_VXLAN_REMCSUM_TX IFLA_VXLAN_REMCSUM_RX IFLA_VXLAN_GBP IFLA_VXLAN_REMCSUM_NOPARTIAL IFLA_VXLAN_FLOWBASED IFLA_VXLAN_MAX = IFLA_VXLAN_FLOWBASED ) const ( BRIDGE_MODE_UNSPEC = iota BRIDGE_MODE_HAIRPIN ) const ( IFLA_BRPORT_UNSPEC = iota IFLA_BRPORT_STATE IFLA_BRPORT_PRIORITY IFLA_BRPORT_COST IFLA_BRPORT_MODE IFLA_BRPORT_GUARD IFLA_BRPORT_PROTECT IFLA_BRPORT_FAST_LEAVE IFLA_BRPORT_LEARNING IFLA_BRPORT_UNICAST_FLOOD IFLA_BRPORT_PROXYARP IFLA_BRPORT_LEARNING_SYNC IFLA_BRPORT_PROXYARP_WIFI IFLA_BRPORT_MAX = IFLA_BRPORT_PROXYARP_WIFI ) const ( IFLA_IPVLAN_UNSPEC = iota IFLA_IPVLAN_MODE IFLA_IPVLAN_FLAG IFLA_IPVLAN_MAX = IFLA_IPVLAN_FLAG ) const ( IFLA_MACVLAN_UNSPEC = iota IFLA_MACVLAN_MODE IFLA_MACVLAN_FLAGS IFLA_MACVLAN_MACADDR_MODE IFLA_MACVLAN_MACADDR IFLA_MACVLAN_MACADDR_DATA IFLA_MACVLAN_MACADDR_COUNT IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS ) const ( MACVLAN_MODE_PRIVATE = 1 MACVLAN_MODE_VEPA = 2 MACVLAN_MODE_BRIDGE = 4 MACVLAN_MODE_PASSTHRU = 8 MACVLAN_MODE_SOURCE = 16 ) const ( MACVLAN_MACADDR_ADD = iota MACVLAN_MACADDR_DEL MACVLAN_MACADDR_FLUSH MACVLAN_MACADDR_SET ) const ( IFLA_BOND_UNSPEC = iota IFLA_BOND_MODE IFLA_BOND_ACTIVE_SLAVE IFLA_BOND_MIIMON IFLA_BOND_UPDELAY IFLA_BOND_DOWNDELAY IFLA_BOND_USE_CARRIER IFLA_BOND_ARP_INTERVAL IFLA_BOND_ARP_IP_TARGET IFLA_BOND_ARP_VALIDATE IFLA_BOND_ARP_ALL_TARGETS IFLA_BOND_PRIMARY IFLA_BOND_PRIMARY_RESELECT IFLA_BOND_FAIL_OVER_MAC IFLA_BOND_XMIT_HASH_POLICY IFLA_BOND_RESEND_IGMP IFLA_BOND_NUM_PEER_NOTIF IFLA_BOND_ALL_SLAVES_ACTIVE IFLA_BOND_MIN_LINKS IFLA_BOND_LP_INTERVAL IFLA_BOND_PACKETS_PER_SLAVE IFLA_BOND_AD_LACP_RATE IFLA_BOND_AD_SELECT IFLA_BOND_AD_INFO IFLA_BOND_AD_ACTOR_SYS_PRIO IFLA_BOND_AD_USER_PORT_KEY IFLA_BOND_AD_ACTOR_SYSTEM IFLA_BOND_TLB_DYNAMIC_LB ) const ( IFLA_BOND_AD_INFO_UNSPEC = iota IFLA_BOND_AD_INFO_AGGREGATOR IFLA_BOND_AD_INFO_NUM_PORTS IFLA_BOND_AD_INFO_ACTOR_KEY IFLA_BOND_AD_INFO_PARTNER_KEY IFLA_BOND_AD_INFO_PARTNER_MAC ) const ( IFLA_BOND_SLAVE_UNSPEC = iota IFLA_BOND_SLAVE_STATE IFLA_BOND_SLAVE_MII_STATUS IFLA_BOND_SLAVE_LINK_FAILURE_COUNT IFLA_BOND_SLAVE_PERM_HWADDR IFLA_BOND_SLAVE_QUEUE_ID IFLA_BOND_SLAVE_AD_AGGREGATOR_ID IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE ) const ( IFLA_GENEVE_UNSPEC = iota IFLA_GENEVE_ID // vni IFLA_GENEVE_REMOTE IFLA_GENEVE_TTL IFLA_GENEVE_TOS IFLA_GENEVE_PORT // destination port IFLA_GENEVE_COLLECT_METADATA IFLA_GENEVE_REMOTE6 IFLA_GENEVE_UDP_CSUM IFLA_GENEVE_UDP_ZERO_CSUM6_TX IFLA_GENEVE_UDP_ZERO_CSUM6_RX IFLA_GENEVE_LABEL IFLA_GENEVE_MAX = IFLA_GENEVE_LABEL ) const ( IFLA_GRE_UNSPEC = iota IFLA_GRE_LINK IFLA_GRE_IFLAGS IFLA_GRE_OFLAGS IFLA_GRE_IKEY IFLA_GRE_OKEY IFLA_GRE_LOCAL IFLA_GRE_REMOTE IFLA_GRE_TTL IFLA_GRE_TOS IFLA_GRE_PMTUDISC IFLA_GRE_ENCAP_LIMIT IFLA_GRE_FLOWINFO IFLA_GRE_FLAGS IFLA_GRE_ENCAP_TYPE IFLA_GRE_ENCAP_FLAGS IFLA_GRE_ENCAP_SPORT IFLA_GRE_ENCAP_DPORT IFLA_GRE_COLLECT_METADATA IFLA_GRE_MAX = IFLA_GRE_COLLECT_METADATA ) const ( GRE_CSUM = 0x8000 GRE_ROUTING = 0x4000 GRE_KEY = 0x2000 GRE_SEQ = 0x1000 GRE_STRICT = 0x0800 GRE_REC = 0x0700 GRE_FLAGS = 0x00F8 GRE_VERSION = 0x0007 ) const ( IFLA_VF_INFO_UNSPEC = iota IFLA_VF_INFO IFLA_VF_INFO_MAX = IFLA_VF_INFO ) const ( IFLA_VF_UNSPEC = iota IFLA_VF_MAC /* Hardware queue specific attributes */ IFLA_VF_VLAN IFLA_VF_TX_RATE /* Max TX Bandwidth Allocation */ IFLA_VF_SPOOFCHK /* Spoof Checking on/off switch */ IFLA_VF_LINK_STATE /* link state enable/disable/auto switch */ IFLA_VF_RATE /* Min and Max TX Bandwidth Allocation */ IFLA_VF_RSS_QUERY_EN /* RSS Redirection Table and Hash Key query * on/off switch */ IFLA_VF_STATS /* network device statistics */ IFLA_VF_TRUST /* Trust state of VF */ IFLA_VF_IB_NODE_GUID /* VF Infiniband node GUID */ IFLA_VF_IB_PORT_GUID /* VF Infiniband port GUID */ IFLA_VF_MAX = IFLA_VF_IB_PORT_GUID ) const ( IFLA_VF_LINK_STATE_AUTO = iota /* link state of the uplink */ IFLA_VF_LINK_STATE_ENABLE /* link always up */ IFLA_VF_LINK_STATE_DISABLE /* link always down */ IFLA_VF_LINK_STATE_MAX = IFLA_VF_LINK_STATE_DISABLE ) const ( IFLA_VF_STATS_RX_PACKETS = iota IFLA_VF_STATS_TX_PACKETS IFLA_VF_STATS_RX_BYTES IFLA_VF_STATS_TX_BYTES IFLA_VF_STATS_BROADCAST IFLA_VF_STATS_MULTICAST IFLA_VF_STATS_RX_DROPPED IFLA_VF_STATS_TX_DROPPED IFLA_VF_STATS_MAX = IFLA_VF_STATS_TX_DROPPED ) const ( SizeofVfMac = 0x24 SizeofVfVlan = 0x0c SizeofVfTxRate = 0x08 SizeofVfRate = 0x0c SizeofVfSpoofchk = 0x08 SizeofVfLinkState = 0x08 SizeofVfRssQueryEn = 0x08 SizeofVfTrust = 0x08 SizeofVfGUID = 0x10 ) // struct ifla_vf_mac { // __u32 vf; // __u8 mac[32]; /* MAX_ADDR_LEN */ // }; type VfMac struct { Vf uint32 Mac [32]byte } func (msg *VfMac) Len() int { return SizeofVfMac } func DeserializeVfMac(b []byte) *VfMac { return (*VfMac)(unsafe.Pointer(&b[0:SizeofVfMac][0])) } func (msg *VfMac) Serialize() []byte { return (*(*[SizeofVfMac]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_vlan { // __u32 vf; // __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ // __u32 qos; // }; type VfVlan struct { Vf uint32 Vlan uint32 Qos uint32 } func (msg *VfVlan) Len() int { return SizeofVfVlan } func DeserializeVfVlan(b []byte) *VfVlan { return (*VfVlan)(unsafe.Pointer(&b[0:SizeofVfVlan][0])) } func (msg *VfVlan) Serialize() []byte { return (*(*[SizeofVfVlan]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_tx_rate { // __u32 vf; // __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ // }; type VfTxRate struct { Vf uint32 Rate uint32 } func (msg *VfTxRate) Len() int { return SizeofVfTxRate } func DeserializeVfTxRate(b []byte) *VfTxRate { return (*VfTxRate)(unsafe.Pointer(&b[0:SizeofVfTxRate][0])) } func (msg *VfTxRate) Serialize() []byte { return (*(*[SizeofVfTxRate]byte)(unsafe.Pointer(msg)))[:] } //struct ifla_vf_stats { // __u64 rx_packets; // __u64 tx_packets; // __u64 rx_bytes; // __u64 tx_bytes; // __u64 broadcast; // __u64 multicast; //}; type VfStats struct { RxPackets uint64 TxPackets uint64 RxBytes uint64 TxBytes uint64 Multicast uint64 Broadcast uint64 RxDropped uint64 TxDropped uint64 } func DeserializeVfStats(b []byte) VfStats { var vfstat VfStats stats, err := ParseRouteAttr(b) if err != nil { return vfstat } var valueVar uint64 for _, stat := range stats { if err := binary.Read(bytes.NewBuffer(stat.Value), NativeEndian(), &valueVar); err != nil { break } switch stat.Attr.Type { case IFLA_VF_STATS_RX_PACKETS: vfstat.RxPackets = valueVar case IFLA_VF_STATS_TX_PACKETS: vfstat.TxPackets = valueVar case IFLA_VF_STATS_RX_BYTES: vfstat.RxBytes = valueVar case IFLA_VF_STATS_TX_BYTES: vfstat.TxBytes = valueVar case IFLA_VF_STATS_MULTICAST: vfstat.Multicast = valueVar case IFLA_VF_STATS_BROADCAST: vfstat.Broadcast = valueVar case IFLA_VF_STATS_RX_DROPPED: vfstat.RxDropped = valueVar case IFLA_VF_STATS_TX_DROPPED: vfstat.TxDropped = valueVar } } return vfstat } // struct ifla_vf_rate { // __u32 vf; // __u32 min_tx_rate; /* Min Bandwidth in Mbps */ // __u32 max_tx_rate; /* Max Bandwidth in Mbps */ // }; type VfRate struct { Vf uint32 MinTxRate uint32 MaxTxRate uint32 } func (msg *VfRate) Len() int { return SizeofVfRate } func DeserializeVfRate(b []byte) *VfRate { return (*VfRate)(unsafe.Pointer(&b[0:SizeofVfRate][0])) } func (msg *VfRate) Serialize() []byte { return (*(*[SizeofVfRate]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_spoofchk { // __u32 vf; // __u32 setting; // }; type VfSpoofchk struct { Vf uint32 Setting uint32 } func (msg *VfSpoofchk) Len() int { return SizeofVfSpoofchk } func DeserializeVfSpoofchk(b []byte) *VfSpoofchk { return (*VfSpoofchk)(unsafe.Pointer(&b[0:SizeofVfSpoofchk][0])) } func (msg *VfSpoofchk) Serialize() []byte { return (*(*[SizeofVfSpoofchk]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_link_state { // __u32 vf; // __u32 link_state; // }; type VfLinkState struct { Vf uint32 LinkState uint32 } func (msg *VfLinkState) Len() int { return SizeofVfLinkState } func DeserializeVfLinkState(b []byte) *VfLinkState { return (*VfLinkState)(unsafe.Pointer(&b[0:SizeofVfLinkState][0])) } func (msg *VfLinkState) Serialize() []byte { return (*(*[SizeofVfLinkState]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_rss_query_en { // __u32 vf; // __u32 setting; // }; type VfRssQueryEn struct { Vf uint32 Setting uint32 } func (msg *VfRssQueryEn) Len() int { return SizeofVfRssQueryEn } func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn { return (*VfRssQueryEn)(unsafe.Pointer(&b[0:SizeofVfRssQueryEn][0])) } func (msg *VfRssQueryEn) Serialize() []byte { return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_trust { // __u32 vf; // __u32 setting; // }; type VfTrust struct { Vf uint32 Setting uint32 } func (msg *VfTrust) Len() int { return SizeofVfTrust } func DeserializeVfTrust(b []byte) *VfTrust { return (*VfTrust)(unsafe.Pointer(&b[0:SizeofVfTrust][0])) } func (msg *VfTrust) Serialize() []byte { return (*(*[SizeofVfTrust]byte)(unsafe.Pointer(msg)))[:] } // struct ifla_vf_guid { // __u32 vf; // __u32 rsvd; // __u64 guid; // }; type VfGUID struct { Vf uint32 Rsvd uint32 GUID uint64 } func (msg *VfGUID) Len() int { return SizeofVfGUID } func DeserializeVfGUID(b []byte) *VfGUID { return (*VfGUID)(unsafe.Pointer(&b[0:SizeofVfGUID][0])) } func (msg *VfGUID) Serialize() []byte { return (*(*[SizeofVfGUID]byte)(unsafe.Pointer(msg)))[:] } const ( XDP_FLAGS_UPDATE_IF_NOEXIST = 1 << iota XDP_FLAGS_SKB_MODE XDP_FLAGS_DRV_MODE XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE ) const ( IFLA_XDP_UNSPEC = iota IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */ IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */ IFLA_XDP_FLAGS /* xdp prog related flags */ IFLA_XDP_PROG_ID /* xdp prog id */ IFLA_XDP_MAX = IFLA_XDP_PROG_ID ) // XDP program attach mode (used as dump value for IFLA_XDP_ATTACHED) const ( XDP_ATTACHED_NONE = iota XDP_ATTACHED_DRV XDP_ATTACHED_SKB XDP_ATTACHED_HW ) const ( IFLA_IPTUN_UNSPEC = iota IFLA_IPTUN_LINK IFLA_IPTUN_LOCAL IFLA_IPTUN_REMOTE IFLA_IPTUN_TTL IFLA_IPTUN_TOS IFLA_IPTUN_ENCAP_LIMIT IFLA_IPTUN_FLOWINFO IFLA_IPTUN_FLAGS IFLA_IPTUN_PROTO IFLA_IPTUN_PMTUDISC IFLA_IPTUN_6RD_PREFIX IFLA_IPTUN_6RD_RELAY_PREFIX IFLA_IPTUN_6RD_PREFIXLEN IFLA_IPTUN_6RD_RELAY_PREFIXLEN IFLA_IPTUN_ENCAP_TYPE IFLA_IPTUN_ENCAP_FLAGS IFLA_IPTUN_ENCAP_SPORT IFLA_IPTUN_ENCAP_DPORT IFLA_IPTUN_COLLECT_METADATA IFLA_IPTUN_MAX = IFLA_IPTUN_COLLECT_METADATA ) const ( IFLA_VTI_UNSPEC = iota IFLA_VTI_LINK IFLA_VTI_IKEY IFLA_VTI_OKEY IFLA_VTI_LOCAL IFLA_VTI_REMOTE IFLA_VTI_MAX = IFLA_VTI_REMOTE ) const ( IFLA_VRF_UNSPEC = iota IFLA_VRF_TABLE ) const ( IFLA_BR_UNSPEC = iota IFLA_BR_FORWARD_DELAY IFLA_BR_HELLO_TIME IFLA_BR_MAX_AGE IFLA_BR_AGEING_TIME IFLA_BR_STP_STATE IFLA_BR_PRIORITY IFLA_BR_VLAN_FILTERING IFLA_BR_VLAN_PROTOCOL IFLA_BR_GROUP_FWD_MASK IFLA_BR_ROOT_ID IFLA_BR_BRIDGE_ID IFLA_BR_ROOT_PORT IFLA_BR_ROOT_PATH_COST IFLA_BR_TOPOLOGY_CHANGE IFLA_BR_TOPOLOGY_CHANGE_DETECTED IFLA_BR_HELLO_TIMER IFLA_BR_TCN_TIMER IFLA_BR_TOPOLOGY_CHANGE_TIMER IFLA_BR_GC_TIMER IFLA_BR_GROUP_ADDR IFLA_BR_FDB_FLUSH IFLA_BR_MCAST_ROUTER IFLA_BR_MCAST_SNOOPING IFLA_BR_MCAST_QUERY_USE_IFADDR IFLA_BR_MCAST_QUERIER IFLA_BR_MCAST_HASH_ELASTICITY IFLA_BR_MCAST_HASH_MAX IFLA_BR_MCAST_LAST_MEMBER_CNT IFLA_BR_MCAST_STARTUP_QUERY_CNT IFLA_BR_MCAST_LAST_MEMBER_INTVL IFLA_BR_MCAST_MEMBERSHIP_INTVL IFLA_BR_MCAST_QUERIER_INTVL IFLA_BR_MCAST_QUERY_INTVL IFLA_BR_MCAST_QUERY_RESPONSE_INTVL IFLA_BR_MCAST_STARTUP_QUERY_INTVL IFLA_BR_NF_CALL_IPTABLES IFLA_BR_NF_CALL_IP6TABLES IFLA_BR_NF_CALL_ARPTABLES IFLA_BR_VLAN_DEFAULT_PVID IFLA_BR_PAD IFLA_BR_VLAN_STATS_ENABLED IFLA_BR_MCAST_STATS_ENABLED IFLA_BR_MCAST_IGMP_VERSION IFLA_BR_MCAST_MLD_VERSION IFLA_BR_MAX = IFLA_BR_MCAST_MLD_VERSION ) const ( IFLA_GTP_UNSPEC = iota IFLA_GTP_FD0 IFLA_GTP_FD1 IFLA_GTP_PDP_HASHSIZE IFLA_GTP_ROLE ) const ( GTP_ROLE_GGSN = iota GTP_ROLE_SGSN ) const ( IFLA_XFRM_UNSPEC = iota IFLA_XFRM_LINK IFLA_XFRM_IF_ID IFLA_XFRM_MAX = iota - 1 ) const ( IFLA_TUN_UNSPEC = iota IFLA_TUN_OWNER IFLA_TUN_GROUP IFLA_TUN_TYPE IFLA_TUN_PI IFLA_TUN_VNET_HDR IFLA_TUN_PERSIST IFLA_TUN_MULTI_QUEUE IFLA_TUN_NUM_QUEUES IFLA_TUN_NUM_DISABLED_QUEUES IFLA_TUN_MAX = IFLA_TUN_NUM_DISABLED_QUEUES ) const ( IFLA_IPOIB_UNSPEC = iota IFLA_IPOIB_PKEY IFLA_IPOIB_MODE IFLA_IPOIB_UMCAST IFLA_IPOIB_MAX = IFLA_IPOIB_UMCAST ) const ( IFLA_CAN_UNSPEC = iota IFLA_CAN_BITTIMING IFLA_CAN_BITTIMING_CONST IFLA_CAN_CLOCK IFLA_CAN_STATE IFLA_CAN_CTRLMODE IFLA_CAN_RESTART_MS IFLA_CAN_RESTART IFLA_CAN_BERR_COUNTER IFLA_CAN_DATA_BITTIMING IFLA_CAN_DATA_BITTIMING_CONST IFLA_CAN_TERMINATION IFLA_CAN_TERMINATION_CONST IFLA_CAN_BITRATE_CONST IFLA_CAN_DATA_BITRATE_CONST IFLA_CAN_BITRATE_MAX IFLA_CAN_MAX = IFLA_CAN_BITRATE_MAX ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/lwt_linux.go ================================================ package nl const ( LWT_BPF_PROG_UNSPEC = iota LWT_BPF_PROG_FD LWT_BPF_PROG_NAME __LWT_BPF_PROG_MAX ) const ( LWT_BPF_PROG_MAX = __LWT_BPF_PROG_MAX - 1 ) const ( LWT_BPF_UNSPEC = iota LWT_BPF_IN LWT_BPF_OUT LWT_BPF_XMIT LWT_BPF_XMIT_HEADROOM __LWT_BPF_MAX ) const ( LWT_BPF_MAX = __LWT_BPF_MAX - 1 ) const ( LWT_BPF_MAX_HEADROOM = 256 ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/mpls_linux.go ================================================ package nl import "encoding/binary" const ( MPLS_LS_LABEL_SHIFT = 12 MPLS_LS_S_SHIFT = 8 ) func EncodeMPLSStack(labels ...int) []byte { b := make([]byte, 4*len(labels)) for idx, label := range labels { l := label << MPLS_LS_LABEL_SHIFT if idx == len(labels)-1 { l |= 1 << MPLS_LS_S_SHIFT } binary.BigEndian.PutUint32(b[idx*4:], uint32(l)) } return b } func DecodeMPLSStack(buf []byte) []int { if len(buf)%4 != 0 { return nil } stack := make([]int, 0, len(buf)/4) for len(buf) > 0 { l := binary.BigEndian.Uint32(buf[:4]) buf = buf[4:] stack = append(stack, int(l)>>MPLS_LS_LABEL_SHIFT) if (l>>MPLS_LS_S_SHIFT)&1 > 0 { break } } return stack } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/nl_linux.go ================================================ // Package nl has low level primitives for making Netlink calls. package nl import ( "bytes" "encoding/binary" "fmt" "net" "runtime" "sync" "sync/atomic" "syscall" "unsafe" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) const ( // Family type definitions FAMILY_ALL = unix.AF_UNSPEC FAMILY_V4 = unix.AF_INET FAMILY_V6 = unix.AF_INET6 FAMILY_MPLS = unix.AF_MPLS // Arbitrary set value (greater than default 4k) to allow receiving // from kernel more verbose messages e.g. for statistics, // tc rules or filters, or other more memory requiring data. RECEIVE_BUFFER_SIZE = 65536 // Kernel netlink pid PidKernel uint32 = 0 ) // SupportedNlFamilies contains the list of netlink families this netlink package supports var SupportedNlFamilies = []int{unix.NETLINK_ROUTE, unix.NETLINK_XFRM, unix.NETLINK_NETFILTER} var nextSeqNr uint32 // Default netlink socket timeout, 60s var SocketTimeoutTv = unix.Timeval{Sec: 60, Usec: 0} // GetIPFamily returns the family type of a net.IP. func GetIPFamily(ip net.IP) int { if len(ip) <= net.IPv4len { return FAMILY_V4 } if ip.To4() != nil { return FAMILY_V4 } return FAMILY_V6 } var nativeEndian binary.ByteOrder // NativeEndian gets native endianness for the system func NativeEndian() binary.ByteOrder { if nativeEndian == nil { var x uint32 = 0x01020304 if *(*byte)(unsafe.Pointer(&x)) == 0x01 { nativeEndian = binary.BigEndian } else { nativeEndian = binary.LittleEndian } } return nativeEndian } // Byte swap a 16 bit value if we aren't big endian func Swap16(i uint16) uint16 { if NativeEndian() == binary.BigEndian { return i } return (i&0xff00)>>8 | (i&0xff)<<8 } // Byte swap a 32 bit value if aren't big endian func Swap32(i uint32) uint32 { if NativeEndian() == binary.BigEndian { return i } return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24 } type NetlinkRequestData interface { Len() int Serialize() []byte } // IfInfomsg is related to links, but it is used for list requests as well type IfInfomsg struct { unix.IfInfomsg } // Create an IfInfomsg with family specified func NewIfInfomsg(family int) *IfInfomsg { return &IfInfomsg{ IfInfomsg: unix.IfInfomsg{ Family: uint8(family), }, } } func DeserializeIfInfomsg(b []byte) *IfInfomsg { return (*IfInfomsg)(unsafe.Pointer(&b[0:unix.SizeofIfInfomsg][0])) } func (msg *IfInfomsg) Serialize() []byte { return (*(*[unix.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:] } func (msg *IfInfomsg) Len() int { return unix.SizeofIfInfomsg } func (msg *IfInfomsg) EncapType() string { switch msg.Type { case 0: return "generic" case unix.ARPHRD_ETHER: return "ether" case unix.ARPHRD_EETHER: return "eether" case unix.ARPHRD_AX25: return "ax25" case unix.ARPHRD_PRONET: return "pronet" case unix.ARPHRD_CHAOS: return "chaos" case unix.ARPHRD_IEEE802: return "ieee802" case unix.ARPHRD_ARCNET: return "arcnet" case unix.ARPHRD_APPLETLK: return "atalk" case unix.ARPHRD_DLCI: return "dlci" case unix.ARPHRD_ATM: return "atm" case unix.ARPHRD_METRICOM: return "metricom" case unix.ARPHRD_IEEE1394: return "ieee1394" case unix.ARPHRD_INFINIBAND: return "infiniband" case unix.ARPHRD_SLIP: return "slip" case unix.ARPHRD_CSLIP: return "cslip" case unix.ARPHRD_SLIP6: return "slip6" case unix.ARPHRD_CSLIP6: return "cslip6" case unix.ARPHRD_RSRVD: return "rsrvd" case unix.ARPHRD_ADAPT: return "adapt" case unix.ARPHRD_ROSE: return "rose" case unix.ARPHRD_X25: return "x25" case unix.ARPHRD_HWX25: return "hwx25" case unix.ARPHRD_PPP: return "ppp" case unix.ARPHRD_HDLC: return "hdlc" case unix.ARPHRD_LAPB: return "lapb" case unix.ARPHRD_DDCMP: return "ddcmp" case unix.ARPHRD_RAWHDLC: return "rawhdlc" case unix.ARPHRD_TUNNEL: return "ipip" case unix.ARPHRD_TUNNEL6: return "tunnel6" case unix.ARPHRD_FRAD: return "frad" case unix.ARPHRD_SKIP: return "skip" case unix.ARPHRD_LOOPBACK: return "loopback" case unix.ARPHRD_LOCALTLK: return "ltalk" case unix.ARPHRD_FDDI: return "fddi" case unix.ARPHRD_BIF: return "bif" case unix.ARPHRD_SIT: return "sit" case unix.ARPHRD_IPDDP: return "ip/ddp" case unix.ARPHRD_IPGRE: return "gre" case unix.ARPHRD_PIMREG: return "pimreg" case unix.ARPHRD_HIPPI: return "hippi" case unix.ARPHRD_ASH: return "ash" case unix.ARPHRD_ECONET: return "econet" case unix.ARPHRD_IRDA: return "irda" case unix.ARPHRD_FCPP: return "fcpp" case unix.ARPHRD_FCAL: return "fcal" case unix.ARPHRD_FCPL: return "fcpl" case unix.ARPHRD_FCFABRIC: return "fcfb0" case unix.ARPHRD_FCFABRIC + 1: return "fcfb1" case unix.ARPHRD_FCFABRIC + 2: return "fcfb2" case unix.ARPHRD_FCFABRIC + 3: return "fcfb3" case unix.ARPHRD_FCFABRIC + 4: return "fcfb4" case unix.ARPHRD_FCFABRIC + 5: return "fcfb5" case unix.ARPHRD_FCFABRIC + 6: return "fcfb6" case unix.ARPHRD_FCFABRIC + 7: return "fcfb7" case unix.ARPHRD_FCFABRIC + 8: return "fcfb8" case unix.ARPHRD_FCFABRIC + 9: return "fcfb9" case unix.ARPHRD_FCFABRIC + 10: return "fcfb10" case unix.ARPHRD_FCFABRIC + 11: return "fcfb11" case unix.ARPHRD_FCFABRIC + 12: return "fcfb12" case unix.ARPHRD_IEEE802_TR: return "tr" case unix.ARPHRD_IEEE80211: return "ieee802.11" case unix.ARPHRD_IEEE80211_PRISM: return "ieee802.11/prism" case unix.ARPHRD_IEEE80211_RADIOTAP: return "ieee802.11/radiotap" case unix.ARPHRD_IEEE802154: return "ieee802.15.4" case 65534: return "none" case 65535: return "void" } return fmt.Sprintf("unknown%d", msg.Type) } func rtaAlignOf(attrlen int) int { return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1) } func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg { msg := NewIfInfomsg(family) parent.children = append(parent.children, msg) return msg } type Uint32Attribute struct { Type uint16 Value uint32 } func (a *Uint32Attribute) Serialize() []byte { native := NativeEndian() buf := make([]byte, rtaAlignOf(8)) native.PutUint16(buf[0:2], 8) native.PutUint16(buf[2:4], a.Type) if a.Type&NLA_F_NET_BYTEORDER != 0 { binary.BigEndian.PutUint32(buf[4:], a.Value) } else { native.PutUint32(buf[4:], a.Value) } return buf } func (a *Uint32Attribute) Len() int { return 8 } // Extend RtAttr to handle data and children type RtAttr struct { unix.RtAttr Data []byte children []NetlinkRequestData } // Create a new Extended RtAttr object func NewRtAttr(attrType int, data []byte) *RtAttr { return &RtAttr{ RtAttr: unix.RtAttr{ Type: uint16(attrType), }, children: []NetlinkRequestData{}, Data: data, } } // NewRtAttrChild adds an RtAttr as a child to the parent and returns the new attribute // // Deprecated: Use AddRtAttr() on the parent object func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { return parent.AddRtAttr(attrType, data) } // AddRtAttr adds an RtAttr as a child and returns the new attribute func (a *RtAttr) AddRtAttr(attrType int, data []byte) *RtAttr { attr := NewRtAttr(attrType, data) a.children = append(a.children, attr) return attr } // AddChild adds an existing NetlinkRequestData as a child. func (a *RtAttr) AddChild(attr NetlinkRequestData) { a.children = append(a.children, attr) } func (a *RtAttr) Len() int { if len(a.children) == 0 { return (unix.SizeofRtAttr + len(a.Data)) } l := 0 for _, child := range a.children { l += rtaAlignOf(child.Len()) } l += unix.SizeofRtAttr return rtaAlignOf(l + len(a.Data)) } // Serialize the RtAttr into a byte array // This can't just unsafe.cast because it must iterate through children. func (a *RtAttr) Serialize() []byte { native := NativeEndian() length := a.Len() buf := make([]byte, rtaAlignOf(length)) next := 4 if a.Data != nil { copy(buf[next:], a.Data) next += rtaAlignOf(len(a.Data)) } if len(a.children) > 0 { for _, child := range a.children { childBuf := child.Serialize() copy(buf[next:], childBuf) next += rtaAlignOf(len(childBuf)) } } if l := uint16(length); l != 0 { native.PutUint16(buf[0:2], l) } native.PutUint16(buf[2:4], a.Type) return buf } type NetlinkRequest struct { unix.NlMsghdr Data []NetlinkRequestData RawData []byte Sockets map[int]*SocketHandle } // Serialize the Netlink Request into a byte array func (req *NetlinkRequest) Serialize() []byte { length := unix.SizeofNlMsghdr dataBytes := make([][]byte, len(req.Data)) for i, data := range req.Data { dataBytes[i] = data.Serialize() length = length + len(dataBytes[i]) } length += len(req.RawData) req.Len = uint32(length) b := make([]byte, length) hdr := (*(*[unix.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:] next := unix.SizeofNlMsghdr copy(b[0:next], hdr) for _, data := range dataBytes { for _, dataByte := range data { b[next] = dataByte next = next + 1 } } // Add the raw data if any if len(req.RawData) > 0 { copy(b[next:length], req.RawData) } return b } func (req *NetlinkRequest) AddData(data NetlinkRequestData) { req.Data = append(req.Data, data) } // AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization func (req *NetlinkRequest) AddRawData(data []byte) { req.RawData = append(req.RawData, data...) } // Execute the request against a the given sockType. // Returns a list of netlink messages in serialized format, optionally filtered // by resType. func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) { var ( s *NetlinkSocket err error ) if req.Sockets != nil { if sh, ok := req.Sockets[sockType]; ok { s = sh.Socket req.Seq = atomic.AddUint32(&sh.Seq, 1) } } sharedSocket := s != nil if s == nil { s, err = getNetlinkSocket(sockType) if err != nil { return nil, err } if err := s.SetSendTimeout(&SocketTimeoutTv); err != nil { return nil, err } if err := s.SetReceiveTimeout(&SocketTimeoutTv); err != nil { return nil, err } defer s.Close() } else { s.Lock() defer s.Unlock() } if err := s.Send(req); err != nil { return nil, err } pid, err := s.GetPid() if err != nil { return nil, err } var res [][]byte done: for { msgs, from, err := s.Receive() if err != nil { return nil, err } if from.Pid != PidKernel { return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, PidKernel) } for _, m := range msgs { if m.Header.Seq != req.Seq { if sharedSocket { continue } return nil, fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, req.Seq) } if m.Header.Pid != pid { continue } if m.Header.Type == unix.NLMSG_DONE || m.Header.Type == unix.NLMSG_ERROR { native := NativeEndian() error := int32(native.Uint32(m.Data[0:4])) if error == 0 { break done } return nil, syscall.Errno(-error) } if resType != 0 && m.Header.Type != resType { continue } res = append(res, m.Data) if m.Header.Flags&unix.NLM_F_MULTI == 0 { break done } } } return res, nil } // Create a new netlink request from proto and flags // Note the Len value will be inaccurate once data is added until // the message is serialized func NewNetlinkRequest(proto, flags int) *NetlinkRequest { return &NetlinkRequest{ NlMsghdr: unix.NlMsghdr{ Len: uint32(unix.SizeofNlMsghdr), Type: uint16(proto), Flags: unix.NLM_F_REQUEST | uint16(flags), Seq: atomic.AddUint32(&nextSeqNr, 1), }, } } type NetlinkSocket struct { fd int32 lsa unix.SockaddrNetlink sync.Mutex } func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol) if err != nil { return nil, err } s := &NetlinkSocket{ fd: int32(fd), } s.lsa.Family = unix.AF_NETLINK if err := unix.Bind(fd, &s.lsa); err != nil { unix.Close(fd) return nil, err } return s, nil } // GetNetlinkSocketAt opens a netlink socket in the network namespace newNs // and positions the thread back into the network namespace specified by curNs, // when done. If curNs is close, the function derives the current namespace and // moves back into it when done. If newNs is close, the socket will be opened // in the current network namespace. func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) { c, err := executeInNetns(newNs, curNs) if err != nil { return nil, err } defer c() return getNetlinkSocket(protocol) } // executeInNetns sets execution of the code following this call to the // network namespace newNs, then moves the thread back to curNs if open, // otherwise to the current netns at the time the function was invoked // In case of success, the caller is expected to execute the returned function // at the end of the code that needs to be executed in the network namespace. // Example: // func jobAt(...) error { // d, err := executeInNetns(...) // if err != nil { return err} // defer d() // < code which needs to be executed in specific netns> // } // TODO: his function probably belongs to netns pkg. func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) { var ( err error moveBack func(netns.NsHandle) error closeNs func() error unlockThd func() ) restore := func() { // order matters if moveBack != nil { moveBack(curNs) } if closeNs != nil { closeNs() } if unlockThd != nil { unlockThd() } } if newNs.IsOpen() { runtime.LockOSThread() unlockThd = runtime.UnlockOSThread if !curNs.IsOpen() { if curNs, err = netns.Get(); err != nil { restore() return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err) } closeNs = curNs.Close } if err := netns.Set(newNs); err != nil { restore() return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err) } moveBack = netns.Set } return restore, nil } // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE) // and subscribe it to multicast groups passed in variable argument list. // Returns the netlink socket on which Receive() method can be called // to retrieve the messages from the kernel. func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, protocol) if err != nil { return nil, err } s := &NetlinkSocket{ fd: int32(fd), } s.lsa.Family = unix.AF_NETLINK for _, g := range groups { s.lsa.Groups |= (1 << (g - 1)) } if err := unix.Bind(fd, &s.lsa); err != nil { unix.Close(fd) return nil, err } return s, nil } // SubscribeAt works like Subscribe plus let's the caller choose the network // namespace in which the socket would be opened (newNs). Then control goes back // to curNs if open, otherwise to the netns at the time this function was called. func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) { c, err := executeInNetns(newNs, curNs) if err != nil { return nil, err } defer c() return Subscribe(protocol, groups...) } func (s *NetlinkSocket) Close() { fd := int(atomic.SwapInt32(&s.fd, -1)) unix.Close(fd) } func (s *NetlinkSocket) GetFd() int { return int(atomic.LoadInt32(&s.fd)) } func (s *NetlinkSocket) Send(request *NetlinkRequest) error { fd := int(atomic.LoadInt32(&s.fd)) if fd < 0 { return fmt.Errorf("Send called on a closed socket") } if err := unix.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil { return err } return nil } func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetlink, error) { fd := int(atomic.LoadInt32(&s.fd)) if fd < 0 { return nil, nil, fmt.Errorf("Receive called on a closed socket") } var fromAddr *unix.SockaddrNetlink var rb [RECEIVE_BUFFER_SIZE]byte nr, from, err := unix.Recvfrom(fd, rb[:], 0) if err != nil { return nil, nil, err } fromAddr, ok := from.(*unix.SockaddrNetlink) if !ok { return nil, nil, fmt.Errorf("Error converting to netlink sockaddr") } if nr < unix.NLMSG_HDRLEN { return nil, nil, fmt.Errorf("Got short response from netlink") } rb2 := make([]byte, nr) copy(rb2, rb[:nr]) nl, err := syscall.ParseNetlinkMessage(rb2) if err != nil { return nil, nil, err } return nl, fromAddr, nil } // SetSendTimeout allows to set a send timeout on the socket func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error { // Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine // remains stuck on a send on a closed fd return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_SNDTIMEO, timeout) } // SetReceiveTimeout allows to set a receive timeout on the socket func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error { // Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine // remains stuck on a recvmsg on a closed fd return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout) } func (s *NetlinkSocket) GetPid() (uint32, error) { fd := int(atomic.LoadInt32(&s.fd)) lsa, err := unix.Getsockname(fd) if err != nil { return 0, err } switch v := lsa.(type) { case *unix.SockaddrNetlink: return v.Pid, nil } return 0, fmt.Errorf("Wrong socket type") } func ZeroTerminated(s string) []byte { bytes := make([]byte, len(s)+1) for i := 0; i < len(s); i++ { bytes[i] = s[i] } bytes[len(s)] = 0 return bytes } func NonZeroTerminated(s string) []byte { bytes := make([]byte, len(s)) for i := 0; i < len(s); i++ { bytes[i] = s[i] } return bytes } func BytesToString(b []byte) string { n := bytes.Index(b, []byte{0}) return string(b[:n]) } func Uint8Attr(v uint8) []byte { return []byte{byte(v)} } func Uint16Attr(v uint16) []byte { native := NativeEndian() bytes := make([]byte, 2) native.PutUint16(bytes, v) return bytes } func Uint32Attr(v uint32) []byte { native := NativeEndian() bytes := make([]byte, 4) native.PutUint32(bytes, v) return bytes } func Uint64Attr(v uint64) []byte { native := NativeEndian() bytes := make([]byte, 8) native.PutUint64(bytes, v) return bytes } func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) { var attrs []syscall.NetlinkRouteAttr for len(b) >= unix.SizeofRtAttr { a, vbuf, alen, err := netlinkRouteAttrAndValue(b) if err != nil { return nil, err } ra := syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(*a), Value: vbuf[:int(a.Len)-unix.SizeofRtAttr]} attrs = append(attrs, ra) b = b[alen:] } return attrs, nil } func netlinkRouteAttrAndValue(b []byte) (*unix.RtAttr, []byte, int, error) { a := (*unix.RtAttr)(unsafe.Pointer(&b[0])) if int(a.Len) < unix.SizeofRtAttr || int(a.Len) > len(b) { return nil, nil, 0, unix.EINVAL } return a, b[unix.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil } // SocketHandle contains the netlink socket and the associated // sequence counter for a specific netlink family type SocketHandle struct { Seq uint32 Socket *NetlinkSocket } // Close closes the netlink socket func (sh *SocketHandle) Close() { if sh.Socket != nil { sh.Socket.Close() } } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go ================================================ // +build !linux package nl import "encoding/binary" var SupportedNlFamilies = []int{} func NativeEndian() binary.ByteOrder { return nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/parse_attr_linux.go ================================================ package nl import ( "encoding/binary" "fmt" "log" ) type Attribute struct { Type uint16 Value []byte } func ParseAttributes(data []byte) <-chan Attribute { native := NativeEndian() result := make(chan Attribute) go func() { i := 0 for i+4 < len(data) { length := int(native.Uint16(data[i : i+2])) attrType := native.Uint16(data[i+2 : i+4]) if length < 4 { log.Printf("attribute 0x%02x has invalid length of %d bytes", attrType, length) break } if len(data) < i+length { log.Printf("attribute 0x%02x of length %d is truncated, only %d bytes remaining", attrType, length, len(data)-i) break } result <- Attribute{ Type: attrType, Value: data[i+4 : i+length], } i += rtaAlignOf(length) } close(result) }() return result } func PrintAttributes(data []byte) { printAttributes(data, 0) } func printAttributes(data []byte, level int) { for attr := range ParseAttributes(data) { for i := 0; i < level; i++ { print("> ") } nested := attr.Type&NLA_F_NESTED != 0 fmt.Printf("type=%d nested=%v len=%v %v\n", attr.Type&NLA_TYPE_MASK, nested, len(attr.Value), attr.Value) if nested { printAttributes(attr.Value, level+1) } } } // Uint32 returns the uint32 value respecting the NET_BYTEORDER flag func (attr *Attribute) Uint32() uint32 { if attr.Type&NLA_F_NET_BYTEORDER != 0 { return binary.BigEndian.Uint32(attr.Value) } else { return NativeEndian().Uint32(attr.Value) } } // Uint64 returns the uint64 value respecting the NET_BYTEORDER flag func (attr *Attribute) Uint64() uint64 { if attr.Type&NLA_F_NET_BYTEORDER != 0 { return binary.BigEndian.Uint64(attr.Value) } else { return NativeEndian().Uint64(attr.Value) } } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/rdma_link_linux.go ================================================ package nl const ( RDMA_NL_GET_CLIENT_SHIFT = 10 ) const ( RDMA_NL_NLDEV = 5 ) const ( RDMA_NLDEV_CMD_GET = 1 RDMA_NLDEV_CMD_SET = 2 RDMA_NLDEV_CMD_SYS_GET = 6 RDMA_NLDEV_CMD_SYS_SET = 7 ) const ( RDMA_NLDEV_ATTR_DEV_INDEX = 1 RDMA_NLDEV_ATTR_DEV_NAME = 2 RDMA_NLDEV_ATTR_PORT_INDEX = 3 RDMA_NLDEV_ATTR_CAP_FLAGS = 4 RDMA_NLDEV_ATTR_FW_VERSION = 5 RDMA_NLDEV_ATTR_NODE_GUID = 6 RDMA_NLDEV_ATTR_SYS_IMAGE_GUID = 7 RDMA_NLDEV_ATTR_SUBNET_PREFIX = 8 RDMA_NLDEV_ATTR_LID = 9 RDMA_NLDEV_ATTR_SM_LID = 10 RDMA_NLDEV_ATTR_LMC = 11 RDMA_NLDEV_ATTR_PORT_STATE = 12 RDMA_NLDEV_ATTR_PORT_PHYS_STATE = 13 RDMA_NLDEV_ATTR_DEV_NODE_TYPE = 14 RDMA_NLDEV_SYS_ATTR_NETNS_MODE = 66 RDMA_NLDEV_NET_NS_FD = 68 ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/route_linux.go ================================================ package nl import ( "unsafe" "golang.org/x/sys/unix" ) type RtMsg struct { unix.RtMsg } func NewRtMsg() *RtMsg { return &RtMsg{ RtMsg: unix.RtMsg{ Table: unix.RT_TABLE_MAIN, Scope: unix.RT_SCOPE_UNIVERSE, Protocol: unix.RTPROT_BOOT, Type: unix.RTN_UNICAST, }, } } func NewRtDelMsg() *RtMsg { return &RtMsg{ RtMsg: unix.RtMsg{ Table: unix.RT_TABLE_MAIN, Scope: unix.RT_SCOPE_NOWHERE, }, } } func (msg *RtMsg) Len() int { return unix.SizeofRtMsg } func DeserializeRtMsg(b []byte) *RtMsg { return (*RtMsg)(unsafe.Pointer(&b[0:unix.SizeofRtMsg][0])) } func (msg *RtMsg) Serialize() []byte { return (*(*[unix.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:] } type RtNexthop struct { unix.RtNexthop Children []NetlinkRequestData } func DeserializeRtNexthop(b []byte) *RtNexthop { return (*RtNexthop)(unsafe.Pointer(&b[0:unix.SizeofRtNexthop][0])) } func (msg *RtNexthop) Len() int { if len(msg.Children) == 0 { return unix.SizeofRtNexthop } l := 0 for _, child := range msg.Children { l += rtaAlignOf(child.Len()) } l += unix.SizeofRtNexthop return rtaAlignOf(l) } func (msg *RtNexthop) Serialize() []byte { length := msg.Len() msg.RtNexthop.Len = uint16(length) buf := make([]byte, length) copy(buf, (*(*[unix.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:]) next := rtaAlignOf(unix.SizeofRtNexthop) if len(msg.Children) > 0 { for _, child := range msg.Children { childBuf := child.Serialize() copy(buf[next:], childBuf) next += rtaAlignOf(len(childBuf)) } } return buf } type RtGenMsg struct { unix.RtGenmsg } func NewRtGenMsg() *RtGenMsg { return &RtGenMsg{ RtGenmsg: unix.RtGenmsg{ Family: unix.AF_UNSPEC, }, } } func (msg *RtGenMsg) Len() int { return rtaAlignOf(unix.SizeofRtGenmsg) } func DeserializeRtGenMsg(b []byte) *RtGenMsg { return &RtGenMsg{RtGenmsg: unix.RtGenmsg{Family: b[0]}} } func (msg *RtGenMsg) Serialize() []byte { out := make([]byte, msg.Len()) out[0] = msg.Family return out } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/seg6_linux.go ================================================ package nl import ( "errors" "fmt" "net" ) type IPv6SrHdr struct { nextHdr uint8 hdrLen uint8 routingType uint8 segmentsLeft uint8 firstSegment uint8 flags uint8 reserved uint16 Segments []net.IP } func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool { if len(s1.Segments) != len(s2.Segments) { return false } for i := range s1.Segments { if s1.Segments[i].Equal(s2.Segments[i]) != true { return false } } return s1.nextHdr == s2.nextHdr && s1.hdrLen == s2.hdrLen && s1.routingType == s2.routingType && s1.segmentsLeft == s2.segmentsLeft && s1.firstSegment == s2.firstSegment && s1.flags == s2.flags // reserved doesn't need to be identical. } // seg6 encap mode const ( SEG6_IPTUN_MODE_INLINE = iota SEG6_IPTUN_MODE_ENCAP ) // number of nested RTATTR // from include/uapi/linux/seg6_iptunnel.h const ( SEG6_IPTUNNEL_UNSPEC = iota SEG6_IPTUNNEL_SRH __SEG6_IPTUNNEL_MAX ) const ( SEG6_IPTUNNEL_MAX = __SEG6_IPTUNNEL_MAX - 1 ) func EncodeSEG6Encap(mode int, segments []net.IP) ([]byte, error) { nsegs := len(segments) // nsegs: number of segments if nsegs == 0 { return nil, errors.New("EncodeSEG6Encap: No Segment in srh") } b := make([]byte, 12, 12+len(segments)*16) native := NativeEndian() native.PutUint32(b, uint32(mode)) b[4] = 0 // srh.nextHdr (0 when calling netlink) b[5] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit) b[6] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA) b[7] = uint8(nsegs - 1) // srh.segmentsLeft b[8] = uint8(nsegs - 1) // srh.firstSegment b[9] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac) // srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07 native.PutUint16(b[10:], 0) // srh.reserved for _, netIP := range segments { b = append(b, netIP...) // srh.Segments } return b, nil } func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) { native := NativeEndian() mode := int(native.Uint32(buf)) srh := IPv6SrHdr{ nextHdr: buf[4], hdrLen: buf[5], routingType: buf[6], segmentsLeft: buf[7], firstSegment: buf[8], flags: buf[9], reserved: native.Uint16(buf[10:12]), } buf = buf[12:] if len(buf)%16 != 0 { err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)\n", len(buf)) return mode, nil, err } for len(buf) > 0 { srh.Segments = append(srh.Segments, net.IP(buf[:16])) buf = buf[16:] } return mode, srh.Segments, nil } func DecodeSEG6Srh(buf []byte) ([]net.IP, error) { native := NativeEndian() srh := IPv6SrHdr{ nextHdr: buf[0], hdrLen: buf[1], routingType: buf[2], segmentsLeft: buf[3], firstSegment: buf[4], flags: buf[5], reserved: native.Uint16(buf[6:8]), } buf = buf[8:] if len(buf)%16 != 0 { err := fmt.Errorf("DecodeSEG6Srh: error parsing Segment List (buf len: %d)", len(buf)) return nil, err } for len(buf) > 0 { srh.Segments = append(srh.Segments, net.IP(buf[:16])) buf = buf[16:] } return srh.Segments, nil } func EncodeSEG6Srh(segments []net.IP) ([]byte, error) { nsegs := len(segments) // nsegs: number of segments if nsegs == 0 { return nil, errors.New("EncodeSEG6Srh: No Segments") } b := make([]byte, 8, 8+len(segments)*16) native := NativeEndian() b[0] = 0 // srh.nextHdr (0 when calling netlink) b[1] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit) b[2] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA) b[3] = uint8(nsegs - 1) // srh.segmentsLeft b[4] = uint8(nsegs - 1) // srh.firstSegment b[5] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac) // srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07 native.PutUint16(b[6:], 0) // srh.reserved for _, netIP := range segments { b = append(b, netIP...) // srh.Segments } return b, nil } // Helper functions func SEG6EncapModeString(mode int) string { switch mode { case SEG6_IPTUN_MODE_INLINE: return "inline" case SEG6_IPTUN_MODE_ENCAP: return "encap" } return "unknown" } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/seg6local_linux.go ================================================ package nl import () // seg6local parameters const ( SEG6_LOCAL_UNSPEC = iota SEG6_LOCAL_ACTION SEG6_LOCAL_SRH SEG6_LOCAL_TABLE SEG6_LOCAL_NH4 SEG6_LOCAL_NH6 SEG6_LOCAL_IIF SEG6_LOCAL_OIF __SEG6_LOCAL_MAX ) const ( SEG6_LOCAL_MAX = __SEG6_LOCAL_MAX ) // seg6local actions const ( SEG6_LOCAL_ACTION_END = iota + 1 // 1 SEG6_LOCAL_ACTION_END_X // 2 SEG6_LOCAL_ACTION_END_T // 3 SEG6_LOCAL_ACTION_END_DX2 // 4 SEG6_LOCAL_ACTION_END_DX6 // 5 SEG6_LOCAL_ACTION_END_DX4 // 6 SEG6_LOCAL_ACTION_END_DT6 // 7 SEG6_LOCAL_ACTION_END_DT4 // 8 SEG6_LOCAL_ACTION_END_B6 // 9 SEG6_LOCAL_ACTION_END_B6_ENCAPS // 10 SEG6_LOCAL_ACTION_END_BM // 11 SEG6_LOCAL_ACTION_END_S // 12 SEG6_LOCAL_ACTION_END_AS // 13 SEG6_LOCAL_ACTION_END_AM // 14 __SEG6_LOCAL_ACTION_MAX ) const ( SEG6_LOCAL_ACTION_MAX = __SEG6_LOCAL_ACTION_MAX - 1 ) // Helper functions func SEG6LocalActionString(action int) string { switch action { case SEG6_LOCAL_ACTION_END: return "End" case SEG6_LOCAL_ACTION_END_X: return "End.X" case SEG6_LOCAL_ACTION_END_T: return "End.T" case SEG6_LOCAL_ACTION_END_DX2: return "End.DX2" case SEG6_LOCAL_ACTION_END_DX6: return "End.DX6" case SEG6_LOCAL_ACTION_END_DX4: return "End.DX4" case SEG6_LOCAL_ACTION_END_DT6: return "End.DT6" case SEG6_LOCAL_ACTION_END_DT4: return "End.DT4" case SEG6_LOCAL_ACTION_END_B6: return "End.B6" case SEG6_LOCAL_ACTION_END_B6_ENCAPS: return "End.B6.Encaps" case SEG6_LOCAL_ACTION_END_BM: return "End.BM" case SEG6_LOCAL_ACTION_END_S: return "End.S" case SEG6_LOCAL_ACTION_END_AS: return "End.AS" case SEG6_LOCAL_ACTION_END_AM: return "End.AM" } return "unknown" } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/syscall.go ================================================ package nl // syscall package lack of rule atributes type. // Thus there are defined below const ( FRA_UNSPEC = iota FRA_DST /* destination address */ FRA_SRC /* source address */ FRA_IIFNAME /* interface name */ FRA_GOTO /* target to jump to (FR_ACT_GOTO) */ FRA_UNUSED2 FRA_PRIORITY /* priority/preference */ FRA_UNUSED3 FRA_UNUSED4 FRA_UNUSED5 FRA_FWMARK /* mark */ FRA_FLOW /* flow/class id */ FRA_TUN_ID FRA_SUPPRESS_IFGROUP FRA_SUPPRESS_PREFIXLEN FRA_TABLE /* Extended table id */ FRA_FWMASK /* mask for netfilter mark */ FRA_OIFNAME FRA_PAD FRA_L3MDEV /* iif or oif is l3mdev goto its table */ FRA_UID_RANGE /* UID range */ FRA_PROTOCOL /* Originator of the rule */ FRA_IP_PROTO /* ip proto */ FRA_SPORT_RANGE /* sport */ FRA_DPORT_RANGE /* dport */ ) // ip rule netlink request types const ( FR_ACT_UNSPEC = iota FR_ACT_TO_TBL /* Pass to fixed table */ FR_ACT_GOTO /* Jump to another rule */ FR_ACT_NOP /* No operation */ FR_ACT_RES3 FR_ACT_RES4 FR_ACT_BLACKHOLE /* Drop without notification */ FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */ FR_ACT_PROHIBIT /* Drop with EACCES */ ) // socket diags related const ( SOCK_DIAG_BY_FAMILY = 20 /* linux.sock_diag.h */ TCPDIAG_NOCOOKIE = 0xFFFFFFFF /* TCPDIAG_NOCOOKIE in net/ipv4/tcp_diag.h*/ ) // RTA_ENCAP subtype const ( MPLS_IPTUNNEL_UNSPEC = iota MPLS_IPTUNNEL_DST ) // light weight tunnel encap types const ( LWTUNNEL_ENCAP_NONE = iota LWTUNNEL_ENCAP_MPLS LWTUNNEL_ENCAP_IP LWTUNNEL_ENCAP_ILA LWTUNNEL_ENCAP_IP6 LWTUNNEL_ENCAP_SEG6 LWTUNNEL_ENCAP_BPF LWTUNNEL_ENCAP_SEG6_LOCAL ) // routing header types const ( IPV6_SRCRT_STRICT = 0x01 // Deprecated; will be removed IPV6_SRCRT_TYPE_0 = 0 // Deprecated; will be removed IPV6_SRCRT_TYPE_2 = 2 // IPv6 type 2 Routing Header IPV6_SRCRT_TYPE_4 = 4 // Segment Routing with IPv6 ) ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/tc_linux.go ================================================ package nl import ( "encoding/binary" "unsafe" ) // LinkLayer const ( LINKLAYER_UNSPEC = iota LINKLAYER_ETHERNET LINKLAYER_ATM ) // ATM const ( ATM_CELL_PAYLOAD = 48 ATM_CELL_SIZE = 53 ) const TC_LINKLAYER_MASK = 0x0F // Police const ( TCA_POLICE_UNSPEC = iota TCA_POLICE_TBF TCA_POLICE_RATE TCA_POLICE_PEAKRATE TCA_POLICE_AVRATE TCA_POLICE_RESULT TCA_POLICE_MAX = TCA_POLICE_RESULT ) // Message types const ( TCA_UNSPEC = iota TCA_KIND TCA_OPTIONS TCA_STATS TCA_XSTATS TCA_RATE TCA_FCNT TCA_STATS2 TCA_STAB TCA_MAX = TCA_STAB ) const ( TCA_ACT_TAB = 1 TCAA_MAX = 1 ) const ( TCA_ACT_UNSPEC = iota TCA_ACT_KIND TCA_ACT_OPTIONS TCA_ACT_INDEX TCA_ACT_STATS TCA_ACT_MAX ) const ( TCA_PRIO_UNSPEC = iota TCA_PRIO_MQ TCA_PRIO_MAX = TCA_PRIO_MQ ) const ( TCA_STATS_UNSPEC = iota TCA_STATS_BASIC TCA_STATS_RATE_EST TCA_STATS_QUEUE TCA_STATS_APP TCA_STATS_MAX = TCA_STATS_APP ) const ( SizeofTcMsg = 0x14 SizeofTcActionMsg = 0x04 SizeofTcPrioMap = 0x14 SizeofTcRateSpec = 0x0c SizeofTcNetemQopt = 0x18 SizeofTcNetemCorr = 0x0c SizeofTcNetemReorder = 0x08 SizeofTcNetemCorrupt = 0x08 SizeofTcTbfQopt = 2*SizeofTcRateSpec + 0x0c SizeofTcHtbCopt = 2*SizeofTcRateSpec + 0x14 SizeofTcHtbGlob = 0x14 SizeofTcU32Key = 0x10 SizeofTcU32Sel = 0x10 // without keys SizeofTcGen = 0x14 SizeofTcConnmark = SizeofTcGen + 0x04 SizeofTcMirred = SizeofTcGen + 0x08 SizeofTcTunnelKey = SizeofTcGen + 0x04 SizeofTcSkbEdit = SizeofTcGen SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 SizeofTcSfqQopt = 0x0b SizeofTcSfqRedStats = 0x18 SizeofTcSfqQoptV1 = SizeofTcSfqQopt + SizeofTcSfqRedStats + 0x1c ) // struct tcmsg { // unsigned char tcm_family; // unsigned char tcm__pad1; // unsigned short tcm__pad2; // int tcm_ifindex; // __u32 tcm_handle; // __u32 tcm_parent; // __u32 tcm_info; // }; type TcMsg struct { Family uint8 Pad [3]byte Ifindex int32 Handle uint32 Parent uint32 Info uint32 } func (msg *TcMsg) Len() int { return SizeofTcMsg } func DeserializeTcMsg(b []byte) *TcMsg { return (*TcMsg)(unsafe.Pointer(&b[0:SizeofTcMsg][0])) } func (x *TcMsg) Serialize() []byte { return (*(*[SizeofTcMsg]byte)(unsafe.Pointer(x)))[:] } // struct tcamsg { // unsigned char tca_family; // unsigned char tca__pad1; // unsigned short tca__pad2; // }; type TcActionMsg struct { Family uint8 Pad [3]byte } func (msg *TcActionMsg) Len() int { return SizeofTcActionMsg } func DeserializeTcActionMsg(b []byte) *TcActionMsg { return (*TcActionMsg)(unsafe.Pointer(&b[0:SizeofTcActionMsg][0])) } func (x *TcActionMsg) Serialize() []byte { return (*(*[SizeofTcActionMsg]byte)(unsafe.Pointer(x)))[:] } const ( TC_PRIO_MAX = 15 ) // struct tc_prio_qopt { // int bands; /* Number of bands */ // __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ // }; type TcPrioMap struct { Bands int32 Priomap [TC_PRIO_MAX + 1]uint8 } func (msg *TcPrioMap) Len() int { return SizeofTcPrioMap } func DeserializeTcPrioMap(b []byte) *TcPrioMap { return (*TcPrioMap)(unsafe.Pointer(&b[0:SizeofTcPrioMap][0])) } func (x *TcPrioMap) Serialize() []byte { return (*(*[SizeofTcPrioMap]byte)(unsafe.Pointer(x)))[:] } const ( TCA_TBF_UNSPEC = iota TCA_TBF_PARMS TCA_TBF_RTAB TCA_TBF_PTAB TCA_TBF_RATE64 TCA_TBF_PRATE64 TCA_TBF_BURST TCA_TBF_PBURST TCA_TBF_MAX = TCA_TBF_PBURST ) // struct tc_ratespec { // unsigned char cell_log; // __u8 linklayer; /* lower 4 bits */ // unsigned short overhead; // short cell_align; // unsigned short mpu; // __u32 rate; // }; type TcRateSpec struct { CellLog uint8 Linklayer uint8 Overhead uint16 CellAlign int16 Mpu uint16 Rate uint32 } func (msg *TcRateSpec) Len() int { return SizeofTcRateSpec } func DeserializeTcRateSpec(b []byte) *TcRateSpec { return (*TcRateSpec)(unsafe.Pointer(&b[0:SizeofTcRateSpec][0])) } func (x *TcRateSpec) Serialize() []byte { return (*(*[SizeofTcRateSpec]byte)(unsafe.Pointer(x)))[:] } /** * NETEM */ const ( TCA_NETEM_UNSPEC = iota TCA_NETEM_CORR TCA_NETEM_DELAY_DIST TCA_NETEM_REORDER TCA_NETEM_CORRUPT TCA_NETEM_LOSS TCA_NETEM_RATE TCA_NETEM_ECN TCA_NETEM_RATE64 TCA_NETEM_MAX = TCA_NETEM_RATE64 ) // struct tc_netem_qopt { // __u32 latency; /* added delay (us) */ // __u32 limit; /* fifo limit (packets) */ // __u32 loss; /* random packet loss (0=none ~0=100%) */ // __u32 gap; /* re-ordering gap (0 for none) */ // __u32 duplicate; /* random packet dup (0=none ~0=100%) */ // __u32 jitter; /* random jitter in latency (us) */ // }; type TcNetemQopt struct { Latency uint32 Limit uint32 Loss uint32 Gap uint32 Duplicate uint32 Jitter uint32 } func (msg *TcNetemQopt) Len() int { return SizeofTcNetemQopt } func DeserializeTcNetemQopt(b []byte) *TcNetemQopt { return (*TcNetemQopt)(unsafe.Pointer(&b[0:SizeofTcNetemQopt][0])) } func (x *TcNetemQopt) Serialize() []byte { return (*(*[SizeofTcNetemQopt]byte)(unsafe.Pointer(x)))[:] } // struct tc_netem_corr { // __u32 delay_corr; /* delay correlation */ // __u32 loss_corr; /* packet loss correlation */ // __u32 dup_corr; /* duplicate correlation */ // }; type TcNetemCorr struct { DelayCorr uint32 LossCorr uint32 DupCorr uint32 } func (msg *TcNetemCorr) Len() int { return SizeofTcNetemCorr } func DeserializeTcNetemCorr(b []byte) *TcNetemCorr { return (*TcNetemCorr)(unsafe.Pointer(&b[0:SizeofTcNetemCorr][0])) } func (x *TcNetemCorr) Serialize() []byte { return (*(*[SizeofTcNetemCorr]byte)(unsafe.Pointer(x)))[:] } // struct tc_netem_reorder { // __u32 probability; // __u32 correlation; // }; type TcNetemReorder struct { Probability uint32 Correlation uint32 } func (msg *TcNetemReorder) Len() int { return SizeofTcNetemReorder } func DeserializeTcNetemReorder(b []byte) *TcNetemReorder { return (*TcNetemReorder)(unsafe.Pointer(&b[0:SizeofTcNetemReorder][0])) } func (x *TcNetemReorder) Serialize() []byte { return (*(*[SizeofTcNetemReorder]byte)(unsafe.Pointer(x)))[:] } // struct tc_netem_corrupt { // __u32 probability; // __u32 correlation; // }; type TcNetemCorrupt struct { Probability uint32 Correlation uint32 } func (msg *TcNetemCorrupt) Len() int { return SizeofTcNetemCorrupt } func DeserializeTcNetemCorrupt(b []byte) *TcNetemCorrupt { return (*TcNetemCorrupt)(unsafe.Pointer(&b[0:SizeofTcNetemCorrupt][0])) } func (x *TcNetemCorrupt) Serialize() []byte { return (*(*[SizeofTcNetemCorrupt]byte)(unsafe.Pointer(x)))[:] } // struct tc_tbf_qopt { // struct tc_ratespec rate; // struct tc_ratespec peakrate; // __u32 limit; // __u32 buffer; // __u32 mtu; // }; type TcTbfQopt struct { Rate TcRateSpec Peakrate TcRateSpec Limit uint32 Buffer uint32 Mtu uint32 } func (msg *TcTbfQopt) Len() int { return SizeofTcTbfQopt } func DeserializeTcTbfQopt(b []byte) *TcTbfQopt { return (*TcTbfQopt)(unsafe.Pointer(&b[0:SizeofTcTbfQopt][0])) } func (x *TcTbfQopt) Serialize() []byte { return (*(*[SizeofTcTbfQopt]byte)(unsafe.Pointer(x)))[:] } const ( TCA_HTB_UNSPEC = iota TCA_HTB_PARMS TCA_HTB_INIT TCA_HTB_CTAB TCA_HTB_RTAB TCA_HTB_DIRECT_QLEN TCA_HTB_RATE64 TCA_HTB_CEIL64 TCA_HTB_MAX = TCA_HTB_CEIL64 ) //struct tc_htb_opt { // struct tc_ratespec rate; // struct tc_ratespec ceil; // __u32 buffer; // __u32 cbuffer; // __u32 quantum; // __u32 level; /* out only */ // __u32 prio; //}; type TcHtbCopt struct { Rate TcRateSpec Ceil TcRateSpec Buffer uint32 Cbuffer uint32 Quantum uint32 Level uint32 Prio uint32 } func (msg *TcHtbCopt) Len() int { return SizeofTcHtbCopt } func DeserializeTcHtbCopt(b []byte) *TcHtbCopt { return (*TcHtbCopt)(unsafe.Pointer(&b[0:SizeofTcHtbCopt][0])) } func (x *TcHtbCopt) Serialize() []byte { return (*(*[SizeofTcHtbCopt]byte)(unsafe.Pointer(x)))[:] } type TcHtbGlob struct { Version uint32 Rate2Quantum uint32 Defcls uint32 Debug uint32 DirectPkts uint32 } func (msg *TcHtbGlob) Len() int { return SizeofTcHtbGlob } func DeserializeTcHtbGlob(b []byte) *TcHtbGlob { return (*TcHtbGlob)(unsafe.Pointer(&b[0:SizeofTcHtbGlob][0])) } func (x *TcHtbGlob) Serialize() []byte { return (*(*[SizeofTcHtbGlob]byte)(unsafe.Pointer(x)))[:] } // HFSC type Curve struct { m1 uint32 d uint32 m2 uint32 } type HfscCopt struct { Rsc Curve Fsc Curve Usc Curve } func (c *Curve) Attrs() (uint32, uint32, uint32) { return c.m1, c.d, c.m2 } func (c *Curve) Set(m1 uint32, d uint32, m2 uint32) { c.m1 = m1 c.d = d c.m2 = m2 } func DeserializeHfscCurve(b []byte) *Curve { return &Curve{ m1: binary.LittleEndian.Uint32(b[0:4]), d: binary.LittleEndian.Uint32(b[4:8]), m2: binary.LittleEndian.Uint32(b[8:12]), } } func SerializeHfscCurve(c *Curve) (b []byte) { t := make([]byte, binary.MaxVarintLen32) binary.LittleEndian.PutUint32(t, c.m1) b = append(b, t[:4]...) binary.LittleEndian.PutUint32(t, c.d) b = append(b, t[:4]...) binary.LittleEndian.PutUint32(t, c.m2) b = append(b, t[:4]...) return b } type TcHfscOpt struct { Defcls uint16 } func (x *TcHfscOpt) Serialize() []byte { return (*(*[2]byte)(unsafe.Pointer(x)))[:] } const ( TCA_U32_UNSPEC = iota TCA_U32_CLASSID TCA_U32_HASH TCA_U32_LINK TCA_U32_DIVISOR TCA_U32_SEL TCA_U32_POLICE TCA_U32_ACT TCA_U32_INDEV TCA_U32_PCNT TCA_U32_MARK TCA_U32_MAX = TCA_U32_MARK ) // struct tc_u32_key { // __be32 mask; // __be32 val; // int off; // int offmask; // }; type TcU32Key struct { Mask uint32 // big endian Val uint32 // big endian Off int32 OffMask int32 } func (msg *TcU32Key) Len() int { return SizeofTcU32Key } func DeserializeTcU32Key(b []byte) *TcU32Key { return (*TcU32Key)(unsafe.Pointer(&b[0:SizeofTcU32Key][0])) } func (x *TcU32Key) Serialize() []byte { return (*(*[SizeofTcU32Key]byte)(unsafe.Pointer(x)))[:] } // struct tc_u32_sel { // unsigned char flags; // unsigned char offshift; // unsigned char nkeys; // // __be16 offmask; // __u16 off; // short offoff; // // short hoff; // __be32 hmask; // struct tc_u32_key keys[0]; // }; const ( TC_U32_TERMINAL = 1 << iota TC_U32_OFFSET = 1 << iota TC_U32_VAROFFSET = 1 << iota TC_U32_EAT = 1 << iota ) type TcU32Sel struct { Flags uint8 Offshift uint8 Nkeys uint8 Pad uint8 Offmask uint16 // big endian Off uint16 Offoff int16 Hoff int16 Hmask uint32 // big endian Keys []TcU32Key } func (msg *TcU32Sel) Len() int { return SizeofTcU32Sel + int(msg.Nkeys)*SizeofTcU32Key } func DeserializeTcU32Sel(b []byte) *TcU32Sel { x := &TcU32Sel{} copy((*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:], b) next := SizeofTcU32Sel var i uint8 for i = 0; i < x.Nkeys; i++ { x.Keys = append(x.Keys, *DeserializeTcU32Key(b[next:])) next += SizeofTcU32Key } return x } func (x *TcU32Sel) Serialize() []byte { // This can't just unsafe.cast because it must iterate through keys. buf := make([]byte, x.Len()) copy(buf, (*(*[SizeofTcU32Sel]byte)(unsafe.Pointer(x)))[:]) next := SizeofTcU32Sel for _, key := range x.Keys { keyBuf := key.Serialize() copy(buf[next:], keyBuf) next += SizeofTcU32Key } return buf } type TcGen struct { Index uint32 Capab uint32 Action int32 Refcnt int32 Bindcnt int32 } func (msg *TcGen) Len() int { return SizeofTcGen } func DeserializeTcGen(b []byte) *TcGen { return (*TcGen)(unsafe.Pointer(&b[0:SizeofTcGen][0])) } func (x *TcGen) Serialize() []byte { return (*(*[SizeofTcGen]byte)(unsafe.Pointer(x)))[:] } // #define tc_gen \ // __u32 index; \ // __u32 capab; \ // int action; \ // int refcnt; \ // int bindcnt const ( TCA_ACT_GACT = 5 ) const ( TCA_GACT_UNSPEC = iota TCA_GACT_TM TCA_GACT_PARMS TCA_GACT_PROB TCA_GACT_MAX = TCA_GACT_PROB ) type TcGact TcGen const ( TCA_ACT_BPF = 13 ) const ( TCA_ACT_BPF_UNSPEC = iota TCA_ACT_BPF_TM TCA_ACT_BPF_PARMS TCA_ACT_BPF_OPS_LEN TCA_ACT_BPF_OPS TCA_ACT_BPF_FD TCA_ACT_BPF_NAME TCA_ACT_BPF_MAX = TCA_ACT_BPF_NAME ) const ( TCA_BPF_FLAG_ACT_DIRECT uint32 = 1 << iota ) const ( TCA_BPF_UNSPEC = iota TCA_BPF_ACT TCA_BPF_POLICE TCA_BPF_CLASSID TCA_BPF_OPS_LEN TCA_BPF_OPS TCA_BPF_FD TCA_BPF_NAME TCA_BPF_FLAGS TCA_BPF_FLAGS_GEN TCA_BPF_TAG TCA_BPF_ID TCA_BPF_MAX = TCA_BPF_ID ) type TcBpf TcGen const ( TCA_ACT_CONNMARK = 14 ) const ( TCA_CONNMARK_UNSPEC = iota TCA_CONNMARK_PARMS TCA_CONNMARK_TM TCA_CONNMARK_MAX = TCA_CONNMARK_TM ) // struct tc_connmark { // tc_gen; // __u16 zone; // }; type TcConnmark struct { TcGen Zone uint16 } func (msg *TcConnmark) Len() int { return SizeofTcConnmark } func DeserializeTcConnmark(b []byte) *TcConnmark { return (*TcConnmark)(unsafe.Pointer(&b[0:SizeofTcConnmark][0])) } func (x *TcConnmark) Serialize() []byte { return (*(*[SizeofTcConnmark]byte)(unsafe.Pointer(x)))[:] } const ( TCA_ACT_MIRRED = 8 ) const ( TCA_MIRRED_UNSPEC = iota TCA_MIRRED_TM TCA_MIRRED_PARMS TCA_MIRRED_MAX = TCA_MIRRED_PARMS ) // struct tc_mirred { // tc_gen; // int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ // __u32 ifindex; /* ifindex of egress port */ // }; type TcMirred struct { TcGen Eaction int32 Ifindex uint32 } func (msg *TcMirred) Len() int { return SizeofTcMirred } func DeserializeTcMirred(b []byte) *TcMirred { return (*TcMirred)(unsafe.Pointer(&b[0:SizeofTcMirred][0])) } func (x *TcMirred) Serialize() []byte { return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:] } const ( TCA_TUNNEL_KEY_UNSPEC = iota TCA_TUNNEL_KEY_TM TCA_TUNNEL_KEY_PARMS TCA_TUNNEL_KEY_ENC_IPV4_SRC TCA_TUNNEL_KEY_ENC_IPV4_DST TCA_TUNNEL_KEY_ENC_IPV6_SRC TCA_TUNNEL_KEY_ENC_IPV6_DST TCA_TUNNEL_KEY_ENC_KEY_ID TCA_TUNNEL_KEY_PAD TCA_TUNNEL_KEY_ENC_DST_PORT TCA_TUNNEL_KEY_NO_CSUM TCA_TUNNEL_KEY_ENC_OPTS TCA_TUNNEL_KEY_ENC_TOS TCA_TUNNEL_KEY_ENC_TTL TCA_TUNNEL_KEY_MAX ) type TcTunnelKey struct { TcGen Action int32 } func (x *TcTunnelKey) Len() int { return SizeofTcTunnelKey } func DeserializeTunnelKey(b []byte) *TcTunnelKey { return (*TcTunnelKey)(unsafe.Pointer(&b[0:SizeofTcTunnelKey][0])) } func (x *TcTunnelKey) Serialize() []byte { return (*(*[SizeofTcTunnelKey]byte)(unsafe.Pointer(x)))[:] } const ( TCA_SKBEDIT_UNSPEC = iota TCA_SKBEDIT_TM TCA_SKBEDIT_PARMS TCA_SKBEDIT_PRIORITY TCA_SKBEDIT_QUEUE_MAPPING TCA_SKBEDIT_MARK TCA_SKBEDIT_PAD TCA_SKBEDIT_PTYPE TCA_SKBEDIT_MAX = TCA_SKBEDIT_MARK ) type TcSkbEdit struct { TcGen } func (x *TcSkbEdit) Len() int { return SizeofTcSkbEdit } func DeserializeSkbEdit(b []byte) *TcSkbEdit { return (*TcSkbEdit)(unsafe.Pointer(&b[0:SizeofTcSkbEdit][0])) } func (x *TcSkbEdit) Serialize() []byte { return (*(*[SizeofTcSkbEdit]byte)(unsafe.Pointer(x)))[:] } // struct tc_police { // __u32 index; // int action; // __u32 limit; // __u32 burst; // __u32 mtu; // struct tc_ratespec rate; // struct tc_ratespec peakrate; // int refcnt; // int bindcnt; // __u32 capab; // }; type TcPolice struct { Index uint32 Action int32 Limit uint32 Burst uint32 Mtu uint32 Rate TcRateSpec PeakRate TcRateSpec Refcnt int32 Bindcnt int32 Capab uint32 } func (msg *TcPolice) Len() int { return SizeofTcPolice } func DeserializeTcPolice(b []byte) *TcPolice { return (*TcPolice)(unsafe.Pointer(&b[0:SizeofTcPolice][0])) } func (x *TcPolice) Serialize() []byte { return (*(*[SizeofTcPolice]byte)(unsafe.Pointer(x)))[:] } const ( TCA_FW_UNSPEC = iota TCA_FW_CLASSID TCA_FW_POLICE TCA_FW_INDEV TCA_FW_ACT TCA_FW_MASK TCA_FW_MAX = TCA_FW_MASK ) const ( TCA_MATCHALL_UNSPEC = iota TCA_MATCHALL_CLASSID TCA_MATCHALL_ACT TCA_MATCHALL_FLAGS ) const ( TCA_FQ_UNSPEC = iota TCA_FQ_PLIMIT // limit of total number of packets in queue TCA_FQ_FLOW_PLIMIT // limit of packets per flow TCA_FQ_QUANTUM // RR quantum TCA_FQ_INITIAL_QUANTUM // RR quantum for new flow TCA_FQ_RATE_ENABLE // enable/disable rate limiting TCA_FQ_FLOW_DEFAULT_RATE // obsolete do not use TCA_FQ_FLOW_MAX_RATE // per flow max rate TCA_FQ_BUCKETS_LOG // log2(number of buckets) TCA_FQ_FLOW_REFILL_DELAY // flow credit refill delay in usec TCA_FQ_ORPHAN_MASK // mask applied to orphaned skb hashes TCA_FQ_LOW_RATE_THRESHOLD // per packet delay under this rate ) const ( TCA_FQ_CODEL_UNSPEC = iota TCA_FQ_CODEL_TARGET TCA_FQ_CODEL_LIMIT TCA_FQ_CODEL_INTERVAL TCA_FQ_CODEL_ECN TCA_FQ_CODEL_FLOWS TCA_FQ_CODEL_QUANTUM TCA_FQ_CODEL_CE_THRESHOLD TCA_FQ_CODEL_DROP_BATCH_SIZE TCA_FQ_CODEL_MEMORY_LIMIT ) const ( TCA_HFSC_UNSPEC = iota TCA_HFSC_RSC TCA_HFSC_FSC TCA_HFSC_USC ) // struct tc_sfq_qopt { // unsigned quantum; /* Bytes per round allocated to flow */ // int perturb_period; /* Period of hash perturbation */ // __u32 limit; /* Maximal packets in queue */ // unsigned divisor; /* Hash divisor */ // unsigned flows; /* Maximal number of flows */ // }; type TcSfqQopt struct { Quantum uint8 Perturb int32 Limit uint32 Divisor uint8 Flows uint8 } func (x *TcSfqQopt) Len() int { return SizeofTcSfqQopt } func DeserializeTcSfqQopt(b []byte) *TcSfqQopt { return (*TcSfqQopt)(unsafe.Pointer(&b[0:SizeofTcSfqQopt][0])) } func (x *TcSfqQopt) Serialize() []byte { return (*(*[SizeofTcSfqQopt]byte)(unsafe.Pointer(x)))[:] } // struct tc_sfqred_stats { // __u32 prob_drop; /* Early drops, below max threshold */ // __u32 forced_drop; /* Early drops, after max threshold */ // __u32 prob_mark; /* Marked packets, below max threshold */ // __u32 forced_mark; /* Marked packets, after max threshold */ // __u32 prob_mark_head; /* Marked packets, below max threshold */ // __u32 forced_mark_head;/* Marked packets, after max threshold */ // }; type TcSfqRedStats struct { ProbDrop uint32 ForcedDrop uint32 ProbMark uint32 ForcedMark uint32 ProbMarkHead uint32 ForcedMarkHead uint32 } func (x *TcSfqRedStats) Len() int { return SizeofTcSfqRedStats } func DeserializeTcSfqRedStats(b []byte) *TcSfqRedStats { return (*TcSfqRedStats)(unsafe.Pointer(&b[0:SizeofTcSfqRedStats][0])) } func (x *TcSfqRedStats) Serialize() []byte { return (*(*[SizeofTcSfqRedStats]byte)(unsafe.Pointer(x)))[:] } // struct tc_sfq_qopt_v1 { // struct tc_sfq_qopt v0; // unsigned int depth; /* max number of packets per flow */ // unsigned int headdrop; // /* SFQRED parameters */ // __u32 limit; /* HARD maximal flow queue length (bytes) */ // __u32 qth_min; /* Min average length threshold (bytes) */ // __u32 qth_max; /* Max average length threshold (bytes) */ // unsigned char Wlog; /* log(W) */ // unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ // unsigned char Scell_log; /* cell size for idle damping */ // unsigned char flags; // __u32 max_P; /* probability, high resolution */ // /* SFQRED stats */ // struct tc_sfqred_stats stats; // }; type TcSfqQoptV1 struct { TcSfqQopt Depth uint32 HeadDrop uint32 Limit uint32 QthMin uint32 QthMax uint32 Wlog byte Plog byte ScellLog byte Flags byte MaxP uint32 TcSfqRedStats } func (x *TcSfqQoptV1) Len() int { return SizeofTcSfqQoptV1 } func DeserializeTcSfqQoptV1(b []byte) *TcSfqQoptV1 { return (*TcSfqQoptV1)(unsafe.Pointer(&b[0:SizeofTcSfqQoptV1][0])) } func (x *TcSfqQoptV1) Serialize() []byte { return (*(*[SizeofTcSfqQoptV1]byte)(unsafe.Pointer(x)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go ================================================ package nl import ( "bytes" "net" "unsafe" ) // Infinity for packet and byte counts const ( XFRM_INF = ^uint64(0) ) type XfrmMsgType uint8 type XfrmMsg interface { Type() XfrmMsgType } // Message Types const ( XFRM_MSG_BASE XfrmMsgType = 0x10 XFRM_MSG_NEWSA = 0x10 XFRM_MSG_DELSA = 0x11 XFRM_MSG_GETSA = 0x12 XFRM_MSG_NEWPOLICY = 0x13 XFRM_MSG_DELPOLICY = 0x14 XFRM_MSG_GETPOLICY = 0x15 XFRM_MSG_ALLOCSPI = 0x16 XFRM_MSG_ACQUIRE = 0x17 XFRM_MSG_EXPIRE = 0x18 XFRM_MSG_UPDPOLICY = 0x19 XFRM_MSG_UPDSA = 0x1a XFRM_MSG_POLEXPIRE = 0x1b XFRM_MSG_FLUSHSA = 0x1c XFRM_MSG_FLUSHPOLICY = 0x1d XFRM_MSG_NEWAE = 0x1e XFRM_MSG_GETAE = 0x1f XFRM_MSG_REPORT = 0x20 XFRM_MSG_MIGRATE = 0x21 XFRM_MSG_NEWSADINFO = 0x22 XFRM_MSG_GETSADINFO = 0x23 XFRM_MSG_NEWSPDINFO = 0x24 XFRM_MSG_GETSPDINFO = 0x25 XFRM_MSG_MAPPING = 0x26 XFRM_MSG_MAX = 0x26 XFRM_NR_MSGTYPES = 0x17 ) // Attribute types const ( /* Netlink message attributes. */ XFRMA_UNSPEC = iota XFRMA_ALG_AUTH /* struct xfrm_algo */ XFRMA_ALG_CRYPT /* struct xfrm_algo */ XFRMA_ALG_COMP /* struct xfrm_algo */ XFRMA_ENCAP /* struct xfrm_algo + struct xfrm_encap_tmpl */ XFRMA_TMPL /* 1 or more struct xfrm_user_tmpl */ XFRMA_SA /* struct xfrm_usersa_info */ XFRMA_POLICY /* struct xfrm_userpolicy_info */ XFRMA_SEC_CTX /* struct xfrm_sec_ctx */ XFRMA_LTIME_VAL XFRMA_REPLAY_VAL XFRMA_REPLAY_THRESH XFRMA_ETIMER_THRESH XFRMA_SRCADDR /* xfrm_address_t */ XFRMA_COADDR /* xfrm_address_t */ XFRMA_LASTUSED /* unsigned long */ XFRMA_POLICY_TYPE /* struct xfrm_userpolicy_type */ XFRMA_MIGRATE XFRMA_ALG_AEAD /* struct xfrm_algo_aead */ XFRMA_KMADDRESS /* struct xfrm_user_kmaddress */ XFRMA_ALG_AUTH_TRUNC /* struct xfrm_algo_auth */ XFRMA_MARK /* struct xfrm_mark */ XFRMA_TFCPAD /* __u32 */ XFRMA_REPLAY_ESN_VAL /* struct xfrm_replay_esn */ XFRMA_SA_EXTRA_FLAGS /* __u32 */ XFRMA_PROTO /* __u8 */ XFRMA_ADDRESS_FILTER /* struct xfrm_address_filter */ XFRMA_PAD XFRMA_OFFLOAD_DEV /* struct xfrm_state_offload */ XFRMA_SET_MARK /* __u32 */ XFRMA_SET_MARK_MASK /* __u32 */ XFRMA_IF_ID /* __u32 */ XFRMA_MAX = iota - 1 ) const XFRMA_OUTPUT_MARK = XFRMA_SET_MARK const ( SizeofXfrmAddress = 0x10 SizeofXfrmSelector = 0x38 SizeofXfrmLifetimeCfg = 0x40 SizeofXfrmLifetimeCur = 0x20 SizeofXfrmId = 0x18 SizeofXfrmMark = 0x08 ) // Netlink groups const ( XFRMNLGRP_NONE = 0x0 XFRMNLGRP_ACQUIRE = 0x1 XFRMNLGRP_EXPIRE = 0x2 XFRMNLGRP_SA = 0x3 XFRMNLGRP_POLICY = 0x4 XFRMNLGRP_AEVENTS = 0x5 XFRMNLGRP_REPORT = 0x6 XFRMNLGRP_MIGRATE = 0x7 XFRMNLGRP_MAPPING = 0x8 __XFRMNLGRP_MAX = 0x9 ) // typedef union { // __be32 a4; // __be32 a6[4]; // } xfrm_address_t; type XfrmAddress [SizeofXfrmAddress]byte func (x *XfrmAddress) ToIP() net.IP { var empty = [12]byte{} ip := make(net.IP, net.IPv6len) if bytes.Equal(x[4:16], empty[:]) { ip[10] = 0xff ip[11] = 0xff copy(ip[12:16], x[0:4]) } else { copy(ip[:], x[:]) } return ip } func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet { ip := x.ToIP() if GetIPFamily(ip) == FAMILY_V4 { return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)} } return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)} } func (x *XfrmAddress) FromIP(ip net.IP) { var empty = [16]byte{} if len(ip) < net.IPv4len { copy(x[4:16], empty[:]) } else if GetIPFamily(ip) == FAMILY_V4 { copy(x[0:4], ip.To4()[0:4]) copy(x[4:16], empty[:12]) } else { copy(x[0:16], ip.To16()[0:16]) } } func DeserializeXfrmAddress(b []byte) *XfrmAddress { return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0])) } func (x *XfrmAddress) Serialize() []byte { return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:] } // struct xfrm_selector { // xfrm_address_t daddr; // xfrm_address_t saddr; // __be16 dport; // __be16 dport_mask; // __be16 sport; // __be16 sport_mask; // __u16 family; // __u8 prefixlen_d; // __u8 prefixlen_s; // __u8 proto; // int ifindex; // __kernel_uid32_t user; // }; type XfrmSelector struct { Daddr XfrmAddress Saddr XfrmAddress Dport uint16 // big endian DportMask uint16 // big endian Sport uint16 // big endian SportMask uint16 // big endian Family uint16 PrefixlenD uint8 PrefixlenS uint8 Proto uint8 Pad [3]byte Ifindex int32 User uint32 } func (msg *XfrmSelector) Len() int { return SizeofXfrmSelector } func DeserializeXfrmSelector(b []byte) *XfrmSelector { return (*XfrmSelector)(unsafe.Pointer(&b[0:SizeofXfrmSelector][0])) } func (msg *XfrmSelector) Serialize() []byte { return (*(*[SizeofXfrmSelector]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_lifetime_cfg { // __u64 soft_byte_limit; // __u64 hard_byte_limit; // __u64 soft_packet_limit; // __u64 hard_packet_limit; // __u64 soft_add_expires_seconds; // __u64 hard_add_expires_seconds; // __u64 soft_use_expires_seconds; // __u64 hard_use_expires_seconds; // }; // type XfrmLifetimeCfg struct { SoftByteLimit uint64 HardByteLimit uint64 SoftPacketLimit uint64 HardPacketLimit uint64 SoftAddExpiresSeconds uint64 HardAddExpiresSeconds uint64 SoftUseExpiresSeconds uint64 HardUseExpiresSeconds uint64 } func (msg *XfrmLifetimeCfg) Len() int { return SizeofXfrmLifetimeCfg } func DeserializeXfrmLifetimeCfg(b []byte) *XfrmLifetimeCfg { return (*XfrmLifetimeCfg)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCfg][0])) } func (msg *XfrmLifetimeCfg) Serialize() []byte { return (*(*[SizeofXfrmLifetimeCfg]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_lifetime_cur { // __u64 bytes; // __u64 packets; // __u64 add_time; // __u64 use_time; // }; type XfrmLifetimeCur struct { Bytes uint64 Packets uint64 AddTime uint64 UseTime uint64 } func (msg *XfrmLifetimeCur) Len() int { return SizeofXfrmLifetimeCur } func DeserializeXfrmLifetimeCur(b []byte) *XfrmLifetimeCur { return (*XfrmLifetimeCur)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCur][0])) } func (msg *XfrmLifetimeCur) Serialize() []byte { return (*(*[SizeofXfrmLifetimeCur]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_id { // xfrm_address_t daddr; // __be32 spi; // __u8 proto; // }; type XfrmId struct { Daddr XfrmAddress Spi uint32 // big endian Proto uint8 Pad [3]byte } func (msg *XfrmId) Len() int { return SizeofXfrmId } func DeserializeXfrmId(b []byte) *XfrmId { return (*XfrmId)(unsafe.Pointer(&b[0:SizeofXfrmId][0])) } func (msg *XfrmId) Serialize() []byte { return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:] } type XfrmMark struct { Value uint32 Mask uint32 } func (msg *XfrmMark) Len() int { return SizeofXfrmMark } func DeserializeXfrmMark(b []byte) *XfrmMark { return (*XfrmMark)(unsafe.Pointer(&b[0:SizeofXfrmMark][0])) } func (msg *XfrmMark) Serialize() []byte { return (*(*[SizeofXfrmMark]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go ================================================ package nl import ( "unsafe" ) const ( SizeofXfrmUserExpire = 0xe8 ) // struct xfrm_user_expire { // struct xfrm_usersa_info state; // __u8 hard; // }; type XfrmUserExpire struct { XfrmUsersaInfo XfrmUsersaInfo Hard uint8 Pad [7]byte } func (msg *XfrmUserExpire) Len() int { return SizeofXfrmUserExpire } func DeserializeXfrmUserExpire(b []byte) *XfrmUserExpire { return (*XfrmUserExpire)(unsafe.Pointer(&b[0:SizeofXfrmUserExpire][0])) } func (msg *XfrmUserExpire) Serialize() []byte { return (*(*[SizeofXfrmUserExpire]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go ================================================ package nl import ( "unsafe" ) const ( SizeofXfrmUserpolicyId = 0x40 SizeofXfrmUserpolicyInfo = 0xa8 SizeofXfrmUserTmpl = 0x40 ) // struct xfrm_userpolicy_id { // struct xfrm_selector sel; // __u32 index; // __u8 dir; // }; // type XfrmUserpolicyId struct { Sel XfrmSelector Index uint32 Dir uint8 Pad [3]byte } func (msg *XfrmUserpolicyId) Len() int { return SizeofXfrmUserpolicyId } func DeserializeXfrmUserpolicyId(b []byte) *XfrmUserpolicyId { return (*XfrmUserpolicyId)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyId][0])) } func (msg *XfrmUserpolicyId) Serialize() []byte { return (*(*[SizeofXfrmUserpolicyId]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_userpolicy_info { // struct xfrm_selector sel; // struct xfrm_lifetime_cfg lft; // struct xfrm_lifetime_cur curlft; // __u32 priority; // __u32 index; // __u8 dir; // __u8 action; // #define XFRM_POLICY_ALLOW 0 // #define XFRM_POLICY_BLOCK 1 // __u8 flags; // #define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ // /* Automatically expand selector to include matching ICMP payloads. */ // #define XFRM_POLICY_ICMP 2 // __u8 share; // }; type XfrmUserpolicyInfo struct { Sel XfrmSelector Lft XfrmLifetimeCfg Curlft XfrmLifetimeCur Priority uint32 Index uint32 Dir uint8 Action uint8 Flags uint8 Share uint8 Pad [4]byte } func (msg *XfrmUserpolicyInfo) Len() int { return SizeofXfrmUserpolicyInfo } func DeserializeXfrmUserpolicyInfo(b []byte) *XfrmUserpolicyInfo { return (*XfrmUserpolicyInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyInfo][0])) } func (msg *XfrmUserpolicyInfo) Serialize() []byte { return (*(*[SizeofXfrmUserpolicyInfo]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_user_tmpl { // struct xfrm_id id; // __u16 family; // xfrm_address_t saddr; // __u32 reqid; // __u8 mode; // __u8 share; // __u8 optional; // __u32 aalgos; // __u32 ealgos; // __u32 calgos; // } type XfrmUserTmpl struct { XfrmId XfrmId Family uint16 Pad1 [2]byte Saddr XfrmAddress Reqid uint32 Mode uint8 Share uint8 Optional uint8 Pad2 byte Aalgos uint32 Ealgos uint32 Calgos uint32 } func (msg *XfrmUserTmpl) Len() int { return SizeofXfrmUserTmpl } func DeserializeXfrmUserTmpl(b []byte) *XfrmUserTmpl { return (*XfrmUserTmpl)(unsafe.Pointer(&b[0:SizeofXfrmUserTmpl][0])) } func (msg *XfrmUserTmpl) Serialize() []byte { return (*(*[SizeofXfrmUserTmpl]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go ================================================ package nl import ( "unsafe" ) const ( SizeofXfrmUsersaId = 0x18 SizeofXfrmStats = 0x0c SizeofXfrmUsersaInfo = 0xe0 SizeofXfrmUserSpiInfo = 0xe8 SizeofXfrmAlgo = 0x44 SizeofXfrmAlgoAuth = 0x48 SizeofXfrmAlgoAEAD = 0x48 SizeofXfrmEncapTmpl = 0x18 SizeofXfrmUsersaFlush = 0x1 SizeofXfrmReplayStateEsn = 0x18 ) const ( XFRM_STATE_NOECN = 1 XFRM_STATE_DECAP_DSCP = 2 XFRM_STATE_NOPMTUDISC = 4 XFRM_STATE_WILDRECV = 8 XFRM_STATE_ICMP = 16 XFRM_STATE_AF_UNSPEC = 32 XFRM_STATE_ALIGN4 = 64 XFRM_STATE_ESN = 128 ) // struct xfrm_usersa_id { // xfrm_address_t daddr; // __be32 spi; // __u16 family; // __u8 proto; // }; type XfrmUsersaId struct { Daddr XfrmAddress Spi uint32 // big endian Family uint16 Proto uint8 Pad byte } func (msg *XfrmUsersaId) Len() int { return SizeofXfrmUsersaId } func DeserializeXfrmUsersaId(b []byte) *XfrmUsersaId { return (*XfrmUsersaId)(unsafe.Pointer(&b[0:SizeofXfrmUsersaId][0])) } func (msg *XfrmUsersaId) Serialize() []byte { return (*(*[SizeofXfrmUsersaId]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_stats { // __u32 replay_window; // __u32 replay; // __u32 integrity_failed; // }; type XfrmStats struct { ReplayWindow uint32 Replay uint32 IntegrityFailed uint32 } func (msg *XfrmStats) Len() int { return SizeofXfrmStats } func DeserializeXfrmStats(b []byte) *XfrmStats { return (*XfrmStats)(unsafe.Pointer(&b[0:SizeofXfrmStats][0])) } func (msg *XfrmStats) Serialize() []byte { return (*(*[SizeofXfrmStats]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_usersa_info { // struct xfrm_selector sel; // struct xfrm_id id; // xfrm_address_t saddr; // struct xfrm_lifetime_cfg lft; // struct xfrm_lifetime_cur curlft; // struct xfrm_stats stats; // __u32 seq; // __u32 reqid; // __u16 family; // __u8 mode; /* XFRM_MODE_xxx */ // __u8 replay_window; // __u8 flags; // #define XFRM_STATE_NOECN 1 // #define XFRM_STATE_DECAP_DSCP 2 // #define XFRM_STATE_NOPMTUDISC 4 // #define XFRM_STATE_WILDRECV 8 // #define XFRM_STATE_ICMP 16 // #define XFRM_STATE_AF_UNSPEC 32 // #define XFRM_STATE_ALIGN4 64 // #define XFRM_STATE_ESN 128 // }; // // #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 // type XfrmUsersaInfo struct { Sel XfrmSelector Id XfrmId Saddr XfrmAddress Lft XfrmLifetimeCfg Curlft XfrmLifetimeCur Stats XfrmStats Seq uint32 Reqid uint32 Family uint16 Mode uint8 ReplayWindow uint8 Flags uint8 Pad [7]byte } func (msg *XfrmUsersaInfo) Len() int { return SizeofXfrmUsersaInfo } func DeserializeXfrmUsersaInfo(b []byte) *XfrmUsersaInfo { return (*XfrmUsersaInfo)(unsafe.Pointer(&b[0:SizeofXfrmUsersaInfo][0])) } func (msg *XfrmUsersaInfo) Serialize() []byte { return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_userspi_info { // struct xfrm_usersa_info info; // __u32 min; // __u32 max; // }; type XfrmUserSpiInfo struct { XfrmUsersaInfo XfrmUsersaInfo Min uint32 Max uint32 } func (msg *XfrmUserSpiInfo) Len() int { return SizeofXfrmUserSpiInfo } func DeserializeXfrmUserSpiInfo(b []byte) *XfrmUserSpiInfo { return (*XfrmUserSpiInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserSpiInfo][0])) } func (msg *XfrmUserSpiInfo) Serialize() []byte { return (*(*[SizeofXfrmUserSpiInfo]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_algo { // char alg_name[64]; // unsigned int alg_key_len; /* in bits */ // char alg_key[0]; // }; type XfrmAlgo struct { AlgName [64]byte AlgKeyLen uint32 AlgKey []byte } func (msg *XfrmAlgo) Len() int { return SizeofXfrmAlgo + int(msg.AlgKeyLen/8) } func DeserializeXfrmAlgo(b []byte) *XfrmAlgo { ret := XfrmAlgo{} copy(ret.AlgName[:], b[0:64]) ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) ret.AlgKey = b[68:ret.Len()] return &ret } func (msg *XfrmAlgo) Serialize() []byte { b := make([]byte, msg.Len()) copy(b[0:64], msg.AlgName[:]) copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) copy(b[68:msg.Len()], msg.AlgKey[:]) return b } // struct xfrm_algo_auth { // char alg_name[64]; // unsigned int alg_key_len; /* in bits */ // unsigned int alg_trunc_len; /* in bits */ // char alg_key[0]; // }; type XfrmAlgoAuth struct { AlgName [64]byte AlgKeyLen uint32 AlgTruncLen uint32 AlgKey []byte } func (msg *XfrmAlgoAuth) Len() int { return SizeofXfrmAlgoAuth + int(msg.AlgKeyLen/8) } func DeserializeXfrmAlgoAuth(b []byte) *XfrmAlgoAuth { ret := XfrmAlgoAuth{} copy(ret.AlgName[:], b[0:64]) ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) ret.AlgTruncLen = *(*uint32)(unsafe.Pointer(&b[68])) ret.AlgKey = b[72:ret.Len()] return &ret } func (msg *XfrmAlgoAuth) Serialize() []byte { b := make([]byte, msg.Len()) copy(b[0:64], msg.AlgName[:]) copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgTruncLen)))[:]) copy(b[72:msg.Len()], msg.AlgKey[:]) return b } // struct xfrm_algo_aead { // char alg_name[64]; // unsigned int alg_key_len; /* in bits */ // unsigned int alg_icv_len; /* in bits */ // char alg_key[0]; // } type XfrmAlgoAEAD struct { AlgName [64]byte AlgKeyLen uint32 AlgICVLen uint32 AlgKey []byte } func (msg *XfrmAlgoAEAD) Len() int { return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8) } func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD { ret := XfrmAlgoAEAD{} copy(ret.AlgName[:], b[0:64]) ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68])) ret.AlgKey = b[72:ret.Len()] return &ret } func (msg *XfrmAlgoAEAD) Serialize() []byte { b := make([]byte, msg.Len()) copy(b[0:64], msg.AlgName[:]) copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:]) copy(b[72:msg.Len()], msg.AlgKey[:]) return b } // struct xfrm_encap_tmpl { // __u16 encap_type; // __be16 encap_sport; // __be16 encap_dport; // xfrm_address_t encap_oa; // }; type XfrmEncapTmpl struct { EncapType uint16 EncapSport uint16 // big endian EncapDport uint16 // big endian Pad [2]byte EncapOa XfrmAddress } func (msg *XfrmEncapTmpl) Len() int { return SizeofXfrmEncapTmpl } func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl { return (*XfrmEncapTmpl)(unsafe.Pointer(&b[0:SizeofXfrmEncapTmpl][0])) } func (msg *XfrmEncapTmpl) Serialize() []byte { return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_usersa_flush { // __u8 proto; // }; type XfrmUsersaFlush struct { Proto uint8 } func (msg *XfrmUsersaFlush) Len() int { return SizeofXfrmUsersaFlush } func DeserializeXfrmUsersaFlush(b []byte) *XfrmUsersaFlush { return (*XfrmUsersaFlush)(unsafe.Pointer(&b[0:SizeofXfrmUsersaFlush][0])) } func (msg *XfrmUsersaFlush) Serialize() []byte { return (*(*[SizeofXfrmUsersaFlush]byte)(unsafe.Pointer(msg)))[:] } // struct xfrm_replay_state_esn { // unsigned int bmp_len; // __u32 oseq; // __u32 seq; // __u32 oseq_hi; // __u32 seq_hi; // __u32 replay_window; // __u32 bmp[0]; // }; type XfrmReplayStateEsn struct { BmpLen uint32 OSeq uint32 Seq uint32 OSeqHi uint32 SeqHi uint32 ReplayWindow uint32 Bmp []uint32 } func (msg *XfrmReplayStateEsn) Serialize() []byte { // We deliberately do not pass Bmp, as it gets set by the kernel. return (*(*[SizeofXfrmReplayStateEsn]byte)(unsafe.Pointer(msg)))[:] } ================================================ FILE: vendor/github.com/vishvananda/netlink/order.go ================================================ package netlink import ( "encoding/binary" "github.com/vishvananda/netlink/nl" ) var ( native = nl.NativeEndian() networkOrder = binary.BigEndian ) func htonl(val uint32) []byte { bytes := make([]byte, 4) binary.BigEndian.PutUint32(bytes, val) return bytes } func htons(val uint16) []byte { bytes := make([]byte, 2) binary.BigEndian.PutUint16(bytes, val) return bytes } func ntohl(buf []byte) uint32 { return binary.BigEndian.Uint32(buf) } func ntohs(buf []byte) uint16 { return binary.BigEndian.Uint16(buf) } ================================================ FILE: vendor/github.com/vishvananda/netlink/protinfo.go ================================================ package netlink import ( "strings" ) // Protinfo represents bridge flags from netlink. type Protinfo struct { Hairpin bool Guard bool FastLeave bool RootBlock bool Learning bool Flood bool ProxyArp bool ProxyArpWiFi bool } // String returns a list of enabled flags func (prot *Protinfo) String() string { if prot == nil { return "" } var boolStrings []string if prot.Hairpin { boolStrings = append(boolStrings, "Hairpin") } if prot.Guard { boolStrings = append(boolStrings, "Guard") } if prot.FastLeave { boolStrings = append(boolStrings, "FastLeave") } if prot.RootBlock { boolStrings = append(boolStrings, "RootBlock") } if prot.Learning { boolStrings = append(boolStrings, "Learning") } if prot.Flood { boolStrings = append(boolStrings, "Flood") } if prot.ProxyArp { boolStrings = append(boolStrings, "ProxyArp") } if prot.ProxyArpWiFi { boolStrings = append(boolStrings, "ProxyArpWiFi") } return strings.Join(boolStrings, " ") } func boolToByte(x bool) []byte { if x { return []byte{1} } return []byte{0} } func byteToBool(x byte) bool { return uint8(x) != 0 } ================================================ FILE: vendor/github.com/vishvananda/netlink/protinfo_linux.go ================================================ package netlink import ( "fmt" "syscall" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) func LinkGetProtinfo(link Link) (Protinfo, error) { return pkgHandle.LinkGetProtinfo(link) } func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) { base := link.Attrs() h.ensureIndex(base) var pi Protinfo req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(unix.AF_BRIDGE) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, 0) if err != nil { return pi, err } for _, m := range msgs { ans := nl.DeserializeIfInfomsg(m) if int(ans.Index) != base.Index { continue } attrs, err := nl.ParseRouteAttr(m[ans.Len():]) if err != nil { return pi, err } for _, attr := range attrs { if attr.Attr.Type != unix.IFLA_PROTINFO|unix.NLA_F_NESTED { continue } infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return pi, err } pi = parseProtinfo(infos) return pi, nil } } return pi, fmt.Errorf("Device with index %d not found", base.Index) } func parseProtinfo(infos []syscall.NetlinkRouteAttr) (pi Protinfo) { for _, info := range infos { switch info.Attr.Type { case nl.IFLA_BRPORT_MODE: pi.Hairpin = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_GUARD: pi.Guard = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_FAST_LEAVE: pi.FastLeave = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_PROTECT: pi.RootBlock = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_LEARNING: pi.Learning = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_UNICAST_FLOOD: pi.Flood = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_PROXYARP: pi.ProxyArp = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_PROXYARP_WIFI: pi.ProxyArpWiFi = byteToBool(info.Value[0]) } } return } ================================================ FILE: vendor/github.com/vishvananda/netlink/qdisc.go ================================================ package netlink import ( "fmt" "math" ) const ( HANDLE_NONE = 0 HANDLE_INGRESS = 0xFFFFFFF1 HANDLE_CLSACT = HANDLE_INGRESS HANDLE_ROOT = 0xFFFFFFFF PRIORITY_MAP_LEN = 16 ) const ( HANDLE_MIN_INGRESS = 0xFFFFFFF2 HANDLE_MIN_EGRESS = 0xFFFFFFF3 ) type Qdisc interface { Attrs() *QdiscAttrs Type() string } // QdiscAttrs represents a netlink qdisc. A qdisc is associated with a link, // has a handle, a parent and a refcnt. The root qdisc of a device should // have parent == HANDLE_ROOT. type QdiscAttrs struct { LinkIndex int Handle uint32 Parent uint32 Refcnt uint32 // read only } func (q QdiscAttrs) String() string { return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt) } func MakeHandle(major, minor uint16) uint32 { return (uint32(major) << 16) | uint32(minor) } func MajorMinor(handle uint32) (uint16, uint16) { return uint16((handle & 0xFFFF0000) >> 16), uint16(handle & 0x0000FFFFF) } func HandleStr(handle uint32) string { switch handle { case HANDLE_NONE: return "none" case HANDLE_INGRESS: return "ingress" case HANDLE_ROOT: return "root" default: major, minor := MajorMinor(handle) return fmt.Sprintf("%x:%x", major, minor) } } func Percentage2u32(percentage float32) uint32 { // FIXME this is most likely not the best way to convert from % to uint32 if percentage == 100 { return math.MaxUint32 } return uint32(math.MaxUint32 * (percentage / 100)) } // PfifoFast is the default qdisc created by the kernel if one has not // been defined for the interface type PfifoFast struct { QdiscAttrs Bands uint8 PriorityMap [PRIORITY_MAP_LEN]uint8 } func (qdisc *PfifoFast) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *PfifoFast) Type() string { return "pfifo_fast" } // Prio is a basic qdisc that works just like PfifoFast type Prio struct { QdiscAttrs Bands uint8 PriorityMap [PRIORITY_MAP_LEN]uint8 } func NewPrio(attrs QdiscAttrs) *Prio { return &Prio{ QdiscAttrs: attrs, Bands: 3, PriorityMap: [PRIORITY_MAP_LEN]uint8{1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, } } func (qdisc *Prio) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Prio) Type() string { return "prio" } // Htb is a classful qdisc that rate limits based on tokens type Htb struct { QdiscAttrs Version uint32 Rate2Quantum uint32 Defcls uint32 Debug uint32 DirectPkts uint32 } func NewHtb(attrs QdiscAttrs) *Htb { return &Htb{ QdiscAttrs: attrs, Version: 3, Defcls: 0, Rate2Quantum: 10, Debug: 0, DirectPkts: 0, } } func (qdisc *Htb) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Htb) Type() string { return "htb" } // Netem is a classless qdisc that rate limits based on tokens type NetemQdiscAttrs struct { Latency uint32 // in us DelayCorr float32 // in % Limit uint32 Loss float32 // in % LossCorr float32 // in % Gap uint32 Duplicate float32 // in % DuplicateCorr float32 // in % Jitter uint32 // in us ReorderProb float32 // in % ReorderCorr float32 // in % CorruptProb float32 // in % CorruptCorr float32 // in % } func (q NetemQdiscAttrs) String() string { return fmt.Sprintf( "{Latency: %d, Limit: %d, Loss: %f, Gap: %d, Duplicate: %f, Jitter: %d}", q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter, ) } type Netem struct { QdiscAttrs Latency uint32 DelayCorr uint32 Limit uint32 Loss uint32 LossCorr uint32 Gap uint32 Duplicate uint32 DuplicateCorr uint32 Jitter uint32 ReorderProb uint32 ReorderCorr uint32 CorruptProb uint32 CorruptCorr uint32 } func (netem *Netem) String() string { return fmt.Sprintf( "{Latency: %v, Limit: %v, Loss: %v, Gap: %v, Duplicate: %v, Jitter: %v}", netem.Latency, netem.Limit, netem.Loss, netem.Gap, netem.Duplicate, netem.Jitter, ) } func (qdisc *Netem) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Netem) Type() string { return "netem" } // Tbf is a classless qdisc that rate limits based on tokens type Tbf struct { QdiscAttrs Rate uint64 Limit uint32 Buffer uint32 Peakrate uint64 Minburst uint32 // TODO: handle other settings } func (qdisc *Tbf) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Tbf) Type() string { return "tbf" } // Ingress is a qdisc for adding ingress filters type Ingress struct { QdiscAttrs } func (qdisc *Ingress) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Ingress) Type() string { return "ingress" } // GenericQdisc qdiscs represent types that are not currently understood // by this netlink library. type GenericQdisc struct { QdiscAttrs QdiscType string } func (qdisc *GenericQdisc) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *GenericQdisc) Type() string { return qdisc.QdiscType } type Hfsc struct { QdiscAttrs Defcls uint16 } func NewHfsc(attrs QdiscAttrs) *Hfsc { return &Hfsc{ QdiscAttrs: attrs, Defcls: 1, } } func (hfsc *Hfsc) Attrs() *QdiscAttrs { return &hfsc.QdiscAttrs } func (hfsc *Hfsc) Type() string { return "hfsc" } func (hfsc *Hfsc) String() string { return fmt.Sprintf( "{%v -- default: %d}", hfsc.Attrs(), hfsc.Defcls, ) } // Fq is a classless packet scheduler meant to be mostly used for locally generated traffic. type Fq struct { QdiscAttrs PacketLimit uint32 FlowPacketLimit uint32 // In bytes Quantum uint32 InitialQuantum uint32 // called RateEnable under the hood Pacing uint32 FlowDefaultRate uint32 FlowMaxRate uint32 // called BucketsLog under the hood Buckets uint32 FlowRefillDelay uint32 LowRateThreshold uint32 } func (fq *Fq) String() string { return fmt.Sprintf( "{PacketLimit: %v, FlowPacketLimit: %v, Quantum: %v, InitialQuantum: %v, Pacing: %v, FlowDefaultRate: %v, FlowMaxRate: %v, Buckets: %v, FlowRefillDelay: %v, LowRateThreshold: %v}", fq.PacketLimit, fq.FlowPacketLimit, fq.Quantum, fq.InitialQuantum, fq.Pacing, fq.FlowDefaultRate, fq.FlowMaxRate, fq.Buckets, fq.FlowRefillDelay, fq.LowRateThreshold, ) } func NewFq(attrs QdiscAttrs) *Fq { return &Fq{ QdiscAttrs: attrs, Pacing: 1, } } func (qdisc *Fq) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Fq) Type() string { return "fq" } // FQ_Codel (Fair Queuing Controlled Delay) is queuing discipline that combines Fair Queuing with the CoDel AQM scheme. type FqCodel struct { QdiscAttrs Target uint32 Limit uint32 Interval uint32 ECN uint32 Flows uint32 Quantum uint32 CEThreshold uint32 DropBatchSize uint32 MemoryLimit uint32 } func (fqcodel *FqCodel) String() string { return fmt.Sprintf( "{%v -- Target: %v, Limit: %v, Interval: %v, ECM: %v, Flows: %v, Quantum: %v}", fqcodel.Attrs(), fqcodel.Target, fqcodel.Limit, fqcodel.Interval, fqcodel.ECN, fqcodel.Flows, fqcodel.Quantum, ) } func NewFqCodel(attrs QdiscAttrs) *FqCodel { return &FqCodel{ QdiscAttrs: attrs, ECN: 1, } } func (qdisc *FqCodel) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *FqCodel) Type() string { return "fq_codel" } type Sfq struct { QdiscAttrs // TODO: Only the simplified options for SFQ are handled here. Support for the extended one can be added later. Quantum uint8 Perturb uint8 Limit uint32 Divisor uint8 } func (sfq *Sfq) String() string { return fmt.Sprintf( "{%v -- Quantum: %v, Perturb: %v, Limit: %v, Divisor: %v}", sfq.Attrs(), sfq.Quantum, sfq.Perturb, sfq.Limit, sfq.Divisor, ) } func (qdisc *Sfq) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } func (qdisc *Sfq) Type() string { return "sfq" } ================================================ FILE: vendor/github.com/vishvananda/netlink/qdisc_linux.go ================================================ package netlink import ( "fmt" "io/ioutil" "strconv" "strings" "syscall" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // NOTE function is here because it uses other linux functions func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem { var limit uint32 = 1000 var lossCorr, delayCorr, duplicateCorr uint32 var reorderProb, reorderCorr uint32 var corruptProb, corruptCorr uint32 latency := nattrs.Latency loss := Percentage2u32(nattrs.Loss) gap := nattrs.Gap duplicate := Percentage2u32(nattrs.Duplicate) jitter := nattrs.Jitter // Correlation if latency > 0 && jitter > 0 { delayCorr = Percentage2u32(nattrs.DelayCorr) } if loss > 0 { lossCorr = Percentage2u32(nattrs.LossCorr) } if duplicate > 0 { duplicateCorr = Percentage2u32(nattrs.DuplicateCorr) } // FIXME should validate values(like loss/duplicate are percentages...) latency = time2Tick(latency) if nattrs.Limit != 0 { limit = nattrs.Limit } // Jitter is only value if latency is > 0 if latency > 0 { jitter = time2Tick(jitter) } reorderProb = Percentage2u32(nattrs.ReorderProb) reorderCorr = Percentage2u32(nattrs.ReorderCorr) if reorderProb > 0 { // ERROR if lantency == 0 if gap == 0 { gap = 1 } } corruptProb = Percentage2u32(nattrs.CorruptProb) corruptCorr = Percentage2u32(nattrs.CorruptCorr) return &Netem{ QdiscAttrs: attrs, Latency: latency, DelayCorr: delayCorr, Limit: limit, Loss: loss, LossCorr: lossCorr, Gap: gap, Duplicate: duplicate, DuplicateCorr: duplicateCorr, Jitter: jitter, ReorderProb: reorderProb, ReorderCorr: reorderCorr, CorruptProb: corruptProb, CorruptCorr: corruptCorr, } } // QdiscDel will delete a qdisc from the system. // Equivalent to: `tc qdisc del $qdisc` func QdiscDel(qdisc Qdisc) error { return pkgHandle.QdiscDel(qdisc) } // QdiscDel will delete a qdisc from the system. // Equivalent to: `tc qdisc del $qdisc` func (h *Handle) QdiscDel(qdisc Qdisc) error { return h.qdiscModify(unix.RTM_DELQDISC, 0, qdisc) } // QdiscChange will change a qdisc in place // Equivalent to: `tc qdisc change $qdisc` // The parent and handle MUST NOT be changed. func QdiscChange(qdisc Qdisc) error { return pkgHandle.QdiscChange(qdisc) } // QdiscChange will change a qdisc in place // Equivalent to: `tc qdisc change $qdisc` // The parent and handle MUST NOT be changed. func (h *Handle) QdiscChange(qdisc Qdisc) error { return h.qdiscModify(unix.RTM_NEWQDISC, 0, qdisc) } // QdiscReplace will replace a qdisc to the system. // Equivalent to: `tc qdisc replace $qdisc` // The handle MUST change. func QdiscReplace(qdisc Qdisc) error { return pkgHandle.QdiscReplace(qdisc) } // QdiscReplace will replace a qdisc to the system. // Equivalent to: `tc qdisc replace $qdisc` // The handle MUST change. func (h *Handle) QdiscReplace(qdisc Qdisc) error { return h.qdiscModify( unix.RTM_NEWQDISC, unix.NLM_F_CREATE|unix.NLM_F_REPLACE, qdisc) } // QdiscAdd will add a qdisc to the system. // Equivalent to: `tc qdisc add $qdisc` func QdiscAdd(qdisc Qdisc) error { return pkgHandle.QdiscAdd(qdisc) } // QdiscAdd will add a qdisc to the system. // Equivalent to: `tc qdisc add $qdisc` func (h *Handle) QdiscAdd(qdisc Qdisc) error { return h.qdiscModify( unix.RTM_NEWQDISC, unix.NLM_F_CREATE|unix.NLM_F_EXCL, qdisc) } func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error { req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK) base := qdisc.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: int32(base.LinkIndex), Handle: base.Handle, Parent: base.Parent, } req.AddData(msg) // When deleting don't bother building the rest of the netlink payload if cmd != unix.RTM_DELQDISC { if err := qdiscPayload(req, qdisc); err != nil { return err } } _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error { req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type()))) options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) switch qdisc := qdisc.(type) { case *Prio: tcmap := nl.TcPrioMap{ Bands: int32(qdisc.Bands), Priomap: qdisc.PriorityMap, } options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize()) case *Tbf: opt := nl.TcTbfQopt{} opt.Rate.Rate = uint32(qdisc.Rate) opt.Peakrate.Rate = uint32(qdisc.Peakrate) opt.Limit = qdisc.Limit opt.Buffer = qdisc.Buffer options.AddRtAttr(nl.TCA_TBF_PARMS, opt.Serialize()) if qdisc.Rate >= uint64(1<<32) { options.AddRtAttr(nl.TCA_TBF_RATE64, nl.Uint64Attr(qdisc.Rate)) } if qdisc.Peakrate >= uint64(1<<32) { options.AddRtAttr(nl.TCA_TBF_PRATE64, nl.Uint64Attr(qdisc.Peakrate)) } if qdisc.Peakrate > 0 { options.AddRtAttr(nl.TCA_TBF_PBURST, nl.Uint32Attr(qdisc.Minburst)) } case *Htb: opt := nl.TcHtbGlob{} opt.Version = qdisc.Version opt.Rate2Quantum = qdisc.Rate2Quantum opt.Defcls = qdisc.Defcls // TODO: Handle Debug properly. For now default to 0 opt.Debug = qdisc.Debug opt.DirectPkts = qdisc.DirectPkts options.AddRtAttr(nl.TCA_HTB_INIT, opt.Serialize()) // options.AddRtAttr(nl.TCA_HTB_DIRECT_QLEN, opt.Serialize()) case *Hfsc: opt := nl.TcHfscOpt{} opt.Defcls = qdisc.Defcls options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize()) case *Netem: opt := nl.TcNetemQopt{} opt.Latency = qdisc.Latency opt.Limit = qdisc.Limit opt.Loss = qdisc.Loss opt.Gap = qdisc.Gap opt.Duplicate = qdisc.Duplicate opt.Jitter = qdisc.Jitter options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize()) // Correlation corr := nl.TcNetemCorr{} corr.DelayCorr = qdisc.DelayCorr corr.LossCorr = qdisc.LossCorr corr.DupCorr = qdisc.DuplicateCorr if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 { options.AddRtAttr(nl.TCA_NETEM_CORR, corr.Serialize()) } // Corruption corruption := nl.TcNetemCorrupt{} corruption.Probability = qdisc.CorruptProb corruption.Correlation = qdisc.CorruptCorr if corruption.Probability > 0 { options.AddRtAttr(nl.TCA_NETEM_CORRUPT, corruption.Serialize()) } // Reorder reorder := nl.TcNetemReorder{} reorder.Probability = qdisc.ReorderProb reorder.Correlation = qdisc.ReorderCorr if reorder.Probability > 0 { options.AddRtAttr(nl.TCA_NETEM_REORDER, reorder.Serialize()) } case *Ingress: // ingress filters must use the proper handle if qdisc.Attrs().Parent != HANDLE_INGRESS { return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS") } case *FqCodel: options.AddRtAttr(nl.TCA_FQ_CODEL_ECN, nl.Uint32Attr((uint32(qdisc.ECN)))) if qdisc.Limit > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_LIMIT, nl.Uint32Attr((uint32(qdisc.Limit)))) } if qdisc.Interval > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_INTERVAL, nl.Uint32Attr((uint32(qdisc.Interval)))) } if qdisc.Flows > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_FLOWS, nl.Uint32Attr((uint32(qdisc.Flows)))) } if qdisc.Quantum > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum)))) } if qdisc.CEThreshold > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_CE_THRESHOLD, nl.Uint32Attr(qdisc.CEThreshold)) } if qdisc.DropBatchSize > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_DROP_BATCH_SIZE, nl.Uint32Attr(qdisc.DropBatchSize)) } if qdisc.MemoryLimit > 0 { options.AddRtAttr(nl.TCA_FQ_CODEL_MEMORY_LIMIT, nl.Uint32Attr(qdisc.MemoryLimit)) } case *Fq: options.AddRtAttr(nl.TCA_FQ_RATE_ENABLE, nl.Uint32Attr((uint32(qdisc.Pacing)))) if qdisc.Buckets > 0 { options.AddRtAttr(nl.TCA_FQ_BUCKETS_LOG, nl.Uint32Attr((uint32(qdisc.Buckets)))) } if qdisc.LowRateThreshold > 0 { options.AddRtAttr(nl.TCA_FQ_LOW_RATE_THRESHOLD, nl.Uint32Attr((uint32(qdisc.LowRateThreshold)))) } if qdisc.Quantum > 0 { options.AddRtAttr(nl.TCA_FQ_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum)))) } if qdisc.InitialQuantum > 0 { options.AddRtAttr(nl.TCA_FQ_INITIAL_QUANTUM, nl.Uint32Attr((uint32(qdisc.InitialQuantum)))) } if qdisc.FlowRefillDelay > 0 { options.AddRtAttr(nl.TCA_FQ_FLOW_REFILL_DELAY, nl.Uint32Attr((uint32(qdisc.FlowRefillDelay)))) } if qdisc.FlowPacketLimit > 0 { options.AddRtAttr(nl.TCA_FQ_FLOW_PLIMIT, nl.Uint32Attr((uint32(qdisc.FlowPacketLimit)))) } if qdisc.FlowMaxRate > 0 { options.AddRtAttr(nl.TCA_FQ_FLOW_MAX_RATE, nl.Uint32Attr((uint32(qdisc.FlowMaxRate)))) } if qdisc.FlowDefaultRate > 0 { options.AddRtAttr(nl.TCA_FQ_FLOW_DEFAULT_RATE, nl.Uint32Attr((uint32(qdisc.FlowDefaultRate)))) } case *Sfq: opt := nl.TcSfqQoptV1{} opt.TcSfqQopt.Quantum = qdisc.Quantum opt.TcSfqQopt.Perturb = int32(qdisc.Perturb) opt.TcSfqQopt.Limit = qdisc.Limit opt.TcSfqQopt.Divisor = qdisc.Divisor options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize()) default: options = nil } if options != nil { req.AddData(options) } return nil } // QdiscList gets a list of qdiscs in the system. // Equivalent to: `tc qdisc show`. // The list can be filtered by link. func QdiscList(link Link) ([]Qdisc, error) { return pkgHandle.QdiscList(link) } // QdiscList gets a list of qdiscs in the system. // Equivalent to: `tc qdisc show`. // The list can be filtered by link. func (h *Handle) QdiscList(link Link) ([]Qdisc, error) { req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP) index := int32(0) if link != nil { base := link.Attrs() h.ensureIndex(base) index = int32(base.Index) } msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: index, } req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC) if err != nil { return nil, err } var res []Qdisc for _, m := range msgs { msg := nl.DeserializeTcMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } // skip qdiscs from other interfaces if link != nil && msg.Ifindex != index { continue } base := QdiscAttrs{ LinkIndex: int(msg.Ifindex), Handle: msg.Handle, Parent: msg.Parent, Refcnt: msg.Info, } var qdisc Qdisc qdiscType := "" for _, attr := range attrs { switch attr.Attr.Type { case nl.TCA_KIND: qdiscType = string(attr.Value[:len(attr.Value)-1]) switch qdiscType { case "pfifo_fast": qdisc = &PfifoFast{} case "prio": qdisc = &Prio{} case "tbf": qdisc = &Tbf{} case "ingress": qdisc = &Ingress{} case "htb": qdisc = &Htb{} case "fq": qdisc = &Fq{} case "hfsc": qdisc = &Hfsc{} case "fq_codel": qdisc = &FqCodel{} case "netem": qdisc = &Netem{} case "sfq": qdisc = &Sfq{} default: qdisc = &GenericQdisc{QdiscType: qdiscType} } case nl.TCA_OPTIONS: switch qdiscType { case "pfifo_fast": // pfifo returns TcPrioMap directly without wrapping it in rtattr if err := parsePfifoFastData(qdisc, attr.Value); err != nil { return nil, err } case "prio": // prio returns TcPrioMap directly without wrapping it in rtattr if err := parsePrioData(qdisc, attr.Value); err != nil { return nil, err } case "tbf": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseTbfData(qdisc, data); err != nil { return nil, err } case "hfsc": if err := parseHfscData(qdisc, attr.Value); err != nil { return nil, err } case "htb": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseHtbData(qdisc, data); err != nil { return nil, err } case "fq": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseFqData(qdisc, data); err != nil { return nil, err } case "fq_codel": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseFqCodelData(qdisc, data); err != nil { return nil, err } case "netem": if err := parseNetemData(qdisc, attr.Value); err != nil { return nil, err } case "sfq": if err := parseSfqData(qdisc, attr.Value); err != nil { return nil, err } // no options for ingress } } } *qdisc.Attrs() = base res = append(res, qdisc) } return res, nil } func parsePfifoFastData(qdisc Qdisc, value []byte) error { pfifo := qdisc.(*PfifoFast) tcmap := nl.DeserializeTcPrioMap(value) pfifo.PriorityMap = tcmap.Priomap pfifo.Bands = uint8(tcmap.Bands) return nil } func parsePrioData(qdisc Qdisc, value []byte) error { prio := qdisc.(*Prio) tcmap := nl.DeserializeTcPrioMap(value) prio.PriorityMap = tcmap.Priomap prio.Bands = uint8(tcmap.Bands) return nil } func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { htb := qdisc.(*Htb) for _, datum := range data { switch datum.Attr.Type { case nl.TCA_HTB_INIT: opt := nl.DeserializeTcHtbGlob(datum.Value) htb.Version = opt.Version htb.Rate2Quantum = opt.Rate2Quantum htb.Defcls = opt.Defcls htb.Debug = opt.Debug htb.DirectPkts = opt.DirectPkts case nl.TCA_HTB_DIRECT_QLEN: // TODO //htb.DirectQlen = native.uint32(datum.Value) } } return nil } func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { fqCodel := qdisc.(*FqCodel) for _, datum := range data { switch datum.Attr.Type { case nl.TCA_FQ_CODEL_TARGET: fqCodel.Target = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_LIMIT: fqCodel.Limit = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_INTERVAL: fqCodel.Interval = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_ECN: fqCodel.ECN = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_FLOWS: fqCodel.Flows = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_QUANTUM: fqCodel.Quantum = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_CE_THRESHOLD: fqCodel.CEThreshold = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_DROP_BATCH_SIZE: fqCodel.DropBatchSize = native.Uint32(datum.Value) case nl.TCA_FQ_CODEL_MEMORY_LIMIT: fqCodel.MemoryLimit = native.Uint32(datum.Value) } } return nil } func parseHfscData(qdisc Qdisc, data []byte) error { Hfsc := qdisc.(*Hfsc) Hfsc.Defcls = native.Uint16(data) return nil } func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { fq := qdisc.(*Fq) for _, datum := range data { switch datum.Attr.Type { case nl.TCA_FQ_BUCKETS_LOG: fq.Buckets = native.Uint32(datum.Value) case nl.TCA_FQ_LOW_RATE_THRESHOLD: fq.LowRateThreshold = native.Uint32(datum.Value) case nl.TCA_FQ_QUANTUM: fq.Quantum = native.Uint32(datum.Value) case nl.TCA_FQ_RATE_ENABLE: fq.Pacing = native.Uint32(datum.Value) case nl.TCA_FQ_INITIAL_QUANTUM: fq.InitialQuantum = native.Uint32(datum.Value) case nl.TCA_FQ_ORPHAN_MASK: // TODO case nl.TCA_FQ_FLOW_REFILL_DELAY: fq.FlowRefillDelay = native.Uint32(datum.Value) case nl.TCA_FQ_FLOW_PLIMIT: fq.FlowPacketLimit = native.Uint32(datum.Value) case nl.TCA_FQ_PLIMIT: fq.PacketLimit = native.Uint32(datum.Value) case nl.TCA_FQ_FLOW_MAX_RATE: fq.FlowMaxRate = native.Uint32(datum.Value) case nl.TCA_FQ_FLOW_DEFAULT_RATE: fq.FlowDefaultRate = native.Uint32(datum.Value) } } return nil } func parseNetemData(qdisc Qdisc, value []byte) error { netem := qdisc.(*Netem) opt := nl.DeserializeTcNetemQopt(value) netem.Latency = opt.Latency netem.Limit = opt.Limit netem.Loss = opt.Loss netem.Gap = opt.Gap netem.Duplicate = opt.Duplicate netem.Jitter = opt.Jitter data, err := nl.ParseRouteAttr(value[nl.SizeofTcNetemQopt:]) if err != nil { return err } for _, datum := range data { switch datum.Attr.Type { case nl.TCA_NETEM_CORR: opt := nl.DeserializeTcNetemCorr(datum.Value) netem.DelayCorr = opt.DelayCorr netem.LossCorr = opt.LossCorr netem.DuplicateCorr = opt.DupCorr case nl.TCA_NETEM_CORRUPT: opt := nl.DeserializeTcNetemCorrupt(datum.Value) netem.CorruptProb = opt.Probability netem.CorruptCorr = opt.Correlation case nl.TCA_NETEM_REORDER: opt := nl.DeserializeTcNetemReorder(datum.Value) netem.ReorderProb = opt.Probability netem.ReorderCorr = opt.Correlation } } return nil } func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { tbf := qdisc.(*Tbf) for _, datum := range data { switch datum.Attr.Type { case nl.TCA_TBF_PARMS: opt := nl.DeserializeTcTbfQopt(datum.Value) tbf.Rate = uint64(opt.Rate.Rate) tbf.Peakrate = uint64(opt.Peakrate.Rate) tbf.Limit = opt.Limit tbf.Buffer = opt.Buffer case nl.TCA_TBF_RATE64: tbf.Rate = native.Uint64(datum.Value[0:8]) case nl.TCA_TBF_PRATE64: tbf.Peakrate = native.Uint64(datum.Value[0:8]) case nl.TCA_TBF_PBURST: tbf.Minburst = native.Uint32(datum.Value[0:4]) } } return nil } func parseSfqData(qdisc Qdisc, value []byte) error { sfq := qdisc.(*Sfq) opt := nl.DeserializeTcSfqQoptV1(value) sfq.Quantum = opt.TcSfqQopt.Quantum sfq.Perturb = uint8(opt.TcSfqQopt.Perturb) sfq.Limit = opt.TcSfqQopt.Limit sfq.Divisor = opt.TcSfqQopt.Divisor return nil } const ( TIME_UNITS_PER_SEC = 1000000 ) var ( tickInUsec float64 clockFactor float64 hz float64 ) func initClock() { data, err := ioutil.ReadFile("/proc/net/psched") if err != nil { return } parts := strings.Split(strings.TrimSpace(string(data)), " ") if len(parts) < 4 { return } var vals [4]uint64 for i := range vals { val, err := strconv.ParseUint(parts[i], 16, 32) if err != nil { return } vals[i] = val } // compatibility if vals[2] == 1000000000 { vals[0] = vals[1] } clockFactor = float64(vals[2]) / TIME_UNITS_PER_SEC tickInUsec = float64(vals[0]) / float64(vals[1]) * clockFactor if vals[2] == 1000000 { // ref https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c#n963 hz = float64(vals[3]) } else { hz = 100 } } func TickInUsec() float64 { if tickInUsec == 0.0 { initClock() } return tickInUsec } func ClockFactor() float64 { if clockFactor == 0.0 { initClock() } return clockFactor } func Hz() float64 { if hz == 0.0 { initClock() } return hz } func time2Tick(time uint32) uint32 { return uint32(float64(time) * TickInUsec()) } func tick2Time(tick uint32) uint32 { return uint32(float64(tick) / TickInUsec()) } func time2Ktime(time uint32) uint32 { return uint32(float64(time) * ClockFactor()) } func ktime2Time(ktime uint32) uint32 { return uint32(float64(ktime) / ClockFactor()) } func burst(rate uint64, buffer uint32) uint32 { return uint32(float64(rate) * float64(tick2Time(buffer)) / TIME_UNITS_PER_SEC) } func latency(rate uint64, limit, buffer uint32) float64 { return TIME_UNITS_PER_SEC*(float64(limit)/float64(rate)) - float64(tick2Time(buffer)) } func Xmittime(rate uint64, size uint32) uint32 { // https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/tc/tc_core.c#n62 return time2Tick(uint32(TIME_UNITS_PER_SEC * (float64(size) / float64(rate)))) } ================================================ FILE: vendor/github.com/vishvananda/netlink/rdma_link_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "fmt" "net" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // LinkAttrs represents data shared by most link types type RdmaLinkAttrs struct { Index uint32 Name string FirmwareVersion string NodeGuid string SysImageGuid string } // Link represents a rdma device from netlink. type RdmaLink struct { Attrs RdmaLinkAttrs } func getProtoField(clientType int, op int) int { return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op) } func uint64ToGuidString(guid uint64) string { //Convert to byte array sysGuidBytes := new(bytes.Buffer) binary.Write(sysGuidBytes, binary.LittleEndian, guid) //Convert to HardwareAddr sysGuidNet := net.HardwareAddr(sysGuidBytes.Bytes()) //Get the String return sysGuidNet.String() } func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) { link := RdmaLink{} reader := bytes.NewReader(data) for reader.Len() >= 4 { _, attrType, len, value := parseNfAttrTLV(reader) switch attrType { case nl.RDMA_NLDEV_ATTR_DEV_INDEX: var Index uint32 r := bytes.NewReader(value) binary.Read(r, nl.NativeEndian(), &Index) link.Attrs.Index = Index case nl.RDMA_NLDEV_ATTR_DEV_NAME: link.Attrs.Name = string(value[0 : len-1]) case nl.RDMA_NLDEV_ATTR_FW_VERSION: link.Attrs.FirmwareVersion = string(value[0 : len-1]) case nl.RDMA_NLDEV_ATTR_NODE_GUID: var guid uint64 r := bytes.NewReader(value) binary.Read(r, nl.NativeEndian(), &guid) link.Attrs.NodeGuid = uint64ToGuidString(guid) case nl.RDMA_NLDEV_ATTR_SYS_IMAGE_GUID: var sysGuid uint64 r := bytes.NewReader(value) binary.Read(r, nl.NativeEndian(), &sysGuid) link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid) } if (len % 4) != 0 { // Skip pad bytes reader.Seek(int64(4-(len%4)), seekCurrent) } } return &link, nil } func execRdmaSetLink(req *nl.NetlinkRequest) error { _, err := req.Execute(unix.NETLINK_RDMA, 0) return err } // RdmaLinkList gets a list of RDMA link devices. // Equivalent to: `rdma dev show` func RdmaLinkList() ([]*RdmaLink, error) { return pkgHandle.RdmaLinkList() } // RdmaLinkList gets a list of RDMA link devices. // Equivalent to: `rdma dev show` func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) { proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) msgs, err := req.Execute(unix.NETLINK_RDMA, 0) if err != nil { return nil, err } var res []*RdmaLink for _, m := range msgs { link, err := executeOneGetRdmaLink(m) if err != nil { return nil, err } res = append(res, link) } return res, nil } // RdmaLinkByName finds a link by name and returns a pointer to the object if // found and nil error, otherwise returns error code. func RdmaLinkByName(name string) (*RdmaLink, error) { return pkgHandle.RdmaLinkByName(name) } // RdmaLinkByName finds a link by name and returns a pointer to the object if // found and nil error, otherwise returns error code. func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) { links, err := h.RdmaLinkList() if err != nil { return nil, err } for _, link := range links { if link.Attrs.Name == name { return link, nil } } return nil, fmt.Errorf("Rdma device %v not found", name) } // RdmaLinkSetName sets the name of the rdma link device. Return nil on success // or error otherwise. // Equivalent to: `rdma dev set $old_devname name $name` func RdmaLinkSetName(link *RdmaLink, name string) error { return pkgHandle.RdmaLinkSetName(link, name) } // RdmaLinkSetName sets the name of the rdma link device. Return nil on success // or error otherwise. // Equivalent to: `rdma dev set $old_devname name $name` func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error { proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) b := make([]byte, 4) native.PutUint32(b, uint32(link.Attrs.Index)) data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b) req.AddData(data) b = make([]byte, len(name)+1) copy(b, name) data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b) req.AddData(data) return execRdmaSetLink(req) } func netnsModeToString(mode uint8) string { switch mode { case 0: return "exclusive" case 1: return "shared" default: return "unknown" } } func executeOneGetRdmaNetnsMode(data []byte) (string, error) { reader := bytes.NewReader(data) for reader.Len() >= 4 { _, attrType, len, value := parseNfAttrTLV(reader) switch attrType { case nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE: var mode uint8 r := bytes.NewReader(value) binary.Read(r, nl.NativeEndian(), &mode) return netnsModeToString(mode), nil } if (len % 4) != 0 { // Skip pad bytes reader.Seek(int64(4-(len%4)), seekCurrent) } } return "", fmt.Errorf("Invalid netns mode") } // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem // Returns mode string and error status as nil on success or returns error // otherwise. // Equivalent to: `rdma system show netns' func RdmaSystemGetNetnsMode() (string, error) { return pkgHandle.RdmaSystemGetNetnsMode() } // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem // Returns mode string and error status as nil on success or returns error // otherwise. // Equivalent to: `rdma system show netns' func (h *Handle) RdmaSystemGetNetnsMode() (string, error) { proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_GET) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) msgs, err := req.Execute(unix.NETLINK_RDMA, 0) if err != nil { return "", err } if len(msgs) == 0 { return "", fmt.Errorf("No valid response from kernel") } return executeOneGetRdmaNetnsMode(msgs[0]) } func netnsModeStringToUint8(mode string) (uint8, error) { switch mode { case "exclusive": return 0, nil case "shared": return 1, nil default: return 0, fmt.Errorf("Invalid mode; %q", mode) } } // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem // Returns nil on success or appropriate error code. // Equivalent to: `rdma system set netns { shared | exclusive }' func RdmaSystemSetNetnsMode(NewMode string) error { return pkgHandle.RdmaSystemSetNetnsMode(NewMode) } // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem // Returns nil on success or appropriate error code. // Equivalent to: `rdma system set netns { shared | exclusive }' func (h *Handle) RdmaSystemSetNetnsMode(NewMode string) error { value, err := netnsModeStringToUint8(NewMode) if err != nil { return err } proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_SET) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) data := nl.NewRtAttr(nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE, []byte{value}) req.AddData(data) _, err = req.Execute(unix.NETLINK_RDMA, 0) return err } // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `rdma dev set $dev netns $ns` func RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { return pkgHandle.RdmaLinkSetNsFd(link, fd) } // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `rdma dev set $dev netns $ns` func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, nl.Uint32Attr(link.Attrs.Index)) req.AddData(data) data = nl.NewRtAttr(nl.RDMA_NLDEV_NET_NS_FD, nl.Uint32Attr(fd)) req.AddData(data) return execRdmaSetLink(req) } ================================================ FILE: vendor/github.com/vishvananda/netlink/route.go ================================================ package netlink import ( "fmt" "net" "strings" ) // Scope is an enum representing a route scope. type Scope uint8 type NextHopFlag int type Destination interface { Family() int Decode([]byte) error Encode() ([]byte, error) String() string Equal(Destination) bool } type Encap interface { Type() int Decode([]byte) error Encode() ([]byte, error) String() string Equal(Encap) bool } //Protocol describe what was the originator of the route type RouteProtocol int // Route represents a netlink route. type Route struct { LinkIndex int ILinkIndex int Scope Scope Dst *net.IPNet Src net.IP Gw net.IP MultiPath []*NexthopInfo Protocol RouteProtocol Priority int Family int Table int Type int Tos int Flags int MPLSDst *int NewDst Destination Encap Encap Via Destination MTU int Window int Rtt int RttVar int Ssthresh int Cwnd int AdvMSS int Reordering int Hoplimit int InitCwnd int Features int RtoMin int InitRwnd int QuickACK int Congctl string FastOpenNoCookie int } func (r Route) String() string { elems := []string{} if len(r.MultiPath) == 0 { elems = append(elems, fmt.Sprintf("Ifindex: %d", r.LinkIndex)) } if r.MPLSDst != nil { elems = append(elems, fmt.Sprintf("Dst: %d", r.MPLSDst)) } else { elems = append(elems, fmt.Sprintf("Dst: %s", r.Dst)) } if r.NewDst != nil { elems = append(elems, fmt.Sprintf("NewDst: %s", r.NewDst)) } if r.Encap != nil { elems = append(elems, fmt.Sprintf("Encap: %s", r.Encap)) } if r.Via != nil { elems = append(elems, fmt.Sprintf("Via: %s", r.Via)) } elems = append(elems, fmt.Sprintf("Src: %s", r.Src)) if len(r.MultiPath) > 0 { elems = append(elems, fmt.Sprintf("Gw: %s", r.MultiPath)) } else { elems = append(elems, fmt.Sprintf("Gw: %s", r.Gw)) } elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags())) elems = append(elems, fmt.Sprintf("Table: %d", r.Table)) return fmt.Sprintf("{%s}", strings.Join(elems, " ")) } func (r Route) Equal(x Route) bool { return r.LinkIndex == x.LinkIndex && r.ILinkIndex == x.ILinkIndex && r.Scope == x.Scope && ipNetEqual(r.Dst, x.Dst) && r.Src.Equal(x.Src) && r.Gw.Equal(x.Gw) && nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) && r.Protocol == x.Protocol && r.Priority == x.Priority && r.Table == x.Table && r.Type == x.Type && r.Tos == x.Tos && r.Hoplimit == x.Hoplimit && r.Flags == x.Flags && (r.MPLSDst == x.MPLSDst || (r.MPLSDst != nil && x.MPLSDst != nil && *r.MPLSDst == *x.MPLSDst)) && (r.NewDst == x.NewDst || (r.NewDst != nil && r.NewDst.Equal(x.NewDst))) && (r.Via == x.Via || (r.Via != nil && r.Via.Equal(x.Via))) && (r.Encap == x.Encap || (r.Encap != nil && r.Encap.Equal(x.Encap))) } func (r *Route) SetFlag(flag NextHopFlag) { r.Flags |= int(flag) } func (r *Route) ClearFlag(flag NextHopFlag) { r.Flags &^= int(flag) } type flagString struct { f NextHopFlag s string } // RouteUpdate is sent when a route changes - type is RTM_NEWROUTE or RTM_DELROUTE type RouteUpdate struct { Type uint16 Route } type NexthopInfo struct { LinkIndex int Hops int Gw net.IP Flags int NewDst Destination Encap Encap Via Destination } func (n *NexthopInfo) String() string { elems := []string{} elems = append(elems, fmt.Sprintf("Ifindex: %d", n.LinkIndex)) if n.NewDst != nil { elems = append(elems, fmt.Sprintf("NewDst: %s", n.NewDst)) } if n.Encap != nil { elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap)) } if n.Via != nil { elems = append(elems, fmt.Sprintf("Via: %s", n.Via)) } elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1)) elems = append(elems, fmt.Sprintf("Gw: %s", n.Gw)) elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags())) return fmt.Sprintf("{%s}", strings.Join(elems, " ")) } func (n NexthopInfo) Equal(x NexthopInfo) bool { return n.LinkIndex == x.LinkIndex && n.Hops == x.Hops && n.Gw.Equal(x.Gw) && n.Flags == x.Flags && (n.NewDst == x.NewDst || (n.NewDst != nil && n.NewDst.Equal(x.NewDst))) && (n.Encap == x.Encap || (n.Encap != nil && n.Encap.Equal(x.Encap))) } type nexthopInfoSlice []*NexthopInfo func (n nexthopInfoSlice) Equal(x []*NexthopInfo) bool { if len(n) != len(x) { return false } for i := range n { if n[i] == nil || x[i] == nil { return false } if !n[i].Equal(*x[i]) { return false } } return true } // ipNetEqual returns true iff both IPNet are equal func ipNetEqual(ipn1 *net.IPNet, ipn2 *net.IPNet) bool { if ipn1 == ipn2 { return true } if ipn1 == nil || ipn2 == nil { return false } m1, _ := ipn1.Mask.Size() m2, _ := ipn2.Mask.Size() return m1 == m2 && ipn1.IP.Equal(ipn2.IP) } ================================================ FILE: vendor/github.com/vishvananda/netlink/route_linux.go ================================================ package netlink import ( "bytes" "encoding/binary" "fmt" "net" "strconv" "strings" "syscall" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) // RtAttr is shared so it is in netlink_linux.go const ( SCOPE_UNIVERSE Scope = unix.RT_SCOPE_UNIVERSE SCOPE_SITE Scope = unix.RT_SCOPE_SITE SCOPE_LINK Scope = unix.RT_SCOPE_LINK SCOPE_HOST Scope = unix.RT_SCOPE_HOST SCOPE_NOWHERE Scope = unix.RT_SCOPE_NOWHERE ) func (s Scope) String() string { switch s { case SCOPE_UNIVERSE: return "universe" case SCOPE_SITE: return "site" case SCOPE_LINK: return "link" case SCOPE_HOST: return "host" case SCOPE_NOWHERE: return "nowhere" default: return "unknown" } } const ( RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota) RT_FILTER_SCOPE RT_FILTER_TYPE RT_FILTER_TOS RT_FILTER_IIF RT_FILTER_OIF RT_FILTER_DST RT_FILTER_SRC RT_FILTER_GW RT_FILTER_TABLE RT_FILTER_HOPLIMIT RT_FILTER_PRIORITY RT_FILTER_MARK RT_FILTER_MASK ) const ( FLAG_ONLINK NextHopFlag = unix.RTNH_F_ONLINK FLAG_PERVASIVE NextHopFlag = unix.RTNH_F_PERVASIVE ) var testFlags = []flagString{ {f: FLAG_ONLINK, s: "onlink"}, {f: FLAG_PERVASIVE, s: "pervasive"}, } func listFlags(flag int) []string { var flags []string for _, tf := range testFlags { if flag&int(tf.f) != 0 { flags = append(flags, tf.s) } } return flags } func (r *Route) ListFlags() []string { return listFlags(r.Flags) } func (n *NexthopInfo) ListFlags() []string { return listFlags(n.Flags) } type MPLSDestination struct { Labels []int } func (d *MPLSDestination) Family() int { return nl.FAMILY_MPLS } func (d *MPLSDestination) Decode(buf []byte) error { d.Labels = nl.DecodeMPLSStack(buf) return nil } func (d *MPLSDestination) Encode() ([]byte, error) { return nl.EncodeMPLSStack(d.Labels...), nil } func (d *MPLSDestination) String() string { s := make([]string, 0, len(d.Labels)) for _, l := range d.Labels { s = append(s, fmt.Sprintf("%d", l)) } return strings.Join(s, "/") } func (d *MPLSDestination) Equal(x Destination) bool { o, ok := x.(*MPLSDestination) if !ok { return false } if d == nil && o == nil { return true } if d == nil || o == nil { return false } if d.Labels == nil && o.Labels == nil { return true } if d.Labels == nil || o.Labels == nil { return false } if len(d.Labels) != len(o.Labels) { return false } for i := range d.Labels { if d.Labels[i] != o.Labels[i] { return false } } return true } type MPLSEncap struct { Labels []int } func (e *MPLSEncap) Type() int { return nl.LWTUNNEL_ENCAP_MPLS } func (e *MPLSEncap) Decode(buf []byte) error { if len(buf) < 4 { return fmt.Errorf("lack of bytes") } l := native.Uint16(buf) if len(buf) < int(l) { return fmt.Errorf("lack of bytes") } buf = buf[:l] typ := native.Uint16(buf[2:]) if typ != nl.MPLS_IPTUNNEL_DST { return fmt.Errorf("unknown MPLS Encap Type: %d", typ) } e.Labels = nl.DecodeMPLSStack(buf[4:]) return nil } func (e *MPLSEncap) Encode() ([]byte, error) { s := nl.EncodeMPLSStack(e.Labels...) hdr := make([]byte, 4) native.PutUint16(hdr, uint16(len(s)+4)) native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST) return append(hdr, s...), nil } func (e *MPLSEncap) String() string { s := make([]string, 0, len(e.Labels)) for _, l := range e.Labels { s = append(s, fmt.Sprintf("%d", l)) } return strings.Join(s, "/") } func (e *MPLSEncap) Equal(x Encap) bool { o, ok := x.(*MPLSEncap) if !ok { return false } if e == nil && o == nil { return true } if e == nil || o == nil { return false } if e.Labels == nil && o.Labels == nil { return true } if e.Labels == nil || o.Labels == nil { return false } if len(e.Labels) != len(o.Labels) { return false } for i := range e.Labels { if e.Labels[i] != o.Labels[i] { return false } } return true } // SEG6 definitions type SEG6Encap struct { Mode int Segments []net.IP } func (e *SEG6Encap) Type() int { return nl.LWTUNNEL_ENCAP_SEG6 } func (e *SEG6Encap) Decode(buf []byte) error { if len(buf) < 4 { return fmt.Errorf("lack of bytes") } // Get Length(l) & Type(typ) : 2 + 2 bytes l := native.Uint16(buf) if len(buf) < int(l) { return fmt.Errorf("lack of bytes") } buf = buf[:l] // make sure buf size upper limit is Length typ := native.Uint16(buf[2:]) // LWTUNNEL_ENCAP_SEG6 has only one attr type SEG6_IPTUNNEL_SRH if typ != nl.SEG6_IPTUNNEL_SRH { return fmt.Errorf("unknown SEG6 Type: %d", typ) } var err error e.Mode, e.Segments, err = nl.DecodeSEG6Encap(buf[4:]) return err } func (e *SEG6Encap) Encode() ([]byte, error) { s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments) hdr := make([]byte, 4) native.PutUint16(hdr, uint16(len(s)+4)) native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH) return append(hdr, s...), err } func (e *SEG6Encap) String() string { segs := make([]string, 0, len(e.Segments)) // append segment backwards (from n to 0) since seg#0 is the last segment. for i := len(e.Segments); i > 0; i-- { segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1])) } str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode), len(e.Segments), strings.Join(segs, " ")) return str } func (e *SEG6Encap) Equal(x Encap) bool { o, ok := x.(*SEG6Encap) if !ok { return false } if e == o { return true } if e == nil || o == nil { return false } if e.Mode != o.Mode { return false } if len(e.Segments) != len(o.Segments) { return false } for i := range e.Segments { if !e.Segments[i].Equal(o.Segments[i]) { return false } } return true } // SEG6LocalEncap definitions type SEG6LocalEncap struct { Flags [nl.SEG6_LOCAL_MAX]bool Action int Segments []net.IP // from SRH in seg6_local_lwt Table int // table id for End.T and End.DT6 InAddr net.IP In6Addr net.IP Iif int Oif int } func (e *SEG6LocalEncap) Type() int { return nl.LWTUNNEL_ENCAP_SEG6_LOCAL } func (e *SEG6LocalEncap) Decode(buf []byte) error { attrs, err := nl.ParseRouteAttr(buf) if err != nil { return err } for _, attr := range attrs { switch attr.Attr.Type { case nl.SEG6_LOCAL_ACTION: e.Action = int(native.Uint32(attr.Value[0:4])) e.Flags[nl.SEG6_LOCAL_ACTION] = true case nl.SEG6_LOCAL_SRH: e.Segments, err = nl.DecodeSEG6Srh(attr.Value[:]) e.Flags[nl.SEG6_LOCAL_SRH] = true case nl.SEG6_LOCAL_TABLE: e.Table = int(native.Uint32(attr.Value[0:4])) e.Flags[nl.SEG6_LOCAL_TABLE] = true case nl.SEG6_LOCAL_NH4: e.InAddr = net.IP(attr.Value[0:4]) e.Flags[nl.SEG6_LOCAL_NH4] = true case nl.SEG6_LOCAL_NH6: e.In6Addr = net.IP(attr.Value[0:16]) e.Flags[nl.SEG6_LOCAL_NH6] = true case nl.SEG6_LOCAL_IIF: e.Iif = int(native.Uint32(attr.Value[0:4])) e.Flags[nl.SEG6_LOCAL_IIF] = true case nl.SEG6_LOCAL_OIF: e.Oif = int(native.Uint32(attr.Value[0:4])) e.Flags[nl.SEG6_LOCAL_OIF] = true } } return err } func (e *SEG6LocalEncap) Encode() ([]byte, error) { var err error res := make([]byte, 8) native.PutUint16(res, 8) // length native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION) native.PutUint32(res[4:], uint32(e.Action)) if e.Flags[nl.SEG6_LOCAL_SRH] { srh, err := nl.EncodeSEG6Srh(e.Segments) if err != nil { return nil, err } attr := make([]byte, 4) native.PutUint16(attr, uint16(len(srh)+4)) native.PutUint16(attr[2:], nl.SEG6_LOCAL_SRH) attr = append(attr, srh...) res = append(res, attr...) } if e.Flags[nl.SEG6_LOCAL_TABLE] { attr := make([]byte, 8) native.PutUint16(attr, 8) native.PutUint16(attr[2:], nl.SEG6_LOCAL_TABLE) native.PutUint32(attr[4:], uint32(e.Table)) res = append(res, attr...) } if e.Flags[nl.SEG6_LOCAL_NH4] { attr := make([]byte, 4) native.PutUint16(attr, 8) native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH4) ipv4 := e.InAddr.To4() if ipv4 == nil { err = fmt.Errorf("SEG6_LOCAL_NH4 has invalid IPv4 address") return nil, err } attr = append(attr, ipv4...) res = append(res, attr...) } if e.Flags[nl.SEG6_LOCAL_NH6] { attr := make([]byte, 4) native.PutUint16(attr, 20) native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH6) attr = append(attr, e.In6Addr...) res = append(res, attr...) } if e.Flags[nl.SEG6_LOCAL_IIF] { attr := make([]byte, 8) native.PutUint16(attr, 8) native.PutUint16(attr[2:], nl.SEG6_LOCAL_IIF) native.PutUint32(attr[4:], uint32(e.Iif)) res = append(res, attr...) } if e.Flags[nl.SEG6_LOCAL_OIF] { attr := make([]byte, 8) native.PutUint16(attr, 8) native.PutUint16(attr[2:], nl.SEG6_LOCAL_OIF) native.PutUint32(attr[4:], uint32(e.Oif)) res = append(res, attr...) } return res, err } func (e *SEG6LocalEncap) String() string { strs := make([]string, 0, nl.SEG6_LOCAL_MAX) strs = append(strs, fmt.Sprintf("action %s", nl.SEG6LocalActionString(e.Action))) if e.Flags[nl.SEG6_LOCAL_TABLE] { strs = append(strs, fmt.Sprintf("table %d", e.Table)) } if e.Flags[nl.SEG6_LOCAL_NH4] { strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr)) } if e.Flags[nl.SEG6_LOCAL_NH6] { strs = append(strs, fmt.Sprintf("nh6 %s", e.In6Addr)) } if e.Flags[nl.SEG6_LOCAL_IIF] { link, err := LinkByIndex(e.Iif) if err != nil { strs = append(strs, fmt.Sprintf("iif %d", e.Iif)) } else { strs = append(strs, fmt.Sprintf("iif %s", link.Attrs().Name)) } } if e.Flags[nl.SEG6_LOCAL_OIF] { link, err := LinkByIndex(e.Oif) if err != nil { strs = append(strs, fmt.Sprintf("oif %d", e.Oif)) } else { strs = append(strs, fmt.Sprintf("oif %s", link.Attrs().Name)) } } if e.Flags[nl.SEG6_LOCAL_SRH] { segs := make([]string, 0, len(e.Segments)) //append segment backwards (from n to 0) since seg#0 is the last segment. for i := len(e.Segments); i > 0; i-- { segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1])) } strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " "))) } return strings.Join(strs, " ") } func (e *SEG6LocalEncap) Equal(x Encap) bool { o, ok := x.(*SEG6LocalEncap) if !ok { return false } if e == o { return true } if e == nil || o == nil { return false } // compare all arrays first for i := range e.Flags { if e.Flags[i] != o.Flags[i] { return false } } if len(e.Segments) != len(o.Segments) { return false } for i := range e.Segments { if !e.Segments[i].Equal(o.Segments[i]) { return false } } // compare values if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) { return false } if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif { return false } return true } // Encap BPF definitions type bpfObj struct { progFd int progName string } type BpfEncap struct { progs [nl.LWT_BPF_MAX]bpfObj headroom int } // SetProg adds a bpf function to the route via netlink RTA_ENCAP. The fd must be a bpf // program loaded with bpf(type=BPF_PROG_TYPE_LWT_*) matching the direction the program should // be applied to (LWT_BPF_IN, LWT_BPF_OUT, LWT_BPF_XMIT). func (e *BpfEncap) SetProg(mode, progFd int, progName string) error { if progFd <= 0 { return fmt.Errorf("lwt bpf SetProg: invalid fd") } if mode <= nl.LWT_BPF_UNSPEC || mode >= nl.LWT_BPF_XMIT_HEADROOM { return fmt.Errorf("lwt bpf SetProg:invalid mode") } e.progs[mode].progFd = progFd e.progs[mode].progName = fmt.Sprintf("%s[fd:%d]", progName, progFd) return nil } // SetXmitHeadroom sets the xmit headroom (LWT_BPF_MAX_HEADROOM) via netlink RTA_ENCAP. // maximum headroom is LWT_BPF_MAX_HEADROOM func (e *BpfEncap) SetXmitHeadroom(headroom int) error { if headroom > nl.LWT_BPF_MAX_HEADROOM || headroom < 0 { return fmt.Errorf("invalid headroom size. range is 0 - %d", nl.LWT_BPF_MAX_HEADROOM) } e.headroom = headroom return nil } func (e *BpfEncap) Type() int { return nl.LWTUNNEL_ENCAP_BPF } func (e *BpfEncap) Decode(buf []byte) error { if len(buf) < 4 { return fmt.Errorf("lwt bpf decode: lack of bytes") } native := nl.NativeEndian() attrs, err := nl.ParseRouteAttr(buf) if err != nil { return fmt.Errorf("lwt bpf decode: failed parsing attribute. err: %v", err) } for _, attr := range attrs { if int(attr.Attr.Type) < 1 { // nl.LWT_BPF_UNSPEC continue } if int(attr.Attr.Type) > nl.LWT_BPF_MAX { return fmt.Errorf("lwt bpf decode: received unknown attribute type: %d", attr.Attr.Type) } switch int(attr.Attr.Type) { case nl.LWT_BPF_MAX_HEADROOM: e.headroom = int(native.Uint32(attr.Value)) default: bpfO := bpfObj{} parsedAttrs, err := nl.ParseRouteAttr(attr.Value) if err != nil { return fmt.Errorf("lwt bpf decode: failed parsing route attribute") } for _, parsedAttr := range parsedAttrs { switch int(parsedAttr.Attr.Type) { case nl.LWT_BPF_PROG_FD: bpfO.progFd = int(native.Uint32(parsedAttr.Value)) case nl.LWT_BPF_PROG_NAME: bpfO.progName = fmt.Sprintf("%s", parsedAttr.Value) default: return fmt.Errorf("lwt bpf decode: received unknown attribute: type: %d, len: %d", parsedAttr.Attr.Type, parsedAttr.Attr.Len) } } e.progs[attr.Attr.Type] = bpfO } } return nil } func (e *BpfEncap) Encode() ([]byte, error) { buf := make([]byte, 0) native = nl.NativeEndian() for index, attr := range e.progs { nlMsg := nl.NewRtAttr(index, []byte{}) if attr.progFd != 0 { nlMsg.AddRtAttr(nl.LWT_BPF_PROG_FD, nl.Uint32Attr(uint32(attr.progFd))) } if attr.progName != "" { nlMsg.AddRtAttr(nl.LWT_BPF_PROG_NAME, nl.ZeroTerminated(attr.progName)) } if nlMsg.Len() > 4 { buf = append(buf, nlMsg.Serialize()...) } } if len(buf) <= 4 { return nil, fmt.Errorf("lwt bpf encode: bpf obj definitions returned empty buffer") } if e.headroom > 0 { hRoom := nl.NewRtAttr(nl.LWT_BPF_XMIT_HEADROOM, nl.Uint32Attr(uint32(e.headroom))) buf = append(buf, hRoom.Serialize()...) } return buf, nil } func (e *BpfEncap) String() string { progs := make([]string, 0) for index, obj := range e.progs { empty := bpfObj{} switch index { case nl.LWT_BPF_IN: if obj != empty { progs = append(progs, fmt.Sprintf("in: %s", obj.progName)) } case nl.LWT_BPF_OUT: if obj != empty { progs = append(progs, fmt.Sprintf("out: %s", obj.progName)) } case nl.LWT_BPF_XMIT: if obj != empty { progs = append(progs, fmt.Sprintf("xmit: %s", obj.progName)) } } } if e.headroom > 0 { progs = append(progs, fmt.Sprintf("xmit headroom: %d", e.headroom)) } return strings.Join(progs, " ") } func (e *BpfEncap) Equal(x Encap) bool { o, ok := x.(*BpfEncap) if !ok { return false } if e.headroom != o.headroom { return false } for i, _ := range o.progs { if o.progs[i] != e.progs[i] { return false } } return true } type Via struct { AddrFamily int Addr net.IP } func (v *Via) Equal(x Destination) bool { o, ok := x.(*Via) if !ok { return false } if v.AddrFamily == x.Family() && v.Addr.Equal(o.Addr) { return true } return false } func (v *Via) String() string { return fmt.Sprintf("Family: %d, Address: %s", v.AddrFamily, v.Addr.String()) } func (v *Via) Family() int { return v.AddrFamily } func (v *Via) Encode() ([]byte, error) { buf := &bytes.Buffer{} err := binary.Write(buf, native, uint16(v.AddrFamily)) if err != nil { return nil, err } err = binary.Write(buf, native, v.Addr) if err != nil { return nil, err } return buf.Bytes(), nil } func (v *Via) Decode(b []byte) error { if len(b) < 6 { return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b)) } v.AddrFamily = int(native.Uint16(b[0:2])) if v.AddrFamily == nl.FAMILY_V4 { v.Addr = net.IP(b[2:6]) return nil } else if v.AddrFamily == nl.FAMILY_V6 { if len(b) < 18 { return fmt.Errorf("decoding failed: buffer too small (%d bytes)", len(b)) } v.Addr = net.IP(b[2:]) return nil } return fmt.Errorf("decoding failed: address family %d unknown", v.AddrFamily) } // RouteAdd will add a route to the system. // Equivalent to: `ip route add $route` func RouteAdd(route *Route) error { return pkgHandle.RouteAdd(route) } // RouteAdd will add a route to the system. // Equivalent to: `ip route add $route` func (h *Handle) RouteAdd(route *Route) error { flags := unix.NLM_F_CREATE | unix.NLM_F_EXCL | unix.NLM_F_ACK req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags) return h.routeHandle(route, req, nl.NewRtMsg()) } // RouteAppend will append a route to the system. // Equivalent to: `ip route append $route` func RouteAppend(route *Route) error { return pkgHandle.RouteAppend(route) } // RouteAppend will append a route to the system. // Equivalent to: `ip route append $route` func (h *Handle) RouteAppend(route *Route) error { flags := unix.NLM_F_CREATE | unix.NLM_F_APPEND | unix.NLM_F_ACK req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags) return h.routeHandle(route, req, nl.NewRtMsg()) } // RouteAddEcmp will add a route to the system. func RouteAddEcmp(route *Route) error { return pkgHandle.RouteAddEcmp(route) } // RouteAddEcmp will add a route to the system. func (h *Handle) RouteAddEcmp(route *Route) error { flags := unix.NLM_F_CREATE | unix.NLM_F_ACK req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags) return h.routeHandle(route, req, nl.NewRtMsg()) } // RouteReplace will add a route to the system. // Equivalent to: `ip route replace $route` func RouteReplace(route *Route) error { return pkgHandle.RouteReplace(route) } // RouteReplace will add a route to the system. // Equivalent to: `ip route replace $route` func (h *Handle) RouteReplace(route *Route) error { flags := unix.NLM_F_CREATE | unix.NLM_F_REPLACE | unix.NLM_F_ACK req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags) return h.routeHandle(route, req, nl.NewRtMsg()) } // RouteDel will delete a route from the system. // Equivalent to: `ip route del $route` func RouteDel(route *Route) error { return pkgHandle.RouteDel(route) } // RouteDel will delete a route from the system. // Equivalent to: `ip route del $route` func (h *Handle) RouteDel(route *Route) error { req := h.newNetlinkRequest(unix.RTM_DELROUTE, unix.NLM_F_ACK) return h.routeHandle(route, req, nl.NewRtDelMsg()) } func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil && route.MPLSDst == nil { return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil") } family := -1 var rtAttrs []*nl.RtAttr if route.Dst != nil && route.Dst.IP != nil { dstLen, _ := route.Dst.Mask.Size() msg.Dst_len = uint8(dstLen) dstFamily := nl.GetIPFamily(route.Dst.IP) family = dstFamily var dstData []byte if dstFamily == FAMILY_V4 { dstData = route.Dst.IP.To4() } else { dstData = route.Dst.IP.To16() } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, dstData)) } else if route.MPLSDst != nil { family = nl.FAMILY_MPLS msg.Dst_len = uint8(20) msg.Type = unix.RTN_UNICAST rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst))) } if route.NewDst != nil { if family != -1 && family != route.NewDst.Family() { return fmt.Errorf("new destination and destination are not the same address family") } buf, err := route.NewDst.Encode() if err != nil { return err } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_NEWDST, buf)) } if route.Encap != nil { buf := make([]byte, 2) native.PutUint16(buf, uint16(route.Encap.Type())) rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP_TYPE, buf)) buf, err := route.Encap.Encode() if err != nil { return err } switch route.Encap.Type() { case nl.LWTUNNEL_ENCAP_BPF: rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP|unix.NLA_F_NESTED, buf)) default: rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf)) } } if route.Src != nil { srcFamily := nl.GetIPFamily(route.Src) if family != -1 && family != srcFamily { return fmt.Errorf("source and destination ip are not the same IP family") } family = srcFamily var srcData []byte if srcFamily == FAMILY_V4 { srcData = route.Src.To4() } else { srcData = route.Src.To16() } // The commonly used src ip for routes is actually PREFSRC rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PREFSRC, srcData)) } if route.Gw != nil { gwFamily := nl.GetIPFamily(route.Gw) if family != -1 && family != gwFamily { return fmt.Errorf("gateway, source, and destination ip are not the same IP family") } family = gwFamily var gwData []byte if gwFamily == FAMILY_V4 { gwData = route.Gw.To4() } else { gwData = route.Gw.To16() } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_GATEWAY, gwData)) } if route.Via != nil { buf, err := route.Via.Encode() if err != nil { return fmt.Errorf("failed to encode RTA_VIA: %v", err) } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_VIA, buf)) } if len(route.MultiPath) > 0 { buf := []byte{} for _, nh := range route.MultiPath { rtnh := &nl.RtNexthop{ RtNexthop: unix.RtNexthop{ Hops: uint8(nh.Hops), Ifindex: int32(nh.LinkIndex), Flags: uint8(nh.Flags), }, } children := []nl.NetlinkRequestData{} if nh.Gw != nil { gwFamily := nl.GetIPFamily(nh.Gw) if family != -1 && family != gwFamily { return fmt.Errorf("gateway, source, and destination ip are not the same IP family") } if gwFamily == FAMILY_V4 { children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To4()))) } else { children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To16()))) } } if nh.NewDst != nil { if family != -1 && family != nh.NewDst.Family() { return fmt.Errorf("new destination and destination are not the same address family") } buf, err := nh.NewDst.Encode() if err != nil { return err } children = append(children, nl.NewRtAttr(unix.RTA_NEWDST, buf)) } if nh.Encap != nil { buf := make([]byte, 2) native.PutUint16(buf, uint16(nh.Encap.Type())) children = append(children, nl.NewRtAttr(unix.RTA_ENCAP_TYPE, buf)) buf, err := nh.Encap.Encode() if err != nil { return err } children = append(children, nl.NewRtAttr(unix.RTA_ENCAP, buf)) } if nh.Via != nil { buf, err := nh.Via.Encode() if err != nil { return err } children = append(children, nl.NewRtAttr(unix.RTA_VIA, buf)) } rtnh.Children = children buf = append(buf, rtnh.Serialize()...) } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_MULTIPATH, buf)) } if route.Table > 0 { if route.Table >= 256 { msg.Table = unix.RT_TABLE_UNSPEC b := make([]byte, 4) native.PutUint32(b, uint32(route.Table)) rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_TABLE, b)) } else { msg.Table = uint8(route.Table) } } if route.Priority > 0 { b := make([]byte, 4) native.PutUint32(b, uint32(route.Priority)) rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PRIORITY, b)) } if route.Tos > 0 { msg.Tos = uint8(route.Tos) } if route.Protocol > 0 { msg.Protocol = uint8(route.Protocol) } if route.Type > 0 { msg.Type = uint8(route.Type) } var metrics []*nl.RtAttr if route.MTU > 0 { b := nl.Uint32Attr(uint32(route.MTU)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_MTU, b)) } if route.Window > 0 { b := nl.Uint32Attr(uint32(route.Window)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_WINDOW, b)) } if route.Rtt > 0 { b := nl.Uint32Attr(uint32(route.Rtt)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_RTT, b)) } if route.RttVar > 0 { b := nl.Uint32Attr(uint32(route.RttVar)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_RTTVAR, b)) } if route.Ssthresh > 0 { b := nl.Uint32Attr(uint32(route.Ssthresh)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_SSTHRESH, b)) } if route.Cwnd > 0 { b := nl.Uint32Attr(uint32(route.Cwnd)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_CWND, b)) } if route.AdvMSS > 0 { b := nl.Uint32Attr(uint32(route.AdvMSS)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_ADVMSS, b)) } if route.Reordering > 0 { b := nl.Uint32Attr(uint32(route.Reordering)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_REORDERING, b)) } if route.Hoplimit > 0 { b := nl.Uint32Attr(uint32(route.Hoplimit)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_HOPLIMIT, b)) } if route.InitCwnd > 0 { b := nl.Uint32Attr(uint32(route.InitCwnd)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_INITCWND, b)) } if route.Features > 0 { b := nl.Uint32Attr(uint32(route.Features)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_FEATURES, b)) } if route.RtoMin > 0 { b := nl.Uint32Attr(uint32(route.RtoMin)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_RTO_MIN, b)) } if route.InitRwnd > 0 { b := nl.Uint32Attr(uint32(route.InitRwnd)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_INITRWND, b)) } if route.QuickACK > 0 { b := nl.Uint32Attr(uint32(route.QuickACK)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_QUICKACK, b)) } if route.Congctl != "" { b := nl.ZeroTerminated(route.Congctl) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_CC_ALGO, b)) } if route.FastOpenNoCookie > 0 { b := nl.Uint32Attr(uint32(route.FastOpenNoCookie)) metrics = append(metrics, nl.NewRtAttr(unix.RTAX_FASTOPEN_NO_COOKIE, b)) } if metrics != nil { attr := nl.NewRtAttr(unix.RTA_METRICS, nil) for _, metric := range metrics { attr.AddChild(metric) } rtAttrs = append(rtAttrs, attr) } msg.Flags = uint32(route.Flags) msg.Scope = uint8(route.Scope) msg.Family = uint8(family) req.AddData(msg) for _, attr := range rtAttrs { req.AddData(attr) } b := make([]byte, 4) native.PutUint32(b, uint32(route.LinkIndex)) req.AddData(nl.NewRtAttr(unix.RTA_OIF, b)) _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // RouteList gets a list of routes in the system. // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. func RouteList(link Link, family int) ([]Route, error) { return pkgHandle.RouteList(link, family) } // RouteList gets a list of routes in the system. // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. func (h *Handle) RouteList(link Link, family int) ([]Route, error) { var routeFilter *Route if link != nil { routeFilter = &Route{ LinkIndex: link.Attrs().Index, } } return h.RouteListFiltered(family, routeFilter, RT_FILTER_OIF) } // RouteListFiltered gets a list of routes in the system filtered with specified rules. // All rules must be defined in RouteFilter struct func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { return pkgHandle.RouteListFiltered(family, filter, filterMask) } // RouteListFiltered gets a list of routes in the system filtered with specified rules. // All rules must be defined in RouteFilter struct func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(family) req.AddData(infmsg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE) if err != nil { return nil, err } var res []Route for _, m := range msgs { msg := nl.DeserializeRtMsg(m) if msg.Flags&unix.RTM_F_CLONED != 0 { // Ignore cloned routes continue } if msg.Table != unix.RT_TABLE_MAIN { if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 { // Ignore non-main tables continue } } route, err := deserializeRoute(m) if err != nil { return nil, err } if filter != nil { switch { case filterMask&RT_FILTER_TABLE != 0 && filter.Table != unix.RT_TABLE_UNSPEC && route.Table != filter.Table: continue case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol: continue case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope: continue case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type: continue case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: continue case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex: continue case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex: continue case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw): continue case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): continue case filterMask&RT_FILTER_DST != 0: if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) { if !ipNetEqual(route.Dst, filter.Dst) { continue } } case filterMask&RT_FILTER_HOPLIMIT != 0 && route.Hoplimit != filter.Hoplimit: continue } } res = append(res, route) } return res, nil } // deserializeRoute decodes a binary netlink message into a Route struct func deserializeRoute(m []byte) (Route, error) { msg := nl.DeserializeRtMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return Route{}, err } route := Route{ Scope: Scope(msg.Scope), Protocol: RouteProtocol(int(msg.Protocol)), Table: int(msg.Table), Type: int(msg.Type), Tos: int(msg.Tos), Flags: int(msg.Flags), Family: int(msg.Family), } var encap, encapType syscall.NetlinkRouteAttr for _, attr := range attrs { switch attr.Attr.Type { case unix.RTA_GATEWAY: route.Gw = net.IP(attr.Value) case unix.RTA_PREFSRC: route.Src = net.IP(attr.Value) case unix.RTA_DST: if msg.Family == nl.FAMILY_MPLS { stack := nl.DecodeMPLSStack(attr.Value) if len(stack) == 0 || len(stack) > 1 { return route, fmt.Errorf("invalid MPLS RTA_DST") } route.MPLSDst = &stack[0] } else { route.Dst = &net.IPNet{ IP: attr.Value, Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), } } case unix.RTA_OIF: route.LinkIndex = int(native.Uint32(attr.Value[0:4])) case unix.RTA_IIF: route.ILinkIndex = int(native.Uint32(attr.Value[0:4])) case unix.RTA_PRIORITY: route.Priority = int(native.Uint32(attr.Value[0:4])) case unix.RTA_TABLE: route.Table = int(native.Uint32(attr.Value[0:4])) case unix.RTA_MULTIPATH: parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) { if len(value) < unix.SizeofRtNexthop { return nil, nil, fmt.Errorf("lack of bytes") } nh := nl.DeserializeRtNexthop(value) if len(value) < int(nh.RtNexthop.Len) { return nil, nil, fmt.Errorf("lack of bytes") } info := &NexthopInfo{ LinkIndex: int(nh.RtNexthop.Ifindex), Hops: int(nh.RtNexthop.Hops), Flags: int(nh.RtNexthop.Flags), } attrs, err := nl.ParseRouteAttr(value[unix.SizeofRtNexthop:int(nh.RtNexthop.Len)]) if err != nil { return nil, nil, err } var encap, encapType syscall.NetlinkRouteAttr for _, attr := range attrs { switch attr.Attr.Type { case unix.RTA_GATEWAY: info.Gw = net.IP(attr.Value) case unix.RTA_NEWDST: var d Destination switch msg.Family { case nl.FAMILY_MPLS: d = &MPLSDestination{} } if err := d.Decode(attr.Value); err != nil { return nil, nil, err } info.NewDst = d case unix.RTA_ENCAP_TYPE: encapType = attr case unix.RTA_ENCAP: encap = attr case unix.RTA_VIA: d := &Via{} if err := d.Decode(attr.Value); err != nil { return nil, nil, err } info.Via = d } } if len(encap.Value) != 0 && len(encapType.Value) != 0 { typ := int(native.Uint16(encapType.Value[0:2])) var e Encap switch typ { case nl.LWTUNNEL_ENCAP_MPLS: e = &MPLSEncap{} if err := e.Decode(encap.Value); err != nil { return nil, nil, err } } info.Encap = e } return info, value[int(nh.RtNexthop.Len):], nil } rest := attr.Value for len(rest) > 0 { info, buf, err := parseRtNexthop(rest) if err != nil { return route, err } route.MultiPath = append(route.MultiPath, info) rest = buf } case unix.RTA_NEWDST: var d Destination switch msg.Family { case nl.FAMILY_MPLS: d = &MPLSDestination{} } if err := d.Decode(attr.Value); err != nil { return route, err } route.NewDst = d case unix.RTA_VIA: v := &Via{} if err := v.Decode(attr.Value); err != nil { return route, err } route.Via = v case unix.RTA_ENCAP_TYPE: encapType = attr case unix.RTA_ENCAP: encap = attr case unix.RTA_METRICS: metrics, err := nl.ParseRouteAttr(attr.Value) if err != nil { return route, err } for _, metric := range metrics { switch metric.Attr.Type { case unix.RTAX_MTU: route.MTU = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_WINDOW: route.Window = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_RTT: route.Rtt = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_RTTVAR: route.RttVar = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_SSTHRESH: route.Ssthresh = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_CWND: route.Cwnd = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_ADVMSS: route.AdvMSS = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_REORDERING: route.Reordering = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_HOPLIMIT: route.Hoplimit = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_INITCWND: route.InitCwnd = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_FEATURES: route.Features = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_RTO_MIN: route.RtoMin = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_INITRWND: route.InitRwnd = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_QUICKACK: route.QuickACK = int(native.Uint32(metric.Value[0:4])) case unix.RTAX_CC_ALGO: route.Congctl = nl.BytesToString(metric.Value) case unix.RTAX_FASTOPEN_NO_COOKIE: route.FastOpenNoCookie = int(native.Uint32(metric.Value[0:4])) } } } } if len(encap.Value) != 0 && len(encapType.Value) != 0 { typ := int(native.Uint16(encapType.Value[0:2])) var e Encap switch typ { case nl.LWTUNNEL_ENCAP_MPLS: e = &MPLSEncap{} if err := e.Decode(encap.Value); err != nil { return route, err } case nl.LWTUNNEL_ENCAP_SEG6: e = &SEG6Encap{} if err := e.Decode(encap.Value); err != nil { return route, err } case nl.LWTUNNEL_ENCAP_SEG6_LOCAL: e = &SEG6LocalEncap{} if err := e.Decode(encap.Value); err != nil { return route, err } case nl.LWTUNNEL_ENCAP_BPF: e = &BpfEncap{} if err := e.Decode(encap.Value); err != nil { return route, err } } route.Encap = e } return route, nil } // RouteGetOptions contains a set of options to use with // RouteGetWithOptions type RouteGetOptions struct { Iif string VrfName string SrcAddr net.IP } // RouteGetWithOptions gets a route to a specific destination from the host system. // Equivalent to: 'ip route get <> vrf '. func RouteGetWithOptions(destination net.IP, options *RouteGetOptions) ([]Route, error) { return pkgHandle.RouteGetWithOptions(destination, options) } // RouteGet gets a route to a specific destination from the host system. // Equivalent to: 'ip route get'. func RouteGet(destination net.IP) ([]Route, error) { return pkgHandle.RouteGet(destination) } // RouteGetWithOptions gets a route to a specific destination from the host system. // Equivalent to: 'ip route get <> vrf '. func (h *Handle) RouteGetWithOptions(destination net.IP, options *RouteGetOptions) ([]Route, error) { req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_REQUEST) family := nl.GetIPFamily(destination) var destinationData []byte var bitlen uint8 if family == FAMILY_V4 { destinationData = destination.To4() bitlen = 32 } else { destinationData = destination.To16() bitlen = 128 } msg := &nl.RtMsg{} msg.Family = uint8(family) msg.Dst_len = bitlen if options != nil && options.SrcAddr != nil { msg.Src_len = bitlen } msg.Flags = unix.RTM_F_LOOKUP_TABLE req.AddData(msg) rtaDst := nl.NewRtAttr(unix.RTA_DST, destinationData) req.AddData(rtaDst) if options != nil { if options.VrfName != "" { link, err := LinkByName(options.VrfName) if err != nil { return nil, err } b := make([]byte, 4) native.PutUint32(b, uint32(link.Attrs().Index)) req.AddData(nl.NewRtAttr(unix.RTA_OIF, b)) } if len(options.Iif) > 0 { link, err := LinkByName(options.Iif) if err != nil { return nil, err } b := make([]byte, 4) native.PutUint32(b, uint32(link.Attrs().Index)) req.AddData(nl.NewRtAttr(unix.RTA_IIF, b)) } if options.SrcAddr != nil { var srcAddr []byte if family == FAMILY_V4 { srcAddr = options.SrcAddr.To4() } else { srcAddr = options.SrcAddr.To16() } req.AddData(nl.NewRtAttr(unix.RTA_SRC, srcAddr)) } } msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE) if err != nil { return nil, err } var res []Route for _, m := range msgs { route, err := deserializeRoute(m) if err != nil { return nil, err } res = append(res, route) } return res, nil } // RouteGet gets a route to a specific destination from the host system. // Equivalent to: 'ip route get'. func (h *Handle) RouteGet(destination net.IP) ([]Route, error) { return h.RouteGetWithOptions(destination, nil) } // RouteSubscribe takes a chan down which notifications will be sent // when routes are added or deleted. Close the 'done' chan to stop subscription. func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error { return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil, false) } // RouteSubscribeAt works like RouteSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { return routeSubscribeAt(ns, netns.None(), ch, done, nil, false) } // RouteSubscribeOptions contains a set of options to use with // RouteSubscribeWithOptions. type RouteSubscribeOptions struct { Namespace *netns.NsHandle ErrorCallback func(error) ListExisting bool } // RouteSubscribeWithOptions work like RouteSubscribe but enable to // provide additional options to modify the behavior. Currently, the // namespace can be provided as well as an error callback. func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error { if options.Namespace == nil { none := netns.None() options.Namespace = &none } return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting) } func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error { s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_ROUTE, unix.RTNLGRP_IPV6_ROUTE) if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } if listExisting { req := pkgHandle.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(infmsg) if err := s.Send(req); err != nil { return err } } go func() { defer close(ch) for { msgs, from, err := s.Receive() if err != nil { if cberr != nil { cberr(fmt.Errorf("Receive failed: %v", err)) } return } if from.Pid != nl.PidKernel { if cberr != nil { cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) } continue } for _, m := range msgs { if m.Header.Type == unix.NLMSG_DONE { continue } if m.Header.Type == unix.NLMSG_ERROR { error := int32(native.Uint32(m.Data[0:4])) if error == 0 { continue } if cberr != nil { cberr(fmt.Errorf("error message: %v", syscall.Errno(-error))) } continue } route, err := deserializeRoute(m.Data) if err != nil { if cberr != nil { cberr(err) } continue } ch <- RouteUpdate{Type: m.Header.Type, Route: route} } } }() return nil } func (p RouteProtocol) String() string { switch int(p) { case unix.RTPROT_BABEL: return "babel" case unix.RTPROT_BGP: return "bgp" case unix.RTPROT_BIRD: return "bird" case unix.RTPROT_BOOT: return "boot" case unix.RTPROT_DHCP: return "dhcp" case unix.RTPROT_DNROUTED: return "dnrouted" case unix.RTPROT_EIGRP: return "eigrp" case unix.RTPROT_GATED: return "gated" case unix.RTPROT_ISIS: return "isis" //case unix.RTPROT_KEEPALIVED: // return "keepalived" case unix.RTPROT_KERNEL: return "kernel" case unix.RTPROT_MROUTED: return "mrouted" case unix.RTPROT_MRT: return "mrt" case unix.RTPROT_NTK: return "ntk" case unix.RTPROT_OSPF: return "ospf" case unix.RTPROT_RA: return "ra" case unix.RTPROT_REDIRECT: return "redirect" case unix.RTPROT_RIP: return "rip" case unix.RTPROT_STATIC: return "static" case unix.RTPROT_UNSPEC: return "unspec" case unix.RTPROT_XORP: return "xorp" case unix.RTPROT_ZEBRA: return "zebra" default: return strconv.Itoa(int(p)) } } ================================================ FILE: vendor/github.com/vishvananda/netlink/route_unspecified.go ================================================ // +build !linux package netlink import "strconv" func (r *Route) ListFlags() []string { return []string{} } func (n *NexthopInfo) ListFlags() []string { return []string{} } func (s Scope) String() string { return "unknown" } func (p RouteProtocol) String() string { return strconv.Itoa(int(p)) } ================================================ FILE: vendor/github.com/vishvananda/netlink/rule.go ================================================ package netlink import ( "fmt" "net" ) // Rule represents a netlink rule. type Rule struct { Priority int Family int Table int Mark int Mask int Tos uint TunID uint Goto int Src *net.IPNet Dst *net.IPNet Flow int IifName string OifName string SuppressIfgroup int SuppressPrefixlen int Invert bool Dport *RulePortRange Sport *RulePortRange } func (r Rule) String() string { return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table) } // NewRule return empty rules. func NewRule() *Rule { return &Rule{ SuppressIfgroup: -1, SuppressPrefixlen: -1, Priority: -1, Mark: -1, Mask: -1, Goto: -1, Flow: -1, } } // NewRulePortRange creates rule sport/dport range. func NewRulePortRange(start, end uint16) *RulePortRange { return &RulePortRange{Start: start, End: end} } // RulePortRange represents rule sport/dport range. type RulePortRange struct { Start uint16 End uint16 } ================================================ FILE: vendor/github.com/vishvananda/netlink/rule_linux.go ================================================ package netlink import ( "bytes" "fmt" "net" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) const FibRuleInvert = 0x2 // RuleAdd adds a rule to the system. // Equivalent to: ip rule add func RuleAdd(rule *Rule) error { return pkgHandle.RuleAdd(rule) } // RuleAdd adds a rule to the system. // Equivalent to: ip rule add func (h *Handle) RuleAdd(rule *Rule) error { req := h.newNetlinkRequest(unix.RTM_NEWRULE, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) return ruleHandle(rule, req) } // RuleDel deletes a rule from the system. // Equivalent to: ip rule del func RuleDel(rule *Rule) error { return pkgHandle.RuleDel(rule) } // RuleDel deletes a rule from the system. // Equivalent to: ip rule del func (h *Handle) RuleDel(rule *Rule) error { req := h.newNetlinkRequest(unix.RTM_DELRULE, unix.NLM_F_ACK) return ruleHandle(rule, req) } func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { msg := nl.NewRtMsg() msg.Family = unix.AF_INET msg.Protocol = unix.RTPROT_BOOT msg.Scope = unix.RT_SCOPE_UNIVERSE msg.Table = unix.RT_TABLE_UNSPEC msg.Type = unix.RTN_UNSPEC if req.NlMsghdr.Flags&unix.NLM_F_CREATE > 0 { msg.Type = unix.RTN_UNICAST } if rule.Invert { msg.Flags |= FibRuleInvert } if rule.Family != 0 { msg.Family = uint8(rule.Family) } if rule.Table >= 0 && rule.Table < 256 { msg.Table = uint8(rule.Table) } if rule.Tos != 0 { msg.Tos = uint8(rule.Tos) } var dstFamily uint8 var rtAttrs []*nl.RtAttr if rule.Dst != nil && rule.Dst.IP != nil { dstLen, _ := rule.Dst.Mask.Size() msg.Dst_len = uint8(dstLen) msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP)) dstFamily = msg.Family var dstData []byte if msg.Family == unix.AF_INET { dstData = rule.Dst.IP.To4() } else { dstData = rule.Dst.IP.To16() } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, dstData)) } if rule.Src != nil && rule.Src.IP != nil { msg.Family = uint8(nl.GetIPFamily(rule.Src.IP)) if dstFamily != 0 && dstFamily != msg.Family { return fmt.Errorf("source and destination ip are not the same IP family") } srcLen, _ := rule.Src.Mask.Size() msg.Src_len = uint8(srcLen) var srcData []byte if msg.Family == unix.AF_INET { srcData = rule.Src.IP.To4() } else { srcData = rule.Src.IP.To16() } rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_SRC, srcData)) } req.AddData(msg) for i := range rtAttrs { req.AddData(rtAttrs[i]) } if rule.Priority >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.Priority)) req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b)) } if rule.Mark >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.Mark)) req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b)) } if rule.Mask >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.Mask)) req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b)) } if rule.Flow >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.Flow)) req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) } if rule.TunID > 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.TunID)) req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b)) } if rule.Table >= 256 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.Table)) req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) } if msg.Table > 0 { if rule.SuppressPrefixlen >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.SuppressPrefixlen)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b)) } if rule.SuppressIfgroup >= 0 { b := make([]byte, 4) native.PutUint32(b, uint32(rule.SuppressIfgroup)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b)) } } if rule.IifName != "" { req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName+"\x00"))) } if rule.OifName != "" { req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName+"\x00"))) } if rule.Goto >= 0 { msg.Type = nl.FR_ACT_GOTO b := make([]byte, 4) native.PutUint32(b, uint32(rule.Goto)) req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) } if rule.Dport != nil { b := rule.Dport.toRtAttrData() req.AddData(nl.NewRtAttr(nl.FRA_DPORT_RANGE, b)) } if rule.Sport != nil { b := rule.Sport.toRtAttrData() req.AddData(nl.NewRtAttr(nl.FRA_SPORT_RANGE, b)) } _, err := req.Execute(unix.NETLINK_ROUTE, 0) return err } // RuleList lists rules in the system. // Equivalent to: ip rule list func RuleList(family int) ([]Rule, error) { return pkgHandle.RuleList(family) } // RuleList lists rules in the system. // Equivalent to: ip rule list func (h *Handle) RuleList(family int) ([]Rule, error) { return h.RuleListFiltered(family, nil, 0) } // RuleListFiltered gets a list of rules in the system filtered by the // specified rule template `filter`. // Equivalent to: ip rule list func RuleListFiltered(family int, filter *Rule, filterMask uint64) ([]Rule, error) { return pkgHandle.RuleListFiltered(family, filter, filterMask) } // RuleListFiltered lists rules in the system. // Equivalent to: ip rule list func (h *Handle) RuleListFiltered(family int, filter *Rule, filterMask uint64) ([]Rule, error) { req := h.newNetlinkRequest(unix.RTM_GETRULE, unix.NLM_F_DUMP|unix.NLM_F_REQUEST) msg := nl.NewIfInfomsg(family) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWRULE) if err != nil { return nil, err } var res = make([]Rule, 0) for i := range msgs { msg := nl.DeserializeRtMsg(msgs[i]) attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():]) if err != nil { return nil, err } rule := NewRule() rule.Invert = msg.Flags&FibRuleInvert > 0 rule.Tos = uint(msg.Tos) for j := range attrs { switch attrs[j].Attr.Type { case unix.RTA_TABLE: rule.Table = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_SRC: rule.Src = &net.IPNet{ IP: attrs[j].Value, Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)), } case nl.FRA_DST: rule.Dst = &net.IPNet{ IP: attrs[j].Value, Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)), } case nl.FRA_FWMARK: rule.Mark = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_FWMASK: rule.Mask = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_TUN_ID: rule.TunID = uint(native.Uint64(attrs[j].Value[0:8])) case nl.FRA_IIFNAME: rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) case nl.FRA_OIFNAME: rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) case nl.FRA_SUPPRESS_PREFIXLEN: i := native.Uint32(attrs[j].Value[0:4]) if i != 0xffffffff { rule.SuppressPrefixlen = int(i) } case nl.FRA_SUPPRESS_IFGROUP: i := native.Uint32(attrs[j].Value[0:4]) if i != 0xffffffff { rule.SuppressIfgroup = int(i) } case nl.FRA_FLOW: rule.Flow = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_GOTO: rule.Goto = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_PRIORITY: rule.Priority = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_DPORT_RANGE: rule.Dport = NewRulePortRange(native.Uint16(attrs[j].Value[0:2]), native.Uint16(attrs[j].Value[2:4])) case nl.FRA_SPORT_RANGE: rule.Sport = NewRulePortRange(native.Uint16(attrs[j].Value[0:2]), native.Uint16(attrs[j].Value[2:4])) } } if filter != nil { switch { case filterMask&RT_FILTER_SRC != 0 && (rule.Src == nil || rule.Src.String() != filter.Src.String()): continue case filterMask&RT_FILTER_DST != 0 && (rule.Dst == nil || rule.Dst.String() != filter.Dst.String()): continue case filterMask&RT_FILTER_TABLE != 0 && filter.Table != unix.RT_TABLE_UNSPEC && rule.Table != filter.Table: continue case filterMask&RT_FILTER_TOS != 0 && rule.Tos != filter.Tos: continue case filterMask&RT_FILTER_PRIORITY != 0 && rule.Priority != filter.Priority: continue case filterMask&RT_FILTER_MARK != 0 && rule.Mark != filter.Mark: continue case filterMask&RT_FILTER_MASK != 0 && rule.Mask != filter.Mask: continue } } res = append(res, *rule) } return res, nil } func (pr *RulePortRange) toRtAttrData() []byte { b := [][]byte{make([]byte, 2), make([]byte, 2)} native.PutUint16(b[0], pr.Start) native.PutUint16(b[1], pr.End) return bytes.Join(b, []byte{}) } ================================================ FILE: vendor/github.com/vishvananda/netlink/socket.go ================================================ package netlink import "net" // SocketID identifies a single socket. type SocketID struct { SourcePort uint16 DestinationPort uint16 Source net.IP Destination net.IP Interface uint32 Cookie [2]uint32 } // Socket represents a netlink socket. type Socket struct { Family uint8 State uint8 Timer uint8 Retrans uint8 ID SocketID Expires uint32 RQueue uint32 WQueue uint32 UID uint32 INode uint32 } ================================================ FILE: vendor/github.com/vishvananda/netlink/socket_linux.go ================================================ package netlink import ( "errors" "fmt" "net" "syscall" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) const ( sizeofSocketID = 0x30 sizeofSocketRequest = sizeofSocketID + 0x8 sizeofSocket = sizeofSocketID + 0x18 ) type socketRequest struct { Family uint8 Protocol uint8 Ext uint8 pad uint8 States uint32 ID SocketID } type writeBuffer struct { Bytes []byte pos int } func (b *writeBuffer) Write(c byte) { b.Bytes[b.pos] = c b.pos++ } func (b *writeBuffer) Next(n int) []byte { s := b.Bytes[b.pos : b.pos+n] b.pos += n return s } func (r *socketRequest) Serialize() []byte { b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)} b.Write(r.Family) b.Write(r.Protocol) b.Write(r.Ext) b.Write(r.pad) native.PutUint32(b.Next(4), r.States) networkOrder.PutUint16(b.Next(2), r.ID.SourcePort) networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort) if r.Family == unix.AF_INET6 { copy(b.Next(16), r.ID.Source) copy(b.Next(16), r.ID.Destination) } else { copy(b.Next(4), r.ID.Source.To4()) b.Next(12) copy(b.Next(4), r.ID.Destination.To4()) b.Next(12) } native.PutUint32(b.Next(4), r.ID.Interface) native.PutUint32(b.Next(4), r.ID.Cookie[0]) native.PutUint32(b.Next(4), r.ID.Cookie[1]) return b.Bytes } func (r *socketRequest) Len() int { return sizeofSocketRequest } type readBuffer struct { Bytes []byte pos int } func (b *readBuffer) Read() byte { c := b.Bytes[b.pos] b.pos++ return c } func (b *readBuffer) Next(n int) []byte { s := b.Bytes[b.pos : b.pos+n] b.pos += n return s } func (s *Socket) deserialize(b []byte) error { if len(b) < sizeofSocket { return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket) } rb := readBuffer{Bytes: b} s.Family = rb.Read() s.State = rb.Read() s.Timer = rb.Read() s.Retrans = rb.Read() s.ID.SourcePort = networkOrder.Uint16(rb.Next(2)) s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2)) if s.Family == unix.AF_INET6 { s.ID.Source = net.IP(rb.Next(16)) s.ID.Destination = net.IP(rb.Next(16)) } else { s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) rb.Next(12) s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) rb.Next(12) } s.ID.Interface = native.Uint32(rb.Next(4)) s.ID.Cookie[0] = native.Uint32(rb.Next(4)) s.ID.Cookie[1] = native.Uint32(rb.Next(4)) s.Expires = native.Uint32(rb.Next(4)) s.RQueue = native.Uint32(rb.Next(4)) s.WQueue = native.Uint32(rb.Next(4)) s.UID = native.Uint32(rb.Next(4)) s.INode = native.Uint32(rb.Next(4)) return nil } // SocketGet returns the Socket identified by its local and remote addresses. func SocketGet(local, remote net.Addr) (*Socket, error) { localTCP, ok := local.(*net.TCPAddr) if !ok { return nil, ErrNotImplemented } remoteTCP, ok := remote.(*net.TCPAddr) if !ok { return nil, ErrNotImplemented } localIP := localTCP.IP.To4() if localIP == nil { return nil, ErrNotImplemented } remoteIP := remoteTCP.IP.To4() if remoteIP == nil { return nil, ErrNotImplemented } s, err := nl.Subscribe(unix.NETLINK_INET_DIAG) if err != nil { return nil, err } defer s.Close() req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, 0) req.AddData(&socketRequest{ Family: unix.AF_INET, Protocol: unix.IPPROTO_TCP, ID: SocketID{ SourcePort: uint16(localTCP.Port), DestinationPort: uint16(remoteTCP.Port), Source: localIP, Destination: remoteIP, Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE}, }, }) s.Send(req) msgs, from, err := s.Receive() if err != nil { return nil, err } if from.Pid != nl.PidKernel { return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel) } if len(msgs) == 0 { return nil, errors.New("no message nor error from netlink") } if len(msgs) > 2 { return nil, fmt.Errorf("multiple (%d) matching sockets", len(msgs)) } sock := &Socket{} if err := sock.deserialize(msgs[0].Data); err != nil { return nil, err } return sock, nil } // SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info. func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) { var result []*InetDiagTCPInfoResp err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error { sockInfo := &Socket{} if err := sockInfo.deserialize(m.Data); err != nil { return err } attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:]) if err != nil { return err } res, err := attrsToInetDiagTCPInfoResp(attrs, sockInfo) if err != nil { return err } result = append(result, res) return nil }) if err != nil { return nil, err } return result, nil } // SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket. func SocketDiagTCP(family uint8) ([]*Socket, error) { var result []*Socket err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error { sockInfo := &Socket{} if err := sockInfo.deserialize(m.Data); err != nil { return err } result = append(result, sockInfo) return nil }) if err != nil { return nil, err } return result, nil } // socketDiagTCPExecutor requests INET_DIAG_INFO for TCP protocol for specified family type. func socketDiagTCPExecutor(family uint8, receiver func(syscall.NetlinkMessage) error) error { s, err := nl.Subscribe(unix.NETLINK_INET_DIAG) if err != nil { return err } defer s.Close() req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) req.AddData(&socketRequest{ Family: family, Protocol: unix.IPPROTO_TCP, Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)), States: uint32(0xfff), // All TCP states }) s.Send(req) loop: for { msgs, from, err := s.Receive() if err != nil { return err } if from.Pid != nl.PidKernel { return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel) } if len(msgs) == 0 { return errors.New("no message nor error from netlink") } for _, m := range msgs { switch m.Header.Type { case unix.NLMSG_DONE: break loop case unix.NLMSG_ERROR: error := int32(native.Uint32(m.Data[0:4])) return syscall.Errno(-error) } if err := receiver(m); err != nil { return err } } } return nil } func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) { var tcpInfo *TCPInfo var tcpBBRInfo *TCPBBRInfo for _, a := range attrs { if a.Attr.Type == INET_DIAG_INFO { tcpInfo = &TCPInfo{} if err := tcpInfo.deserialize(a.Value); err != nil { return nil, err } continue } if a.Attr.Type == INET_DIAG_BBRINFO { tcpBBRInfo = &TCPBBRInfo{} if err := tcpBBRInfo.deserialize(a.Value); err != nil { return nil, err } continue } } return &InetDiagTCPInfoResp{ InetDiagMsg: sockInfo, TCPInfo: tcpInfo, TCPBBRInfo: tcpBBRInfo, }, nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/tcp.go ================================================ package netlink // TCP States const ( TCP_ESTABLISHED = iota + 0x01 TCP_SYN_SENT TCP_SYN_RECV TCP_FIN_WAIT1 TCP_FIN_WAIT2 TCP_TIME_WAIT TCP_CLOSE TCP_CLOSE_WAIT TCP_LAST_ACK TCP_LISTEN TCP_CLOSING TCP_NEW_SYN_REC TCP_MAX_STATES ) type TCPInfo struct { State uint8 Ca_state uint8 Retransmits uint8 Probes uint8 Backoff uint8 Options uint8 Snd_wscale uint8 // no uint4 Rcv_wscale uint8 Delivery_rate_app_limited uint8 Fastopen_client_fail uint8 Rto uint32 Ato uint32 Snd_mss uint32 Rcv_mss uint32 Unacked uint32 Sacked uint32 Lost uint32 Retrans uint32 Fackets uint32 Last_data_sent uint32 Last_ack_sent uint32 Last_data_recv uint32 Last_ack_recv uint32 Pmtu uint32 Rcv_ssthresh uint32 Rtt uint32 Rttvar uint32 Snd_ssthresh uint32 Snd_cwnd uint32 Advmss uint32 Reordering uint32 Rcv_rtt uint32 Rcv_space uint32 Total_retrans uint32 Pacing_rate uint64 Max_pacing_rate uint64 Bytes_acked uint64 /* RFC4898 tcpEStatsAppHCThruOctetsAcked */ Bytes_received uint64 /* RFC4898 tcpEStatsAppHCThruOctetsReceived */ Segs_out uint32 /* RFC4898 tcpEStatsPerfSegsOut */ Segs_in uint32 /* RFC4898 tcpEStatsPerfSegsIn */ Notsent_bytes uint32 Min_rtt uint32 Data_segs_in uint32 /* RFC4898 tcpEStatsDataSegsIn */ Data_segs_out uint32 /* RFC4898 tcpEStatsDataSegsOut */ Delivery_rate uint64 Busy_time uint64 /* Time (usec) busy sending data */ Rwnd_limited uint64 /* Time (usec) limited by receive window */ Sndbuf_limited uint64 /* Time (usec) limited by send buffer */ Delivered uint32 Delivered_ce uint32 Bytes_sent uint64 /* RFC4898 tcpEStatsPerfHCDataOctetsOut */ Bytes_retrans uint64 /* RFC4898 tcpEStatsPerfOctetsRetrans */ Dsack_dups uint32 /* RFC4898 tcpEStatsStackDSACKDups */ Reord_seen uint32 /* reordering events seen */ Rcv_ooopack uint32 /* Out-of-order packets received */ Snd_wnd uint32 /* peer's advertised receive window after * scaling (bytes) */ } type TCPBBRInfo struct { BBRBW uint64 BBRMinRTT uint32 BBRPacingGain uint32 BBRCwndGain uint32 } ================================================ FILE: vendor/github.com/vishvananda/netlink/tcp_linux.go ================================================ package netlink import ( "bytes" "errors" "io" ) const ( tcpBBRInfoLen = 20 ) func checkDeserErr(err error) error { if err == io.EOF { return nil } return err } func (t *TCPInfo) deserialize(b []byte) error { var err error rb := bytes.NewBuffer(b) t.State, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Ca_state, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Retransmits, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Probes, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Backoff, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Options, err = rb.ReadByte() if err != nil { return checkDeserErr(err) } scales, err := rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Snd_wscale = scales >> 4 // first 4 bits t.Rcv_wscale = scales & 0xf // last 4 bits rateLimAndFastOpen, err := rb.ReadByte() if err != nil { return checkDeserErr(err) } t.Delivery_rate_app_limited = rateLimAndFastOpen >> 7 // get first bit t.Fastopen_client_fail = rateLimAndFastOpen >> 5 & 3 // get next two bits next := rb.Next(4) if len(next) == 0 { return nil } t.Rto = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Ato = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Snd_mss = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rcv_mss = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Unacked = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Sacked = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Lost = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Retrans = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Fackets = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Last_data_sent = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Last_ack_sent = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Last_data_recv = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Last_ack_recv = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Pmtu = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rcv_ssthresh = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rtt = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rttvar = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Snd_ssthresh = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Snd_cwnd = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Advmss = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Reordering = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rcv_rtt = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rcv_space = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Total_retrans = native.Uint32(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Pacing_rate = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Max_pacing_rate = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Bytes_acked = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Bytes_received = native.Uint64(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Segs_out = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Segs_in = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Notsent_bytes = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Min_rtt = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Data_segs_in = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Data_segs_out = native.Uint32(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Delivery_rate = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Busy_time = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Rwnd_limited = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Sndbuf_limited = native.Uint64(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Delivered = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Delivered_ce = native.Uint32(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Bytes_sent = native.Uint64(next) next = rb.Next(8) if len(next) == 0 { return nil } t.Bytes_retrans = native.Uint64(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Dsack_dups = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Reord_seen = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Rcv_ooopack = native.Uint32(next) next = rb.Next(4) if len(next) == 0 { return nil } t.Snd_wnd = native.Uint32(next) return nil } func (t *TCPBBRInfo) deserialize(b []byte) error { if len(b) != tcpBBRInfoLen { return errors.New("Invalid length") } rb := bytes.NewBuffer(b) t.BBRBW = native.Uint64(rb.Next(8)) t.BBRMinRTT = native.Uint32(rb.Next(4)) t.BBRPacingGain = native.Uint32(rb.Next(4)) t.BBRCwndGain = native.Uint32(rb.Next(4)) return nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm.go ================================================ package netlink import ( "fmt" "golang.org/x/sys/unix" ) // Proto is an enum representing an ipsec protocol. type Proto uint8 const ( XFRM_PROTO_ROUTE2 Proto = unix.IPPROTO_ROUTING XFRM_PROTO_ESP Proto = unix.IPPROTO_ESP XFRM_PROTO_AH Proto = unix.IPPROTO_AH XFRM_PROTO_HAO Proto = unix.IPPROTO_DSTOPTS XFRM_PROTO_COMP Proto = 0x6c // NOTE not defined on darwin XFRM_PROTO_IPSEC_ANY Proto = unix.IPPROTO_RAW ) func (p Proto) String() string { switch p { case XFRM_PROTO_ROUTE2: return "route2" case XFRM_PROTO_ESP: return "esp" case XFRM_PROTO_AH: return "ah" case XFRM_PROTO_HAO: return "hao" case XFRM_PROTO_COMP: return "comp" case XFRM_PROTO_IPSEC_ANY: return "ipsec-any" } return fmt.Sprintf("%d", p) } // Mode is an enum representing an ipsec transport. type Mode uint8 const ( XFRM_MODE_TRANSPORT Mode = iota XFRM_MODE_TUNNEL XFRM_MODE_ROUTEOPTIMIZATION XFRM_MODE_IN_TRIGGER XFRM_MODE_BEET XFRM_MODE_MAX ) func (m Mode) String() string { switch m { case XFRM_MODE_TRANSPORT: return "transport" case XFRM_MODE_TUNNEL: return "tunnel" case XFRM_MODE_ROUTEOPTIMIZATION: return "ro" case XFRM_MODE_IN_TRIGGER: return "in_trigger" case XFRM_MODE_BEET: return "beet" } return fmt.Sprintf("%d", m) } // XfrmMark represents the mark associated to the state or policy type XfrmMark struct { Value uint32 Mask uint32 } func (m *XfrmMark) String() string { return fmt.Sprintf("(0x%x,0x%x)", m.Value, m.Mask) } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go ================================================ package netlink import ( "fmt" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) type XfrmMsg interface { Type() nl.XfrmMsgType } type XfrmMsgExpire struct { XfrmState *XfrmState Hard bool } func (ue *XfrmMsgExpire) Type() nl.XfrmMsgType { return nl.XFRM_MSG_EXPIRE } func parseXfrmMsgExpire(b []byte) *XfrmMsgExpire { var e XfrmMsgExpire msg := nl.DeserializeXfrmUserExpire(b) e.XfrmState = xfrmStateFromXfrmUsersaInfo(&msg.XfrmUsersaInfo) e.Hard = msg.Hard == 1 return &e } func XfrmMonitor(ch chan<- XfrmMsg, done <-chan struct{}, errorChan chan<- error, types ...nl.XfrmMsgType) error { groups, err := xfrmMcastGroups(types) if err != nil { return nil } s, err := nl.SubscribeAt(netns.None(), netns.None(), unix.NETLINK_XFRM, groups...) if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } go func() { defer close(ch) for { msgs, from, err := s.Receive() if err != nil { errorChan <- err return } if from.Pid != nl.PidKernel { errorChan <- fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel) return } for _, m := range msgs { switch m.Header.Type { case nl.XFRM_MSG_EXPIRE: ch <- parseXfrmMsgExpire(m.Data) default: errorChan <- fmt.Errorf("unsupported msg type: %x", m.Header.Type) } } } }() return nil } func xfrmMcastGroups(types []nl.XfrmMsgType) ([]uint, error) { groups := make([]uint, 0) if len(types) == 0 { return nil, fmt.Errorf("no xfrm msg type specified") } for _, t := range types { var group uint switch t { case nl.XFRM_MSG_EXPIRE: group = nl.XFRMNLGRP_EXPIRE default: return nil, fmt.Errorf("unsupported group: %x", t) } groups = append(groups, group) } return groups, nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm_policy.go ================================================ package netlink import ( "fmt" "net" ) // Dir is an enum representing an ipsec template direction. type Dir uint8 const ( XFRM_DIR_IN Dir = iota XFRM_DIR_OUT XFRM_DIR_FWD XFRM_SOCKET_IN XFRM_SOCKET_OUT XFRM_SOCKET_FWD ) func (d Dir) String() string { switch d { case XFRM_DIR_IN: return "dir in" case XFRM_DIR_OUT: return "dir out" case XFRM_DIR_FWD: return "dir fwd" case XFRM_SOCKET_IN: return "socket in" case XFRM_SOCKET_OUT: return "socket out" case XFRM_SOCKET_FWD: return "socket fwd" } return fmt.Sprintf("socket %d", d-XFRM_SOCKET_IN) } // PolicyAction is an enum representing an ipsec policy action. type PolicyAction uint8 const ( XFRM_POLICY_ALLOW PolicyAction = 0 XFRM_POLICY_BLOCK PolicyAction = 1 ) func (a PolicyAction) String() string { switch a { case XFRM_POLICY_ALLOW: return "allow" case XFRM_POLICY_BLOCK: return "block" default: return fmt.Sprintf("action %d", a) } } // XfrmPolicyTmpl encapsulates a rule for the base addresses of an ipsec // policy. These rules are matched with XfrmState to determine encryption // and authentication algorithms. type XfrmPolicyTmpl struct { Dst net.IP Src net.IP Proto Proto Mode Mode Spi int Reqid int Optional int } func (t XfrmPolicyTmpl) String() string { return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, Mode: %s, Spi: 0x%x, Reqid: 0x%x}", t.Dst, t.Src, t.Proto, t.Mode, t.Spi, t.Reqid) } // XfrmPolicy represents an ipsec policy. It represents the overlay network // and has a list of XfrmPolicyTmpls representing the base addresses of // the policy. type XfrmPolicy struct { Dst *net.IPNet Src *net.IPNet Proto Proto DstPort int SrcPort int Dir Dir Priority int Index int Action PolicyAction Ifindex int Ifid int Mark *XfrmMark Tmpls []XfrmPolicyTmpl } func (p XfrmPolicy) String() string { return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Action: %s, Ifindex: %d, Ifid: %d, Mark: %s, Tmpls: %s}", p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Action, p.Ifindex, p.Ifid, p.Mark, p.Tmpls) } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go ================================================ package netlink import ( "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) { sel.Family = uint16(nl.FAMILY_V4) if policy.Dst != nil { sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP)) sel.Daddr.FromIP(policy.Dst.IP) prefixlenD, _ := policy.Dst.Mask.Size() sel.PrefixlenD = uint8(prefixlenD) } if policy.Src != nil { sel.Saddr.FromIP(policy.Src.IP) prefixlenS, _ := policy.Src.Mask.Size() sel.PrefixlenS = uint8(prefixlenS) } sel.Proto = uint8(policy.Proto) sel.Dport = nl.Swap16(uint16(policy.DstPort)) sel.Sport = nl.Swap16(uint16(policy.SrcPort)) if sel.Dport != 0 { sel.DportMask = ^uint16(0) } if sel.Sport != 0 { sel.SportMask = ^uint16(0) } sel.Ifindex = int32(policy.Ifindex) } // XfrmPolicyAdd will add an xfrm policy to the system. // Equivalent to: `ip xfrm policy add $policy` func XfrmPolicyAdd(policy *XfrmPolicy) error { return pkgHandle.XfrmPolicyAdd(policy) } // XfrmPolicyAdd will add an xfrm policy to the system. // Equivalent to: `ip xfrm policy add $policy` func (h *Handle) XfrmPolicyAdd(policy *XfrmPolicy) error { return h.xfrmPolicyAddOrUpdate(policy, nl.XFRM_MSG_NEWPOLICY) } // XfrmPolicyUpdate will update an xfrm policy to the system. // Equivalent to: `ip xfrm policy update $policy` func XfrmPolicyUpdate(policy *XfrmPolicy) error { return pkgHandle.XfrmPolicyUpdate(policy) } // XfrmPolicyUpdate will update an xfrm policy to the system. // Equivalent to: `ip xfrm policy update $policy` func (h *Handle) XfrmPolicyUpdate(policy *XfrmPolicy) error { return h.xfrmPolicyAddOrUpdate(policy, nl.XFRM_MSG_UPDPOLICY) } func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error { req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) msg := &nl.XfrmUserpolicyInfo{} selFromPolicy(&msg.Sel, policy) msg.Priority = uint32(policy.Priority) msg.Index = uint32(policy.Index) msg.Dir = uint8(policy.Dir) msg.Action = uint8(policy.Action) msg.Lft.SoftByteLimit = nl.XFRM_INF msg.Lft.HardByteLimit = nl.XFRM_INF msg.Lft.SoftPacketLimit = nl.XFRM_INF msg.Lft.HardPacketLimit = nl.XFRM_INF req.AddData(msg) tmplData := make([]byte, nl.SizeofXfrmUserTmpl*len(policy.Tmpls)) for i, tmpl := range policy.Tmpls { start := i * nl.SizeofXfrmUserTmpl userTmpl := nl.DeserializeXfrmUserTmpl(tmplData[start : start+nl.SizeofXfrmUserTmpl]) userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst) userTmpl.Saddr.FromIP(tmpl.Src) userTmpl.XfrmId.Proto = uint8(tmpl.Proto) userTmpl.XfrmId.Spi = nl.Swap32(uint32(tmpl.Spi)) userTmpl.Mode = uint8(tmpl.Mode) userTmpl.Reqid = uint32(tmpl.Reqid) userTmpl.Optional = uint8(tmpl.Optional) userTmpl.Aalgos = ^uint32(0) userTmpl.Ealgos = ^uint32(0) userTmpl.Calgos = ^uint32(0) } if len(tmplData) > 0 { tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData) req.AddData(tmpls) } if policy.Mark != nil { out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(policy.Mark)) req.AddData(out) } ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid))) req.AddData(ifId) _, err := req.Execute(unix.NETLINK_XFRM, 0) return err } // XfrmPolicyDel will delete an xfrm policy from the system. Note that // the Tmpls are ignored when matching the policy to delete. // Equivalent to: `ip xfrm policy del $policy` func XfrmPolicyDel(policy *XfrmPolicy) error { return pkgHandle.XfrmPolicyDel(policy) } // XfrmPolicyDel will delete an xfrm policy from the system. Note that // the Tmpls are ignored when matching the policy to delete. // Equivalent to: `ip xfrm policy del $policy` func (h *Handle) XfrmPolicyDel(policy *XfrmPolicy) error { _, err := h.xfrmPolicyGetOrDelete(policy, nl.XFRM_MSG_DELPOLICY) return err } // XfrmPolicyList gets a list of xfrm policies in the system. // Equivalent to: `ip xfrm policy show`. // The list can be filtered by ip family. func XfrmPolicyList(family int) ([]XfrmPolicy, error) { return pkgHandle.XfrmPolicyList(family) } // XfrmPolicyList gets a list of xfrm policies in the system. // Equivalent to: `ip xfrm policy show`. // The list can be filtered by ip family. func (h *Handle) XfrmPolicyList(family int) ([]XfrmPolicy, error) { req := h.newNetlinkRequest(nl.XFRM_MSG_GETPOLICY, unix.NLM_F_DUMP) msg := nl.NewIfInfomsg(family) req.AddData(msg) msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY) if err != nil { return nil, err } var res []XfrmPolicy for _, m := range msgs { if policy, err := parseXfrmPolicy(m, family); err == nil { res = append(res, *policy) } else if err == familyError { continue } else { return nil, err } } return res, nil } // XfrmPolicyGet gets a the policy described by the index or selector, if found. // Equivalent to: `ip xfrm policy get { SELECTOR | index INDEX } dir DIR [ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]`. func XfrmPolicyGet(policy *XfrmPolicy) (*XfrmPolicy, error) { return pkgHandle.XfrmPolicyGet(policy) } // XfrmPolicyGet gets a the policy described by the index or selector, if found. // Equivalent to: `ip xfrm policy get { SELECTOR | index INDEX } dir DIR [ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]`. func (h *Handle) XfrmPolicyGet(policy *XfrmPolicy) (*XfrmPolicy, error) { return h.xfrmPolicyGetOrDelete(policy, nl.XFRM_MSG_GETPOLICY) } // XfrmPolicyFlush will flush the policies on the system. // Equivalent to: `ip xfrm policy flush` func XfrmPolicyFlush() error { return pkgHandle.XfrmPolicyFlush() } // XfrmPolicyFlush will flush the policies on the system. // Equivalent to: `ip xfrm policy flush` func (h *Handle) XfrmPolicyFlush() error { req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHPOLICY, unix.NLM_F_ACK) _, err := req.Execute(unix.NETLINK_XFRM, 0) return err } func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPolicy, error) { req := h.newNetlinkRequest(nlProto, unix.NLM_F_ACK) msg := &nl.XfrmUserpolicyId{} selFromPolicy(&msg.Sel, policy) msg.Index = uint32(policy.Index) msg.Dir = uint8(policy.Dir) req.AddData(msg) if policy.Mark != nil { out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(policy.Mark)) req.AddData(out) } ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid))) req.AddData(ifId) resType := nl.XFRM_MSG_NEWPOLICY if nlProto == nl.XFRM_MSG_DELPOLICY { resType = 0 } msgs, err := req.Execute(unix.NETLINK_XFRM, uint16(resType)) if err != nil { return nil, err } if nlProto == nl.XFRM_MSG_DELPOLICY { return nil, err } return parseXfrmPolicy(msgs[0], FAMILY_ALL) } func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) { msg := nl.DeserializeXfrmUserpolicyInfo(m) // This is mainly for the policy dump if family != FAMILY_ALL && family != int(msg.Sel.Family) { return nil, familyError } var policy XfrmPolicy policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD) policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS) policy.Proto = Proto(msg.Sel.Proto) policy.DstPort = int(nl.Swap16(msg.Sel.Dport)) policy.SrcPort = int(nl.Swap16(msg.Sel.Sport)) policy.Ifindex = int(msg.Sel.Ifindex) policy.Priority = int(msg.Priority) policy.Index = int(msg.Index) policy.Dir = Dir(msg.Dir) policy.Action = PolicyAction(msg.Action) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } for _, attr := range attrs { switch attr.Attr.Type { case nl.XFRMA_TMPL: max := len(attr.Value) for i := 0; i < max; i += nl.SizeofXfrmUserTmpl { var resTmpl XfrmPolicyTmpl tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl]) resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP() resTmpl.Src = tmpl.Saddr.ToIP() resTmpl.Proto = Proto(tmpl.XfrmId.Proto) resTmpl.Mode = Mode(tmpl.Mode) resTmpl.Spi = int(nl.Swap32(tmpl.XfrmId.Spi)) resTmpl.Reqid = int(tmpl.Reqid) resTmpl.Optional = int(tmpl.Optional) policy.Tmpls = append(policy.Tmpls, resTmpl) } case nl.XFRMA_MARK: mark := nl.DeserializeXfrmMark(attr.Value[:]) policy.Mark = new(XfrmMark) policy.Mark.Value = mark.Value policy.Mark.Mask = mark.Mask case nl.XFRMA_IF_ID: policy.Ifid = int(native.Uint32(attr.Value)) } } return &policy, nil } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm_state.go ================================================ package netlink import ( "fmt" "net" "time" ) // XfrmStateAlgo represents the algorithm to use for the ipsec encryption. type XfrmStateAlgo struct { Name string Key []byte TruncateLen int // Auth only ICVLen int // AEAD only } func (a XfrmStateAlgo) String() string { base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key) if a.TruncateLen != 0 { base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen) } if a.ICVLen != 0 { base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen) } return fmt.Sprintf("%s}", base) } // EncapType is an enum representing the optional packet encapsulation. type EncapType uint8 const ( XFRM_ENCAP_ESPINUDP_NONIKE EncapType = iota + 1 XFRM_ENCAP_ESPINUDP ) func (e EncapType) String() string { switch e { case XFRM_ENCAP_ESPINUDP_NONIKE: return "espinudp-non-ike" case XFRM_ENCAP_ESPINUDP: return "espinudp" } return "unknown" } // XfrmStateEncap represents the encapsulation to use for the ipsec encryption. type XfrmStateEncap struct { Type EncapType SrcPort int DstPort int OriginalAddress net.IP } func (e XfrmStateEncap) String() string { return fmt.Sprintf("{Type: %s, Srcport: %d, DstPort: %d, OriginalAddress: %v}", e.Type, e.SrcPort, e.DstPort, e.OriginalAddress) } // XfrmStateLimits represents the configured limits for the state. type XfrmStateLimits struct { ByteSoft uint64 ByteHard uint64 PacketSoft uint64 PacketHard uint64 TimeSoft uint64 TimeHard uint64 TimeUseSoft uint64 TimeUseHard uint64 } // XfrmStateStats represents the current number of bytes/packets // processed by this State, the State's installation and first use // time and the replay window counters. type XfrmStateStats struct { ReplayWindow uint32 Replay uint32 Failed uint32 Bytes uint64 Packets uint64 AddTime uint64 UseTime uint64 } // XfrmState represents the state of an ipsec policy. It optionally // contains an XfrmStateAlgo for encryption and one for authentication. type XfrmState struct { Dst net.IP Src net.IP Proto Proto Mode Mode Spi int Reqid int ReplayWindow int Limits XfrmStateLimits Statistics XfrmStateStats Mark *XfrmMark OutputMark *XfrmMark Ifid int Auth *XfrmStateAlgo Crypt *XfrmStateAlgo Aead *XfrmStateAlgo Encap *XfrmStateEncap ESN bool } func (sa XfrmState) String() string { return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, OutputMark: %v, Ifid: %d, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t", sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.OutputMark, sa.Ifid, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN) } func (sa XfrmState) Print(stats bool) string { if !stats { return sa.String() } at := time.Unix(int64(sa.Statistics.AddTime), 0).Format(time.UnixDate) ut := "-" if sa.Statistics.UseTime > 0 { ut = time.Unix(int64(sa.Statistics.UseTime), 0).Format(time.UnixDate) } return fmt.Sprintf("%s, ByteSoft: %s, ByteHard: %s, PacketSoft: %s, PacketHard: %s, TimeSoft: %d, TimeHard: %d, TimeUseSoft: %d, TimeUseHard: %d, Bytes: %d, Packets: %d, "+ "AddTime: %s, UseTime: %s, ReplayWindow: %d, Replay: %d, Failed: %d", sa.String(), printLimit(sa.Limits.ByteSoft), printLimit(sa.Limits.ByteHard), printLimit(sa.Limits.PacketSoft), printLimit(sa.Limits.PacketHard), sa.Limits.TimeSoft, sa.Limits.TimeHard, sa.Limits.TimeUseSoft, sa.Limits.TimeUseHard, sa.Statistics.Bytes, sa.Statistics.Packets, at, ut, sa.Statistics.ReplayWindow, sa.Statistics.Replay, sa.Statistics.Failed) } func printLimit(lmt uint64) string { if lmt == ^uint64(0) { return "(INF)" } return fmt.Sprintf("%d", lmt) } ================================================ FILE: vendor/github.com/vishvananda/netlink/xfrm_state_linux.go ================================================ package netlink import ( "fmt" "unsafe" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) func writeStateAlgo(a *XfrmStateAlgo) []byte { algo := nl.XfrmAlgo{ AlgKeyLen: uint32(len(a.Key) * 8), AlgKey: a.Key, } end := len(a.Name) if end > 64 { end = 64 } copy(algo.AlgName[:end], a.Name) return algo.Serialize() } func writeStateAlgoAuth(a *XfrmStateAlgo) []byte { algo := nl.XfrmAlgoAuth{ AlgKeyLen: uint32(len(a.Key) * 8), AlgTruncLen: uint32(a.TruncateLen), AlgKey: a.Key, } end := len(a.Name) if end > 64 { end = 64 } copy(algo.AlgName[:end], a.Name) return algo.Serialize() } func writeStateAlgoAead(a *XfrmStateAlgo) []byte { algo := nl.XfrmAlgoAEAD{ AlgKeyLen: uint32(len(a.Key) * 8), AlgICVLen: uint32(a.ICVLen), AlgKey: a.Key, } end := len(a.Name) if end > 64 { end = 64 } copy(algo.AlgName[:end], a.Name) return algo.Serialize() } func writeMark(m *XfrmMark) []byte { mark := &nl.XfrmMark{ Value: m.Value, Mask: m.Mask, } if mark.Mask == 0 { mark.Mask = ^uint32(0) } return mark.Serialize() } func writeReplayEsn(replayWindow int) []byte { replayEsn := &nl.XfrmReplayStateEsn{ OSeq: 0, Seq: 0, OSeqHi: 0, SeqHi: 0, ReplayWindow: uint32(replayWindow), } // Linux stores the bitmap to identify the already received sequence packets in blocks of uint32 elements. // Therefore bitmap length is the minimum number of uint32 elements needed. The following is a ceiling operation. bytesPerElem := int(unsafe.Sizeof(replayEsn.BmpLen)) // Any uint32 variable is good for this replayEsn.BmpLen = uint32((replayWindow + (bytesPerElem * 8) - 1) / (bytesPerElem * 8)) return replayEsn.Serialize() } // XfrmStateAdd will add an xfrm state to the system. // Equivalent to: `ip xfrm state add $state` func XfrmStateAdd(state *XfrmState) error { return pkgHandle.XfrmStateAdd(state) } // XfrmStateAdd will add an xfrm state to the system. // Equivalent to: `ip xfrm state add $state` func (h *Handle) XfrmStateAdd(state *XfrmState) error { return h.xfrmStateAddOrUpdate(state, nl.XFRM_MSG_NEWSA) } // XfrmStateAllocSpi will allocate an xfrm state in the system. // Equivalent to: `ip xfrm state allocspi` func XfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) { return pkgHandle.xfrmStateAllocSpi(state) } // XfrmStateUpdate will update an xfrm state to the system. // Equivalent to: `ip xfrm state update $state` func XfrmStateUpdate(state *XfrmState) error { return pkgHandle.XfrmStateUpdate(state) } // XfrmStateUpdate will update an xfrm state to the system. // Equivalent to: `ip xfrm state update $state` func (h *Handle) XfrmStateUpdate(state *XfrmState) error { return h.xfrmStateAddOrUpdate(state, nl.XFRM_MSG_UPDSA) } func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error { // A state with spi 0 can't be deleted so don't allow it to be set if state.Spi == 0 { return fmt.Errorf("Spi must be set when adding xfrm state.") } req := h.newNetlinkRequest(nlProto, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) msg := xfrmUsersaInfoFromXfrmState(state) if state.ESN { if state.ReplayWindow == 0 { return fmt.Errorf("ESN flag set without ReplayWindow") } msg.Flags |= nl.XFRM_STATE_ESN msg.ReplayWindow = 0 } limitsToLft(state.Limits, &msg.Lft) req.AddData(msg) if state.Auth != nil { out := nl.NewRtAttr(nl.XFRMA_ALG_AUTH_TRUNC, writeStateAlgoAuth(state.Auth)) req.AddData(out) } if state.Crypt != nil { out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt)) req.AddData(out) } if state.Aead != nil { out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead)) req.AddData(out) } if state.Encap != nil { encapData := make([]byte, nl.SizeofXfrmEncapTmpl) encap := nl.DeserializeXfrmEncapTmpl(encapData) encap.EncapType = uint16(state.Encap.Type) encap.EncapSport = nl.Swap16(uint16(state.Encap.SrcPort)) encap.EncapDport = nl.Swap16(uint16(state.Encap.DstPort)) encap.EncapOa.FromIP(state.Encap.OriginalAddress) out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData) req.AddData(out) } if state.Mark != nil { out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) req.AddData(out) } if state.ESN { out := nl.NewRtAttr(nl.XFRMA_REPLAY_ESN_VAL, writeReplayEsn(state.ReplayWindow)) req.AddData(out) } if state.OutputMark != nil { out := nl.NewRtAttr(nl.XFRMA_SET_MARK, nl.Uint32Attr(state.OutputMark.Value)) req.AddData(out) if state.OutputMark.Mask != 0 { out = nl.NewRtAttr(nl.XFRMA_SET_MARK_MASK, nl.Uint32Attr(state.OutputMark.Mask)) req.AddData(out) } } ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid))) req.AddData(ifId) _, err := req.Execute(unix.NETLINK_XFRM, 0) return err } func (h *Handle) xfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) { req := h.newNetlinkRequest(nl.XFRM_MSG_ALLOCSPI, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) msg := &nl.XfrmUserSpiInfo{} msg.XfrmUsersaInfo = *(xfrmUsersaInfoFromXfrmState(state)) // 1-255 is reserved by IANA for future use msg.Min = 0x100 msg.Max = 0xffffffff req.AddData(msg) if state.Mark != nil { out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) req.AddData(out) } msgs, err := req.Execute(unix.NETLINK_XFRM, 0) if err != nil { return nil, err } return parseXfrmState(msgs[0], FAMILY_ALL) } // XfrmStateDel will delete an xfrm state from the system. Note that // the Algos are ignored when matching the state to delete. // Equivalent to: `ip xfrm state del $state` func XfrmStateDel(state *XfrmState) error { return pkgHandle.XfrmStateDel(state) } // XfrmStateDel will delete an xfrm state from the system. Note that // the Algos are ignored when matching the state to delete. // Equivalent to: `ip xfrm state del $state` func (h *Handle) XfrmStateDel(state *XfrmState) error { _, err := h.xfrmStateGetOrDelete(state, nl.XFRM_MSG_DELSA) return err } // XfrmStateList gets a list of xfrm states in the system. // Equivalent to: `ip [-4|-6] xfrm state show`. // The list can be filtered by ip family. func XfrmStateList(family int) ([]XfrmState, error) { return pkgHandle.XfrmStateList(family) } // XfrmStateList gets a list of xfrm states in the system. // Equivalent to: `ip xfrm state show`. // The list can be filtered by ip family. func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) { req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, unix.NLM_F_DUMP) msgs, err := req.Execute(unix.NETLINK_XFRM, nl.XFRM_MSG_NEWSA) if err != nil { return nil, err } var res []XfrmState for _, m := range msgs { if state, err := parseXfrmState(m, family); err == nil { res = append(res, *state) } else if err == familyError { continue } else { return nil, err } } return res, nil } // XfrmStateGet gets the xfrm state described by the ID, if found. // Equivalent to: `ip xfrm state get ID [ mark MARK [ mask MASK ] ]`. // Only the fields which constitue the SA ID must be filled in: // ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ] // mark is optional func XfrmStateGet(state *XfrmState) (*XfrmState, error) { return pkgHandle.XfrmStateGet(state) } // XfrmStateGet gets the xfrm state described by the ID, if found. // Equivalent to: `ip xfrm state get ID [ mark MARK [ mask MASK ] ]`. // Only the fields which constitue the SA ID must be filled in: // ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ] // mark is optional func (h *Handle) XfrmStateGet(state *XfrmState) (*XfrmState, error) { return h.xfrmStateGetOrDelete(state, nl.XFRM_MSG_GETSA) } func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState, error) { req := h.newNetlinkRequest(nlProto, unix.NLM_F_ACK) msg := &nl.XfrmUsersaId{} msg.Family = uint16(nl.GetIPFamily(state.Dst)) msg.Daddr.FromIP(state.Dst) msg.Proto = uint8(state.Proto) msg.Spi = nl.Swap32(uint32(state.Spi)) req.AddData(msg) if state.Mark != nil { out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) req.AddData(out) } if state.Src != nil { out := nl.NewRtAttr(nl.XFRMA_SRCADDR, state.Src.To16()) req.AddData(out) } ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid))) req.AddData(ifId) resType := nl.XFRM_MSG_NEWSA if nlProto == nl.XFRM_MSG_DELSA { resType = 0 } msgs, err := req.Execute(unix.NETLINK_XFRM, uint16(resType)) if err != nil { return nil, err } if nlProto == nl.XFRM_MSG_DELSA { return nil, nil } s, err := parseXfrmState(msgs[0], FAMILY_ALL) if err != nil { return nil, err } return s, nil } var familyError = fmt.Errorf("family error") func xfrmStateFromXfrmUsersaInfo(msg *nl.XfrmUsersaInfo) *XfrmState { var state XfrmState state.Dst = msg.Id.Daddr.ToIP() state.Src = msg.Saddr.ToIP() state.Proto = Proto(msg.Id.Proto) state.Mode = Mode(msg.Mode) state.Spi = int(nl.Swap32(msg.Id.Spi)) state.Reqid = int(msg.Reqid) state.ReplayWindow = int(msg.ReplayWindow) lftToLimits(&msg.Lft, &state.Limits) curToStats(&msg.Curlft, &msg.Stats, &state.Statistics) return &state } func parseXfrmState(m []byte, family int) (*XfrmState, error) { msg := nl.DeserializeXfrmUsersaInfo(m) // This is mainly for the state dump if family != FAMILY_ALL && family != int(msg.Family) { return nil, familyError } state := xfrmStateFromXfrmUsersaInfo(msg) attrs, err := nl.ParseRouteAttr(m[nl.SizeofXfrmUsersaInfo:]) if err != nil { return nil, err } for _, attr := range attrs { switch attr.Attr.Type { case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT: var resAlgo *XfrmStateAlgo if attr.Attr.Type == nl.XFRMA_ALG_AUTH { if state.Auth == nil { state.Auth = new(XfrmStateAlgo) } resAlgo = state.Auth } else { state.Crypt = new(XfrmStateAlgo) resAlgo = state.Crypt } algo := nl.DeserializeXfrmAlgo(attr.Value[:]) (*resAlgo).Name = nl.BytesToString(algo.AlgName[:]) (*resAlgo).Key = algo.AlgKey case nl.XFRMA_ALG_AUTH_TRUNC: if state.Auth == nil { state.Auth = new(XfrmStateAlgo) } algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:]) state.Auth.Name = nl.BytesToString(algo.AlgName[:]) state.Auth.Key = algo.AlgKey state.Auth.TruncateLen = int(algo.AlgTruncLen) case nl.XFRMA_ALG_AEAD: state.Aead = new(XfrmStateAlgo) algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:]) state.Aead.Name = nl.BytesToString(algo.AlgName[:]) state.Aead.Key = algo.AlgKey state.Aead.ICVLen = int(algo.AlgICVLen) case nl.XFRMA_ENCAP: encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) state.Encap = new(XfrmStateEncap) state.Encap.Type = EncapType(encap.EncapType) state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport)) state.Encap.DstPort = int(nl.Swap16(encap.EncapDport)) state.Encap.OriginalAddress = encap.EncapOa.ToIP() case nl.XFRMA_MARK: mark := nl.DeserializeXfrmMark(attr.Value[:]) state.Mark = new(XfrmMark) state.Mark.Value = mark.Value state.Mark.Mask = mark.Mask case nl.XFRMA_SET_MARK: if state.OutputMark == nil { state.OutputMark = new(XfrmMark) } state.OutputMark.Value = native.Uint32(attr.Value) case nl.XFRMA_SET_MARK_MASK: if state.OutputMark == nil { state.OutputMark = new(XfrmMark) } state.OutputMark.Mask = native.Uint32(attr.Value) if state.OutputMark.Mask == 0xffffffff { state.OutputMark.Mask = 0 } case nl.XFRMA_IF_ID: state.Ifid = int(native.Uint32(attr.Value)) } } return state, nil } // XfrmStateFlush will flush the xfrm state on the system. // proto = 0 means any transformation protocols // Equivalent to: `ip xfrm state flush [ proto XFRM-PROTO ]` func XfrmStateFlush(proto Proto) error { return pkgHandle.XfrmStateFlush(proto) } // XfrmStateFlush will flush the xfrm state on the system. // proto = 0 means any transformation protocols // Equivalent to: `ip xfrm state flush [ proto XFRM-PROTO ]` func (h *Handle) XfrmStateFlush(proto Proto) error { req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHSA, unix.NLM_F_ACK) req.AddData(&nl.XfrmUsersaFlush{Proto: uint8(proto)}) _, err := req.Execute(unix.NETLINK_XFRM, 0) return err } func limitsToLft(lmts XfrmStateLimits, lft *nl.XfrmLifetimeCfg) { if lmts.ByteSoft != 0 { lft.SoftByteLimit = lmts.ByteSoft } else { lft.SoftByteLimit = nl.XFRM_INF } if lmts.ByteHard != 0 { lft.HardByteLimit = lmts.ByteHard } else { lft.HardByteLimit = nl.XFRM_INF } if lmts.PacketSoft != 0 { lft.SoftPacketLimit = lmts.PacketSoft } else { lft.SoftPacketLimit = nl.XFRM_INF } if lmts.PacketHard != 0 { lft.HardPacketLimit = lmts.PacketHard } else { lft.HardPacketLimit = nl.XFRM_INF } lft.SoftAddExpiresSeconds = lmts.TimeSoft lft.HardAddExpiresSeconds = lmts.TimeHard lft.SoftUseExpiresSeconds = lmts.TimeUseSoft lft.HardUseExpiresSeconds = lmts.TimeUseHard } func lftToLimits(lft *nl.XfrmLifetimeCfg, lmts *XfrmStateLimits) { *lmts = *(*XfrmStateLimits)(unsafe.Pointer(lft)) } func curToStats(cur *nl.XfrmLifetimeCur, wstats *nl.XfrmStats, stats *XfrmStateStats) { stats.Bytes = cur.Bytes stats.Packets = cur.Packets stats.AddTime = cur.AddTime stats.UseTime = cur.UseTime stats.ReplayWindow = wstats.ReplayWindow stats.Replay = wstats.Replay stats.Failed = wstats.IntegrityFailed } func xfrmUsersaInfoFromXfrmState(state *XfrmState) *nl.XfrmUsersaInfo { msg := &nl.XfrmUsersaInfo{} msg.Family = uint16(nl.GetIPFamily(state.Dst)) msg.Id.Daddr.FromIP(state.Dst) msg.Saddr.FromIP(state.Src) msg.Id.Proto = uint8(state.Proto) msg.Mode = uint8(state.Mode) msg.Id.Spi = nl.Swap32(uint32(state.Spi)) msg.Reqid = uint32(state.Reqid) msg.ReplayWindow = uint8(state.ReplayWindow) return msg } ================================================ FILE: vendor/github.com/vishvananda/netns/LICENSE ================================================ 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 Copyright 2014 Vishvananda Ishaya. Copyright 2014 Docker, Inc. 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: vendor/github.com/vishvananda/netns/README.md ================================================ # netns - network namespaces in go # The netns package provides an ultra-simple interface for handling network namespaces in go. Changing namespaces requires elevated privileges, so in most cases this code needs to be run as root. ## Local Build and Test ## You can use go get command: go get github.com/vishvananda/netns Testing (requires root): sudo -E go test github.com/vishvananda/netns ## Example ## ```go package main import ( "fmt" "net" "runtime" "github.com/vishvananda/netns" ) func main() { // Lock the OS Thread so we don't accidentally switch namespaces runtime.LockOSThread() defer runtime.UnlockOSThread() // Save the current network namespace origns, _ := netns.Get() defer origns.Close() // Create a new network namespace newns, _ := netns.New() defer newns.Close() // Do something with the network namespace ifaces, _ := net.Interfaces() fmt.Printf("Interfaces: %v\n", ifaces) // Switch back to the original namespace netns.Set(origns) } ``` ================================================ FILE: vendor/github.com/vishvananda/netns/go.mod ================================================ module github.com/vishvananda/netns go 1.12 require golang.org/x/sys v0.0.0-20200217220822-9197077df867 ================================================ FILE: vendor/github.com/vishvananda/netns/go.sum ================================================ golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= ================================================ FILE: vendor/github.com/vishvananda/netns/netns.go ================================================ // Package netns allows ultra-simple network namespace handling. NsHandles // can be retrieved and set. Note that the current namespace is thread // local so actions that set and reset namespaces should use LockOSThread // to make sure the namespace doesn't change due to a goroutine switch. // It is best to close NsHandles when you are done with them. This can be // accomplished via a `defer ns.Close()` on the handle. Changing namespaces // requires elevated privileges, so in most cases this code needs to be run // as root. package netns import ( "fmt" "golang.org/x/sys/unix" ) // NsHandle is a handle to a network namespace. It can be cast directly // to an int and used as a file descriptor. type NsHandle int // Equal determines if two network handles refer to the same network // namespace. This is done by comparing the device and inode that the // file descriptors point to. func (ns NsHandle) Equal(other NsHandle) bool { if ns == other { return true } var s1, s2 unix.Stat_t if err := unix.Fstat(int(ns), &s1); err != nil { return false } if err := unix.Fstat(int(other), &s2); err != nil { return false } return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino) } // String shows the file descriptor number and its dev and inode. func (ns NsHandle) String() string { if ns == -1 { return "NS(None)" } var s unix.Stat_t if err := unix.Fstat(int(ns), &s); err != nil { return fmt.Sprintf("NS(%d: unknown)", ns) } return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino) } // UniqueId returns a string which uniquely identifies the namespace // associated with the network handle. func (ns NsHandle) UniqueId() string { if ns == -1 { return "NS(none)" } var s unix.Stat_t if err := unix.Fstat(int(ns), &s); err != nil { return "NS(unknown)" } return fmt.Sprintf("NS(%d:%d)", s.Dev, s.Ino) } // IsOpen returns true if Close() has not been called. func (ns NsHandle) IsOpen() bool { return ns != -1 } // Close closes the NsHandle and resets its file descriptor to -1. // It is not safe to use an NsHandle after Close() is called. func (ns *NsHandle) Close() error { if err := unix.Close(int(*ns)); err != nil { return err } (*ns) = -1 return nil } // None gets an empty (closed) NsHandle. func None() NsHandle { return NsHandle(-1) } ================================================ FILE: vendor/github.com/vishvananda/netns/netns_linux.go ================================================ // +build linux package netns import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "strconv" "strings" "syscall" "golang.org/x/sys/unix" ) // Deprecated: use syscall pkg instead (go >= 1.5 needed). const ( CLONE_NEWUTS = 0x04000000 /* New utsname group? */ CLONE_NEWIPC = 0x08000000 /* New ipcs */ CLONE_NEWUSER = 0x10000000 /* New user namespace */ CLONE_NEWPID = 0x20000000 /* New pid namespace */ CLONE_NEWNET = 0x40000000 /* New network namespace */ CLONE_IO = 0x80000000 /* Get io context */ bindMountPath = "/run/netns" /* Bind mount path for named netns */ ) // Setns sets namespace using syscall. Note that this should be a method // in syscall but it has not been added. func Setns(ns NsHandle, nstype int) (err error) { return unix.Setns(int(ns), nstype) } // Set sets the current network namespace to the namespace represented // by NsHandle. func Set(ns NsHandle) (err error) { return Setns(ns, CLONE_NEWNET) } // New creates a new network namespace, sets it as current and returns // a handle to it. func New() (ns NsHandle, err error) { if err := unix.Unshare(CLONE_NEWNET); err != nil { return -1, err } return Get() } // NewNamed creates a new named network namespace and returns a handle to it func NewNamed(name string) (NsHandle, error) { if _, err := os.Stat(bindMountPath); os.IsNotExist(err) { err = os.MkdirAll(bindMountPath, 0755) if err != nil { return None(), err } } newNs, err := New() if err != nil { return None(), err } namedPath := path.Join(bindMountPath, name) f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0444) if err != nil { return None(), err } f.Close() nsPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid()) err = syscall.Mount(nsPath, namedPath, "bind", syscall.MS_BIND, "") if err != nil { return None(), err } return newNs, nil } // DeleteNamed deletes a named network namespace func DeleteNamed(name string) error { namedPath := path.Join(bindMountPath, name) err := syscall.Unmount(namedPath, syscall.MNT_DETACH) if err != nil { return err } return os.Remove(namedPath) } // Get gets a handle to the current threads network namespace. func Get() (NsHandle, error) { return GetFromThread(os.Getpid(), unix.Gettid()) } // GetFromPath gets a handle to a network namespace // identified by the path func GetFromPath(path string) (NsHandle, error) { fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC, 0) if err != nil { return -1, err } return NsHandle(fd), nil } // GetFromName gets a handle to a named network namespace such as one // created by `ip netns add`. func GetFromName(name string) (NsHandle, error) { return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name)) } // GetFromPid gets a handle to the network namespace of a given pid. func GetFromPid(pid int) (NsHandle, error) { return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid)) } // GetFromThread gets a handle to the network namespace of a given pid and tid. func GetFromThread(pid, tid int) (NsHandle, error) { return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)) } // GetFromDocker gets a handle to the network namespace of a docker container. // Id is prefixed matched against the running docker containers, so a short // identifier can be used as long as it isn't ambiguous. func GetFromDocker(id string) (NsHandle, error) { pid, err := getPidForContainer(id) if err != nil { return -1, err } return GetFromPid(pid) } // borrowed from docker/utils/utils.go func findCgroupMountpoint(cgroupType string) (string, error) { output, err := ioutil.ReadFile("/proc/mounts") if err != nil { return "", err } // /proc/mounts has 6 fields per line, one mount per line, e.g. // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 for _, line := range strings.Split(string(output), "\n") { parts := strings.Split(line, " ") if len(parts) == 6 && parts[2] == "cgroup" { for _, opt := range strings.Split(parts[3], ",") { if opt == cgroupType { return parts[1], nil } } } } return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) } // Returns the relative path to the cgroup docker is running in. // borrowed from docker/utils/utils.go // modified to get the docker pid instead of using /proc/self func getThisCgroup(cgroupType string) (string, error) { dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") if err != nil { return "", err } result := strings.Split(string(dockerpid), "\n") if len(result) == 0 || len(result[0]) == 0 { return "", fmt.Errorf("docker pid not found in /var/run/docker.pid") } pid, err := strconv.Atoi(result[0]) if err != nil { return "", err } output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) if err != nil { return "", err } for _, line := range strings.Split(string(output), "\n") { parts := strings.Split(line, ":") // any type used by docker should work if parts[1] == cgroupType { return parts[2], nil } } return "", fmt.Errorf("cgroup '%s' not found in /proc/%d/cgroup", cgroupType, pid) } // Returns the first pid in a container. // borrowed from docker/utils/utils.go // modified to only return the first pid // modified to glob with id // modified to search for newer docker containers func getPidForContainer(id string) (int, error) { pid := 0 // memory is chosen randomly, any cgroup used by docker works cgroupType := "memory" cgroupRoot, err := findCgroupMountpoint(cgroupType) if err != nil { return pid, err } cgroupThis, err := getThisCgroup(cgroupType) if err != nil { return pid, err } id += "*" attempts := []string{ filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), // With more recent lxc versions use, cgroup will be in lxc/ filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), // With more recent docker, cgroup will be in docker/ filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), // Even more recent docker versions under systemd use docker-.scope/ filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", "tasks"), // Even more recent docker versions under cgroup/systemd/docker// filepath.Join(cgroupRoot, "..", "systemd", "docker", id, "tasks"), // Kubernetes with docker and CNI is even more different filepath.Join(cgroupRoot, "..", "systemd", "kubepods", "*", "pod*", id, "tasks"), // Another flavor of containers location in recent kubernetes 1.11+ filepath.Join(cgroupRoot, cgroupThis, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"), // When runs inside of a container with recent kubernetes 1.11+ filepath.Join(cgroupRoot, "kubepods.slice", "kubepods-besteffort.slice", "*", "docker-"+id+".scope", "tasks"), } var filename string for _, attempt := range attempts { filenames, _ := filepath.Glob(attempt) if len(filenames) > 1 { return pid, fmt.Errorf("Ambiguous id supplied: %v", filenames) } else if len(filenames) == 1 { filename = filenames[0] break } } if filename == "" { return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) } output, err := ioutil.ReadFile(filename) if err != nil { return pid, err } result := strings.Split(string(output), "\n") if len(result) == 0 || len(result[0]) == 0 { return pid, fmt.Errorf("No pid found for container") } pid, err = strconv.Atoi(result[0]) if err != nil { return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) } return pid, nil } ================================================ FILE: vendor/github.com/vishvananda/netns/netns_unspecified.go ================================================ // +build !linux package netns import ( "errors" ) var ( ErrNotImplemented = errors.New("not implemented") ) func Set(ns NsHandle) (err error) { return ErrNotImplemented } func New() (ns NsHandle, err error) { return -1, ErrNotImplemented } func Get() (NsHandle, error) { return -1, ErrNotImplemented } func GetFromPath(path string) (NsHandle, error) { return -1, ErrNotImplemented } func GetFromName(name string) (NsHandle, error) { return -1, ErrNotImplemented } func GetFromPid(pid int) (NsHandle, error) { return -1, ErrNotImplemented } func GetFromThread(pid, tid int) (NsHandle, error) { return -1, ErrNotImplemented } func GetFromDocker(id string) (NsHandle, error) { return -1, ErrNotImplemented } ================================================ FILE: vendor/golang.org/x/net/AUTHORS ================================================ # 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. ================================================ FILE: vendor/golang.org/x/net/CONTRIBUTORS ================================================ # 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. ================================================ FILE: vendor/golang.org/x/net/LICENSE ================================================ Copyright (c) 2009 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. ================================================ FILE: vendor/golang.org/x/net/PATENTS ================================================ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google 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, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ================================================ FILE: vendor/golang.org/x/net/html/atom/atom.go ================================================ // 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. // Package atom provides integer codes (also known as atoms) for a fixed set of // frequently occurring HTML strings: tag names and attribute keys such as "p" // and "id". // // Sharing an atom's name between all elements with the same tag can result in // fewer string allocations when tokenizing and parsing HTML. Integer // comparisons are also generally faster than string comparisons. // // The value of an atom's particular code is not guaranteed to stay the same // between versions of this package. Neither is any ordering guaranteed: // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to // be dense. The only guarantees are that e.g. looking up "div" will yield // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. package atom // import "golang.org/x/net/html/atom" // Atom is an integer code for a string. The zero value maps to "". type Atom uint32 // String returns the atom's name. func (a Atom) String() string { start := uint32(a >> 8) n := uint32(a & 0xff) if start+n > uint32(len(atomText)) { return "" } return atomText[start : start+n] } func (a Atom) string() string { return atomText[a>>8 : a>>8+a&0xff] } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s []byte) uint32 { for i := range s { h ^= uint32(s[i]) h *= 16777619 } return h } func match(s string, t []byte) bool { for i, c := range t { if s[i] != c { return false } } return true } // Lookup returns the atom whose name is s. It returns zero if there is no // such atom. The lookup is case sensitive. func Lookup(s []byte) Atom { if len(s) == 0 || len(s) > maxAtomLen { return 0 } h := fnv(hash0, s) if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } return 0 } // String returns a string whose contents are equal to s. In that sense, it is // equivalent to string(s) but may be more efficient. func String(s []byte) string { if a := Lookup(s); a != 0 { return a.String() } return string(s) } ================================================ FILE: vendor/golang.org/x/net/html/atom/table.go ================================================ // Code generated by go generate gen.go; DO NOT EDIT. //go:generate go run gen.go package atom const ( A Atom = 0x1 Abbr Atom = 0x4 Accept Atom = 0x1a06 AcceptCharset Atom = 0x1a0e Accesskey Atom = 0x2c09 Acronym Atom = 0xaa07 Action Atom = 0x27206 Address Atom = 0x6f307 Align Atom = 0xb105 Allowfullscreen Atom = 0x2080f Allowpaymentrequest Atom = 0xc113 Allowusermedia Atom = 0xdd0e Alt Atom = 0xf303 Annotation Atom = 0x1c90a AnnotationXml Atom = 0x1c90e Applet Atom = 0x31906 Area Atom = 0x35604 Article Atom = 0x3fc07 As Atom = 0x3c02 Aside Atom = 0x10705 Async Atom = 0xff05 Audio Atom = 0x11505 Autocomplete Atom = 0x2780c Autofocus Atom = 0x12109 Autoplay Atom = 0x13c08 B Atom = 0x101 Base Atom = 0x3b04 Basefont Atom = 0x3b08 Bdi Atom = 0xba03 Bdo Atom = 0x14b03 Bgsound Atom = 0x15e07 Big Atom = 0x17003 Blink Atom = 0x17305 Blockquote Atom = 0x1870a Body Atom = 0x2804 Br Atom = 0x202 Button Atom = 0x19106 Canvas Atom = 0x10306 Caption Atom = 0x23107 Center Atom = 0x22006 Challenge Atom = 0x29b09 Charset Atom = 0x2107 Checked Atom = 0x47907 Cite Atom = 0x19c04 Class Atom = 0x56405 Code Atom = 0x5c504 Col Atom = 0x1ab03 Colgroup Atom = 0x1ab08 Color Atom = 0x1bf05 Cols Atom = 0x1c404 Colspan Atom = 0x1c407 Command Atom = 0x1d707 Content Atom = 0x58b07 Contenteditable Atom = 0x58b0f Contextmenu Atom = 0x3800b Controls Atom = 0x1de08 Coords Atom = 0x1ea06 Crossorigin Atom = 0x1fb0b Data Atom = 0x4a504 Datalist Atom = 0x4a508 Datetime Atom = 0x2b808 Dd Atom = 0x2d702 Default Atom = 0x10a07 Defer Atom = 0x5c705 Del Atom = 0x45203 Desc Atom = 0x56104 Details Atom = 0x7207 Dfn Atom = 0x8703 Dialog Atom = 0xbb06 Dir Atom = 0x9303 Dirname Atom = 0x9307 Disabled Atom = 0x16408 Div Atom = 0x16b03 Dl Atom = 0x5e602 Download Atom = 0x46308 Draggable Atom = 0x17a09 Dropzone Atom = 0x40508 Dt Atom = 0x64b02 Em Atom = 0x6e02 Embed Atom = 0x6e05 Enctype Atom = 0x28d07 Face Atom = 0x21e04 Fieldset Atom = 0x22608 Figcaption Atom = 0x22e0a Figure Atom = 0x24806 Font Atom = 0x3f04 Footer Atom = 0xf606 For Atom = 0x25403 ForeignObject Atom = 0x2540d Foreignobject Atom = 0x2610d Form Atom = 0x26e04 Formaction Atom = 0x26e0a Formenctype Atom = 0x2890b Formmethod Atom = 0x2a40a Formnovalidate Atom = 0x2ae0e Formtarget Atom = 0x2c00a Frame Atom = 0x8b05 Frameset Atom = 0x8b08 H1 Atom = 0x15c02 H2 Atom = 0x2de02 H3 Atom = 0x30d02 H4 Atom = 0x34502 H5 Atom = 0x34f02 H6 Atom = 0x64d02 Head Atom = 0x33104 Header Atom = 0x33106 Headers Atom = 0x33107 Height Atom = 0x5206 Hgroup Atom = 0x2ca06 Hidden Atom = 0x2d506 High Atom = 0x2db04 Hr Atom = 0x15702 Href Atom = 0x2e004 Hreflang Atom = 0x2e008 Html Atom = 0x5604 HttpEquiv Atom = 0x2e80a I Atom = 0x601 Icon Atom = 0x58a04 Id Atom = 0x10902 Iframe Atom = 0x2fc06 Image Atom = 0x30205 Img Atom = 0x30703 Input Atom = 0x44b05 Inputmode Atom = 0x44b09 Ins Atom = 0x20403 Integrity Atom = 0x23f09 Is Atom = 0x16502 Isindex Atom = 0x30f07 Ismap Atom = 0x31605 Itemid Atom = 0x38b06 Itemprop Atom = 0x19d08 Itemref Atom = 0x3cd07 Itemscope Atom = 0x67109 Itemtype Atom = 0x31f08 Kbd Atom = 0xb903 Keygen Atom = 0x3206 Keytype Atom = 0xd607 Kind Atom = 0x17704 Label Atom = 0x5905 Lang Atom = 0x2e404 Legend Atom = 0x18106 Li Atom = 0xb202 Link Atom = 0x17404 List Atom = 0x4a904 Listing Atom = 0x4a907 Loop Atom = 0x5d04 Low Atom = 0xc303 Main Atom = 0x1004 Malignmark Atom = 0xb00a Manifest Atom = 0x6d708 Map Atom = 0x31803 Mark Atom = 0xb604 Marquee Atom = 0x32707 Math Atom = 0x32e04 Max Atom = 0x33d03 Maxlength Atom = 0x33d09 Media Atom = 0xe605 Mediagroup Atom = 0xe60a Menu Atom = 0x38704 Menuitem Atom = 0x38708 Meta Atom = 0x4b804 Meter Atom = 0x9805 Method Atom = 0x2a806 Mglyph Atom = 0x30806 Mi Atom = 0x34702 Min Atom = 0x34703 Minlength Atom = 0x34709 Mn Atom = 0x2b102 Mo Atom = 0xa402 Ms Atom = 0x67402 Mtext Atom = 0x35105 Multiple Atom = 0x35f08 Muted Atom = 0x36705 Name Atom = 0x9604 Nav Atom = 0x1303 Nobr Atom = 0x3704 Noembed Atom = 0x6c07 Noframes Atom = 0x8908 Nomodule Atom = 0xa208 Nonce Atom = 0x1a605 Noscript Atom = 0x21608 Novalidate Atom = 0x2b20a Object Atom = 0x26806 Ol Atom = 0x13702 Onabort Atom = 0x19507 Onafterprint Atom = 0x2360c Onautocomplete Atom = 0x2760e Onautocompleteerror Atom = 0x27613 Onauxclick Atom = 0x61f0a Onbeforeprint Atom = 0x69e0d Onbeforeunload Atom = 0x6e70e Onblur Atom = 0x56d06 Oncancel Atom = 0x11908 Oncanplay Atom = 0x14d09 Oncanplaythrough Atom = 0x14d10 Onchange Atom = 0x41b08 Onclick Atom = 0x2f507 Onclose Atom = 0x36c07 Oncontextmenu Atom = 0x37e0d Oncopy Atom = 0x39106 Oncuechange Atom = 0x3970b Oncut Atom = 0x3a205 Ondblclick Atom = 0x3a70a Ondrag Atom = 0x3b106 Ondragend Atom = 0x3b109 Ondragenter Atom = 0x3ba0b Ondragexit Atom = 0x3c50a Ondragleave Atom = 0x3df0b Ondragover Atom = 0x3ea0a Ondragstart Atom = 0x3f40b Ondrop Atom = 0x40306 Ondurationchange Atom = 0x41310 Onemptied Atom = 0x40a09 Onended Atom = 0x42307 Onerror Atom = 0x42a07 Onfocus Atom = 0x43107 Onhashchange Atom = 0x43d0c Oninput Atom = 0x44907 Oninvalid Atom = 0x45509 Onkeydown Atom = 0x45e09 Onkeypress Atom = 0x46b0a Onkeyup Atom = 0x48007 Onlanguagechange Atom = 0x48d10 Onload Atom = 0x49d06 Onloadeddata Atom = 0x49d0c Onloadedmetadata Atom = 0x4b010 Onloadend Atom = 0x4c609 Onloadstart Atom = 0x4cf0b Onmessage Atom = 0x4da09 Onmessageerror Atom = 0x4da0e Onmousedown Atom = 0x4e80b Onmouseenter Atom = 0x4f30c Onmouseleave Atom = 0x4ff0c Onmousemove Atom = 0x50b0b Onmouseout Atom = 0x5160a Onmouseover Atom = 0x5230b Onmouseup Atom = 0x52e09 Onmousewheel Atom = 0x53c0c Onoffline Atom = 0x54809 Ononline Atom = 0x55108 Onpagehide Atom = 0x5590a Onpageshow Atom = 0x5730a Onpaste Atom = 0x57f07 Onpause Atom = 0x59a07 Onplay Atom = 0x5a406 Onplaying Atom = 0x5a409 Onpopstate Atom = 0x5ad0a Onprogress Atom = 0x5b70a Onratechange Atom = 0x5cc0c Onrejectionhandled Atom = 0x5d812 Onreset Atom = 0x5ea07 Onresize Atom = 0x5f108 Onscroll Atom = 0x60008 Onsecuritypolicyviolation Atom = 0x60819 Onseeked Atom = 0x62908 Onseeking Atom = 0x63109 Onselect Atom = 0x63a08 Onshow Atom = 0x64406 Onsort Atom = 0x64f06 Onstalled Atom = 0x65909 Onstorage Atom = 0x66209 Onsubmit Atom = 0x66b08 Onsuspend Atom = 0x67b09 Ontimeupdate Atom = 0x400c Ontoggle Atom = 0x68408 Onunhandledrejection Atom = 0x68c14 Onunload Atom = 0x6ab08 Onvolumechange Atom = 0x6b30e Onwaiting Atom = 0x6c109 Onwheel Atom = 0x6ca07 Open Atom = 0x1a304 Optgroup Atom = 0x5f08 Optimum Atom = 0x6d107 Option Atom = 0x6e306 Output Atom = 0x51d06 P Atom = 0xc01 Param Atom = 0xc05 Pattern Atom = 0x6607 Picture Atom = 0x7b07 Ping Atom = 0xef04 Placeholder Atom = 0x1310b Plaintext Atom = 0x1b209 Playsinline Atom = 0x1400b Poster Atom = 0x2cf06 Pre Atom = 0x47003 Preload Atom = 0x48607 Progress Atom = 0x5b908 Prompt Atom = 0x53606 Public Atom = 0x58606 Q Atom = 0xcf01 Radiogroup Atom = 0x30a Rb Atom = 0x3a02 Readonly Atom = 0x35708 Referrerpolicy Atom = 0x3d10e Rel Atom = 0x48703 Required Atom = 0x24c08 Reversed Atom = 0x8008 Rows Atom = 0x9c04 Rowspan Atom = 0x9c07 Rp Atom = 0x23c02 Rt Atom = 0x19a02 Rtc Atom = 0x19a03 Ruby Atom = 0xfb04 S Atom = 0x2501 Samp Atom = 0x7804 Sandbox Atom = 0x12907 Scope Atom = 0x67505 Scoped Atom = 0x67506 Script Atom = 0x21806 Seamless Atom = 0x37108 Section Atom = 0x56807 Select Atom = 0x63c06 Selected Atom = 0x63c08 Shape Atom = 0x1e505 Size Atom = 0x5f504 Sizes Atom = 0x5f505 Slot Atom = 0x1ef04 Small Atom = 0x20605 Sortable Atom = 0x65108 Sorted Atom = 0x33706 Source Atom = 0x37806 Spacer Atom = 0x43706 Span Atom = 0x9f04 Spellcheck Atom = 0x4740a Src Atom = 0x5c003 Srcdoc Atom = 0x5c006 Srclang Atom = 0x5f907 Srcset Atom = 0x6f906 Start Atom = 0x3fa05 Step Atom = 0x58304 Strike Atom = 0xd206 Strong Atom = 0x6dd06 Style Atom = 0x6ff05 Sub Atom = 0x66d03 Summary Atom = 0x70407 Sup Atom = 0x70b03 Svg Atom = 0x70e03 System Atom = 0x71106 Tabindex Atom = 0x4be08 Table Atom = 0x59505 Target Atom = 0x2c406 Tbody Atom = 0x2705 Td Atom = 0x9202 Template Atom = 0x71408 Textarea Atom = 0x35208 Tfoot Atom = 0xf505 Th Atom = 0x15602 Thead Atom = 0x33005 Time Atom = 0x4204 Title Atom = 0x11005 Tr Atom = 0xcc02 Track Atom = 0x1ba05 Translate Atom = 0x1f209 Tt Atom = 0x6802 Type Atom = 0xd904 Typemustmatch Atom = 0x2900d U Atom = 0xb01 Ul Atom = 0xa702 Updateviacache Atom = 0x460e Usemap Atom = 0x59e06 Value Atom = 0x1505 Var Atom = 0x16d03 Video Atom = 0x2f105 Wbr Atom = 0x57c03 Width Atom = 0x64905 Workertype Atom = 0x71c0a Wrap Atom = 0x72604 Xmp Atom = 0x12f03 ) const hash0 = 0x81cdf10e const maxAtomLen = 25 var table = [1 << 9]Atom{ 0x1: 0xe60a, // mediagroup 0x2: 0x2e404, // lang 0x4: 0x2c09, // accesskey 0x5: 0x8b08, // frameset 0x7: 0x63a08, // onselect 0x8: 0x71106, // system 0xa: 0x64905, // width 0xc: 0x2890b, // formenctype 0xd: 0x13702, // ol 0xe: 0x3970b, // oncuechange 0x10: 0x14b03, // bdo 0x11: 0x11505, // audio 0x12: 0x17a09, // draggable 0x14: 0x2f105, // video 0x15: 0x2b102, // mn 0x16: 0x38704, // menu 0x17: 0x2cf06, // poster 0x19: 0xf606, // footer 0x1a: 0x2a806, // method 0x1b: 0x2b808, // datetime 0x1c: 0x19507, // onabort 0x1d: 0x460e, // updateviacache 0x1e: 0xff05, // async 0x1f: 0x49d06, // onload 0x21: 0x11908, // oncancel 0x22: 0x62908, // onseeked 0x23: 0x30205, // image 0x24: 0x5d812, // onrejectionhandled 0x26: 0x17404, // link 0x27: 0x51d06, // output 0x28: 0x33104, // head 0x29: 0x4ff0c, // onmouseleave 0x2a: 0x57f07, // onpaste 0x2b: 0x5a409, // onplaying 0x2c: 0x1c407, // colspan 0x2f: 0x1bf05, // color 0x30: 0x5f504, // size 0x31: 0x2e80a, // http-equiv 0x33: 0x601, // i 0x34: 0x5590a, // onpagehide 0x35: 0x68c14, // onunhandledrejection 0x37: 0x42a07, // onerror 0x3a: 0x3b08, // basefont 0x3f: 0x1303, // nav 0x40: 0x17704, // kind 0x41: 0x35708, // readonly 0x42: 0x30806, // mglyph 0x44: 0xb202, // li 0x46: 0x2d506, // hidden 0x47: 0x70e03, // svg 0x48: 0x58304, // step 0x49: 0x23f09, // integrity 0x4a: 0x58606, // public 0x4c: 0x1ab03, // col 0x4d: 0x1870a, // blockquote 0x4e: 0x34f02, // h5 0x50: 0x5b908, // progress 0x51: 0x5f505, // sizes 0x52: 0x34502, // h4 0x56: 0x33005, // thead 0x57: 0xd607, // keytype 0x58: 0x5b70a, // onprogress 0x59: 0x44b09, // inputmode 0x5a: 0x3b109, // ondragend 0x5d: 0x3a205, // oncut 0x5e: 0x43706, // spacer 0x5f: 0x1ab08, // colgroup 0x62: 0x16502, // is 0x65: 0x3c02, // as 0x66: 0x54809, // onoffline 0x67: 0x33706, // sorted 0x69: 0x48d10, // onlanguagechange 0x6c: 0x43d0c, // onhashchange 0x6d: 0x9604, // name 0x6e: 0xf505, // tfoot 0x6f: 0x56104, // desc 0x70: 0x33d03, // max 0x72: 0x1ea06, // coords 0x73: 0x30d02, // h3 0x74: 0x6e70e, // onbeforeunload 0x75: 0x9c04, // rows 0x76: 0x63c06, // select 0x77: 0x9805, // meter 0x78: 0x38b06, // itemid 0x79: 0x53c0c, // onmousewheel 0x7a: 0x5c006, // srcdoc 0x7d: 0x1ba05, // track 0x7f: 0x31f08, // itemtype 0x82: 0xa402, // mo 0x83: 0x41b08, // onchange 0x84: 0x33107, // headers 0x85: 0x5cc0c, // onratechange 0x86: 0x60819, // onsecuritypolicyviolation 0x88: 0x4a508, // datalist 0x89: 0x4e80b, // onmousedown 0x8a: 0x1ef04, // slot 0x8b: 0x4b010, // onloadedmetadata 0x8c: 0x1a06, // accept 0x8d: 0x26806, // object 0x91: 0x6b30e, // onvolumechange 0x92: 0x2107, // charset 0x93: 0x27613, // onautocompleteerror 0x94: 0xc113, // allowpaymentrequest 0x95: 0x2804, // body 0x96: 0x10a07, // default 0x97: 0x63c08, // selected 0x98: 0x21e04, // face 0x99: 0x1e505, // shape 0x9b: 0x68408, // ontoggle 0x9e: 0x64b02, // dt 0x9f: 0xb604, // mark 0xa1: 0xb01, // u 0xa4: 0x6ab08, // onunload 0xa5: 0x5d04, // loop 0xa6: 0x16408, // disabled 0xaa: 0x42307, // onended 0xab: 0xb00a, // malignmark 0xad: 0x67b09, // onsuspend 0xae: 0x35105, // mtext 0xaf: 0x64f06, // onsort 0xb0: 0x19d08, // itemprop 0xb3: 0x67109, // itemscope 0xb4: 0x17305, // blink 0xb6: 0x3b106, // ondrag 0xb7: 0xa702, // ul 0xb8: 0x26e04, // form 0xb9: 0x12907, // sandbox 0xba: 0x8b05, // frame 0xbb: 0x1505, // value 0xbc: 0x66209, // onstorage 0xbf: 0xaa07, // acronym 0xc0: 0x19a02, // rt 0xc2: 0x202, // br 0xc3: 0x22608, // fieldset 0xc4: 0x2900d, // typemustmatch 0xc5: 0xa208, // nomodule 0xc6: 0x6c07, // noembed 0xc7: 0x69e0d, // onbeforeprint 0xc8: 0x19106, // button 0xc9: 0x2f507, // onclick 0xca: 0x70407, // summary 0xcd: 0xfb04, // ruby 0xce: 0x56405, // class 0xcf: 0x3f40b, // ondragstart 0xd0: 0x23107, // caption 0xd4: 0xdd0e, // allowusermedia 0xd5: 0x4cf0b, // onloadstart 0xd9: 0x16b03, // div 0xda: 0x4a904, // list 0xdb: 0x32e04, // math 0xdc: 0x44b05, // input 0xdf: 0x3ea0a, // ondragover 0xe0: 0x2de02, // h2 0xe2: 0x1b209, // plaintext 0xe4: 0x4f30c, // onmouseenter 0xe7: 0x47907, // checked 0xe8: 0x47003, // pre 0xea: 0x35f08, // multiple 0xeb: 0xba03, // bdi 0xec: 0x33d09, // maxlength 0xed: 0xcf01, // q 0xee: 0x61f0a, // onauxclick 0xf0: 0x57c03, // wbr 0xf2: 0x3b04, // base 0xf3: 0x6e306, // option 0xf5: 0x41310, // ondurationchange 0xf7: 0x8908, // noframes 0xf9: 0x40508, // dropzone 0xfb: 0x67505, // scope 0xfc: 0x8008, // reversed 0xfd: 0x3ba0b, // ondragenter 0xfe: 0x3fa05, // start 0xff: 0x12f03, // xmp 0x100: 0x5f907, // srclang 0x101: 0x30703, // img 0x104: 0x101, // b 0x105: 0x25403, // for 0x106: 0x10705, // aside 0x107: 0x44907, // oninput 0x108: 0x35604, // area 0x109: 0x2a40a, // formmethod 0x10a: 0x72604, // wrap 0x10c: 0x23c02, // rp 0x10d: 0x46b0a, // onkeypress 0x10e: 0x6802, // tt 0x110: 0x34702, // mi 0x111: 0x36705, // muted 0x112: 0xf303, // alt 0x113: 0x5c504, // code 0x114: 0x6e02, // em 0x115: 0x3c50a, // ondragexit 0x117: 0x9f04, // span 0x119: 0x6d708, // manifest 0x11a: 0x38708, // menuitem 0x11b: 0x58b07, // content 0x11d: 0x6c109, // onwaiting 0x11f: 0x4c609, // onloadend 0x121: 0x37e0d, // oncontextmenu 0x123: 0x56d06, // onblur 0x124: 0x3fc07, // article 0x125: 0x9303, // dir 0x126: 0xef04, // ping 0x127: 0x24c08, // required 0x128: 0x45509, // oninvalid 0x129: 0xb105, // align 0x12b: 0x58a04, // icon 0x12c: 0x64d02, // h6 0x12d: 0x1c404, // cols 0x12e: 0x22e0a, // figcaption 0x12f: 0x45e09, // onkeydown 0x130: 0x66b08, // onsubmit 0x131: 0x14d09, // oncanplay 0x132: 0x70b03, // sup 0x133: 0xc01, // p 0x135: 0x40a09, // onemptied 0x136: 0x39106, // oncopy 0x137: 0x19c04, // cite 0x138: 0x3a70a, // ondblclick 0x13a: 0x50b0b, // onmousemove 0x13c: 0x66d03, // sub 0x13d: 0x48703, // rel 0x13e: 0x5f08, // optgroup 0x142: 0x9c07, // rowspan 0x143: 0x37806, // source 0x144: 0x21608, // noscript 0x145: 0x1a304, // open 0x146: 0x20403, // ins 0x147: 0x2540d, // foreignObject 0x148: 0x5ad0a, // onpopstate 0x14a: 0x28d07, // enctype 0x14b: 0x2760e, // onautocomplete 0x14c: 0x35208, // textarea 0x14e: 0x2780c, // autocomplete 0x14f: 0x15702, // hr 0x150: 0x1de08, // controls 0x151: 0x10902, // id 0x153: 0x2360c, // onafterprint 0x155: 0x2610d, // foreignobject 0x156: 0x32707, // marquee 0x157: 0x59a07, // onpause 0x158: 0x5e602, // dl 0x159: 0x5206, // height 0x15a: 0x34703, // min 0x15b: 0x9307, // dirname 0x15c: 0x1f209, // translate 0x15d: 0x5604, // html 0x15e: 0x34709, // minlength 0x15f: 0x48607, // preload 0x160: 0x71408, // template 0x161: 0x3df0b, // ondragleave 0x162: 0x3a02, // rb 0x164: 0x5c003, // src 0x165: 0x6dd06, // strong 0x167: 0x7804, // samp 0x168: 0x6f307, // address 0x169: 0x55108, // ononline 0x16b: 0x1310b, // placeholder 0x16c: 0x2c406, // target 0x16d: 0x20605, // small 0x16e: 0x6ca07, // onwheel 0x16f: 0x1c90a, // annotation 0x170: 0x4740a, // spellcheck 0x171: 0x7207, // details 0x172: 0x10306, // canvas 0x173: 0x12109, // autofocus 0x174: 0xc05, // param 0x176: 0x46308, // download 0x177: 0x45203, // del 0x178: 0x36c07, // onclose 0x179: 0xb903, // kbd 0x17a: 0x31906, // applet 0x17b: 0x2e004, // href 0x17c: 0x5f108, // onresize 0x17e: 0x49d0c, // onloadeddata 0x180: 0xcc02, // tr 0x181: 0x2c00a, // formtarget 0x182: 0x11005, // title 0x183: 0x6ff05, // style 0x184: 0xd206, // strike 0x185: 0x59e06, // usemap 0x186: 0x2fc06, // iframe 0x187: 0x1004, // main 0x189: 0x7b07, // picture 0x18c: 0x31605, // ismap 0x18e: 0x4a504, // data 0x18f: 0x5905, // label 0x191: 0x3d10e, // referrerpolicy 0x192: 0x15602, // th 0x194: 0x53606, // prompt 0x195: 0x56807, // section 0x197: 0x6d107, // optimum 0x198: 0x2db04, // high 0x199: 0x15c02, // h1 0x19a: 0x65909, // onstalled 0x19b: 0x16d03, // var 0x19c: 0x4204, // time 0x19e: 0x67402, // ms 0x19f: 0x33106, // header 0x1a0: 0x4da09, // onmessage 0x1a1: 0x1a605, // nonce 0x1a2: 0x26e0a, // formaction 0x1a3: 0x22006, // center 0x1a4: 0x3704, // nobr 0x1a5: 0x59505, // table 0x1a6: 0x4a907, // listing 0x1a7: 0x18106, // legend 0x1a9: 0x29b09, // challenge 0x1aa: 0x24806, // figure 0x1ab: 0xe605, // media 0x1ae: 0xd904, // type 0x1af: 0x3f04, // font 0x1b0: 0x4da0e, // onmessageerror 0x1b1: 0x37108, // seamless 0x1b2: 0x8703, // dfn 0x1b3: 0x5c705, // defer 0x1b4: 0xc303, // low 0x1b5: 0x19a03, // rtc 0x1b6: 0x5230b, // onmouseover 0x1b7: 0x2b20a, // novalidate 0x1b8: 0x71c0a, // workertype 0x1ba: 0x3cd07, // itemref 0x1bd: 0x1, // a 0x1be: 0x31803, // map 0x1bf: 0x400c, // ontimeupdate 0x1c0: 0x15e07, // bgsound 0x1c1: 0x3206, // keygen 0x1c2: 0x2705, // tbody 0x1c5: 0x64406, // onshow 0x1c7: 0x2501, // s 0x1c8: 0x6607, // pattern 0x1cc: 0x14d10, // oncanplaythrough 0x1ce: 0x2d702, // dd 0x1cf: 0x6f906, // srcset 0x1d0: 0x17003, // big 0x1d2: 0x65108, // sortable 0x1d3: 0x48007, // onkeyup 0x1d5: 0x5a406, // onplay 0x1d7: 0x4b804, // meta 0x1d8: 0x40306, // ondrop 0x1da: 0x60008, // onscroll 0x1db: 0x1fb0b, // crossorigin 0x1dc: 0x5730a, // onpageshow 0x1dd: 0x4, // abbr 0x1de: 0x9202, // td 0x1df: 0x58b0f, // contenteditable 0x1e0: 0x27206, // action 0x1e1: 0x1400b, // playsinline 0x1e2: 0x43107, // onfocus 0x1e3: 0x2e008, // hreflang 0x1e5: 0x5160a, // onmouseout 0x1e6: 0x5ea07, // onreset 0x1e7: 0x13c08, // autoplay 0x1e8: 0x63109, // onseeking 0x1ea: 0x67506, // scoped 0x1ec: 0x30a, // radiogroup 0x1ee: 0x3800b, // contextmenu 0x1ef: 0x52e09, // onmouseup 0x1f1: 0x2ca06, // hgroup 0x1f2: 0x2080f, // allowfullscreen 0x1f3: 0x4be08, // tabindex 0x1f6: 0x30f07, // isindex 0x1f7: 0x1a0e, // accept-charset 0x1f8: 0x2ae0e, // formnovalidate 0x1fb: 0x1c90e, // annotation-xml 0x1fc: 0x6e05, // embed 0x1fd: 0x21806, // script 0x1fe: 0xbb06, // dialog 0x1ff: 0x1d707, // command } const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" + "asefontimeupdateviacacheightmlabelooptgroupatternoembedetail" + "sampictureversedfnoframesetdirnameterowspanomoduleacronymali" + "gnmarkbdialogallowpaymentrequestrikeytypeallowusermediagroup" + "ingaltfooterubyasyncanvasidefaultitleaudioncancelautofocusan" + "dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" + "bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" + "penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" + "ntrolshapecoordslotranslatecrossoriginsmallowfullscreenoscri" + "ptfacenterfieldsetfigcaptionafterprintegrityfigurequiredfore" + "ignObjectforeignobjectformactionautocompleteerrorformenctype" + "mustmatchallengeformmethodformnovalidatetimeformtargethgroup" + "osterhiddenhigh2hreflanghttp-equivideonclickiframeimageimgly" + "ph3isindexismappletitemtypemarqueematheadersortedmaxlength4m" + "inlength5mtextareadonlymultiplemutedoncloseamlessourceoncont" + "extmenuitemidoncopyoncuechangeoncutondblclickondragendondrag" + "enterondragexitemreferrerpolicyondragleaveondragoverondragst" + "articleondropzonemptiedondurationchangeonendedonerroronfocus" + "paceronhashchangeoninputmodeloninvalidonkeydownloadonkeypres" + "spellcheckedonkeyupreloadonlanguagechangeonloadeddatalisting" + "onloadedmetadatabindexonloadendonloadstartonmessageerroronmo" + "usedownonmouseenteronmouseleaveonmousemoveonmouseoutputonmou" + "seoveronmouseupromptonmousewheelonofflineononlineonpagehides" + "classectionbluronpageshowbronpastepublicontenteditableonpaus" + "emaponplayingonpopstateonprogressrcdocodeferonratechangeonre" + "jectionhandledonresetonresizesrclangonscrollonsecuritypolicy" + "violationauxclickonseekedonseekingonselectedonshowidth6onsor" + "tableonstalledonstorageonsubmitemscopedonsuspendontoggleonun" + "handledrejectionbeforeprintonunloadonvolumechangeonwaitingon" + "wheeloptimumanifestrongoptionbeforeunloaddressrcsetstylesumm" + "arysupsvgsystemplateworkertypewrap" ================================================ FILE: vendor/golang.org/x/net/html/charset/charset.go ================================================ // 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. // Package charset provides common text encodings for HTML documents. // // The mapping from encoding labels to encodings is defined at // https://encoding.spec.whatwg.org/. package charset // import "golang.org/x/net/html/charset" import ( "bytes" "fmt" "io" "mime" "strings" "unicode/utf8" "golang.org/x/net/html" "golang.org/x/text/encoding" "golang.org/x/text/encoding/charmap" "golang.org/x/text/encoding/htmlindex" "golang.org/x/text/transform" ) // Lookup returns the encoding with the specified label, and its canonical // name. It returns nil and the empty string if label is not one of the // standard encodings for HTML. Matching is case-insensitive and ignores // leading and trailing whitespace. Encoders will use HTML escape sequences for // runes that are not supported by the character set. func Lookup(label string) (e encoding.Encoding, name string) { e, err := htmlindex.Get(label) if err != nil { return nil, "" } name, _ = htmlindex.Name(e) return &htmlEncoding{e}, name } type htmlEncoding struct{ encoding.Encoding } func (h *htmlEncoding) NewEncoder() *encoding.Encoder { // HTML requires a non-terminating legacy encoder. We use HTML escapes to // substitute unsupported code points. return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder()) } // DetermineEncoding determines the encoding of an HTML document by examining // up to the first 1024 bytes of content and the declared Content-Type. // // See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) { if len(content) > 1024 { content = content[:1024] } for _, b := range boms { if bytes.HasPrefix(content, b.bom) { e, name = Lookup(b.enc) return e, name, true } } if _, params, err := mime.ParseMediaType(contentType); err == nil { if cs, ok := params["charset"]; ok { if e, name = Lookup(cs); e != nil { return e, name, true } } } if len(content) > 0 { e, name = prescan(content) if e != nil { return e, name, false } } // Try to detect UTF-8. // First eliminate any partial rune at the end. for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- { b := content[i] if b < 0x80 { break } if utf8.RuneStart(b) { content = content[:i] break } } hasHighBit := false for _, c := range content { if c >= 0x80 { hasHighBit = true break } } if hasHighBit && utf8.Valid(content) { return encoding.Nop, "utf-8", false } // TODO: change default depending on user's locale? return charmap.Windows1252, "windows-1252", false } // NewReader returns an io.Reader that converts the content of r to UTF-8. // It calls DetermineEncoding to find out what r's encoding is. func NewReader(r io.Reader, contentType string) (io.Reader, error) { preview := make([]byte, 1024) n, err := io.ReadFull(r, preview) switch { case err == io.ErrUnexpectedEOF: preview = preview[:n] r = bytes.NewReader(preview) case err != nil: return nil, err default: r = io.MultiReader(bytes.NewReader(preview), r) } if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop { r = transform.NewReader(r, e.NewDecoder()) } return r, nil } // NewReaderLabel returns a reader that converts from the specified charset to // UTF-8. It uses Lookup to find the encoding that corresponds to label, and // returns an error if Lookup returns nil. It is suitable for use as // encoding/xml.Decoder's CharsetReader function. func NewReaderLabel(label string, input io.Reader) (io.Reader, error) { e, _ := Lookup(label) if e == nil { return nil, fmt.Errorf("unsupported charset: %q", label) } return transform.NewReader(input, e.NewDecoder()), nil } func prescan(content []byte) (e encoding.Encoding, name string) { z := html.NewTokenizer(bytes.NewReader(content)) for { switch z.Next() { case html.ErrorToken: return nil, "" case html.StartTagToken, html.SelfClosingTagToken: tagName, hasAttr := z.TagName() if !bytes.Equal(tagName, []byte("meta")) { continue } attrList := make(map[string]bool) gotPragma := false const ( dontKnow = iota doNeedPragma doNotNeedPragma ) needPragma := dontKnow name = "" e = nil for hasAttr { var key, val []byte key, val, hasAttr = z.TagAttr() ks := string(key) if attrList[ks] { continue } attrList[ks] = true for i, c := range val { if 'A' <= c && c <= 'Z' { val[i] = c + 0x20 } } switch ks { case "http-equiv": if bytes.Equal(val, []byte("content-type")) { gotPragma = true } case "content": if e == nil { name = fromMetaElement(string(val)) if name != "" { e, name = Lookup(name) if e != nil { needPragma = doNeedPragma } } } case "charset": e, name = Lookup(string(val)) needPragma = doNotNeedPragma } } if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma { continue } if strings.HasPrefix(name, "utf-16") { name = "utf-8" e = encoding.Nop } if e != nil { return e, name } } } } func fromMetaElement(s string) string { for s != "" { csLoc := strings.Index(s, "charset") if csLoc == -1 { return "" } s = s[csLoc+len("charset"):] s = strings.TrimLeft(s, " \t\n\f\r") if !strings.HasPrefix(s, "=") { continue } s = s[1:] s = strings.TrimLeft(s, " \t\n\f\r") if s == "" { return "" } if q := s[0]; q == '"' || q == '\'' { s = s[1:] closeQuote := strings.IndexRune(s, rune(q)) if closeQuote == -1 { return "" } return s[:closeQuote] } end := strings.IndexAny(s, "; \t\n\f\r") if end == -1 { end = len(s) } return s[:end] } return "" } var boms = []struct { bom []byte enc string }{ {[]byte{0xfe, 0xff}, "utf-16be"}, {[]byte{0xff, 0xfe}, "utf-16le"}, {[]byte{0xef, 0xbb, 0xbf}, "utf-8"}, } ================================================ FILE: vendor/golang.org/x/net/html/const.go ================================================ // 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. package html // Section 12.2.4.2 of the HTML5 specification says "The following elements // have varying levels of special parsing rules". // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements var isSpecialElementMap = map[string]bool{ "address": true, "applet": true, "area": true, "article": true, "aside": true, "base": true, "basefont": true, "bgsound": true, "blockquote": true, "body": true, "br": true, "button": true, "caption": true, "center": true, "col": true, "colgroup": true, "dd": true, "details": true, "dir": true, "div": true, "dl": true, "dt": true, "embed": true, "fieldset": true, "figcaption": true, "figure": true, "footer": true, "form": true, "frame": true, "frameset": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "header": true, "hgroup": true, "hr": true, "html": true, "iframe": true, "img": true, "input": true, "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. "li": true, "link": true, "listing": true, "main": true, "marquee": true, "menu": true, "meta": true, "nav": true, "noembed": true, "noframes": true, "noscript": true, "object": true, "ol": true, "p": true, "param": true, "plaintext": true, "pre": true, "script": true, "section": true, "select": true, "source": true, "style": true, "summary": true, "table": true, "tbody": true, "td": true, "template": true, "textarea": true, "tfoot": true, "th": true, "thead": true, "title": true, "tr": true, "track": true, "ul": true, "wbr": true, "xmp": true, } func isSpecialElement(element *Node) bool { switch element.Namespace { case "", "html": return isSpecialElementMap[element.Data] case "math": switch element.Data { case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": return true } case "svg": switch element.Data { case "foreignObject", "desc", "title": return true } } return false } ================================================ FILE: vendor/golang.org/x/net/html/doc.go ================================================ // 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. /* Package html implements an HTML5-compliant tokenizer and parser. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. z := html.NewTokenizer(r) Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), which parses the next token and returns its type, or an error: for { tt := z.Next() if tt == html.ErrorToken { // ... return ... } // Process the current token. } There are two APIs for retrieving the current token. The high-level API is to call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs allow optionally calling Raw after Next but before Token, Text, TagName, or TagAttr. In EBNF notation, the valid call sequence per token is: Next {Raw} [ Token | Text | TagName {TagAttr} ] Token returns an independent data structure that completely describes a token. Entities (such as "<") are unescaped, tag names and attribute keys are lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. return z.Err() } emitToken(z.Token()) } The low-level API performs fewer allocations and copies, but the contents of the []byte values returned by Text, TagName and TagAttr may change on the next call to Next. For example, to extract an HTML page's anchor text: depth := 0 for { tt := z.Next() switch tt { case html.ErrorToken: return z.Err() case html.TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, // if it doesn't process it immediately. emitBytes(z.Text()) } case html.StartTagToken, html.EndTagToken: tn, _ := z.TagName() if len(tn) == 1 && tn[0] == 'a' { if tt == html.StartTagToken { depth++ } else { depth-- } } } } Parsing is done by calling Parse with an io.Reader, which returns the root of the parse tree (the document element) as a *Node. It is the caller's responsibility to ensure that the Reader provides UTF-8 encoded HTML. For example, to process each anchor node in depth-first order: doc, err := html.Parse(r) if err != nil { // ... } var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) The relevant specifications include: https://html.spec.whatwg.org/multipage/syntax.html and https://html.spec.whatwg.org/multipage/syntax.html#tokenization */ package html // import "golang.org/x/net/html" // The tokenization algorithm implemented by this package is not a line-by-line // transliteration of the relatively verbose state-machine in the WHATWG // specification. A more direct approach is used instead, where the program // counter implies the state, such as whether it is tokenizing a tag or a text // node. Specification compliance is verified by checking expected and actual // outputs over a test suite rather than aiming for algorithmic fidelity. // TODO(nigeltao): Does a DOM API belong in this package or a separate one? // TODO(nigeltao): How does parsing interact with a JavaScript engine? ================================================ FILE: vendor/golang.org/x/net/html/doctype.go ================================================ // 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. package html import ( "strings" ) // parseDoctype parses the data from a DoctypeToken into a name, // public identifier, and system identifier. It returns a Node whose Type // is DoctypeNode, whose Data is the name, and which has attributes // named "system" and "public" for the two identifiers if they were present. // quirks is whether the document should be parsed in "quirks mode". func parseDoctype(s string) (n *Node, quirks bool) { n = &Node{Type: DoctypeNode} // Find the name. space := strings.IndexAny(s, whitespace) if space == -1 { space = len(s) } n.Data = s[:space] // The comparison to "html" is case-sensitive. if n.Data != "html" { quirks = true } n.Data = strings.ToLower(n.Data) s = strings.TrimLeft(s[space:], whitespace) if len(s) < 6 { // It can't start with "PUBLIC" or "SYSTEM". // Ignore the rest of the string. return n, quirks || s != "" } key := strings.ToLower(s[:6]) s = s[6:] for key == "public" || key == "system" { s = strings.TrimLeft(s, whitespace) if s == "" { break } quote := s[0] if quote != '"' && quote != '\'' { break } s = s[1:] q := strings.IndexRune(s, rune(quote)) var id string if q == -1 { id = s s = "" } else { id = s[:q] s = s[q+1:] } n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) if key == "public" { key = "system" } else { key = "" } } if key != "" || s != "" { quirks = true } else if len(n.Attr) > 0 { if n.Attr[0].Key == "public" { public := strings.ToLower(n.Attr[0].Val) switch public { case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": quirks = true default: for _, q := range quirkyIDs { if strings.HasPrefix(public, q) { quirks = true break } } } // The following two public IDs only cause quirks mode if there is no system ID. if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { quirks = true } } if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { quirks = true } } return n, quirks } // quirkyIDs is a list of public doctype identifiers that cause a document // to be interpreted in quirks mode. The identifiers should be in lower case. var quirkyIDs = []string{ "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//", } ================================================ FILE: vendor/golang.org/x/net/html/entity.go ================================================ // 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. package html // All entities that do not end with ';' are 6 or fewer bytes long. const longestEntityWithoutSemicolon = 6 // entity is a map from HTML entity names to their values. The semicolon matters: // https://html.spec.whatwg.org/multipage/syntax.html#named-character-references // lists both "amp" and "amp;" as two separate entries. // // Note that the HTML5 list is larger than the HTML4 list at // http://www.w3.org/TR/html4/sgml/entities.html var entity = map[string]rune{ "AElig;": '\U000000C6', "AMP;": '\U00000026', "Aacute;": '\U000000C1', "Abreve;": '\U00000102', "Acirc;": '\U000000C2', "Acy;": '\U00000410', "Afr;": '\U0001D504', "Agrave;": '\U000000C0', "Alpha;": '\U00000391', "Amacr;": '\U00000100', "And;": '\U00002A53', "Aogon;": '\U00000104', "Aopf;": '\U0001D538', "ApplyFunction;": '\U00002061', "Aring;": '\U000000C5', "Ascr;": '\U0001D49C', "Assign;": '\U00002254', "Atilde;": '\U000000C3', "Auml;": '\U000000C4', "Backslash;": '\U00002216', "Barv;": '\U00002AE7', "Barwed;": '\U00002306', "Bcy;": '\U00000411', "Because;": '\U00002235', "Bernoullis;": '\U0000212C', "Beta;": '\U00000392', "Bfr;": '\U0001D505', "Bopf;": '\U0001D539', "Breve;": '\U000002D8', "Bscr;": '\U0000212C', "Bumpeq;": '\U0000224E', "CHcy;": '\U00000427', "COPY;": '\U000000A9', "Cacute;": '\U00000106', "Cap;": '\U000022D2', "CapitalDifferentialD;": '\U00002145', "Cayleys;": '\U0000212D', "Ccaron;": '\U0000010C', "Ccedil;": '\U000000C7', "Ccirc;": '\U00000108', "Cconint;": '\U00002230', "Cdot;": '\U0000010A', "Cedilla;": '\U000000B8', "CenterDot;": '\U000000B7', "Cfr;": '\U0000212D', "Chi;": '\U000003A7', "CircleDot;": '\U00002299', "CircleMinus;": '\U00002296', "CirclePlus;": '\U00002295', "CircleTimes;": '\U00002297', "ClockwiseContourIntegral;": '\U00002232', "CloseCurlyDoubleQuote;": '\U0000201D', "CloseCurlyQuote;": '\U00002019', "Colon;": '\U00002237', "Colone;": '\U00002A74', "Congruent;": '\U00002261', "Conint;": '\U0000222F', "ContourIntegral;": '\U0000222E', "Copf;": '\U00002102', "Coproduct;": '\U00002210', "CounterClockwiseContourIntegral;": '\U00002233', "Cross;": '\U00002A2F', "Cscr;": '\U0001D49E', "Cup;": '\U000022D3', "CupCap;": '\U0000224D', "DD;": '\U00002145', "DDotrahd;": '\U00002911', "DJcy;": '\U00000402', "DScy;": '\U00000405', "DZcy;": '\U0000040F', "Dagger;": '\U00002021', "Darr;": '\U000021A1', "Dashv;": '\U00002AE4', "Dcaron;": '\U0000010E', "Dcy;": '\U00000414', "Del;": '\U00002207', "Delta;": '\U00000394', "Dfr;": '\U0001D507', "DiacriticalAcute;": '\U000000B4', "DiacriticalDot;": '\U000002D9', "DiacriticalDoubleAcute;": '\U000002DD', "DiacriticalGrave;": '\U00000060', "DiacriticalTilde;": '\U000002DC', "Diamond;": '\U000022C4', "DifferentialD;": '\U00002146', "Dopf;": '\U0001D53B', "Dot;": '\U000000A8', "DotDot;": '\U000020DC', "DotEqual;": '\U00002250', "DoubleContourIntegral;": '\U0000222F', "DoubleDot;": '\U000000A8', "DoubleDownArrow;": '\U000021D3', "DoubleLeftArrow;": '\U000021D0', "DoubleLeftRightArrow;": '\U000021D4', "DoubleLeftTee;": '\U00002AE4', "DoubleLongLeftArrow;": '\U000027F8', "DoubleLongLeftRightArrow;": '\U000027FA', "DoubleLongRightArrow;": '\U000027F9', "DoubleRightArrow;": '\U000021D2', "DoubleRightTee;": '\U000022A8', "DoubleUpArrow;": '\U000021D1', "DoubleUpDownArrow;": '\U000021D5', "DoubleVerticalBar;": '\U00002225', "DownArrow;": '\U00002193', "DownArrowBar;": '\U00002913', "DownArrowUpArrow;": '\U000021F5', "DownBreve;": '\U00000311', "DownLeftRightVector;": '\U00002950', "DownLeftTeeVector;": '\U0000295E', "DownLeftVector;": '\U000021BD', "DownLeftVectorBar;": '\U00002956', "DownRightTeeVector;": '\U0000295F', "DownRightVector;": '\U000021C1', "DownRightVectorBar;": '\U00002957', "DownTee;": '\U000022A4', "DownTeeArrow;": '\U000021A7', "Downarrow;": '\U000021D3', "Dscr;": '\U0001D49F', "Dstrok;": '\U00000110', "ENG;": '\U0000014A', "ETH;": '\U000000D0', "Eacute;": '\U000000C9', "Ecaron;": '\U0000011A', "Ecirc;": '\U000000CA', "Ecy;": '\U0000042D', "Edot;": '\U00000116', "Efr;": '\U0001D508', "Egrave;": '\U000000C8', "Element;": '\U00002208', "Emacr;": '\U00000112', "EmptySmallSquare;": '\U000025FB', "EmptyVerySmallSquare;": '\U000025AB', "Eogon;": '\U00000118', "Eopf;": '\U0001D53C', "Epsilon;": '\U00000395', "Equal;": '\U00002A75', "EqualTilde;": '\U00002242', "Equilibrium;": '\U000021CC', "Escr;": '\U00002130', "Esim;": '\U00002A73', "Eta;": '\U00000397', "Euml;": '\U000000CB', "Exists;": '\U00002203', "ExponentialE;": '\U00002147', "Fcy;": '\U00000424', "Ffr;": '\U0001D509', "FilledSmallSquare;": '\U000025FC', "FilledVerySmallSquare;": '\U000025AA', "Fopf;": '\U0001D53D', "ForAll;": '\U00002200', "Fouriertrf;": '\U00002131', "Fscr;": '\U00002131', "GJcy;": '\U00000403', "GT;": '\U0000003E', "Gamma;": '\U00000393', "Gammad;": '\U000003DC', "Gbreve;": '\U0000011E', "Gcedil;": '\U00000122', "Gcirc;": '\U0000011C', "Gcy;": '\U00000413', "Gdot;": '\U00000120', "Gfr;": '\U0001D50A', "Gg;": '\U000022D9', "Gopf;": '\U0001D53E', "GreaterEqual;": '\U00002265', "GreaterEqualLess;": '\U000022DB', "GreaterFullEqual;": '\U00002267', "GreaterGreater;": '\U00002AA2', "GreaterLess;": '\U00002277', "GreaterSlantEqual;": '\U00002A7E', "GreaterTilde;": '\U00002273', "Gscr;": '\U0001D4A2', "Gt;": '\U0000226B', "HARDcy;": '\U0000042A', "Hacek;": '\U000002C7', "Hat;": '\U0000005E', "Hcirc;": '\U00000124', "Hfr;": '\U0000210C', "HilbertSpace;": '\U0000210B', "Hopf;": '\U0000210D', "HorizontalLine;": '\U00002500', "Hscr;": '\U0000210B', "Hstrok;": '\U00000126', "HumpDownHump;": '\U0000224E', "HumpEqual;": '\U0000224F', "IEcy;": '\U00000415', "IJlig;": '\U00000132', "IOcy;": '\U00000401', "Iacute;": '\U000000CD', "Icirc;": '\U000000CE', "Icy;": '\U00000418', "Idot;": '\U00000130', "Ifr;": '\U00002111', "Igrave;": '\U000000CC', "Im;": '\U00002111', "Imacr;": '\U0000012A', "ImaginaryI;": '\U00002148', "Implies;": '\U000021D2', "Int;": '\U0000222C', "Integral;": '\U0000222B', "Intersection;": '\U000022C2', "InvisibleComma;": '\U00002063', "InvisibleTimes;": '\U00002062', "Iogon;": '\U0000012E', "Iopf;": '\U0001D540', "Iota;": '\U00000399', "Iscr;": '\U00002110', "Itilde;": '\U00000128', "Iukcy;": '\U00000406', "Iuml;": '\U000000CF', "Jcirc;": '\U00000134', "Jcy;": '\U00000419', "Jfr;": '\U0001D50D', "Jopf;": '\U0001D541', "Jscr;": '\U0001D4A5', "Jsercy;": '\U00000408', "Jukcy;": '\U00000404', "KHcy;": '\U00000425', "KJcy;": '\U0000040C', "Kappa;": '\U0000039A', "Kcedil;": '\U00000136', "Kcy;": '\U0000041A', "Kfr;": '\U0001D50E', "Kopf;": '\U0001D542', "Kscr;": '\U0001D4A6', "LJcy;": '\U00000409', "LT;": '\U0000003C', "Lacute;": '\U00000139', "Lambda;": '\U0000039B', "Lang;": '\U000027EA', "Laplacetrf;": '\U00002112', "Larr;": '\U0000219E', "Lcaron;": '\U0000013D', "Lcedil;": '\U0000013B', "Lcy;": '\U0000041B', "LeftAngleBracket;": '\U000027E8', "LeftArrow;": '\U00002190', "LeftArrowBar;": '\U000021E4', "LeftArrowRightArrow;": '\U000021C6', "LeftCeiling;": '\U00002308', "LeftDoubleBracket;": '\U000027E6', "LeftDownTeeVector;": '\U00002961', "LeftDownVector;": '\U000021C3', "LeftDownVectorBar;": '\U00002959', "LeftFloor;": '\U0000230A', "LeftRightArrow;": '\U00002194', "LeftRightVector;": '\U0000294E', "LeftTee;": '\U000022A3', "LeftTeeArrow;": '\U000021A4', "LeftTeeVector;": '\U0000295A', "LeftTriangle;": '\U000022B2', "LeftTriangleBar;": '\U000029CF', "LeftTriangleEqual;": '\U000022B4', "LeftUpDownVector;": '\U00002951', "LeftUpTeeVector;": '\U00002960', "LeftUpVector;": '\U000021BF', "LeftUpVectorBar;": '\U00002958', "LeftVector;": '\U000021BC', "LeftVectorBar;": '\U00002952', "Leftarrow;": '\U000021D0', "Leftrightarrow;": '\U000021D4', "LessEqualGreater;": '\U000022DA', "LessFullEqual;": '\U00002266', "LessGreater;": '\U00002276', "LessLess;": '\U00002AA1', "LessSlantEqual;": '\U00002A7D', "LessTilde;": '\U00002272', "Lfr;": '\U0001D50F', "Ll;": '\U000022D8', "Lleftarrow;": '\U000021DA', "Lmidot;": '\U0000013F', "LongLeftArrow;": '\U000027F5', "LongLeftRightArrow;": '\U000027F7', "LongRightArrow;": '\U000027F6', "Longleftarrow;": '\U000027F8', "Longleftrightarrow;": '\U000027FA', "Longrightarrow;": '\U000027F9', "Lopf;": '\U0001D543', "LowerLeftArrow;": '\U00002199', "LowerRightArrow;": '\U00002198', "Lscr;": '\U00002112', "Lsh;": '\U000021B0', "Lstrok;": '\U00000141', "Lt;": '\U0000226A', "Map;": '\U00002905', "Mcy;": '\U0000041C', "MediumSpace;": '\U0000205F', "Mellintrf;": '\U00002133', "Mfr;": '\U0001D510', "MinusPlus;": '\U00002213', "Mopf;": '\U0001D544', "Mscr;": '\U00002133', "Mu;": '\U0000039C', "NJcy;": '\U0000040A', "Nacute;": '\U00000143', "Ncaron;": '\U00000147', "Ncedil;": '\U00000145', "Ncy;": '\U0000041D', "NegativeMediumSpace;": '\U0000200B', "NegativeThickSpace;": '\U0000200B', "NegativeThinSpace;": '\U0000200B', "NegativeVeryThinSpace;": '\U0000200B', "NestedGreaterGreater;": '\U0000226B', "NestedLessLess;": '\U0000226A', "NewLine;": '\U0000000A', "Nfr;": '\U0001D511', "NoBreak;": '\U00002060', "NonBreakingSpace;": '\U000000A0', "Nopf;": '\U00002115', "Not;": '\U00002AEC', "NotCongruent;": '\U00002262', "NotCupCap;": '\U0000226D', "NotDoubleVerticalBar;": '\U00002226', "NotElement;": '\U00002209', "NotEqual;": '\U00002260', "NotExists;": '\U00002204', "NotGreater;": '\U0000226F', "NotGreaterEqual;": '\U00002271', "NotGreaterLess;": '\U00002279', "NotGreaterTilde;": '\U00002275', "NotLeftTriangle;": '\U000022EA', "NotLeftTriangleEqual;": '\U000022EC', "NotLess;": '\U0000226E', "NotLessEqual;": '\U00002270', "NotLessGreater;": '\U00002278', "NotLessTilde;": '\U00002274', "NotPrecedes;": '\U00002280', "NotPrecedesSlantEqual;": '\U000022E0', "NotReverseElement;": '\U0000220C', "NotRightTriangle;": '\U000022EB', "NotRightTriangleEqual;": '\U000022ED', "NotSquareSubsetEqual;": '\U000022E2', "NotSquareSupersetEqual;": '\U000022E3', "NotSubsetEqual;": '\U00002288', "NotSucceeds;": '\U00002281', "NotSucceedsSlantEqual;": '\U000022E1', "NotSupersetEqual;": '\U00002289', "NotTilde;": '\U00002241', "NotTildeEqual;": '\U00002244', "NotTildeFullEqual;": '\U00002247', "NotTildeTilde;": '\U00002249', "NotVerticalBar;": '\U00002224', "Nscr;": '\U0001D4A9', "Ntilde;": '\U000000D1', "Nu;": '\U0000039D', "OElig;": '\U00000152', "Oacute;": '\U000000D3', "Ocirc;": '\U000000D4', "Ocy;": '\U0000041E', "Odblac;": '\U00000150', "Ofr;": '\U0001D512', "Ograve;": '\U000000D2', "Omacr;": '\U0000014C', "Omega;": '\U000003A9', "Omicron;": '\U0000039F', "Oopf;": '\U0001D546', "OpenCurlyDoubleQuote;": '\U0000201C', "OpenCurlyQuote;": '\U00002018', "Or;": '\U00002A54', "Oscr;": '\U0001D4AA', "Oslash;": '\U000000D8', "Otilde;": '\U000000D5', "Otimes;": '\U00002A37', "Ouml;": '\U000000D6', "OverBar;": '\U0000203E', "OverBrace;": '\U000023DE', "OverBracket;": '\U000023B4', "OverParenthesis;": '\U000023DC', "PartialD;": '\U00002202', "Pcy;": '\U0000041F', "Pfr;": '\U0001D513', "Phi;": '\U000003A6', "Pi;": '\U000003A0', "PlusMinus;": '\U000000B1', "Poincareplane;": '\U0000210C', "Popf;": '\U00002119', "Pr;": '\U00002ABB', "Precedes;": '\U0000227A', "PrecedesEqual;": '\U00002AAF', "PrecedesSlantEqual;": '\U0000227C', "PrecedesTilde;": '\U0000227E', "Prime;": '\U00002033', "Product;": '\U0000220F', "Proportion;": '\U00002237', "Proportional;": '\U0000221D', "Pscr;": '\U0001D4AB', "Psi;": '\U000003A8', "QUOT;": '\U00000022', "Qfr;": '\U0001D514', "Qopf;": '\U0000211A', "Qscr;": '\U0001D4AC', "RBarr;": '\U00002910', "REG;": '\U000000AE', "Racute;": '\U00000154', "Rang;": '\U000027EB', "Rarr;": '\U000021A0', "Rarrtl;": '\U00002916', "Rcaron;": '\U00000158', "Rcedil;": '\U00000156', "Rcy;": '\U00000420', "Re;": '\U0000211C', "ReverseElement;": '\U0000220B', "ReverseEquilibrium;": '\U000021CB', "ReverseUpEquilibrium;": '\U0000296F', "Rfr;": '\U0000211C', "Rho;": '\U000003A1', "RightAngleBracket;": '\U000027E9', "RightArrow;": '\U00002192', "RightArrowBar;": '\U000021E5', "RightArrowLeftArrow;": '\U000021C4', "RightCeiling;": '\U00002309', "RightDoubleBracket;": '\U000027E7', "RightDownTeeVector;": '\U0000295D', "RightDownVector;": '\U000021C2', "RightDownVectorBar;": '\U00002955', "RightFloor;": '\U0000230B', "RightTee;": '\U000022A2', "RightTeeArrow;": '\U000021A6', "RightTeeVector;": '\U0000295B', "RightTriangle;": '\U000022B3', "RightTriangleBar;": '\U000029D0', "RightTriangleEqual;": '\U000022B5', "RightUpDownVector;": '\U0000294F', "RightUpTeeVector;": '\U0000295C', "RightUpVector;": '\U000021BE', "RightUpVectorBar;": '\U00002954', "RightVector;": '\U000021C0', "RightVectorBar;": '\U00002953', "Rightarrow;": '\U000021D2', "Ropf;": '\U0000211D', "RoundImplies;": '\U00002970', "Rrightarrow;": '\U000021DB', "Rscr;": '\U0000211B', "Rsh;": '\U000021B1', "RuleDelayed;": '\U000029F4', "SHCHcy;": '\U00000429', "SHcy;": '\U00000428', "SOFTcy;": '\U0000042C', "Sacute;": '\U0000015A', "Sc;": '\U00002ABC', "Scaron;": '\U00000160', "Scedil;": '\U0000015E', "Scirc;": '\U0000015C', "Scy;": '\U00000421', "Sfr;": '\U0001D516', "ShortDownArrow;": '\U00002193', "ShortLeftArrow;": '\U00002190', "ShortRightArrow;": '\U00002192', "ShortUpArrow;": '\U00002191', "Sigma;": '\U000003A3', "SmallCircle;": '\U00002218', "Sopf;": '\U0001D54A', "Sqrt;": '\U0000221A', "Square;": '\U000025A1', "SquareIntersection;": '\U00002293', "SquareSubset;": '\U0000228F', "SquareSubsetEqual;": '\U00002291', "SquareSuperset;": '\U00002290', "SquareSupersetEqual;": '\U00002292', "SquareUnion;": '\U00002294', "Sscr;": '\U0001D4AE', "Star;": '\U000022C6', "Sub;": '\U000022D0', "Subset;": '\U000022D0', "SubsetEqual;": '\U00002286', "Succeeds;": '\U0000227B', "SucceedsEqual;": '\U00002AB0', "SucceedsSlantEqual;": '\U0000227D', "SucceedsTilde;": '\U0000227F', "SuchThat;": '\U0000220B', "Sum;": '\U00002211', "Sup;": '\U000022D1', "Superset;": '\U00002283', "SupersetEqual;": '\U00002287', "Supset;": '\U000022D1', "THORN;": '\U000000DE', "TRADE;": '\U00002122', "TSHcy;": '\U0000040B', "TScy;": '\U00000426', "Tab;": '\U00000009', "Tau;": '\U000003A4', "Tcaron;": '\U00000164', "Tcedil;": '\U00000162', "Tcy;": '\U00000422', "Tfr;": '\U0001D517', "Therefore;": '\U00002234', "Theta;": '\U00000398', "ThinSpace;": '\U00002009', "Tilde;": '\U0000223C', "TildeEqual;": '\U00002243', "TildeFullEqual;": '\U00002245', "TildeTilde;": '\U00002248', "Topf;": '\U0001D54B', "TripleDot;": '\U000020DB', "Tscr;": '\U0001D4AF', "Tstrok;": '\U00000166', "Uacute;": '\U000000DA', "Uarr;": '\U0000219F', "Uarrocir;": '\U00002949', "Ubrcy;": '\U0000040E', "Ubreve;": '\U0000016C', "Ucirc;": '\U000000DB', "Ucy;": '\U00000423', "Udblac;": '\U00000170', "Ufr;": '\U0001D518', "Ugrave;": '\U000000D9', "Umacr;": '\U0000016A', "UnderBar;": '\U0000005F', "UnderBrace;": '\U000023DF', "UnderBracket;": '\U000023B5', "UnderParenthesis;": '\U000023DD', "Union;": '\U000022C3', "UnionPlus;": '\U0000228E', "Uogon;": '\U00000172', "Uopf;": '\U0001D54C', "UpArrow;": '\U00002191', "UpArrowBar;": '\U00002912', "UpArrowDownArrow;": '\U000021C5', "UpDownArrow;": '\U00002195', "UpEquilibrium;": '\U0000296E', "UpTee;": '\U000022A5', "UpTeeArrow;": '\U000021A5', "Uparrow;": '\U000021D1', "Updownarrow;": '\U000021D5', "UpperLeftArrow;": '\U00002196', "UpperRightArrow;": '\U00002197', "Upsi;": '\U000003D2', "Upsilon;": '\U000003A5', "Uring;": '\U0000016E', "Uscr;": '\U0001D4B0', "Utilde;": '\U00000168', "Uuml;": '\U000000DC', "VDash;": '\U000022AB', "Vbar;": '\U00002AEB', "Vcy;": '\U00000412', "Vdash;": '\U000022A9', "Vdashl;": '\U00002AE6', "Vee;": '\U000022C1', "Verbar;": '\U00002016', "Vert;": '\U00002016', "VerticalBar;": '\U00002223', "VerticalLine;": '\U0000007C', "VerticalSeparator;": '\U00002758', "VerticalTilde;": '\U00002240', "VeryThinSpace;": '\U0000200A', "Vfr;": '\U0001D519', "Vopf;": '\U0001D54D', "Vscr;": '\U0001D4B1', "Vvdash;": '\U000022AA', "Wcirc;": '\U00000174', "Wedge;": '\U000022C0', "Wfr;": '\U0001D51A', "Wopf;": '\U0001D54E', "Wscr;": '\U0001D4B2', "Xfr;": '\U0001D51B', "Xi;": '\U0000039E', "Xopf;": '\U0001D54F', "Xscr;": '\U0001D4B3', "YAcy;": '\U0000042F', "YIcy;": '\U00000407', "YUcy;": '\U0000042E', "Yacute;": '\U000000DD', "Ycirc;": '\U00000176', "Ycy;": '\U0000042B', "Yfr;": '\U0001D51C', "Yopf;": '\U0001D550', "Yscr;": '\U0001D4B4', "Yuml;": '\U00000178', "ZHcy;": '\U00000416', "Zacute;": '\U00000179', "Zcaron;": '\U0000017D', "Zcy;": '\U00000417', "Zdot;": '\U0000017B', "ZeroWidthSpace;": '\U0000200B', "Zeta;": '\U00000396', "Zfr;": '\U00002128', "Zopf;": '\U00002124', "Zscr;": '\U0001D4B5', "aacute;": '\U000000E1', "abreve;": '\U00000103', "ac;": '\U0000223E', "acd;": '\U0000223F', "acirc;": '\U000000E2', "acute;": '\U000000B4', "acy;": '\U00000430', "aelig;": '\U000000E6', "af;": '\U00002061', "afr;": '\U0001D51E', "agrave;": '\U000000E0', "alefsym;": '\U00002135', "aleph;": '\U00002135', "alpha;": '\U000003B1', "amacr;": '\U00000101', "amalg;": '\U00002A3F', "amp;": '\U00000026', "and;": '\U00002227', "andand;": '\U00002A55', "andd;": '\U00002A5C', "andslope;": '\U00002A58', "andv;": '\U00002A5A', "ang;": '\U00002220', "ange;": '\U000029A4', "angle;": '\U00002220', "angmsd;": '\U00002221', "angmsdaa;": '\U000029A8', "angmsdab;": '\U000029A9', "angmsdac;": '\U000029AA', "angmsdad;": '\U000029AB', "angmsdae;": '\U000029AC', "angmsdaf;": '\U000029AD', "angmsdag;": '\U000029AE', "angmsdah;": '\U000029AF', "angrt;": '\U0000221F', "angrtvb;": '\U000022BE', "angrtvbd;": '\U0000299D', "angsph;": '\U00002222', "angst;": '\U000000C5', "angzarr;": '\U0000237C', "aogon;": '\U00000105', "aopf;": '\U0001D552', "ap;": '\U00002248', "apE;": '\U00002A70', "apacir;": '\U00002A6F', "ape;": '\U0000224A', "apid;": '\U0000224B', "apos;": '\U00000027', "approx;": '\U00002248', "approxeq;": '\U0000224A', "aring;": '\U000000E5', "ascr;": '\U0001D4B6', "ast;": '\U0000002A', "asymp;": '\U00002248', "asympeq;": '\U0000224D', "atilde;": '\U000000E3', "auml;": '\U000000E4', "awconint;": '\U00002233', "awint;": '\U00002A11', "bNot;": '\U00002AED', "backcong;": '\U0000224C', "backepsilon;": '\U000003F6', "backprime;": '\U00002035', "backsim;": '\U0000223D', "backsimeq;": '\U000022CD', "barvee;": '\U000022BD', "barwed;": '\U00002305', "barwedge;": '\U00002305', "bbrk;": '\U000023B5', "bbrktbrk;": '\U000023B6', "bcong;": '\U0000224C', "bcy;": '\U00000431', "bdquo;": '\U0000201E', "becaus;": '\U00002235', "because;": '\U00002235', "bemptyv;": '\U000029B0', "bepsi;": '\U000003F6', "bernou;": '\U0000212C', "beta;": '\U000003B2', "beth;": '\U00002136', "between;": '\U0000226C', "bfr;": '\U0001D51F', "bigcap;": '\U000022C2', "bigcirc;": '\U000025EF', "bigcup;": '\U000022C3', "bigodot;": '\U00002A00', "bigoplus;": '\U00002A01', "bigotimes;": '\U00002A02', "bigsqcup;": '\U00002A06', "bigstar;": '\U00002605', "bigtriangledown;": '\U000025BD', "bigtriangleup;": '\U000025B3', "biguplus;": '\U00002A04', "bigvee;": '\U000022C1', "bigwedge;": '\U000022C0', "bkarow;": '\U0000290D', "blacklozenge;": '\U000029EB', "blacksquare;": '\U000025AA', "blacktriangle;": '\U000025B4', "blacktriangledown;": '\U000025BE', "blacktriangleleft;": '\U000025C2', "blacktriangleright;": '\U000025B8', "blank;": '\U00002423', "blk12;": '\U00002592', "blk14;": '\U00002591', "blk34;": '\U00002593', "block;": '\U00002588', "bnot;": '\U00002310', "bopf;": '\U0001D553', "bot;": '\U000022A5', "bottom;": '\U000022A5', "bowtie;": '\U000022C8', "boxDL;": '\U00002557', "boxDR;": '\U00002554', "boxDl;": '\U00002556', "boxDr;": '\U00002553', "boxH;": '\U00002550', "boxHD;": '\U00002566', "boxHU;": '\U00002569', "boxHd;": '\U00002564', "boxHu;": '\U00002567', "boxUL;": '\U0000255D', "boxUR;": '\U0000255A', "boxUl;": '\U0000255C', "boxUr;": '\U00002559', "boxV;": '\U00002551', "boxVH;": '\U0000256C', "boxVL;": '\U00002563', "boxVR;": '\U00002560', "boxVh;": '\U0000256B', "boxVl;": '\U00002562', "boxVr;": '\U0000255F', "boxbox;": '\U000029C9', "boxdL;": '\U00002555', "boxdR;": '\U00002552', "boxdl;": '\U00002510', "boxdr;": '\U0000250C', "boxh;": '\U00002500', "boxhD;": '\U00002565', "boxhU;": '\U00002568', "boxhd;": '\U0000252C', "boxhu;": '\U00002534', "boxminus;": '\U0000229F', "boxplus;": '\U0000229E', "boxtimes;": '\U000022A0', "boxuL;": '\U0000255B', "boxuR;": '\U00002558', "boxul;": '\U00002518', "boxur;": '\U00002514', "boxv;": '\U00002502', "boxvH;": '\U0000256A', "boxvL;": '\U00002561', "boxvR;": '\U0000255E', "boxvh;": '\U0000253C', "boxvl;": '\U00002524', "boxvr;": '\U0000251C', "bprime;": '\U00002035', "breve;": '\U000002D8', "brvbar;": '\U000000A6', "bscr;": '\U0001D4B7', "bsemi;": '\U0000204F', "bsim;": '\U0000223D', "bsime;": '\U000022CD', "bsol;": '\U0000005C', "bsolb;": '\U000029C5', "bsolhsub;": '\U000027C8', "bull;": '\U00002022', "bullet;": '\U00002022', "bump;": '\U0000224E', "bumpE;": '\U00002AAE', "bumpe;": '\U0000224F', "bumpeq;": '\U0000224F', "cacute;": '\U00000107', "cap;": '\U00002229', "capand;": '\U00002A44', "capbrcup;": '\U00002A49', "capcap;": '\U00002A4B', "capcup;": '\U00002A47', "capdot;": '\U00002A40', "caret;": '\U00002041', "caron;": '\U000002C7', "ccaps;": '\U00002A4D', "ccaron;": '\U0000010D', "ccedil;": '\U000000E7', "ccirc;": '\U00000109', "ccups;": '\U00002A4C', "ccupssm;": '\U00002A50', "cdot;": '\U0000010B', "cedil;": '\U000000B8', "cemptyv;": '\U000029B2', "cent;": '\U000000A2', "centerdot;": '\U000000B7', "cfr;": '\U0001D520', "chcy;": '\U00000447', "check;": '\U00002713', "checkmark;": '\U00002713', "chi;": '\U000003C7', "cir;": '\U000025CB', "cirE;": '\U000029C3', "circ;": '\U000002C6', "circeq;": '\U00002257', "circlearrowleft;": '\U000021BA', "circlearrowright;": '\U000021BB', "circledR;": '\U000000AE', "circledS;": '\U000024C8', "circledast;": '\U0000229B', "circledcirc;": '\U0000229A', "circleddash;": '\U0000229D', "cire;": '\U00002257', "cirfnint;": '\U00002A10', "cirmid;": '\U00002AEF', "cirscir;": '\U000029C2', "clubs;": '\U00002663', "clubsuit;": '\U00002663', "colon;": '\U0000003A', "colone;": '\U00002254', "coloneq;": '\U00002254', "comma;": '\U0000002C', "commat;": '\U00000040', "comp;": '\U00002201', "compfn;": '\U00002218', "complement;": '\U00002201', "complexes;": '\U00002102', "cong;": '\U00002245', "congdot;": '\U00002A6D', "conint;": '\U0000222E', "copf;": '\U0001D554', "coprod;": '\U00002210', "copy;": '\U000000A9', "copysr;": '\U00002117', "crarr;": '\U000021B5', "cross;": '\U00002717', "cscr;": '\U0001D4B8', "csub;": '\U00002ACF', "csube;": '\U00002AD1', "csup;": '\U00002AD0', "csupe;": '\U00002AD2', "ctdot;": '\U000022EF', "cudarrl;": '\U00002938', "cudarrr;": '\U00002935', "cuepr;": '\U000022DE', "cuesc;": '\U000022DF', "cularr;": '\U000021B6', "cularrp;": '\U0000293D', "cup;": '\U0000222A', "cupbrcap;": '\U00002A48', "cupcap;": '\U00002A46', "cupcup;": '\U00002A4A', "cupdot;": '\U0000228D', "cupor;": '\U00002A45', "curarr;": '\U000021B7', "curarrm;": '\U0000293C', "curlyeqprec;": '\U000022DE', "curlyeqsucc;": '\U000022DF', "curlyvee;": '\U000022CE', "curlywedge;": '\U000022CF', "curren;": '\U000000A4', "curvearrowleft;": '\U000021B6', "curvearrowright;": '\U000021B7', "cuvee;": '\U000022CE', "cuwed;": '\U000022CF', "cwconint;": '\U00002232', "cwint;": '\U00002231', "cylcty;": '\U0000232D', "dArr;": '\U000021D3', "dHar;": '\U00002965', "dagger;": '\U00002020', "daleth;": '\U00002138', "darr;": '\U00002193', "dash;": '\U00002010', "dashv;": '\U000022A3', "dbkarow;": '\U0000290F', "dblac;": '\U000002DD', "dcaron;": '\U0000010F', "dcy;": '\U00000434', "dd;": '\U00002146', "ddagger;": '\U00002021', "ddarr;": '\U000021CA', "ddotseq;": '\U00002A77', "deg;": '\U000000B0', "delta;": '\U000003B4', "demptyv;": '\U000029B1', "dfisht;": '\U0000297F', "dfr;": '\U0001D521', "dharl;": '\U000021C3', "dharr;": '\U000021C2', "diam;": '\U000022C4', "diamond;": '\U000022C4', "diamondsuit;": '\U00002666', "diams;": '\U00002666', "die;": '\U000000A8', "digamma;": '\U000003DD', "disin;": '\U000022F2', "div;": '\U000000F7', "divide;": '\U000000F7', "divideontimes;": '\U000022C7', "divonx;": '\U000022C7', "djcy;": '\U00000452', "dlcorn;": '\U0000231E', "dlcrop;": '\U0000230D', "dollar;": '\U00000024', "dopf;": '\U0001D555', "dot;": '\U000002D9', "doteq;": '\U00002250', "doteqdot;": '\U00002251', "dotminus;": '\U00002238', "dotplus;": '\U00002214', "dotsquare;": '\U000022A1', "doublebarwedge;": '\U00002306', "downarrow;": '\U00002193', "downdownarrows;": '\U000021CA', "downharpoonleft;": '\U000021C3', "downharpoonright;": '\U000021C2', "drbkarow;": '\U00002910', "drcorn;": '\U0000231F', "drcrop;": '\U0000230C', "dscr;": '\U0001D4B9', "dscy;": '\U00000455', "dsol;": '\U000029F6', "dstrok;": '\U00000111', "dtdot;": '\U000022F1', "dtri;": '\U000025BF', "dtrif;": '\U000025BE', "duarr;": '\U000021F5', "duhar;": '\U0000296F', "dwangle;": '\U000029A6', "dzcy;": '\U0000045F', "dzigrarr;": '\U000027FF', "eDDot;": '\U00002A77', "eDot;": '\U00002251', "eacute;": '\U000000E9', "easter;": '\U00002A6E', "ecaron;": '\U0000011B', "ecir;": '\U00002256', "ecirc;": '\U000000EA', "ecolon;": '\U00002255', "ecy;": '\U0000044D', "edot;": '\U00000117', "ee;": '\U00002147', "efDot;": '\U00002252', "efr;": '\U0001D522', "eg;": '\U00002A9A', "egrave;": '\U000000E8', "egs;": '\U00002A96', "egsdot;": '\U00002A98', "el;": '\U00002A99', "elinters;": '\U000023E7', "ell;": '\U00002113', "els;": '\U00002A95', "elsdot;": '\U00002A97', "emacr;": '\U00000113', "empty;": '\U00002205', "emptyset;": '\U00002205', "emptyv;": '\U00002205', "emsp;": '\U00002003', "emsp13;": '\U00002004', "emsp14;": '\U00002005', "eng;": '\U0000014B', "ensp;": '\U00002002', "eogon;": '\U00000119', "eopf;": '\U0001D556', "epar;": '\U000022D5', "eparsl;": '\U000029E3', "eplus;": '\U00002A71', "epsi;": '\U000003B5', "epsilon;": '\U000003B5', "epsiv;": '\U000003F5', "eqcirc;": '\U00002256', "eqcolon;": '\U00002255', "eqsim;": '\U00002242', "eqslantgtr;": '\U00002A96', "eqslantless;": '\U00002A95', "equals;": '\U0000003D', "equest;": '\U0000225F', "equiv;": '\U00002261', "equivDD;": '\U00002A78', "eqvparsl;": '\U000029E5', "erDot;": '\U00002253', "erarr;": '\U00002971', "escr;": '\U0000212F', "esdot;": '\U00002250', "esim;": '\U00002242', "eta;": '\U000003B7', "eth;": '\U000000F0', "euml;": '\U000000EB', "euro;": '\U000020AC', "excl;": '\U00000021', "exist;": '\U00002203', "expectation;": '\U00002130', "exponentiale;": '\U00002147', "fallingdotseq;": '\U00002252', "fcy;": '\U00000444', "female;": '\U00002640', "ffilig;": '\U0000FB03', "fflig;": '\U0000FB00', "ffllig;": '\U0000FB04', "ffr;": '\U0001D523', "filig;": '\U0000FB01', "flat;": '\U0000266D', "fllig;": '\U0000FB02', "fltns;": '\U000025B1', "fnof;": '\U00000192', "fopf;": '\U0001D557', "forall;": '\U00002200', "fork;": '\U000022D4', "forkv;": '\U00002AD9', "fpartint;": '\U00002A0D', "frac12;": '\U000000BD', "frac13;": '\U00002153', "frac14;": '\U000000BC', "frac15;": '\U00002155', "frac16;": '\U00002159', "frac18;": '\U0000215B', "frac23;": '\U00002154', "frac25;": '\U00002156', "frac34;": '\U000000BE', "frac35;": '\U00002157', "frac38;": '\U0000215C', "frac45;": '\U00002158', "frac56;": '\U0000215A', "frac58;": '\U0000215D', "frac78;": '\U0000215E', "frasl;": '\U00002044', "frown;": '\U00002322', "fscr;": '\U0001D4BB', "gE;": '\U00002267', "gEl;": '\U00002A8C', "gacute;": '\U000001F5', "gamma;": '\U000003B3', "gammad;": '\U000003DD', "gap;": '\U00002A86', "gbreve;": '\U0000011F', "gcirc;": '\U0000011D', "gcy;": '\U00000433', "gdot;": '\U00000121', "ge;": '\U00002265', "gel;": '\U000022DB', "geq;": '\U00002265', "geqq;": '\U00002267', "geqslant;": '\U00002A7E', "ges;": '\U00002A7E', "gescc;": '\U00002AA9', "gesdot;": '\U00002A80', "gesdoto;": '\U00002A82', "gesdotol;": '\U00002A84', "gesles;": '\U00002A94', "gfr;": '\U0001D524', "gg;": '\U0000226B', "ggg;": '\U000022D9', "gimel;": '\U00002137', "gjcy;": '\U00000453', "gl;": '\U00002277', "glE;": '\U00002A92', "gla;": '\U00002AA5', "glj;": '\U00002AA4', "gnE;": '\U00002269', "gnap;": '\U00002A8A', "gnapprox;": '\U00002A8A', "gne;": '\U00002A88', "gneq;": '\U00002A88', "gneqq;": '\U00002269', "gnsim;": '\U000022E7', "gopf;": '\U0001D558', "grave;": '\U00000060', "gscr;": '\U0000210A', "gsim;": '\U00002273', "gsime;": '\U00002A8E', "gsiml;": '\U00002A90', "gt;": '\U0000003E', "gtcc;": '\U00002AA7', "gtcir;": '\U00002A7A', "gtdot;": '\U000022D7', "gtlPar;": '\U00002995', "gtquest;": '\U00002A7C', "gtrapprox;": '\U00002A86', "gtrarr;": '\U00002978', "gtrdot;": '\U000022D7', "gtreqless;": '\U000022DB', "gtreqqless;": '\U00002A8C', "gtrless;": '\U00002277', "gtrsim;": '\U00002273', "hArr;": '\U000021D4', "hairsp;": '\U0000200A', "half;": '\U000000BD', "hamilt;": '\U0000210B', "hardcy;": '\U0000044A', "harr;": '\U00002194', "harrcir;": '\U00002948', "harrw;": '\U000021AD', "hbar;": '\U0000210F', "hcirc;": '\U00000125', "hearts;": '\U00002665', "heartsuit;": '\U00002665', "hellip;": '\U00002026', "hercon;": '\U000022B9', "hfr;": '\U0001D525', "hksearow;": '\U00002925', "hkswarow;": '\U00002926', "hoarr;": '\U000021FF', "homtht;": '\U0000223B', "hookleftarrow;": '\U000021A9', "hookrightarrow;": '\U000021AA', "hopf;": '\U0001D559', "horbar;": '\U00002015', "hscr;": '\U0001D4BD', "hslash;": '\U0000210F', "hstrok;": '\U00000127', "hybull;": '\U00002043', "hyphen;": '\U00002010', "iacute;": '\U000000ED', "ic;": '\U00002063', "icirc;": '\U000000EE', "icy;": '\U00000438', "iecy;": '\U00000435', "iexcl;": '\U000000A1', "iff;": '\U000021D4', "ifr;": '\U0001D526', "igrave;": '\U000000EC', "ii;": '\U00002148', "iiiint;": '\U00002A0C', "iiint;": '\U0000222D', "iinfin;": '\U000029DC', "iiota;": '\U00002129', "ijlig;": '\U00000133', "imacr;": '\U0000012B', "image;": '\U00002111', "imagline;": '\U00002110', "imagpart;": '\U00002111', "imath;": '\U00000131', "imof;": '\U000022B7', "imped;": '\U000001B5', "in;": '\U00002208', "incare;": '\U00002105', "infin;": '\U0000221E', "infintie;": '\U000029DD', "inodot;": '\U00000131', "int;": '\U0000222B', "intcal;": '\U000022BA', "integers;": '\U00002124', "intercal;": '\U000022BA', "intlarhk;": '\U00002A17', "intprod;": '\U00002A3C', "iocy;": '\U00000451', "iogon;": '\U0000012F', "iopf;": '\U0001D55A', "iota;": '\U000003B9', "iprod;": '\U00002A3C', "iquest;": '\U000000BF', "iscr;": '\U0001D4BE', "isin;": '\U00002208', "isinE;": '\U000022F9', "isindot;": '\U000022F5', "isins;": '\U000022F4', "isinsv;": '\U000022F3', "isinv;": '\U00002208', "it;": '\U00002062', "itilde;": '\U00000129', "iukcy;": '\U00000456', "iuml;": '\U000000EF', "jcirc;": '\U00000135', "jcy;": '\U00000439', "jfr;": '\U0001D527', "jmath;": '\U00000237', "jopf;": '\U0001D55B', "jscr;": '\U0001D4BF', "jsercy;": '\U00000458', "jukcy;": '\U00000454', "kappa;": '\U000003BA', "kappav;": '\U000003F0', "kcedil;": '\U00000137', "kcy;": '\U0000043A', "kfr;": '\U0001D528', "kgreen;": '\U00000138', "khcy;": '\U00000445', "kjcy;": '\U0000045C', "kopf;": '\U0001D55C', "kscr;": '\U0001D4C0', "lAarr;": '\U000021DA', "lArr;": '\U000021D0', "lAtail;": '\U0000291B', "lBarr;": '\U0000290E', "lE;": '\U00002266', "lEg;": '\U00002A8B', "lHar;": '\U00002962', "lacute;": '\U0000013A', "laemptyv;": '\U000029B4', "lagran;": '\U00002112', "lambda;": '\U000003BB', "lang;": '\U000027E8', "langd;": '\U00002991', "langle;": '\U000027E8', "lap;": '\U00002A85', "laquo;": '\U000000AB', "larr;": '\U00002190', "larrb;": '\U000021E4', "larrbfs;": '\U0000291F', "larrfs;": '\U0000291D', "larrhk;": '\U000021A9', "larrlp;": '\U000021AB', "larrpl;": '\U00002939', "larrsim;": '\U00002973', "larrtl;": '\U000021A2', "lat;": '\U00002AAB', "latail;": '\U00002919', "late;": '\U00002AAD', "lbarr;": '\U0000290C', "lbbrk;": '\U00002772', "lbrace;": '\U0000007B', "lbrack;": '\U0000005B', "lbrke;": '\U0000298B', "lbrksld;": '\U0000298F', "lbrkslu;": '\U0000298D', "lcaron;": '\U0000013E', "lcedil;": '\U0000013C', "lceil;": '\U00002308', "lcub;": '\U0000007B', "lcy;": '\U0000043B', "ldca;": '\U00002936', "ldquo;": '\U0000201C', "ldquor;": '\U0000201E', "ldrdhar;": '\U00002967', "ldrushar;": '\U0000294B', "ldsh;": '\U000021B2', "le;": '\U00002264', "leftarrow;": '\U00002190', "leftarrowtail;": '\U000021A2', "leftharpoondown;": '\U000021BD', "leftharpoonup;": '\U000021BC', "leftleftarrows;": '\U000021C7', "leftrightarrow;": '\U00002194', "leftrightarrows;": '\U000021C6', "leftrightharpoons;": '\U000021CB', "leftrightsquigarrow;": '\U000021AD', "leftthreetimes;": '\U000022CB', "leg;": '\U000022DA', "leq;": '\U00002264', "leqq;": '\U00002266', "leqslant;": '\U00002A7D', "les;": '\U00002A7D', "lescc;": '\U00002AA8', "lesdot;": '\U00002A7F', "lesdoto;": '\U00002A81', "lesdotor;": '\U00002A83', "lesges;": '\U00002A93', "lessapprox;": '\U00002A85', "lessdot;": '\U000022D6', "lesseqgtr;": '\U000022DA', "lesseqqgtr;": '\U00002A8B', "lessgtr;": '\U00002276', "lesssim;": '\U00002272', "lfisht;": '\U0000297C', "lfloor;": '\U0000230A', "lfr;": '\U0001D529', "lg;": '\U00002276', "lgE;": '\U00002A91', "lhard;": '\U000021BD', "lharu;": '\U000021BC', "lharul;": '\U0000296A', "lhblk;": '\U00002584', "ljcy;": '\U00000459', "ll;": '\U0000226A', "llarr;": '\U000021C7', "llcorner;": '\U0000231E', "llhard;": '\U0000296B', "lltri;": '\U000025FA', "lmidot;": '\U00000140', "lmoust;": '\U000023B0', "lmoustache;": '\U000023B0', "lnE;": '\U00002268', "lnap;": '\U00002A89', "lnapprox;": '\U00002A89', "lne;": '\U00002A87', "lneq;": '\U00002A87', "lneqq;": '\U00002268', "lnsim;": '\U000022E6', "loang;": '\U000027EC', "loarr;": '\U000021FD', "lobrk;": '\U000027E6', "longleftarrow;": '\U000027F5', "longleftrightarrow;": '\U000027F7', "longmapsto;": '\U000027FC', "longrightarrow;": '\U000027F6', "looparrowleft;": '\U000021AB', "looparrowright;": '\U000021AC', "lopar;": '\U00002985', "lopf;": '\U0001D55D', "loplus;": '\U00002A2D', "lotimes;": '\U00002A34', "lowast;": '\U00002217', "lowbar;": '\U0000005F', "loz;": '\U000025CA', "lozenge;": '\U000025CA', "lozf;": '\U000029EB', "lpar;": '\U00000028', "lparlt;": '\U00002993', "lrarr;": '\U000021C6', "lrcorner;": '\U0000231F', "lrhar;": '\U000021CB', "lrhard;": '\U0000296D', "lrm;": '\U0000200E', "lrtri;": '\U000022BF', "lsaquo;": '\U00002039', "lscr;": '\U0001D4C1', "lsh;": '\U000021B0', "lsim;": '\U00002272', "lsime;": '\U00002A8D', "lsimg;": '\U00002A8F', "lsqb;": '\U0000005B', "lsquo;": '\U00002018', "lsquor;": '\U0000201A', "lstrok;": '\U00000142', "lt;": '\U0000003C', "ltcc;": '\U00002AA6', "ltcir;": '\U00002A79', "ltdot;": '\U000022D6', "lthree;": '\U000022CB', "ltimes;": '\U000022C9', "ltlarr;": '\U00002976', "ltquest;": '\U00002A7B', "ltrPar;": '\U00002996', "ltri;": '\U000025C3', "ltrie;": '\U000022B4', "ltrif;": '\U000025C2', "lurdshar;": '\U0000294A', "luruhar;": '\U00002966', "mDDot;": '\U0000223A', "macr;": '\U000000AF', "male;": '\U00002642', "malt;": '\U00002720', "maltese;": '\U00002720', "map;": '\U000021A6', "mapsto;": '\U000021A6', "mapstodown;": '\U000021A7', "mapstoleft;": '\U000021A4', "mapstoup;": '\U000021A5', "marker;": '\U000025AE', "mcomma;": '\U00002A29', "mcy;": '\U0000043C', "mdash;": '\U00002014', "measuredangle;": '\U00002221', "mfr;": '\U0001D52A', "mho;": '\U00002127', "micro;": '\U000000B5', "mid;": '\U00002223', "midast;": '\U0000002A', "midcir;": '\U00002AF0', "middot;": '\U000000B7', "minus;": '\U00002212', "minusb;": '\U0000229F', "minusd;": '\U00002238', "minusdu;": '\U00002A2A', "mlcp;": '\U00002ADB', "mldr;": '\U00002026', "mnplus;": '\U00002213', "models;": '\U000022A7', "mopf;": '\U0001D55E', "mp;": '\U00002213', "mscr;": '\U0001D4C2', "mstpos;": '\U0000223E', "mu;": '\U000003BC', "multimap;": '\U000022B8', "mumap;": '\U000022B8', "nLeftarrow;": '\U000021CD', "nLeftrightarrow;": '\U000021CE', "nRightarrow;": '\U000021CF', "nVDash;": '\U000022AF', "nVdash;": '\U000022AE', "nabla;": '\U00002207', "nacute;": '\U00000144', "nap;": '\U00002249', "napos;": '\U00000149', "napprox;": '\U00002249', "natur;": '\U0000266E', "natural;": '\U0000266E', "naturals;": '\U00002115', "nbsp;": '\U000000A0', "ncap;": '\U00002A43', "ncaron;": '\U00000148', "ncedil;": '\U00000146', "ncong;": '\U00002247', "ncup;": '\U00002A42', "ncy;": '\U0000043D', "ndash;": '\U00002013', "ne;": '\U00002260', "neArr;": '\U000021D7', "nearhk;": '\U00002924', "nearr;": '\U00002197', "nearrow;": '\U00002197', "nequiv;": '\U00002262', "nesear;": '\U00002928', "nexist;": '\U00002204', "nexists;": '\U00002204', "nfr;": '\U0001D52B', "nge;": '\U00002271', "ngeq;": '\U00002271', "ngsim;": '\U00002275', "ngt;": '\U0000226F', "ngtr;": '\U0000226F', "nhArr;": '\U000021CE', "nharr;": '\U000021AE', "nhpar;": '\U00002AF2', "ni;": '\U0000220B', "nis;": '\U000022FC', "nisd;": '\U000022FA', "niv;": '\U0000220B', "njcy;": '\U0000045A', "nlArr;": '\U000021CD', "nlarr;": '\U0000219A', "nldr;": '\U00002025', "nle;": '\U00002270', "nleftarrow;": '\U0000219A', "nleftrightarrow;": '\U000021AE', "nleq;": '\U00002270', "nless;": '\U0000226E', "nlsim;": '\U00002274', "nlt;": '\U0000226E', "nltri;": '\U000022EA', "nltrie;": '\U000022EC', "nmid;": '\U00002224', "nopf;": '\U0001D55F', "not;": '\U000000AC', "notin;": '\U00002209', "notinva;": '\U00002209', "notinvb;": '\U000022F7', "notinvc;": '\U000022F6', "notni;": '\U0000220C', "notniva;": '\U0000220C', "notnivb;": '\U000022FE', "notnivc;": '\U000022FD', "npar;": '\U00002226', "nparallel;": '\U00002226', "npolint;": '\U00002A14', "npr;": '\U00002280', "nprcue;": '\U000022E0', "nprec;": '\U00002280', "nrArr;": '\U000021CF', "nrarr;": '\U0000219B', "nrightarrow;": '\U0000219B', "nrtri;": '\U000022EB', "nrtrie;": '\U000022ED', "nsc;": '\U00002281', "nsccue;": '\U000022E1', "nscr;": '\U0001D4C3', "nshortmid;": '\U00002224', "nshortparallel;": '\U00002226', "nsim;": '\U00002241', "nsime;": '\U00002244', "nsimeq;": '\U00002244', "nsmid;": '\U00002224', "nspar;": '\U00002226', "nsqsube;": '\U000022E2', "nsqsupe;": '\U000022E3', "nsub;": '\U00002284', "nsube;": '\U00002288', "nsubseteq;": '\U00002288', "nsucc;": '\U00002281', "nsup;": '\U00002285', "nsupe;": '\U00002289', "nsupseteq;": '\U00002289', "ntgl;": '\U00002279', "ntilde;": '\U000000F1', "ntlg;": '\U00002278', "ntriangleleft;": '\U000022EA', "ntrianglelefteq;": '\U000022EC', "ntriangleright;": '\U000022EB', "ntrianglerighteq;": '\U000022ED', "nu;": '\U000003BD', "num;": '\U00000023', "numero;": '\U00002116', "numsp;": '\U00002007', "nvDash;": '\U000022AD', "nvHarr;": '\U00002904', "nvdash;": '\U000022AC', "nvinfin;": '\U000029DE', "nvlArr;": '\U00002902', "nvrArr;": '\U00002903', "nwArr;": '\U000021D6', "nwarhk;": '\U00002923', "nwarr;": '\U00002196', "nwarrow;": '\U00002196', "nwnear;": '\U00002927', "oS;": '\U000024C8', "oacute;": '\U000000F3', "oast;": '\U0000229B', "ocir;": '\U0000229A', "ocirc;": '\U000000F4', "ocy;": '\U0000043E', "odash;": '\U0000229D', "odblac;": '\U00000151', "odiv;": '\U00002A38', "odot;": '\U00002299', "odsold;": '\U000029BC', "oelig;": '\U00000153', "ofcir;": '\U000029BF', "ofr;": '\U0001D52C', "ogon;": '\U000002DB', "ograve;": '\U000000F2', "ogt;": '\U000029C1', "ohbar;": '\U000029B5', "ohm;": '\U000003A9', "oint;": '\U0000222E', "olarr;": '\U000021BA', "olcir;": '\U000029BE', "olcross;": '\U000029BB', "oline;": '\U0000203E', "olt;": '\U000029C0', "omacr;": '\U0000014D', "omega;": '\U000003C9', "omicron;": '\U000003BF', "omid;": '\U000029B6', "ominus;": '\U00002296', "oopf;": '\U0001D560', "opar;": '\U000029B7', "operp;": '\U000029B9', "oplus;": '\U00002295', "or;": '\U00002228', "orarr;": '\U000021BB', "ord;": '\U00002A5D', "order;": '\U00002134', "orderof;": '\U00002134', "ordf;": '\U000000AA', "ordm;": '\U000000BA', "origof;": '\U000022B6', "oror;": '\U00002A56', "orslope;": '\U00002A57', "orv;": '\U00002A5B', "oscr;": '\U00002134', "oslash;": '\U000000F8', "osol;": '\U00002298', "otilde;": '\U000000F5', "otimes;": '\U00002297', "otimesas;": '\U00002A36', "ouml;": '\U000000F6', "ovbar;": '\U0000233D', "par;": '\U00002225', "para;": '\U000000B6', "parallel;": '\U00002225', "parsim;": '\U00002AF3', "parsl;": '\U00002AFD', "part;": '\U00002202', "pcy;": '\U0000043F', "percnt;": '\U00000025', "period;": '\U0000002E', "permil;": '\U00002030', "perp;": '\U000022A5', "pertenk;": '\U00002031', "pfr;": '\U0001D52D', "phi;": '\U000003C6', "phiv;": '\U000003D5', "phmmat;": '\U00002133', "phone;": '\U0000260E', "pi;": '\U000003C0', "pitchfork;": '\U000022D4', "piv;": '\U000003D6', "planck;": '\U0000210F', "planckh;": '\U0000210E', "plankv;": '\U0000210F', "plus;": '\U0000002B', "plusacir;": '\U00002A23', "plusb;": '\U0000229E', "pluscir;": '\U00002A22', "plusdo;": '\U00002214', "plusdu;": '\U00002A25', "pluse;": '\U00002A72', "plusmn;": '\U000000B1', "plussim;": '\U00002A26', "plustwo;": '\U00002A27', "pm;": '\U000000B1', "pointint;": '\U00002A15', "popf;": '\U0001D561', "pound;": '\U000000A3', "pr;": '\U0000227A', "prE;": '\U00002AB3', "prap;": '\U00002AB7', "prcue;": '\U0000227C', "pre;": '\U00002AAF', "prec;": '\U0000227A', "precapprox;": '\U00002AB7', "preccurlyeq;": '\U0000227C', "preceq;": '\U00002AAF', "precnapprox;": '\U00002AB9', "precneqq;": '\U00002AB5', "precnsim;": '\U000022E8', "precsim;": '\U0000227E', "prime;": '\U00002032', "primes;": '\U00002119', "prnE;": '\U00002AB5', "prnap;": '\U00002AB9', "prnsim;": '\U000022E8', "prod;": '\U0000220F', "profalar;": '\U0000232E', "profline;": '\U00002312', "profsurf;": '\U00002313', "prop;": '\U0000221D', "propto;": '\U0000221D', "prsim;": '\U0000227E', "prurel;": '\U000022B0', "pscr;": '\U0001D4C5', "psi;": '\U000003C8', "puncsp;": '\U00002008', "qfr;": '\U0001D52E', "qint;": '\U00002A0C', "qopf;": '\U0001D562', "qprime;": '\U00002057', "qscr;": '\U0001D4C6', "quaternions;": '\U0000210D', "quatint;": '\U00002A16', "quest;": '\U0000003F', "questeq;": '\U0000225F', "quot;": '\U00000022', "rAarr;": '\U000021DB', "rArr;": '\U000021D2', "rAtail;": '\U0000291C', "rBarr;": '\U0000290F', "rHar;": '\U00002964', "racute;": '\U00000155', "radic;": '\U0000221A', "raemptyv;": '\U000029B3', "rang;": '\U000027E9', "rangd;": '\U00002992', "range;": '\U000029A5', "rangle;": '\U000027E9', "raquo;": '\U000000BB', "rarr;": '\U00002192', "rarrap;": '\U00002975', "rarrb;": '\U000021E5', "rarrbfs;": '\U00002920', "rarrc;": '\U00002933', "rarrfs;": '\U0000291E', "rarrhk;": '\U000021AA', "rarrlp;": '\U000021AC', "rarrpl;": '\U00002945', "rarrsim;": '\U00002974', "rarrtl;": '\U000021A3', "rarrw;": '\U0000219D', "ratail;": '\U0000291A', "ratio;": '\U00002236', "rationals;": '\U0000211A', "rbarr;": '\U0000290D', "rbbrk;": '\U00002773', "rbrace;": '\U0000007D', "rbrack;": '\U0000005D', "rbrke;": '\U0000298C', "rbrksld;": '\U0000298E', "rbrkslu;": '\U00002990', "rcaron;": '\U00000159', "rcedil;": '\U00000157', "rceil;": '\U00002309', "rcub;": '\U0000007D', "rcy;": '\U00000440', "rdca;": '\U00002937', "rdldhar;": '\U00002969', "rdquo;": '\U0000201D', "rdquor;": '\U0000201D', "rdsh;": '\U000021B3', "real;": '\U0000211C', "realine;": '\U0000211B', "realpart;": '\U0000211C', "reals;": '\U0000211D', "rect;": '\U000025AD', "reg;": '\U000000AE', "rfisht;": '\U0000297D', "rfloor;": '\U0000230B', "rfr;": '\U0001D52F', "rhard;": '\U000021C1', "rharu;": '\U000021C0', "rharul;": '\U0000296C', "rho;": '\U000003C1', "rhov;": '\U000003F1', "rightarrow;": '\U00002192', "rightarrowtail;": '\U000021A3', "rightharpoondown;": '\U000021C1', "rightharpoonup;": '\U000021C0', "rightleftarrows;": '\U000021C4', "rightleftharpoons;": '\U000021CC', "rightrightarrows;": '\U000021C9', "rightsquigarrow;": '\U0000219D', "rightthreetimes;": '\U000022CC', "ring;": '\U000002DA', "risingdotseq;": '\U00002253', "rlarr;": '\U000021C4', "rlhar;": '\U000021CC', "rlm;": '\U0000200F', "rmoust;": '\U000023B1', "rmoustache;": '\U000023B1', "rnmid;": '\U00002AEE', "roang;": '\U000027ED', "roarr;": '\U000021FE', "robrk;": '\U000027E7', "ropar;": '\U00002986', "ropf;": '\U0001D563', "roplus;": '\U00002A2E', "rotimes;": '\U00002A35', "rpar;": '\U00000029', "rpargt;": '\U00002994', "rppolint;": '\U00002A12', "rrarr;": '\U000021C9', "rsaquo;": '\U0000203A', "rscr;": '\U0001D4C7', "rsh;": '\U000021B1', "rsqb;": '\U0000005D', "rsquo;": '\U00002019', "rsquor;": '\U00002019', "rthree;": '\U000022CC', "rtimes;": '\U000022CA', "rtri;": '\U000025B9', "rtrie;": '\U000022B5', "rtrif;": '\U000025B8', "rtriltri;": '\U000029CE', "ruluhar;": '\U00002968', "rx;": '\U0000211E', "sacute;": '\U0000015B', "sbquo;": '\U0000201A', "sc;": '\U0000227B', "scE;": '\U00002AB4', "scap;": '\U00002AB8', "scaron;": '\U00000161', "sccue;": '\U0000227D', "sce;": '\U00002AB0', "scedil;": '\U0000015F', "scirc;": '\U0000015D', "scnE;": '\U00002AB6', "scnap;": '\U00002ABA', "scnsim;": '\U000022E9', "scpolint;": '\U00002A13', "scsim;": '\U0000227F', "scy;": '\U00000441', "sdot;": '\U000022C5', "sdotb;": '\U000022A1', "sdote;": '\U00002A66', "seArr;": '\U000021D8', "searhk;": '\U00002925', "searr;": '\U00002198', "searrow;": '\U00002198', "sect;": '\U000000A7', "semi;": '\U0000003B', "seswar;": '\U00002929', "setminus;": '\U00002216', "setmn;": '\U00002216', "sext;": '\U00002736', "sfr;": '\U0001D530', "sfrown;": '\U00002322', "sharp;": '\U0000266F', "shchcy;": '\U00000449', "shcy;": '\U00000448', "shortmid;": '\U00002223', "shortparallel;": '\U00002225', "shy;": '\U000000AD', "sigma;": '\U000003C3', "sigmaf;": '\U000003C2', "sigmav;": '\U000003C2', "sim;": '\U0000223C', "simdot;": '\U00002A6A', "sime;": '\U00002243', "simeq;": '\U00002243', "simg;": '\U00002A9E', "simgE;": '\U00002AA0', "siml;": '\U00002A9D', "simlE;": '\U00002A9F', "simne;": '\U00002246', "simplus;": '\U00002A24', "simrarr;": '\U00002972', "slarr;": '\U00002190', "smallsetminus;": '\U00002216', "smashp;": '\U00002A33', "smeparsl;": '\U000029E4', "smid;": '\U00002223', "smile;": '\U00002323', "smt;": '\U00002AAA', "smte;": '\U00002AAC', "softcy;": '\U0000044C', "sol;": '\U0000002F', "solb;": '\U000029C4', "solbar;": '\U0000233F', "sopf;": '\U0001D564', "spades;": '\U00002660', "spadesuit;": '\U00002660', "spar;": '\U00002225', "sqcap;": '\U00002293', "sqcup;": '\U00002294', "sqsub;": '\U0000228F', "sqsube;": '\U00002291', "sqsubset;": '\U0000228F', "sqsubseteq;": '\U00002291', "sqsup;": '\U00002290', "sqsupe;": '\U00002292', "sqsupset;": '\U00002290', "sqsupseteq;": '\U00002292', "squ;": '\U000025A1', "square;": '\U000025A1', "squarf;": '\U000025AA', "squf;": '\U000025AA', "srarr;": '\U00002192', "sscr;": '\U0001D4C8', "ssetmn;": '\U00002216', "ssmile;": '\U00002323', "sstarf;": '\U000022C6', "star;": '\U00002606', "starf;": '\U00002605', "straightepsilon;": '\U000003F5', "straightphi;": '\U000003D5', "strns;": '\U000000AF', "sub;": '\U00002282', "subE;": '\U00002AC5', "subdot;": '\U00002ABD', "sube;": '\U00002286', "subedot;": '\U00002AC3', "submult;": '\U00002AC1', "subnE;": '\U00002ACB', "subne;": '\U0000228A', "subplus;": '\U00002ABF', "subrarr;": '\U00002979', "subset;": '\U00002282', "subseteq;": '\U00002286', "subseteqq;": '\U00002AC5', "subsetneq;": '\U0000228A', "subsetneqq;": '\U00002ACB', "subsim;": '\U00002AC7', "subsub;": '\U00002AD5', "subsup;": '\U00002AD3', "succ;": '\U0000227B', "succapprox;": '\U00002AB8', "succcurlyeq;": '\U0000227D', "succeq;": '\U00002AB0', "succnapprox;": '\U00002ABA', "succneqq;": '\U00002AB6', "succnsim;": '\U000022E9', "succsim;": '\U0000227F', "sum;": '\U00002211', "sung;": '\U0000266A', "sup;": '\U00002283', "sup1;": '\U000000B9', "sup2;": '\U000000B2', "sup3;": '\U000000B3', "supE;": '\U00002AC6', "supdot;": '\U00002ABE', "supdsub;": '\U00002AD8', "supe;": '\U00002287', "supedot;": '\U00002AC4', "suphsol;": '\U000027C9', "suphsub;": '\U00002AD7', "suplarr;": '\U0000297B', "supmult;": '\U00002AC2', "supnE;": '\U00002ACC', "supne;": '\U0000228B', "supplus;": '\U00002AC0', "supset;": '\U00002283', "supseteq;": '\U00002287', "supseteqq;": '\U00002AC6', "supsetneq;": '\U0000228B', "supsetneqq;": '\U00002ACC', "supsim;": '\U00002AC8', "supsub;": '\U00002AD4', "supsup;": '\U00002AD6', "swArr;": '\U000021D9', "swarhk;": '\U00002926', "swarr;": '\U00002199', "swarrow;": '\U00002199', "swnwar;": '\U0000292A', "szlig;": '\U000000DF', "target;": '\U00002316', "tau;": '\U000003C4', "tbrk;": '\U000023B4', "tcaron;": '\U00000165', "tcedil;": '\U00000163', "tcy;": '\U00000442', "tdot;": '\U000020DB', "telrec;": '\U00002315', "tfr;": '\U0001D531', "there4;": '\U00002234', "therefore;": '\U00002234', "theta;": '\U000003B8', "thetasym;": '\U000003D1', "thetav;": '\U000003D1', "thickapprox;": '\U00002248', "thicksim;": '\U0000223C', "thinsp;": '\U00002009', "thkap;": '\U00002248', "thksim;": '\U0000223C', "thorn;": '\U000000FE', "tilde;": '\U000002DC', "times;": '\U000000D7', "timesb;": '\U000022A0', "timesbar;": '\U00002A31', "timesd;": '\U00002A30', "tint;": '\U0000222D', "toea;": '\U00002928', "top;": '\U000022A4', "topbot;": '\U00002336', "topcir;": '\U00002AF1', "topf;": '\U0001D565', "topfork;": '\U00002ADA', "tosa;": '\U00002929', "tprime;": '\U00002034', "trade;": '\U00002122', "triangle;": '\U000025B5', "triangledown;": '\U000025BF', "triangleleft;": '\U000025C3', "trianglelefteq;": '\U000022B4', "triangleq;": '\U0000225C', "triangleright;": '\U000025B9', "trianglerighteq;": '\U000022B5', "tridot;": '\U000025EC', "trie;": '\U0000225C', "triminus;": '\U00002A3A', "triplus;": '\U00002A39', "trisb;": '\U000029CD', "tritime;": '\U00002A3B', "trpezium;": '\U000023E2', "tscr;": '\U0001D4C9', "tscy;": '\U00000446', "tshcy;": '\U0000045B', "tstrok;": '\U00000167', "twixt;": '\U0000226C', "twoheadleftarrow;": '\U0000219E', "twoheadrightarrow;": '\U000021A0', "uArr;": '\U000021D1', "uHar;": '\U00002963', "uacute;": '\U000000FA', "uarr;": '\U00002191', "ubrcy;": '\U0000045E', "ubreve;": '\U0000016D', "ucirc;": '\U000000FB', "ucy;": '\U00000443', "udarr;": '\U000021C5', "udblac;": '\U00000171', "udhar;": '\U0000296E', "ufisht;": '\U0000297E', "ufr;": '\U0001D532', "ugrave;": '\U000000F9', "uharl;": '\U000021BF', "uharr;": '\U000021BE', "uhblk;": '\U00002580', "ulcorn;": '\U0000231C', "ulcorner;": '\U0000231C', "ulcrop;": '\U0000230F', "ultri;": '\U000025F8', "umacr;": '\U0000016B', "uml;": '\U000000A8', "uogon;": '\U00000173', "uopf;": '\U0001D566', "uparrow;": '\U00002191', "updownarrow;": '\U00002195', "upharpoonleft;": '\U000021BF', "upharpoonright;": '\U000021BE', "uplus;": '\U0000228E', "upsi;": '\U000003C5', "upsih;": '\U000003D2', "upsilon;": '\U000003C5', "upuparrows;": '\U000021C8', "urcorn;": '\U0000231D', "urcorner;": '\U0000231D', "urcrop;": '\U0000230E', "uring;": '\U0000016F', "urtri;": '\U000025F9', "uscr;": '\U0001D4CA', "utdot;": '\U000022F0', "utilde;": '\U00000169', "utri;": '\U000025B5', "utrif;": '\U000025B4', "uuarr;": '\U000021C8', "uuml;": '\U000000FC', "uwangle;": '\U000029A7', "vArr;": '\U000021D5', "vBar;": '\U00002AE8', "vBarv;": '\U00002AE9', "vDash;": '\U000022A8', "vangrt;": '\U0000299C', "varepsilon;": '\U000003F5', "varkappa;": '\U000003F0', "varnothing;": '\U00002205', "varphi;": '\U000003D5', "varpi;": '\U000003D6', "varpropto;": '\U0000221D', "varr;": '\U00002195', "varrho;": '\U000003F1', "varsigma;": '\U000003C2', "vartheta;": '\U000003D1', "vartriangleleft;": '\U000022B2', "vartriangleright;": '\U000022B3', "vcy;": '\U00000432', "vdash;": '\U000022A2', "vee;": '\U00002228', "veebar;": '\U000022BB', "veeeq;": '\U0000225A', "vellip;": '\U000022EE', "verbar;": '\U0000007C', "vert;": '\U0000007C', "vfr;": '\U0001D533', "vltri;": '\U000022B2', "vopf;": '\U0001D567', "vprop;": '\U0000221D', "vrtri;": '\U000022B3', "vscr;": '\U0001D4CB', "vzigzag;": '\U0000299A', "wcirc;": '\U00000175', "wedbar;": '\U00002A5F', "wedge;": '\U00002227', "wedgeq;": '\U00002259', "weierp;": '\U00002118', "wfr;": '\U0001D534', "wopf;": '\U0001D568', "wp;": '\U00002118', "wr;": '\U00002240', "wreath;": '\U00002240', "wscr;": '\U0001D4CC', "xcap;": '\U000022C2', "xcirc;": '\U000025EF', "xcup;": '\U000022C3', "xdtri;": '\U000025BD', "xfr;": '\U0001D535', "xhArr;": '\U000027FA', "xharr;": '\U000027F7', "xi;": '\U000003BE', "xlArr;": '\U000027F8', "xlarr;": '\U000027F5', "xmap;": '\U000027FC', "xnis;": '\U000022FB', "xodot;": '\U00002A00', "xopf;": '\U0001D569', "xoplus;": '\U00002A01', "xotime;": '\U00002A02', "xrArr;": '\U000027F9', "xrarr;": '\U000027F6', "xscr;": '\U0001D4CD', "xsqcup;": '\U00002A06', "xuplus;": '\U00002A04', "xutri;": '\U000025B3', "xvee;": '\U000022C1', "xwedge;": '\U000022C0', "yacute;": '\U000000FD', "yacy;": '\U0000044F', "ycirc;": '\U00000177', "ycy;": '\U0000044B', "yen;": '\U000000A5', "yfr;": '\U0001D536', "yicy;": '\U00000457', "yopf;": '\U0001D56A', "yscr;": '\U0001D4CE', "yucy;": '\U0000044E', "yuml;": '\U000000FF', "zacute;": '\U0000017A', "zcaron;": '\U0000017E', "zcy;": '\U00000437', "zdot;": '\U0000017C', "zeetrf;": '\U00002128', "zeta;": '\U000003B6', "zfr;": '\U0001D537', "zhcy;": '\U00000436', "zigrarr;": '\U000021DD', "zopf;": '\U0001D56B', "zscr;": '\U0001D4CF', "zwj;": '\U0000200D', "zwnj;": '\U0000200C', "AElig": '\U000000C6', "AMP": '\U00000026', "Aacute": '\U000000C1', "Acirc": '\U000000C2', "Agrave": '\U000000C0', "Aring": '\U000000C5', "Atilde": '\U000000C3', "Auml": '\U000000C4', "COPY": '\U000000A9', "Ccedil": '\U000000C7', "ETH": '\U000000D0', "Eacute": '\U000000C9', "Ecirc": '\U000000CA', "Egrave": '\U000000C8', "Euml": '\U000000CB', "GT": '\U0000003E', "Iacute": '\U000000CD', "Icirc": '\U000000CE', "Igrave": '\U000000CC', "Iuml": '\U000000CF', "LT": '\U0000003C', "Ntilde": '\U000000D1', "Oacute": '\U000000D3', "Ocirc": '\U000000D4', "Ograve": '\U000000D2', "Oslash": '\U000000D8', "Otilde": '\U000000D5', "Ouml": '\U000000D6', "QUOT": '\U00000022', "REG": '\U000000AE', "THORN": '\U000000DE', "Uacute": '\U000000DA', "Ucirc": '\U000000DB', "Ugrave": '\U000000D9', "Uuml": '\U000000DC', "Yacute": '\U000000DD', "aacute": '\U000000E1', "acirc": '\U000000E2', "acute": '\U000000B4', "aelig": '\U000000E6', "agrave": '\U000000E0', "amp": '\U00000026', "aring": '\U000000E5', "atilde": '\U000000E3', "auml": '\U000000E4', "brvbar": '\U000000A6', "ccedil": '\U000000E7', "cedil": '\U000000B8', "cent": '\U000000A2', "copy": '\U000000A9', "curren": '\U000000A4', "deg": '\U000000B0', "divide": '\U000000F7', "eacute": '\U000000E9', "ecirc": '\U000000EA', "egrave": '\U000000E8', "eth": '\U000000F0', "euml": '\U000000EB', "frac12": '\U000000BD', "frac14": '\U000000BC', "frac34": '\U000000BE', "gt": '\U0000003E', "iacute": '\U000000ED', "icirc": '\U000000EE', "iexcl": '\U000000A1', "igrave": '\U000000EC', "iquest": '\U000000BF', "iuml": '\U000000EF', "laquo": '\U000000AB', "lt": '\U0000003C', "macr": '\U000000AF', "micro": '\U000000B5', "middot": '\U000000B7', "nbsp": '\U000000A0', "not": '\U000000AC', "ntilde": '\U000000F1', "oacute": '\U000000F3', "ocirc": '\U000000F4', "ograve": '\U000000F2', "ordf": '\U000000AA', "ordm": '\U000000BA', "oslash": '\U000000F8', "otilde": '\U000000F5', "ouml": '\U000000F6', "para": '\U000000B6', "plusmn": '\U000000B1', "pound": '\U000000A3', "quot": '\U00000022', "raquo": '\U000000BB', "reg": '\U000000AE', "sect": '\U000000A7', "shy": '\U000000AD', "sup1": '\U000000B9', "sup2": '\U000000B2', "sup3": '\U000000B3', "szlig": '\U000000DF', "thorn": '\U000000FE', "times": '\U000000D7', "uacute": '\U000000FA', "ucirc": '\U000000FB', "ugrave": '\U000000F9', "uml": '\U000000A8', "uuml": '\U000000FC', "yacute": '\U000000FD', "yen": '\U000000A5', "yuml": '\U000000FF', } // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ // TODO(nigeltao): Handle replacements that are wider than their names. // "nLt;": {'\u226A', '\u20D2'}, // "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, "NotHumpDownHump;": {'\u224E', '\u0338'}, "NotHumpEqual;": {'\u224F', '\u0338'}, "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, "NotLessLess;": {'\u226A', '\u0338'}, "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, "NotNestedLessLess;": {'\u2AA1', '\u0338'}, "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, "NotRightTriangleBar;": {'\u29D0', '\u0338'}, "NotSquareSubset;": {'\u228F', '\u0338'}, "NotSquareSuperset;": {'\u2290', '\u0338'}, "NotSubset;": {'\u2282', '\u20D2'}, "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, "NotSucceedsTilde;": {'\u227F', '\u0338'}, "NotSuperset;": {'\u2283', '\u20D2'}, "ThickSpace;": {'\u205F', '\u200A'}, "acE;": {'\u223E', '\u0333'}, "bne;": {'\u003D', '\u20E5'}, "bnequiv;": {'\u2261', '\u20E5'}, "caps;": {'\u2229', '\uFE00'}, "cups;": {'\u222A', '\uFE00'}, "fjlig;": {'\u0066', '\u006A'}, "gesl;": {'\u22DB', '\uFE00'}, "gvertneqq;": {'\u2269', '\uFE00'}, "gvnE;": {'\u2269', '\uFE00'}, "lates;": {'\u2AAD', '\uFE00'}, "lesg;": {'\u22DA', '\uFE00'}, "lvertneqq;": {'\u2268', '\uFE00'}, "lvnE;": {'\u2268', '\uFE00'}, "nGg;": {'\u22D9', '\u0338'}, "nGtv;": {'\u226B', '\u0338'}, "nLl;": {'\u22D8', '\u0338'}, "nLtv;": {'\u226A', '\u0338'}, "nang;": {'\u2220', '\u20D2'}, "napE;": {'\u2A70', '\u0338'}, "napid;": {'\u224B', '\u0338'}, "nbump;": {'\u224E', '\u0338'}, "nbumpe;": {'\u224F', '\u0338'}, "ncongdot;": {'\u2A6D', '\u0338'}, "nedot;": {'\u2250', '\u0338'}, "nesim;": {'\u2242', '\u0338'}, "ngE;": {'\u2267', '\u0338'}, "ngeqq;": {'\u2267', '\u0338'}, "ngeqslant;": {'\u2A7E', '\u0338'}, "nges;": {'\u2A7E', '\u0338'}, "nlE;": {'\u2266', '\u0338'}, "nleqq;": {'\u2266', '\u0338'}, "nleqslant;": {'\u2A7D', '\u0338'}, "nles;": {'\u2A7D', '\u0338'}, "notinE;": {'\u22F9', '\u0338'}, "notindot;": {'\u22F5', '\u0338'}, "nparsl;": {'\u2AFD', '\u20E5'}, "npart;": {'\u2202', '\u0338'}, "npre;": {'\u2AAF', '\u0338'}, "npreceq;": {'\u2AAF', '\u0338'}, "nrarrc;": {'\u2933', '\u0338'}, "nrarrw;": {'\u219D', '\u0338'}, "nsce;": {'\u2AB0', '\u0338'}, "nsubE;": {'\u2AC5', '\u0338'}, "nsubset;": {'\u2282', '\u20D2'}, "nsubseteqq;": {'\u2AC5', '\u0338'}, "nsucceq;": {'\u2AB0', '\u0338'}, "nsupE;": {'\u2AC6', '\u0338'}, "nsupset;": {'\u2283', '\u20D2'}, "nsupseteqq;": {'\u2AC6', '\u0338'}, "nvap;": {'\u224D', '\u20D2'}, "nvge;": {'\u2265', '\u20D2'}, "nvgt;": {'\u003E', '\u20D2'}, "nvle;": {'\u2264', '\u20D2'}, "nvlt;": {'\u003C', '\u20D2'}, "nvltrie;": {'\u22B4', '\u20D2'}, "nvrtrie;": {'\u22B5', '\u20D2'}, "nvsim;": {'\u223C', '\u20D2'}, "race;": {'\u223D', '\u0331'}, "smtes;": {'\u2AAC', '\uFE00'}, "sqcaps;": {'\u2293', '\uFE00'}, "sqcups;": {'\u2294', '\uFE00'}, "varsubsetneq;": {'\u228A', '\uFE00'}, "varsubsetneqq;": {'\u2ACB', '\uFE00'}, "varsupsetneq;": {'\u228B', '\uFE00'}, "varsupsetneqq;": {'\u2ACC', '\uFE00'}, "vnsub;": {'\u2282', '\u20D2'}, "vnsup;": {'\u2283', '\u20D2'}, "vsubnE;": {'\u2ACB', '\uFE00'}, "vsubne;": {'\u228A', '\uFE00'}, "vsupnE;": {'\u2ACC', '\uFE00'}, "vsupne;": {'\u228B', '\uFE00'}, } ================================================ FILE: vendor/golang.org/x/net/html/escape.go ================================================ // 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. package html import ( "bytes" "strings" "unicode/utf8" ) // These replacements permit compatibility with old numeric entities that // assumed Windows-1252 encoding. // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference var replacementTable = [...]rune{ '\u20AC', // First entry is what 0x80 should be replaced with. '\u0081', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021', '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u008D', '\u017D', '\u008F', '\u0090', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014', '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u009D', '\u017E', '\u0178', // Last entry is 0x9F. // 0x00->'\uFFFD' is handled programmatically. // 0x0D->'\u000D' is a no-op. } // unescapeEntity reads an entity like "<" from b[src:] and writes the // corresponding "<" to b[dst:], returning the incremented dst and src cursors. // Precondition: b[src] == '&' && dst <= src. // attribute should be true if parsing an attribute value. func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. i, s := 1, b[src:] if len(s) <= 1 { b[dst] = b[src] return dst + 1, src + 1 } if s[i] == '#' { if len(s) <= 3 { // We need to have at least "&#.". b[dst] = b[src] return dst + 1, src + 1 } i++ c := s[i] hex := false if c == 'x' || c == 'X' { hex = true i++ } x := '\x00' for i < len(s) { c = s[i] i++ if hex { if '0' <= c && c <= '9' { x = 16*x + rune(c) - '0' continue } else if 'a' <= c && c <= 'f' { x = 16*x + rune(c) - 'a' + 10 continue } else if 'A' <= c && c <= 'F' { x = 16*x + rune(c) - 'A' + 10 continue } } else if '0' <= c && c <= '9' { x = 10*x + rune(c) - '0' continue } if c != ';' { i-- } break } if i <= 3 { // No characters matched. b[dst] = b[src] return dst + 1, src + 1 } if 0x80 <= x && x <= 0x9F { // Replace characters from Windows-1252 with UTF-8 equivalents. x = replacementTable[x-0x80] } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { // Replace invalid characters with the replacement character. x = '\uFFFD' } return dst + utf8.EncodeRune(b[dst:], x), src + i } // Consume the maximum number of characters possible, with the // consumed characters matching one of the named references. for i < len(s) { c := s[i] i++ // Lower-cased characters are more common in entities, so we check for them first. if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { continue } if c != ';' { i-- } break } entityName := string(s[1:i]) if entityName == "" { // No-op. } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + i } else if x := entity2[entityName]; x[0] != 0 { dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { maxLen = longestEntityWithoutSemicolon } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 } } } dst1, src1 = dst+i, src+i copy(b[dst:dst1], b[src:src1]) return dst1, src1 } // unescape unescapes b's entities in-place, so that "a<b" becomes "a': esc = ">" case '"': // """ is shorter than """. esc = """ case '\r': esc = " " default: panic("unrecognized escape character") } s = s[i+1:] if _, err := w.WriteString(esc); err != nil { return err } i = strings.IndexAny(s, escapedChars) } _, err := w.WriteString(s) return err } // EscapeString escapes special characters like "<" to become "<". It // escapes only five such characters: <, >, &, ' and ". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func EscapeString(s string) string { if strings.IndexAny(s, escapedChars) == -1 { return s } var buf bytes.Buffer escape(&buf, s) return buf.String() } // UnescapeString unescapes entities like "<" to become "<". It unescapes a // larger range of entities than EscapeString escapes. For example, "á" // unescapes to "á", as does "á" and "&xE1;". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { for _, c := range s { if c == '&' { return string(unescape([]byte(s), false)) } } return s } ================================================ FILE: vendor/golang.org/x/net/html/foreign.go ================================================ // 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. package html import ( "strings" ) func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { for i := range aa { if newName, ok := nameMap[aa[i].Key]; ok { aa[i].Key = newName } } } func adjustForeignAttributes(aa []Attribute) { for i, a := range aa { if a.Key == "" || a.Key[0] != 'x' { continue } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] } } } func htmlIntegrationPoint(n *Node) bool { if n.Type != ElementNode { return false } switch n.Namespace { case "math": if n.Data == "annotation-xml" { for _, a := range n.Attr { if a.Key == "encoding" { val := strings.ToLower(a.Val) if val == "text/html" || val == "application/xhtml+xml" { return true } } } } case "svg": switch n.Data { case "desc", "foreignObject", "title": return true } } return false } func mathMLTextIntegrationPoint(n *Node) bool { if n.Namespace != "math" { return false } switch n.Data { case "mi", "mo", "mn", "ms", "mtext": return true } return false } // Section 12.2.6.5. var breakout = map[string]bool{ "b": true, "big": true, "blockquote": true, "body": true, "br": true, "center": true, "code": true, "dd": true, "div": true, "dl": true, "dt": true, "em": true, "embed": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "hr": true, "i": true, "img": true, "li": true, "listing": true, "menu": true, "meta": true, "nobr": true, "ol": true, "p": true, "pre": true, "ruby": true, "s": true, "small": true, "span": true, "strong": true, "strike": true, "sub": true, "sup": true, "table": true, "tt": true, "u": true, "ul": true, "var": true, } // Section 12.2.6.5. var svgTagNameAdjustments = map[string]string{ "altglyph": "altGlyph", "altglyphdef": "altGlyphDef", "altglyphitem": "altGlyphItem", "animatecolor": "animateColor", "animatemotion": "animateMotion", "animatetransform": "animateTransform", "clippath": "clipPath", "feblend": "feBlend", "fecolormatrix": "feColorMatrix", "fecomponenttransfer": "feComponentTransfer", "fecomposite": "feComposite", "feconvolvematrix": "feConvolveMatrix", "fediffuselighting": "feDiffuseLighting", "fedisplacementmap": "feDisplacementMap", "fedistantlight": "feDistantLight", "feflood": "feFlood", "fefunca": "feFuncA", "fefuncb": "feFuncB", "fefuncg": "feFuncG", "fefuncr": "feFuncR", "fegaussianblur": "feGaussianBlur", "feimage": "feImage", "femerge": "feMerge", "femergenode": "feMergeNode", "femorphology": "feMorphology", "feoffset": "feOffset", "fepointlight": "fePointLight", "fespecularlighting": "feSpecularLighting", "fespotlight": "feSpotLight", "fetile": "feTile", "feturbulence": "feTurbulence", "foreignobject": "foreignObject", "glyphref": "glyphRef", "lineargradient": "linearGradient", "radialgradient": "radialGradient", "textpath": "textPath", } // Section 12.2.6.1 var mathMLAttributeAdjustments = map[string]string{ "definitionurl": "definitionURL", } var svgAttributeAdjustments = map[string]string{ "attributename": "attributeName", "attributetype": "attributeType", "basefrequency": "baseFrequency", "baseprofile": "baseProfile", "calcmode": "calcMode", "clippathunits": "clipPathUnits", "diffuseconstant": "diffuseConstant", "edgemode": "edgeMode", "filterunits": "filterUnits", "glyphref": "glyphRef", "gradienttransform": "gradientTransform", "gradientunits": "gradientUnits", "kernelmatrix": "kernelMatrix", "kernelunitlength": "kernelUnitLength", "keypoints": "keyPoints", "keysplines": "keySplines", "keytimes": "keyTimes", "lengthadjust": "lengthAdjust", "limitingconeangle": "limitingConeAngle", "markerheight": "markerHeight", "markerunits": "markerUnits", "markerwidth": "markerWidth", "maskcontentunits": "maskContentUnits", "maskunits": "maskUnits", "numoctaves": "numOctaves", "pathlength": "pathLength", "patterncontentunits": "patternContentUnits", "patterntransform": "patternTransform", "patternunits": "patternUnits", "pointsatx": "pointsAtX", "pointsaty": "pointsAtY", "pointsatz": "pointsAtZ", "preservealpha": "preserveAlpha", "preserveaspectratio": "preserveAspectRatio", "primitiveunits": "primitiveUnits", "refx": "refX", "refy": "refY", "repeatcount": "repeatCount", "repeatdur": "repeatDur", "requiredextensions": "requiredExtensions", "requiredfeatures": "requiredFeatures", "specularconstant": "specularConstant", "specularexponent": "specularExponent", "spreadmethod": "spreadMethod", "startoffset": "startOffset", "stddeviation": "stdDeviation", "stitchtiles": "stitchTiles", "surfacescale": "surfaceScale", "systemlanguage": "systemLanguage", "tablevalues": "tableValues", "targetx": "targetX", "targety": "targetY", "textlength": "textLength", "viewbox": "viewBox", "viewtarget": "viewTarget", "xchannelselector": "xChannelSelector", "ychannelselector": "yChannelSelector", "zoomandpan": "zoomAndPan", } ================================================ FILE: vendor/golang.org/x/net/html/node.go ================================================ // 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. package html import ( "golang.org/x/net/html/atom" ) // A NodeType is the type of a Node. type NodeType uint32 const ( ErrorNode NodeType = iota TextNode DocumentNode ElementNode CommentNode DoctypeNode // RawNode nodes are not returned by the parser, but can be part of the // Node tree passed to func Render to insert raw HTML (without escaping). // If so, this package makes no guarantee that the rendered HTML is secure // (from e.g. Cross Site Scripting attacks) or well-formed. RawNode scopeMarkerNode ) // Section 12.2.4.3 says "The markers are inserted when entering applet, // object, marquee, template, td, th, and caption elements, and are used // to prevent formatting from "leaking" into applet, object, marquee, // template, td, th, and caption elements". var scopeMarker = Node{Type: scopeMarkerNode} // A Node consists of a NodeType and some Data (tag name for element nodes, // content for text) and are part of a tree of Nodes. Element nodes may also // have a Namespace and contain a slice of Attributes. Data is unescaped, so // that it looks like "a 0 { return (*s)[i-1] } return nil } // index returns the index of the top-most occurrence of n in the stack, or -1 // if n is not present. func (s *nodeStack) index(n *Node) int { for i := len(*s) - 1; i >= 0; i-- { if (*s)[i] == n { return i } } return -1 } // contains returns whether a is within s. func (s *nodeStack) contains(a atom.Atom) bool { for _, n := range *s { if n.DataAtom == a && n.Namespace == "" { return true } } return false } // insert inserts a node at the given index. func (s *nodeStack) insert(i int, n *Node) { (*s) = append(*s, nil) copy((*s)[i+1:], (*s)[i:]) (*s)[i] = n } // remove removes a node from the stack. It is a no-op if n is not present. func (s *nodeStack) remove(n *Node) { i := s.index(n) if i == -1 { return } copy((*s)[i:], (*s)[i+1:]) j := len(*s) - 1 (*s)[j] = nil *s = (*s)[:j] } type insertionModeStack []insertionMode func (s *insertionModeStack) pop() (im insertionMode) { i := len(*s) im = (*s)[i-1] *s = (*s)[:i-1] return im } func (s *insertionModeStack) top() insertionMode { if i := len(*s); i > 0 { return (*s)[i-1] } return nil } ================================================ FILE: vendor/golang.org/x/net/html/parse.go ================================================ // 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. package html import ( "errors" "fmt" "io" "strings" a "golang.org/x/net/html/atom" ) // A parser implements the HTML5 parsing algorithm: // https://html.spec.whatwg.org/multipage/syntax.html#tree-construction type parser struct { // tokenizer provides the tokens for the parser. tokenizer *Tokenizer // tok is the most recently read token. tok Token // Self-closing tags like
are treated as start tags, except that // hasSelfClosingToken is set while they are being processed. hasSelfClosingToken bool // doc is the document root element. doc *Node // The stack of open elements (section 12.2.4.2) and active formatting // elements (section 12.2.4.3). oe, afe nodeStack // Element pointers (section 12.2.4.4). head, form *Node // Other parsing state flags (section 12.2.4.5). scripting, framesetOK bool // The stack of template insertion modes templateStack insertionModeStack // im is the current insertion mode. im insertionMode // originalIM is the insertion mode to go back to after completing a text // or inTableText insertion mode. originalIM insertionMode // fosterParenting is whether new elements should be inserted according to // the foster parenting rules (section 12.2.6.1). fosterParenting bool // quirks is whether the parser is operating in "quirks mode." quirks bool // fragment is whether the parser is parsing an HTML fragment. fragment bool // context is the context element when parsing an HTML fragment // (section 12.4). context *Node } func (p *parser) top() *Node { if n := p.oe.top(); n != nil { return n } return p.doc } // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } ) type scope int const ( defaultScope scope = iota listItemScope buttonScope tableScope tableRowScope tableBodyScope selectScope ) // popUntil pops the stack of open elements at the highest element whose tag // is in matchTags, provided there is no higher element in the scope's stop // tags (as defined in section 12.2.4.2). It returns whether or not there was // such an element. If there was not, popUntil leaves the stack unchanged. // // For example, the set of stop tags for table scope is: "html", "table". If // the stack was: // ["html", "body", "font", "table", "b", "i", "u"] // then popUntil(tableScope, "font") would return false, but // popUntil(tableScope, "i") would return true and the stack would become: // ["html", "body", "font", "table", "b"] // // If an element's tag is in both the stop tags and matchTags, then the stack // will be popped and the function returns true (provided, of course, there was // no higher element in the stack that was also in the stop tags). For example, // popUntil(tableScope, "table") returns true and leaves: // ["html", "body", "font"] func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { if i := p.indexOfElementInScope(s, matchTags...); i != -1 { p.oe = p.oe[:i] return true } return false } // indexOfElementInScope returns the index in p.oe of the highest element whose // tag is in matchTags that is in scope. If no matching element is in scope, it // returns -1. func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom if p.oe[i].Namespace == "" { for _, t := range matchTags { if t == tagAtom { return i } } switch s { case defaultScope: // No-op. case listItemScope: if tagAtom == a.Ol || tagAtom == a.Ul { return -1 } case buttonScope: if tagAtom == a.Button { return -1 } case tableScope: if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } case selectScope: if tagAtom != a.Optgroup && tagAtom != a.Option { return -1 } default: panic("unreachable") } } switch s { case defaultScope, listItemScope, buttonScope: for _, t := range defaultScopeStopTags[p.oe[i].Namespace] { if t == tagAtom { return -1 } } } } return -1 } // elementInScope is like popUntil, except that it doesn't modify the stack of // open elements. func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool { return p.indexOfElementInScope(s, matchTags...) != -1 } // clearStackToContext pops elements off the stack of open elements until a // scope-defined element is found. func (p *parser) clearStackToContext(s scope) { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom switch s { case tableScope: if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { p.oe = p.oe[:i+1] return } case tableRowScope: if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template { p.oe = p.oe[:i+1] return } case tableBodyScope: if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template { p.oe = p.oe[:i+1] return } default: panic("unreachable") } } } // parseGenericRawTextElements implements the generic raw text element parsing // algorithm defined in 12.2.6.2. // https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text // TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part // officially, need to make tokenizer consider both states. func (p *parser) parseGenericRawTextElement() { p.addElement() p.originalIM = p.im p.im = textIM } // generateImpliedEndTags pops nodes off the stack of open elements as long as // the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc. // If exceptions are specified, nodes with that name will not be popped off. func (p *parser) generateImpliedEndTags(exceptions ...string) { var i int loop: for i = len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if n.Type != ElementNode { break } switch n.DataAtom { case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc: for _, except := range exceptions { if n.Data == except { break loop } } continue } break } p.oe = p.oe[:i+1] } // addChild adds a child node n to the top element, and pushes n onto the stack // of open elements if it is an element node. func (p *parser) addChild(n *Node) { if p.shouldFosterParent() { p.fosterParent(n) } else { p.top().AppendChild(n) } if n.Type == ElementNode { p.oe = append(p.oe, n) } } // shouldFosterParent returns whether the next node to be added should be // foster parented. func (p *parser) shouldFosterParent() bool { if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: return true } } return false } // fosterParent adds a child node according to the foster parenting rules. // Section 12.2.6.1, "foster parenting". func (p *parser) fosterParent(n *Node) { var table, parent, prev, template *Node var i int for i = len(p.oe) - 1; i >= 0; i-- { if p.oe[i].DataAtom == a.Table { table = p.oe[i] break } } var j int for j = len(p.oe) - 1; j >= 0; j-- { if p.oe[j].DataAtom == a.Template { template = p.oe[j] break } } if template != nil && (table == nil || j > i) { template.AppendChild(n) return } if table == nil { // The foster parent is the html element. parent = p.oe[0] } else { parent = table.Parent } if parent == nil { parent = p.oe[i-1] } if table != nil { prev = table.PrevSibling } else { prev = parent.LastChild } if prev != nil && prev.Type == TextNode && n.Type == TextNode { prev.Data += n.Data return } parent.InsertBefore(n, table) } // addText adds text to the preceding node if it is a text node, or else it // calls addChild with a new text node. func (p *parser) addText(text string) { if text == "" { return } if p.shouldFosterParent() { p.fosterParent(&Node{ Type: TextNode, Data: text, }) return } t := p.top() if n := t.LastChild; n != nil && n.Type == TextNode { n.Data += text return } p.addChild(&Node{ Type: TextNode, Data: text, }) } // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ Type: ElementNode, DataAtom: p.tok.DataAtom, Data: p.tok.Data, Attr: p.tok.Attr, }) } // Section 12.2.4.3. func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: for i := len(p.afe) - 1; i >= 0; i-- { n := p.afe[i] if n.Type == scopeMarkerNode { break } if n.Type != ElementNode { continue } if n.Namespace != "" { continue } if n.DataAtom != tagAtom { continue } if len(n.Attr) != len(attr) { continue } compareAttributes: for _, t0 := range n.Attr { for _, t1 := range attr { if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { // Found a match for this attribute, continue with the next attribute. continue compareAttributes } } // If we get here, there is no attribute that matches a. // Therefore the element is not identical to the new one. continue findIdenticalElements } identicalElements++ if identicalElements >= 3 { p.afe.remove(n) } } p.afe = append(p.afe, p.top()) } // Section 12.2.4.3. func (p *parser) clearActiveFormattingElements() { for { if n := p.afe.pop(); len(p.afe) == 0 || n.Type == scopeMarkerNode { return } } } // Section 12.2.4.3. func (p *parser) reconstructActiveFormattingElements() { n := p.afe.top() if n == nil { return } if n.Type == scopeMarkerNode || p.oe.index(n) != -1 { return } i := len(p.afe) - 1 for n.Type != scopeMarkerNode && p.oe.index(n) == -1 { if i == 0 { i = -1 break } i-- n = p.afe[i] } for { i++ clone := p.afe[i].clone() p.addChild(clone) p.afe[i] = clone if i == len(p.afe)-1 { break } } } // Section 12.2.5. func (p *parser) acknowledgeSelfClosingTag() { p.hasSelfClosingToken = false } // An insertion mode (section 12.2.4.1) is the state transition function from // a particular state in the HTML5 parser's state machine. It updates the // parser's fields depending on parser.tok (where ErrorToken means EOF). // It returns whether the token was consumed. type insertionMode func(*parser) bool // setOriginalIM sets the insertion mode to return to after completing a text or // inTableText insertion mode. // Section 12.2.4.1, "using the rules for". func (p *parser) setOriginalIM() { if p.originalIM != nil { panic("html: bad parser state: originalIM was set twice") } p.originalIM = p.im } // Section 12.2.4.1, "reset the insertion mode". func (p *parser) resetInsertionMode() { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] last := i == 0 if last && p.context != nil { n = p.context } switch n.DataAtom { case a.Select: if !last { for ancestor, first := n, p.oe[0]; ancestor != first; { ancestor = p.oe[p.oe.index(ancestor)-1] switch ancestor.DataAtom { case a.Template: p.im = inSelectIM return case a.Table: p.im = inSelectInTableIM return } } } p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 p.im = inCellIM case a.Tr: p.im = inRowIM case a.Tbody, a.Thead, a.Tfoot: p.im = inTableBodyIM case a.Caption: p.im = inCaptionIM case a.Colgroup: p.im = inColumnGroupIM case a.Table: p.im = inTableIM case a.Template: // TODO: remove this divergence from the HTML5 spec. if n.Namespace != "" { continue } p.im = p.templateStack.top() case a.Head: // TODO: remove this divergence from the HTML5 spec. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 p.im = inHeadIM case a.Body: p.im = inBodyIM case a.Frameset: p.im = inFramesetIM case a.Html: if p.head == nil { p.im = beforeHeadIM } else { p.im = afterHeadIM } default: if last { p.im = inBodyIM return } continue } return } } const whitespace = " \t\r\n\f" // Section 12.2.6.4.1. func initialIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: n, quirks := parseDoctype(p.tok.Data) p.doc.AppendChild(n) p.quirks = quirks p.im = beforeHTMLIM return true } p.quirks = true p.im = beforeHTMLIM return false } // Section 12.2.6.4.2. func beforeHTMLIM(p *parser) bool { switch p.tok.Type { case DoctypeToken: // Ignore the token. return true case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: if p.tok.DataAtom == a.Html { p.addElement() p.im = beforeHeadIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false default: // Ignore the token. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true } p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false } // Section 12.2.6.4.3. func beforeHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: switch p.tok.DataAtom { case a.Head: p.addElement() p.head = p.top() p.im = inHeadIM return true case a.Html: return inBodyIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false } // Section 12.2.6.4.4. func inHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) < len(p.tok.Data) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { return true } p.tok.Data = s } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() return true case a.Noscript: if p.scripting { p.parseGenericRawTextElement() return true } p.addElement() p.im = inHeadNoscriptIM // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() return true case a.Script, a.Title: p.addElement() p.setOriginalIM() p.im = textIM return true case a.Noframes, a.Style: p.parseGenericRawTextElement() return true case a.Head: // Ignore the token. return true case a.Template: p.addElement() p.afe = append(p.afe, &scopeMarker) p.framesetOK = false p.im = inTemplateIM p.templateStack = append(p.templateStack, inTemplateIM) return true } case EndTagToken: switch p.tok.DataAtom { case a.Head: p.oe.pop() p.im = afterHeadIM return true case a.Body, a.Html, a.Br: p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) return false case a.Template: if !p.oe.contains(a.Template) { return true } // TODO: remove this divergence from the HTML5 spec. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 p.generateImpliedEndTags() for i := len(p.oe) - 1; i >= 0; i-- { if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { p.oe = p.oe[:i] break } } p.clearActiveFormattingElements() p.templateStack.pop() p.resetInsertionMode() return true default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) return false } // 12.2.6.4.5. func inHeadNoscriptIM(p *parser) bool { switch p.tok.Type { case DoctypeToken: // Ignore the token. return true case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style: return inHeadIM(p) case a.Head: // Ignore the token. return true case a.Noscript: // Don't let the tokenizer go into raw text mode even when a