├── .github └── workflows │ ├── binary_branch.yaml │ ├── binary_release.yaml │ ├── image_branch.yaml │ └── image_release.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bpf ├── LICENSE.BSD-2-Clause ├── LICENSE.GPL-2.0 ├── access.h ├── api.h ├── bpf.h ├── bpf_core_read.h ├── bpf_endian.h ├── bpf_helper_defs.h ├── bpf_helpers.h ├── bpf_tracing.h ├── btf.h ├── builtins.h ├── compiler.h ├── csum.h ├── ctx │ ├── common.h │ ├── ctx.h │ ├── skb.h │ ├── sock.h │ ├── unspec.h │ └── xdp.h ├── errno.h ├── features.h ├── features_skb.h ├── features_xdp.h ├── helpers.h ├── helpers_skb.h ├── helpers_sock.h ├── helpers_xdp.h ├── libbpf.h ├── libbpf_common.h ├── libbpf_legacy.h ├── loader.h ├── section.h ├── skel_internal.h ├── stddef.h ├── tailcall.h ├── types_mapper.h ├── verifier.h └── xsk.h ├── cmd └── flowctl │ ├── main.go │ └── sub.mk ├── config.yaml ├── examples ├── agent_output_hook │ ├── config.yaml │ └── flowlog.json └── dummy_flow_test │ ├── README.md │ ├── config.yaml │ ├── flow.yaml │ └── out.pcap ├── go.mod ├── go.sum ├── img └── linux_datapath.drawio.svg ├── misc ├── benchmarking_20221111 │ ├── README.md │ ├── benchmark_logs.md │ ├── generate_scripts.sh │ ├── n_flow_performance.png │ ├── n_flow_performance.py │ ├── n_hook_performance.png │ ├── n_hook_performance.py │ ├── optimization.png │ └── optimization.py ├── clean_netns.sh ├── create_netns.sh ├── hook_command_example_dummy.sh ├── hook_command_example_hostname.sh └── hook_command_example_ifname.sh └── pkg ├── ebpfmap ├── perf.go └── types.go ├── flowctl ├── agent.go ├── app.go ├── data │ └── filter.bpf.c ├── dump.go ├── flush.go ├── ipfix.go ├── meter.go └── prom.go ├── goroute2 ├── link.go ├── netns.go └── tc.go ├── hook ├── command.go ├── interface.go └── shell.go ├── ipfix ├── config.go ├── editme.go └── types.go └── util ├── bytes.go ├── cobra.go ├── execute.go ├── file.go ├── ip.go ├── misc.go ├── tablewriter.go ├── time.go └── version.go /.github/workflows/binary_branch.yaml: -------------------------------------------------------------------------------- 1 | name: branch binary 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Publish for ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | include: 15 | - os: ubuntu-latest 16 | artifact_name: flowctl 17 | asset_name: flowctl.linux-amd64 18 | steps: 19 | - name: set up Go 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: 1.17 23 | id: go 24 | - name: checkout 25 | uses: actions/checkout@v2 26 | - name: download modules 27 | if: steps.cache.outputs.cache-hit != 'true' 28 | run: go mod download 29 | - name: test 30 | run: go test ./... -v 31 | - name: build 32 | run: | 33 | go build -o ${{ matrix.artifact_name }} -ldflags "\ 34 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitSHA=$(git rev-parse HEAD) \ 35 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitBranch=$(git symbolic-ref HEAD | sed -e 's#refs/heads/##g') \ 36 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitTag=none \ 37 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.buildDate=$(date '+%Y/%m/%d-%H:%M:%S-%Z') \ 38 | " cmd/flowctl/main.go 39 | - name: upload 40 | uses: svenstaro/upload-release-action@v2 41 | with: 42 | repo_token: ${{ secrets.GITHUB_TOKEN }} 43 | file: ${{ matrix.artifact_name }} 44 | asset_name: ${{ matrix.asset_name }} 45 | tag: branch-main 46 | overwrite: true 47 | -------------------------------------------------------------------------------- /.github/workflows/binary_release.yaml: -------------------------------------------------------------------------------- 1 | name: release binary 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: Publish for ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | include: 15 | - os: ubuntu-latest 16 | artifact_name: flowctl 17 | asset_name: flowctl.linux-amd64 18 | steps: 19 | - name: set up Go 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: 1.17 23 | id: go 24 | - name: checkout 25 | uses: actions/checkout@v2 26 | - name: download modules 27 | if: steps.cache.outputs.cache-hit != 'true' 28 | run: go mod download 29 | - name: test 30 | run: go test ./... -v 31 | - name: build 32 | run: | 33 | go build -o ${{ matrix.artifact_name }} -ldflags "\ 34 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitSHA=$(git rev-parse HEAD) \ 35 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitBranch=none \ 36 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitTag=$(git describe --tags --abbrev=0) \ 37 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.buildDate=$(date '+%Y/%m/%d-%H:%M:%S-%Z') \ 38 | " cmd/flowctl/main.go 39 | 40 | - name: upload 41 | uses: svenstaro/upload-release-action@v2 42 | with: 43 | repo_token: ${{ secrets.GITHUB_TOKEN }} 44 | file: ${{ matrix.artifact_name }} 45 | asset_name: ${{ matrix.asset_name }} 46 | tag: ${{ github.ref }} 47 | #overwrite: true 48 | -------------------------------------------------------------------------------- /.github/workflows/image_branch.yaml: -------------------------------------------------------------------------------- 1 | name: branch container-image 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docker-build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: clone repo 13 | uses: actions/checkout@v2 14 | 15 | - name: login ghcr 16 | id: login_docker 17 | uses: docker/login-action@v1 18 | with: 19 | registry: ghcr.io 20 | username: vsix-robot 21 | password: ${{ secrets.VSIX_ROBOT }} 22 | 23 | - name: push with branch name 24 | id: run_docker_push_with_branch_name 25 | run: | 26 | docker build \ 27 | --build-arg GIT_SHA=$(git rev-parse HEAD) \ 28 | --build-arg GIT_BRANCH=$(git symbolic-ref HEAD | sed -e 's#refs/heads/##g') \ 29 | --build-arg GIT_TAG=none \ 30 | --build-arg BUILD_DATE=$(date '+%Y/%m/%d-%H:%M:%S-%Z') \ 31 | --tag ghcr.io/wide-vsix/linux-flow-exporter:branch-main . 32 | docker push ghcr.io/wide-vsix/linux-flow-exporter:branch-main 33 | env: 34 | BUILDKIT: 1 35 | -------------------------------------------------------------------------------- /.github/workflows/image_release.yaml: -------------------------------------------------------------------------------- 1 | name: release container-image 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | 7 | jobs: 8 | docker-build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: clone repo 12 | uses: actions/checkout@v2 13 | 14 | - name: login ghcr 15 | id: login_docker 16 | uses: docker/login-action@v1 17 | with: 18 | registry: ghcr.io 19 | username: vsix-robot 20 | password: ${{ secrets.VSIX_ROBOT }} 21 | 22 | - name: push with branch name 23 | id: run_docker_push_with_branch_name 24 | run: | 25 | VERSION=$(echo ${{ github.ref }} | sed -e "s#refs/tags/##g") 26 | docker build \ 27 | --build-arg GIT_SHA=$(git rev-parse HEAD) \ 28 | --build-arg GIT_BRANCH=nona \ 29 | --build-arg GIT_TAG=$VERSION \ 30 | --build-arg BUILD_DATE=$(date '+%Y/%m/%d-%H:%M:%S-%Z') \ 31 | --tag ghcr.io/wide-vsix/linux-flow-exporter:$VERSION . 32 | docker push ghcr.io/wide-vsix/linux-flow-exporter:$VERSION 33 | env: 34 | BUILDKIT: 1 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.o 7 | *.dylib 8 | /bin/ 9 | /local.mk 10 | *.swp 11 | .DS_Store 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG CILIUM_LLVM_IMAGE=quay.io/cilium/cilium-llvm:3408daa17f6490a464dfc746961e28ae31964c66 2 | ARG UBUNTU_IMAGE=docker.io/library/ubuntu:22.04 3 | 4 | # STAGE(llvm-dist) 5 | FROM ${CILIUM_LLVM_IMAGE} as llvm-dist 6 | 7 | # STAGE(iproute2-dist) 8 | FROM ${UBUNTU_IMAGE} as iproute2-dist 9 | RUN DEBIAN_FRONTEND=noninteractive apt update && apt install -y \ 10 | vim curl git gcc make flex bison clang-12 libbsd-dev libbfd-dev \ 11 | libcap-dev libelf-dev gcc-multilib pkg-config linux-tools-`uname -r` 12 | ARG LIBBPF_VERSION="0.8.0" 13 | ARG IPROUTE2_VERSION="5.18.0" 14 | ADD https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz . 15 | RUN tar xvf v${LIBBPF_VERSION}.tar.gz 16 | RUN cd libbpf-${LIBBPF_VERSION}/src && make install BUILD_STATIC_ONLY=1 && make install_pkgconfig 17 | ADD https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/snapshot/iproute2-${IPROUTE2_VERSION}.tar.gz . 18 | RUN tar xvf iproute2-${IPROUTE2_VERSION}.tar.gz 19 | RUN cd iproute2-${IPROUTE2_VERSION} && ./configure --libbpf_force=on --libbpf_dir=/ && make install 20 | 21 | # STAGE(flowctl-dist) 22 | FROM golang:1.17 as flowctl-dist 23 | ARG GIT_SHA=unknown 24 | ARG GIT_BRANCH=unknown 25 | ARG GIT_TAG=unknown 26 | ARG BUILD_DATE=unknown 27 | WORKDIR /opt 28 | COPY ./ ./ 29 | RUN CGO_ENABLED=0 go build -o ./bin/flowctl -ldflags "\ 30 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitSHA=$GIT_SHA \ 31 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitBranch=$GIT_BRANCH \ 32 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.gitTag=$GIT_TAG \ 33 | -X github.com/wide-vsix/linux-flow-exporter/pkg/util.buildDate=$BUILD_DATE \ 34 | " ./cmd/flowctl/main.go 35 | 36 | # STAGE(rootfs) 37 | FROM ${UBUNTU_IMAGE} as rootfs 38 | RUN DEBIAN_FRONTEND=noninteractive apt update && apt install -y --no-install-recommends \ 39 | curl libelf1 libmnl0 bash-completion iptables ipset kmod ca-certificates \ 40 | libelf-dev libbsd-dev jq 41 | COPY ./bpf /usr/include/bpf 42 | COPY --from=llvm-dist /usr/local/bin/clang /usr/local/bin/llc /usr/local/bin/ 43 | COPY --from=iproute2-dist /usr/sbin/ip /usr/sbin/ss /usr/sbin/tc /usr/sbin/ 44 | COPY --from=flowctl-dist /opt/bin/flowctl /usr/bin/ 45 | RUN echo "source /etc/bash_completion" >> /root/.bashrc 46 | RUN echo ". <(flowctl completion bash)" >> /root/.bashrc 47 | 48 | # FINAL STAGE 49 | FROM scratch 50 | LABEL org.opencontainers.image.source https://github.com/wide-vsix/linux-flow-exporter 51 | COPY --from=rootfs / / 52 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Hiroki Shirokura. 2 | # Copyright 2022 Keio University. 3 | # Copyright 2022 Wide Project. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | include ./cmd/*/sub.mk 18 | ifeq ($(shell test -e local.mk && echo -n yes),yes) 19 | include local.mk 20 | endif 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-flow-exporter 2 | 3 | eBPF based IPFIX exporter. This software is an IPFIX flow-exporter for routing 4 | with Linux kernel. It records flow stats forwarded by the kernel using tc-ebpf, 5 | without AF_PACKET or conntrack. Some flow-exporter using AF_PACKET has 6 | performance issues due to frequent user/kernel communication, and Some one using 7 | conntrack does not work properly in a multipath environment. This software is an 8 | flow-exporter that does not have such issues and supports multipath environment 9 | with less performance issues. 10 | 11 | ## System Components 12 | 13 | - ebpflow: in-kernel flow-stats collector with ebpf 14 | - flowctl: user-space cli utility includes: 15 | - daemonized agent for IPFIX flow-exporter 16 | - dump the in-kernel flow-stats from the user-space 17 | - IPFIX dummy data transmitter for test 18 | - dependencies (tested) 19 | - linux kernel 5.x+ 20 | - iproute2 5.18+ 21 | 22 | Requirements: you can verifiy compatibity with `dependency-check` subcmd. 23 | - `clang --version`: 10.0.0 24 | - `uname -r`: 5.15.0-1008-gcp 25 | - `ip -V`: ip utility, iproute2-5.18.0, libbpf 0.8.0 26 | - iproute2 is needed to attach ebpf program into the kernel. 27 | 28 | ``` 29 | $ sudo flowctl dependency-check 30 | clang version (expect v10.0.0): v12.0.1 (VALID) 31 | kernel version (expect v5.4.0): v5.15.0 (VALID) 32 | iproute2 binary version (expect v5.4.0): v5.18.0 (VALID) 33 | iproute2 libbpf version (expect v0.8.0): v0.8.0 (VALID) 34 | ``` 35 | 36 | ![](./img/linux_datapath.drawio.svg) 37 | 38 | ## Example Usage 39 | 40 | ```yaml 41 | collectors: 42 | - address: 10.146.0.6:2100 43 | templates: 44 | - id: 1024 45 | template: 46 | - FlowEndMilliseconds 47 | - FlowStartMilliseconds 48 | - OctetDeltaCount 49 | - PacketDeltaCount 50 | - IpVersion 51 | - FlowDirection 52 | - SourceIPv4Address 53 | - DestinationIPv4Address 54 | ``` 55 | 56 | ``` 57 | git clone 58 | cd 59 | sudo ./misc/create_netns.sh 60 | sudo flowctl meter attach --netns ns0 -n eth1 61 | sudo flowctl meter attach --netns ns0 -n eth2 62 | sudo flowctl meter attach --netns ns0 -n eth3 63 | ``` 64 | 65 | ### flowctl usage 66 | 67 | ```shell 68 | docker run --rm --name tmp -it --privileged --net=host -v /usr/include/asm:/usr/include/asm -v /var/run/netns:/var/run/netns ghcr.io/wide-vsix/linux-flow-exporter:branch-master bash 69 | flowctl meter status 70 | ``` 71 | 72 | ```shell 73 | ## How to check current flow cache 74 | $ sudo flowctl dump 75 | IFINDEX PROTO SRC DST PKTS BYTES 76 | 98 6 172.17.0.7:49375 172.67.134.3:80 1707 186818 77 | 98 6 172.17.0.7:41585 104.21.25.104:80 1710 187560 78 | 98 6 172.17.0.7:37869 104.21.25.104:80 9 486 79 | 80 | $ sudo flowctl flush -i 98 -p 6 -s 172.17.0.7 -S 37869 -d 104.21.25.104 -D 80 # one cache 81 | $ sudo flowctl flush --all # all caches 82 | ``` 83 | 84 | ## Limitation 85 | 86 | This software works ONLY for tcp. 87 | 88 | ## Utilities 89 | 90 | ``` 91 | docker run --rm -td --name tmp1 nicolaka/netshoot bash 92 | ``` 93 | 94 | ## Background Why we need 95 | 96 | - conntrack doesn't support async traffic 97 | - libpcap based approach consume extreamly big computing cost 98 | 99 | REFS 100 | - [Let's read RFC regarding IPFIX (ja) by Asama-san](https://enog.jp/wordpress/wp-content/uploads/2011/12/ipfix.pdf) 101 | - [SKB Definition](https://elixir.bootlin.com/linux/latest/source/include/linux/skbuff.h) 102 | - [Connection Tracking (conntrack): Design and Implementation Inside Linux Kernel](https://arthurchiao.art/blog/conntrack-design-and-implementation/) 103 | - [Packet mark in a Cloud Native world, LPC](https://lpc.events/event/7/contributions/683/attachments/554/979/lpc20-pkt-mark-slides.pdf) 104 | - [VMware NSX IPFIX for Distributed Firewall](https://docs.vmware.com/en/VMware-NSX-Data-Center-for-vSphere/6.4/com.vmware.nsx.admin.doc/GUID-2C625B52-17F0-4604-B5C9-6DF1FA9A70F8.html) 105 | - [VMware NSX IPFIX for Logical Switch](https://docs.vmware.com/en/VMware-NSX-Data-Center-for-vSphere/6.4/com.vmware.nsx.admin.doc/GUID-6054CF07-3019-4539-A6CC-1F613E275E27.html) 106 | - [Comparison and Practice of packet processing implementations and acceleration methods (ja), Ebiken-san, Higebu-san, JANOG45](https://www.janog.gr.jp/meeting/janog45/program/pktfwd/) 107 | - [IN-kernel metadata propagation technique from XDP buffer to SKB](https://github.com/torvalds/linux/blob/master/samples/bpf/xdp2skb_meta_kern.c) 108 | - [Private Discussion for metadata practice in eBPF](https://netmoles.slack.com/archives/C01DYBEETN0/p1667363998874089) 109 | - [One of the Reference design for traffic control mech using eBPF(both xdp and tc), ENOG63 by Higebu-san](https://www.docswell.com/s/higebu/ZQWRMZ-xdp-based-mobile-network-data-plane#p21) 110 | - [An open, unofficial registry of linux packet mark bits (aka fwmark, connmark, netfilter, iptables, nftables)](https://github.com/fwmark/registry) 111 | - [BPF Features by Linux Kernel Version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) 112 | 113 | ## Specification 114 | 115 | ### Supported Text Log Keys 116 | 117 | supported 118 | ``` 119 | src, string 120 | dst, string 121 | proto, string 122 | starttime 123 | endtime 124 | pkts 125 | bytes 126 | ``` 127 | 128 | consideration to support 129 | ``` 130 | matched acl rule number 131 | ``` 132 | 133 | ### Supported IPFIX IETF IE 134 | 135 | reference: [IANA registration](https://www.iana.org/assignments/ipfix/ipfix.xhtml) 136 | 137 | ``` 138 | {"FlowEndMilliseconds", netflow.IPFIX_FIELD_flowEndMilliseconds, 8}, 139 | {"FlowStartMilliseconds", netflow.IPFIX_FIELD_flowStartMilliseconds, 8}, 140 | {"FlowEndNanoseconds", netflow.IPFIX_FIELD_flowEndNanoseconds, 8}, 141 | {"FlowStartNanoseconds", netflow.IPFIX_FIELD_flowStartNanoseconds, 8}, 142 | {"OctetDeltaCount", netflow.IPFIX_FIELD_octetDeltaCount, 8}, 143 | {"PacketDeltaCount", netflow.IPFIX_FIELD_packetDeltaCount, 8}, 144 | {"IpVersion", netflow.IPFIX_FIELD_ipVersion, 1}, 145 | {"IngressInterface", netflow.IPFIX_FIELD_ingressInterface, 4}, 146 | {"EgressInterface", netflow.IPFIX_FIELD_egressInterface, 4}, 147 | {"SourceIPv4Address", netflow.IPFIX_FIELD_sourceIPv4Address, 4}, 148 | {"DestinationIPv4Address", netflow.IPFIX_FIELD_destinationIPv4Address, 4}, 149 | {"ProtocolIdentifier", netflow.IPFIX_FIELD_protocolIdentifier, 1}, 150 | {"SourceTransportPort", netflow.IPFIX_FIELD_sourceTransportPort, 2}, 151 | {"DestinationTransportPort", netflow.IPFIX_FIELD_destinationTransportPort, 2}, 152 | ``` 153 | 154 | under the development 155 | ``` 156 | {"forwardingStatus", 89, 1} 157 | ``` 158 | 159 | follow will be supported, in mid term 160 | ``` 161 | {"flowDirection", netflow.IPFIX_FIELD_flowDirection, 1}, 162 | {"tcpControlBits", netflow.IPFIX_FIELD_tcpControlBits, 1}, 163 | {"icmpTypeCodeIPv4", netflow.IPFIX_FIELD_icmpTypeCodeIPv4, 2}, 164 | ``` 165 | 166 | follow will be supported, in long term 167 | ``` 168 | {"ipClassOfService", netflow.IPFIX_FIELD_ipClassOfService, 1}, 169 | {"sourceIPv4PrefixLength", netflow.IPFIX_FIELD_sourceIPv4PrefixLength, 1}, 170 | {"destinationIPv4PrefixLength", netflow.IPFIX_FIELD_destinationIPv4PrefixLength, 1}, 171 | {"ipNextHopIPv4Address", netflow.IPFIX_FIELD_ipNextHopIPv4Address, 4}, 172 | {"bgpSourceAsNumber", netflow.IPFIX_FIELD_bgpSourceAsNumber, 4}, 173 | {"bgpDestinationAsNumber", netflow.IPFIX_FIELD_bgpDestinationAsNumber, 4}, 174 | {"bgpNextHopIPv4Address", netflow.IPFIX_FIELD_bgpNextHopIPv4Address, 4}, 175 | {"minimumTTL", netflow.IPFIX_FIELD_minimumTTL, 1}, 176 | {"maximumTTL", netflow.IPFIX_FIELD_maximumTTL, 1}, 177 | {"fragmentIdentification", netflow.IPFIX_FIELD_fragmentIdentification, 4}, 178 | {"vlanId", netflow.IPFIX_FIELD_vlanId, 2}, 179 | {"flowEndReason", netflow.IPFIX_FIELD_flowEndReason, 1}, 180 | {"dot1qVlanId", netflow.IPFIX_FIELD_dot1qVlanId, 2}, 181 | {"dot1qCustomerVlanId", netflow.IPFIX_FIELD_dot1qCustomerVlanId, 2}, 182 | ``` 183 | 184 | 185 | ### Supported IPFIX Enterprise IE 186 | 187 | Enterprise No: 28972 188 | ([Keio University, iana registry](https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers)). 189 | It may be updated by LINE Corporation 190 | 191 | ``` 192 | ``` 193 | 194 | ## Pre Build Packages 195 | 196 | ![](https://github.com/wide-vsix/linux-flow-exporter/actions/workflows/binary_branch.yaml/badge.svg)
197 | ![](https://github.com/wide-vsix/linux-flow-exporter/actions/workflows/binary_release.yaml/badge.svg)
198 | ![](https://github.com/wide-vsix/linux-flow-exporter/actions/workflows/image_branch.yaml/badge.svg)
199 | ![](https://github.com/wide-vsix/linux-flow-exporter/actions/workflows/image_release.yaml/badge.svg)
200 | 201 | You can use 202 | [automated build container images](https://github.com/wide-vsix/linux-flow-exporter/pkgs/container/linux-flow-exporter/versions?filters%5Bversion_type%5D=tagged) or 203 | [automated build elf-binary](https://github.com/wide-vsix/linux-flow-exporter/releases). 204 | Both are automatically created by updating main-branch and creating release. 205 | Please note that flowctl depends on clang, iproute2 and linux kernel. 206 | You can use `flowctl dependency-check` to check for dependency problems. 207 | 208 | for container (latest version) 209 | ``` 210 | docker run -it --rm --privileged --net=host \ 211 | -v /usr/include/asm:/usr/include/asm \ 212 | -v /var/run/netns:/var/run/netns \ 213 | ghcr.io/wide-vsix/linux-flow-exporter:branch-main bash 214 | flowctl version 215 | ``` 216 | 217 | for container (specified version) 218 | ``` 219 | docker run -it --rm --privileged --net=host \ 220 | -v /usr/include/asm:/usr/include/asm \ 221 | -v /var/run/netns:/var/run/netns \ 222 | ghcr.io/wide-vsix/linux-flow-exporter:v0.0.5 bash 223 | flowctl version 224 | ``` 225 | 226 | for binary (latest version) 227 | ``` 228 | curl -Lo /usr/local/bin/flowctl https://github.com/wide-vsix/linux-flow-exporter/releases/download/branch-main/flowctl.linux-amd64 229 | chmod +x /usr/local/bin/flowctl 230 | flowctl version 231 | ``` 232 | 233 | for binary (specified version) 234 | ``` 235 | curl -Lo /usr/local/bin/flowctl https://github.com/wide-vsix/linux-flow-exporter/releases/download/v0.0.5/flowctl.linux-amd64 236 | chmod +x /usr/local/bin/flowctl 237 | flowctl version 238 | ``` 239 | 240 | ## Licence 241 | 242 | The user space components are licensed under the 243 | [Apache License, Version 2.0](https://github.com/wide-vsix/linux-flow-exporter/blob/main/LICENSE). 244 | The BPF code templates are dual-licensed under the 245 | [General Public License](https://github.com/wide-vsix/linux-flow-exporter/blob/main/bpf/LICENSE.GPL-2.0), 246 | Version 2.0 (only) and the 247 | [2-Clause BSD License](https://github.com/wide-vsix/linux-flow-exporter/blob/main/bpf/LICENSE.BSD-2-Clause) 248 | (you can use the terms of either license, at your option). 249 | -------------------------------------------------------------------------------- /bpf/LICENSE.BSD-2-Clause: -------------------------------------------------------------------------------- 1 | Copyright Authors of Cilium. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /bpf/access.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_ACCESS_H_ 5 | #define __BPF_ACCESS_H_ 6 | 7 | #include "compiler.h" 8 | 9 | #if defined(__bpf__) 10 | static __always_inline __maybe_unused __u32 11 | map_array_get_32(const __u32 *array, __u32 index, const __u32 limit) 12 | { 13 | __u32 datum = 0; 14 | 15 | if (__builtin_constant_p(index) || 16 | !__builtin_constant_p(limit)) 17 | __throw_build_bug(); 18 | 19 | /* LLVM tends to optimize code away that is needed for the verifier to 20 | * understand dynamic map access. Input constraint is that index < limit 21 | * for this util function, so we never fail here, and returned datum is 22 | * always valid. 23 | */ 24 | asm volatile("%[index] <<= 2\n\t" 25 | "if %[index] > %[limit] goto +1\n\t" 26 | "%[array] += %[index]\n\t" 27 | "%[datum] = *(u32 *)(%[array] + 0)\n\t" 28 | : [datum]"=r"(datum) 29 | : [limit]"i"(limit), [array]"r"(array), [index]"r"(index) 30 | : /* no clobbers */ ); 31 | 32 | return datum; 33 | } 34 | #else 35 | # define map_array_get_32(array, index, limit) __throw_build_bug() 36 | #endif /* __bpf__ */ 37 | #endif /* __BPF_ACCESS_H_ */ 38 | -------------------------------------------------------------------------------- /bpf/api.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_API__ 5 | #define __BPF_API__ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "compiler.h" 13 | #include "section.h" 14 | #include "helpers.h" 15 | #include "builtins.h" 16 | #include "tailcall.h" 17 | #include "errno.h" 18 | #include "loader.h" 19 | #include "csum.h" 20 | #include "access.h" 21 | 22 | #endif /* __BPF_API__ */ 23 | -------------------------------------------------------------------------------- /bpf/bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * common eBPF ELF operations. 5 | * 6 | * Copyright (C) 2013-2015 Alexei Starovoitov 7 | * Copyright (C) 2015 Wang Nan 8 | * Copyright (C) 2015 Huawei Inc. 9 | * 10 | * This program is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; 13 | * version 2.1 of the License (not later!) 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this program; if not, see 22 | */ 23 | #ifndef __LIBBPF_BPF_H 24 | #define __LIBBPF_BPF_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "libbpf_common.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | struct bpf_create_map_attr { 38 | const char *name; 39 | enum bpf_map_type map_type; 40 | __u32 map_flags; 41 | __u32 key_size; 42 | __u32 value_size; 43 | __u32 max_entries; 44 | __u32 numa_node; 45 | __u32 btf_fd; 46 | __u32 btf_key_type_id; 47 | __u32 btf_value_type_id; 48 | __u32 map_ifindex; 49 | union { 50 | __u32 inner_map_fd; 51 | __u32 btf_vmlinux_value_type_id; 52 | }; 53 | }; 54 | 55 | LIBBPF_API int 56 | bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr); 57 | LIBBPF_API int bpf_create_map_node(enum bpf_map_type map_type, const char *name, 58 | int key_size, int value_size, 59 | int max_entries, __u32 map_flags, int node); 60 | LIBBPF_API int bpf_create_map_name(enum bpf_map_type map_type, const char *name, 61 | int key_size, int value_size, 62 | int max_entries, __u32 map_flags); 63 | LIBBPF_API int bpf_create_map(enum bpf_map_type map_type, int key_size, 64 | int value_size, int max_entries, __u32 map_flags); 65 | LIBBPF_API int bpf_create_map_in_map_node(enum bpf_map_type map_type, 66 | const char *name, int key_size, 67 | int inner_map_fd, int max_entries, 68 | __u32 map_flags, int node); 69 | LIBBPF_API int bpf_create_map_in_map(enum bpf_map_type map_type, 70 | const char *name, int key_size, 71 | int inner_map_fd, int max_entries, 72 | __u32 map_flags); 73 | 74 | struct bpf_load_program_attr { 75 | enum bpf_prog_type prog_type; 76 | enum bpf_attach_type expected_attach_type; 77 | const char *name; 78 | const struct bpf_insn *insns; 79 | size_t insns_cnt; 80 | const char *license; 81 | union { 82 | __u32 kern_version; 83 | __u32 attach_prog_fd; 84 | }; 85 | union { 86 | __u32 prog_ifindex; 87 | __u32 attach_btf_id; 88 | }; 89 | __u32 prog_btf_fd; 90 | __u32 func_info_rec_size; 91 | const void *func_info; 92 | __u32 func_info_cnt; 93 | __u32 line_info_rec_size; 94 | const void *line_info; 95 | __u32 line_info_cnt; 96 | __u32 log_level; 97 | __u32 prog_flags; 98 | }; 99 | 100 | /* Flags to direct loading requirements */ 101 | #define MAPS_RELAX_COMPAT 0x01 102 | 103 | /* Recommend log buffer size */ 104 | #define BPF_LOG_BUF_SIZE (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */ 105 | LIBBPF_API int 106 | bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, 107 | char *log_buf, size_t log_buf_sz); 108 | LIBBPF_API int bpf_load_program(enum bpf_prog_type type, 109 | const struct bpf_insn *insns, size_t insns_cnt, 110 | const char *license, __u32 kern_version, 111 | char *log_buf, size_t log_buf_sz); 112 | LIBBPF_API int bpf_verify_program(enum bpf_prog_type type, 113 | const struct bpf_insn *insns, 114 | size_t insns_cnt, __u32 prog_flags, 115 | const char *license, __u32 kern_version, 116 | char *log_buf, size_t log_buf_sz, 117 | int log_level); 118 | 119 | LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value, 120 | __u64 flags); 121 | 122 | LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value); 123 | LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, 124 | __u64 flags); 125 | LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key, 126 | void *value); 127 | LIBBPF_API int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, 128 | void *value, __u64 flags); 129 | LIBBPF_API int bpf_map_delete_elem(int fd, const void *key); 130 | LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key); 131 | LIBBPF_API int bpf_map_freeze(int fd); 132 | 133 | struct bpf_map_batch_opts { 134 | size_t sz; /* size of this struct for forward/backward compatibility */ 135 | __u64 elem_flags; 136 | __u64 flags; 137 | }; 138 | #define bpf_map_batch_opts__last_field flags 139 | 140 | LIBBPF_API int bpf_map_delete_batch(int fd, void *keys, 141 | __u32 *count, 142 | const struct bpf_map_batch_opts *opts); 143 | LIBBPF_API int bpf_map_lookup_batch(int fd, void *in_batch, void *out_batch, 144 | void *keys, void *values, __u32 *count, 145 | const struct bpf_map_batch_opts *opts); 146 | LIBBPF_API int bpf_map_lookup_and_delete_batch(int fd, void *in_batch, 147 | void *out_batch, void *keys, 148 | void *values, __u32 *count, 149 | const struct bpf_map_batch_opts *opts); 150 | LIBBPF_API int bpf_map_update_batch(int fd, void *keys, void *values, 151 | __u32 *count, 152 | const struct bpf_map_batch_opts *opts); 153 | 154 | LIBBPF_API int bpf_obj_pin(int fd, const char *pathname); 155 | LIBBPF_API int bpf_obj_get(const char *pathname); 156 | 157 | struct bpf_prog_attach_opts { 158 | size_t sz; /* size of this struct for forward/backward compatibility */ 159 | unsigned int flags; 160 | int replace_prog_fd; 161 | }; 162 | #define bpf_prog_attach_opts__last_field replace_prog_fd 163 | 164 | LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd, 165 | enum bpf_attach_type type, unsigned int flags); 166 | LIBBPF_API int bpf_prog_attach_xattr(int prog_fd, int attachable_fd, 167 | enum bpf_attach_type type, 168 | const struct bpf_prog_attach_opts *opts); 169 | LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); 170 | LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd, 171 | enum bpf_attach_type type); 172 | 173 | union bpf_iter_link_info; /* defined in up-to-date linux/bpf.h */ 174 | struct bpf_link_create_opts { 175 | size_t sz; /* size of this struct for forward/backward compatibility */ 176 | __u32 flags; 177 | union bpf_iter_link_info *iter_info; 178 | __u32 iter_info_len; 179 | __u32 target_btf_id; 180 | union { 181 | struct { 182 | __u64 bpf_cookie; 183 | } perf_event; 184 | }; 185 | size_t :0; 186 | }; 187 | #define bpf_link_create_opts__last_field perf_event 188 | 189 | LIBBPF_API int bpf_link_create(int prog_fd, int target_fd, 190 | enum bpf_attach_type attach_type, 191 | const struct bpf_link_create_opts *opts); 192 | 193 | LIBBPF_API int bpf_link_detach(int link_fd); 194 | 195 | struct bpf_link_update_opts { 196 | size_t sz; /* size of this struct for forward/backward compatibility */ 197 | __u32 flags; /* extra flags */ 198 | __u32 old_prog_fd; /* expected old program FD */ 199 | }; 200 | #define bpf_link_update_opts__last_field old_prog_fd 201 | 202 | LIBBPF_API int bpf_link_update(int link_fd, int new_prog_fd, 203 | const struct bpf_link_update_opts *opts); 204 | 205 | LIBBPF_API int bpf_iter_create(int link_fd); 206 | 207 | struct bpf_prog_test_run_attr { 208 | int prog_fd; 209 | int repeat; 210 | const void *data_in; 211 | __u32 data_size_in; 212 | void *data_out; /* optional */ 213 | __u32 data_size_out; /* in: max length of data_out 214 | * out: length of data_out */ 215 | __u32 retval; /* out: return code of the BPF program */ 216 | __u32 duration; /* out: average per repetition in ns */ 217 | const void *ctx_in; /* optional */ 218 | __u32 ctx_size_in; 219 | void *ctx_out; /* optional */ 220 | __u32 ctx_size_out; /* in: max length of ctx_out 221 | * out: length of cxt_out */ 222 | }; 223 | 224 | LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr); 225 | 226 | /* 227 | * bpf_prog_test_run does not check that data_out is large enough. Consider 228 | * using bpf_prog_test_run_xattr instead. 229 | */ 230 | LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data, 231 | __u32 size, void *data_out, __u32 *size_out, 232 | __u32 *retval, __u32 *duration); 233 | LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id); 234 | LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id); 235 | LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id); 236 | LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id); 237 | LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id); 238 | LIBBPF_API int bpf_map_get_fd_by_id(__u32 id); 239 | LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id); 240 | LIBBPF_API int bpf_link_get_fd_by_id(__u32 id); 241 | LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len); 242 | LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type, 243 | __u32 query_flags, __u32 *attach_flags, 244 | __u32 *prog_ids, __u32 *prog_cnt); 245 | LIBBPF_API int bpf_raw_tracepoint_open(const char *name, int prog_fd); 246 | LIBBPF_API int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf, 247 | __u32 log_buf_size, bool do_log); 248 | LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, 249 | __u32 *buf_len, __u32 *prog_id, __u32 *fd_type, 250 | __u64 *probe_offset, __u64 *probe_addr); 251 | 252 | enum bpf_stats_type; /* defined in up-to-date linux/bpf.h */ 253 | LIBBPF_API int bpf_enable_stats(enum bpf_stats_type type); 254 | 255 | struct bpf_prog_bind_opts { 256 | size_t sz; /* size of this struct for forward/backward compatibility */ 257 | __u32 flags; 258 | }; 259 | #define bpf_prog_bind_opts__last_field flags 260 | 261 | LIBBPF_API int bpf_prog_bind_map(int prog_fd, int map_fd, 262 | const struct bpf_prog_bind_opts *opts); 263 | 264 | struct bpf_test_run_opts { 265 | size_t sz; /* size of this struct for forward/backward compatibility */ 266 | const void *data_in; /* optional */ 267 | void *data_out; /* optional */ 268 | __u32 data_size_in; 269 | __u32 data_size_out; /* in: max length of data_out 270 | * out: length of data_out 271 | */ 272 | const void *ctx_in; /* optional */ 273 | void *ctx_out; /* optional */ 274 | __u32 ctx_size_in; 275 | __u32 ctx_size_out; /* in: max length of ctx_out 276 | * out: length of cxt_out 277 | */ 278 | __u32 retval; /* out: return code of the BPF program */ 279 | int repeat; 280 | __u32 duration; /* out: average per repetition in ns */ 281 | __u32 flags; 282 | __u32 cpu; 283 | }; 284 | #define bpf_test_run_opts__last_field cpu 285 | 286 | LIBBPF_API int bpf_prog_test_run_opts(int prog_fd, 287 | struct bpf_test_run_opts *opts); 288 | 289 | #ifdef __cplusplus 290 | } /* extern "C" */ 291 | #endif 292 | 293 | #endif /* __LIBBPF_BPF_H */ 294 | -------------------------------------------------------------------------------- /bpf/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_ENDIAN__ 3 | #define __BPF_ENDIAN__ 4 | 5 | /* 6 | * Isolate byte #n and put it into byte #m, for __u##b type. 7 | * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: 8 | * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 9 | * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 10 | * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 11 | * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 12 | */ 13 | #define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) 14 | 15 | #define ___bpf_swab16(x) ((__u16)( \ 16 | ___bpf_mvb(x, 16, 0, 1) | \ 17 | ___bpf_mvb(x, 16, 1, 0))) 18 | 19 | #define ___bpf_swab32(x) ((__u32)( \ 20 | ___bpf_mvb(x, 32, 0, 3) | \ 21 | ___bpf_mvb(x, 32, 1, 2) | \ 22 | ___bpf_mvb(x, 32, 2, 1) | \ 23 | ___bpf_mvb(x, 32, 3, 0))) 24 | 25 | #define ___bpf_swab64(x) ((__u64)( \ 26 | ___bpf_mvb(x, 64, 0, 7) | \ 27 | ___bpf_mvb(x, 64, 1, 6) | \ 28 | ___bpf_mvb(x, 64, 2, 5) | \ 29 | ___bpf_mvb(x, 64, 3, 4) | \ 30 | ___bpf_mvb(x, 64, 4, 3) | \ 31 | ___bpf_mvb(x, 64, 5, 2) | \ 32 | ___bpf_mvb(x, 64, 6, 1) | \ 33 | ___bpf_mvb(x, 64, 7, 0))) 34 | 35 | /* LLVM's BPF target selects the endianness of the CPU 36 | * it compiles on, or the user specifies (bpfel/bpfeb), 37 | * respectively. The used __BYTE_ORDER__ is defined by 38 | * the compiler, we cannot rely on __BYTE_ORDER from 39 | * libc headers, since it doesn't reflect the actual 40 | * requested byte order. 41 | * 42 | * Note, LLVM's BPF target has different __builtin_bswapX() 43 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 44 | * in bpfel and bpfeb case, which means below, that we map 45 | * to cpu_to_be16(). We could use it unconditionally in BPF 46 | * case, but better not rely on it, so that this header here 47 | * can be used from application and BPF program side, which 48 | * use different targets. 49 | */ 50 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 51 | # define __bpf_ntohs(x) __builtin_bswap16(x) 52 | # define __bpf_htons(x) __builtin_bswap16(x) 53 | # define __bpf_constant_ntohs(x) ___bpf_swab16(x) 54 | # define __bpf_constant_htons(x) ___bpf_swab16(x) 55 | # define __bpf_ntohl(x) __builtin_bswap32(x) 56 | # define __bpf_htonl(x) __builtin_bswap32(x) 57 | # define __bpf_constant_ntohl(x) ___bpf_swab32(x) 58 | # define __bpf_constant_htonl(x) ___bpf_swab32(x) 59 | # define __bpf_be64_to_cpu(x) __builtin_bswap64(x) 60 | # define __bpf_cpu_to_be64(x) __builtin_bswap64(x) 61 | # define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) 62 | # define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) 63 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 64 | # define __bpf_ntohs(x) (x) 65 | # define __bpf_htons(x) (x) 66 | # define __bpf_constant_ntohs(x) (x) 67 | # define __bpf_constant_htons(x) (x) 68 | # define __bpf_ntohl(x) (x) 69 | # define __bpf_htonl(x) (x) 70 | # define __bpf_constant_ntohl(x) (x) 71 | # define __bpf_constant_htonl(x) (x) 72 | # define __bpf_be64_to_cpu(x) (x) 73 | # define __bpf_cpu_to_be64(x) (x) 74 | # define __bpf_constant_be64_to_cpu(x) (x) 75 | # define __bpf_constant_cpu_to_be64(x) (x) 76 | #else 77 | # error "Fix your compiler's __BYTE_ORDER__?!" 78 | #endif 79 | 80 | #define bpf_htons(x) \ 81 | (__builtin_constant_p(x) ? \ 82 | __bpf_constant_htons(x) : __bpf_htons(x)) 83 | #define bpf_ntohs(x) \ 84 | (__builtin_constant_p(x) ? \ 85 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 86 | #define bpf_htonl(x) \ 87 | (__builtin_constant_p(x) ? \ 88 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 89 | #define bpf_ntohl(x) \ 90 | (__builtin_constant_p(x) ? \ 91 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 92 | #define bpf_cpu_to_be64(x) \ 93 | (__builtin_constant_p(x) ? \ 94 | __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) 95 | #define bpf_be64_to_cpu(x) \ 96 | (__builtin_constant_p(x) ? \ 97 | __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) 98 | 99 | #endif /* __BPF_ENDIAN__ */ 100 | -------------------------------------------------------------------------------- /bpf/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_HELPERS__ 3 | #define __BPF_HELPERS__ 4 | 5 | /* 6 | * Note that bpf programs need to include either 7 | * vmlinux.h (auto-generated from BTF) or linux/types.h 8 | * in advance since bpf_helper_defs.h uses such types 9 | * as __u64. 10 | */ 11 | #include "bpf_helper_defs.h" 12 | 13 | #define __uint(name, val) int (*name)[val] 14 | #define __type(name, val) typeof(val) *name 15 | #define __array(name, val) typeof(val) *name[] 16 | 17 | /* Helper macro to print out debug messages */ 18 | #define bpf_printk(fmt, ...) \ 19 | ({ \ 20 | char ____fmt[] = fmt; \ 21 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 22 | ##__VA_ARGS__); \ 23 | }) 24 | 25 | /* 26 | * Helper macro to place programs, maps, license in 27 | * different sections in elf_bpf file. Section names 28 | * are interpreted by libbpf depending on the context (BPF programs, BPF maps, 29 | * extern variables, etc). 30 | * To allow use of SEC() with externs (e.g., for extern .maps declarations), 31 | * make sure __attribute__((unused)) doesn't trigger compilation warning. 32 | */ 33 | #define SEC(name) \ 34 | _Pragma("GCC diagnostic push") \ 35 | _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ 36 | __attribute__((section(name), used)) \ 37 | _Pragma("GCC diagnostic pop") \ 38 | 39 | /* Avoid 'linux/stddef.h' definition of '__always_inline'. */ 40 | #undef __always_inline 41 | #define __always_inline inline __attribute__((always_inline)) 42 | 43 | #ifndef __noinline 44 | #define __noinline __attribute__((noinline)) 45 | #endif 46 | #ifndef __weak 47 | #define __weak __attribute__((weak)) 48 | #endif 49 | 50 | /* 51 | * Use __hidden attribute to mark a non-static BPF subprogram effectively 52 | * static for BPF verifier's verification algorithm purposes, allowing more 53 | * extensive and permissive BPF verification process, taking into account 54 | * subprogram's caller context. 55 | */ 56 | #define __hidden __attribute__((visibility("hidden"))) 57 | 58 | /* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include 59 | * any system-level headers (such as stddef.h, linux/version.h, etc), and 60 | * commonly-used macros like NULL and KERNEL_VERSION aren't available through 61 | * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define 62 | * them on their own. So as a convenience, provide such definitions here. 63 | */ 64 | #ifndef NULL 65 | #define NULL ((void *)0) 66 | #endif 67 | 68 | #ifndef KERNEL_VERSION 69 | #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) 70 | #endif 71 | 72 | /* 73 | * Helper macros to manipulate data structures 74 | */ 75 | #ifndef offsetof 76 | #define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) 77 | #endif 78 | #ifndef container_of 79 | #define container_of(ptr, type, member) \ 80 | ({ \ 81 | void *__mptr = (void *)(ptr); \ 82 | ((type *)(__mptr - offsetof(type, member))); \ 83 | }) 84 | #endif 85 | 86 | /* 87 | * Helper macro to throw a compilation error if __bpf_unreachable() gets 88 | * built into the resulting code. This works given BPF back end does not 89 | * implement __builtin_trap(). This is useful to assert that certain paths 90 | * of the program code are never used and hence eliminated by the compiler. 91 | * 92 | * For example, consider a switch statement that covers known cases used by 93 | * the program. __bpf_unreachable() can then reside in the default case. If 94 | * the program gets extended such that a case is not covered in the switch 95 | * statement, then it will throw a build error due to the default case not 96 | * being compiled out. 97 | */ 98 | #ifndef __bpf_unreachable 99 | # define __bpf_unreachable() __builtin_trap() 100 | #endif 101 | 102 | /* 103 | * Helper function to perform a tail call with a constant/immediate map slot. 104 | */ 105 | #if __clang_major__ >= 8 && defined(__bpf__) 106 | static __always_inline void 107 | bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) 108 | { 109 | if (!__builtin_constant_p(slot)) 110 | __bpf_unreachable(); 111 | 112 | /* 113 | * Provide a hard guarantee that LLVM won't optimize setting r2 (map 114 | * pointer) and r3 (constant map index) from _different paths_ ending 115 | * up at the _same_ call insn as otherwise we won't be able to use the 116 | * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel 117 | * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key 118 | * tracking for prog array pokes") for details on verifier tracking. 119 | * 120 | * Note on clobber list: we need to stay in-line with BPF calling 121 | * convention, so even if we don't end up using r0, r4, r5, we need 122 | * to mark them as clobber so that LLVM doesn't end up using them 123 | * before / after the call. 124 | */ 125 | asm volatile("r1 = %[ctx]\n\t" 126 | "r2 = %[map]\n\t" 127 | "r3 = %[slot]\n\t" 128 | "call 12" 129 | :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) 130 | : "r0", "r1", "r2", "r3", "r4", "r5"); 131 | } 132 | #endif 133 | 134 | /* 135 | * Helper structure used by eBPF C program 136 | * to describe BPF map attributes to libbpf loader 137 | */ 138 | struct bpf_map_def { 139 | unsigned int type; 140 | unsigned int key_size; 141 | unsigned int value_size; 142 | unsigned int max_entries; 143 | unsigned int map_flags; 144 | }; 145 | 146 | enum libbpf_pin_type { 147 | LIBBPF_PIN_NONE, 148 | /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ 149 | LIBBPF_PIN_BY_NAME, 150 | }; 151 | 152 | enum libbpf_tristate { 153 | TRI_NO = 0, 154 | TRI_YES = 1, 155 | TRI_MODULE = 2, 156 | }; 157 | 158 | #define __kconfig __attribute__((section(".kconfig"))) 159 | #define __ksym __attribute__((section(".ksyms"))) 160 | 161 | #ifndef ___bpf_concat 162 | #define ___bpf_concat(a, b) a ## b 163 | #endif 164 | #ifndef ___bpf_apply 165 | #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) 166 | #endif 167 | #ifndef ___bpf_nth 168 | #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N 169 | #endif 170 | #ifndef ___bpf_narg 171 | #define ___bpf_narg(...) \ 172 | ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 173 | #endif 174 | 175 | #define ___bpf_fill0(arr, p, x) do {} while (0) 176 | #define ___bpf_fill1(arr, p, x) arr[p] = x 177 | #define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) 178 | #define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) 179 | #define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) 180 | #define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) 181 | #define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) 182 | #define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) 183 | #define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) 184 | #define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) 185 | #define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) 186 | #define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) 187 | #define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) 188 | #define ___bpf_fill(arr, args...) \ 189 | ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) 190 | 191 | /* 192 | * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values 193 | * in a structure. 194 | */ 195 | #define BPF_SEQ_PRINTF(seq, fmt, args...) \ 196 | ({ \ 197 | static const char ___fmt[] = fmt; \ 198 | unsigned long long ___param[___bpf_narg(args)]; \ 199 | \ 200 | _Pragma("GCC diagnostic push") \ 201 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 202 | ___bpf_fill(___param, args); \ 203 | _Pragma("GCC diagnostic pop") \ 204 | \ 205 | bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ 206 | ___param, sizeof(___param)); \ 207 | }) 208 | 209 | /* 210 | * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of 211 | * an array of u64. 212 | */ 213 | #define BPF_SNPRINTF(out, out_size, fmt, args...) \ 214 | ({ \ 215 | static const char ___fmt[] = fmt; \ 216 | unsigned long long ___param[___bpf_narg(args)]; \ 217 | \ 218 | _Pragma("GCC diagnostic push") \ 219 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 220 | ___bpf_fill(___param, args); \ 221 | _Pragma("GCC diagnostic pop") \ 222 | \ 223 | bpf_snprintf(out, out_size, ___fmt, \ 224 | ___param, sizeof(___param)); \ 225 | }) 226 | 227 | #endif 228 | -------------------------------------------------------------------------------- /bpf/compiler.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_COMPILER_H_ 5 | #define __BPF_COMPILER_H_ 6 | 7 | # include "stddef.h" 8 | 9 | #ifndef __section 10 | # define __section(X) __attribute__((section(X), used)) 11 | #endif 12 | 13 | #ifndef __maybe_unused 14 | # define __maybe_unused __attribute__((__unused__)) 15 | #endif 16 | 17 | #ifndef offsetof 18 | # define offsetof(T, M) __builtin_offsetof(T, M) 19 | #endif 20 | 21 | #ifndef field_sizeof 22 | # define field_sizeof(T, M) sizeof((((T *)NULL)->M)) 23 | #endif 24 | 25 | #ifndef __packed 26 | # define __packed __attribute__((packed)) 27 | #endif 28 | 29 | #ifndef __nobuiltin 30 | # if __clang_major__ >= 10 31 | # define __nobuiltin(X) __attribute__((no_builtin(X))) 32 | # else 33 | # define __nobuiltin(X) 34 | # endif 35 | #endif 36 | 37 | #ifndef likely 38 | # define likely(X) __builtin_expect(!!(X), 1) 39 | #endif 40 | 41 | #ifndef unlikely 42 | # define unlikely(X) __builtin_expect(!!(X), 0) 43 | #endif 44 | 45 | #ifndef always_succeeds /* Mainly for documentation purpose. */ 46 | # define always_succeeds(X) likely(X) 47 | #endif 48 | 49 | #undef __always_inline /* stddef.h defines its own */ 50 | #define __always_inline inline __attribute__((always_inline)) 51 | 52 | #ifndef __stringify 53 | # define __stringify(X) #X 54 | #endif 55 | 56 | #ifndef __fetch 57 | # define __fetch(X) (__u32)(__u64)(&(X)) 58 | #endif 59 | 60 | #ifndef __aligned 61 | # define __aligned(X) __attribute__((aligned(X))) 62 | #endif 63 | 64 | #ifndef build_bug_on 65 | # define build_bug_on(E) ((void)sizeof(char[1 - 2*!!(E)])) 66 | #endif 67 | 68 | #ifndef __throw_build_bug 69 | # define __throw_build_bug() __builtin_trap() 70 | #endif 71 | 72 | #ifndef __printf 73 | # define __printf(X, Y) __attribute__((__format__(printf, X, Y))) 74 | #endif 75 | 76 | #ifndef barrier 77 | # define barrier() asm volatile("": : :"memory") 78 | #endif 79 | 80 | #ifndef barrier_data 81 | # define barrier_data(ptr) asm volatile("": :"r"(ptr) :"memory") 82 | #endif 83 | 84 | static __always_inline void bpf_barrier(void) 85 | { 86 | /* Workaround to avoid verifier complaint: 87 | * "dereference of modified ctx ptr R5 off=48+0, ctx+const is allowed, 88 | * ctx+const+const is not" 89 | */ 90 | barrier(); 91 | } 92 | 93 | #ifndef ARRAY_SIZE 94 | # define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) 95 | #endif 96 | 97 | #ifndef __READ_ONCE 98 | # define __READ_ONCE(X) (*(volatile typeof(X) *)&X) 99 | #endif 100 | 101 | #ifndef __WRITE_ONCE 102 | # define __WRITE_ONCE(X, V) (*(volatile typeof(X) *)&X) = (V) 103 | #endif 104 | 105 | /* {READ,WRITE}_ONCE() with verifier workaround via bpf_barrier(). */ 106 | 107 | #ifndef READ_ONCE 108 | # define READ_ONCE(X) \ 109 | ({ typeof(X) __val = __READ_ONCE(X); \ 110 | bpf_barrier(); \ 111 | __val; }) 112 | #endif 113 | 114 | #ifndef WRITE_ONCE 115 | # define WRITE_ONCE(X, V) \ 116 | ({ typeof(X) __val = (V); \ 117 | __WRITE_ONCE(X, __val); \ 118 | bpf_barrier(); \ 119 | __val; }) 120 | #endif 121 | 122 | #endif /* __BPF_COMPILER_H_ */ 123 | -------------------------------------------------------------------------------- /bpf/csum.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CSUM_H_ 5 | #define __BPF_CSUM_H_ 6 | 7 | #include "compiler.h" 8 | #include "helpers.h" 9 | 10 | static __always_inline __sum16 csum_fold(__wsum csum) 11 | { 12 | csum = (csum & 0xffff) + (csum >> 16); 13 | csum = (csum & 0xffff) + (csum >> 16); 14 | return (__sum16)~csum; 15 | } 16 | 17 | static __always_inline __wsum csum_unfold(__sum16 csum) 18 | { 19 | return (__wsum)csum; 20 | } 21 | 22 | static __always_inline __wsum csum_add(__wsum csum, __wsum addend) 23 | { 24 | csum += addend; 25 | return csum + (csum < addend); 26 | } 27 | 28 | static __always_inline __wsum csum_sub(__wsum csum, __wsum addend) 29 | { 30 | return csum_add(csum, ~addend); 31 | } 32 | 33 | static __always_inline __wsum csum_diff(const void *from, __u32 size_from, 34 | const void *to, __u32 size_to, 35 | __u32 seed) 36 | { 37 | if (__builtin_constant_p(size_from) && 38 | __builtin_constant_p(size_to)) { 39 | /* Optimizations for frequent hot-path cases that are tiny to just 40 | * inline into the code instead of calling more expensive helper. 41 | */ 42 | if (size_from == 4 && size_to == 4 && 43 | __builtin_constant_p(seed) && seed == 0) 44 | return csum_add(~(*(__u32 *)from), *(__u32 *)to); 45 | if (size_from == 4 && size_to == 4) 46 | return csum_add(seed, 47 | csum_add(~(*(__u32 *)from), 48 | *(__u32 *)to)); 49 | } 50 | 51 | return csum_diff_external(from, size_from, to, size_to, seed); 52 | } 53 | 54 | #endif /* __BPF_CSUM_H_ */ 55 | -------------------------------------------------------------------------------- /bpf/ctx/common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CTX_COMMON_H_ 5 | #define __BPF_CTX_COMMON_H_ 6 | 7 | #include 8 | #include 9 | 10 | #include "../compiler.h" 11 | #include "../errno.h" 12 | 13 | #define __ctx_skb 1 14 | #define __ctx_xdp 2 15 | 16 | static __always_inline void *ctx_data(const struct __ctx_buff *ctx) 17 | { 18 | return (void *)(unsigned long)ctx->data; 19 | } 20 | 21 | static __always_inline void *ctx_data_meta(const struct __ctx_buff *ctx) 22 | { 23 | return (void *)(unsigned long)ctx->data_meta; 24 | } 25 | 26 | static __always_inline void *ctx_data_end(const struct __ctx_buff *ctx) 27 | { 28 | return (void *)(unsigned long)ctx->data_end; 29 | } 30 | 31 | static __always_inline bool ctx_no_room(const void *needed, const void *limit) 32 | { 33 | return unlikely(needed > limit); 34 | } 35 | 36 | #endif /* __BPF_CTX_COMMON_H_ */ 37 | -------------------------------------------------------------------------------- /bpf/ctx/ctx.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CTX_CTX_H_ 5 | #define __BPF_CTX_CTX_H_ 6 | 7 | #ifndef __ctx_buff 8 | # error "No __ctx_buff context defined. Please either include 'bpf/ctx/skb.h' or 'bpf/ctx/xdp.h'." 9 | #endif 10 | 11 | #endif /* __BPF_CTX_CTX_H_ */ 12 | -------------------------------------------------------------------------------- /bpf/ctx/skb.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CTX_SKB_H_ 5 | #define __BPF_CTX_SKB_H_ 6 | 7 | #define __ctx_buff __sk_buff 8 | #define __ctx_is __ctx_skb 9 | 10 | #include "common.h" 11 | #include "../helpers_skb.h" 12 | 13 | #ifndef TC_ACT_OK 14 | # define TC_ACT_OK 0 15 | #endif 16 | 17 | #ifndef TC_ACT_SHOT 18 | # define TC_ACT_SHOT 2 19 | #endif 20 | 21 | #ifndef TC_ACT_REDIRECT 22 | # define TC_ACT_REDIRECT 7 23 | #endif 24 | 25 | #define CTX_ACT_OK TC_ACT_OK 26 | #define CTX_ACT_DROP TC_ACT_SHOT 27 | #define CTX_ACT_TX TC_ACT_REDIRECT 28 | #define CTX_ACT_REDIRECT TC_ACT_REDIRECT 29 | 30 | /* Discouraged since prologue will unclone full skb. */ 31 | #define CTX_DIRECT_WRITE_OK 0 32 | 33 | #define META_PIVOT field_sizeof(struct __sk_buff, cb) 34 | 35 | #define ctx_load_bytes skb_load_bytes 36 | #define ctx_store_bytes skb_store_bytes 37 | 38 | #define ctx_adjust_hroom skb_adjust_room 39 | 40 | #define ctx_change_type skb_change_type 41 | #define ctx_change_proto skb_change_proto 42 | #define ctx_change_tail skb_change_tail 43 | 44 | #define ctx_pull_data skb_pull_data 45 | 46 | #define ctx_get_tunnel_key skb_get_tunnel_key 47 | #define ctx_set_tunnel_key skb_set_tunnel_key 48 | 49 | #define ctx_event_output skb_event_output 50 | 51 | #define ctx_adjust_meta ({ -ENOTSUPP; }) 52 | 53 | /* Avoid expensive calls into the kernel flow dissector if it's not an L4 54 | * hash. We currently only use the hash for debugging. If needed later, we 55 | * can map it to BPF_FUNC(get_hash_recalc) to get the L4 hash. 56 | */ 57 | #define get_hash(ctx) ctx->hash 58 | #define get_hash_recalc(ctx) get_hash(ctx) 59 | 60 | static __always_inline __maybe_unused int 61 | ctx_redirect(const struct __sk_buff *ctx __maybe_unused, int ifindex, __u32 flags) 62 | { 63 | return redirect(ifindex, flags); 64 | } 65 | 66 | static __always_inline __maybe_unused int 67 | ctx_redirect_peer(const struct __sk_buff *ctx __maybe_unused, int ifindex, __u32 flags) 68 | { 69 | return redirect_peer(ifindex, flags); 70 | } 71 | 72 | static __always_inline __maybe_unused int 73 | ctx_adjust_troom(struct __sk_buff *ctx, const __s32 len_diff) 74 | { 75 | return skb_change_tail(ctx, ctx->len + len_diff, 0); 76 | } 77 | 78 | static __always_inline __maybe_unused __u64 79 | ctx_full_len(const struct __sk_buff *ctx) 80 | { 81 | return ctx->len; 82 | } 83 | 84 | static __always_inline __maybe_unused __u32 85 | ctx_wire_len(const struct __sk_buff *ctx) 86 | { 87 | return ctx->wire_len; 88 | } 89 | 90 | static __always_inline __maybe_unused void 91 | ctx_store_meta(struct __sk_buff *ctx, const __u32 off, __u32 data) 92 | { 93 | ctx->cb[off] = data; 94 | } 95 | 96 | static __always_inline __maybe_unused __u32 97 | ctx_load_meta(const struct __sk_buff *ctx, const __u32 off) 98 | { 99 | return ctx->cb[off]; 100 | } 101 | 102 | static __always_inline __maybe_unused __u16 103 | ctx_get_protocol(const struct __sk_buff *ctx) 104 | { 105 | return (__u16)ctx->protocol; 106 | } 107 | 108 | static __always_inline __maybe_unused __u32 109 | ctx_get_ifindex(const struct __sk_buff *ctx) 110 | { 111 | return ctx->ifindex; 112 | } 113 | 114 | #endif /* __BPF_CTX_SKB_H_ */ 115 | -------------------------------------------------------------------------------- /bpf/ctx/sock.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CTX_SOCK_H_ 5 | #define __BPF_CTX_SOCK_H_ 6 | 7 | #include "../helpers_sock.h" 8 | 9 | #define __ctx_sock bpf_sock_addr 10 | #undef ctx_event_output 11 | #define ctx_event_output sock_event_output 12 | 13 | #endif /* __BPF_CTX_SOCK_H_ */ 14 | -------------------------------------------------------------------------------- /bpf/ctx/unspec.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_CTX_UNSPEC_H_ 5 | #define __BPF_CTX_UNSPEC_H_ 6 | 7 | /* We do not care which context we need in this case, but it must be 8 | * something compilable, thus we reuse skb ctx here. 9 | */ 10 | 11 | #include "skb.h" 12 | 13 | #endif /* __BPF_CTX_UNSPEC_H_ */ 14 | -------------------------------------------------------------------------------- /bpf/errno.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_ERRNO__ 5 | #define __BPF_ERRNO__ 6 | 7 | /* Few basic errno codes as we don't want to include errno.h. */ 8 | 9 | #ifndef EPERM 10 | # define EPERM 1 11 | #endif 12 | #ifndef ENOENT 13 | # define ENOENT 2 14 | #endif 15 | #ifndef ENXIO 16 | # define ENXIO 6 17 | #endif 18 | #ifndef ENOMEM 19 | # define ENOMEM 12 20 | #endif 21 | #ifndef EFAULT 22 | # define EFAULT 14 23 | #endif 24 | #ifndef EINVAL 25 | # define EINVAL 22 26 | #endif 27 | #ifndef ENOTSUP 28 | # define ENOTSUP 95 29 | #endif 30 | #ifndef EADDRINUSE 31 | # define EADDRINUSE 98 32 | #endif 33 | #ifndef ENOTSUPP 34 | # define ENOTSUPP 524 35 | #endif 36 | 37 | #endif /* __BPF_ERRNO__ */ 38 | -------------------------------------------------------------------------------- /bpf/features.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef ____BPF_FEATURES____ 5 | #define ____BPF_FEATURES____ 6 | 7 | #include 8 | 9 | /* Neither skb nor xdp related features. */ 10 | 11 | /* Testing both here since both were added to the same kernel release 12 | * and we need to ensure both are enabled. 13 | */ 14 | #if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_get_netns_cookie) && \ 15 | HAVE_PROG_TYPE_HELPER(cgroup_sock, bpf_get_netns_cookie) 16 | # define BPF_HAVE_NETNS_COOKIE 1 17 | #endif 18 | 19 | #if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_get_socket_cookie) 20 | # define BPF_HAVE_SOCKET_COOKIE 1 21 | #endif 22 | 23 | #if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_jiffies64) && \ 24 | HAVE_PROG_TYPE_HELPER(cgroup_sock, bpf_jiffies64) && \ 25 | HAVE_PROG_TYPE_HELPER(sched_cls, bpf_jiffies64) && \ 26 | HAVE_PROG_TYPE_HELPER(xdp, bpf_jiffies64) 27 | # define BPF_HAVE_JIFFIES 1 28 | #endif 29 | 30 | #if HAVE_PROG_TYPE_HELPER(sched_cls, bpf_csum_level) 31 | # define BPF_HAVE_CSUM_LEVEL 1 32 | #endif 33 | 34 | #if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_sk_lookup_tcp) && \ 35 | HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_sk_lookup_udp) 36 | # define BPF_HAVE_SOCKET_LOOKUP 1 37 | #endif 38 | 39 | #if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_get_current_cgroup_id) 40 | # define BPF_HAVE_CGROUP_ID 1 41 | #endif 42 | 43 | #endif /* ____BPF_FEATURES____ */ 44 | -------------------------------------------------------------------------------- /bpf/features_skb.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_FEATURES_SKB__ 5 | #define __BPF_FEATURES_SKB__ 6 | 7 | #include "features.h" 8 | 9 | /* Only skb related features. */ 10 | 11 | #if HAVE_PROG_TYPE_HELPER(sched_cls, bpf_skb_change_tail) 12 | # define BPF_HAVE_CHANGE_TAIL 1 13 | #endif 14 | 15 | #if HAVE_PROG_TYPE_HELPER(sched_cls, bpf_fib_lookup) 16 | # define BPF_HAVE_FIB_LOOKUP 1 17 | #endif 18 | 19 | #endif /* __BPF_FEATURES_SKB__ */ 20 | -------------------------------------------------------------------------------- /bpf/features_xdp.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_FEATURES_XDP__ 5 | #define __BPF_FEATURES_XDP__ 6 | 7 | #include "features.h" 8 | 9 | /* Only xdp related features. */ 10 | 11 | #if HAVE_PROG_TYPE_HELPER(xdp, bpf_fib_lookup) 12 | # define BPF_HAVE_FIB_LOOKUP 1 13 | #endif 14 | 15 | #endif /* __BPF_FEATURES_XDP__ */ 16 | -------------------------------------------------------------------------------- /bpf/helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_HELPERS__ 5 | #define __BPF_HELPERS__ 6 | 7 | #include 8 | 9 | #include "ctx/ctx.h" 10 | #include "compiler.h" 11 | 12 | #ifndef BPF_FUNC 13 | # define BPF_FUNC(NAME, ...) \ 14 | (* NAME)(__VA_ARGS__) __maybe_unused = (void *)BPF_FUNC_##NAME 15 | #endif 16 | 17 | #ifndef BPF_STUB 18 | # define BPF_STUB(NAME, ...) \ 19 | (* NAME##__stub)(__VA_ARGS__) __maybe_unused = (void *)((__u32)-1) 20 | #endif 21 | 22 | #ifndef BPF_FUNC_REMAP 23 | # define BPF_FUNC_REMAP(NAME, ...) \ 24 | (* NAME)(__VA_ARGS__) __maybe_unused 25 | #endif 26 | 27 | #if __ctx_is == __ctx_skb 28 | # include "helpers_skb.h" 29 | #else 30 | # include "helpers_xdp.h" 31 | #endif 32 | 33 | /* Map access/manipulation */ 34 | static void *BPF_FUNC(map_lookup_elem, const void *map, const void *key); 35 | static int BPF_FUNC(map_update_elem, const void *map, const void *key, 36 | const void *value, __u32 flags); 37 | static int BPF_FUNC(map_delete_elem, const void *map, const void *key); 38 | 39 | /* Time access */ 40 | static __u64 BPF_FUNC(ktime_get_ns); 41 | static __u64 BPF_FUNC(ktime_get_boot_ns); 42 | static __u64 BPF_FUNC(jiffies64); 43 | #define jiffies jiffies64() 44 | 45 | /* We have cookies! ;-) */ 46 | static __sock_cookie BPF_FUNC(get_socket_cookie, void *ctx); 47 | static __net_cookie BPF_FUNC(get_netns_cookie, void *ctx); 48 | 49 | /* Legacy cgroups */ 50 | static __u32 BPF_FUNC(get_cgroup_classid); 51 | 52 | /* Debugging */ 53 | static __printf(1, 3) void 54 | BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...); 55 | 56 | /* Random numbers */ 57 | static __u32 BPF_FUNC(get_prandom_u32); 58 | 59 | /* Checksumming */ 60 | static int BPF_FUNC_REMAP(csum_diff_external, const void *from, __u32 size_from, 61 | const void *to, __u32 size_to, __u32 seed) = 62 | (void *)BPF_FUNC_csum_diff; 63 | 64 | /* Tail calls */ 65 | static void BPF_FUNC(tail_call, void *ctx, const void *map, __u32 index); 66 | 67 | /* System helpers */ 68 | static __u32 BPF_FUNC(get_smp_processor_id); 69 | 70 | /* Padded struct so the dmac at the end can be passed to another helper 71 | * e.g. as a map value buffer. Otherwise verifier will trip over it with 72 | * 'invalid indirect read from stack off'. 73 | */ 74 | struct bpf_fib_lookup_padded { 75 | struct bpf_fib_lookup l; 76 | __u8 pad[2]; 77 | }; 78 | 79 | /* Routing helpers */ 80 | static int BPF_FUNC(fib_lookup, void *ctx, struct bpf_fib_lookup *params, 81 | __u32 plen, __u32 flags); 82 | 83 | /* Sockops and SK_MSG helpers */ 84 | static int BPF_FUNC(sock_map_update, struct bpf_sock_ops *skops, void *map, 85 | __u32 key, __u64 flags); 86 | static int BPF_FUNC(sock_hash_update, struct bpf_sock_ops *skops, void *map, 87 | void *key, __u64 flags); 88 | static int BPF_FUNC(msg_redirect_hash, struct sk_msg_md *md, void *map, 89 | void *key, __u64 flags); 90 | 91 | /* Socket lookup helpers */ 92 | static struct bpf_sock *BPF_FUNC(sk_lookup_tcp, void *ctx, 93 | struct bpf_sock_tuple *tuple, __u32 tuple_size, 94 | __u64 netns, __u64 flags); 95 | static struct bpf_sock *BPF_FUNC(sk_lookup_udp, void *ctx, 96 | struct bpf_sock_tuple *tuple, __u32 tuple_size, 97 | __u64 netns, __u64 flags); 98 | 99 | /* Socket helpers, misc */ 100 | /* Remapped name to avoid clash with getsockopt(2) when included from 101 | * regular applications. 102 | */ 103 | static int BPF_FUNC_REMAP(get_socket_opt, void *ctx, int level, int optname, 104 | void *optval, int optlen) = 105 | (void *)BPF_FUNC_getsockopt; 106 | 107 | static __u64 BPF_FUNC(get_current_cgroup_id); 108 | 109 | #endif /* __BPF_HELPERS__ */ 110 | -------------------------------------------------------------------------------- /bpf/helpers_skb.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_HELPERS_SKB__ 5 | #define __BPF_HELPERS_SKB__ 6 | 7 | #include 8 | 9 | #include "compiler.h" 10 | #include "helpers.h" 11 | #include "features_skb.h" 12 | 13 | /* Only used helpers in Cilium go below. */ 14 | 15 | /* Packet redirection */ 16 | static int BPF_FUNC(redirect, int ifindex, __u32 flags); 17 | static int BPF_FUNC(redirect_neigh, int ifindex, struct bpf_redir_neigh *params, 18 | int plen, __u32 flags); 19 | static int BPF_FUNC(redirect_peer, int ifindex, __u32 flags); 20 | 21 | /* Packet manipulation */ 22 | static int BPF_FUNC(skb_load_bytes, struct __sk_buff *skb, __u32 off, 23 | void *to, __u32 len); 24 | static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, __u32 off, 25 | const void *from, __u32 len, __u32 flags); 26 | 27 | static int BPF_FUNC(l3_csum_replace, struct __sk_buff *skb, __u32 off, 28 | __u32 from, __u32 to, __u32 flags); 29 | static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, __u32 off, 30 | __u32 from, __u32 to, __u32 flags); 31 | 32 | static int BPF_FUNC(skb_adjust_room, struct __sk_buff *skb, __s32 len_diff, 33 | __u32 mode, __u64 flags); 34 | 35 | static int BPF_FUNC(skb_change_type, struct __sk_buff *skb, __u32 type); 36 | static int BPF_FUNC(skb_change_proto, struct __sk_buff *skb, __u32 proto, 37 | __u32 flags); 38 | static int BPF_FUNC(skb_change_tail, struct __sk_buff *skb, __u32 nlen, 39 | __u32 flags); 40 | static int BPF_FUNC(skb_change_head, struct __sk_buff *skb, __u32 head_room, 41 | __u64 flags); 42 | 43 | static int BPF_FUNC(skb_pull_data, struct __sk_buff *skb, __u32 len); 44 | 45 | /* Packet tunnel encap/decap */ 46 | static int BPF_FUNC(skb_get_tunnel_key, struct __sk_buff *skb, 47 | struct bpf_tunnel_key *to, __u32 size, __u32 flags); 48 | static int BPF_FUNC(skb_set_tunnel_key, struct __sk_buff *skb, 49 | const struct bpf_tunnel_key *from, __u32 size, 50 | __u32 flags); 51 | 52 | /* Events for user space */ 53 | static int BPF_FUNC_REMAP(skb_event_output, struct __sk_buff *skb, void *map, 54 | __u64 index, const void *data, __u32 size) = 55 | (void *)BPF_FUNC_perf_event_output; 56 | 57 | /* Socket lookup, assign, release */ 58 | static struct bpf_sock *BPF_FUNC(skc_lookup_tcp, struct __sk_buff *skb, 59 | struct bpf_sock_tuple *tuple, __u32 tuple_size, 60 | __u64 netns, __u64 flags); 61 | static int BPF_FUNC(sk_release, struct bpf_sock *sk); 62 | static int BPF_FUNC(sk_assign, struct __sk_buff *skb, struct bpf_sock *sk, 63 | __u64 flags); 64 | 65 | #endif /* __BPF_HELPERS_SKB__ */ 66 | -------------------------------------------------------------------------------- /bpf/helpers_sock.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_HELPERS_SOCK__ 5 | #define __BPF_HELPERS_SOCK__ 6 | 7 | #include 8 | 9 | #include "helpers.h" 10 | 11 | /* Only used helpers in Cilium go below. */ 12 | 13 | /* Events for user space */ 14 | static int BPF_FUNC_REMAP(sock_event_output, struct bpf_sock_addr *sock, void *map, 15 | __u64 index, const void *data, __u32 size) = 16 | (void *)BPF_FUNC_perf_event_output; 17 | 18 | #endif /* __BPF_HELPERS_SOCK__ */ 19 | 20 | -------------------------------------------------------------------------------- /bpf/helpers_xdp.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_HELPERS_XDP__ 5 | #define __BPF_HELPERS_XDP__ 6 | 7 | #include 8 | 9 | #include "compiler.h" 10 | #include "helpers.h" 11 | #include "features_xdp.h" 12 | 13 | /* Only used helpers in Cilium go below. */ 14 | 15 | /* Packet misc meta data & encapsulation helper */ 16 | static int BPF_FUNC(xdp_adjust_meta, struct xdp_md *xdp, int delta); 17 | static int BPF_FUNC(xdp_adjust_head, struct xdp_md *xdp, int delta); 18 | static int BPF_FUNC(xdp_adjust_tail, struct xdp_md *xdp, int delta); 19 | 20 | /* Packet redirection */ 21 | static int BPF_FUNC(redirect, int ifindex, __u32 flags); 22 | 23 | /* Packet manipulation */ 24 | static int BPF_STUB(xdp_load_bytes, struct xdp_md *xdp, __u32 off, 25 | void *to, __u32 len); 26 | static int BPF_STUB(xdp_store_bytes, struct xdp_md *xdp, __u32 off, 27 | const void *from, __u32 len, __u32 flags); 28 | 29 | static int BPF_STUB(l3_csum_replace, struct xdp_md *xdp, __u32 off, 30 | __u32 from, __u32 to, __u32 flags); 31 | static int BPF_STUB(l4_csum_replace, struct xdp_md *xdp, __u32 off, 32 | __u32 from, __u32 to, __u32 flags); 33 | 34 | static int BPF_STUB(xdp_adjust_room, struct xdp_md *xdp, __s32 len_diff, 35 | __u32 mode, __u64 flags); 36 | 37 | static int BPF_STUB(xdp_change_type, struct xdp_md *xdp, __u32 type); 38 | static int BPF_STUB(xdp_change_proto, struct xdp_md *xdp, __u32 proto, 39 | __u32 flags); 40 | static int BPF_STUB(xdp_change_tail, struct xdp_md *xdp, __u32 nlen, 41 | __u32 flags); 42 | 43 | /* Packet tunnel encap/decap */ 44 | static int BPF_STUB(xdp_get_tunnel_key, struct xdp_md *xdp, 45 | struct bpf_tunnel_key *to, __u32 size, __u32 flags); 46 | static int BPF_STUB(xdp_set_tunnel_key, struct xdp_md *xdp, 47 | const struct bpf_tunnel_key *from, __u32 size, 48 | __u32 flags); 49 | 50 | /* Events for user space */ 51 | static int BPF_FUNC_REMAP(xdp_event_output, struct xdp_md *xdp, void *map, 52 | __u64 index, const void *data, __u32 size) = 53 | (void *)BPF_FUNC_perf_event_output; 54 | 55 | #endif /* __BPF_HELPERS_XDP__ */ 56 | -------------------------------------------------------------------------------- /bpf/libbpf_common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * Common user-facing libbpf helpers. 5 | * 6 | * Copyright (c) 2019 Facebook 7 | */ 8 | 9 | #ifndef __LIBBPF_LIBBPF_COMMON_H 10 | #define __LIBBPF_LIBBPF_COMMON_H 11 | 12 | #include 13 | 14 | #ifndef LIBBPF_API 15 | #define LIBBPF_API __attribute__((visibility("default"))) 16 | #endif 17 | 18 | #define LIBBPF_DEPRECATED(msg) __attribute__((deprecated(msg))) 19 | 20 | /* Helper macro to declare and initialize libbpf options struct 21 | * 22 | * This dance with uninitialized declaration, followed by memset to zero, 23 | * followed by assignment using compound literal syntax is done to preserve 24 | * ability to use a nice struct field initialization syntax and **hopefully** 25 | * have all the padding bytes initialized to zero. It's not guaranteed though, 26 | * when copying literal, that compiler won't copy garbage in literal's padding 27 | * bytes, but that's the best way I've found and it seems to work in practice. 28 | * 29 | * Macro declares opts struct of given type and name, zero-initializes, 30 | * including any extra padding, it with memset() and then assigns initial 31 | * values provided by users in struct initializer-syntax as varargs. 32 | */ 33 | #define DECLARE_LIBBPF_OPTS(TYPE, NAME, ...) \ 34 | struct TYPE NAME = ({ \ 35 | memset(&NAME, 0, sizeof(struct TYPE)); \ 36 | (struct TYPE) { \ 37 | .sz = sizeof(struct TYPE), \ 38 | __VA_ARGS__ \ 39 | }; \ 40 | }) 41 | 42 | #endif /* __LIBBPF_LIBBPF_COMMON_H */ 43 | -------------------------------------------------------------------------------- /bpf/libbpf_legacy.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * Libbpf legacy APIs (either discouraged or deprecated, as mentioned in [0]) 5 | * 6 | * [0] https://docs.google.com/document/d/1UyjTZuPFWiPFyKk1tV5an11_iaRuec6U-ZESZ54nNTY 7 | * 8 | * Copyright (C) 2021 Facebook 9 | */ 10 | #ifndef __LIBBPF_LEGACY_BPF_H 11 | #define __LIBBPF_LEGACY_BPF_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "libbpf_common.h" 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | enum libbpf_strict_mode { 24 | /* Turn on all supported strict features of libbpf to simulate libbpf 25 | * v1.0 behavior. 26 | * This will be the default behavior in libbpf v1.0. 27 | */ 28 | LIBBPF_STRICT_ALL = 0xffffffff, 29 | 30 | /* 31 | * Disable any libbpf 1.0 behaviors. This is the default before libbpf 32 | * v1.0. It won't be supported anymore in v1.0, please update your 33 | * code so that it handles LIBBPF_STRICT_ALL mode before libbpf v1.0. 34 | */ 35 | LIBBPF_STRICT_NONE = 0x00, 36 | /* 37 | * Return NULL pointers on error, not ERR_PTR(err). 38 | * Additionally, libbpf also always sets errno to corresponding Exx 39 | * (positive) error code. 40 | */ 41 | LIBBPF_STRICT_CLEAN_PTRS = 0x01, 42 | /* 43 | * Return actual error codes from low-level APIs directly, not just -1. 44 | * Additionally, libbpf also always sets errno to corresponding Exx 45 | * (positive) error code. 46 | */ 47 | LIBBPF_STRICT_DIRECT_ERRS = 0x02, 48 | 49 | __LIBBPF_STRICT_LAST, 50 | }; 51 | 52 | LIBBPF_API int libbpf_set_strict_mode(enum libbpf_strict_mode mode); 53 | 54 | 55 | #ifdef __cplusplus 56 | } /* extern "C" */ 57 | #endif 58 | 59 | #endif /* __LIBBPF_LEGACY_BPF_H */ 60 | -------------------------------------------------------------------------------- /bpf/loader.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_LOADER__ 5 | #define __BPF_LOADER__ 6 | 7 | #include 8 | 9 | #define __uint(name, val) int(*(name))[val] 10 | #define __type(name, val) typeof(val) *(name) 11 | #define __array(name, val) typeof(val) *(name)[] 12 | 13 | #define PIN_NONE 0 14 | #define PIN_OBJECT_NS 1 15 | #define PIN_GLOBAL_NS 2 16 | 17 | #define LIBBPF_PIN_BY_NAME 1 18 | 19 | struct bpf_elf_map { 20 | __u32 type; 21 | __u32 size_key; 22 | __u32 size_value; 23 | __u32 max_elem; 24 | __u32 flags; 25 | __u32 id; 26 | __u32 pinning; 27 | __u32 inner_id; 28 | __u32 inner_idx; 29 | }; 30 | 31 | #endif /* __BPF_LOADER__ */ 32 | -------------------------------------------------------------------------------- /bpf/section.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_SECTION__ 5 | #define __BPF_SECTION__ 6 | 7 | #include "compiler.h" 8 | 9 | #ifndef __section_tail 10 | # define __section_tail(ID, KEY) __section(__stringify(ID) "/" __stringify(KEY)) 11 | #endif 12 | 13 | #ifndef __section_license 14 | # define __section_license __section("license") 15 | #endif 16 | 17 | #ifndef __section_maps 18 | # define __section_maps __section("maps") 19 | #endif 20 | 21 | #ifndef __section_maps_btf 22 | # define __section_maps_btf __section(".maps") 23 | #endif 24 | 25 | #ifndef BPF_LICENSE 26 | # define BPF_LICENSE(NAME) \ 27 | char ____license[] __section_license = NAME 28 | #endif 29 | 30 | #endif /* __BPF_SECTION__ */ 31 | -------------------------------------------------------------------------------- /bpf/skel_internal.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2021 Facebook */ 3 | #ifndef __SKEL_INTERNAL_H 4 | #define __SKEL_INTERNAL_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* This file is a base header for auto-generated *.lskel.h files. 11 | * Its contents will change and may become part of auto-generation in the future. 12 | * 13 | * The layout of bpf_[map|prog]_desc and bpf_loader_ctx is feature dependent 14 | * and will change from one version of libbpf to another and features 15 | * requested during loader program generation. 16 | */ 17 | struct bpf_map_desc { 18 | union { 19 | /* input for the loader prog */ 20 | struct { 21 | __aligned_u64 initial_value; 22 | __u32 max_entries; 23 | }; 24 | /* output of the loader prog */ 25 | struct { 26 | int map_fd; 27 | }; 28 | }; 29 | }; 30 | struct bpf_prog_desc { 31 | int prog_fd; 32 | }; 33 | 34 | struct bpf_loader_ctx { 35 | size_t sz; 36 | __u32 log_level; 37 | __u32 log_size; 38 | __u64 log_buf; 39 | }; 40 | 41 | struct bpf_load_and_run_opts { 42 | struct bpf_loader_ctx *ctx; 43 | const void *data; 44 | const void *insns; 45 | __u32 data_sz; 46 | __u32 insns_sz; 47 | const char *errstr; 48 | }; 49 | 50 | static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, 51 | unsigned int size) 52 | { 53 | return syscall(__NR_bpf, cmd, attr, size); 54 | } 55 | 56 | static inline int skel_closenz(int fd) 57 | { 58 | if (fd > 0) 59 | return close(fd); 60 | return -EINVAL; 61 | } 62 | 63 | static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) 64 | { 65 | int map_fd = -1, prog_fd = -1, key = 0, err; 66 | union bpf_attr attr; 67 | 68 | map_fd = bpf_create_map_name(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, 69 | opts->data_sz, 1, 0); 70 | if (map_fd < 0) { 71 | opts->errstr = "failed to create loader map"; 72 | err = -errno; 73 | goto out; 74 | } 75 | 76 | err = bpf_map_update_elem(map_fd, &key, opts->data, 0); 77 | if (err < 0) { 78 | opts->errstr = "failed to update loader map"; 79 | err = -errno; 80 | goto out; 81 | } 82 | 83 | memset(&attr, 0, sizeof(attr)); 84 | attr.prog_type = BPF_PROG_TYPE_SYSCALL; 85 | attr.insns = (long) opts->insns; 86 | attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn); 87 | attr.license = (long) "Dual BSD/GPL"; 88 | memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 89 | attr.fd_array = (long) &map_fd; 90 | attr.log_level = opts->ctx->log_level; 91 | attr.log_size = opts->ctx->log_size; 92 | attr.log_buf = opts->ctx->log_buf; 93 | attr.prog_flags = BPF_F_SLEEPABLE; 94 | prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); 95 | if (prog_fd < 0) { 96 | opts->errstr = "failed to load loader prog"; 97 | err = -errno; 98 | goto out; 99 | } 100 | 101 | memset(&attr, 0, sizeof(attr)); 102 | attr.test.prog_fd = prog_fd; 103 | attr.test.ctx_in = (long) opts->ctx; 104 | attr.test.ctx_size_in = opts->ctx->sz; 105 | err = skel_sys_bpf(BPF_PROG_RUN, &attr, sizeof(attr)); 106 | if (err < 0 || (int)attr.test.retval < 0) { 107 | opts->errstr = "failed to execute loader prog"; 108 | if (err < 0) 109 | err = -errno; 110 | else 111 | err = (int)attr.test.retval; 112 | goto out; 113 | } 114 | err = 0; 115 | out: 116 | if (map_fd >= 0) 117 | close(map_fd); 118 | if (prog_fd >= 0) 119 | close(prog_fd); 120 | return err; 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /bpf/stddef.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef NULL 5 | #define NULL ((void *)0) 6 | #endif 7 | 8 | #ifndef __BPF_STDDEF_H_ 9 | #define __BPF_STDDEF_H_ 10 | 11 | 12 | #define bool _Bool 13 | 14 | enum { 15 | false = 0, 16 | true = 1, 17 | }; 18 | 19 | #endif /* __BPF_STDDEF_H_ */ 20 | -------------------------------------------------------------------------------- /bpf/tailcall.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_TAILCALL_H_ 5 | #define __BPF_TAILCALL_H_ 6 | 7 | #include "compiler.h" 8 | 9 | #if defined(__bpf__) 10 | static __always_inline __maybe_unused void 11 | tail_call_static(const struct __ctx_buff *ctx, const void *map, 12 | const __u32 slot) 13 | { 14 | if (!__builtin_constant_p(slot)) 15 | __throw_build_bug(); 16 | 17 | /* Don't gamble, but _guarantee_ that LLVM won't optimize setting 18 | * r2 and r3 from different paths ending up at the same call insn as 19 | * otherwise we won't be able to use the jmpq/nopl retpoline-free 20 | * patching by the x86-64 JIT in the kernel. 21 | * 22 | * Note on clobber list: we need to stay in-line with BPF calling 23 | * convention, so even if we don't end up using r0, r4, r5, we need 24 | * to mark them as clobber so that LLVM doesn't end up using them 25 | * before / after the call. 26 | */ 27 | asm volatile("r1 = %[ctx]\n\t" 28 | "r2 = %[map]\n\t" 29 | "r3 = %[slot]\n\t" 30 | "call 12\n\t" 31 | :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) 32 | : "r0", "r1", "r2", "r3", "r4", "r5"); 33 | } 34 | 35 | static __always_inline __maybe_unused void 36 | tail_call_dynamic(struct __ctx_buff *ctx, const void *map, __u32 slot) 37 | { 38 | if (__builtin_constant_p(slot)) 39 | __throw_build_bug(); 40 | 41 | /* Only for the case where slot is not known at compilation time, 42 | * we give LLVM a free pass to optimize since we cannot do much 43 | * here anyway as x86-64 JIT will emit a retpoline for this case. 44 | */ 45 | tail_call(ctx, map, slot); 46 | } 47 | #else 48 | /* BPF unit tests compile some BPF code under their native arch. Tail calls 49 | * won't work in this context. Only compile above under __bpf__ target. 50 | */ 51 | # define tail_call_static(ctx, map, slot) __throw_build_bug() 52 | # define tail_call_dynamic(ctx, map, slot) __throw_build_bug() 53 | #endif /* __bpf__ */ 54 | #endif /* __BPF_TAILCALL_H_ */ 55 | -------------------------------------------------------------------------------- /bpf/types_mapper.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_TYPES_MAPPER__ 5 | #define __BPF_TYPES_MAPPER__ 6 | 7 | typedef __signed__ char __s8; 8 | typedef unsigned char __u8; 9 | 10 | typedef __signed__ short __s16; 11 | typedef unsigned short __u16; 12 | 13 | typedef __signed__ int __s32; 14 | typedef unsigned int __u32; 15 | 16 | typedef __signed__ long long __s64; 17 | typedef unsigned long long __u64; 18 | 19 | typedef __u16 __le16; 20 | typedef __u16 __be16; 21 | 22 | typedef __u32 __le32; 23 | typedef __u32 __be32; 24 | 25 | typedef __u64 __le64; 26 | typedef __u64 __be64; 27 | 28 | typedef __u16 __sum16; 29 | typedef __u32 __wsum; 30 | 31 | typedef __u64 __aligned_u64; 32 | 33 | typedef __u64 __net_cookie; 34 | typedef __u64 __sock_cookie; 35 | 36 | #endif /* __BPF_TYPES_MAPPER__ */ 37 | -------------------------------------------------------------------------------- /bpf/verifier.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* Copyright Authors of Cilium */ 3 | 4 | #ifndef __BPF_VERIFIER__ 5 | #define __BPF_VERIFIER__ 6 | 7 | #include "compiler.h" 8 | 9 | /* relax_verifier is a dummy helper call to introduce a pruning checkpoint 10 | * to help relax the verifier to avoid reaching complexity limits on older 11 | * kernels. 12 | */ 13 | static __always_inline void relax_verifier(void) 14 | { 15 | #ifndef HAVE_LARGE_INSN_LIMIT 16 | volatile int __maybe_unused id = get_smp_processor_id(); 17 | #endif 18 | } 19 | 20 | #endif /* __BPF_VERIFIER__ */ 21 | -------------------------------------------------------------------------------- /bpf/xsk.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * AF_XDP user-space access library. 5 | * 6 | * Copyright (c) 2018 - 2019 Intel Corporation. 7 | * Copyright (c) 2019 Facebook 8 | * 9 | * Author(s): Magnus Karlsson 10 | */ 11 | 12 | #ifndef __LIBBPF_XSK_H 13 | #define __LIBBPF_XSK_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "libbpf.h" 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | /* Load-Acquire Store-Release barriers used by the XDP socket 27 | * library. The following macros should *NOT* be considered part of 28 | * the xsk.h API, and is subject to change anytime. 29 | * 30 | * LIBRARY INTERNAL 31 | */ 32 | 33 | #define __XSK_READ_ONCE(x) (*(volatile typeof(x) *)&x) 34 | #define __XSK_WRITE_ONCE(x, v) (*(volatile typeof(x) *)&x) = (v) 35 | 36 | #if defined(__i386__) || defined(__x86_64__) 37 | # define libbpf_smp_store_release(p, v) \ 38 | do { \ 39 | asm volatile("" : : : "memory"); \ 40 | __XSK_WRITE_ONCE(*p, v); \ 41 | } while (0) 42 | # define libbpf_smp_load_acquire(p) \ 43 | ({ \ 44 | typeof(*p) ___p1 = __XSK_READ_ONCE(*p); \ 45 | asm volatile("" : : : "memory"); \ 46 | ___p1; \ 47 | }) 48 | #elif defined(__aarch64__) 49 | # define libbpf_smp_store_release(p, v) \ 50 | asm volatile ("stlr %w1, %0" : "=Q" (*p) : "r" (v) : "memory") 51 | # define libbpf_smp_load_acquire(p) \ 52 | ({ \ 53 | typeof(*p) ___p1; \ 54 | asm volatile ("ldar %w0, %1" \ 55 | : "=r" (___p1) : "Q" (*p) : "memory"); \ 56 | ___p1; \ 57 | }) 58 | #elif defined(__riscv) 59 | # define libbpf_smp_store_release(p, v) \ 60 | do { \ 61 | asm volatile ("fence rw,w" : : : "memory"); \ 62 | __XSK_WRITE_ONCE(*p, v); \ 63 | } while (0) 64 | # define libbpf_smp_load_acquire(p) \ 65 | ({ \ 66 | typeof(*p) ___p1 = __XSK_READ_ONCE(*p); \ 67 | asm volatile ("fence r,rw" : : : "memory"); \ 68 | ___p1; \ 69 | }) 70 | #endif 71 | 72 | #ifndef libbpf_smp_store_release 73 | #define libbpf_smp_store_release(p, v) \ 74 | do { \ 75 | __sync_synchronize(); \ 76 | __XSK_WRITE_ONCE(*p, v); \ 77 | } while (0) 78 | #endif 79 | 80 | #ifndef libbpf_smp_load_acquire 81 | #define libbpf_smp_load_acquire(p) \ 82 | ({ \ 83 | typeof(*p) ___p1 = __XSK_READ_ONCE(*p); \ 84 | __sync_synchronize(); \ 85 | ___p1; \ 86 | }) 87 | #endif 88 | 89 | /* LIBRARY INTERNAL -- END */ 90 | 91 | /* Do not access these members directly. Use the functions below. */ 92 | #define DEFINE_XSK_RING(name) \ 93 | struct name { \ 94 | __u32 cached_prod; \ 95 | __u32 cached_cons; \ 96 | __u32 mask; \ 97 | __u32 size; \ 98 | __u32 *producer; \ 99 | __u32 *consumer; \ 100 | void *ring; \ 101 | __u32 *flags; \ 102 | } 103 | 104 | DEFINE_XSK_RING(xsk_ring_prod); 105 | DEFINE_XSK_RING(xsk_ring_cons); 106 | 107 | /* For a detailed explanation on the memory barriers associated with the 108 | * ring, please take a look at net/xdp/xsk_queue.h. 109 | */ 110 | 111 | struct xsk_umem; 112 | struct xsk_socket; 113 | 114 | static inline __u64 *xsk_ring_prod__fill_addr(struct xsk_ring_prod *fill, 115 | __u32 idx) 116 | { 117 | __u64 *addrs = (__u64 *)fill->ring; 118 | 119 | return &addrs[idx & fill->mask]; 120 | } 121 | 122 | static inline const __u64 * 123 | xsk_ring_cons__comp_addr(const struct xsk_ring_cons *comp, __u32 idx) 124 | { 125 | const __u64 *addrs = (const __u64 *)comp->ring; 126 | 127 | return &addrs[idx & comp->mask]; 128 | } 129 | 130 | static inline struct xdp_desc *xsk_ring_prod__tx_desc(struct xsk_ring_prod *tx, 131 | __u32 idx) 132 | { 133 | struct xdp_desc *descs = (struct xdp_desc *)tx->ring; 134 | 135 | return &descs[idx & tx->mask]; 136 | } 137 | 138 | static inline const struct xdp_desc * 139 | xsk_ring_cons__rx_desc(const struct xsk_ring_cons *rx, __u32 idx) 140 | { 141 | const struct xdp_desc *descs = (const struct xdp_desc *)rx->ring; 142 | 143 | return &descs[idx & rx->mask]; 144 | } 145 | 146 | static inline int xsk_ring_prod__needs_wakeup(const struct xsk_ring_prod *r) 147 | { 148 | return *r->flags & XDP_RING_NEED_WAKEUP; 149 | } 150 | 151 | static inline __u32 xsk_prod_nb_free(struct xsk_ring_prod *r, __u32 nb) 152 | { 153 | __u32 free_entries = r->cached_cons - r->cached_prod; 154 | 155 | if (free_entries >= nb) 156 | return free_entries; 157 | 158 | /* Refresh the local tail pointer. 159 | * cached_cons is r->size bigger than the real consumer pointer so 160 | * that this addition can be avoided in the more frequently 161 | * executed code that computs free_entries in the beginning of 162 | * this function. Without this optimization it whould have been 163 | * free_entries = r->cached_prod - r->cached_cons + r->size. 164 | */ 165 | r->cached_cons = libbpf_smp_load_acquire(r->consumer); 166 | r->cached_cons += r->size; 167 | 168 | return r->cached_cons - r->cached_prod; 169 | } 170 | 171 | static inline __u32 xsk_cons_nb_avail(struct xsk_ring_cons *r, __u32 nb) 172 | { 173 | __u32 entries = r->cached_prod - r->cached_cons; 174 | 175 | if (entries == 0) { 176 | r->cached_prod = libbpf_smp_load_acquire(r->producer); 177 | entries = r->cached_prod - r->cached_cons; 178 | } 179 | 180 | return (entries > nb) ? nb : entries; 181 | } 182 | 183 | static inline __u32 xsk_ring_prod__reserve(struct xsk_ring_prod *prod, __u32 nb, __u32 *idx) 184 | { 185 | if (xsk_prod_nb_free(prod, nb) < nb) 186 | return 0; 187 | 188 | *idx = prod->cached_prod; 189 | prod->cached_prod += nb; 190 | 191 | return nb; 192 | } 193 | 194 | static inline void xsk_ring_prod__submit(struct xsk_ring_prod *prod, __u32 nb) 195 | { 196 | /* Make sure everything has been written to the ring before indicating 197 | * this to the kernel by writing the producer pointer. 198 | */ 199 | libbpf_smp_store_release(prod->producer, *prod->producer + nb); 200 | } 201 | 202 | static inline __u32 xsk_ring_cons__peek(struct xsk_ring_cons *cons, __u32 nb, __u32 *idx) 203 | { 204 | __u32 entries = xsk_cons_nb_avail(cons, nb); 205 | 206 | if (entries > 0) { 207 | *idx = cons->cached_cons; 208 | cons->cached_cons += entries; 209 | } 210 | 211 | return entries; 212 | } 213 | 214 | static inline void xsk_ring_cons__cancel(struct xsk_ring_cons *cons, __u32 nb) 215 | { 216 | cons->cached_cons -= nb; 217 | } 218 | 219 | static inline void xsk_ring_cons__release(struct xsk_ring_cons *cons, __u32 nb) 220 | { 221 | /* Make sure data has been read before indicating we are done 222 | * with the entries by updating the consumer pointer. 223 | */ 224 | libbpf_smp_store_release(cons->consumer, *cons->consumer + nb); 225 | 226 | } 227 | 228 | static inline void *xsk_umem__get_data(void *umem_area, __u64 addr) 229 | { 230 | return &((char *)umem_area)[addr]; 231 | } 232 | 233 | static inline __u64 xsk_umem__extract_addr(__u64 addr) 234 | { 235 | return addr & XSK_UNALIGNED_BUF_ADDR_MASK; 236 | } 237 | 238 | static inline __u64 xsk_umem__extract_offset(__u64 addr) 239 | { 240 | return addr >> XSK_UNALIGNED_BUF_OFFSET_SHIFT; 241 | } 242 | 243 | static inline __u64 xsk_umem__add_offset_to_addr(__u64 addr) 244 | { 245 | return xsk_umem__extract_addr(addr) + xsk_umem__extract_offset(addr); 246 | } 247 | 248 | LIBBPF_API int xsk_umem__fd(const struct xsk_umem *umem); 249 | LIBBPF_API int xsk_socket__fd(const struct xsk_socket *xsk); 250 | 251 | #define XSK_RING_CONS__DEFAULT_NUM_DESCS 2048 252 | #define XSK_RING_PROD__DEFAULT_NUM_DESCS 2048 253 | #define XSK_UMEM__DEFAULT_FRAME_SHIFT 12 /* 4096 bytes */ 254 | #define XSK_UMEM__DEFAULT_FRAME_SIZE (1 << XSK_UMEM__DEFAULT_FRAME_SHIFT) 255 | #define XSK_UMEM__DEFAULT_FRAME_HEADROOM 0 256 | #define XSK_UMEM__DEFAULT_FLAGS 0 257 | 258 | struct xsk_umem_config { 259 | __u32 fill_size; 260 | __u32 comp_size; 261 | __u32 frame_size; 262 | __u32 frame_headroom; 263 | __u32 flags; 264 | }; 265 | 266 | LIBBPF_API int xsk_setup_xdp_prog(int ifindex, 267 | int *xsks_map_fd); 268 | LIBBPF_API int xsk_socket__update_xskmap(struct xsk_socket *xsk, 269 | int xsks_map_fd); 270 | 271 | /* Flags for the libbpf_flags field. */ 272 | #define XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD (1 << 0) 273 | 274 | struct xsk_socket_config { 275 | __u32 rx_size; 276 | __u32 tx_size; 277 | __u32 libbpf_flags; 278 | __u32 xdp_flags; 279 | __u16 bind_flags; 280 | }; 281 | 282 | /* Set config to NULL to get the default configuration. */ 283 | LIBBPF_API int xsk_umem__create(struct xsk_umem **umem, 284 | void *umem_area, __u64 size, 285 | struct xsk_ring_prod *fill, 286 | struct xsk_ring_cons *comp, 287 | const struct xsk_umem_config *config); 288 | LIBBPF_API int xsk_umem__create_v0_0_2(struct xsk_umem **umem, 289 | void *umem_area, __u64 size, 290 | struct xsk_ring_prod *fill, 291 | struct xsk_ring_cons *comp, 292 | const struct xsk_umem_config *config); 293 | LIBBPF_API int xsk_umem__create_v0_0_4(struct xsk_umem **umem, 294 | void *umem_area, __u64 size, 295 | struct xsk_ring_prod *fill, 296 | struct xsk_ring_cons *comp, 297 | const struct xsk_umem_config *config); 298 | LIBBPF_API int xsk_socket__create(struct xsk_socket **xsk, 299 | const char *ifname, __u32 queue_id, 300 | struct xsk_umem *umem, 301 | struct xsk_ring_cons *rx, 302 | struct xsk_ring_prod *tx, 303 | const struct xsk_socket_config *config); 304 | LIBBPF_API int 305 | xsk_socket__create_shared(struct xsk_socket **xsk_ptr, 306 | const char *ifname, 307 | __u32 queue_id, struct xsk_umem *umem, 308 | struct xsk_ring_cons *rx, 309 | struct xsk_ring_prod *tx, 310 | struct xsk_ring_prod *fill, 311 | struct xsk_ring_cons *comp, 312 | const struct xsk_socket_config *config); 313 | 314 | /* Returns 0 for success and -EBUSY if the umem is still in use. */ 315 | LIBBPF_API int xsk_umem__delete(struct xsk_umem *umem); 316 | LIBBPF_API void xsk_socket__delete(struct xsk_socket *xsk); 317 | 318 | #ifdef __cplusplus 319 | } /* extern "C" */ 320 | #endif 321 | 322 | #endif /* __LIBBPF_XSK_H */ 323 | -------------------------------------------------------------------------------- /cmd/flowctl/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package main 20 | 21 | import ( 22 | "math/rand" 23 | "os" 24 | "time" 25 | 26 | "github.com/wide-vsix/linux-flow-exporter/pkg/flowctl" 27 | ) 28 | 29 | func main() { 30 | rand.Seed(time.Now().UnixNano()) 31 | if err := flowctl.NewCommand().Execute(); err != nil { 32 | os.Exit(1) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cmd/flowctl/sub.mk: -------------------------------------------------------------------------------- 1 | flowctl-build: 2 | CGO_ENABLED=0 go build -o bin/flowctl cmd/flowctl/main.go 3 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | maxIpfixMessageLen: 100 2 | timerFinishedDrainSeconds: 5 3 | timerForceDrainSeconds: 30 4 | timerTemplateFlushSeconds: 60 5 | 6 | ## FUTURE PLAN?? 7 | # flowMetering: 8 | # interfaces: 9 | # - name: eth1 10 | # netns: ns0 11 | # - name: eth2 12 | # netns: ns0 13 | # - name: eth3 14 | # netns: ns0 15 | 16 | outputs: 17 | - collector: 18 | remoteAddress: 35.194.97.218:2055 19 | localAddress: 0.0.0.0:50101 20 | - collector: 21 | remoteAddress: localhost:2100 22 | localAddress: 0.0.0.0:50102 23 | # HELPER 24 | # nfcapd -w -l /tmp/netflow -t 1000 -p 2100 25 | # nfdump -r /tmp/netflow/nfcapd.202207101030 -o extended 26 | - log: 27 | file: /tmp/flow.log 28 | # hooks: 29 | # - name: hostname addition 30 | # command: /usr/bin/hook_command_example_hostname.sh 31 | # - name: shell to resolve hostname 32 | # shell: | 33 | # #!/bin/sh 34 | # echo `cat` | jq --arg hostname $(hostname) '. + {hostname: $hostname}' 35 | # - name: shell to resolve ifname from ifindex 36 | # shell: | 37 | # #!/bin/sh 38 | # IN=$(cat) 39 | # I_IDX=$(echo $IN | jq .ingressIfindex -r) 40 | # E_IDX=$(echo $IN | jq .egressIfindex -r ) 41 | # I_NAME=$(ip -n ns0 -j link | jq --argjson idx $I_IDX '.[] | select(.ifindex == $idx) | .ifname' -r) 42 | # E_NAME=$(ip -n ns0 -j link | jq --argjson idx $E_IDX '.[] | select(.ifindex == $idx) | .ifname' -r) 43 | # echo $IN | jq --arg i_name $I_NAME --arg e_name $E_NAME '. + {ingressIfname: $i_name, egressIfname: $e_name}' 44 | templates: 45 | - id: 1001 46 | template: 47 | - name: SourceIPv4Address 48 | - name: DestinationIPv4Address 49 | - name: ProtocolIdentifier 50 | - name: OctetDeltaCount 51 | - name: PacketDeltaCount 52 | - id: 1002 53 | template: 54 | - name: SourceIPv4Address 55 | - name: DestinationIPv4Address 56 | - name: ProtocolIdentifier 57 | - name: SourceTransportPort 58 | - name: DestinationTransportPort 59 | - name: IngressInterface 60 | - name: OctetDeltaCount 61 | - name: PacketDeltaCount 62 | - id: 1004 63 | template: 64 | - name: SourceIPv4Address 65 | - name: DestinationIPv4Address 66 | - name: ProtocolIdentifier 67 | - name: SourceTransportPort 68 | - name: DestinationTransportPort 69 | - name: IngressInterface 70 | - name: OctetDeltaCount 71 | - name: PacketDeltaCount 72 | - name: FlowStartMilliseconds 73 | - name: FlowEndMilliseconds 74 | - id: 1005 75 | template: 76 | - name: SourceIPv4Address 77 | - name: DestinationIPv4Address 78 | - name: ProtocolIdentifier 79 | - name: SourceTransportPort 80 | - name: DestinationTransportPort 81 | - name: IngressInterface 82 | - name: OctetDeltaCount 83 | - name: PacketDeltaCount 84 | - name: FlowStartNanoseconds 85 | - name: FlowEndNanoseconds 86 | -------------------------------------------------------------------------------- /examples/agent_output_hook/config.yaml: -------------------------------------------------------------------------------- 1 | maxIpfixMessageLen: 100 2 | timerFinishedDrainSeconds: 1 3 | timerForceDrainSeconds: 30 4 | timerTemplateFlushSeconds: 60 5 | outputs: 6 | - log: 7 | file: /tmp/flowlog.json 8 | hooks: 9 | - shell: | 10 | #!/bin/sh 11 | jq '[.[] | . + { 12 | foo1:"foo1", 13 | foo2:"foo2", 14 | foo3:"foo3", 15 | foo4:"foo4", 16 | foo5:"foo5", 17 | foo6:"foo6", 18 | foo7:"foo7", 19 | foo8:"foo8" 20 | }]' 21 | -------------------------------------------------------------------------------- /examples/agent_output_hook/flowlog.json: -------------------------------------------------------------------------------- 1 | { 2 | "level": "info", 3 | "ts": 1667266821.7434478, 4 | "caller": "flowctl/ipfix.go:430", 5 | "msg": "flowlog", 6 | "bytes": 594, 7 | "finished": 1, 8 | "foo": "bar", 9 | "pkts": 6, 10 | "proto": 6, 11 | "start": 12168400776634952, 12 | "action": 0, 13 | "ingressIfindex": 3, 14 | "dport": 32816, 15 | "egressIfindex": 2, 16 | "hostname": "slankdev", 17 | "src": "10.2.0.2", 18 | "ingressIfname": "eth2", 19 | "egressIfname": "eth1", 20 | "dst": "10.1.0.2", 21 | "sport": 8080, 22 | "end": 12168400778056844 23 | } 24 | { 25 | "level": "info", 26 | "ts": 1667266821.9629853, 27 | "caller": "flowctl/ipfix.go:430", 28 | "msg": "flowlog", 29 | "dport": 8080, 30 | "dst": "10.2.0.2", 31 | "proto": 6, 32 | "src": "10.1.0.2", 33 | "finished": 1, 34 | "sport": 32816, 35 | "start": 12168400776608754, 36 | "ingressIfname": "eth1", 37 | "egressIfindex": 3, 38 | "foo": "bar", 39 | "egressIfname": "eth2", 40 | "pkts": 6, 41 | "action": 0, 42 | "bytes": 481, 43 | "end": 12168400778037424, 44 | "hostname": "slankdev", 45 | "ingressIfindex": 2 46 | } 47 | -------------------------------------------------------------------------------- /examples/dummy_flow_test/README.md: -------------------------------------------------------------------------------- 1 | # Dummy Flow test 2 | 3 | ```shell 4 | $ cd /path/to/linux-flow-exporter 5 | $ flowctl ipfix file -c ./examples/dummy_flow_test/config.yaml -f ./examples/dummy_flow_test/flow.yaml 6 | ``` 7 | -------------------------------------------------------------------------------- /examples/dummy_flow_test/config.yaml: -------------------------------------------------------------------------------- 1 | maxIpfixMessageLen: 100 2 | collectors: 3 | - remoteAddress: 35.194.97.218:2055 4 | localAddress: 0.0.0.0:50101 5 | templates: 6 | - id: 1001 7 | template: 8 | - name: SourceIPv4Address 9 | - name: DestinationIPv4Address 10 | - name: ProtocolIdentifier 11 | - name: PacketDeltaCount 12 | - id: 1002 13 | template: 14 | - name: SourceIPv4Address 15 | - name: DestinationIPv4Address 16 | - name: ProtocolIdentifier 17 | - name: SourceTransportPort 18 | - name: DestinationTransportPort 19 | - name: PacketDeltaCount 20 | -------------------------------------------------------------------------------- /examples/dummy_flow_test/flow.yaml: -------------------------------------------------------------------------------- 1 | flowsets: 2 | - templateId: 1001 3 | flows: 4 | - SourceIPv4Address: 0x0a010001 5 | DestinationIPv4Address: 0x0a010002 6 | ProtocolIdentifier: 6 7 | SourceTransportPort: 9999 8 | DestinationTransportPort: 22 9 | PacketDeltaCount: 100 10 | - SourceIPv4Address: 0x0a010001 11 | DestinationIPv4Address: 0x0a010002 12 | ProtocolIdentifier: 6 13 | SourceTransportPort: 9999 14 | DestinationTransportPort: 22 15 | PacketDeltaCount: 100 16 | - SourceIPv4Address: 0x0a010001 17 | DestinationIPv4Address: 0x0a010002 18 | ProtocolIdentifier: 6 19 | SourceTransportPort: 9999 20 | DestinationTransportPort: 22 21 | PacketDeltaCount: 100 22 | - SourceIPv4Address: 0x0a010001 23 | DestinationIPv4Address: 0x0a010002 24 | ProtocolIdentifier: 6 25 | SourceTransportPort: 9999 26 | DestinationTransportPort: 22 27 | PacketDeltaCount: 100 28 | - SourceIPv4Address: 0x0a010001 29 | DestinationIPv4Address: 0x0a010002 30 | ProtocolIdentifier: 6 31 | SourceTransportPort: 9999 32 | DestinationTransportPort: 22 33 | PacketDeltaCount: 100 34 | - SourceIPv4Address: 0x0a010001 35 | DestinationIPv4Address: 0x0a010002 36 | ProtocolIdentifier: 6 37 | SourceTransportPort: 9999 38 | DestinationTransportPort: 22 39 | PacketDeltaCount: 100 40 | - templateId: 1002 41 | flows: 42 | - SourceIPv4Address: 0x0a010001 43 | DestinationIPv4Address: 0x0a010003 44 | ProtocolIdentifier: 6 45 | SourceTransportPort: 8888 46 | DestinationTransportPort: 22 47 | PacketDeltaCount: 1000 48 | - SourceIPv4Address: 0x0a010001 49 | DestinationIPv4Address: 0x0a010003 50 | ProtocolIdentifier: 6 51 | SourceTransportPort: 8888 52 | DestinationTransportPort: 22 53 | PacketDeltaCount: 1000 54 | - SourceIPv4Address: 0x0a010001 55 | DestinationIPv4Address: 0x0a010003 56 | ProtocolIdentifier: 6 57 | SourceTransportPort: 8888 58 | DestinationTransportPort: 22 59 | PacketDeltaCount: 1000 60 | - SourceIPv4Address: 0x0a010001 61 | DestinationIPv4Address: 0x0a010003 62 | ProtocolIdentifier: 6 63 | SourceTransportPort: 8888 64 | DestinationTransportPort: 22 65 | PacketDeltaCount: 1000 66 | - SourceIPv4Address: 0x0a010001 67 | DestinationIPv4Address: 0x0a010003 68 | ProtocolIdentifier: 6 69 | SourceTransportPort: 8888 70 | DestinationTransportPort: 22 71 | PacketDeltaCount: 1000 72 | - SourceIPv4Address: 0x0a010001 73 | DestinationIPv4Address: 0x0a010003 74 | ProtocolIdentifier: 6 75 | SourceTransportPort: 8888 76 | DestinationTransportPort: 22 77 | PacketDeltaCount: 1000 78 | - SourceIPv4Address: 0x0a010001 79 | DestinationIPv4Address: 0x0a010003 80 | ProtocolIdentifier: 6 81 | SourceTransportPort: 8888 82 | DestinationTransportPort: 22 83 | PacketDeltaCount: 1000 84 | -------------------------------------------------------------------------------- /examples/dummy_flow_test/out.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wide-vsix/linux-flow-exporter/ddfb6dc36495808af8b54392213596cc92a0f2f6/examples/dummy_flow_test/out.pcap -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wide-vsix/linux-flow-exporter 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.9.0 7 | github.com/cloudflare/goflow v2.1.0+incompatible 8 | github.com/fatih/color v1.13.0 9 | github.com/go-logr/logr v1.2.3 10 | github.com/go-logr/zapr v1.2.3 11 | github.com/hairyhenderson/go-which v0.2.0 12 | github.com/olekukonko/tablewriter v0.0.5 13 | github.com/prometheus/client_golang v1.13.0 14 | github.com/spf13/cobra v1.5.0 15 | github.com/zcalusic/sysinfo v0.9.5 16 | go.uber.org/zap v1.21.0 17 | golang.org/x/mod v0.4.2 18 | gopkg.in/yaml.v2 v2.4.0 19 | ) 20 | 21 | require ( 22 | github.com/beorn7/perks v1.0.1 // indirect 23 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 24 | github.com/golang/protobuf v1.5.2 // indirect 25 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 26 | github.com/mattn/go-colorable v0.1.12 // indirect 27 | github.com/mattn/go-isatty v0.0.14 // indirect 28 | github.com/mattn/go-runewidth v0.0.9 // indirect 29 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect 30 | github.com/prometheus/client_model v0.2.0 // indirect 31 | github.com/prometheus/common v0.37.0 // indirect 32 | github.com/prometheus/procfs v0.8.0 // indirect 33 | github.com/spf13/afero v1.3.3 // indirect 34 | github.com/spf13/pflag v1.0.5 // indirect 35 | go.uber.org/atomic v1.7.0 // indirect 36 | go.uber.org/goleak v1.1.12 // indirect 37 | go.uber.org/multierr v1.6.0 // indirect 38 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 39 | golang.org/x/text v0.3.7 // indirect 40 | google.golang.org/protobuf v1.28.1 // indirect 41 | gopkg.in/yaml.v3 v3.0.1 // indirect 42 | ) 43 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/README.md: -------------------------------------------------------------------------------- 1 | ## Benchmarking 2 | 3 | If we use linux-flow-exporter without fine-tuning, it may not perform well. For 4 | example, if you use multiple hooks, each hooks do the memory-copy and 5 | command-execution for each, which may slow down the performance. The following 6 | figure shows a specific example of slow performance depending on the number of 7 | hook chains. 8 | 9 | ![n_hook_performance](./n_hook_performance.png) 10 | 11 | There are two potential causes, one is caused by the hook type and the other is 12 | caused by the hook execution. There are two hook types, shell and command, and 13 | since command only executes and script generates tempolary shell-script file on 14 | demand. Therefore, if file operations are the primary cause, it should be 15 | improved by simply changing shell to command. As you can see from the figure, 16 | these are not affected. It seems realistic to improve the performance by 17 | improving the hook execution. 18 | 19 | The number of hooks can be reduced by using a single command, although the 20 | number of pipes remains the same, but the number of child command executions can 21 | be reduced. We show how much performance can be improved by combining the above 22 | tasks into a single hook. Furthermore, if we are using jq or other such queries 23 | in a split format, aggregating the queries themselves may also improve the 24 | performance by reducing the number of pipes. The following figure shows how much 25 | performance can be improved by aggregating multiple hooks into single hook. 26 | 27 | ![optimization](./optimization.png) 28 | 29 | The goal of the linux-flow-exporter is to process 6M flows per second. This 30 | number is set based on the session processing performance of commercial firewall 31 | products, assuming that the number of flowlog discharge opportunities This 32 | number is set based on the session processing performance of commercial firewall 33 | products when assuming that the number of flowlog discharge opportunities is 34 | about the same as the number of new sessions. (Note that session and flow are 35 | different units.) 36 | 37 | * Reference: Paloalto Firewall appliance spec 38 | * PA5280 new session: 3.5M session/sec 39 | * PA5280 max sessions: 100M session 40 | * PA-7080 new session: 6M session/sec 41 | * PA-7080 max sessions: 416M session 42 | * [Reference: Hitachi Solutions Paloalto Catalog](https://www.hitachi-solutions.co.jp/paloalto/sp/download/thanks/pr01_6746.html) 43 | * [Reference: Paloalto Spec Sheet](https://www.paloaltonetworks.com/resources/datasheets/product-summary-specsheet) 44 | 45 | To improve this performance issue, change the frequency of hook execution that 46 | it will be independent of the number of flow-data. The following figure shows 47 | the performance difference as the number of flows increases. By introducing 48 | batch hook mode, it is really improved. 98+% latency is eliminated. 49 | 50 | ![n_flow_performance](./n_flow_performance.png) 51 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/benchmark_logs.md: -------------------------------------------------------------------------------- 1 | ## Appendix 2 | 3 | test command 4 | ``` 5 | > iperf3 -c 10.2.0.2 -P128 -t5 6 | ``` 7 | 8 | fixed configuration 9 | ``` 10 | maxIpfixMessageLen: 100 11 | timerFinishedDrainSeconds: 1 12 | timerForceDrainSeconds: 100000 13 | timerTemplateFlushSeconds: 100000 14 | ``` 15 | 16 | fixed commands 17 | ``` 18 | #!/bin/sh 19 | mkdir -p /var/run/flowctl 20 | echo "#!/bin/sh\njq '. + {foo1: \"foo1\"}'" > /var/run/flowctl/hook1.sh 21 | echo "#!/bin/sh\njq '. + {foo2: \"foo2\"}'" > /var/run/flowctl/hook2.sh 22 | echo "#!/bin/sh\njq '. + {foo3: \"foo3\"}'" > /var/run/flowctl/hook3.sh 23 | echo "#!/bin/sh\njq '. + {foo4: \"foo4\"}'" > /var/run/flowctl/hook4.sh 24 | echo "#!/bin/sh\njq '. + {foo5: \"foo5\"}'" > /var/run/flowctl/hook5.sh 25 | echo "#!/bin/sh\njq '. + {foo6: \"foo6\"}'" > /var/run/flowctl/hook6.sh 26 | echo "#!/bin/sh\njq '. + {foo7: \"foo7\"}'" > /var/run/flowctl/hook7.sh 27 | echo "#!/bin/sh\njq '. + {foo8: \"foo8\"}'" > /var/run/flowctl/hook8.sh 28 | cat < /var/run/flowctl/hook_all1.sh 29 | #!/bin/sh 30 | jq '. + {foo1: "foo1"}' \ 31 | | jq '. + {foo2: "foo2"}' \ 32 | | jq '. + {foo3: "foo3"}' \ 33 | | jq '. + {foo4: "foo4"}' \ 34 | | jq '. + {foo5: "foo5"}' \ 35 | | jq '. + {foo6: "foo6"}' \ 36 | | jq '. + {foo7: "foo7"}' \ 37 | | jq '. + {foo8: "foo8"}' 38 | EOF 39 | cat < /var/run/flowctl/hook_all2.sh 40 | #!/bin/sh 41 | jq '.+{foo1:"foo1",foo2:"foo2",foo3:"foo3",foo4:"foo4",foo5:"foo5",foo6:"foo6",foo7:"foo7",foo8:"foo8"}' 42 | EOF 43 | chmod +x /var/run/flowctl/hook*.sh 44 | ``` 45 | 46 | ``` 47 | outputs: 48 | - log: 49 | file: /tmp/flowlog.json 50 | 51 | {"level":"info","msg":"drain finished flows","usec":4618,"nFlows":258} 52 | {"level":"info","msg":"drain finished flows","usec":5086,"nFlows":258} 53 | {"level":"info","msg":"drain finished flows","usec":7614,"nFlows":258} 54 | ``` 55 | 56 | ``` 57 | outputs: 58 | - log: 59 | file: /tmp/flowlog.json 60 | hooks: 61 | - shell: "#!/bin/sh\njq '. + {foo1: \"foo1\"}'" 62 | 63 | {"level":"info","msg":"drain finished flows","usec":8347875,"nFlows":258} 64 | {"level":"info","msg":"drain finished flows","usec":8343217,"nFlows":258} 65 | {"level":"info","msg":"drain finished flows","usec":8315474,"nFlows":258} 66 | ``` 67 | 68 | ``` 69 | outputs: 70 | - log: 71 | file: /tmp/flowlog.json 72 | hooks: 73 | - shell: "#!/bin/sh\njq '. + {foo1: \"foo1\"}'" 74 | - shell: "#!/bin/sh\njq '. + {foo2: \"foo2\"}'" 75 | 76 | {"level":"info","msg":"drain finished flows","usec":16466085,"nFlows":258} 77 | {"level":"info","msg":"drain finished flows","usec":16467925,"nFlows":258} 78 | {"level":"info","msg":"drain finished flows","usec":16415695,"nFlows":258} 79 | ``` 80 | 81 | ``` 82 | outputs: 83 | - log: 84 | file: /tmp/flowlog.json 85 | hooks: 86 | - shell: "#!/bin/sh\njq '. + {foo1: \"foo1\"}'" 87 | - shell: "#!/bin/sh\njq '. + {foo2: \"foo2\"}'" 88 | - shell: "#!/bin/sh\njq '. + {foo3: \"foo3\"}'" 89 | - shell: "#!/bin/sh\njq '. + {foo4: \"foo4\"}'" 90 | - shell: "#!/bin/sh\njq '. + {foo5: \"foo5\"}'" 91 | - shell: "#!/bin/sh\njq '. + {foo6: \"foo6\"}'" 92 | - shell: "#!/bin/sh\njq '. + {foo7: \"foo7\"}'" 93 | - shell: "#!/bin/sh\njq '. + {foo8: \"foo8\"}'" 94 | 95 | 96 | {"level":"info","msg":"drain finished flows","usec":65414095,"nFlows":258} 97 | {"level":"info","msg":"drain finished flows","usec":65104397,"nFlows":258} 98 | {"level":"info","msg":"drain finished flows","usec":65380369,"nFlows":258} 99 | ``` 100 | 101 | ``` 102 | outputs: 103 | - log: 104 | file: /tmp/flowlog.json 105 | hooks: 106 | - command: /var/run/flowctl/hook1.sh 107 | 108 | {"level":"info","msg":"drain finished flows","usec":8116680,"nFlows":258} 109 | {"level":"info","msg":"drain finished flows","usec":8073259,"nFlows":258} 110 | {"level":"info","msg":"drain finished flows","usec":8144323,"nFlows":258} 111 | ``` 112 | 113 | ``` 114 | outputs: 115 | - log: 116 | file: /tmp/flowlog.json 117 | hooks: 118 | - command: /var/run/flowctl/hook1.sh 119 | - command: /var/run/flowctl/hook2.sh 120 | 121 | 122 | {"level":"info","msg":"drain finished flows","usec":16311184,"nFlows":258} 123 | {"level":"info","msg":"drain finished flows","usec":16360520,"nFlows":258} 124 | {"level":"info","msg":"drain finished flows","usec":16176635,"nFlows":258} 125 | ``` 126 | 127 | ``` 128 | outputs: 129 | - log: 130 | file: /tmp/flowlog.json 131 | hooks: 132 | - command: /var/run/flowctl/hook1.sh 133 | - command: /var/run/flowctl/hook2.sh 134 | - command: /var/run/flowctl/hook3.sh 135 | - command: /var/run/flowctl/hook4.sh 136 | - command: /var/run/flowctl/hook5.sh 137 | - command: /var/run/flowctl/hook6.sh 138 | - command: /var/run/flowctl/hook7.sh 139 | - command: /var/run/flowctl/hook8.sh 140 | 141 | {"level":"info","msg":"drain finished flows","usec":64781848,"nFlows":258} 142 | {"level":"info","msg":"drain finished flows","usec":65212016,"nFlows":258} 143 | {"level":"info","msg":"drain finished flows","usec":64988219,"nFlows":258} 144 | ``` 145 | 146 | ``` 147 | outputs: 148 | - log: 149 | file: /tmp/flowlog.json 150 | hooks: 151 | - command: /var/run/flowctl/hook_all1.sh 152 | 153 | {"level":"info","msg":"drain finished flows","usec":2085080,"nFlows":34} 154 | {"level":"info","msg":"drain finished flows","usec":2090360,"nFlows":34} 155 | 156 | {"level":"info","msg":"drain finished flows","usec":4068813,"nFlows":66} 157 | {"level":"info","msg":"drain finished flows","usec":4074041,"nFlows":66} 158 | {"level":"info","msg":"drain finished flows","usec":4069566,"nFlows":66} 159 | 160 | {"level":"info","msg":"drain finished flows","usec":7909099,"nFlows":130} 161 | {"level":"info","msg":"drain finished flows","usec":7975187,"nFlows":130} 162 | {"level":"info","msg":"drain finished flows","usec":8010269,"nFlows":130} 163 | 164 | {"level":"info","msg":"drain finished flows","usec":15793583,"nFlows":258} 165 | {"level":"info","msg":"drain finished flows","usec":15694148,"nFlows":258} 166 | {"level":"info","msg":"drain finished flows","usec":15797545,"nFlows":258} 167 | ``` 168 | 169 | ``` 170 | outputs: 171 | - log: 172 | file: /tmp/flowlog.json 173 | hooks: 174 | - command: /var/run/flowctl/hook_all2.sh 175 | 176 | {"level":"info","msg":"force drain current flows","usec":1079169,"nFlows":34} 177 | {"level":"info","msg":"force drain current flows","usec":1077704,"nFlows":34} 178 | {"level":"info","msg":"force drain current flows","usec":1070113,"nFlows":34} 179 | 180 | (2106597+2086915)/2 181 | (4105261+4095091+4131419)/3 182 | 183 | {"level":"info","msg":"drain finished flows","usec":8125250,"nFlows":258} 184 | {"level":"info","msg":"drain finished flows","usec":8160731,"nFlows":258} 185 | {"level":"info","msg":"drain finished flows","usec":8215767,"nFlows":258} 186 | ``` 187 | 188 | ``` 189 | outputs: 190 | - log: 191 | file: /tmp/flowlog.json 192 | hooks: 193 | - shell: | 194 | #!/bin/sh 195 | jq '[.[] | . + {foo1:"foo1"}]' \ 196 | | jq '[.[] | . + {foo2:"foo2"}]' \ 197 | | jq '[.[] | . + {foo3:"foo3"}]' \ 198 | | jq '[.[] | . + {foo4:"foo4"}]' \ 199 | | jq '[.[] | . + {foo5:"foo5"}]' \ 200 | | jq '[.[] | . + {foo6:"foo6"}]' \ 201 | | jq '[.[] | . + {foo7:"foo7"}]' \ 202 | | jq '[.[] | . + {foo8:"foo8"}]' 203 | 204 | 258: (129003+131560+130863)/3 205 | 206 | {"level":"info","msg":"drain finished flows","usec":129003,"nFlows":258} 207 | {"level":"info","msg":"drain finished flows","usec":131560,"nFlows":258} 208 | {"level":"info","msg":"drain finished flows","usec":130863,"nFlows":258} 209 | ``` 210 | 211 | ``` 212 | outputs: 213 | - log: 214 | file: /tmp/flowlog.json 215 | hooks: 216 | - command: /var/run/flowctl/hookbatch_all1.sh #many-jq 217 | 218 | 34: (69832+74551+70318)/3 219 | {"level":"info","msg":"force drain current flows","usec":69832,"nFlows":34} 220 | {"level":"info","msg":"force drain current flows","usec":74551,"nFlows":34} 221 | {"level":"info","msg":"force drain current flows","usec":70318,"nFlows":34} 222 | 223 | 66: (75263+78929+78031)/3 224 | {"level":"info","msg":"drain finished flows","usec":75263,"nFlows":66} 225 | {"level":"info","msg":"drain finished flows","usec":78929,"nFlows":66} 226 | {"level":"info","msg":"drain finished flows","usec":78031,"nFlows":66} 227 | 228 | 130: (99978+96594+95671)/3 229 | {"level":"info","msg":"drain finished flows","usec":99978,"nFlows":130} 230 | {"level":"info","msg":"drain finished flows","usec":96594,"nFlows":130} 231 | {"level":"info","msg":"drain finished flows","usec":95671,"nFlows":130} 232 | 233 | 258: (130711+127055+129459)/3 234 | {"level":"info","msg":"drain finished flows","usec":130711,"nFlows":258} 235 | {"level":"info","msg":"drain finished flows","usec":127055,"nFlows":258} 236 | {"level":"info","msg":"drain finished flows","usec":129459,"nFlows":258} 237 | ``` 238 | 239 | ``` 240 | outputs: 241 | - log: 242 | file: /tmp/flowlog.json 243 | hooks: 244 | - shell: | 245 | #!/bin/sh 246 | jq '[.[] | . + {foo1:"foo1",foo2:"foo2",foo3:"foo3",foo4:"foo4",foo5:"foo5",foo6:"foo6",foo7:"foo7",foo8:"foo8"}]' 247 | 248 | 258: (57903+52242+51003)/3 249 | 250 | {"level":"info","msg":"drain finished flows","usec":57903,"nFlows":258} 251 | {"level":"info","msg":"drain finished flows","usec":52242,"nFlows":258} 252 | {"level":"info","msg":"drain finished flows","usec":51003,"nFlows":258} 253 | ``` 254 | 255 | ``` 256 | outputs: 257 | - log: 258 | file: /tmp/flowlog.json 259 | hooks: 260 | - command: /var/run/flowctl/hookbatch_all2.sh #one-jq 261 | 262 | 34: (36970+38420+34403)/3 263 | {"level":"info","msg":"force drain current flows","usec":36970,"nFlows":34} 264 | {"level":"info","msg":"force drain current flows","usec":38420,"nFlows":34} 265 | {"level":"info","msg":"force drain current flows","usec":34403,"nFlows":34} 266 | 267 | 66: (40202+39713+39580)/3 268 | {"level":"info","msg":"force drain current flows","usec":40202,"nFlows":66} 269 | {"level":"info","msg":"force drain current flows","usec":39713,"nFlows":66} 270 | {"level":"info","msg":"force drain current flows","usec":39580,"nFlows":66} 271 | 272 | 130: (44911+45686+47030)/3 273 | {"level":"info","msg":"force drain current flows","usec":44911,"nFlows":130} 274 | {"level":"info","msg":"force drain current flows","usec":45686,"nFlows":130} 275 | {"level":"info","msg":"force drain current flows","usec":47030,"nFlows":130} 276 | 277 | 258: (52168+56735+51616)/3 278 | {"level":"info","msg":"drain finished flows","usec":52168,"nFlows":258} 279 | {"level":"info","msg":"drain finished flows","usec":56735,"nFlows":258} 280 | {"level":"info","msg":"drain finished flows","usec":51616,"nFlows":258} 281 | ``` 282 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/generate_scripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p /var/run/flowctl 3 | echo "#!/bin/sh\njq '. + {foo1: \"foo1\"}'" > /var/run/flowctl/hook1.sh 4 | echo "#!/bin/sh\njq '. + {foo2: \"foo2\"}'" > /var/run/flowctl/hook2.sh 5 | echo "#!/bin/sh\njq '. + {foo3: \"foo3\"}'" > /var/run/flowctl/hook3.sh 6 | echo "#!/bin/sh\njq '. + {foo4: \"foo4\"}'" > /var/run/flowctl/hook4.sh 7 | echo "#!/bin/sh\njq '. + {foo5: \"foo5\"}'" > /var/run/flowctl/hook5.sh 8 | echo "#!/bin/sh\njq '. + {foo6: \"foo6\"}'" > /var/run/flowctl/hook6.sh 9 | echo "#!/bin/sh\njq '. + {foo7: \"foo7\"}'" > /var/run/flowctl/hook7.sh 10 | echo "#!/bin/sh\njq '. + {foo8: \"foo8\"}'" > /var/run/flowctl/hook8.sh 11 | cat < /var/run/flowctl/hook_all1.sh 12 | #!/bin/sh 13 | jq '. + {foo1: "foo1"}' \ 14 | | jq '. + {foo2: "foo2"}' \ 15 | | jq '. + {foo3: "foo3"}' \ 16 | | jq '. + {foo4: "foo4"}' \ 17 | | jq '. + {foo5: "foo5"}' \ 18 | | jq '. + {foo6: "foo6"}' \ 19 | | jq '. + {foo7: "foo7"}' \ 20 | | jq '. + {foo8: "foo8"}' 21 | EOF 22 | cat < /var/run/flowctl/hook_all2.sh 23 | #!/bin/sh 24 | jq '.+{foo1:"foo1",foo2:"foo2",foo3:"foo3",foo4:"foo4",foo5:"foo5",foo6:"foo6",foo7:"foo7",foo8:"foo8"}' 25 | EOF 26 | cat < /var/run/flowctl/hookbatch_all1.sh 27 | #!/bin/sh 28 | jq '[.[] | . + {foo1:"foo1"}]' \\ 29 | | jq '[.[] | . + {foo2:"foo2"}]' \\ 30 | | jq '[.[] | . + {foo3:"foo3"}]' \\ 31 | | jq '[.[] | . + {foo4:"foo4"}]' \\ 32 | | jq '[.[] | . + {foo5:"foo5"}]' \\ 33 | | jq '[.[] | . + {foo6:"foo6"}]' \\ 34 | | jq '[.[] | . + {foo7:"foo7"}]' \\ 35 | | jq '[.[] | . + {foo8:"foo8"}]' 36 | EOF 37 | cat < /var/run/flowctl/hookbatch_all2.sh 38 | #!/bin/sh 39 | jq '[.[] | . + {foo1:"foo1",foo2:"foo2",foo3:"foo3",foo4:"foo4",foo5:"foo5",foo6:"foo6",foo7:"foo7",foo8:"foo8"}]' 40 | EOF 41 | chmod +x /var/run/flowctl/hook*.sh 42 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/n_flow_performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wide-vsix/linux-flow-exporter/ddfb6dc36495808af8b54392213596cc92a0f2f6/misc/benchmarking_20221111/n_flow_performance.png -------------------------------------------------------------------------------- /misc/benchmarking_20221111/n_flow_performance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | labels = ['34', '66', '130', '258'] 6 | nobatch_many_jq = [2087720, 4070806, 7964851, 15761758] 7 | nobatch_one_jq = [1075662, 2096756, 4110590, 8167249] 8 | batch_many_jq = [(69832+74551+70318)/3, 9 | (75263+78929+78031)/3, 10 | (99978+96594+95671)/3, 11 | (130711+127055+129459)/3] 12 | batch_one_jq = [(36970+38420+34403)/3, 13 | (40202+39713+39580)/3, 14 | (44911+45686+47030)/3, 15 | (52168+56735+51616)/3] 16 | 17 | x = np.arange(len(labels)) 18 | width = 0.2 19 | fig, ax = plt.subplots() 20 | rects1 = ax.bar(x - 1.5*width/1, nobatch_many_jq, width, label='nobatch-1shell-8jq') 21 | rects2 = ax.bar(x - width/2, nobatch_one_jq, width, label='nobatch-1shell-1jq') 22 | rects3 = ax.bar(x + width/2, batch_many_jq, width, label='batch-1shell-8jq') 23 | rects4 = ax.bar(x + 1.5*width/1, batch_one_jq, width, label='batch-1shell-1jq') 24 | 25 | ax.set_ylabel('latency [usec]') 26 | ax.set_xticks(x, labels) 27 | ax.set_title('#Flows Performance') 28 | ax.legend() 29 | 30 | ax.bar_label(rects1, padding=3) 31 | ax.bar_label(rects2, padding=3) 32 | ax.bar_label(rects3, padding=3) 33 | ax.bar_label(rects4, padding=3) 34 | fig.tight_layout() 35 | plt.savefig("n_flow_performance.png") 36 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/n_hook_performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wide-vsix/linux-flow-exporter/ddfb6dc36495808af8b54392213596cc92a0f2f6/misc/benchmarking_20221111/n_hook_performance.png -------------------------------------------------------------------------------- /misc/benchmarking_20221111/n_hook_performance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | labels = ['nothing', '1 hook', '2 hooks', '8 hooks'] 6 | shell_means = [5772, 8335522, 16449901, 65299620] 7 | command_means = [5772, 8111420, 16282779, 64994027] 8 | 9 | x = np.arange(len(labels)) 10 | width = 0.35 11 | fig, ax = plt.subplots() 12 | rects1 = ax.bar(x - width/2, shell_means, width, label='shell') 13 | rects2 = ax.bar(x + width/2, command_means, width, label='command') 14 | 15 | ax.set_ylabel('latency [usec]') 16 | ax.set_xticks(x, labels) 17 | ax.set_title('#Hooks Performance') 18 | ax.legend() 19 | 20 | ax.bar_label(rects1, padding=3) 21 | ax.bar_label(rects2, padding=3) 22 | fig.tight_layout() 23 | plt.savefig("n_hook_performance.png") 24 | -------------------------------------------------------------------------------- /misc/benchmarking_20221111/optimization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wide-vsix/linux-flow-exporter/ddfb6dc36495808af8b54392213596cc92a0f2f6/misc/benchmarking_20221111/optimization.png -------------------------------------------------------------------------------- /misc/benchmarking_20221111/optimization.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | labels = ['(shell-1jq)x8', '(shell-8jq)x1', '(shell-1jq)x1'] 6 | shell_means = [65299620, 15761758, 8167249] 7 | 8 | x = np.arange(len(labels)) 9 | width = 0.35 10 | fig, ax = plt.subplots() 11 | rects1 = ax.bar(x, shell_means, width, label='shell') 12 | 13 | ax.set_ylabel('latency [usec]') 14 | ax.set_xticks(x, labels) 15 | ax.set_title('Optimize Shell Hooks') 16 | ax.legend() 17 | 18 | ax.bar_label(rects1, padding=3) 19 | #ax.bar_label(rects2, padding=3) 20 | fig.tight_layout() 21 | plt.savefig("optimization.png") 22 | -------------------------------------------------------------------------------- /misc/clean_netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -xe 3 | 4 | ip netns del ns0 || true 5 | ip netns del ns1 || true 6 | ip netns del ns2 || true 7 | ip netns del ns3 || true 8 | -------------------------------------------------------------------------------- /misc/create_netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 10.2.0.0/24 3 | # .2 .1 4 | # [ns2](eth0)---(eth3) .1 .2 5 | # [ns0](eth1)----(eth0)[ns1] 6 | # [ns3](eth0)---(eth4) 10.1.0.0/24 7 | # .2 .1 8 | # 10.3.0.0/24 9 | set -xe 10 | 11 | ip netns add ns0 12 | ip netns add ns1 13 | ip netns add ns2 14 | ip netns add ns3 15 | 16 | ip -n ns0 link set lo up 17 | ip -n ns1 link set lo up 18 | ip -n ns2 link set lo up 19 | ip -n ns3 link set lo up 20 | 21 | ip link add eth0 netns ns1 type veth peer name eth1 netns ns0 22 | ip link add eth0 netns ns2 type veth peer name eth2 netns ns0 23 | ip link add eth0 netns ns3 type veth peer name eth3 netns ns0 24 | 25 | ip -n ns0 link set eth1 up 26 | ip -n ns0 link set eth2 up 27 | ip -n ns0 link set eth3 up 28 | ip -n ns1 link set eth0 up 29 | ip -n ns2 link set eth0 up 30 | ip -n ns3 link set eth0 up 31 | 32 | ip -n ns0 addr add 10.1.0.1/24 dev eth1 33 | ip -n ns0 addr add 10.2.0.1/24 dev eth2 34 | ip -n ns0 addr add 10.3.0.1/24 dev eth3 35 | ip -n ns1 addr add 10.1.0.2/24 dev eth0 36 | ip -n ns2 addr add 10.2.0.2/24 dev eth0 37 | ip -n ns3 addr add 10.3.0.2/24 dev eth0 38 | 39 | ip -n ns1 route add default via 10.1.0.1 40 | ip -n ns2 route add default via 10.2.0.1 41 | ip -n ns3 route add default via 10.3.0.1 42 | -------------------------------------------------------------------------------- /misc/hook_command_example_dummy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # IN: 3 | # { 4 | # "src": "10.1.0.1", 5 | # "dst": "10.2.0.1", 6 | # "pkts": 10, 7 | # "bytes": 1000 8 | # } 9 | # 10 | # OUT: 11 | # { 12 | # "src": "10.1.0.1", 13 | # "dst": "10.2.0.1", 14 | # "pkts": 10, 15 | # "bytes": 1000, 16 | # "foo": "bar" 17 | # } 18 | echo `cat` | jq '. + {foo: "bar"}' 19 | -------------------------------------------------------------------------------- /misc/hook_command_example_hostname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # IN: 3 | # { 4 | # "src": "10.1.0.1", 5 | # "dst": "10.2.0.1", 6 | # "pkts": 10, 7 | # "bytes": 1000 8 | # } 9 | # 10 | # OUT: 11 | # { 12 | # "src": "10.1.0.1", 13 | # "dst": "10.2.0.1", 14 | # "pkts": 10, 15 | # "bytes": 1000, 16 | # "hostname": "machine1" 17 | # } 18 | echo `cat` | jq --arg hostname $(hostname) '. + {hostname: $hostname}' 19 | -------------------------------------------------------------------------------- /misc/hook_command_example_ifname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # IN: 3 | # { 4 | # "ingressIfindex": 1, 5 | # "egressIfindex": 2, 6 | # "pkts": 10, 7 | # "bytes": 1000 8 | # } 9 | # 10 | # OUT: 11 | # { 12 | # "ingressIfindex": 1, 13 | # "egressIfindex": 2, 14 | # "ingressIfname": 1, 15 | # "egressIfname": 2, 16 | # "pkts": 10, 17 | # "bytes": 1000 18 | # } 19 | IN=$(cat) 20 | I_IDX=$(echo $IN | jq .ingressIfindex -r) 21 | E_IDX=$(echo $IN | jq .egressIfindex -r ) 22 | I_NAME=$(ip -n ns0 -j link | jq --argjson idx $I_IDX '.[] | select(.ifindex == $idx) | .ifname' -r) 23 | E_NAME=$(ip -n ns0 -j link | jq --argjson idx $E_IDX '.[] | select(.ifindex == $idx) | .ifname' -r) 24 | echo $IN | jq --arg i_name $I_NAME --arg e_name $E_NAME '. + {ingressIfname: $i_name, egressIfname: $e_name}' 25 | -------------------------------------------------------------------------------- /pkg/ebpfmap/perf.go: -------------------------------------------------------------------------------- 1 | package ebpfmap 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | 7 | "github.com/cilium/ebpf" 8 | "github.com/cilium/ebpf/perf" 9 | ) 10 | 11 | type PerfObject struct { 12 | MapID ebpf.MapID 13 | rec perf.Record 14 | } 15 | 16 | func (po PerfObject) Ifindex() uint32 { 17 | padding := make([]byte, 4-len(po.rec.RawSample)) 18 | i := binary.LittleEndian.Uint32(append(padding, po.rec.RawSample...)) 19 | return i 20 | } 21 | 22 | func StartReaderPerMap(mapID ebpf.MapID, poCh chan PerfObject) error { 23 | m, err := ebpf.NewMapFromID(mapID) 24 | if err != nil { 25 | return err 26 | } 27 | defer m.Close() 28 | 29 | rd, err := perf.NewReader(m, 4096) 30 | if err != nil { 31 | return err 32 | } 33 | defer rd.Close() 34 | 35 | for { 36 | rec, err := rd.Read() 37 | if err != nil { 38 | return err 39 | } 40 | po := PerfObject{ 41 | MapID: mapID, 42 | rec: rec, 43 | } 44 | poCh <- po 45 | } 46 | } 47 | 48 | func StartReader() (chan PerfObject, error) { 49 | ids, err := GetMapIDsByNameType("events", ebpf.PerfEventArray) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | poCh := make(chan PerfObject, 10) 55 | for _, id := range ids { 56 | go func(id ebpf.MapID) { 57 | for { 58 | if err := StartReaderPerMap(id, poCh); err != nil { 59 | fmt.Printf("FAIL: %s ... ignored", err.Error()) 60 | } 61 | } 62 | }(id) 63 | } 64 | 65 | return poCh, nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/ebpfmap/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package ebpfmap 20 | 21 | import ( 22 | "fmt" 23 | "math" 24 | 25 | "github.com/cilium/ebpf" 26 | "github.com/wide-vsix/linux-flow-exporter/pkg/ipfix" 27 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 28 | ) 29 | 30 | const ( 31 | mapName = "flow_stats" 32 | mapType = ebpf.PerCPUHash 33 | 34 | metricsMapName = "metrics" 35 | metricsMapType = ebpf.PerCPUHash 36 | ) 37 | 38 | type FlowKey struct { 39 | IngressIfindex uint32 40 | EgressIfindex uint32 41 | Saddr uint32 42 | Daddr uint32 43 | Sport uint16 44 | Dport uint16 45 | Proto uint8 46 | Mark uint32 47 | } 48 | 49 | type FlowVal struct { 50 | FlowPkts uint32 `json:"cnt"` 51 | FlowBytes uint32 `json:"data_bytes"` 52 | FlowStartMilliSecond uint64 `json:"flow_start_msec"` 53 | FlowEndMilliSecond uint64 `json:"flow_end_msec"` 54 | Finished uint8 `json:"finished"` 55 | } 56 | 57 | type Flow struct { 58 | Key FlowKey 59 | Val FlowVal 60 | } 61 | 62 | func (k FlowKey) String() string { 63 | saddr := util.ConvertUint32ToIP(k.Saddr) 64 | daddr := util.ConvertUint32ToIP(k.Daddr) 65 | return fmt.Sprintf("%d/%d/%d/%s:%d/%s:%d", 66 | k.IngressIfindex, 67 | k.EgressIfindex, 68 | k.Proto, 69 | saddr.String(), 70 | k.Sport, 71 | daddr.String(), 72 | k.Dport, 73 | ) 74 | } 75 | 76 | func (v *FlowVal) Merge(src FlowVal) { 77 | v.FlowPkts += src.FlowPkts 78 | v.FlowBytes += src.FlowBytes 79 | if v.FlowStartMilliSecond == 0 { 80 | v.FlowStartMilliSecond = math.MaxUint64 81 | } 82 | if src.FlowStartMilliSecond != 0 && 83 | src.FlowStartMilliSecond <= v.FlowStartMilliSecond { 84 | v.FlowStartMilliSecond = src.FlowStartMilliSecond 85 | } 86 | if src.FlowEndMilliSecond != 0 && 87 | src.FlowEndMilliSecond >= v.FlowEndMilliSecond { 88 | v.FlowEndMilliSecond = src.FlowEndMilliSecond 89 | } 90 | if src.Finished != 0 { 91 | v.Finished = 1 92 | } 93 | } 94 | 95 | func GetMapIDsByNameType(mapName string, mapType ebpf.MapType) ([]ebpf.MapID, error) { 96 | ids := []ebpf.MapID{} 97 | for id := ebpf.MapID(0); ; { 98 | var err error 99 | id, err = ebpf.MapGetNextID(ebpf.MapID(id)) 100 | if err != nil { 101 | break 102 | } 103 | m, err := ebpf.NewMapFromID(id) 104 | if err != nil { 105 | return nil, err 106 | } 107 | info, err := m.Info() 108 | if err != nil { 109 | return nil, err 110 | } 111 | if err := m.Close(); err != nil { 112 | return nil, err 113 | } 114 | 115 | if info.Name != mapName || info.Type != mapType { 116 | continue 117 | } 118 | ids = append(ids, id) 119 | } 120 | return ids, nil 121 | } 122 | 123 | type StatsMetricsKey struct { 124 | IngressIfindex uint32 `json:"ingress_ifindex"` 125 | EgressIfindex uint32 `json:"egress_ifindex"` 126 | } 127 | 128 | type StatsMetricsVal struct { 129 | SynPkts uint32 `json:"syn_pkts"` 130 | TotalPkts uint32 `json:"total_pkts"` 131 | TotalBytes uint32 `json:"total_bytes"` 132 | OverflowPkts uint32 `json:"overflow_pkts"` 133 | OverflowBytes uint32 `json:"overflow_bytes"` 134 | TotalLatencyNanoseconds uint32 `json:"latency_nano_sum"` 135 | } 136 | 137 | func GetStats() (map[StatsMetricsKey]StatsMetricsVal, error) { 138 | ids, err := GetMapIDsByNameType(metricsMapName, metricsMapType) 139 | if err != nil { 140 | return nil, err 141 | } 142 | 143 | ret := map[StatsMetricsKey]StatsMetricsVal{} 144 | for _, id := range ids { 145 | m, err := ebpf.NewMapFromID(id) 146 | if err != nil { 147 | return nil, err 148 | } 149 | 150 | key := StatsMetricsKey{} 151 | perCpuVals := []StatsMetricsVal{} 152 | entries := m.Iterate() 153 | for entries.Next(&key, &perCpuVals) { 154 | val := StatsMetricsVal{} 155 | for _, perCpuVal := range perCpuVals { 156 | val.SynPkts += perCpuVal.SynPkts 157 | val.OverflowPkts += perCpuVal.OverflowPkts 158 | val.OverflowBytes += perCpuVal.OverflowBytes 159 | val.TotalPkts += perCpuVal.TotalPkts 160 | val.TotalBytes += perCpuVal.TotalBytes 161 | val.TotalLatencyNanoseconds += perCpuVal.TotalLatencyNanoseconds 162 | } 163 | ret[key] = val 164 | } 165 | } 166 | 167 | return ret, nil 168 | } 169 | 170 | func Dump() ([]Flow, error) { 171 | ids, err := GetMapIDsByNameType(mapName, mapType) 172 | if err != nil { 173 | return nil, err 174 | } 175 | 176 | flows := []Flow{} 177 | for _, id := range ids { 178 | m, err := ebpf.NewMapFromID(id) 179 | if err != nil { 180 | return nil, err 181 | } 182 | 183 | key := FlowKey{} 184 | perCpuVals := []FlowVal{} 185 | entries := m.Iterate() 186 | for entries.Next(&key, &perCpuVals) { 187 | val := FlowVal{} 188 | for _, perCpuVal := range perCpuVals { 189 | val.Merge(perCpuVal) 190 | } 191 | flows = append(flows, Flow{key, val}) 192 | } 193 | if err := entries.Err(); err != nil { 194 | return nil, err 195 | } 196 | if err := m.Close(); err != nil { 197 | return nil, err 198 | } 199 | } 200 | return flows, nil 201 | } 202 | 203 | func Delete(key FlowKey) error { 204 | ids, err := GetMapIDsByNameType(mapName, mapType) 205 | if err != nil { 206 | return err 207 | } 208 | for _, id := range ids { 209 | m, err := ebpf.NewMapFromID(id) 210 | if err != nil { 211 | return err 212 | } 213 | if err := m.Delete(key); err != nil { 214 | return err 215 | } 216 | if err := m.Close(); err != nil { 217 | return err 218 | } 219 | } 220 | return nil 221 | } 222 | 223 | func DeleteFinished() error { 224 | ids, err := GetMapIDsByNameType(mapName, mapType) 225 | if err != nil { 226 | return err 227 | } 228 | for _, id := range ids { 229 | m, err := ebpf.NewMapFromID(id) 230 | if err != nil { 231 | return err 232 | } 233 | key := FlowKey{} 234 | perCpuVals := []FlowVal{} 235 | entries := m.Iterate() 236 | for entries.Next(&key, &perCpuVals) { 237 | for _, perCpuVal := range perCpuVals { 238 | if perCpuVal.Finished > 0 { 239 | if err := m.Delete(key); err != nil { 240 | return err 241 | } 242 | break 243 | } 244 | } 245 | } 246 | if err := m.Close(); err != nil { 247 | return err 248 | } 249 | } 250 | return nil 251 | } 252 | 253 | func DeleteAll() error { 254 | ids, err := GetMapIDsByNameType(mapName, mapType) 255 | if err != nil { 256 | return err 257 | } 258 | for _, id := range ids { 259 | m, err := ebpf.NewMapFromID(id) 260 | if err != nil { 261 | return err 262 | } 263 | key := FlowKey{} 264 | perCpuVals := []FlowVal{} 265 | entries := m.Iterate() 266 | for entries.Next(&key, &perCpuVals) { 267 | if err := m.Delete(key); err != nil { 268 | return err 269 | } 270 | } 271 | if err := m.Close(); err != nil { 272 | return err 273 | } 274 | } 275 | return nil 276 | } 277 | 278 | func ToIpfixFlowFile(ebflows []Flow) (*ipfix.FlowFile, error) { 279 | flows := []ipfix.Flow{} 280 | for _, ebflow := range ebflows { 281 | s, err := util.KtimeToRealMilli(ebflow.Val.FlowStartMilliSecond / 1000000) 282 | if err != nil { 283 | return nil, err 284 | } 285 | e, err := util.KtimeToRealMilli(ebflow.Val.FlowEndMilliSecond / 1000000) 286 | if err != nil { 287 | return nil, err 288 | } 289 | 290 | flows = append(flows, ipfix.Flow{ 291 | IpVersion: 4, 292 | SourceIPv4Address: util.BS32(ebflow.Key.Saddr), 293 | DestinationIPv4Address: util.BS32(ebflow.Key.Daddr), 294 | ProtocolIdentifier: ebflow.Key.Proto, 295 | SourceTransportPort: ebflow.Key.Sport, 296 | DestinationTransportPort: ebflow.Key.Dport, 297 | OctetDeltaCount: uint64(ebflow.Val.FlowBytes), 298 | PacketDeltaCount: uint64(ebflow.Val.FlowPkts), 299 | FlowStartMilliseconds: s, 300 | FlowEndMilliseconds: e, 301 | }) 302 | } 303 | 304 | flowFile := &ipfix.FlowFile{ 305 | FlowSets: []struct { 306 | TemplateID uint16 `yaml:"templateId"` 307 | Flows []ipfix.Flow `yaml:"flows"` 308 | }{ 309 | { 310 | TemplateID: uint16(1004), 311 | Flows: flows, 312 | }, 313 | }, 314 | } 315 | return flowFile, nil 316 | } 317 | -------------------------------------------------------------------------------- /pkg/flowctl/agent.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package flowctl 20 | 21 | import ( 22 | "time" 23 | 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | var cliOptAgent = struct { 28 | Config string 29 | FlowFile string 30 | }{} 31 | 32 | func NewCommandAgent() *cobra.Command { 33 | cmd := &cobra.Command{ 34 | Use: "agent", 35 | Run: func(cmd *cobra.Command, args []string) { 36 | go func() { 37 | slog.Info("metrics exporter thread is started") 38 | if err := threadMetricsExporter(); err != nil { 39 | panic(err) 40 | } 41 | slog.Info("metrics exporter thread is finished") 42 | }() 43 | go func() { 44 | slog.Info("flow exporter thread is started") 45 | if err := threadFlowExporter(); err != nil { 46 | panic(err) 47 | } 48 | slog.Info("flow exporter thread is finished") 49 | }() 50 | for { 51 | time.Sleep(1 * time.Second) 52 | } 53 | }, 54 | } 55 | cmd.Flags().StringVarP(&cliOptAgent.Config, "config", "c", "./config.yaml", 56 | "Specifiy ipfix configuration") 57 | return cmd 58 | } 59 | -------------------------------------------------------------------------------- /pkg/flowctl/app.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package flowctl 20 | 21 | import ( 22 | _ "embed" 23 | "fmt" 24 | "strings" 25 | 26 | "github.com/spf13/cobra" 27 | "golang.org/x/mod/semver" 28 | 29 | "github.com/wide-vsix/linux-flow-exporter/pkg/goroute2" 30 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 31 | ) 32 | 33 | //go:embed data/filter.bpf.c 34 | var filterBpfFileContent []byte 35 | 36 | func NewCommand() *cobra.Command { 37 | cmd := &cobra.Command{ 38 | Use: "flowctl", 39 | } 40 | cmd.AddCommand(NewCommandDump()) 41 | cmd.AddCommand(NewCommandFlush()) 42 | cmd.AddCommand(NewCommandIpfix()) 43 | cmd.AddCommand(NewCommandMeter()) 44 | cmd.AddCommand(NewCommandDependencyCheck()) 45 | cmd.AddCommand(NewCommandEbpf()) 46 | cmd.AddCommand(NewCommandAgent()) 47 | cmd.AddCommand(util.NewCommandVersion()) 48 | cmd.AddCommand(util.NewCmdCompletion(cmd)) 49 | return cmd 50 | } 51 | 52 | type SystemCapability struct { 53 | ClangVersionCurrent string 54 | ClangVersionExpected string 55 | KernelVersionCurrent string 56 | KernelVersionExpected string 57 | Iproute2binVersionCurrent string 58 | Iproute2binVersionExpected string 59 | Iproute2lbpfVersionCurrent string 60 | Iproute2lbpfVersionExpected string 61 | } 62 | 63 | func (sc *SystemCapability) Get() error { 64 | var err error 65 | sc.ClangVersionExpected = "v10.0.0" 66 | sc.KernelVersionExpected = "v5.4.0" 67 | sc.Iproute2binVersionExpected = "v5.4.0" 68 | sc.Iproute2lbpfVersionExpected = "v0.8.0" 69 | sc.ClangVersionCurrent, err = util.GetClangVersion() 70 | if err != nil { 71 | return err 72 | } 73 | sc.KernelVersionCurrent, err = util.GetKernelVersion() 74 | if err != nil { 75 | return err 76 | } 77 | sc.Iproute2binVersionCurrent, sc.Iproute2lbpfVersionCurrent, err = util.GetIproute2Version() 78 | if err != nil { 79 | return err 80 | } 81 | return nil 82 | } 83 | 84 | func (sc SystemCapability) Capable() bool { 85 | if semver.Compare(sc.ClangVersionCurrent, sc.ClangVersionExpected) < 0 { 86 | return false 87 | } 88 | if semver.Compare(sc.KernelVersionCurrent, sc.KernelVersionExpected) < 0 { 89 | return false 90 | } 91 | if semver.Compare(sc.Iproute2binVersionCurrent, sc.Iproute2binVersionExpected) < 0 { 92 | return false 93 | } 94 | if semver.Compare(sc.Iproute2lbpfVersionCurrent, sc.Iproute2lbpfVersionExpected) < 0 { 95 | return false 96 | } 97 | return true 98 | } 99 | 100 | func (sc SystemCapability) DumpToStdout() { 101 | validate := func(currentVersion, expectedVersion string) string { 102 | if currentVersion == "" { 103 | return "NOT-INSTALLED" 104 | } else { 105 | if semver.Compare(currentVersion, expectedVersion) >= 0 { 106 | return "VALID" 107 | } else { 108 | return "INVALID" 109 | } 110 | } 111 | } 112 | 113 | // Verify clang version 114 | fmt.Printf("clang version (expect %s): %s (%s)\n", 115 | sc.ClangVersionExpected, sc.ClangVersionCurrent, 116 | validate(sc.ClangVersionCurrent, sc.ClangVersionExpected)) 117 | 118 | // Verify kernel version 119 | fmt.Printf("kernel version (expect %s): %s (%s)\n", 120 | sc.KernelVersionExpected, sc.KernelVersionCurrent, 121 | validate(sc.KernelVersionCurrent, sc.KernelVersionExpected)) 122 | 123 | // Verify iproute2 and its libbpf version 124 | fmt.Printf("iproute2 binary version (expect %s): %s (%s)\n", 125 | sc.Iproute2binVersionExpected, sc.Iproute2binVersionCurrent, 126 | validate(sc.Iproute2binVersionCurrent, sc.Iproute2binVersionExpected)) 127 | fmt.Printf("iproute2 libbpf version (expect %s): %s (%s)\n", 128 | sc.Iproute2lbpfVersionExpected, sc.Iproute2lbpfVersionCurrent, 129 | validate(sc.Iproute2lbpfVersionCurrent, sc.Iproute2lbpfVersionExpected)) 130 | } 131 | 132 | func NewCommandDependencyCheck() *cobra.Command { 133 | cmd := &cobra.Command{ 134 | Use: "dependency-check", 135 | RunE: func(cmd *cobra.Command, args []string) error { 136 | sc := SystemCapability{} 137 | if err := sc.Get(); err != nil { 138 | return err 139 | } 140 | sc.DumpToStdout() 141 | return nil 142 | }, 143 | } 144 | return cmd 145 | } 146 | 147 | func NewCommandEbpf() *cobra.Command { 148 | cmd := &cobra.Command{ 149 | Use: "ebpf", 150 | } 151 | cmd.AddCommand(NewCommandEbpfCodeDump()) 152 | return cmd 153 | } 154 | 155 | func NewCommandEbpfCodeDump() *cobra.Command { 156 | cmd := &cobra.Command{ 157 | Use: "code-dump", 158 | RunE: func(cmd *cobra.Command, args []string) error { 159 | fmt.Printf("%s\n", string(filterBpfFileContent)) 160 | return nil 161 | }, 162 | } 163 | return cmd 164 | } 165 | 166 | func getTcEbpfByteCode(netns, dev, direction string) (string, error) { 167 | clsActIsEnabled, err := goroute2.ClsActIsEnabled(netns, dev) 168 | if err != nil { 169 | return "", err 170 | } 171 | if clsActIsEnabled { 172 | rules, err := goroute2.ListTcFilterRules(netns, dev, direction) 173 | if err != nil { 174 | return "", err 175 | } 176 | for _, rule := range rules { 177 | if rule.Kind == "bpf" && rule.Options.BpfName != "" { 178 | return rule.Options.BpfName, nil 179 | } 180 | } 181 | } 182 | return "", nil 183 | } 184 | 185 | func getFlowMeterByteCode(netns, dev, dir string) (*FlowMeterByteCode, error) { 186 | bpfname, err := getTcEbpfByteCode(netns, dev, dir) 187 | if err != nil { 188 | return nil, err 189 | } 190 | if bpfname == "" { 191 | return nil, nil 192 | } 193 | bc, err := DecodeFromBpfName(bpfname) 194 | if err != nil { 195 | return nil, err 196 | } 197 | return bc, nil 198 | } 199 | 200 | func parseInterface(s string) (netns string, device string, err error) { 201 | if s == "" { 202 | return "", "", fmt.Errorf("interface is not specified") 203 | } 204 | words := strings.Split(s, ":") 205 | switch { 206 | case len(words) == 2: 207 | return words[0], words[1], nil 208 | case len(words) == 1: 209 | return "", words[0], nil 210 | default: 211 | return "", "", fmt.Errorf("invalid formant (%s)", s) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /pkg/flowctl/data/filter.bpf.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define IP_MF 0x2000 31 | #define IP_OFFSET 0x1FFF 32 | #ifndef INTERFACE_MAX_FLOW_LIMIT 33 | #define INTERFACE_MAX_FLOW_LIMIT 8 34 | #endif /* INTERFACE_MAX_FLOW_LIMIT */ 35 | #define MAX_INTERFACES 512 36 | 37 | #define assert_len(interest, end) \ 38 | ({ \ 39 | if ((unsigned long)(interest + 1) > end) \ 40 | return TC_ACT_SHOT; \ 41 | }) 42 | 43 | struct flowkey { 44 | __u32 ingress_ifindex; 45 | __u32 egress_ifindex; 46 | __u32 saddr; 47 | __u32 daddr; 48 | __u16 sport; 49 | __u16 dport; 50 | __u8 proto; 51 | __u32 mark; 52 | } __attribute__ ((packed)); 53 | 54 | struct flowval { 55 | __u32 cnt; // pkts; 56 | __u32 data_bytes; // bytes; 57 | __u64 flow_start_msec; 58 | __u64 flow_end_msec; 59 | __u8 finished; 60 | } __attribute__ ((packed)); 61 | 62 | struct metricskey { 63 | __u32 ingress_ifindex; 64 | __u32 egress_ifindex; 65 | } __attribute__ ((packed)); 66 | 67 | struct metricsval { 68 | __u32 syn_pkts; 69 | __u32 total_pkts; 70 | __u32 total_bytes; 71 | __u32 overflow_pkts; 72 | __u32 overflow_bytes; 73 | __u32 latency_nano_sum; 74 | } __attribute__ ((packed)); 75 | 76 | struct meta_info { 77 | __u64 tstamp; 78 | } __attribute__ ((packed)); 79 | 80 | struct { 81 | __uint(type, BPF_MAP_TYPE_PERCPU_HASH); 82 | __uint(max_entries, INTERFACE_MAX_FLOW_LIMIT); 83 | __type(key, struct flowkey); 84 | __type(value, struct flowval); 85 | } flow_stats SEC(".maps"); 86 | 87 | struct { 88 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 89 | __uint(key_size, sizeof(__u32)); 90 | __uint(value_size, sizeof(__u32)); 91 | } events SEC(".maps"); 92 | 93 | struct { 94 | __uint(type, BPF_MAP_TYPE_PERCPU_HASH); 95 | __uint(max_entries, MAX_INTERFACES); 96 | __type(key, struct metricskey); 97 | __type(value, struct metricsval); 98 | } metrics SEC(".maps"); 99 | 100 | #ifdef DEBUG 101 | static inline void 102 | debug_skb(struct __sk_buff *skb, const char *name) 103 | { 104 | bpf_printk("%s(%u:%u)", name, skb->ingress_ifindex, skb->ifindex); 105 | bpf_printk(" tstamp:%u mark:%u l4_hash:%u", skb->tstamp, skb->mark, skb->hash); 106 | bpf_printk(" cb[0]: %u", skb->cb[0]); 107 | bpf_printk(" cb[1]: %u", skb->cb[1]); 108 | bpf_printk(" cb[2]: %u", skb->cb[2]); 109 | bpf_printk(" cb[3]: %u", skb->cb[3]); 110 | bpf_printk(" cb[4]: %u", skb->cb[4]); 111 | bpf_printk(" data_meta: %u", skb->data_meta); 112 | bpf_printk(" data: %u", skb->data); 113 | bpf_printk(" data_end: %u", skb->data_end); 114 | } 115 | #endif /* DEBUG */ 116 | 117 | #if 0 118 | static inline void metrics_count_syn(__u32 ifindex) 119 | { 120 | struct metricsval *mv = bpf_map_lookup_elem(&metrics, &ifindex); 121 | if (mv) { 122 | mv->syn_pkts = mv->syn_pkts + 1; 123 | } else { 124 | struct metricsval initval = {0}; 125 | initval.syn_pkts = 1; 126 | bpf_map_update_elem(&metrics, &ifindex, &initval, BPF_ANY); 127 | } 128 | } 129 | #endif 130 | 131 | static inline __u64 forwarding_duration_ns(struct __sk_buff *skb) 132 | { 133 | if (skb->data_meta < skb->data) { 134 | struct meta_info *meta = (struct meta_info *)skb->data_meta; 135 | assert_len(meta, skb->data); 136 | return bpf_ktime_get_ns() - meta->tstamp; 137 | } else { 138 | return 0; 139 | } 140 | } 141 | 142 | static inline void metrics_count_final(struct __sk_buff *skb, __u8 overflow) 143 | { 144 | struct metricskey key = {}; 145 | key.ingress_ifindex = skb->ingress_ifindex; 146 | key.egress_ifindex = skb->ifindex; 147 | struct metricsval *mv = bpf_map_lookup_elem(&metrics, &key); 148 | if (mv) { 149 | mv->total_pkts = mv->total_pkts + 1; 150 | mv->total_bytes = mv->total_bytes + skb->len; 151 | if (overflow) { 152 | mv->overflow_pkts = mv->overflow_pkts + 1; 153 | mv->overflow_bytes = mv->overflow_bytes + skb->len; 154 | } 155 | mv->latency_nano_sum += forwarding_duration_ns(skb); 156 | } else { 157 | struct metricsval initval = {0}; 158 | initval.total_pkts = 1; 159 | initval.total_bytes = skb->len; 160 | if (overflow) { 161 | initval.overflow_pkts = 1; 162 | initval.overflow_bytes = skb->len; 163 | } 164 | initval.latency_nano_sum = forwarding_duration_ns(skb); 165 | bpf_map_update_elem(&metrics, &key, &initval, BPF_ANY); 166 | } 167 | } 168 | 169 | static inline void record(const struct tcphdr *th, const struct iphdr *ih, 170 | struct __sk_buff *skb) 171 | { 172 | __u16 dport = th->dest; 173 | __u16 sport = th->source; 174 | __u32 daddr = ih->daddr; 175 | __u32 saddr = ih->saddr; 176 | __u8 proto = ih->protocol; 177 | __u8 finished = 0; 178 | __u32 mark = skb->mark; 179 | struct flowkey key = {0}; 180 | key.ingress_ifindex = skb->ingress_ifindex; 181 | key.egress_ifindex = skb->ifindex; 182 | key.daddr = daddr; 183 | key.saddr = saddr; 184 | key.dport = bpf_htons(dport); 185 | key.sport = bpf_htons(sport); 186 | key.proto = proto; 187 | key.mark = mark; 188 | if (th->fin > 0 || th->rst > 0) 189 | finished = 1; 190 | 191 | __u8 overflow = 0; 192 | struct flowval *val = bpf_map_lookup_elem(&flow_stats, &key); 193 | if (val) { 194 | val->cnt = val->cnt + 1; 195 | val->data_bytes = val->data_bytes + skb->len; 196 | val->flow_end_msec = bpf_ktime_get_ns(); 197 | if (val->finished == 0) 198 | val->finished = finished; 199 | } else { 200 | struct flowval initval = {0}; 201 | initval.cnt = 1; 202 | initval.data_bytes = skb->len; 203 | initval.flow_start_msec = bpf_ktime_get_ns(); 204 | initval.finished = finished; 205 | int ret = bpf_map_update_elem(&flow_stats, &key, &initval, BPF_ANY); 206 | if (ret != 0) { 207 | __u32 msg = skb->ingress_ifindex; 208 | bpf_perf_event_output(skb, &events, BPF_F_CURRENT_CPU, &msg, sizeof(msg)); 209 | overflow = 1; 210 | } 211 | } 212 | 213 | metrics_count_final(skb, overflow); 214 | } 215 | 216 | static inline int 217 | process_ipv4_tcp(struct __sk_buff *skb) 218 | { 219 | __u64 data = skb->data; 220 | __u64 data_end = skb->data_end; 221 | __u64 pkt_len = 0; 222 | 223 | struct iphdr *ih = (struct iphdr *)(data + sizeof(struct ethhdr)); 224 | assert_len(ih, data_end); 225 | pkt_len = data_end - data; 226 | 227 | __u8 hdr_len = ih->ihl * 4; 228 | struct tcphdr *th = (struct tcphdr *)((char *)ih + hdr_len); 229 | assert_len(th, data_end); 230 | 231 | record(th, ih, skb); 232 | return TC_ACT_OK; 233 | } 234 | 235 | static inline int 236 | process_ipv4_icmp(struct __sk_buff *skb) 237 | { 238 | // bpf_printk("icmp packet"); 239 | return TC_ACT_OK; 240 | } 241 | 242 | static inline int 243 | process_ipv4_udp(struct __sk_buff *skb) 244 | { 245 | // bpf_printk("udp packet"); 246 | return TC_ACT_OK; 247 | } 248 | 249 | static inline int 250 | process_ipv4(struct __sk_buff *skb) 251 | { 252 | __u64 data = skb->data; 253 | __u64 data_end = skb->data_end; 254 | __u64 pkt_len = 0; 255 | 256 | struct iphdr *ih = (struct iphdr *)(data + sizeof(struct ethhdr)); 257 | assert_len(ih, data_end); 258 | pkt_len = data_end - data; 259 | 260 | if (ih->ihl < 5) 261 | return TC_ACT_SHOT; 262 | 263 | switch (ih->protocol) { 264 | case IPPROTO_ICMP: 265 | return process_ipv4_icmp(skb); 266 | case IPPROTO_TCP: 267 | return process_ipv4_tcp(skb); 268 | case IPPROTO_UDP: 269 | return process_ipv4_udp(skb); 270 | default: 271 | return TC_ACT_OK; 272 | } 273 | } 274 | 275 | static inline int 276 | process_ethernet(struct __sk_buff *skb) 277 | { 278 | __u64 data = skb->data; 279 | __u64 data_end = skb->data_end; 280 | __u64 pkt_len = 0; 281 | 282 | struct ethhdr *eth_hdr = (struct ethhdr *)data; 283 | assert_len(eth_hdr, data_end); 284 | pkt_len = data_end - data; 285 | 286 | switch (bpf_htons(eth_hdr->h_proto)) { 287 | case 0x0800: 288 | return process_ipv4(skb); 289 | default: 290 | return TC_ACT_SHOT; 291 | } 292 | } 293 | 294 | SEC("xdp-ingress") int 295 | xdp_ingress(struct xdp_md *ctx) 296 | { 297 | int ret = bpf_xdp_adjust_meta(ctx, -(int)sizeof(struct meta_info)); 298 | if (ret < 0) 299 | return XDP_ABORTED; 300 | return XDP_PASS; 301 | } 302 | 303 | SEC("tc-ingress") int 304 | tc_ingress(struct __sk_buff *skb) 305 | { 306 | struct meta_info *meta = (struct meta_info *)skb->data_meta; 307 | assert_len(meta, skb->data); 308 | meta->tstamp = bpf_ktime_get_ns(); 309 | return TC_ACT_OK; 310 | } 311 | 312 | SEC("tc-egress") int 313 | tc_egress(struct __sk_buff *skb) 314 | { 315 | return process_ethernet(skb); 316 | } 317 | 318 | char __license[] SEC("license") = "GPL"; 319 | -------------------------------------------------------------------------------- /pkg/flowctl/dump.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package flowctl 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | "strconv" 25 | 26 | "github.com/spf13/cobra" 27 | "github.com/wide-vsix/linux-flow-exporter/pkg/ebpfmap" 28 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 29 | ) 30 | 31 | var cliOptDump = struct { 32 | Output string 33 | }{} 34 | 35 | func NewCommandDump() *cobra.Command { 36 | cmd := &cobra.Command{ 37 | Use: "dump", 38 | RunE: fnDump, 39 | } 40 | cmd.Flags().StringVarP(&cliOptDump.Output, "output", "o", "normal", 41 | "Output format. One of: normal|wide|json") 42 | return cmd 43 | } 44 | 45 | func fnDump(cmd *cobra.Command, args []string) error { 46 | flows, err := ebpfmap.Dump() 47 | if err != nil { 48 | return err 49 | } 50 | 51 | table := util.NewTableWriter(os.Stdout) 52 | hdr := []string{"Iif", "Oif", "Proto", "Src", "Dst", "Mark", "Pkts", "Bytes"} 53 | if cliOptDump.Output == "wide" { 54 | hdr = append(hdr, []string{"Start", "End", "Finished"}...) 55 | } 56 | table.SetHeader(hdr) 57 | 58 | for _, flow := range flows { 59 | data := []string{ 60 | fmt.Sprintf("%d", flow.Key.IngressIfindex), 61 | fmt.Sprintf("%d", flow.Key.EgressIfindex), 62 | fmt.Sprintf("%d", flow.Key.Proto), 63 | fmt.Sprintf("%s:%d", util.ConvertUint32ToIP(flow.Key.Saddr), flow.Key.Sport), 64 | fmt.Sprintf("%s:%d", util.ConvertUint32ToIP(flow.Key.Daddr), flow.Key.Dport), 65 | fmt.Sprintf("%d", flow.Key.Mark), 66 | fmt.Sprintf("%d", flow.Val.FlowPkts), 67 | fmt.Sprintf("%d", flow.Val.FlowBytes), 68 | } 69 | if cliOptDump.Output == "wide" { 70 | data = append(data, []string{ 71 | fmt.Sprintf("%d", flow.Val.FlowStartMilliSecond), 72 | fmt.Sprintf("%d", flow.Val.FlowEndMilliSecond), 73 | strconv.FormatBool(flow.Val.Finished == uint8(1)), 74 | }...) 75 | } 76 | table.Append(data) 77 | } 78 | table.Render() 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /pkg/flowctl/flush.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package flowctl 20 | 21 | import ( 22 | "net" 23 | 24 | "github.com/spf13/cobra" 25 | "github.com/wide-vsix/linux-flow-exporter/pkg/ebpfmap" 26 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 27 | ) 28 | 29 | var cliOptFlush = struct { 30 | All bool 31 | Ifindex int 32 | Proto int 33 | Saddr string 34 | Daddr string 35 | Sport int 36 | Dport int 37 | }{} 38 | 39 | func NewCommandFlush() *cobra.Command { 40 | cmd := &cobra.Command{ 41 | Use: "flush", 42 | RunE: fnFlush, 43 | } 44 | cmd.Flags().BoolVarP(&cliOptFlush.All, "all", "A", false, 45 | "Flush all cache entries") 46 | cmd.Flags().IntVarP(&cliOptFlush.Ifindex, "ifindex", "i", 0, 47 | "Specifiy flow ifindex") 48 | cmd.Flags().IntVarP(&cliOptFlush.Proto, "proto", "p", 0, 49 | "Specifiy flow protocol (6,11,etc..)") 50 | cmd.Flags().IntVarP(&cliOptFlush.Sport, "sport", "S", 0, 51 | "Specifiy flow source port") 52 | cmd.Flags().IntVarP(&cliOptFlush.Dport, "dport", "D", 0, 53 | "Specifiy flow dest port") 54 | cmd.Flags().StringVarP(&cliOptFlush.Saddr, "saddr", "s", "", 55 | "Specifiy flow source address") 56 | cmd.Flags().StringVarP(&cliOptFlush.Daddr, "daddr", "d", "", 57 | "Specifiy flow dest address") 58 | return cmd 59 | } 60 | 61 | func fnFlush(cmd *cobra.Command, args []string) error { 62 | if cliOptFlush.All { 63 | if err := ebpfmap.DeleteAll(); err != nil { 64 | return err 65 | } 66 | } else { 67 | if err := ebpfmap.Delete(ebpfmap.FlowKey{ 68 | IngressIfindex: uint32(cliOptFlush.Ifindex), 69 | Proto: uint8(cliOptFlush.Proto), 70 | Saddr: util.ConvertIPToUint32(net.ParseIP(cliOptFlush.Saddr)), 71 | Daddr: util.ConvertIPToUint32(net.ParseIP(cliOptFlush.Daddr)), 72 | Sport: uint16(cliOptFlush.Sport), 73 | Dport: uint16(cliOptFlush.Dport), 74 | }); err != nil { 75 | return err 76 | } 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/flowctl/prom.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package flowctl 20 | 21 | import ( 22 | "fmt" 23 | "net/http" 24 | "time" 25 | 26 | "github.com/wide-vsix/linux-flow-exporter/pkg/ebpfmap" 27 | 28 | "github.com/prometheus/client_golang/prometheus" 29 | "github.com/prometheus/client_golang/prometheus/promauto" 30 | "github.com/prometheus/client_golang/prometheus/promhttp" 31 | ) 32 | 33 | var ( 34 | // namespace is metrics's head strings 35 | namespace = "linux_flow_exporter" 36 | // overflowPkts represents "total" of pkts couldn't be metered 37 | overflowPkts = promauto.NewGaugeVec( 38 | prometheus.GaugeOpts{ 39 | Namespace: namespace, 40 | Name: "overflow_pkts", 41 | Help: "eBPF map overflow counter for each interfaces", 42 | }, 43 | []string{"ingressIfindex", "egressIfindex"}, 44 | ) 45 | // overflowBytes represents "total" of bytes couldn't be metered 46 | overflowBytes = promauto.NewGaugeVec( 47 | prometheus.GaugeOpts{ 48 | Namespace: namespace, 49 | Name: "overflow_bytes", 50 | Help: "eBPF map overflow counter for each interfaces", 51 | }, 52 | []string{"ingressIfindex", "egressIfindex"}, 53 | ) 54 | // totalPkts represents "total" of pkts could be metered 55 | totalPkts = promauto.NewGaugeVec( 56 | prometheus.GaugeOpts{ 57 | Namespace: namespace, 58 | Name: "total_pkts", 59 | Help: "eBPF map total counter for each interfaces", 60 | }, 61 | []string{"ingressIfindex", "egressIfindex"}, 62 | ) 63 | // totalBytes represents "total" of bytes could be metered 64 | totalBytes = promauto.NewGaugeVec( 65 | prometheus.GaugeOpts{ 66 | Namespace: namespace, 67 | Name: "total_bytes", 68 | Help: "eBPF map total counter for each interfaces", 69 | }, 70 | []string{"ingressIfindex", "egressIfindex"}, 71 | ) 72 | // totalLatencyNanoseconds sums up the total time taken for the transfer. 73 | totalLatencyNanoseconds = promauto.NewGaugeVec( 74 | prometheus.GaugeOpts{ 75 | Namespace: namespace, 76 | Name: "total_latency_nanoseconds", 77 | }, 78 | []string{"ingressIfindex", "egressIfindex"}, 79 | ) 80 | ) 81 | 82 | func threadMetricsExporter() error { 83 | go func() { 84 | for { 85 | stats, err := ebpfmap.GetStats() 86 | if err != nil { 87 | continue 88 | } 89 | for key, metrics := range stats { 90 | l := prometheus.Labels{ 91 | "ingressIfindex": fmt.Sprintf("%d", key.IngressIfindex), 92 | "egressIfindex": fmt.Sprintf("%d", key.EgressIfindex), 93 | } 94 | overflowPkts.With(l).Set(float64(metrics.OverflowPkts)) 95 | overflowBytes.With(l).Set(float64(metrics.OverflowBytes)) 96 | totalPkts.With(l).Set(float64(metrics.TotalPkts)) 97 | totalBytes.With(l).Set(float64(metrics.TotalBytes)) 98 | totalLatencyNanoseconds.With(l).Set(float64(metrics.TotalLatencyNanoseconds)) 99 | } 100 | time.Sleep(time.Second) 101 | } 102 | }() 103 | http.Handle("/metrics", promhttp.Handler()) 104 | http.ListenAndServe(":9999", nil) 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /pkg/goroute2/link.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package goroute2 20 | 21 | import ( 22 | "encoding/json" 23 | "fmt" 24 | 25 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 26 | ) 27 | 28 | type LinkDetailXdpProg struct { 29 | ID int `json:"id"` 30 | Name string `json:"name"` 31 | Tag string `json:"tag"` 32 | Jited int `json:"jited"` 33 | LoadTime uint64 `json:"load_time"` 34 | CreateByUid int `json:"created_by_uid"` 35 | BtfID int `json:"btf_id"` 36 | } 37 | 38 | type XdpMode int 39 | 40 | func (xm XdpMode) String() string { 41 | switch int(xm) { 42 | case 2: 43 | return "generic" 44 | case 1: 45 | return "native" 46 | default: 47 | return "unknown" 48 | } 49 | } 50 | 51 | type LinkDetailXdp struct { 52 | Mode XdpMode `json:"mode"` 53 | Prog LinkDetailXdpProg `json:"prog"` 54 | } 55 | 56 | type LinkDetail struct { 57 | Ifindex int `json:"ifindex"` 58 | Ifname string `json:"ifname"` 59 | Flags []string `json:"flags"` 60 | Mtu int `json:"mtu"` 61 | Xdp *LinkDetailXdp `json:"xdp"` 62 | LinkInfo *struct { 63 | InfoKind string `json:"info_kind"` 64 | } `json:"linkinfo"` 65 | } 66 | 67 | func GetLinkDetail(netns, dev string) (*LinkDetail, error) { 68 | n := "" 69 | if netns != "" { 70 | n = fmt.Sprintf("ip netns exec %s", netns) 71 | } 72 | o, err := util.LocalExecutef("%s ip -j -d link show dev %s", n, dev) 73 | if err != nil { 74 | return nil, err 75 | } 76 | ld := []LinkDetail{} 77 | if err := json.Unmarshal([]byte(o), &ld); err != nil { 78 | return nil, err 79 | } 80 | return &ld[0], nil 81 | } 82 | -------------------------------------------------------------------------------- /pkg/goroute2/netns.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package goroute2 20 | 21 | import ( 22 | "encoding/json" 23 | "fmt" 24 | "regexp" 25 | 26 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 27 | ) 28 | 29 | func ListNetns() ([]string, error) { 30 | o, err := util.LocalExecute("ip -j netns") 31 | if err != nil { 32 | return nil, err 33 | } 34 | type Netns struct { 35 | Name string `json:"name"` 36 | } 37 | netnss := []Netns{} 38 | if err := json.Unmarshal([]byte(o), &netnss); err != nil { 39 | return nil, err 40 | } 41 | ret := []string{} 42 | for _, netns := range netnss { 43 | ret = append(ret, netns.Name) 44 | } 45 | return ret, nil 46 | } 47 | 48 | type Link struct { 49 | Ifindex uint32 `json:"ifindex"` 50 | Ifname string `json:"ifname"` 51 | Operstate string `json:"operstate"` 52 | Linkmode string `json:"linkmode"` 53 | Group string `json:"group"` 54 | LinkType string `json:"link_type"` 55 | Address string `json:"address"` 56 | Broadcast string `json:"broadcast"` 57 | Mtu int `json:"mtu"` 58 | } 59 | 60 | func ListLink(netns string) ([]Link, error) { 61 | n := "" 62 | if netns != "" { 63 | n = fmt.Sprintf("ip netns exec %s", netns) 64 | } 65 | o, err := util.LocalExecutef("%s ip -d -j link list", n) 66 | if err != nil { 67 | return nil, err 68 | } 69 | links := []Link{} 70 | if err := json.Unmarshal([]byte(o), &links); err != nil { 71 | return nil, err 72 | } 73 | return links, nil 74 | } 75 | 76 | func ListLinkMatch(netns string, regex string) ([]Link, error) { 77 | list, err := ListLink(netns) 78 | if err != nil { 79 | return nil, err 80 | } 81 | filtered := []Link{} 82 | for _, item := range list { 83 | re := regexp.MustCompile(regex) 84 | if re.Match([]byte(item.Ifname)) { 85 | filtered = append(filtered, item) 86 | } 87 | } 88 | return filtered, nil 89 | } 90 | -------------------------------------------------------------------------------- /pkg/goroute2/tc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package goroute2 20 | 21 | import ( 22 | "encoding/json" 23 | "fmt" 24 | 25 | "github.com/wide-vsix/linux-flow-exporter/pkg/util" 26 | ) 27 | 28 | type TcFilterRule struct { 29 | Protocol string `json:"protocol"` 30 | Pref uint `json:"pref"` 31 | Kind string `json:"kind"` 32 | Chain uint `json:"chain"` 33 | Options struct { 34 | Handle string `json:"handle"` 35 | BpfName string `json:"bpf_name"` 36 | NotInHw bool `json:"not_in_hw"` 37 | Prog struct { 38 | ID int `json:"id"` 39 | Name string `json:"name"` 40 | Tag string `json:"tag"` 41 | Jited int `json:"jited"` 42 | } `json:"prog"` 43 | } `json:"options"` 44 | } 45 | 46 | func ListTcFilterRules(netns, dev, dir string) ([]TcFilterRule, error) { 47 | n := "" 48 | if netns != "" { 49 | n = fmt.Sprintf("ip netns exec %s", netns) 50 | } 51 | o, err := util.LocalExecutef("%s tc -j filter list dev %s %s", n, dev, dir) 52 | if err != nil { 53 | return nil, err 54 | } 55 | rules := []TcFilterRule{} 56 | if err := json.Unmarshal([]byte(o), &rules); err != nil { 57 | return nil, err 58 | } 59 | return rules, nil 60 | } 61 | 62 | // $ tc -j qdisc list dev eth1 | jq 63 | // [ 64 | // { 65 | // "kind": "noqueue", 66 | // "handle": "0:", 67 | // "root": true, 68 | // "refcnt": 2, 69 | // "options": {} 70 | // }, 71 | // { 72 | // "kind": "clsact", 73 | // "handle": "ffff:", 74 | // "parent": "ffff:fff1", 75 | // "options": {} 76 | // } 77 | // ] 78 | type TcQdisc struct { 79 | Kind string `json:"kind"` 80 | Handle string `json:"handle"` 81 | Root bool `json:"root"` 82 | Refcnt uint `json:"refcnt"` 83 | } 84 | 85 | func ListTcQdisc(netns, dev string) ([]TcQdisc, error) { 86 | n := "" 87 | if netns != "" { 88 | n = fmt.Sprintf("ip netns exec %s", netns) 89 | } 90 | o, err := util.LocalExecutef("%s tc -j qdisc list dev %s", n, dev) 91 | if err != nil { 92 | return nil, err 93 | } 94 | rules := []TcQdisc{} 95 | if err := json.Unmarshal([]byte(o), &rules); err != nil { 96 | return nil, err 97 | } 98 | return rules, nil 99 | } 100 | 101 | func ClsActIsEnabled(netns, dev string) (bool, error) { 102 | qdiscs, err := ListTcQdisc(netns, dev) 103 | if err != nil { 104 | return false, err 105 | } 106 | for _, qdisc := range qdiscs { 107 | if qdisc.Kind == "clsact" { 108 | return true, nil 109 | } 110 | } 111 | return false, nil 112 | } 113 | 114 | func EnsureClsactEnabled(netns, dev string) error { 115 | clsActIsEnabled, err := ClsActIsEnabled(netns, dev) 116 | if err != nil { 117 | return err 118 | } 119 | if !clsActIsEnabled { 120 | netnsPreCmd := "" 121 | if netns != "" { 122 | netnsPreCmd = fmt.Sprintf("ip netns exec %s", netns) 123 | } 124 | if _, err := util.LocalExecutef("%s tc qdisc add dev %s clsact", 125 | netnsPreCmd, dev); err != nil { 126 | return err 127 | } 128 | } 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /pkg/hook/command.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 LINE Corporation. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package hook 19 | 20 | import ( 21 | "bytes" 22 | "encoding/json" 23 | "fmt" 24 | "os/exec" 25 | ) 26 | 27 | type Command string 28 | 29 | var _ Hook = (*Command)(nil) 30 | 31 | func (c Command) ExecuteBatch(in []map[string]interface{}) ( 32 | []map[string]interface{}, error) { 33 | 34 | // Prepare input/output 35 | stdoutbuf := bytes.Buffer{} 36 | stderrbuf := bytes.Buffer{} 37 | stdinbytes, err := json.Marshal(in) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | // Execute child process 43 | cmd := exec.Command(string(c)) 44 | cmd.Stdout = &stdoutbuf 45 | cmd.Stderr = &stderrbuf 46 | cmd.Stdin = bytes.NewBuffer(stdinbytes) 47 | if err := cmd.Run(); err != nil { 48 | return nil, fmt.Errorf("child process is failed: err=%v stderr=%s", 49 | err, stderrbuf.String()) 50 | } 51 | 52 | // Convert back to map data from json-bytes 53 | out := []map[string]interface{}{} 54 | if err := json.Unmarshal(stdoutbuf.Bytes(), &out); err != nil { 55 | return nil, err 56 | } 57 | return out, nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/hook/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 LINE Corporation. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package hook 19 | 20 | type Hook interface { 21 | ExecuteBatch(in []map[string]interface{}) ([]map[string]interface{}, error) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/hook/shell.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 LINE Corporation. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | package hook 19 | 20 | import ( 21 | "bytes" 22 | "crypto/sha1" 23 | "encoding/json" 24 | "fmt" 25 | "os" 26 | "os/exec" 27 | ) 28 | 29 | type Shell string 30 | 31 | var _ Hook = (*Shell)(nil) 32 | 33 | func (s Shell) ExecuteBatch(in []map[string]interface{}) ( 34 | []map[string]interface{}, error) { 35 | 36 | // Create temp file from hook shell script 37 | hash := sha1.New() 38 | hash.Write([]byte(s)) 39 | filename := fmt.Sprintf("/tmp/%x.sh", hash.Sum(nil)) 40 | if err := os.WriteFile(filename, []byte(s), 0755); err != nil { 41 | return nil, err 42 | } 43 | 44 | // Prepare input/output 45 | stdoutbuf := bytes.Buffer{} 46 | stderrbuf := bytes.Buffer{} 47 | stdinbytes, err := json.Marshal(in) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | // Execute child process 53 | cmd := exec.Command(filename) 54 | cmd.Stdout = &stdoutbuf 55 | cmd.Stderr = &stderrbuf 56 | cmd.Stdin = bytes.NewBuffer(stdinbytes) 57 | if err := cmd.Run(); err != nil { 58 | return nil, fmt.Errorf("child process is failed: err=%v stderr=%s", 59 | err, stderrbuf.String()) 60 | } 61 | 62 | // Convert back to map data from json-bytes 63 | out := []map[string]interface{}{} 64 | if err := json.Unmarshal(stdoutbuf.Bytes(), &out); err != nil { 65 | return nil, err 66 | } 67 | return out, nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/ipfix/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package ipfix 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/wide-vsix/linux-flow-exporter/pkg/hook" 25 | ) 26 | 27 | type OutputCollector struct { 28 | RemoteAddress string `yaml:"remoteAddress"` 29 | LocalAddress string `yaml:"localAddress"` 30 | } 31 | 32 | // Hook can speficy external mechianism to make log-data updated Only one of the 33 | // other Hook backends will be enabled. 34 | // 35 | // DEVELOPER_NOTE(slankdev): 36 | // To add a new hook backend, follow hook.Command, hook.Shell in /pkg/hook. If 37 | // you add hook.Foo to type Hook struct, please edit Hook.Valid() function and 38 | // Hook.Execute() function at the same time. 39 | // 40 | // TODO(slankdev): 41 | // performance Currently, an external program is executed for a single log data, 42 | // but this is inefficient for a large number of log data, so batch processing 43 | // should be used in the future. 44 | type Hook struct { 45 | // Name make operator to know which hook is executed or failed. 46 | Name string `yaml:"name"` 47 | // Command is a hook to argument data using an external program like CNI. 48 | // It sends log data via standard input to the command it executes. Receive 49 | // modified log data on stdout. If the command fails, the log data is lost. 50 | // It respects ansible.builtin.command, but may be changed in the future. 51 | // 52 | // ## Example 53 | // ``` 54 | // hooks: 55 | // - command: /usr/bin/cmd1 56 | // ``` 57 | Command *hook.Command `yaml:"command"` 58 | // Shell is the backend that executes the external program. It is similar to 59 | // the Command hook, but it allows you to write shell scripts directly in the 60 | // config file, so you should use this one for simple operations. For example, 61 | // you can use jq to add property, resolve ifname from ifindex, add hostname, 62 | // and so on. 63 | // 64 | // ## Example ``` hooks: - name: test hook1 65 | // hooks: 66 | // - shell: | 67 | // #!/bin/sh 68 | // echo `cat` | jq --arg hostname $(hostname) '. + {hostname: $hostname}' 69 | // ``` 70 | Shell *hook.Shell `yaml:"shell"` 71 | } 72 | 73 | func (h Hook) Valid() bool { 74 | cnt := 0 75 | if h.Command != nil { 76 | cnt++ 77 | } 78 | if h.Shell != nil { 79 | cnt++ 80 | } 81 | return cnt == 1 82 | } 83 | 84 | func (h Hook) ExecuteBatch(m []map[string]interface{}) ([]map[string]interface{}, error) { 85 | if !h.Valid() { 86 | return nil, fmt.Errorf("invalid hook") 87 | } 88 | if h.Shell != nil { 89 | return h.Shell.ExecuteBatch(m) 90 | } 91 | if h.Command != nil { 92 | return h.Command.ExecuteBatch(m) 93 | } 94 | return nil, fmt.Errorf("(no reach code)") 95 | } 96 | 97 | type OutputLog struct { 98 | File string `yaml:"file"` 99 | // Hooks are the extention for special argmentation. Multiple Hooks can be 100 | // set. You can change the data structure as you like by using an external 101 | // mechanism called flowctl agent. Hooks are arrays and are executed in order. 102 | // If a Hook fails, the log data will be lost. 103 | Hooks []Hook `yaml:"hooks"` 104 | } 105 | 106 | type Output struct { 107 | Collector *OutputCollector `yaml:"collector"` 108 | Log *OutputLog `yaml:"log"` 109 | } 110 | 111 | func (o Output) Valid() bool { 112 | return !(o.Collector != nil && o.Log != nil) 113 | } 114 | 115 | type Config struct { 116 | // MaxIpfixMessageLen Indicates the maximum size of an IPFIX message. The 117 | // message is divided and sent according to this value. This value is shared 118 | // by all collector output instances. 119 | MaxIpfixMessageLen int `yaml:"maxIpfixMessageLen"` 120 | // TimerTemplateFlushSeconds indicates the interval for sending IPFIX flow 121 | // template periodically. This value is shared by all collector output 122 | // instances. 123 | TimerTemplateFlushSeconds uint `yaml:"timerTemplateFlushSeconds"` 124 | // TimerFinishedDrainSecond indicates the interval to drain the finished Flow. 125 | // This interval is shared by all output instances. 126 | TimerFinishedDrainSeconds uint `yaml:"timerFinishedDrainSeconds"` 127 | // TimerForceDrainSecond specifies the interval to force a full Cache to be 128 | // drained for each Interface. This interval is shared by all output 129 | // instances. 130 | TimerForceDrainSeconds uint `yaml:"timerForceDrainSeconds"` 131 | // Output can contain multiple destinations to which the recorded flow cache 132 | // is transferred. IPFIX Collector, Filelog, etc. can be specified. 133 | Outputs []Output `yaml:"outputs"` 134 | Templates []struct { 135 | ID uint16 `yaml:"id"` 136 | Template []struct { 137 | Name string `yaml:"name"` 138 | } `yaml:"template"` 139 | } `yaml:"templates"` 140 | } 141 | 142 | type FlowFile struct { 143 | FlowSets []struct { 144 | TemplateID uint16 `yaml:"templateId"` 145 | Flows []Flow `yaml:"flows"` 146 | } `yaml:"flowsets"` 147 | } 148 | 149 | func (f FlowFile) ToFlowDataMessages(config *Config, 150 | seqnumStart int) ([]FlowDataMessage, error) { 151 | 152 | flowSeq := uint32(seqnumStart) 153 | msgs := []FlowDataMessage{} 154 | for _, fs := range f.FlowSets { 155 | // NOTE: fragmentation is needed 156 | // If you send a lot of flow information, you need to split IPFIX messages 157 | // according to the UDP mtu size. nFlows indicates how many flow information 158 | // are included in one IPFIX message. 159 | hdrLen := 20 // ipfix-hdr(16) + flowset-hdr(4) 160 | flowLen, err := config.getTemplateLength(fs.TemplateID) 161 | if err != nil { 162 | return nil, err 163 | } 164 | nFlows := (int(config.MaxIpfixMessageLen) - int(hdrLen)) / int(flowLen) 165 | 166 | // NOTE: Assemble the IPFIX message by dividing 167 | // the flow list according to the nFlow value. 168 | flows := fs.Flows 169 | for len(flows) > 0 { 170 | var n int 171 | if len(flows) < nFlows { 172 | n = len(flows) 173 | } else { 174 | n = nFlows 175 | } 176 | msgs = append(msgs, FlowDataMessage{ 177 | Header: Header{ 178 | VersionNumber: 10, 179 | SysupTime: 0, 180 | SequenceNumber: flowSeq, 181 | SourceID: 0, 182 | }, 183 | FlowSets: []FlowSet{ 184 | { 185 | FlowSetID: fs.TemplateID, 186 | Flow: flows[:n], 187 | }, 188 | }, 189 | }) 190 | flowSeq += uint32(n) 191 | flows = flows[n:] 192 | } 193 | 194 | } 195 | return msgs, nil 196 | } 197 | 198 | type fieldTableItem struct { 199 | Name string 200 | Value uint16 201 | Length int 202 | } 203 | 204 | func (c Config) ToFlowTemplatesMessage() (TemplateMessage, error) { 205 | msg := TemplateMessage{ 206 | Header: Header{ 207 | VersionNumber: 10, 208 | SysupTime: 0, 209 | SequenceNumber: 0, 210 | SourceID: 0, 211 | }, 212 | } 213 | 214 | for _, item := range c.Templates { 215 | fields := []FlowTemplateField{} 216 | for _, template := range item.Template { 217 | value, err := getIPFixFieldsValueByName(template.Name) 218 | if err != nil { 219 | return msg, err 220 | } 221 | length, err := getIPFixFieldsLengthByName(template.Name) 222 | if err != nil { 223 | return msg, err 224 | } 225 | 226 | fields = append(fields, FlowTemplateField{ 227 | FieldType: uint16(value), 228 | FieldLength: uint16(length), 229 | }) 230 | } 231 | 232 | msg.Templates = append(msg.Templates, FlowTemplate{ 233 | TemplateID: item.ID, 234 | Fields: fields, 235 | }) 236 | } 237 | return msg, nil 238 | } 239 | 240 | func getIPFixFieldsValueByName(name string) (uint16, error) { 241 | for _, field := range ipfixfields { 242 | if field.Name == name { 243 | return field.Value, nil 244 | } 245 | } 246 | return 0, fmt.Errorf("not found") 247 | } 248 | 249 | func getIPFixFieldsLengthByName(name string) (int, error) { 250 | for _, field := range ipfixfields { 251 | if field.Name == name { 252 | return field.Length, nil 253 | } 254 | } 255 | return 0, fmt.Errorf("not found") 256 | } 257 | 258 | func getTemplateFieldTypes(id uint16, config *Config) ([]uint16, error) { 259 | for _, template := range config.Templates { 260 | if template.ID == id { 261 | fields := []uint16{} 262 | for _, t := range template.Template { 263 | v, err := getIPFixFieldsValueByName(t.Name) 264 | if err != nil { 265 | return nil, err 266 | } 267 | fields = append(fields, uint16(v)) 268 | } 269 | return fields, nil 270 | } 271 | } 272 | return nil, fmt.Errorf("not found") 273 | } 274 | 275 | func (c Config) getTemplateLength(id uint16) (int, error) { 276 | for _, template := range c.Templates { 277 | if template.ID == id { 278 | len := 0 279 | for _, item := range template.Template { 280 | tmpLen, err := getIPFixFieldsLengthByName(item.Name) 281 | if err != nil { 282 | return 0, err 283 | } 284 | len += tmpLen 285 | } 286 | return len, nil 287 | } 288 | } 289 | return 0, fmt.Errorf("not found") 290 | } 291 | -------------------------------------------------------------------------------- /pkg/ipfix/editme.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package ipfix 20 | 21 | import ( 22 | "bytes" 23 | "encoding/binary" 24 | 25 | "github.com/cloudflare/goflow/decoders/netflow" 26 | ) 27 | 28 | type Flow struct { 29 | FlowEndMilliseconds uint64 `yaml:"FlowEndMilliseconds"` 30 | FlowStartMilliseconds uint64 `yaml:"FlowStartMilliseconds"` 31 | FlowEndNanoseconds uint64 `yaml:"FlowEndNanoseconds"` 32 | FlowStartNanoseconds uint64 `yaml:"FlowStartNanoseconds"` 33 | OctetDeltaCount uint64 `yaml:"OctetDeltaCount"` 34 | PacketDeltaCount uint64 `yaml:"PacketDeltaCount"` 35 | IpVersion uint8 `yaml:"IpVersion"` 36 | IngressInterface uint32 `yaml:"IngressInterface"` 37 | EgressInterface uint32 `yaml:"EgressInterface"` 38 | FlowDirection uint8 `yaml:"FlowDirection"` 39 | SourceIPv4Address uint32 `yaml:"SourceIPv4Address"` 40 | DestinationIPv4Address uint32 `yaml:"DestinationIPv4Address"` 41 | SourceTransportPort uint16 `yaml:"SourceTransportPort"` 42 | DestinationTransportPort uint16 `yaml:"DestinationTransportPort"` 43 | TcpControlBits uint8 `yaml:"TcpControlBits"` 44 | ProtocolIdentifier uint8 `yaml:"ProtocolIdentifier"` 45 | IpClassOfService uint8 `yaml:"IpClassOfService"` 46 | SourceIPv4PrefixLength uint8 `yaml:"SourceIPv4PrefixLength"` 47 | DestinationIPv4PrefixLength uint8 `yaml:"DestinationIPv4PrefixLength"` 48 | IpNextHopIPv4Address uint32 `yaml:"IpNextHopIPv4Address"` 49 | BgpSourceAsNumber uint32 `yaml:"BgpSourceAsNumber"` 50 | BgpDestinationAsNumber uint32 `yaml:"BgpDestinationAsNumber"` 51 | BgpNextHopIPv4Address uint32 `yaml:"BgpNextHopIPv4Address"` 52 | IcmpTypeCodeIPv4 uint16 `yaml:"IcmpTypeCodeIPv4"` 53 | MinimumTTL uint8 `yaml:"MinimumTTL"` 54 | MaximumTTL uint8 `yaml:"MaximumTTL"` 55 | FragmentIdentification uint32 `yaml:"FragmentIdentification"` 56 | VlanId uint16 `yaml:"VlanId"` 57 | FlowEndReason uint8 `yaml:"FlowEndReason"` 58 | Dot1qVlanId uint16 `yaml:"Dot1qVlanId"` 59 | Dot1qCustomerVlanId uint16 `yaml:"Dot1qCustomerVlanId"` 60 | } 61 | 62 | var ipfixfields = []fieldTableItem{ 63 | {"FlowEndMilliseconds", netflow.IPFIX_FIELD_flowEndMilliseconds, 8}, 64 | {"FlowStartMilliseconds", netflow.IPFIX_FIELD_flowStartMilliseconds, 8}, 65 | {"FlowEndNanoseconds", netflow.IPFIX_FIELD_flowEndNanoseconds, 8}, 66 | {"FlowStartNanoseconds", netflow.IPFIX_FIELD_flowStartNanoseconds, 8}, 67 | {"OctetDeltaCount", netflow.IPFIX_FIELD_octetDeltaCount, 8}, 68 | {"PacketDeltaCount", netflow.IPFIX_FIELD_packetDeltaCount, 8}, 69 | {"IpVersion", netflow.IPFIX_FIELD_ipVersion, 1}, 70 | {"IngressInterface", netflow.IPFIX_FIELD_ingressInterface, 4}, 71 | {"EgressInterface", netflow.IPFIX_FIELD_egressInterface, 4}, 72 | {"FlowDirection", netflow.IPFIX_FIELD_flowDirection, 1}, 73 | {"SourceIPv4Address", netflow.IPFIX_FIELD_sourceIPv4Address, 4}, 74 | {"DestinationIPv4Address", netflow.IPFIX_FIELD_destinationIPv4Address, 4}, 75 | {"SourceTransportPort", netflow.IPFIX_FIELD_sourceTransportPort, 2}, 76 | {"DestinationTransportPort", netflow.IPFIX_FIELD_destinationTransportPort, 2}, 77 | {"TcpControlBits", netflow.IPFIX_FIELD_tcpControlBits, 1}, 78 | {"ProtocolIdentifier", netflow.IPFIX_FIELD_protocolIdentifier, 1}, 79 | {"IpClassOfService", netflow.IPFIX_FIELD_ipClassOfService, 1}, 80 | {"SourceIPv4PrefixLength", netflow.IPFIX_FIELD_sourceIPv4PrefixLength, 1}, 81 | {"DestinationIPv4PrefixLength", netflow.IPFIX_FIELD_destinationIPv4PrefixLength, 1}, 82 | {"IpNextHopIPv4Address", netflow.IPFIX_FIELD_ipNextHopIPv4Address, 4}, 83 | {"BgpSourceAsNumber", netflow.IPFIX_FIELD_bgpSourceAsNumber, 4}, 84 | {"BgpDestinationAsNumber", netflow.IPFIX_FIELD_bgpDestinationAsNumber, 4}, 85 | {"BgpNextHopIPv4Address", netflow.IPFIX_FIELD_bgpNextHopIPv4Address, 4}, 86 | {"IcmpTypeCodeIPv4", netflow.IPFIX_FIELD_icmpTypeCodeIPv4, 2}, 87 | {"MinimumTTL", netflow.IPFIX_FIELD_minimumTTL, 1}, 88 | {"MaximumTTL", netflow.IPFIX_FIELD_maximumTTL, 1}, 89 | {"FragmentIdentification", netflow.IPFIX_FIELD_fragmentIdentification, 4}, 90 | {"VlanId", netflow.IPFIX_FIELD_vlanId, 2}, 91 | {"FlowEndReason", netflow.IPFIX_FIELD_flowEndReason, 1}, 92 | {"Dot1qVlanId", netflow.IPFIX_FIELD_dot1qVlanId, 2}, 93 | {"Dot1qCustomerVlanId", netflow.IPFIX_FIELD_dot1qCustomerVlanId, 2}, 94 | } 95 | 96 | func binaryWrite(fieldType uint16, buf *bytes.Buffer, flow *Flow) error { 97 | switch fieldType { 98 | case netflow.IPFIX_FIELD_flowEndMilliseconds: 99 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowEndMilliseconds); err != nil { 100 | return err 101 | } 102 | case netflow.IPFIX_FIELD_flowStartMilliseconds: 103 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowStartMilliseconds); err != nil { 104 | return err 105 | } 106 | case netflow.IPFIX_FIELD_flowEndNanoseconds: 107 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowEndNanoseconds); err != nil { 108 | return err 109 | } 110 | case netflow.IPFIX_FIELD_flowStartNanoseconds: 111 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowStartNanoseconds); err != nil { 112 | return err 113 | } 114 | case netflow.IPFIX_FIELD_octetDeltaCount: 115 | if err := binary.Write(buf, binary.BigEndian, &flow.OctetDeltaCount); err != nil { 116 | return err 117 | } 118 | case netflow.IPFIX_FIELD_packetDeltaCount: 119 | if err := binary.Write(buf, binary.BigEndian, &flow.PacketDeltaCount); err != nil { 120 | return err 121 | } 122 | case netflow.IPFIX_FIELD_ipVersion: 123 | if err := binary.Write(buf, binary.BigEndian, &flow.IpVersion); err != nil { 124 | return err 125 | } 126 | case netflow.IPFIX_FIELD_ingressInterface: 127 | if err := binary.Write(buf, binary.BigEndian, &flow.IngressInterface); err != nil { 128 | return err 129 | } 130 | case netflow.IPFIX_FIELD_egressInterface: 131 | if err := binary.Write(buf, binary.BigEndian, &flow.EgressInterface); err != nil { 132 | return err 133 | } 134 | case netflow.IPFIX_FIELD_flowDirection: 135 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowDirection); err != nil { 136 | return err 137 | } 138 | case netflow.IPFIX_FIELD_sourceIPv4Address: 139 | if err := binary.Write(buf, binary.BigEndian, &flow.SourceIPv4Address); err != nil { 140 | return err 141 | } 142 | case netflow.IPFIX_FIELD_destinationIPv4Address: 143 | if err := binary.Write(buf, binary.BigEndian, &flow.DestinationIPv4Address); err != nil { 144 | return err 145 | } 146 | case netflow.IPFIX_FIELD_sourceTransportPort: 147 | if err := binary.Write(buf, binary.BigEndian, &flow.SourceTransportPort); err != nil { 148 | return err 149 | } 150 | case netflow.IPFIX_FIELD_destinationTransportPort: 151 | if err := binary.Write(buf, binary.BigEndian, &flow.DestinationTransportPort); err != nil { 152 | return err 153 | } 154 | case netflow.IPFIX_FIELD_tcpControlBits: 155 | if err := binary.Write(buf, binary.BigEndian, &flow.TcpControlBits); err != nil { 156 | return err 157 | } 158 | case netflow.IPFIX_FIELD_protocolIdentifier: 159 | if err := binary.Write(buf, binary.BigEndian, &flow.ProtocolIdentifier); err != nil { 160 | return err 161 | } 162 | case netflow.IPFIX_FIELD_ipClassOfService: 163 | if err := binary.Write(buf, binary.BigEndian, &flow.IpClassOfService); err != nil { 164 | return err 165 | } 166 | case netflow.IPFIX_FIELD_sourceIPv4PrefixLength: 167 | if err := binary.Write(buf, binary.BigEndian, &flow.SourceIPv4PrefixLength); err != nil { 168 | return err 169 | } 170 | case netflow.IPFIX_FIELD_destinationIPv4PrefixLength: 171 | if err := binary.Write(buf, binary.BigEndian, &flow.DestinationIPv4PrefixLength); err != nil { 172 | return err 173 | } 174 | case netflow.IPFIX_FIELD_ipNextHopIPv4Address: 175 | if err := binary.Write(buf, binary.BigEndian, &flow.IpNextHopIPv4Address); err != nil { 176 | return err 177 | } 178 | case netflow.IPFIX_FIELD_bgpSourceAsNumber: 179 | if err := binary.Write(buf, binary.BigEndian, &flow.BgpSourceAsNumber); err != nil { 180 | return err 181 | } 182 | case netflow.IPFIX_FIELD_bgpDestinationAsNumber: 183 | if err := binary.Write(buf, binary.BigEndian, &flow.BgpDestinationAsNumber); err != nil { 184 | return err 185 | } 186 | case netflow.IPFIX_FIELD_bgpNextHopIPv4Address: 187 | if err := binary.Write(buf, binary.BigEndian, &flow.BgpNextHopIPv4Address); err != nil { 188 | return err 189 | } 190 | case netflow.IPFIX_FIELD_icmpTypeCodeIPv4: 191 | if err := binary.Write(buf, binary.BigEndian, &flow.IcmpTypeCodeIPv4); err != nil { 192 | return err 193 | } 194 | case netflow.IPFIX_FIELD_minimumTTL: 195 | if err := binary.Write(buf, binary.BigEndian, &flow.MinimumTTL); err != nil { 196 | return err 197 | } 198 | case netflow.IPFIX_FIELD_maximumTTL: 199 | if err := binary.Write(buf, binary.BigEndian, &flow.MaximumTTL); err != nil { 200 | return err 201 | } 202 | case netflow.IPFIX_FIELD_fragmentIdentification: 203 | if err := binary.Write(buf, binary.BigEndian, &flow.FragmentIdentification); err != nil { 204 | return err 205 | } 206 | case netflow.IPFIX_FIELD_vlanId: 207 | if err := binary.Write(buf, binary.BigEndian, &flow.VlanId); err != nil { 208 | return err 209 | } 210 | case netflow.IPFIX_FIELD_flowEndReason: 211 | if err := binary.Write(buf, binary.BigEndian, &flow.FlowEndReason); err != nil { 212 | return err 213 | } 214 | case netflow.IPFIX_FIELD_dot1qVlanId: 215 | if err := binary.Write(buf, binary.BigEndian, &flow.Dot1qVlanId); err != nil { 216 | return err 217 | } 218 | case netflow.IPFIX_FIELD_dot1qCustomerVlanId: 219 | if err := binary.Write(buf, binary.BigEndian, &flow.Dot1qCustomerVlanId); err != nil { 220 | return err 221 | } 222 | } 223 | return nil 224 | } 225 | -------------------------------------------------------------------------------- /pkg/ipfix/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package ipfix 20 | 21 | import ( 22 | "bytes" 23 | "encoding/binary" 24 | ) 25 | 26 | type Header struct { 27 | VersionNumber uint16 28 | SysupTime uint32 29 | SequenceNumber uint32 30 | SourceID uint32 31 | } 32 | 33 | type FlowDataMessage struct { 34 | Header Header 35 | FlowSets []FlowSet 36 | } 37 | 38 | type FlowSet struct { 39 | FlowSetID uint16 `yaml:"flowSetId"` 40 | Flow []Flow `yaml:"flow"` 41 | } 42 | 43 | type TemplateMessage struct { 44 | Header Header 45 | Templates []FlowTemplate 46 | } 47 | 48 | type FlowTemplate struct { 49 | TemplateID uint16 50 | Fields []FlowTemplateField 51 | } 52 | 53 | type FlowTemplateField struct { 54 | FieldType uint16 55 | FieldLength uint16 56 | } 57 | 58 | func (m TemplateMessage) Write(buf *bytes.Buffer) error { 59 | cnt := 16 // ipfix message header length (const) 60 | for _, t := range m.Templates { 61 | cnt += 8 // ipfix flowset header length (const) 62 | cnt += 4 * len(t.Fields) // ipfix field definition length 63 | } 64 | 65 | // https://www.rfc-editor.org/rfc/rfc3954.html#section-5.1 66 | if err := binary.Write(buf, binary.BigEndian, &struct { 67 | VersionNumber uint16 68 | Count uint16 69 | SysupTime uint32 70 | SequenceNumber uint32 71 | SourceID uint32 72 | }{ 73 | VersionNumber: m.Header.VersionNumber, 74 | Count: uint16(cnt), 75 | SysupTime: m.Header.SysupTime, 76 | SequenceNumber: m.Header.SequenceNumber, 77 | SourceID: m.Header.SourceID, 78 | }); err != nil { 79 | return err 80 | } 81 | 82 | // https://www.rfc-editor.org/rfc/rfc3954.html#section-5.2 83 | for _, t := range m.Templates { 84 | const flowsetHdrLen = 8 85 | flowsetlen := len(t.Fields)*4 + flowsetHdrLen 86 | 87 | if err := binary.Write(buf, binary.BigEndian, &struct { 88 | FlowSetID uint16 89 | Length uint16 90 | TemplateID uint16 91 | FieldCount uint16 92 | }{ 93 | 2, // TODO(slankdev): 2 is FLOW-TEMPLATE 94 | uint16(flowsetlen), 95 | t.TemplateID, 96 | uint16(len(t.Fields)), 97 | }); err != nil { 98 | return err 99 | } 100 | for _, field := range t.Fields { 101 | if err := binary.Write(buf, binary.BigEndian, &field); err != nil { 102 | return err 103 | } 104 | } 105 | } 106 | return nil 107 | } 108 | 109 | func (m FlowDataMessage) Write(buf *bytes.Buffer, config *Config) error { 110 | cnt := 16 // ipfix message header length (const) 111 | for _, fs := range m.FlowSets { 112 | cnt += 4 // ipfix flowset header length (const) 113 | l, err := config.getTemplateLength(fs.FlowSetID) 114 | if err != nil { 115 | return err 116 | } 117 | cnt += l * len(fs.Flow) 118 | } 119 | 120 | // https://www.rfc-editor.org/rfc/rfc3954.html#section-5.1 121 | if err := binary.Write(buf, binary.BigEndian, &struct { 122 | VersionNumber uint16 123 | Count uint16 124 | SysupTime uint32 125 | SequenceNumber uint32 126 | SourceID uint32 127 | }{ 128 | VersionNumber: m.Header.VersionNumber, 129 | Count: uint16(cnt), 130 | SysupTime: m.Header.SysupTime, 131 | SequenceNumber: m.Header.SequenceNumber, 132 | SourceID: m.Header.SourceID, 133 | }); err != nil { 134 | return err 135 | } 136 | 137 | for _, fs := range m.FlowSets { 138 | flowSetLen, err := config.getTemplateLength(fs.FlowSetID) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | if err := binary.Write(buf, binary.BigEndian, &struct { 144 | FlowSetID uint16 145 | FlowSetLength uint16 146 | }{ 147 | FlowSetID: fs.FlowSetID, 148 | FlowSetLength: uint16(len(fs.Flow)*flowSetLen + 4), 149 | }); err != nil { 150 | return err 151 | } 152 | 153 | for _, f := range fs.Flow { 154 | ftypes, err := getTemplateFieldTypes(fs.FlowSetID, config) 155 | if err != nil { 156 | return err 157 | } 158 | for _, ftype := range ftypes { 159 | if err := binaryWrite(ftype, buf, &f); err != nil { 160 | return err 161 | } 162 | } 163 | } 164 | } 165 | return nil 166 | } 167 | -------------------------------------------------------------------------------- /pkg/util/bytes.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func BS16(v uint16) uint16 { 4 | v2 := uint16(0) 5 | v2 |= v<<8 | v>>8 6 | return v2 7 | } 8 | 9 | func BS32(v uint32) uint32 { 10 | v2 := uint32(0) 11 | v2 |= (0xff000000 & (v << 24)) | (0x00ff0000 & (v << 8)) | (0x0000ff00 & (v >> 8)) | (0x000000ff & (v >> 24)) 12 | return v2 13 | } 14 | -------------------------------------------------------------------------------- /pkg/util/cobra.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func NewCmdCompletion(rootCmd *cobra.Command) *cobra.Command { 29 | cmd := &cobra.Command{ 30 | Use: "completion [sub operation]", 31 | Short: "Display completion snippet", 32 | Args: cobra.MinimumNArgs(1), 33 | } 34 | 35 | cmd.AddCommand(&cobra.Command{ 36 | Use: "bash", 37 | Short: "Display bash-completion snippet", 38 | Args: cobra.MinimumNArgs(0), 39 | Run: func(cmd *cobra.Command, args []string) { 40 | _ = rootCmd.GenBashCompletion(os.Stdout) 41 | cli := " " 42 | for _, arg := range os.Args { 43 | cli += arg + " " 44 | } 45 | fmt.Printf("#### \". <(%s)\" in your bashrc¥n", cli) 46 | }, 47 | SilenceUsage: true, 48 | }) 49 | 50 | cmd.AddCommand(&cobra.Command{ 51 | Use: "zsh", 52 | Short: "Display zsh-completion snippet", 53 | Args: cobra.MinimumNArgs(0), 54 | Run: func(cmd *cobra.Command, args []string) { 55 | _ = rootCmd.GenZshCompletion(os.Stdout) 56 | }, 57 | SilenceUsage: true, 58 | }) 59 | 60 | return cmd 61 | } 62 | -------------------------------------------------------------------------------- /pkg/util/execute.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | "os/exec" 25 | 26 | "github.com/fatih/color" 27 | ) 28 | 29 | var silence = true 30 | 31 | func SetLocalExecuteSilence(v bool) { 32 | silence = v 33 | } 34 | 35 | func LocalExecute(cmdstr string) (string, error) { 36 | stdoutbuf := bytes.Buffer{} 37 | stderrbuf := bytes.Buffer{} 38 | 39 | cmd := exec.Command("sh", "-c", cmdstr) 40 | cmd.Stdout = &stdoutbuf 41 | cmd.Stderr = &stderrbuf 42 | 43 | if err := cmd.Run(); err != nil { 44 | str := fmt.Sprintf("CommandExecute [%s] ", cmd) 45 | str += color.RedString("Failed") 46 | str += color.RedString("%s", err.Error()) 47 | println(str) 48 | println(stderrbuf.String()) 49 | return "", err 50 | } 51 | 52 | if !silence { 53 | str := fmt.Sprintf("CommandExecute [%s] ", cmd) 54 | str += color.GreenString("Success") 55 | fmt.Printf("%s\n", str) 56 | } 57 | return stdoutbuf.String(), nil 58 | } 59 | 60 | func LocalExecutef(fs string, a ...interface{}) (string, error) { 61 | cmd := fmt.Sprintf(fs, a...) 62 | return LocalExecute(cmd) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/util/file.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "io/ioutil" 23 | 24 | "gopkg.in/yaml.v2" 25 | ) 26 | 27 | func FileUnmarshalAsYaml(in string, v interface{}) error { 28 | yamlFile, err := ioutil.ReadFile(in) 29 | if err != nil { 30 | return err 31 | } 32 | err = yaml.Unmarshal(yamlFile, v) 33 | if err != nil { 34 | return err 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/util/ip.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "bytes" 23 | "encoding/binary" 24 | "net" 25 | ) 26 | 27 | func ConvertUint32ToIP(nn uint32) net.IP { 28 | ip := make(net.IP, 4) 29 | binary.LittleEndian.PutUint32(ip, nn) 30 | return ip 31 | } 32 | 33 | func ConvertIPToUint32(ip net.IP) uint32 { 34 | var val uint32 35 | binary.Read(bytes.NewBuffer(ip.To4()), binary.LittleEndian, &val) 36 | return val 37 | } 38 | 39 | func UdpTransmit(local, remote string, buf *bytes.Buffer) error { 40 | laddr, err := net.ResolveUDPAddr("udp", local) 41 | if err != nil { 42 | return err 43 | } 44 | raddr, err := net.ResolveUDPAddr("udp", remote) 45 | if err != nil { 46 | return err 47 | } 48 | conn, err := net.DialUDP("udp", laddr, raddr) 49 | if err != nil { 50 | return err 51 | } 52 | defer conn.Close() 53 | if _, err = conn.Write(buf.Bytes()); err != nil { 54 | return err 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/util/misc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "fmt" 23 | "regexp" 24 | "strings" 25 | 26 | "github.com/hairyhenderson/go-which" 27 | "github.com/zcalusic/sysinfo" 28 | "golang.org/x/mod/semver" 29 | ) 30 | 31 | // GetClangVersion returns the version string of clang as semver-formant This 32 | // string format can be compared by semver buitin package. 33 | // ref: // https://pkg.go.dev/golang.org/x/mod/semver#Major 34 | // clang may not work well because version output results may differ depending 35 | // on the platform. 36 | // 37 | // DOCKER-TESTED: [centos:centos7, ubuntu:22.04, fedora:37] 38 | func GetClangVersion() (string, error) { 39 | clangPath := which.Which("clang") 40 | if clangPath == "" { 41 | return "", nil 42 | } 43 | 44 | out, err := LocalExecutef("%s --version", clangPath) 45 | if err != nil { 46 | return "", err 47 | } 48 | 49 | // [EXAMPLE:centos 7] 50 | // $ clang --version 51 | // clang version 3.4.2 (tags/RELEASE_34/dot2-final) 52 | // Target: x86_64-redhat-linux-gnu 53 | // Thread model: posix 54 | // 55 | // [EXAMPLE:fedora 37] 56 | // $ clang --version 57 | // clang version 15.0.0 (Fedora 15.0.0-5.fc38) 58 | // Target: x86_64-redhat-linux-gnu 59 | // Thread model: posix 60 | // InstalledDir: /usr/bin 61 | // 62 | // [EXAMPLE:ubuntu 22.04] 63 | // $ clang --version 64 | // Ubuntu clang version 12.0.1-19ubuntu3 65 | // Target: x86_64-pc-linux-gnu 66 | // Thread model: posix 67 | // InstalledDir: /usr/bin 68 | 69 | lines := regexp.MustCompile("\r\n|\n").Split(out, -1) 70 | for _, line := range lines { 71 | if strings.Contains(line, "clang version") { 72 | words := strings.Fields(line) 73 | for idx := range words { 74 | if idx > 1 { 75 | if words[idx-2] == "clang" && words[idx-1] == "version" { 76 | subword := strings.Split(words[idx], "-") 77 | semverVal := fmt.Sprintf("v%s", subword[0]) 78 | if !semver.IsValid(semverVal) { 79 | return "", fmt.Errorf("version unresolved") 80 | } 81 | return semverVal, nil 82 | } 83 | } 84 | } 85 | } 86 | } 87 | return "", fmt.Errorf("version unresolved") 88 | } 89 | 90 | func GetIproute2Version() (string, string, error) { 91 | iproute2Path := which.Which("ip") 92 | if iproute2Path == "" { 93 | return "", "", nil 94 | } 95 | 96 | out, err := LocalExecutef("%s -V", iproute2Path) 97 | if err != nil { 98 | return "", "", err 99 | } 100 | 101 | // [EXAMPLE:centos 7] 102 | // $ ip -V 103 | // ip utility, iproute2-ss170501 104 | // 105 | // [EXAMPLE:ubuntu 20.04] 106 | // $ ip -V 107 | // ip utility, iproute2-5.18.0, libbpf 0.8.0 108 | // 109 | // [EXAMPLE: fedora 37] 110 | // $ ip -V 111 | // ip utility, iproute2-6.0.0, libbpf 0.8.0 112 | 113 | binVersion := "v0.0.0" 114 | libVersion := "v0.0.0" 115 | 116 | words := strings.Fields(out) 117 | for idx := range words { 118 | word := strings.Replace(words[idx], ",", "", -1) 119 | if strings.Contains(word, "iproute2-") { 120 | subwords := strings.Split(word, "-") 121 | if len(subwords) < 2 { 122 | return "", "", fmt.Errorf("invalid formant (%s)", word) 123 | } 124 | binVersionTmp := fmt.Sprintf("v%s", subwords[1]) 125 | if semver.IsValid(binVersionTmp) { 126 | binVersion = binVersionTmp 127 | } 128 | } 129 | if idx > 1 && strings.Replace(words[idx-1], ",", "", -1) == "libbpf" { 130 | libVersion = fmt.Sprintf("v%s", word) 131 | } 132 | } 133 | 134 | return binVersion, libVersion, nil 135 | } 136 | 137 | func GetKernelVersion() (string, error) { 138 | var si sysinfo.SysInfo 139 | si.GetSysInfo() 140 | semverVal := fmt.Sprintf("v%s", strings.Split(si.Kernel.Release, "-")[0]) 141 | if !semver.IsValid(semverVal) { 142 | return "", fmt.Errorf("invalid format (%s)", semverVal) 143 | } 144 | return semverVal, nil 145 | } 146 | -------------------------------------------------------------------------------- /pkg/util/tablewriter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Hiroki Shirokura. 3 | Copyright 2022 Keio University. 4 | Copyright 2022 Wide Project. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "io" 23 | 24 | "github.com/olekukonko/tablewriter" 25 | ) 26 | 27 | func NewTableWriter(writer io.Writer) *tablewriter.Table { 28 | table := tablewriter.NewWriter(writer) 29 | table.SetAutoWrapText(false) 30 | table.SetAutoFormatHeaders(true) 31 | table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) 32 | table.SetAlignment(tablewriter.ALIGN_LEFT) 33 | table.SetCenterSeparator("") 34 | table.SetColumnSeparator("") 35 | table.SetRowSeparator("") 36 | table.SetHeaderLine(false) 37 | table.SetBorder(false) 38 | table.SetTablePadding(" ") 39 | table.SetNoWhiteSpace(true) 40 | return table 41 | } 42 | -------------------------------------------------------------------------------- /pkg/util/time.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "syscall" 5 | "time" 6 | ) 7 | 8 | func KtimeToReal(ktime uint64) (uint64, error) { 9 | sysinfo := &syscall.Sysinfo_t{} 10 | if err := syscall.Sysinfo(sysinfo); err != nil { 11 | return 0, err 12 | } 13 | 14 | dt := time.Now() 15 | dt = dt.Add(-1 * (time.Second * time.Duration(uint64(sysinfo.Uptime)))) 16 | return uint64(dt.UnixNano()) + ktime, nil 17 | } 18 | 19 | func TimeNow() uint64 { 20 | return uint64(time.Now().Unix()) 21 | } 22 | 23 | func TimeNowNano() uint64 { 24 | return uint64(time.Now().UnixNano()) 25 | } 26 | 27 | func KtimeToRealNano(ktime uint64) (uint64, error) { 28 | sysinfo := &syscall.Sysinfo_t{} 29 | if err := syscall.Sysinfo(sysinfo); err != nil { 30 | return 0, err 31 | } 32 | 33 | dt := time.Now() 34 | dt = dt.Add(-1 * (time.Second * time.Duration(uint64(sysinfo.Uptime)))) 35 | return uint64(dt.UnixNano()) + ktime, nil 36 | } 37 | 38 | func KtimeToRealMilli(ktimeMilli uint64) (uint64, error) { 39 | sysinfo := &syscall.Sysinfo_t{} 40 | if err := syscall.Sysinfo(sysinfo); err != nil { 41 | return 0, err 42 | } 43 | 44 | dt := time.Now() 45 | dt = dt.Add(-1 * (time.Second * time.Duration(uint64(sysinfo.Uptime)))) 46 | return uint64(dt.UnixMilli()) + ktimeMilli, nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/util/version.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var ( 11 | gitSHA = "unknown" 12 | gitBranch = "unknown" 13 | gitTag = "unknown" 14 | buildDate = "unknown" 15 | ) 16 | 17 | func NewCommandVersion() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: "version", 20 | RunE: func(cmd *cobra.Command, args []string) error { 21 | fmt.Printf("Build Date: %s\n", buildDate) 22 | fmt.Printf("Git SHA/Branch/Tag: %s/%s/%s\n", gitSHA, gitBranch, gitTag) 23 | fmt.Printf("Go Version/OS/Arch: %s/%s/%s\n", 24 | runtime.Version(), runtime.GOOS, runtime.GOARCH) 25 | return nil 26 | }, 27 | } 28 | return cmd 29 | } 30 | 31 | // VersionGitSHA returns Git hash string 32 | func VersionGitSHA() string { 33 | return gitSHA 34 | } 35 | 36 | // VersionGitTag returns Git tag string 37 | func VersionGitTag() string { 38 | return gitTag 39 | } 40 | 41 | // VersionGitBranch returns current Git branch name 42 | func VersionGitBranch() string { 43 | return gitBranch 44 | } 45 | 46 | // VersionBuildDate returns date when your binary is built 47 | func VersionBuildDate() string { 48 | return buildDate 49 | } 50 | --------------------------------------------------------------------------------