mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55e670033b | ||
|
|
589fe49232 | ||
|
|
511c9a5ded | ||
|
|
5ad73ea8eb | ||
|
|
0e917594d9 | ||
|
|
432e75c76a | ||
|
|
f7d957fc35 | ||
|
|
bd274abccc | ||
|
|
0603885bb6 | ||
|
|
eed5f5ff0e | ||
|
|
ec099786cb | ||
|
|
86f2e5b066 | ||
|
|
386bd58956 | ||
|
|
4789212001 | ||
|
|
997b217d4b | ||
|
|
00ce2f86af | ||
|
|
968e76e5ae | ||
|
|
a3cc866b28 | ||
|
|
b4684d7d90 | ||
|
|
baca7d7e6c | ||
|
|
208deccd09 | ||
|
|
2e5215a35d | ||
|
|
b9cb84efdf | ||
|
|
f7a642525c | ||
|
|
51e6fed5fc | ||
|
|
c5232c2953 | ||
|
|
0a9243f27c | ||
|
|
b7167b5859 | ||
|
|
b63eeb6301 | ||
|
|
5b5c094e4b | ||
|
|
c4bd77cf28 | ||
|
|
94c9cacf4f | ||
|
|
9a5b31b470 | ||
|
|
07ad03fecf | ||
|
|
2225d258cb | ||
|
|
e9316f7be4 | ||
|
|
9ac04cd24d | ||
|
|
76c4d231f2 | ||
|
|
160b68cbc5 | ||
|
|
212504f6c2 | ||
|
|
114cd97025 | ||
|
|
b2be91cdb5 | ||
|
|
7cb5adfc7a | ||
|
|
335b2d35bb | ||
|
|
e3c5c133a6 | ||
|
|
d367ce9e52 | ||
|
|
a1fa76173a | ||
|
|
912a97993e | ||
|
|
706274829f | ||
|
|
4358f5600f | ||
|
|
7cf99098cf | ||
|
|
720050cc39 | ||
|
|
e99088b8d2 | ||
|
|
4b574ecbc1 | ||
|
|
8115fe5db9 | ||
|
|
0dabb34467 | ||
|
|
c290f0b008 | ||
|
|
01e7b9f4c2 | ||
|
|
0da917322c | ||
|
|
23cd29a88e | ||
|
|
3c54e12586 | ||
|
|
c71120e7d4 | ||
|
|
ee29dc4fb1 | ||
|
|
77dc451e83 | ||
|
|
8c2d39753e | ||
|
|
3790c74ca8 | ||
|
|
b07931c582 | ||
|
|
d374f93447 | ||
|
|
c8fa2a287f | ||
|
|
eefaf5afac | ||
|
|
3b5b4ac729 | ||
|
|
b983528f33 | ||
|
|
0f4131b7ae | ||
|
|
0c315300a3 | ||
|
|
7490778bbb | ||
|
|
fc57c98098 | ||
|
|
34bc48fd64 | ||
|
|
56b45e84f7 | ||
|
|
98f93118cf | ||
|
|
dca5b2da39 | ||
|
|
5bd9b7e92b | ||
|
|
f3cccbb51c | ||
|
|
4f88883837 | ||
|
|
8a03eb1568 | ||
|
|
9e0d6f8ce8 | ||
|
|
de73ed90a5 | ||
|
|
90ea4baa06 | ||
|
|
c8c084a087 | ||
|
|
5ad2b958a6 | ||
|
|
361ba7600d | ||
|
|
61e830c4e8 | ||
|
|
216c7aaf6d | ||
|
|
876e17ab18 | ||
|
|
166dbca282 | ||
|
|
24ff7caecb | ||
|
|
99abd12302 | ||
|
|
4a49552392 | ||
|
|
556de3055b | ||
|
|
9d84f40720 | ||
|
|
f70791fc61 | ||
|
|
8f2fcb7026 | ||
|
|
2eec01d9c0 | ||
|
|
047895f088 | ||
|
|
b93460fb27 | ||
|
|
8ab062621e | ||
|
|
c380abfc75 | ||
|
|
029af29881 | ||
|
|
b2720342c4 | ||
|
|
b51ef20d30 | ||
|
|
a95a000cad | ||
|
|
c251f2c3b4 | ||
|
|
ffb7f8aeaf | ||
|
|
265dd7eb1d | ||
|
|
c21ba84b05 | ||
|
|
7d06c5f2d7 | ||
|
|
7e648ff260 | ||
|
|
00db8d26ec | ||
|
|
f45e756fca | ||
|
|
3530c325ba | ||
|
|
6d6114bf26 | ||
|
|
cda9de7d8d | ||
|
|
9004bf3170 | ||
|
|
903cb9f9f1 | ||
|
|
6c46285010 | ||
|
|
1b72bf39d8 | ||
|
|
112870d870 | ||
|
|
a859f419f6 | ||
|
|
fa728236d4 | ||
|
|
ade7ee06a0 | ||
|
|
30cca4d7db | ||
|
|
0f18b17a8a | ||
|
|
1b756aeb3d | ||
|
|
a69b0a90ed | ||
|
|
8778495915 | ||
|
|
13deb22077 | ||
|
|
1635649aa5 | ||
|
|
3425d6288b | ||
|
|
fe3227db22 | ||
|
|
2684e47946 | ||
|
|
fcb6a5e141 | ||
|
|
ff9a67e931 | ||
|
|
f288fe9ea6 | ||
|
|
7a42a51643 | ||
|
|
bd4051bd02 | ||
|
|
bab23e05b0 | ||
|
|
4200cc7ef6 | ||
|
|
d9df85de52 | ||
|
|
379ce89334 | ||
|
|
34aaf35425 | ||
|
|
375d7bff86 | ||
|
|
4e9d289d7a | ||
|
|
b5f98ed351 | ||
|
|
4cc3e083d8 | ||
|
|
adae541dd9 | ||
|
|
0734e23cf3 | ||
|
|
6a8b0f70e7 | ||
|
|
f8454de6b6 | ||
|
|
e97e49498e | ||
|
|
9f2ff06ec8 | ||
|
|
6715764c4f | ||
|
|
62f2ffe2d8 | ||
|
|
90b9f85617 | ||
|
|
e619848985 | ||
|
|
7f93fb5858 | ||
|
|
7b8a4646fc | ||
|
|
b5487cd0ce | ||
|
|
acfe430810 | ||
|
|
9285b162b7 | ||
|
|
8d13f9be89 | ||
|
|
4ba9028985 | ||
|
|
b4473a5413 | ||
|
|
3b01e69ba2 | ||
|
|
76b658917a | ||
|
|
b260423213 |
+16
-14
@@ -1,27 +1,29 @@
|
||||
问问题前先搜索ISSUE,并搞清楚下面的问题:
|
||||
|
||||
1. 检查 ```-key xxx``` 至少三遍, ***保证***两边一致。
|
||||
2. 保证```-nocomp, -datashard, -parityshard, -key, -crypt```两边一致。
|
||||
2. 保证```-nocomp, -datashard, -parityshard, -key, -crypt, -smuxver```两边一致。
|
||||
3. 是否在服务器端,正确设定了转发的目标服务器地址 ***--target***。
|
||||
4. 如果第3条不确定,尝试在服务器上telnet target port试试。
|
||||
5. 防火墙是否关闭了UDP通信。
|
||||
6. 两端的版本是否一致?
|
||||
7. 是不是最新版本?
|
||||
8. 两端分别是什么操作系统?
|
||||
9. 两端的输出日志是什么?
|
||||
4. 是否在客户端,正确的连接到了 client的监听端口。
|
||||
5. 如果第3条不确定,尝试在服务器上telnet target port试试。
|
||||
6. 防火墙是否关闭了UDP通信,或者设置了UDP的最大发包速率?
|
||||
7. 两端的版本是否一致?
|
||||
8. 是不是最新版本?
|
||||
9. 两端分别是什么操作系统?
|
||||
10. 两端的输出日志是什么?
|
||||
|
||||
Before firing issue, make sure you figured out the following common questions.
|
||||
|
||||
PLEASE DO SEARCH FIRST.
|
||||
|
||||
1. Check your ```-key xxx``` for at least 3 times, ***MAKE SURE*** both sides share the same secret.
|
||||
2. ```-nocomp, -datashard, -parityshard, -key, -crypt``` ***must be the same*** on both side.
|
||||
2. ```-nocomp, -datashard, -parityshard, -key, -crypt, -smuxver``` ***must be the same*** on both side.
|
||||
3. Did you correctly set the ***-target*** on the server side?
|
||||
4. ***MAKE SURE*** ```telnet target port``` on your server successful(don't ask me why couldn't).
|
||||
5. Does your ***firewall allows UDP*** communications? (including your ISP Cable-Modem)
|
||||
6. Are you using the **same version** for both client & server
|
||||
7. Are you using the **latest release**?
|
||||
8. Which **OS** do you use?
|
||||
9. Which end for this issue related to, **client or server**?
|
||||
4. Did you correctly connected to the listening port on client side?
|
||||
5. ***MAKE SURE*** ```telnet target port``` on your server successful(don't ask me why couldn't).
|
||||
6. Does your ***firewall allows UDP*** communications? (including your ISP Cable-Modem)
|
||||
7. Are you using the **same version** for both client & server
|
||||
8. Are you using the **latest release**?
|
||||
9. Which **OS** do you use?
|
||||
10. Which end for this issue related to, **client or server**?
|
||||
|
||||
|
||||
|
||||
+5
-7
@@ -1,18 +1,16 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
install:
|
||||
- go get github.com/xtaci/kcptun/client
|
||||
- go get github.com/xtaci/kcptun/server
|
||||
- env GO111MODULE=on go get github.com/xtaci/kcptun/client
|
||||
- env GO111MODULE=on go get github.com/xtaci/kcptun/server
|
||||
before_script:
|
||||
script:
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/client
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- env GO111MODULE=on $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/server
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- env GO111MODULE=on $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- exit 0
|
||||
|
||||
+11
-7
@@ -1,11 +1,15 @@
|
||||
FROM golang:alpine as builder
|
||||
FROM golang:1.21.0-alpine3.18 as builder
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add git gcc libc-dev linux-headers
|
||||
RUN go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/client && go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/server
|
||||
ENV GO111MODULE=on
|
||||
RUN apk add git
|
||||
RUN git clone https://github.com/xtaci/kcptun.git
|
||||
RUN cd kcptun && \
|
||||
go build -mod=vendor -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" -o /client github.com/xtaci/kcptun/client && \
|
||||
go build -mod=vendor -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" -o /server github.com/xtaci/kcptun/server
|
||||
|
||||
FROM alpine:3.9
|
||||
COPY --from=builder /go/bin /bin
|
||||
FROM alpine:3.18
|
||||
RUN apk add --no-cache iptables
|
||||
COPY --from=builder /client /bin
|
||||
COPY --from=builder /server /bin
|
||||
EXPOSE 29900/udp
|
||||
EXPOSE 12948
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
# <img src="logo.png" alt="kcptun" height="54px" />
|
||||
|
||||
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
|
||||
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
|
||||
|
||||
[1]: https://images.microbadger.com/badges/image/xtaci/kcptun.svg
|
||||
[2]: https://microbadger.com/images/xtaci/kcptun
|
||||
[1]: https://img.shields.io/docker/pulls/xtaci/kcptun
|
||||
[2]: https://hub.docker.com/r/xtaci/kcptun
|
||||
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
|
||||
[4]: https://travis-ci.org/xtaci/kcptun
|
||||
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
|
||||
[6]: https://goreportcard.com/report/github.com/xtaci/kcptun
|
||||
[7]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[8]: https://raw.githubusercontent.com/xtaci/kcptun/master/LICENSE.md
|
||||
[11]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[11]: https://img.shields.io/github/license/xtaci/kcptun
|
||||
[12]: LICENSE.md
|
||||
[13]: https://img.shields.io/github/release/xtaci/kcptun.svg
|
||||
[13]: https://img.shields.io/github/v/release/xtaci/kcptun?color=orange
|
||||
[14]: https://github.com/xtaci/kcptun/releases/latest
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800&color=orange
|
||||
[16]: https://github.com/xtaci/kcptun/releases
|
||||
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
|
||||
[18]: https://github.com/skywind3000/kcp
|
||||
@@ -23,6 +21,15 @@
|
||||
|
||||
> *Disclaimer: kcptun maintains a single website — [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun). Any websites other than [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun) are not endorsed by xtaci.*
|
||||
|
||||
### Requirements
|
||||
|
||||
| Target | Minimum | Recommended |
|
||||
| --- | --- | --- |
|
||||
| System | aix darwin dragonfly freebsd linux netbsd openbsd solaris windows | linux |
|
||||
| Memory | >20MB | >32MB |
|
||||
| CPU | ANY | amd64 with AES-NI & AVX2 |
|
||||
|
||||
|
||||
### QuickStart
|
||||
|
||||
Increase the number of open files on your server, as:
|
||||
@@ -59,10 +66,13 @@ which tunnels the original connection:
|
||||
|
||||
> Application -> Target Server(8388/tcp)
|
||||
|
||||
### Install from source
|
||||
### Build from source
|
||||
|
||||
```
|
||||
$go get -u github.com/xtaci/kcptun/...
|
||||
$ git clone https://github.com/xtaci/kcptun.git
|
||||
$ cd kcptun
|
||||
$ ./build-release.sh
|
||||
$ cd build
|
||||
```
|
||||
|
||||
All precompiled releases are genereated from `build-release.sh` script.
|
||||
@@ -96,7 +106,7 @@ All precompiled releases are genereated from `build-release.sh` script.
|
||||
|
||||
> eg: `-mode fast3`
|
||||
|
||||
> Aggresiveness/Responsiveness on retransmission for embeded modes are:
|
||||
> Aggresiveness/Responsiveness on retransmission for embedded modes are:
|
||||
|
||||
> *fast3 > fast2 > fast > normal > default*
|
||||
|
||||
@@ -105,6 +115,8 @@ All precompiled releases are genereated from `build-release.sh` script.
|
||||
Since streams are multiplexed into a single physical channel, head of line blocking may appear under certain circumstances, by
|
||||
increasing `-smuxbuf` to a larger value (default 4MB) may mitigate this problem, obviously this will costs more memory.
|
||||
|
||||
For versions >= v20190924, you can switch to smux version 2, smux v2 has options to limit per-stream memory usage, now set `-smuxver 2` to enable smux v2, and adjust `-streambuf` to limit per-stream memory usage, eg: `-streambuf 2097152` can limit per-stream memory usage to 2MB. By limiting stream buffer on the receiver side, a back-pressure will be conducted to the sender and limits reading, and finally prevent source from sending too much data to occupy every bits of buffer along the link. (Setting -smuxver **MUST** be **IDENTICAL** on both side, default is 1. )
|
||||
|
||||
#### Slow Devices
|
||||
|
||||
kcptun made use of **ReedSolomon-Codes** to recover lost packets, which requires massive amount of computation, a low-end ARM device cannot satisfy kcptun well. To unleash the full potential of kcptun, a multi-core x86 homeserver CPU like AMD Opteron is recommended.
|
||||
@@ -119,7 +131,7 @@ If you insist on running under some ARM routers, you'd better turn off `FEC` and
|
||||
#### Usage
|
||||
|
||||
```
|
||||
xtaci@gw:~$ ./client_linux_amd64 -h
|
||||
➜ ~ ./client_linux_amd64 -h
|
||||
NAME:
|
||||
kcptun - client(with SMUX)
|
||||
|
||||
@@ -127,20 +139,20 @@ USAGE:
|
||||
client_linux_amd64 [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
20190409
|
||||
20190924
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--localaddr value, -l value local listen address (default: ":12948")
|
||||
--remoteaddr value, -r value kcp server address (default: "vps:29900")
|
||||
--remoteaddr value, -r value kcp server address, eg: "IP:29900" a for single port, "IP:minport-maxport" for port range (default: "vps:29900")
|
||||
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
|
||||
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal, manual (default: "fast")
|
||||
--conn value set num of UDP connections to server (default: 1)
|
||||
--autoexpire value set auto expiration time(in seconds) for a single UDP connection, 0 to disable (default: 0)
|
||||
--scavengettl value set how long an expired connection can live(in sec), -1 to disable (default: 600)
|
||||
--scavengettl value set how long an expired connection can live (in seconds) (default: 600)
|
||||
--mtu value set maximum transmission unit for UDP packets (default: 1350)
|
||||
--sndwnd value set send window size(num of packets) (default: 128)
|
||||
--rcvwnd value set receive window size(num of packets) (default: 512)
|
||||
@@ -149,17 +161,20 @@ GLOBAL OPTIONS:
|
||||
--dscp value set DSCP(6bit) (default: 0)
|
||||
--nocomp disable compression
|
||||
--sockbuf value per-socket buffer in bytes (default: 4194304)
|
||||
--smuxver value specify smux version, available 1,2 (default: 1)
|
||||
--smuxbuf value the overall de-mux buffer in bytes (default: 4194304)
|
||||
--streambuf value per stream receive buffer in bytes, smux v2+ (default: 2097152)
|
||||
--keepalive value seconds between heartbeats (default: 10)
|
||||
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
|
||||
--snmpperiod value snmp collect period, in seconds (default: 60)
|
||||
--log value specify a log file to output, default goes to stderr
|
||||
--quiet to suppress the 'stream open/close' messages
|
||||
--tcp to emulate a TCP connection(linux)
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
|
||||
xtaci@gw:~$ ./server_linux_amd64 -h
|
||||
➜ ~ ./server_linux_amd64 -h
|
||||
NAME:
|
||||
kcptun - server(with SMUX)
|
||||
|
||||
@@ -167,14 +182,14 @@ USAGE:
|
||||
server_linux_amd64 [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
20190409
|
||||
20190924
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--listen value, -l value kcp server listen address (default: ":29900")
|
||||
--target value, -t value target server address (default: "127.0.0.1:12948")
|
||||
--listen value, -l value kcp server listen address, eg: "IP:29900" for a single port, "IP:minport-maxport" for port range (default: ":29900")
|
||||
--target value, -t value target server address, or path/to/unix_socket (default: "127.0.0.1:12948")
|
||||
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
|
||||
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal, manual (default: "fast")
|
||||
@@ -186,18 +201,36 @@ GLOBAL OPTIONS:
|
||||
--dscp value set DSCP(6bit) (default: 0)
|
||||
--nocomp disable compression
|
||||
--sockbuf value per-socket buffer in bytes (default: 4194304)
|
||||
--smuxver value specify smux version, available 1,2 (default: 1)
|
||||
--smuxbuf value the overall de-mux buffer in bytes (default: 4194304)
|
||||
--streambuf value per stream receive buffer in bytes, smux v2+ (default: 2097152)
|
||||
--keepalive value seconds between heartbeats (default: 10)
|
||||
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
|
||||
--snmpperiod value snmp collect period, in seconds (default: 60)
|
||||
--pprof start profiling server on :6060
|
||||
--log value specify a log file to output, default goes to stderr
|
||||
--quiet to suppress the 'stream open/close' messages
|
||||
--tcp to emulate a TCP connection(linux)
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
#### Multiport Dialer
|
||||
|
||||
kcptun supports multi-port dialer like below:
|
||||
|
||||
```
|
||||
client: --remoteaddr IP:minport-maxport
|
||||
server: --listen IP:minport-maxport
|
||||
|
||||
eg:
|
||||
client: --remoteaddr IP:3000-4000
|
||||
server: --listen 0.0.0.0:3000-4000
|
||||
```
|
||||
by specifying port-range, kcptun will automatically switch to next random port within port-range when establishing each new connection.
|
||||
|
||||
|
||||
#### Forward Error Correction
|
||||
|
||||
In coding theory, the [Reed–Solomon code](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) belongs to the class of non-binary cyclic error-correcting codes. The Reed–Solomon code is based on univariate polynomials over finite fields.
|
||||
@@ -206,8 +239,6 @@ It is able to detect and correct multiple symbol errors. By adding t check symbo
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard m -parityshard n``` on **BOTH** KCP Client & KCP Server **MUST** be **IDENTICAL**.
|
||||
|
||||
#### DSCP
|
||||
|
||||
Differentiated services or DiffServ is a computer networking architecture that specifies a simple, scalable and coarse-grained mechanism for classifying and managing network traffic and providing quality of service (QoS) on modern IP networks. DiffServ can, for example, be used to provide low-latency to critical network traffic such as voice or streaming media while providing simple best-effort service to non-critical services such as web traffic or file transfers.
|
||||
@@ -275,7 +306,7 @@ Reference: https://blog.golang.org/go15gc
|
||||
Primary memory allocation are done from a global buffer pool *xmit.Buf*, in kcp-go, when we need to allocate some bytes, we can get from that pool, and a *fixed-capacity* 1500 bytes(mtuLimit) will be returned, the *rx queue*, *tx queue* and *fec queue* all receive bytes from there, and they will return the bytes to the pool after using to prevent *unnecessary zer0ing* of bytes.
|
||||
The pool mechanism maintained a *high watermark* for slice objects, these *in-flight* objects from the pool will survive from the perodical garbage collection, meanwhile the pool kept the ability to return the memory to runtime if in idle, `-sndwnd`,`-rcvwnd`,`-ds`, `-ps`, these parameters affect this *high watermark*, the larger the value, the bigger the memory consumption will be.
|
||||
|
||||
`-smuxbuf` also affects the maximum memory consumption, this parameter maintains a subtle balance between *concurrency* and *resource*, you can increase this value(default 4MB) to boost concurrency if you have many clients to serve and you get a powerful server at the same time, and also you can decrease this value to serve only 1 or 2 clients and hope this program can run under some embeded SoC system with limited memory and only you can access. (Notice that the `-smuxbuf` value is not proprotional to concurrency, you need to test.)
|
||||
`-smuxbuf` also affects the maximum memory consumption, this parameter maintains a subtle balance between *concurrency* and *resource*, you can increase this value(default 4MB) to boost concurrency if you have many clients to serve and you get a powerful server at the same time, and also you can decrease this value to serve only 1 or 2 clients and hope this program can run under some embedded SoC system with limited memory and only you can access. (Notice that the `-smuxbuf` value is not proprotional to concurrency, you need to test.)
|
||||
|
||||
|
||||
#### Compression
|
||||
@@ -339,13 +370,12 @@ Low-level KCP configuration can be altered by using manual mode like above, make
|
||||
|
||||
### Identical Parmeters
|
||||
|
||||
The parameters below **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
These parameters **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
|
||||
1. -key
|
||||
1. -crypt
|
||||
1. -nocomp
|
||||
1. -datashard
|
||||
1. -parityshard
|
||||
1. -smuxver
|
||||
|
||||
### References
|
||||
|
||||
@@ -364,15 +394,7 @@ The parameters below **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
1. http://http2.github.io/ -- What is HTTP/2?
|
||||
1. http://www.lartc.org/ -- Linux Advanced Routing & Traffic Control
|
||||
1. https://en.wikipedia.org/wiki/Noisy-channel_coding_theorem -- Noisy channel coding theorem
|
||||
1. https://zhuanlan.zhihu.com/p/53849089 -- kcptun开发小记
|
||||
|
||||
### Donate
|
||||
|
||||
via Ethereum(ETH): Address: 0x2e4b43ab3d0983da282592571eef61ae5e60f726 , Or scan here:
|
||||
|
||||
<img src="0x2e4b43ab3d0983da282592571eef61ae5e60f726.png" alt="kcptun" height="120px" />
|
||||
|
||||
via WeChat
|
||||
|
||||
<img src="wechat_donate.jpg" alt="kcptun" height="120px" />
|
||||
|
||||
(注意:我没有任何社交网站的账号,请小心骗子。)
|
||||
|
||||
+25
-20
@@ -6,11 +6,8 @@ cd $BUILD_DIR
|
||||
|
||||
sum="sha1sum"
|
||||
|
||||
if [ "$GO111MODULE" != "on" ]; then
|
||||
echo "GO111MODULE is off"
|
||||
else
|
||||
echo "GO111MODULE is on"
|
||||
fi
|
||||
export GO111MODULE=on
|
||||
echo "Setting GO111MODULE to" $GO111MODULE
|
||||
|
||||
if ! hash sha1sum 2>/dev/null; then
|
||||
if ! hash shasum 2>/dev/null; then
|
||||
@@ -38,8 +35,8 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_amd64${suffix} server_${os}_amd64${suffix};fi
|
||||
tar -zcf kcptun-${os}-amd64-$VERSION.tar.gz client_${os}_amd64${suffix} server_${os}_amd64${suffix}
|
||||
$sum kcptun-${os}-amd64-$VERSION.tar.gz
|
||||
@@ -53,8 +50,8 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_386${suffix} server_${os}_386${suffix};fi
|
||||
tar -zcf kcptun-${os}-386-$VERSION.tar.gz client_${os}_386${suffix} server_${os}_386${suffix}
|
||||
$sum kcptun-${os}-386-$VERSION.tar.gz
|
||||
@@ -63,25 +60,33 @@ done
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm$v server_linux_arm$v;fi
|
||||
tar -zcf kcptun-linux-arm$v-$VERSION.tar.gz client_linux_arm$v server_linux_arm$v
|
||||
$sum kcptun-linux-arm$v-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# ARM64
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm64 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm64 server_linux_arm64*;fi
|
||||
tar -zcf kcptun-linux-arm64-$VERSION.tar.gz client_linux_arm64 server_linux_arm64
|
||||
$sum kcptun-linux-arm64-$VERSION.tar.gz
|
||||
OSES=(linux darwin windows)
|
||||
for os in ${OSES[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_arm64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_arm64${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_arm64${suffix} server_${os}_arm64${suffix};fi
|
||||
tar -zcf kcptun-${os}-arm64-$VERSION.tar.gz client_${os}_arm64${suffix} server_${os}_arm64${suffix}
|
||||
$sum kcptun-${os}-arm64-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
#MIPS32LE
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
|
||||
|
||||
if $UPX; then upx -9 client_linux_mips* server_linux_mips*;fi
|
||||
tar -zcf kcptun-linux-mipsle-$VERSION.tar.gz client_linux_mipsle server_linux_mipsle
|
||||
|
||||
@@ -28,13 +28,16 @@ type Config struct {
|
||||
Resend int `json:"resend"`
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
SmuxVer int `json:"smuxver"`
|
||||
SmuxBuf int `json:"smuxbuf"`
|
||||
StreamBuf int `json:"streambuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
Quiet bool `json:"quiet"`
|
||||
TCP bool `json:"tcp"`
|
||||
Pprof bool `json:"pprof"`
|
||||
}
|
||||
|
||||
func parseJSONConfig(config *Config, path string) error {
|
||||
|
||||
Binary file not shown.
+23
-4
@@ -1,18 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
|
||||
mp, err := generic.ParseMultiPort(config.RemoteAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var randport uint64
|
||||
err = binary.Read(rand.Reader, binary.LittleEndian, &randport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remoteAddr := fmt.Sprintf("%v:%v", mp.Host, uint64(mp.MinPort)+randport%uint64(mp.MaxPort-mp.MinPort+1))
|
||||
|
||||
if config.TCP {
|
||||
conn, err := tcpraw.Dial("tcp", config.RemoteAddr)
|
||||
conn, err := tcpraw.Dial("tcp", remoteAddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tcpraw.Dial()")
|
||||
}
|
||||
return kcp.NewConn(config.RemoteAddr, block, config.DataShard, config.ParityShard, conn)
|
||||
return kcp.NewConn(remoteAddr, block, config.DataShard, config.ParityShard, conn)
|
||||
}
|
||||
return kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
|
||||
return kcp.DialWithOptions(remoteAddr, block, config.DataShard, config.ParityShard)
|
||||
|
||||
}
|
||||
|
||||
+147
-88
@@ -7,36 +7,41 @@ import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/smux"
|
||||
)
|
||||
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
const SALT = "kcp-go"
|
||||
const (
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
// maximum supported smux version
|
||||
maxSmuxVer = 2
|
||||
// stream copy buffer size
|
||||
bufSize = 4096
|
||||
)
|
||||
|
||||
// VERSION is injected by buildflags
|
||||
var VERSION = "SELFBUILD"
|
||||
|
||||
// A pool for stream copying
|
||||
var xmitBuf sync.Pool
|
||||
|
||||
func handleClient(sess *smux.Session, p1 net.Conn, quiet bool) {
|
||||
// handleClient aggregates connection p1 on mux with 'writeLock'
|
||||
func handleClient(session *smux.Session, p1 net.Conn, quiet bool) {
|
||||
logln := func(v ...interface{}) {
|
||||
if !quiet {
|
||||
log.Println(v...)
|
||||
}
|
||||
}
|
||||
defer p1.Close()
|
||||
p2, err := sess.OpenStream()
|
||||
p2, err := session.OpenStream()
|
||||
if err != nil {
|
||||
logln(err)
|
||||
return
|
||||
@@ -48,21 +53,19 @@ func handleClient(sess *smux.Session, p1 net.Conn, quiet bool) {
|
||||
defer logln("stream closed", "in:", p1.RemoteAddr(), "out:", fmt.Sprint(p2.RemoteAddr(), "(", p2.ID(), ")"))
|
||||
|
||||
// start tunnel & wait for tunnel termination
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) chan struct{} {
|
||||
die := make(chan struct{})
|
||||
go func() {
|
||||
buf := xmitBuf.Get().([]byte)
|
||||
generic.CopyBuffer(dst, src, buf)
|
||||
xmitBuf.Put(buf)
|
||||
close(die)
|
||||
}()
|
||||
return die
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) {
|
||||
if _, err := generic.Copy(dst, src); err != nil {
|
||||
// report protocol error
|
||||
if err == smux.ErrInvalidProtocol {
|
||||
log.Println("smux", err, "in:", p1.RemoteAddr(), "out:", fmt.Sprint(p2.RemoteAddr(), "(", p2.ID(), ")"))
|
||||
}
|
||||
}
|
||||
p1.Close()
|
||||
p2.Close()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-streamCopy(p1, p2):
|
||||
case <-streamCopy(p2, p1):
|
||||
}
|
||||
go streamCopy(p1, p2)
|
||||
streamCopy(p2, p1)
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
@@ -72,15 +75,17 @@ func checkError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
type timedSession struct {
|
||||
session *smux.Session
|
||||
expiryDate time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
if VERSION == "SELFBUILD" {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
xmitBuf.New = func() interface{} {
|
||||
return make([]byte, 32768)
|
||||
}
|
||||
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
@@ -95,7 +100,7 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "remoteaddr, r",
|
||||
Value: "vps:29900",
|
||||
Usage: "kcp server address",
|
||||
Usage: `kcp server address, eg: "IP:29900" a for single port, "IP:minport-maxport" for port range`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
@@ -106,7 +111,7 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none, null",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
@@ -126,7 +131,7 @@ func main() {
|
||||
cli.IntFlag{
|
||||
Name: "scavengettl",
|
||||
Value: 600,
|
||||
Usage: "set how long an expired connection can live(in sec), -1 to disable",
|
||||
Usage: "set how long an expired connection can live (in seconds)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
@@ -192,11 +197,21 @@ func main() {
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Usage: "per-socket buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxver",
|
||||
Value: 1,
|
||||
Usage: "specify smux version, available 1,2",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxbuf",
|
||||
Value: 4194304,
|
||||
Usage: "the overall de-mux buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "streambuf",
|
||||
Value: 2097152,
|
||||
Usage: "per stream receive buffer in bytes, smux v2+",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
@@ -230,6 +245,10 @@ func main() {
|
||||
Value: "", // when the value is not empty, the config path must exists
|
||||
Usage: "config from json file, which will override the command from shell",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "pprof",
|
||||
Usage: "start profiling server on :6060",
|
||||
},
|
||||
}
|
||||
myApp.Action = func(c *cli.Context) error {
|
||||
config := Config{}
|
||||
@@ -255,12 +274,15 @@ func main() {
|
||||
config.NoCongestion = c.Int("nc")
|
||||
config.SockBuf = c.Int("sockbuf")
|
||||
config.SmuxBuf = c.Int("smuxbuf")
|
||||
config.StreamBuf = c.Int("streambuf")
|
||||
config.SmuxVer = c.Int("smuxver")
|
||||
config.KeepAlive = c.Int("keepalive")
|
||||
config.Log = c.String("log")
|
||||
config.SnmpLog = c.String("snmplog")
|
||||
config.SnmpPeriod = c.Int("snmpperiod")
|
||||
config.Quiet = c.Bool("quiet")
|
||||
config.TCP = c.Bool("tcp")
|
||||
config.Pprof = c.Bool("pprof")
|
||||
|
||||
if c.String("c") != "" {
|
||||
err := parseJSONConfig(&config, c.String("c"))
|
||||
@@ -287,15 +309,59 @@ func main() {
|
||||
}
|
||||
|
||||
log.Println("version:", VERSION)
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
|
||||
checkError(err)
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
checkError(err)
|
||||
var listener net.Listener
|
||||
var isUnix bool
|
||||
if _, _, err := net.SplitHostPort(config.LocalAddr); err != nil {
|
||||
isUnix = true
|
||||
}
|
||||
if isUnix {
|
||||
addr, err := net.ResolveUnixAddr("unix", config.LocalAddr)
|
||||
checkError(err)
|
||||
listener, err = net.ListenUnix("unix", addr)
|
||||
checkError(err)
|
||||
} else {
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
|
||||
checkError(err)
|
||||
listener, err = net.ListenTCP("tcp", addr)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
log.Println("smux version:", config.SmuxVer)
|
||||
log.Println("listening on:", listener.Addr())
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("remote address:", config.RemoteAddr)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("streambuf:", config.StreamBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("conn:", config.Conn)
|
||||
log.Println("autoexpire:", config.AutoExpire)
|
||||
log.Println("scavengettl:", config.ScavengeTTL)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
log.Println("pprof:", config.Pprof)
|
||||
|
||||
// parameters check
|
||||
if config.SmuxVer > maxSmuxVer {
|
||||
log.Fatal("unsupported smux version:", config.SmuxVer)
|
||||
}
|
||||
|
||||
log.Println("initiating key derivation")
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
log.Println("key derivation done")
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
case "null":
|
||||
block = nil
|
||||
case "sm4":
|
||||
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
|
||||
case "tea":
|
||||
@@ -325,31 +391,6 @@ func main() {
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
|
||||
log.Println("listening on:", listener.Addr())
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("remote address:", config.RemoteAddr)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("conn:", config.Conn)
|
||||
log.Println("autoexpire:", config.AutoExpire)
|
||||
log.Println("scavengettl:", config.ScavengeTTL)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
createConn := func() (*smux.Session, error) {
|
||||
kcpconn, err := dial(&config, block)
|
||||
if err != nil {
|
||||
@@ -371,6 +412,16 @@ func main() {
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
log.Println("smux version:", config.SmuxVer, "on connection:", kcpconn.LocalAddr(), "->", kcpconn.RemoteAddr())
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.Version = config.SmuxVer
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
if err := smux.VerifyConfig(smuxConfig); err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
// stream multiplex
|
||||
var session *smux.Session
|
||||
@@ -382,7 +433,6 @@ func main() {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createConn()")
|
||||
}
|
||||
log.Println("connection:", kcpconn.LocalAddr(), "->", kcpconn.RemoteAddr())
|
||||
return session, nil
|
||||
}
|
||||
|
||||
@@ -398,33 +448,37 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]struct {
|
||||
session *smux.Session
|
||||
ttl time.Time
|
||||
}, numconn)
|
||||
// start snmp logger
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
|
||||
for k := range muxes {
|
||||
muxes[k].session = waitConn()
|
||||
muxes[k].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
// start pprof
|
||||
if config.Pprof {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
|
||||
chScavenger := make(chan *smux.Session, 128)
|
||||
go scavenger(chScavenger, config.ScavengeTTL)
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
// start scavenger
|
||||
chScavenger := make(chan timedSession, 128)
|
||||
go scavenger(chScavenger, &config)
|
||||
|
||||
// start listener
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]timedSession, numconn)
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
p1, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
idx := rr % numconn
|
||||
|
||||
// do auto expiration && reconnection
|
||||
if muxes[idx].session.IsClosed() || (config.AutoExpire > 0 && time.Now().After(muxes[idx].ttl)) {
|
||||
chScavenger <- muxes[idx].session
|
||||
if muxes[idx].session == nil || muxes[idx].session.IsClosed() ||
|
||||
(config.AutoExpire > 0 && time.Now().After(muxes[idx].expiryDate)) {
|
||||
muxes[idx].session = waitConn()
|
||||
muxes[idx].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
muxes[idx].expiryDate = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
if config.AutoExpire > 0 { // only when autoexpire set
|
||||
chScavenger <- muxes[idx]
|
||||
}
|
||||
}
|
||||
|
||||
go handleClient(muxes[idx].session, p1, config.Quiet)
|
||||
@@ -434,30 +488,35 @@ func main() {
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
type scavengeSession struct {
|
||||
session *smux.Session
|
||||
ts time.Time
|
||||
}
|
||||
func scavenger(ch chan timedSession, config *Config) {
|
||||
// When AutoExpire is set to 0 (default), sessionList will keep empty.
|
||||
// Then this routine won't need to do anything; thus just terminate it.
|
||||
if config.AutoExpire <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
func scavenger(ch chan *smux.Session, ttl int) {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
var sessionList []scavengeSession
|
||||
var sessionList []timedSession
|
||||
for {
|
||||
select {
|
||||
case sess := <-ch:
|
||||
sessionList = append(sessionList, scavengeSession{sess, time.Now()})
|
||||
log.Println("session marked as expired", sess.RemoteAddr())
|
||||
case item := <-ch:
|
||||
sessionList = append(sessionList, timedSession{
|
||||
item.session,
|
||||
item.expiryDate.Add(time.Duration(config.ScavengeTTL) * time.Second)})
|
||||
case <-ticker.C:
|
||||
var newList []scavengeSession
|
||||
if len(sessionList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var newList []timedSession
|
||||
for k := range sessionList {
|
||||
s := sessionList[k]
|
||||
if s.session.NumStreams() == 0 || s.session.IsClosed() {
|
||||
log.Println("session normally closed", s.session.RemoteAddr())
|
||||
s.session.Close()
|
||||
} else if ttl >= 0 && time.Since(s.ts) >= time.Duration(ttl)*time.Second {
|
||||
log.Println("session reached scavenge ttl", s.session.RemoteAddr())
|
||||
if s.session.IsClosed() {
|
||||
log.Println("scavenger: session normally closed:", s.session.LocalAddr())
|
||||
} else if time.Now().After(s.expiryDate) {
|
||||
s.session.Close()
|
||||
log.Println("scavenger: session closed due to ttl:", s.session.LocalAddr())
|
||||
} else {
|
||||
newList = append(newList, sessionList[k])
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[unit]
|
||||
Description=kcptun
|
||||
|
||||
Wants=network.target
|
||||
After=syslog.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=GOGC=20
|
||||
ExecStart=/home/user/client_linux_amd64 -c /home/user/local.json
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
KillMode=process
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"localaddr": ":2000",
|
||||
"remoteaddr": "11.22.33.44:2000",
|
||||
"key": "PASSWORD",
|
||||
"crypt": "aes-128",
|
||||
"mode": "fast3",
|
||||
"mtu": 1400,
|
||||
"sndwnd": 128,
|
||||
"rcvwnd": 1024,
|
||||
"datashard": 10,
|
||||
"parityshard": 3,
|
||||
"dscp": 46,
|
||||
"nocomp": true,
|
||||
"acknodelay": false,
|
||||
"nodelay": 1,
|
||||
"interval": 40,
|
||||
"resend": 2,
|
||||
"nc": 1,
|
||||
"sockbuf": 16777217,
|
||||
"smuxver": 1,
|
||||
"smuxbuf": 16777217,
|
||||
"streambuf": 2097152,
|
||||
"keepalive": 10,
|
||||
"autoexpire": 1800,
|
||||
"quiet": false,
|
||||
"tcp": false
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"listen": ":2000",
|
||||
"target": "127.0.0.1:9999",
|
||||
"key": "PASSWORD",
|
||||
"crypt": "aes-128",
|
||||
"mode": "fast3",
|
||||
"mtu": 1400,
|
||||
"sndwnd": 2048,
|
||||
"rcvwnd": 2048,
|
||||
"datashard": 10,
|
||||
"parityshard": 3,
|
||||
"dscp": 46,
|
||||
"nocomp": true,
|
||||
"acknodelay": false,
|
||||
"nodelay": 1,
|
||||
"interval": 40,
|
||||
"resend": 2,
|
||||
"nc": 1,
|
||||
"sockbuf": 16777217,
|
||||
"smuxver": 1,
|
||||
"smuxbuf": 16777217,
|
||||
"streambuf": 2097152,
|
||||
"keepalive": 10,
|
||||
"pprof":false,
|
||||
"quiet":false,
|
||||
"tcp":false
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package generic
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/pkg/errors"
|
||||
@@ -32,6 +33,26 @@ func (c *CompStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *CompStream) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *CompStream) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *CompStream) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *CompStream) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *CompStream) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func NewCompStream(conn net.Conn) *CompStream {
|
||||
c := new(CompStream)
|
||||
c.conn = conn
|
||||
|
||||
+18
-30
@@ -1,36 +1,24 @@
|
||||
package generic
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// io.CopyBuffer has extra tests for interface like io.ReaderFrom and io.WriterTo
|
||||
// which is not efficient in memory management from tests
|
||||
func CopyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
|
||||
if buf != nil && len(buf) == 0 {
|
||||
panic("empty buffer in copyBuffer")
|
||||
const bufSize = 4096
|
||||
|
||||
// Memory optimized io.Copy function specified for this library
|
||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
// If the reader has a WriteTo method, use it to do the copy.
|
||||
// Avoids an allocation and a copy.
|
||||
if wt, ok := src.(io.WriterTo); ok {
|
||||
return wt.WriteTo(dst)
|
||||
}
|
||||
// Similarly, if the writer has a ReadFrom method, use it to do the copy.
|
||||
if rt, ok := dst.(io.ReaderFrom); ok {
|
||||
return rt.ReadFrom(src)
|
||||
}
|
||||
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
// fallback to standard io.CopyBuffer
|
||||
buf := make([]byte, bufSize)
|
||||
return io.CopyBuffer(dst, src, buf)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type MultiPort struct {
|
||||
Host string
|
||||
MinPort uint64
|
||||
MaxPort uint64
|
||||
}
|
||||
|
||||
// Parse mulitport listener or dialer
|
||||
func ParseMultiPort(addr string) (*MultiPort, error) {
|
||||
remoteAddrMatcher := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
|
||||
matches := remoteAddrMatcher.FindStringSubmatch(addr)
|
||||
|
||||
if len(matches) >= 4 {
|
||||
var minPort, maxPort int
|
||||
minPort, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maxPort = minPort
|
||||
|
||||
// multiport assignment
|
||||
if matches[3] != "" {
|
||||
maxPort, err = strconv.Atoi(matches[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if (minPort > maxPort) || minPort > 65535 || maxPort > 65535 || minPort == 0 || maxPort == 0 {
|
||||
return nil, errors.Errorf("invalid port range specified: minport:%v -> maxport %v", minPort, maxPort)
|
||||
}
|
||||
|
||||
mp := new(MultiPort)
|
||||
mp.Host = matches[1]
|
||||
mp.MinPort = uint64(minPort)
|
||||
mp.MaxPort = uint64(maxPort)
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("malformed address:%v", addr)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
reg := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
|
||||
matches := reg.FindStringSubmatch("www.unknown.unknown:20000-21000")
|
||||
for i := 0; i < len(matches); i++ {
|
||||
fmt.Println(matches[i])
|
||||
}
|
||||
|
||||
minPort, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
maxPort, err := strconv.Atoi(matches[3])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log("minport:", minPort)
|
||||
t.Log("maxport:", maxPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("%v:%v", matches[1], uint64(minPort)+1000%uint64(maxPort-minPort+1))
|
||||
|
||||
t.Log("RemoteAddr:", remoteAddr)
|
||||
|
||||
testcase2 := "1.2.3.4:20000"
|
||||
matches = reg.FindStringSubmatch(testcase2)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase2, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
testcase3 := ":20000-20001"
|
||||
matches = reg.FindStringSubmatch(testcase3)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase3, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
testcase4 := ":20000"
|
||||
matches = reg.FindStringSubmatch(testcase4)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase4, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
)
|
||||
|
||||
func SnmpLogger(path string, interval int) {
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
module github.com/xtaci/kcptun
|
||||
|
||||
require (
|
||||
github.com/coreos/go-iptables v0.4.1 // indirect
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/google/gopacket v1.1.17 // indirect
|
||||
github.com/klauspost/cpuid v1.2.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.2 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
||||
github.com/tjfoc/gmsm v1.0.1 // indirect
|
||||
github.com/urfave/cli v1.20.0
|
||||
github.com/xtaci/kcp-go v5.4.2+incompatible
|
||||
github.com/xtaci/smux v1.3.4
|
||||
github.com/xtaci/tcpraw v1.2.0
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 // indirect
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
golang.org/x/tools v0.0.0-20190610231749-f8d1dee965f7 // indirect
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/urfave/cli v1.22.14
|
||||
github.com/xtaci/kcp-go/v5 v5.6.3
|
||||
github.com/xtaci/smux v1.5.24
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
golang.org/x/crypto v0.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/coreos/go-iptables v0.7.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.8 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/templexxx/cpu v0.1.0 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.2 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
)
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.0
|
||||
|
||||
@@ -1,201 +1,132 @@
|
||||
github.com/coreos/go-iptables v0.4.1 h1:TyEMaK2xD/EcB0385QcvX/OvI2XI7s4SJEI2EhZFfEU=
|
||||
github.com/coreos/go-iptables v0.4.1/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
|
||||
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8=
|
||||
github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18=
|
||||
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
||||
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
|
||||
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xtaci/kcp-go v5.0.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.1.1+incompatible h1:A6zXUGblo98vosfEdaHcy0cTBZKY2dByJxICuaV+L5g=
|
||||
github.com/xtaci/kcp-go v5.1.1+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.1.2+incompatible h1:UafCgw2Yk3QOf8MRm8jYEWJt1l4J61BgLpMsROkJQxo=
|
||||
github.com/xtaci/kcp-go v5.1.2+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.1.3+incompatible h1:s96+ulBrZlxk4DPRPgEGPBV8o55kYKKFVivDwVgZNcA=
|
||||
github.com/xtaci/kcp-go v5.1.3+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.1.4+incompatible h1:GHIUAicdHMKwTys8sx2ZnZbFaK8lvmwDniQlFa2BOo4=
|
||||
github.com/xtaci/kcp-go v5.1.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.1+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.3+incompatible h1:LD/Go8xYxtcyvOvFzeo387BV3UNPcwJvMY+8H+JDhfk=
|
||||
github.com/xtaci/kcp-go v5.2.3+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.4+incompatible h1:96bjdOFrUFfU4wYXqGwPJkEOdvnVwciGlAGOHEDpgy8=
|
||||
github.com/xtaci/kcp-go v5.2.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.5+incompatible h1:9C3xCFYCw6HUMN1/DQhLlUcbx1DmFiLbpiNA/moLYPM=
|
||||
github.com/xtaci/kcp-go v5.2.5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.6+incompatible h1:WokjD7IJJopwivFVIleBjmuHYlhGTlR/JAhCsapl1dk=
|
||||
github.com/xtaci/kcp-go v5.2.6+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.7+incompatible h1:NkJrwfMj3K6lRHfppR8H+oayahPS6GkGGF4BKiRAiFg=
|
||||
github.com/xtaci/kcp-go v5.2.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.2.8+incompatible h1:WRlKHMAWdB08NEmWW0cpdEQcJsOYRdFwP7lA62AhpWo=
|
||||
github.com/xtaci/kcp-go v5.2.8+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.2+incompatible h1:sWzdr6Wcs/ZlRY0U8PeeMgpmtimIutFNfrxgQTPoA9U=
|
||||
github.com/xtaci/kcp-go v5.3.2+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.3+incompatible h1:zlvskph4pBYpt5C8Y22GO2EL3saYpE/2WSZRUEKZXbE=
|
||||
github.com/xtaci/kcp-go v5.3.3+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.4+incompatible h1:vHlrCvUC4nDSGQQxbgzwwF35sup3mKAxr5Wj3Yzg4c4=
|
||||
github.com/xtaci/kcp-go v5.3.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.5+incompatible h1:PD0cqKLJDAMKgX2qzWfK98fbcrVPYDEdstlY0SN0q20=
|
||||
github.com/xtaci/kcp-go v5.3.5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.6+incompatible h1:g3/sc8dtbFG6td3W7BTyJNr/5Sb0zMNBFgEtqnSuxmw=
|
||||
github.com/xtaci/kcp-go v5.3.6+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.7+incompatible h1:iWiEoXj7BnFNR1XUPAQssR8DJGw9E7BE0l1rqFCAi3Y=
|
||||
github.com/xtaci/kcp-go v5.3.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.8+incompatible h1:1f9hBNbkoMfLryf0P82GzrMkSkqBLUztOmmqKqh7XxM=
|
||||
github.com/xtaci/kcp-go v5.3.8+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.3.9+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.1+incompatible h1:zRYDrtFx/DBNI2pngZNOYxEDvWyGN6zZlIB2QcXHL9M=
|
||||
github.com/xtaci/kcp-go v5.4.1+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.2+incompatible h1:srUoSnFj4dkRzo2b+yz1WENGB32vAv0iuMS8wjYmaEI=
|
||||
github.com/xtaci/kcp-go v5.4.2+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/smux v1.1.1 h1:ZyIo9XHuHkAeENzHR8yGWC+6xUSCTeP2tPTRE8mnLvc=
|
||||
github.com/xtaci/smux v1.1.1/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.1.2 h1:AeAzHKqvDeFEcicL9Q06LTjIVW2I55iooUbpDRGHth4=
|
||||
github.com/xtaci/smux v1.1.2/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.1/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.2 h1:pF/P78jvAXNG3yddhxKYTXMmNLzxXxdGKKjUBvxF8lk=
|
||||
github.com/xtaci/smux v1.2.2/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.3/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.4 h1:l+peIfF3MUC5OmA93nKlezj8Nhejjv76BvnTyQEB2RI=
|
||||
github.com/xtaci/smux v1.2.4/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.5 h1:p5K13hQEKeMo/H0XwO7jNxuT2G4/guo7Z7blfxBWBpI=
|
||||
github.com/xtaci/smux v1.2.5/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.6 h1:lcooP+CKdWf1IE0d35eJ4MuUt7+kA3hwtQN0uPdd5lo=
|
||||
github.com/xtaci/smux v1.2.6/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.7 h1:h7m7UL5zNZRaqEAtFXReqAVuStI3Al85zvJSGxqDqF0=
|
||||
github.com/xtaci/smux v1.2.7/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.8 h1:mNFGEdnACCih1aER/JF4JVJIcOl7Mkf5yrQNzdDY6jg=
|
||||
github.com/xtaci/smux v1.2.8/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.9 h1:aLBpFdORtDMzFJnrktW5hhe6UScbiS9inicP01KWR7M=
|
||||
github.com/xtaci/smux v1.2.9/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.10 h1:DQiH08XfO+33X7l5XOGV5urQHrgK8PRFWYxT/z9Hrnw=
|
||||
github.com/xtaci/smux v1.2.10/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.11 h1:QI4M2HgkkpsVU3Bfcmyx10qURBEeHfKi7xDhGEORfu0=
|
||||
github.com/xtaci/smux v1.2.11/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.2.12 h1:POGYLDuR61+XZlVzKUdI4fTLtJnyVba++XS4Q6T5e3M=
|
||||
github.com/xtaci/smux v1.2.12/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.3.1 h1:qMkK+dTu8ozfYz13UaX5q9GRIx0Qwux3FvcjtKZmDF8=
|
||||
github.com/xtaci/smux v1.3.1/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.3.2 h1:8NsGbxBW6oSaeLu5E6V8AmklZm6EhBz7BeCOU3Zdhwc=
|
||||
github.com/xtaci/smux v1.3.2/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.3.3 h1:+vnzZHTLGHrj+LzUZEkKmvu4KkG7fj4jwMPqhawvErg=
|
||||
github.com/xtaci/smux v1.3.3/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/smux v1.3.4 h1:7BJNo6A3d8CJXV1cCwwN9lZaGigNB2BbSDKIbtf7bHE=
|
||||
github.com/xtaci/smux v1.3.4/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw=
|
||||
github.com/xtaci/tcpraw v1.1.3 h1:9kYm8HlXn0G/bNH7Xkm7Ypn/w17vnl/JfDQxBWueUAY=
|
||||
github.com/xtaci/tcpraw v1.1.3/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.4 h1:AUO0vsyx0e3gbhOXC13iQ4FHjYoGwYZjf+I28HyZJ6g=
|
||||
github.com/xtaci/tcpraw v1.1.4/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.5 h1:/1AhipCVBsFF5aperX3ioqtO/UnBgv9QbO/NnApwu08=
|
||||
github.com/xtaci/tcpraw v1.1.5/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.6 h1:QlLq9NcyMn+t90URntTd/EETMxBMNwuiJ6JLek/8W7I=
|
||||
github.com/xtaci/tcpraw v1.1.6/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.7 h1:5M1VRTJTAPnLj7fNHaWLjFzRKTv+ooS1s8A1PChHhMA=
|
||||
github.com/xtaci/tcpraw v1.1.7/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.8 h1:4oh8U6muwXZrnEnMIv1f5cDCv7e3sIQnCd4k6hAIIIQ=
|
||||
github.com/xtaci/tcpraw v1.1.8/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.9 h1:8IvLbvBnqYLzXSU1DazMSGjE2GsR/MbQYubQSXO3uVw=
|
||||
github.com/xtaci/tcpraw v1.1.9/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.11 h1:yzeuBhNbvfo7U8q4LsFgeyxGyXqVUIr8kxEguSguG3U=
|
||||
github.com/xtaci/tcpraw v1.1.11/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.12 h1:IgYqbZUd9CQiOyVahwtGBC7f6WlYrR9toI+omhgAShM=
|
||||
github.com/xtaci/tcpraw v1.1.12/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.13 h1:uL09wQfiqAm2s95WhId1SBiWSU4K7S0v+X7zIQNcz38=
|
||||
github.com/xtaci/tcpraw v1.1.13/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.14 h1:aaVZkMAOzGihIZTizUzhEHczD0+V3zkSxkK1ynTJGyo=
|
||||
github.com/xtaci/tcpraw v1.1.14/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.15 h1:w3FN0U2TxXMAVPvRg0ChxQrJ6x4PdSstXAKyYnmpWC0=
|
||||
github.com/xtaci/tcpraw v1.1.15/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.16 h1:L7Yd9ai+mY3axAZY+tAW9aV3IPoTMbt9g8mLSYjsW5w=
|
||||
github.com/xtaci/tcpraw v1.1.16/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.17 h1:2bKo62lMFYAnkYx5NXDHOW1NWqsBYdO37zmJ+3lfZ5E=
|
||||
github.com/xtaci/tcpraw v1.1.17/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.1.18 h1:Jl5UpGZ6Ck0bwXSWdOZ3ExUa6DXx30mCaqvpRnhB3TY=
|
||||
github.com/xtaci/tcpraw v1.1.18/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/xtaci/tcpraw v1.2.0 h1:ktu8W19TkPj3Av2tDhnp7YGKG9tk1Jz3LI8bI5usT6Q=
|
||||
github.com/xtaci/tcpraw v1.2.0/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY=
|
||||
github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
|
||||
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
|
||||
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.3 h1:yd59SKXdJ0PBxeMBy3apalxFCEmBLGgQmL6nP46tU0g=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.3/go.mod h1:uIuw2KEg3FcmEdS4PeXHaGty9Ui7NYb1WKIrSDwpMg4=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
|
||||
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
|
||||
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a h1:Igim7XhdOpBnWPuYJ70XcNpq8q3BCACtVgNfoJxOV7g=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af h1:6qGQw30u837TXZbCmLFR9AVA+RjJU1LIbvk0oIkDZGY=
|
||||
golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd h1:sMHc2rZHuzQmrbVoSpt9HgerkXPyIeCSO6k0zUMGfFk=
|
||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 h1:rlLehGeYg6jfoyz/eDqDU1iRXLKfR42nnNh57ytKEWo=
|
||||
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190324223953-e3b2ff56ed87 h1:yh5/K199RObPR6zqVBYf+AyJuweAqx+fOe9s3cekn1Y=
|
||||
golang.org/x/net v0.0.0-20190324223953-e3b2ff56ed87/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca h1:hyA6yiAgbUwuWqtscNvWAI7U1CtlaD1KilQ6iudt1aI=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 h1:iC0Y6EDq+rhnAePxGvJs2kzUAYcwESqdcGRPzEUfzTU=
|
||||
golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 h1:HdqqaWmYAUI7/dmByKKEw+yxDksGSo+9GjkUc9Zp34E=
|
||||
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190424024845-afe8014c977f h1:uALRiwYevCJtciRa4mKKFkrs5jY4F2OTf1D2sfi1swY=
|
||||
golang.org/x/net v0.0.0-20190424024845-afe8014c977f/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 h1:FP8hkuE6yUEaJnK7O2eTuejKWwW+Rhfj80dQ2JcKxCU=
|
||||
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
|
||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190329044733-9eb1bfa1ce65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190416152802-12500544f89f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98MHunKK7OTtsoU6bYWs4=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190424031103-cb2dda6eabdf/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190428024724-550556f78a90/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190509153222-73554e0f7805/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190511041617-99f201b6807e/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190513233021-7d589f28aaf4/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190610231749-f8d1dee965f7/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
%global debug_package %{nil}
|
||||
%global _dwz_low_mem_die_limit 0
|
||||
%global __os_install_post /usr/lib/rpm/brp-compress %{nil}
|
||||
|
||||
Name: kcptun
|
||||
Version: 20210922
|
||||
Release: 1
|
||||
Summary: A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/xtaci/kcptun
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: golang
|
||||
BuildRequires: glibc-static
|
||||
|
||||
%description
|
||||
A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC
|
||||
|
||||
%package server
|
||||
Summary: kcptun-server
|
||||
Requires: systemd
|
||||
|
||||
%description server
|
||||
A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC
|
||||
|
||||
%package client
|
||||
Summary: kcptun-client
|
||||
Requires: systemd
|
||||
|
||||
%description client
|
||||
A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC
|
||||
|
||||
%prep
|
||||
%autosetup
|
||||
|
||||
%build
|
||||
LDFLAGS='-s -w -linkmode=external -extldflags -static -X main.VERSION=%{version}'
|
||||
go build -ldflags "$LDFLAGS" -o %{name}-server github.com/xtaci/kcptun/server
|
||||
go build -ldflags "$LDFLAGS" -o %{name}-client github.com/xtaci/kcptun/client
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%{__mkdir} -p $RPM_BUILD_ROOT%{_bindir}
|
||||
%{__install} -p -m 755 kcptun-server $RPM_BUILD_ROOT%{_bindir}/kcptun-server
|
||||
%{__install} -p -m 755 kcptun-client $RPM_BUILD_ROOT%{_bindir}/kcptun-client
|
||||
|
||||
%{__mkdir} -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}
|
||||
%{__install} -p -m 644 examples/server.json $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/server.json
|
||||
%{__install} -p -m 644 examples/local.json $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/client.json
|
||||
|
||||
%{__mkdir} -p $RPM_BUILD_ROOT%{_unitdir}
|
||||
cat > $RPM_BUILD_ROOT%{_unitdir}/kcptun-server.service <<EOF
|
||||
[Unit]
|
||||
Description=kcptun-server
|
||||
Wants=network.target
|
||||
After=syslog.target network-online.target
|
||||
ConditionFileIsExecutable=/usr/bin/kcptun-server
|
||||
ConditionPathExists=/etc/kcptun/server.json
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=GOGC=20
|
||||
ExecStart=/usr/bin/kcptun-server -c /etc/kcptun/server.json
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
KillMode=process
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > $RPM_BUILD_ROOT%{_unitdir}/kcptun-client.service <<EOF
|
||||
[Unit]
|
||||
Description=kcptun-client
|
||||
After=syslog.target network-online.target
|
||||
ConditionFileIsExecutable=/usr/bin/kcptun-client
|
||||
ConditionPathExists=/etc/kcptun/client.json
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=GOGC=20
|
||||
ExecStart=/usr/bin/kcptun-client -c /etc/kcptun/client.json
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
KillMode=process
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
%post server
|
||||
%systemd_post %{name}-server.service
|
||||
|
||||
%preun server
|
||||
%systemd_preun %{name}-server.service
|
||||
|
||||
%postun server
|
||||
%systemd_postun_with_restart %{name}-server.service
|
||||
|
||||
%post client
|
||||
%systemd_post %{name}-client.service
|
||||
|
||||
%preun client
|
||||
%systemd_preun %{name}-client.service
|
||||
|
||||
%postun client
|
||||
%systemd_postun_with_restart %{name}-client.service
|
||||
|
||||
%files server
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}/kcptun-server
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/server.json
|
||||
%{_unitdir}/kcptun-server.service
|
||||
%license LICENSE.md
|
||||
%doc README.md Dockerfile
|
||||
|
||||
%files client
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}/kcptun-client
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/client.json
|
||||
%{_unitdir}/kcptun-client.service
|
||||
%license LICENSE.md
|
||||
%doc README.md Dockerfile
|
||||
|
||||
%changelog
|
||||
* Thu Dec 30 2021 Purple Grape <purplegrape4@gmail.com>
|
||||
- First package for kcptun
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 55 KiB |
@@ -26,6 +26,8 @@ type Config struct {
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
SmuxBuf int `json:"smuxbuf"`
|
||||
StreamBuf int `json:"streambuf"`
|
||||
SmuxVer int `json:"smuxver"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
func listen(config *Config, block kcp.BlockCrypt) (*kcp.Listener, error) {
|
||||
if config.TCP {
|
||||
conn, err := tcpraw.Listen("tcp", config.Listen)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tcpraw.Listen()")
|
||||
}
|
||||
return kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
}
|
||||
return kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
|
||||
}
|
||||
+144
-72
@@ -16,25 +16,38 @@ import (
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/smux"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
const SALT = "kcp-go"
|
||||
const (
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
// maximum supported smux version
|
||||
maxSmuxVer = 2
|
||||
// stream copy buffer size
|
||||
bufSize = 4096
|
||||
)
|
||||
|
||||
// VERSION is injected by buildflags
|
||||
var VERSION = "SELFBUILD"
|
||||
|
||||
// A pool for stream copying
|
||||
var xmitBuf sync.Pool
|
||||
|
||||
// handle multiplex-ed connection
|
||||
func handleMux(conn io.ReadWriteCloser, config *Config) {
|
||||
func handleMux(conn net.Conn, config *Config) {
|
||||
// check if target is unix domain socket
|
||||
var isUnix bool
|
||||
if _, _, err := net.SplitHostPort(config.Target); err != nil {
|
||||
isUnix = true
|
||||
}
|
||||
log.Println("smux version:", config.SmuxVer, "on connection:", conn.LocalAddr(), "->", conn.RemoteAddr())
|
||||
|
||||
// stream multiplex
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.Version = config.SmuxVer
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
mux, err := smux.Server(conn, smuxConfig)
|
||||
@@ -43,6 +56,7 @@ func handleMux(conn io.ReadWriteCloser, config *Config) {
|
||||
return
|
||||
}
|
||||
defer mux.Close()
|
||||
|
||||
for {
|
||||
stream, err := mux.AcceptStream()
|
||||
if err != nil {
|
||||
@@ -51,7 +65,14 @@ func handleMux(conn io.ReadWriteCloser, config *Config) {
|
||||
}
|
||||
|
||||
go func(p1 *smux.Stream) {
|
||||
p2, err := net.Dial("tcp", config.Target)
|
||||
var p2 net.Conn
|
||||
var err error
|
||||
if !isUnix {
|
||||
p2, err = net.Dial("tcp", config.Target)
|
||||
} else {
|
||||
p2, err = net.Dial("unix", config.Target)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
p1.Close()
|
||||
@@ -76,21 +97,18 @@ func handleClient(p1 *smux.Stream, p2 net.Conn, quiet bool) {
|
||||
defer logln("stream closed", "in:", fmt.Sprint(p1.RemoteAddr(), "(", p1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
|
||||
// start tunnel & wait for tunnel termination
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) chan struct{} {
|
||||
die := make(chan struct{})
|
||||
go func() {
|
||||
buf := xmitBuf.Get().([]byte)
|
||||
generic.CopyBuffer(dst, src, buf)
|
||||
xmitBuf.Put(buf)
|
||||
close(die)
|
||||
}()
|
||||
return die
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) {
|
||||
if _, err := generic.Copy(dst, src); err != nil {
|
||||
if err == smux.ErrInvalidProtocol {
|
||||
log.Println("smux", err, "in:", fmt.Sprint(p1.RemoteAddr(), "(", p1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
}
|
||||
}
|
||||
p1.Close()
|
||||
p2.Close()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-streamCopy(p1, p2):
|
||||
case <-streamCopy(p2, p1):
|
||||
}
|
||||
go streamCopy(p2, p1)
|
||||
streamCopy(p1, p2)
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
@@ -106,9 +124,6 @@ func main() {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
xmitBuf.New = func() interface{} {
|
||||
return make([]byte, 32768)
|
||||
}
|
||||
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
@@ -118,12 +133,12 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "listen,l",
|
||||
Value: ":29900",
|
||||
Usage: "kcp server listen address",
|
||||
Usage: `kcp server listen address, eg: "IP:29900" for a single port, "IP:minport-maxport" for port range`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "target, t",
|
||||
Value: "127.0.0.1:12948",
|
||||
Usage: "target server address",
|
||||
Usage: "target server address, or path/to/unix_socket",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
@@ -134,7 +149,7 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none, null",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
@@ -205,11 +220,21 @@ func main() {
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Usage: "per-socket buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxver",
|
||||
Value: 1,
|
||||
Usage: "specify smux version, available 1,2",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxbuf",
|
||||
Value: 4194304,
|
||||
Usage: "the overall de-mux buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "streambuf",
|
||||
Value: 2097152,
|
||||
Usage: "per stream receive buffer in bytes, smux v2+",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
@@ -269,6 +294,8 @@ func main() {
|
||||
config.NoCongestion = c.Int("nc")
|
||||
config.SockBuf = c.Int("sockbuf")
|
||||
config.SmuxBuf = c.Int("smuxbuf")
|
||||
config.StreamBuf = c.Int("streambuf")
|
||||
config.SmuxVer = c.Int("smuxver")
|
||||
config.KeepAlive = c.Int("keepalive")
|
||||
config.Log = c.String("log")
|
||||
config.SnmpLog = c.String("snmplog")
|
||||
@@ -303,10 +330,39 @@ func main() {
|
||||
}
|
||||
|
||||
log.Println("version:", VERSION)
|
||||
log.Println("smux version:", config.SmuxVer)
|
||||
log.Println("listening on:", config.Listen)
|
||||
log.Println("target:", config.Target)
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("streambuf:", config.StreamBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("pprof:", config.Pprof)
|
||||
log.Println("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
|
||||
// parameters check
|
||||
if config.SmuxVer > maxSmuxVer {
|
||||
log.Fatal("unsupported smux version:", config.SmuxVer)
|
||||
}
|
||||
|
||||
log.Println("initiating key derivation")
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
log.Println("key derivation done")
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
case "null":
|
||||
block = nil
|
||||
case "sm4":
|
||||
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
|
||||
case "tea":
|
||||
@@ -336,61 +392,77 @@ func main() {
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
|
||||
lis, err := listen(&config, block)
|
||||
checkError(err)
|
||||
log.Println("listening on:", lis.Addr())
|
||||
log.Println("target:", config.Target)
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("pprof:", config.Pprof)
|
||||
log.Println("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
|
||||
if err := lis.SetDSCP(config.DSCP); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
}
|
||||
if err := lis.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := lis.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
if config.Pprof {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
|
||||
for {
|
||||
if conn, err := lis.AcceptKCP(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetWriteDelay(false)
|
||||
conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
conn.SetMtu(config.MTU)
|
||||
conn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
conn.SetACKNoDelay(config.AckNodelay)
|
||||
// main loop
|
||||
var wg sync.WaitGroup
|
||||
loop := func(lis *kcp.Listener) {
|
||||
defer wg.Done()
|
||||
if err := lis.SetDSCP(config.DSCP); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
}
|
||||
if err := lis.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := lis.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
|
||||
if config.NoComp {
|
||||
go handleMux(conn, &config)
|
||||
for {
|
||||
if conn, err := lis.AcceptKCP(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetWriteDelay(false)
|
||||
conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
conn.SetMtu(config.MTU)
|
||||
conn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
conn.SetACKNoDelay(config.AckNodelay)
|
||||
|
||||
if config.NoComp {
|
||||
go handleMux(conn, &config)
|
||||
} else {
|
||||
go handleMux(generic.NewCompStream(conn), &config)
|
||||
}
|
||||
} else {
|
||||
go handleMux(generic.NewCompStream(conn), &config)
|
||||
log.Printf("%+v", err)
|
||||
}
|
||||
} else {
|
||||
log.Printf("%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
mp, err := generic.ParseMultiPort(config.Listen)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// create multiple listener
|
||||
for port := mp.MinPort; port <= mp.MaxPort; port++ {
|
||||
listenAddr := fmt.Sprintf("%v:%v", mp.Host, port)
|
||||
if config.TCP { // tcp dual stack
|
||||
if conn, err := tcpraw.Listen("tcp", listenAddr); err == nil {
|
||||
log.Printf("Listening on: %v/tcp", listenAddr)
|
||||
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// udp stack
|
||||
log.Printf("Listening on: %v/udp", listenAddr)
|
||||
lis, err := kcp.ListenWithOptions(listenAddr, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
CoreOS Project
|
||||
Copyright 2018 CoreOS, Inc
|
||||
|
||||
This product includes software developed at CoreOS, Inc.
|
||||
(http://www.coreos.com/).
|
||||
+719
@@ -0,0 +1,719 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Adds the output of stderr to exec.ExitError
|
||||
type Error struct {
|
||||
exec.ExitError
|
||||
cmd exec.Cmd
|
||||
msg string
|
||||
exitStatus *int //for overriding
|
||||
}
|
||||
|
||||
func (e *Error) ExitStatus() int {
|
||||
if e.exitStatus != nil {
|
||||
return *e.exitStatus
|
||||
}
|
||||
return e.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg)
|
||||
}
|
||||
|
||||
// IsNotExist returns true if the error is due to the chain or rule not existing
|
||||
func (e *Error) IsNotExist() bool {
|
||||
if e.ExitStatus() != 1 {
|
||||
return false
|
||||
}
|
||||
msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n"
|
||||
msgNoChainExist := "No chain/target/match by that name.\n"
|
||||
msgENOENT := "No such file or directory"
|
||||
return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) || strings.Contains(e.msg, msgENOENT)
|
||||
}
|
||||
|
||||
// Protocol to differentiate between IPv4 and IPv6
|
||||
type Protocol byte
|
||||
|
||||
const (
|
||||
ProtocolIPv4 Protocol = iota
|
||||
ProtocolIPv6
|
||||
)
|
||||
|
||||
type IPTables struct {
|
||||
path string
|
||||
proto Protocol
|
||||
hasCheck bool
|
||||
hasWait bool
|
||||
waitSupportSecond bool
|
||||
hasRandomFully bool
|
||||
v1 int
|
||||
v2 int
|
||||
v3 int
|
||||
mode string // the underlying iptables operating mode, e.g. nf_tables
|
||||
timeout int // time to wait for the iptables lock, default waits forever
|
||||
}
|
||||
|
||||
// Stat represents a structured statistic entry.
|
||||
type Stat struct {
|
||||
Packets uint64 `json:"pkts"`
|
||||
Bytes uint64 `json:"bytes"`
|
||||
Target string `json:"target"`
|
||||
Protocol string `json:"prot"`
|
||||
Opt string `json:"opt"`
|
||||
Input string `json:"in"`
|
||||
Output string `json:"out"`
|
||||
Source *net.IPNet `json:"source"`
|
||||
Destination *net.IPNet `json:"destination"`
|
||||
Options string `json:"options"`
|
||||
}
|
||||
|
||||
type option func(*IPTables)
|
||||
|
||||
func IPFamily(proto Protocol) option {
|
||||
return func(ipt *IPTables) {
|
||||
ipt.proto = proto
|
||||
}
|
||||
}
|
||||
|
||||
func Timeout(timeout int) option {
|
||||
return func(ipt *IPTables) {
|
||||
ipt.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new IPTables configured with the options passed as parameter.
|
||||
// For backwards compatibility, by default always uses IPv4 and timeout 0.
|
||||
// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing
|
||||
// the IPFamily and Timeout options as follow:
|
||||
//
|
||||
// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5))
|
||||
func New(opts ...option) (*IPTables, error) {
|
||||
|
||||
ipt := &IPTables{
|
||||
proto: ProtocolIPv4,
|
||||
timeout: 0,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(ipt)
|
||||
}
|
||||
|
||||
path, err := exec.LookPath(getIptablesCommand(ipt.proto))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipt.path = path
|
||||
|
||||
vstring, err := getIptablesVersionString(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get iptables version: %v", err)
|
||||
}
|
||||
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err)
|
||||
}
|
||||
ipt.v1 = v1
|
||||
ipt.v2 = v2
|
||||
ipt.v3 = v3
|
||||
ipt.mode = mode
|
||||
|
||||
checkPresent, waitPresent, waitSupportSecond, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
|
||||
ipt.hasCheck = checkPresent
|
||||
ipt.hasWait = waitPresent
|
||||
ipt.waitSupportSecond = waitSupportSecond
|
||||
ipt.hasRandomFully = randomFullyPresent
|
||||
|
||||
return ipt, nil
|
||||
}
|
||||
|
||||
// New creates a new IPTables for the given proto.
|
||||
// The proto will determine which command is used, either "iptables" or "ip6tables".
|
||||
func NewWithProtocol(proto Protocol) (*IPTables, error) {
|
||||
return New(IPFamily(proto), Timeout(0))
|
||||
}
|
||||
|
||||
// Proto returns the protocol used by this IPTables.
|
||||
func (ipt *IPTables) Proto() Protocol {
|
||||
return ipt.proto
|
||||
}
|
||||
|
||||
// Exists checks if given rulespec in specified table/chain exists
|
||||
func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
|
||||
if !ipt.hasCheck {
|
||||
return ipt.existsForOldIptables(table, chain, rulespec)
|
||||
|
||||
}
|
||||
cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
|
||||
err := ipt.run(cmd...)
|
||||
eerr, eok := err.(*Error)
|
||||
switch {
|
||||
case err == nil:
|
||||
return true, nil
|
||||
case eok && eerr.ExitStatus() == 1:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts rulespec to specified table/chain (in specified pos)
|
||||
func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// Replace replaces rulespec to specified table/chain (in specified pos)
|
||||
func (ipt *IPTables) Replace(table, chain string, pos int, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-R", chain, strconv.Itoa(pos)}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// InsertUnique acts like Insert except that it won't insert a duplicate (no matter the position in the chain)
|
||||
func (ipt *IPTables) InsertUnique(table, chain string, pos int, rulespec ...string) error {
|
||||
exists, err := ipt.Exists(table, chain, rulespec...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ipt.Insert(table, chain, pos, rulespec...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append appends rulespec to specified table/chain
|
||||
func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// AppendUnique acts like Append except that it won't add a duplicate
|
||||
func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
|
||||
exists, err := ipt.Exists(table, chain, rulespec...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ipt.Append(table, chain, rulespec...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes rulespec in specified table/chain
|
||||
func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error {
|
||||
exists, err := ipt.Exists(table, chain, rulespec...)
|
||||
if err == nil && exists {
|
||||
err = ipt.Delete(table, chain, rulespec...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// List rules in specified table/chain
|
||||
func (ipt *IPTables) ListById(table, chain string, id int) (string, error) {
|
||||
args := []string{"-t", table, "-S", chain, strconv.Itoa(id)}
|
||||
rule, err := ipt.executeList(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rule[0], nil
|
||||
}
|
||||
|
||||
// List rules in specified table/chain
|
||||
func (ipt *IPTables) List(table, chain string) ([]string, error) {
|
||||
args := []string{"-t", table, "-S", chain}
|
||||
return ipt.executeList(args)
|
||||
}
|
||||
|
||||
// List rules (with counters) in specified table/chain
|
||||
func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) {
|
||||
args := []string{"-t", table, "-v", "-S", chain}
|
||||
return ipt.executeList(args)
|
||||
}
|
||||
|
||||
// ListChains returns a slice containing the name of each chain in the specified table.
|
||||
func (ipt *IPTables) ListChains(table string) ([]string, error) {
|
||||
args := []string{"-t", table, "-S"}
|
||||
|
||||
result, err := ipt.executeList(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Iterate over rules to find all default (-P) and user-specified (-N) chains.
|
||||
// Chains definition always come before rules.
|
||||
// Format is the following:
|
||||
// -P OUTPUT ACCEPT
|
||||
// -N Custom
|
||||
var chains []string
|
||||
for _, val := range result {
|
||||
if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") {
|
||||
chains = append(chains, strings.Fields(val)[1])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
// '-S' is fine with non existing rule index as long as the chain exists
|
||||
// therefore pass index 1 to reduce overhead for large chains
|
||||
func (ipt *IPTables) ChainExists(table, chain string) (bool, error) {
|
||||
err := ipt.run("-t", table, "-S", chain, "1")
|
||||
eerr, eok := err.(*Error)
|
||||
switch {
|
||||
case err == nil:
|
||||
return true, nil
|
||||
case eok && eerr.ExitStatus() == 1:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Stats lists rules including the byte and packet counts
|
||||
func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
|
||||
args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
|
||||
lines, err := ipt.executeList(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appendSubnet := func(addr string) string {
|
||||
if strings.IndexByte(addr, byte('/')) < 0 {
|
||||
if strings.IndexByte(addr, '.') < 0 {
|
||||
return addr + "/128"
|
||||
}
|
||||
return addr + "/32"
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
ipv6 := ipt.proto == ProtocolIPv6
|
||||
|
||||
// Skip the warning if exist
|
||||
if strings.HasPrefix(lines[0], "#") {
|
||||
lines = lines[1:]
|
||||
}
|
||||
|
||||
rows := [][]string{}
|
||||
for i, line := range lines {
|
||||
// Skip over chain name and field header
|
||||
if i < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fields:
|
||||
// 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options
|
||||
line = strings.TrimSpace(line)
|
||||
fields := strings.Fields(line)
|
||||
|
||||
// The ip6tables verbose output cannot be naively split due to the default "opt"
|
||||
// field containing 2 single spaces.
|
||||
if ipv6 {
|
||||
// Check if field 6 is "opt" or "source" address
|
||||
dest := fields[6]
|
||||
ip, _, _ := net.ParseCIDR(dest)
|
||||
if ip == nil {
|
||||
ip = net.ParseIP(dest)
|
||||
}
|
||||
|
||||
// If we detected a CIDR or IP, the "opt" field is empty.. insert it.
|
||||
if ip != nil {
|
||||
f := []string{}
|
||||
f = append(f, fields[:4]...)
|
||||
f = append(f, " ") // Empty "opt" field for ip6tables
|
||||
f = append(f, fields[4:]...)
|
||||
fields = f
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust "source" and "destination" to include netmask, to match regular
|
||||
// List output
|
||||
fields[7] = appendSubnet(fields[7])
|
||||
fields[8] = appendSubnet(fields[8])
|
||||
|
||||
// Combine "options" fields 9... into a single space-delimited field.
|
||||
options := fields[9:]
|
||||
fields = fields[:9]
|
||||
fields = append(fields, strings.Join(options, " "))
|
||||
rows = append(rows, fields)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// ParseStat parses a single statistic row into a Stat struct. The input should
|
||||
// be a string slice that is returned from calling the Stat method.
|
||||
func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) {
|
||||
// For forward-compatibility, expect at least 10 fields in the stat
|
||||
if len(stat) < 10 {
|
||||
return parsed, fmt.Errorf("stat contained fewer fields than expected")
|
||||
}
|
||||
|
||||
// Convert the fields that are not plain strings
|
||||
parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64)
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse packets")
|
||||
}
|
||||
parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64)
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse bytes")
|
||||
}
|
||||
_, parsed.Source, err = net.ParseCIDR(stat[7])
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse source")
|
||||
}
|
||||
_, parsed.Destination, err = net.ParseCIDR(stat[8])
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse destination")
|
||||
}
|
||||
|
||||
// Put the fields that are strings
|
||||
parsed.Target = stat[2]
|
||||
parsed.Protocol = stat[3]
|
||||
parsed.Opt = stat[4]
|
||||
parsed.Input = stat[5]
|
||||
parsed.Output = stat[6]
|
||||
parsed.Options = stat[9]
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
// StructuredStats returns statistics as structured data which may be further
|
||||
// parsed and marshaled.
|
||||
func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) {
|
||||
rawStats, err := ipt.Stats(table, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
structStats := []Stat{}
|
||||
for _, rawStat := range rawStats {
|
||||
stat, err := ipt.ParseStat(rawStat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
structStats = append(structStats, stat)
|
||||
}
|
||||
|
||||
return structStats, nil
|
||||
}
|
||||
|
||||
func (ipt *IPTables) executeList(args []string) ([]string, error) {
|
||||
var stdout bytes.Buffer
|
||||
if err := ipt.runWithOutput(args, &stdout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := strings.Split(stdout.String(), "\n")
|
||||
|
||||
// strip trailing newline
|
||||
if len(rules) > 0 && rules[len(rules)-1] == "" {
|
||||
rules = rules[:len(rules)-1]
|
||||
}
|
||||
|
||||
for i, rule := range rules {
|
||||
rules[i] = filterRuleOutput(rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// NewChain creates a new chain in the specified table.
|
||||
// If the chain already exists, it will result in an error.
|
||||
func (ipt *IPTables) NewChain(table, chain string) error {
|
||||
return ipt.run("-t", table, "-N", chain)
|
||||
}
|
||||
|
||||
const existsErr = 1
|
||||
|
||||
// ClearChain flushed (deletes all rules) in the specified table/chain.
|
||||
// If the chain does not exist, a new one will be created
|
||||
func (ipt *IPTables) ClearChain(table, chain string) error {
|
||||
err := ipt.NewChain(table, chain)
|
||||
|
||||
eerr, eok := err.(*Error)
|
||||
switch {
|
||||
case err == nil:
|
||||
return nil
|
||||
case eok && eerr.ExitStatus() == existsErr:
|
||||
// chain already exists. Flush (clear) it.
|
||||
return ipt.run("-t", table, "-F", chain)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// RenameChain renames the old chain to the new one.
|
||||
func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error {
|
||||
return ipt.run("-t", table, "-E", oldChain, newChain)
|
||||
}
|
||||
|
||||
// DeleteChain deletes the chain in the specified table.
|
||||
// The chain must be empty
|
||||
func (ipt *IPTables) DeleteChain(table, chain string) error {
|
||||
return ipt.run("-t", table, "-X", chain)
|
||||
}
|
||||
|
||||
func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error {
|
||||
exists, err := ipt.ChainExists(table, chain)
|
||||
if err != nil || !exists {
|
||||
return err
|
||||
}
|
||||
err = ipt.run("-t", table, "-F", chain)
|
||||
if err == nil {
|
||||
err = ipt.run("-t", table, "-X", chain)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (ipt *IPTables) ClearAll() error {
|
||||
return ipt.run("-F")
|
||||
}
|
||||
|
||||
func (ipt *IPTables) DeleteAll() error {
|
||||
return ipt.run("-X")
|
||||
}
|
||||
|
||||
// ChangePolicy changes policy on chain to target
|
||||
func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
|
||||
return ipt.run("-t", table, "-P", chain, target)
|
||||
}
|
||||
|
||||
// Check if the underlying iptables command supports the --random-fully flag
|
||||
func (ipt *IPTables) HasRandomFully() bool {
|
||||
return ipt.hasRandomFully
|
||||
}
|
||||
|
||||
// Return version components of the underlying iptables command
|
||||
func (ipt *IPTables) GetIptablesVersion() (int, int, int) {
|
||||
return ipt.v1, ipt.v2, ipt.v3
|
||||
}
|
||||
|
||||
// run runs an iptables command with the given arguments, ignoring
|
||||
// any stdout output
|
||||
func (ipt *IPTables) run(args ...string) error {
|
||||
return ipt.runWithOutput(args, nil)
|
||||
}
|
||||
|
||||
// runWithOutput runs an iptables command with the given arguments,
|
||||
// writing any stdout output to the given writer
|
||||
func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
|
||||
args = append([]string{ipt.path}, args...)
|
||||
if ipt.hasWait {
|
||||
args = append(args, "--wait")
|
||||
if ipt.timeout != 0 && ipt.waitSupportSecond {
|
||||
args = append(args, strconv.Itoa(ipt.timeout))
|
||||
}
|
||||
} else {
|
||||
fmu, err := newXtablesFileLock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ul, err := fmu.tryLock()
|
||||
if err != nil {
|
||||
syscall.Close(fmu.fd)
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = ul.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Cmd{
|
||||
Path: ipt.path,
|
||||
Args: args,
|
||||
Stdout: stdout,
|
||||
Stderr: &stderr,
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
switch e := err.(type) {
|
||||
case *exec.ExitError:
|
||||
return &Error{*e, cmd, stderr.String(), nil}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables".
|
||||
func getIptablesCommand(proto Protocol) string {
|
||||
if proto == ProtocolIPv6 {
|
||||
return "ip6tables"
|
||||
} else {
|
||||
return "iptables"
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if iptables has the "-C" and "--wait" flag
|
||||
func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool, bool) {
|
||||
return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesWaitSupportSecond(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3)
|
||||
}
|
||||
|
||||
// getIptablesVersion returns the first three components of the iptables version
|
||||
// and the operating mode (e.g. nf_tables or legacy)
|
||||
// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil)
|
||||
func extractIptablesVersion(str string) (int, int, int, string, error) {
|
||||
versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`)
|
||||
result := versionMatcher.FindStringSubmatch(str)
|
||||
if result == nil {
|
||||
return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str)
|
||||
}
|
||||
|
||||
v1, err := strconv.Atoi(result[1])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
v2, err := strconv.Atoi(result[2])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
v3, err := strconv.Atoi(result[3])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
mode := "legacy"
|
||||
if result[4] != "" {
|
||||
mode = result[4]
|
||||
}
|
||||
return v1, v2, v3, mode, nil
|
||||
}
|
||||
|
||||
// Runs "iptables --version" to get the version string
|
||||
func getIptablesVersionString(path string) (string, error) {
|
||||
cmd := exec.Command(path, "--version")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.4.11, when --check was added
|
||||
func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 4 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 4 && v3 >= 11 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.4.20, when --wait was added
|
||||
func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 4 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 4 && v3 >= 20 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if an iptablse version is after 1.6.0, when --wait support second
|
||||
func iptablesWaitSupportSecond(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 >= 6 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.6.2, when --random-fully was added
|
||||
func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 6 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 6 && v3 >= 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if a rule specification exists for a table
|
||||
func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) {
|
||||
rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ")
|
||||
args := []string{"-t", table, "-S"}
|
||||
var stdout bytes.Buffer
|
||||
err := ipt.runWithOutput(args, &stdout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.Contains(stdout.String(), rs), nil
|
||||
}
|
||||
|
||||
// counterRegex is the regex used to detect nftables counter format
|
||||
var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `)
|
||||
|
||||
// filterRuleOutput works around some inconsistencies in output.
|
||||
// For example, when iptables is in legacy vs. nftables mode, it produces
|
||||
// different results.
|
||||
func filterRuleOutput(rule string) string {
|
||||
out := rule
|
||||
|
||||
// work around an output difference in nftables mode where counters
|
||||
// are output in iptables-save format, rather than iptables -S format
|
||||
// The string begins with "[0:0]"
|
||||
//
|
||||
// Fixes #49
|
||||
if groups := counterRegex.FindStringSubmatch(out); groups != nil {
|
||||
// drop the brackets
|
||||
out = out[len(groups[0]):]
|
||||
out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2])
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// In earlier versions of iptables, the xtables lock was implemented
|
||||
// via a Unix socket, but now flock is used via this lockfile:
|
||||
// http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707
|
||||
// Note the LSB-conforming "/run" directory does not exist on old
|
||||
// distributions, so assume "/var" is symlinked
|
||||
xtablesLockFilePath = "/var/run/xtables.lock"
|
||||
|
||||
defaultFilePerm = 0600
|
||||
)
|
||||
|
||||
type Unlocker interface {
|
||||
Unlock() error
|
||||
}
|
||||
|
||||
type nopUnlocker struct{}
|
||||
|
||||
func (_ nopUnlocker) Unlock() error { return nil }
|
||||
|
||||
type fileLock struct {
|
||||
// mu is used to protect against concurrent invocations from within this process
|
||||
mu sync.Mutex
|
||||
fd int
|
||||
}
|
||||
|
||||
// tryLock takes an exclusive lock on the xtables lock file without blocking.
|
||||
// This is best-effort only: if the exclusive lock would block (i.e. because
|
||||
// another process already holds it), no error is returned. Otherwise, any
|
||||
// error encountered during the locking operation is returned.
|
||||
// The returned Unlocker should be used to release the lock when the caller is
|
||||
// done invoking iptables commands.
|
||||
func (l *fileLock) tryLock() (Unlocker, error) {
|
||||
l.mu.Lock()
|
||||
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
switch err {
|
||||
case syscall.EWOULDBLOCK:
|
||||
l.mu.Unlock()
|
||||
return nopUnlocker{}, nil
|
||||
case nil:
|
||||
return l, nil
|
||||
default:
|
||||
l.mu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock closes the underlying file, which implicitly unlocks it as well. It
|
||||
// also unlocks the associated mutex.
|
||||
func (l *fileLock) Unlock() error {
|
||||
defer l.mu.Unlock()
|
||||
return syscall.Close(l.fd)
|
||||
}
|
||||
|
||||
// newXtablesFileLock opens a new lock on the xtables lockfile without
|
||||
// acquiring the lock
|
||||
func newXtablesFileLock() (*fileLock, error) {
|
||||
fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fileLock{fd: fd}, nil
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Brian Goff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package md2man
|
||||
|
||||
import (
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// Render converts a markdown document into a roff formatted document.
|
||||
func Render(doc []byte) []byte {
|
||||
renderer := NewRoffRenderer()
|
||||
|
||||
return blackfriday.Run(doc,
|
||||
[]blackfriday.Option{blackfriday.WithRenderer(renderer),
|
||||
blackfriday.WithExtensions(renderer.GetExtensions())}...)
|
||||
}
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
package md2man
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// roffRenderer implements the blackfriday.Renderer interface for creating
|
||||
// roff format (manpages) from markdown text
|
||||
type roffRenderer struct {
|
||||
extensions blackfriday.Extensions
|
||||
listCounters []int
|
||||
firstHeader bool
|
||||
firstDD bool
|
||||
listDepth int
|
||||
}
|
||||
|
||||
const (
|
||||
titleHeader = ".TH "
|
||||
topLevelHeader = "\n\n.SH "
|
||||
secondLevelHdr = "\n.SH "
|
||||
otherHeader = "\n.SS "
|
||||
crTag = "\n"
|
||||
emphTag = "\\fI"
|
||||
emphCloseTag = "\\fP"
|
||||
strongTag = "\\fB"
|
||||
strongCloseTag = "\\fP"
|
||||
breakTag = "\n.br\n"
|
||||
paraTag = "\n.PP\n"
|
||||
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
|
||||
linkTag = "\n\\[la]"
|
||||
linkCloseTag = "\\[ra]"
|
||||
codespanTag = "\\fB\\fC"
|
||||
codespanCloseTag = "\\fR"
|
||||
codeTag = "\n.PP\n.RS\n\n.nf\n"
|
||||
codeCloseTag = "\n.fi\n.RE\n"
|
||||
quoteTag = "\n.PP\n.RS\n"
|
||||
quoteCloseTag = "\n.RE\n"
|
||||
listTag = "\n.RS\n"
|
||||
listCloseTag = "\n.RE\n"
|
||||
dtTag = "\n.TP\n"
|
||||
dd2Tag = "\n"
|
||||
tableStart = "\n.TS\nallbox;\n"
|
||||
tableEnd = ".TE\n"
|
||||
tableCellStart = "T{\n"
|
||||
tableCellEnd = "\nT}\n"
|
||||
)
|
||||
|
||||
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
|
||||
// from markdown
|
||||
func NewRoffRenderer() *roffRenderer { // nolint: golint
|
||||
var extensions blackfriday.Extensions
|
||||
|
||||
extensions |= blackfriday.NoIntraEmphasis
|
||||
extensions |= blackfriday.Tables
|
||||
extensions |= blackfriday.FencedCode
|
||||
extensions |= blackfriday.SpaceHeadings
|
||||
extensions |= blackfriday.Footnotes
|
||||
extensions |= blackfriday.Titleblock
|
||||
extensions |= blackfriday.DefinitionLists
|
||||
return &roffRenderer{
|
||||
extensions: extensions,
|
||||
}
|
||||
}
|
||||
|
||||
// GetExtensions returns the list of extensions used by this renderer implementation
|
||||
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
|
||||
return r.extensions
|
||||
}
|
||||
|
||||
// RenderHeader handles outputting the header at document start
|
||||
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
|
||||
// disable hyphenation
|
||||
out(w, ".nh\n")
|
||||
}
|
||||
|
||||
// RenderFooter handles outputting the footer at the document end; the roff
|
||||
// renderer has no footer information
|
||||
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
|
||||
}
|
||||
|
||||
// RenderNode is called for each node in a markdown document; based on the node
|
||||
// type the equivalent roff output is sent to the writer
|
||||
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
|
||||
var walkAction = blackfriday.GoToNext
|
||||
|
||||
switch node.Type {
|
||||
case blackfriday.Text:
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
case blackfriday.Softbreak:
|
||||
out(w, crTag)
|
||||
case blackfriday.Hardbreak:
|
||||
out(w, breakTag)
|
||||
case blackfriday.Emph:
|
||||
if entering {
|
||||
out(w, emphTag)
|
||||
} else {
|
||||
out(w, emphCloseTag)
|
||||
}
|
||||
case blackfriday.Strong:
|
||||
if entering {
|
||||
out(w, strongTag)
|
||||
} else {
|
||||
out(w, strongCloseTag)
|
||||
}
|
||||
case blackfriday.Link:
|
||||
if !entering {
|
||||
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
|
||||
}
|
||||
case blackfriday.Image:
|
||||
// ignore images
|
||||
walkAction = blackfriday.SkipChildren
|
||||
case blackfriday.Code:
|
||||
out(w, codespanTag)
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
out(w, codespanCloseTag)
|
||||
case blackfriday.Document:
|
||||
break
|
||||
case blackfriday.Paragraph:
|
||||
// roff .PP markers break lists
|
||||
if r.listDepth > 0 {
|
||||
return blackfriday.GoToNext
|
||||
}
|
||||
if entering {
|
||||
out(w, paraTag)
|
||||
} else {
|
||||
out(w, crTag)
|
||||
}
|
||||
case blackfriday.BlockQuote:
|
||||
if entering {
|
||||
out(w, quoteTag)
|
||||
} else {
|
||||
out(w, quoteCloseTag)
|
||||
}
|
||||
case blackfriday.Heading:
|
||||
r.handleHeading(w, node, entering)
|
||||
case blackfriday.HorizontalRule:
|
||||
out(w, hruleTag)
|
||||
case blackfriday.List:
|
||||
r.handleList(w, node, entering)
|
||||
case blackfriday.Item:
|
||||
r.handleItem(w, node, entering)
|
||||
case blackfriday.CodeBlock:
|
||||
out(w, codeTag)
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
out(w, codeCloseTag)
|
||||
case blackfriday.Table:
|
||||
r.handleTable(w, node, entering)
|
||||
case blackfriday.TableHead:
|
||||
case blackfriday.TableBody:
|
||||
case blackfriday.TableRow:
|
||||
// no action as cell entries do all the nroff formatting
|
||||
return blackfriday.GoToNext
|
||||
case blackfriday.TableCell:
|
||||
r.handleTableCell(w, node, entering)
|
||||
case blackfriday.HTMLSpan:
|
||||
// ignore other HTML tags
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
|
||||
}
|
||||
return walkAction
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
switch node.Level {
|
||||
case 1:
|
||||
if !r.firstHeader {
|
||||
out(w, titleHeader)
|
||||
r.firstHeader = true
|
||||
break
|
||||
}
|
||||
out(w, topLevelHeader)
|
||||
case 2:
|
||||
out(w, secondLevelHdr)
|
||||
default:
|
||||
out(w, otherHeader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
openTag := listTag
|
||||
closeTag := listCloseTag
|
||||
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||
// tags for definition lists handled within Item node
|
||||
openTag = ""
|
||||
closeTag = ""
|
||||
}
|
||||
if entering {
|
||||
r.listDepth++
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
r.listCounters = append(r.listCounters, 1)
|
||||
}
|
||||
out(w, openTag)
|
||||
} else {
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
r.listCounters = r.listCounters[:len(r.listCounters)-1]
|
||||
}
|
||||
out(w, closeTag)
|
||||
r.listDepth--
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
|
||||
r.listCounters[len(r.listCounters)-1]++
|
||||
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
|
||||
// DT (definition term): line just before DD (see below).
|
||||
out(w, dtTag)
|
||||
r.firstDD = true
|
||||
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||
// DD (definition description): line that starts with ": ".
|
||||
//
|
||||
// We have to distinguish between the first DD and the
|
||||
// subsequent ones, as there should be no vertical
|
||||
// whitespace between the DT and the first DD.
|
||||
if r.firstDD {
|
||||
r.firstDD = false
|
||||
} else {
|
||||
out(w, dd2Tag)
|
||||
}
|
||||
} else {
|
||||
out(w, ".IP \\(bu 2\n")
|
||||
}
|
||||
} else {
|
||||
out(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
out(w, tableStart)
|
||||
// call walker to count cells (and rows?) so format section can be produced
|
||||
columns := countColumns(node)
|
||||
out(w, strings.Repeat("l ", columns)+"\n")
|
||||
out(w, strings.Repeat("l ", columns)+".\n")
|
||||
} else {
|
||||
out(w, tableEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
var start string
|
||||
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
|
||||
start = "\t"
|
||||
}
|
||||
if node.IsHeader {
|
||||
start += codespanTag
|
||||
} else if nodeLiteralSize(node) > 30 {
|
||||
start += tableCellStart
|
||||
}
|
||||
out(w, start)
|
||||
} else {
|
||||
var end string
|
||||
if node.IsHeader {
|
||||
end = codespanCloseTag
|
||||
} else if nodeLiteralSize(node) > 30 {
|
||||
end = tableCellEnd
|
||||
}
|
||||
if node.Next == nil && end != tableCellEnd {
|
||||
// Last cell: need to carriage return if we are at the end of the
|
||||
// header row and content isn't wrapped in a "tablecell"
|
||||
end += crTag
|
||||
}
|
||||
out(w, end)
|
||||
}
|
||||
}
|
||||
|
||||
func nodeLiteralSize(node *blackfriday.Node) int {
|
||||
total := 0
|
||||
for n := node.FirstChild; n != nil; n = n.FirstChild {
|
||||
total += len(n.Literal)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// because roff format requires knowing the column count before outputting any table
|
||||
// data we need to walk a table tree and count the columns
|
||||
func countColumns(node *blackfriday.Node) int {
|
||||
var columns int
|
||||
|
||||
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
switch node.Type {
|
||||
case blackfriday.TableRow:
|
||||
if !entering {
|
||||
return blackfriday.Terminate
|
||||
}
|
||||
case blackfriday.TableCell:
|
||||
if entering {
|
||||
columns++
|
||||
}
|
||||
default:
|
||||
}
|
||||
return blackfriday.GoToNext
|
||||
})
|
||||
return columns
|
||||
}
|
||||
|
||||
func out(w io.Writer, output string) {
|
||||
io.WriteString(w, output) // nolint: errcheck
|
||||
}
|
||||
|
||||
func escapeSpecialChars(w io.Writer, text []byte) {
|
||||
for i := 0; i < len(text); i++ {
|
||||
// escape initial apostrophe or period
|
||||
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
|
||||
out(w, "\\&")
|
||||
}
|
||||
|
||||
// directly copy normal characters
|
||||
org := i
|
||||
|
||||
for i < len(text) && text[i] != '\\' {
|
||||
i++
|
||||
}
|
||||
if i > org {
|
||||
w.Write(text[org:i]) // nolint: errcheck
|
||||
}
|
||||
|
||||
// escape a character
|
||||
if i >= len(text) {
|
||||
break
|
||||
}
|
||||
|
||||
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
cmd/snappytool/snappytool
|
||||
testdata/bench
|
||||
|
||||
# These explicitly listed benchmark data files are for an obsolete version of
|
||||
# snappy_test.go.
|
||||
testdata/alice29.txt
|
||||
testdata/asyoulik.txt
|
||||
testdata/fireworks.jpeg
|
||||
testdata/geo.protodata
|
||||
testdata/html
|
||||
testdata/html_x_4
|
||||
testdata/kppkn.gtb
|
||||
testdata/lcet10.txt
|
||||
testdata/paper-100k.pdf
|
||||
testdata/plrabn12.txt
|
||||
testdata/urls.10K
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Amazon.com, Inc
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Eric Buth <eric@topos.com>
|
||||
Google Inc.
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Snappy-Go repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alex Legg <alexlegg@google.com>
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Eric Buth <eric@topos.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Jonathan Swinney <jswinney@amazon.com>
|
||||
Kai Backman <kaib@golang.org>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
Marc-Antoine Ruel <maruel@chromium.org>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Rob Pike <r@golang.org>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
The Snappy compression format in the Go programming language.
|
||||
|
||||
To download and install from source:
|
||||
$ go get github.com/golang/snappy
|
||||
|
||||
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||
under the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
Benchmarks.
|
||||
|
||||
The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten
|
||||
or so files, the same set used by the C++ Snappy code (github.com/google/snappy
|
||||
and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @
|
||||
3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29:
|
||||
|
||||
"go test -test.bench=."
|
||||
|
||||
_UFlat0-8 2.19GB/s ± 0% html
|
||||
_UFlat1-8 1.41GB/s ± 0% urls
|
||||
_UFlat2-8 23.5GB/s ± 2% jpg
|
||||
_UFlat3-8 1.91GB/s ± 0% jpg_200
|
||||
_UFlat4-8 14.0GB/s ± 1% pdf
|
||||
_UFlat5-8 1.97GB/s ± 0% html4
|
||||
_UFlat6-8 814MB/s ± 0% txt1
|
||||
_UFlat7-8 785MB/s ± 0% txt2
|
||||
_UFlat8-8 857MB/s ± 0% txt3
|
||||
_UFlat9-8 719MB/s ± 1% txt4
|
||||
_UFlat10-8 2.84GB/s ± 0% pb
|
||||
_UFlat11-8 1.05GB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 1.04GB/s ± 0% html
|
||||
_ZFlat1-8 534MB/s ± 0% urls
|
||||
_ZFlat2-8 15.7GB/s ± 1% jpg
|
||||
_ZFlat3-8 740MB/s ± 3% jpg_200
|
||||
_ZFlat4-8 9.20GB/s ± 1% pdf
|
||||
_ZFlat5-8 991MB/s ± 0% html4
|
||||
_ZFlat6-8 379MB/s ± 0% txt1
|
||||
_ZFlat7-8 352MB/s ± 0% txt2
|
||||
_ZFlat8-8 396MB/s ± 1% txt3
|
||||
_ZFlat9-8 327MB/s ± 1% txt4
|
||||
_ZFlat10-8 1.33GB/s ± 1% pb
|
||||
_ZFlat11-8 605MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
"go test -test.bench=. -tags=noasm"
|
||||
|
||||
_UFlat0-8 621MB/s ± 2% html
|
||||
_UFlat1-8 494MB/s ± 1% urls
|
||||
_UFlat2-8 23.2GB/s ± 1% jpg
|
||||
_UFlat3-8 1.12GB/s ± 1% jpg_200
|
||||
_UFlat4-8 4.35GB/s ± 1% pdf
|
||||
_UFlat5-8 609MB/s ± 0% html4
|
||||
_UFlat6-8 296MB/s ± 0% txt1
|
||||
_UFlat7-8 288MB/s ± 0% txt2
|
||||
_UFlat8-8 309MB/s ± 1% txt3
|
||||
_UFlat9-8 280MB/s ± 1% txt4
|
||||
_UFlat10-8 753MB/s ± 0% pb
|
||||
_UFlat11-8 400MB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 409MB/s ± 1% html
|
||||
_ZFlat1-8 250MB/s ± 1% urls
|
||||
_ZFlat2-8 12.3GB/s ± 1% jpg
|
||||
_ZFlat3-8 132MB/s ± 0% jpg_200
|
||||
_ZFlat4-8 2.92GB/s ± 0% pdf
|
||||
_ZFlat5-8 405MB/s ± 1% html4
|
||||
_ZFlat6-8 179MB/s ± 1% txt1
|
||||
_ZFlat7-8 170MB/s ± 1% txt2
|
||||
_ZFlat8-8 189MB/s ± 1% txt3
|
||||
_ZFlat9-8 164MB/s ± 1% txt4
|
||||
_ZFlat10-8 479MB/s ± 1% pb
|
||||
_ZFlat11-8 270MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
For comparison (Go's encoded output is byte-for-byte identical to C++'s), here
|
||||
are the numbers from C++ Snappy's
|
||||
|
||||
make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log
|
||||
|
||||
BM_UFlat/0 2.4GB/s html
|
||||
BM_UFlat/1 1.4GB/s urls
|
||||
BM_UFlat/2 21.8GB/s jpg
|
||||
BM_UFlat/3 1.5GB/s jpg_200
|
||||
BM_UFlat/4 13.3GB/s pdf
|
||||
BM_UFlat/5 2.1GB/s html4
|
||||
BM_UFlat/6 1.0GB/s txt1
|
||||
BM_UFlat/7 959.4MB/s txt2
|
||||
BM_UFlat/8 1.0GB/s txt3
|
||||
BM_UFlat/9 864.5MB/s txt4
|
||||
BM_UFlat/10 2.9GB/s pb
|
||||
BM_UFlat/11 1.2GB/s gaviota
|
||||
|
||||
BM_ZFlat/0 944.3MB/s html (22.31 %)
|
||||
BM_ZFlat/1 501.6MB/s urls (47.78 %)
|
||||
BM_ZFlat/2 14.3GB/s jpg (99.95 %)
|
||||
BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %)
|
||||
BM_ZFlat/4 8.3GB/s pdf (83.30 %)
|
||||
BM_ZFlat/5 903.5MB/s html4 (22.52 %)
|
||||
BM_ZFlat/6 336.0MB/s txt1 (57.88 %)
|
||||
BM_ZFlat/7 312.3MB/s txt2 (61.91 %)
|
||||
BM_ZFlat/8 353.1MB/s txt3 (54.99 %)
|
||||
BM_ZFlat/9 289.9MB/s txt4 (66.26 %)
|
||||
BM_ZFlat/10 1.2GB/s pb (19.68 %)
|
||||
BM_ZFlat/11 527.4MB/s gaviota (37.72 %)
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCorrupt reports that the input is invalid.
|
||||
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||
// ErrTooLarge reports that the uncompressed length is too large.
|
||||
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||
// ErrUnsupported reports that the input isn't supported.
|
||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||
|
||||
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
||||
)
|
||||
|
||||
// DecodedLen returns the length of the decoded block.
|
||||
func DecodedLen(src []byte) (int, error) {
|
||||
v, _, err := decodedLen(src)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// decodedLen returns the length of the decoded block and the number of bytes
|
||||
// that the length header occupied.
|
||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||
v, n := binary.Uvarint(src)
|
||||
if n <= 0 || v > 0xffffffff {
|
||||
return 0, 0, ErrCorrupt
|
||||
}
|
||||
|
||||
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||
if wordSize == 32 && v > 0x7fffffff {
|
||||
return 0, 0, ErrTooLarge
|
||||
}
|
||||
return int(v), n, nil
|
||||
}
|
||||
|
||||
const (
|
||||
decodeErrCodeCorrupt = 1
|
||||
decodeErrCodeUnsupportedLiteralLength = 2
|
||||
)
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
//
|
||||
// Decode handles the Snappy block format, not the Snappy stream format.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
dLen, s, err := decodedLen(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dLen <= len(dst) {
|
||||
dst = dst[:dLen]
|
||||
} else {
|
||||
dst = make([]byte, dLen)
|
||||
}
|
||||
switch decode(dst, src[s:]) {
|
||||
case 0:
|
||||
return dst, nil
|
||||
case decodeErrCodeUnsupportedLiteralLength:
|
||||
return nil, errUnsupportedLiteralLength
|
||||
}
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||
// format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
decoded: make([]byte, maxBlockSize),
|
||||
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||
//
|
||||
// Reader handles the Snappy stream format, not the Snappy block format.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
decoded []byte
|
||||
buf []byte
|
||||
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||
i, j int
|
||||
readHeader bool
|
||||
}
|
||||
|
||||
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||
// a new one.
|
||||
func (r *Reader) Reset(reader io.Reader) {
|
||||
r.r = reader
|
||||
r.err = nil
|
||||
r.i = 0
|
||||
r.j = 0
|
||||
r.readHeader = false
|
||||
}
|
||||
|
||||
func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||
if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) {
|
||||
r.err = ErrCorrupt
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Reader) fill() error {
|
||||
for r.i >= r.j {
|
||||
if !r.readFull(r.buf[:4], true) {
|
||||
return r.err
|
||||
}
|
||||
chunkType := r.buf[0]
|
||||
if !r.readHeader {
|
||||
if chunkType != chunkTypeStreamIdentifier {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.readHeader = true
|
||||
}
|
||||
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||
if chunkLen > len(r.buf) {
|
||||
r.err = ErrUnsupported
|
||||
return r.err
|
||||
}
|
||||
|
||||
// The chunk types are specified at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
switch chunkType {
|
||||
case chunkTypeCompressedData:
|
||||
// Section 4.2. Compressed data (chunk type 0x00).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:chunkLen]
|
||||
if !r.readFull(buf, false) {
|
||||
return r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
buf = buf[checksumSize:]
|
||||
|
||||
n, err := DecodedLen(buf)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return r.err
|
||||
}
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if _, err := Decode(r.decoded, buf); err != nil {
|
||||
r.err = err
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeUncompressedData:
|
||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:checksumSize]
|
||||
if !r.readFull(buf, false) {
|
||||
return r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
// Read directly into r.decoded instead of via r.buf.
|
||||
n := chunkLen - checksumSize
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.decoded[:n], false) {
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeStreamIdentifier:
|
||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||
if chunkLen != len(magicBody) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||
return r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if chunkType <= 0x7f {
|
||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||
r.err = ErrUnsupported
|
||||
return r.err
|
||||
}
|
||||
// Section 4.4 Padding (chunk type 0xfe).
|
||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||
if !r.readFull(r.buf[:chunkLen], false) {
|
||||
return r.err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read satisfies the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
if err := r.fill(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n := copy(p, r.decoded[r.i:r.j])
|
||||
r.i += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadByte satisfies the io.ByteReader interface.
|
||||
func (r *Reader) ReadByte() (byte, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
if err := r.fill(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c := r.decoded[r.i]
|
||||
r.i++
|
||||
return c, nil
|
||||
}
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - AX scratch
|
||||
// - BX scratch
|
||||
// - CX length or x
|
||||
// - DX offset
|
||||
// - SI &src[s]
|
||||
// - DI &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI.
|
||||
// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI.
|
||||
TEXT ·decode(SB), NOSPLIT, $48-56
|
||||
// Initialize SI, DI and R8-R13.
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, DI
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, SI
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMPQ SI, R13
|
||||
JEQ end
|
||||
|
||||
// CX = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBLZX (SI), CX
|
||||
MOVL CX, BX
|
||||
ANDL $3, BX
|
||||
CMPL BX, $1
|
||||
JAE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
SHRL $2, CX
|
||||
CMPL CX, $60
|
||||
JAE tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
INCQ SI
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that CX == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// CX can hold 64 bits, so the increment cannot overflow.
|
||||
INCQ CX
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// AX = len(dst) - d
|
||||
// BX = len(src) - s
|
||||
MOVQ R10, AX
|
||||
SUBQ DI, AX
|
||||
MOVQ R13, BX
|
||||
SUBQ SI, BX
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMPQ CX, $16
|
||||
JGT callMemmove
|
||||
CMPQ AX, $16
|
||||
JLT callMemmove
|
||||
CMPQ BX, $16
|
||||
JLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU X0, 0(DI)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMPQ CX, AX
|
||||
JGT errCorrupt
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// DI, SI and CX as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ SI, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVQ CX, 40(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVQ 40(SP), CX
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADDQ CX, SI
|
||||
SUBQ $58, SI
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
CMPL CX, $61
|
||||
JEQ tagLit61
|
||||
JA tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBLZX -1(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVWLZX -2(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPL CX, $62
|
||||
JA tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVWLZX -3(SI), CX
|
||||
MOVBLZX -1(SI), BX
|
||||
SHLL $16, BX
|
||||
ORL BX, CX
|
||||
JMP doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVL -4(SI), CX
|
||||
JMP doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy4:
|
||||
// case tagCopy4:
|
||||
// s += 5
|
||||
ADDQ $5, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-5])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
MOVLQZX -4(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADDQ $3, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
MOVWQZX -2(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - BX == src[s] & 0x03
|
||||
// - CX == src[s]
|
||||
CMPQ BX, $2
|
||||
JEQ tagCopy2
|
||||
JA tagCopy4
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADDQ $2, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
MOVQ CX, DX
|
||||
ANDQ $0xe0, DX
|
||||
SHLQ $3, DX
|
||||
MOVBQZX -1(SI), BX
|
||||
ORQ BX, DX
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
SHRQ $2, CX
|
||||
ANDQ $7, CX
|
||||
ADDQ $4, CX
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - CX == length && CX > 0
|
||||
// - DX == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
CMPQ DX, $0
|
||||
JLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVQ DI, BX
|
||||
SUBQ R8, BX
|
||||
CMPQ BX, DX
|
||||
JLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVQ R10, BX
|
||||
SUBQ DI, BX
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVQ R10, R14
|
||||
SUBQ DI, R14
|
||||
MOVQ DI, R15
|
||||
SUBQ DX, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMPQ CX, $16
|
||||
JGT slowForwardCopy
|
||||
CMPQ DX, $8
|
||||
JLT slowForwardCopy
|
||||
CMPQ R14, $16
|
||||
JLT slowForwardCopy
|
||||
MOVQ 0(R15), AX
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ 8(R15), BX
|
||||
MOVQ BX, 8(DI)
|
||||
ADDQ CX, DI
|
||||
JMP loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUBQ $10, R14
|
||||
CMPQ CX, R14
|
||||
JGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMPQ DX, $8
|
||||
JGE fixUpSlowForwardCopy
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (DI)
|
||||
SUBQ DX, CX
|
||||
ADDQ DX, DI
|
||||
ADDQ DX, DX
|
||||
JMP makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by DI being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save DI to AX so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVQ DI, AX
|
||||
ADDQ CX, DI
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
CMPQ CX, $0
|
||||
JLE loop
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (AX)
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, AX
|
||||
SUBQ $8, CX
|
||||
JMP finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), BX
|
||||
MOVB BX, (DI)
|
||||
INCQ R15
|
||||
INCQ DI
|
||||
DECQ CX
|
||||
JNZ verySlowForwardCopy
|
||||
JMP loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMPQ DI, R10
|
||||
JNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVQ $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVQ $1, ret+48(FP)
|
||||
RET
|
||||
+494
@@ -0,0 +1,494 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - R2 scratch
|
||||
// - R3 scratch
|
||||
// - R4 length or x
|
||||
// - R5 offset
|
||||
// - R6 &src[s]
|
||||
// - R7 &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7.
|
||||
// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6.
|
||||
TEXT ·decode(SB), NOSPLIT, $56-56
|
||||
// Initialize R6, R7 and R8-R13.
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD dst_len+8(FP), R9
|
||||
MOVD R8, R7
|
||||
MOVD R8, R10
|
||||
ADD R9, R10, R10
|
||||
MOVD src_base+24(FP), R11
|
||||
MOVD src_len+32(FP), R12
|
||||
MOVD R11, R6
|
||||
MOVD R11, R13
|
||||
ADD R12, R13, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMP R13, R6
|
||||
BEQ end
|
||||
|
||||
// R4 = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBU (R6), R4
|
||||
MOVW R4, R3
|
||||
ANDW $3, R3
|
||||
MOVW $1, R1
|
||||
CMPW R1, R3
|
||||
BGE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
MOVW $60, R1
|
||||
LSRW $2, R4, R4
|
||||
CMPW R4, R1
|
||||
BLS tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
ADD $1, R6, R6
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that R4 == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// R4 can hold 64 bits, so the increment cannot overflow.
|
||||
ADD $1, R4, R4
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// R2 = len(dst) - d
|
||||
// R3 = len(src) - s
|
||||
MOVD R10, R2
|
||||
SUB R7, R2, R2
|
||||
MOVD R13, R3
|
||||
SUB R6, R3, R3
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMP $16, R4
|
||||
BGT callMemmove
|
||||
CMP $16, R2
|
||||
BLT callMemmove
|
||||
CMP $16, R3
|
||||
BLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
LDP 0(R6), (R14, R15)
|
||||
STP (R14, R15), 0(R7)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADD R4, R7, R7
|
||||
ADD R4, R6, R6
|
||||
B loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMP R2, R4
|
||||
BGT errCorrupt
|
||||
CMP R3, R4
|
||||
BGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// R7, R6 and R4 as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVD R7, 8(RSP)
|
||||
MOVD R6, 16(RSP)
|
||||
MOVD R4, 24(RSP)
|
||||
MOVD R7, 32(RSP)
|
||||
MOVD R6, 40(RSP)
|
||||
MOVD R4, 48(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVD 32(RSP), R7
|
||||
MOVD 40(RSP), R6
|
||||
MOVD 48(RSP), R4
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD dst_len+8(FP), R9
|
||||
MOVD R8, R10
|
||||
ADD R9, R10, R10
|
||||
MOVD src_base+24(FP), R11
|
||||
MOVD src_len+32(FP), R12
|
||||
MOVD R11, R13
|
||||
ADD R12, R13, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADD R4, R7, R7
|
||||
ADD R4, R6, R6
|
||||
B loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADD R4, R6, R6
|
||||
SUB $58, R6, R6
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
MOVW $61, R1
|
||||
CMPW R1, R4
|
||||
BEQ tagLit61
|
||||
BGT tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBU -1(R6), R4
|
||||
B doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVHU -2(R6), R4
|
||||
B doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPW $62, R4
|
||||
BHI tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVHU -3(R6), R4
|
||||
MOVBU -1(R6), R3
|
||||
ORR R3<<16, R4
|
||||
B doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVWU -4(R6), R4
|
||||
B doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy4:
|
||||
// case tagCopy4:
|
||||
// s += 5
|
||||
ADD $5, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-5])>>2
|
||||
MOVD $1, R1
|
||||
ADD R4>>2, R1, R4
|
||||
|
||||
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
MOVWU -4(R6), R5
|
||||
B doCopy
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADD $3, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
MOVD $1, R1
|
||||
ADD R4>>2, R1, R4
|
||||
|
||||
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
MOVHU -2(R6), R5
|
||||
B doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - R3 == src[s] & 0x03
|
||||
// - R4 == src[s]
|
||||
CMP $2, R3
|
||||
BEQ tagCopy2
|
||||
BGT tagCopy4
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADD $2, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
MOVD R4, R5
|
||||
AND $0xe0, R5
|
||||
MOVBU -1(R6), R3
|
||||
ORR R5<<3, R3, R5
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
MOVD $7, R1
|
||||
AND R4>>2, R1, R4
|
||||
ADD $4, R4, R4
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - R4 == length && R4 > 0
|
||||
// - R5 == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
MOVD $0, R1
|
||||
CMP R1, R5
|
||||
BLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVD R7, R3
|
||||
SUB R8, R3, R3
|
||||
CMP R5, R3
|
||||
BLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVD R10, R3
|
||||
SUB R7, R3, R3
|
||||
CMP R3, R4
|
||||
BGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVD R10, R14
|
||||
SUB R7, R14, R14
|
||||
MOVD R7, R15
|
||||
SUB R5, R15, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMP $16, R4
|
||||
BGT slowForwardCopy
|
||||
CMP $8, R5
|
||||
BLT slowForwardCopy
|
||||
CMP $16, R14
|
||||
BLT slowForwardCopy
|
||||
MOVD 0(R15), R2
|
||||
MOVD R2, 0(R7)
|
||||
MOVD 8(R15), R3
|
||||
MOVD R3, 8(R7)
|
||||
ADD R4, R7, R7
|
||||
B loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUB $10, R14, R14
|
||||
CMP R14, R4
|
||||
BGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMP $8, R5
|
||||
BGE fixUpSlowForwardCopy
|
||||
MOVD (R15), R3
|
||||
MOVD R3, (R7)
|
||||
SUB R5, R4, R4
|
||||
ADD R5, R7, R7
|
||||
ADD R5, R5, R5
|
||||
B makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by R7 being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save R7 to R2 so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVD R7, R2
|
||||
ADD R4, R7, R7
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
MOVD $0, R1
|
||||
CMP R1, R4
|
||||
BLE loop
|
||||
MOVD (R15), R3
|
||||
MOVD R3, (R2)
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R2, R2
|
||||
SUB $8, R4, R4
|
||||
B finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), R3
|
||||
MOVB R3, (R7)
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
SUB $1, R4, R4
|
||||
CBNZ R4, verySlowForwardCopy
|
||||
B loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMP R10, R7
|
||||
BNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVD $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVD $1, R2
|
||||
MOVD R2, ret+48(FP)
|
||||
RET
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
// decode has the same semantics as in decode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func decode(dst, src []byte) int
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
// decode writes the decoding of src to dst. It assumes that the varint-encoded
|
||||
// length of the decompressed bytes has already been read, and that len(dst)
|
||||
// equals that length.
|
||||
//
|
||||
// It returns 0 on success or a decodeErrCodeXxx error code on failure.
|
||||
func decode(dst, src []byte) int {
|
||||
var d, s, offset, length int
|
||||
for s < len(src) {
|
||||
switch src[s] & 0x03 {
|
||||
case tagLiteral:
|
||||
x := uint32(src[s] >> 2)
|
||||
switch {
|
||||
case x < 60:
|
||||
s++
|
||||
case x == 60:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-1])
|
||||
case x == 61:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
case x == 62:
|
||||
s += 4
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
case x == 63:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
}
|
||||
length = int(x) + 1
|
||||
if length <= 0 {
|
||||
return decodeErrCodeUnsupportedLiteralLength
|
||||
}
|
||||
if length > len(dst)-d || length > len(src)-s {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
copy(dst[d:], src[s:s+length])
|
||||
d += length
|
||||
s += length
|
||||
continue
|
||||
|
||||
case tagCopy1:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 4 + int(src[s-2])>>2&0x7
|
||||
offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
|
||||
case tagCopy2:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-3])>>2
|
||||
offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
|
||||
case tagCopy4:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-5])>>2
|
||||
offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
}
|
||||
|
||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
// Copy from an earlier sub-slice of dst to a later sub-slice.
|
||||
// If no overlap, use the built-in copy:
|
||||
if offset >= length {
|
||||
copy(dst[d:d+length], dst[d-offset:])
|
||||
d += length
|
||||
continue
|
||||
}
|
||||
|
||||
// Unlike the built-in copy function, this byte-by-byte copy always runs
|
||||
// forwards, even if the slices overlap. Conceptually, this is:
|
||||
//
|
||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||
//
|
||||
// We align the slices into a and b and show the compiler they are the same size.
|
||||
// This allows the loop to run without bounds checks.
|
||||
a := dst[d : d+length]
|
||||
b := dst[d-offset:]
|
||||
b = b[:len(a)]
|
||||
for i := range a {
|
||||
a[i] = b[i]
|
||||
}
|
||||
d += length
|
||||
}
|
||||
if d != len(dst) {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
//
|
||||
// Encode handles the Snappy block format, not the Snappy stream format.
|
||||
func Encode(dst, src []byte) []byte {
|
||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||
panic(ErrTooLarge)
|
||||
} else if len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||
|
||||
for len(src) > 0 {
|
||||
p := src
|
||||
src = nil
|
||||
if len(p) > maxBlockSize {
|
||||
p, src = p[:maxBlockSize], p[maxBlockSize:]
|
||||
}
|
||||
if len(p) < minNonLiteralBlockSize {
|
||||
d += emitLiteral(dst[d:], p)
|
||||
} else {
|
||||
d += encodeBlock(dst[d:], p)
|
||||
}
|
||||
}
|
||||
return dst[:d]
|
||||
}
|
||||
|
||||
// inputMargin is the minimum number of extra input bytes to keep, inside
|
||||
// encodeBlock's inner loop. On some architectures, this margin lets us
|
||||
// implement a fast path for emitLiteral, where the copy of short (<= 16 byte)
|
||||
// literals can be implemented as a single load to and store from a 16-byte
|
||||
// register. That literal's actual length can be as short as 1 byte, so this
|
||||
// can copy up to 15 bytes too much, but that's OK as subsequent iterations of
|
||||
// the encoding loop will fix up the copy overrun, and this inputMargin ensures
|
||||
// that we don't overrun the dst and src buffers.
|
||||
const inputMargin = 16 - 1
|
||||
|
||||
// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that
|
||||
// could be encoded with a copy tag. This is the minimum with respect to the
|
||||
// algorithm used by encodeBlock, not a minimum enforced by the file format.
|
||||
//
|
||||
// The encoded output must start with at least a 1 byte literal, as there are
|
||||
// no previous bytes to copy. A minimal (1 byte) copy after that, generated
|
||||
// from an emitCopy call in encodeBlock's main loop, would require at least
|
||||
// another inputMargin bytes, for the reason above: we want any emitLiteral
|
||||
// calls inside encodeBlock's main loop to use the fast path if possible, which
|
||||
// requires being able to overrun by inputMargin bytes. Thus,
|
||||
// minNonLiteralBlockSize equals 1 + 1 + inputMargin.
|
||||
//
|
||||
// The C++ code doesn't use this exact threshold, but it could, as discussed at
|
||||
// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion
|
||||
// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an
|
||||
// optimization. It should not affect the encoded form. This is tested by
|
||||
// TestSameEncodingAsCppShortCopies.
|
||||
const minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||
|
||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||
// uncompressed length.
|
||||
//
|
||||
// It will return a negative value if srcLen is too large to encode.
|
||||
func MaxEncodedLen(srcLen int) int {
|
||||
n := uint64(srcLen)
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
// Compressed data can be defined as:
|
||||
// compressed := item* literal*
|
||||
// item := literal* copy
|
||||
//
|
||||
// The trailing literal sequence has a space blowup of at most 62/60
|
||||
// since a literal of length 60 needs one tag byte + one extra byte
|
||||
// for length information.
|
||||
//
|
||||
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||
// 4 bytes of data. Because of a special check in the encoding code,
|
||||
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||
// to at most the 62/60 blowup for representing literals.
|
||||
//
|
||||
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||
//
|
||||
// This last factor dominates the blowup, so the final estimate is:
|
||||
n = 32 + n + n/6
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
var errClosed = errors.New("snappy: Writer is closed")
|
||||
|
||||
// NewWriter returns a new Writer that compresses to w.
|
||||
//
|
||||
// The Writer returned does not buffer writes. There is no need to Flush or
|
||||
// Close such a Writer.
|
||||
//
|
||||
// Deprecated: the Writer returned is not suitable for many small writes, only
|
||||
// for few large writes. Use NewBufferedWriter instead, which is efficient
|
||||
// regardless of the frequency and shape of the writes, and remember to Close
|
||||
// that Writer when done.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBufferedWriter returns a new Writer that compresses to w, using the
|
||||
// framing format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
//
|
||||
// The Writer returned buffers writes. Users must call Close to guarantee all
|
||||
// data has been forwarded to the underlying io.Writer. They may also call
|
||||
// Flush zero or more times before calling Close.
|
||||
func NewBufferedWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
ibuf: make([]byte, 0, maxBlockSize),
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
||||
//
|
||||
// Writer handles the Snappy stream format, not the Snappy block format.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
err error
|
||||
|
||||
// ibuf is a buffer for the incoming (uncompressed) bytes.
|
||||
//
|
||||
// Its use is optional. For backwards compatibility, Writers created by the
|
||||
// NewWriter function have ibuf == nil, do not buffer incoming bytes, and
|
||||
// therefore do not need to be Flush'ed or Close'd.
|
||||
ibuf []byte
|
||||
|
||||
// obuf is a buffer for the outgoing (compressed) bytes.
|
||||
obuf []byte
|
||||
|
||||
// wroteStreamHeader is whether we have written the stream header.
|
||||
wroteStreamHeader bool
|
||||
}
|
||||
|
||||
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||
// w. This permits reusing a Writer rather than allocating a new one.
|
||||
func (w *Writer) Reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.err = nil
|
||||
if w.ibuf != nil {
|
||||
w.ibuf = w.ibuf[:0]
|
||||
}
|
||||
w.wroteStreamHeader = false
|
||||
}
|
||||
|
||||
// Write satisfies the io.Writer interface.
|
||||
func (w *Writer) Write(p []byte) (nRet int, errRet error) {
|
||||
if w.ibuf == nil {
|
||||
// Do not buffer incoming bytes. This does not perform or compress well
|
||||
// if the caller of Writer.Write writes many small slices. This
|
||||
// behavior is therefore deprecated, but still supported for backwards
|
||||
// compatibility with code that doesn't explicitly Flush or Close.
|
||||
return w.write(p)
|
||||
}
|
||||
|
||||
// The remainder of this method is based on bufio.Writer.Write from the
|
||||
// standard library.
|
||||
|
||||
for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil {
|
||||
var n int
|
||||
if len(w.ibuf) == 0 {
|
||||
// Large write, empty buffer.
|
||||
// Write directly from p to avoid copy.
|
||||
n, _ = w.write(p)
|
||||
} else {
|
||||
n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
w.Flush()
|
||||
}
|
||||
nRet += n
|
||||
p = p[n:]
|
||||
}
|
||||
if w.err != nil {
|
||||
return nRet, w.err
|
||||
}
|
||||
n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
nRet += n
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
func (w *Writer) write(p []byte) (nRet int, errRet error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for len(p) > 0 {
|
||||
obufStart := len(magicChunk)
|
||||
if !w.wroteStreamHeader {
|
||||
w.wroteStreamHeader = true
|
||||
copy(w.obuf, magicChunk)
|
||||
obufStart = 0
|
||||
}
|
||||
|
||||
var uncompressed []byte
|
||||
if len(p) > maxBlockSize {
|
||||
uncompressed, p = p[:maxBlockSize], p[maxBlockSize:]
|
||||
} else {
|
||||
uncompressed, p = p, nil
|
||||
}
|
||||
checksum := crc(uncompressed)
|
||||
|
||||
// Compress the buffer, discarding the result if the improvement
|
||||
// isn't at least 12.5%.
|
||||
compressed := Encode(w.obuf[obufHeaderLen:], uncompressed)
|
||||
chunkType := uint8(chunkTypeCompressedData)
|
||||
chunkLen := 4 + len(compressed)
|
||||
obufEnd := obufHeaderLen + len(compressed)
|
||||
if len(compressed) >= len(uncompressed)-len(uncompressed)/8 {
|
||||
chunkType = chunkTypeUncompressedData
|
||||
chunkLen = 4 + len(uncompressed)
|
||||
obufEnd = obufHeaderLen
|
||||
}
|
||||
|
||||
// Fill in the per-chunk header that comes before the body.
|
||||
w.obuf[len(magicChunk)+0] = chunkType
|
||||
w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0)
|
||||
w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8)
|
||||
w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16)
|
||||
w.obuf[len(magicChunk)+4] = uint8(checksum >> 0)
|
||||
w.obuf[len(magicChunk)+5] = uint8(checksum >> 8)
|
||||
w.obuf[len(magicChunk)+6] = uint8(checksum >> 16)
|
||||
w.obuf[len(magicChunk)+7] = uint8(checksum >> 24)
|
||||
|
||||
if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
if chunkType == chunkTypeUncompressedData {
|
||||
if _, err := w.w.Write(uncompressed); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
}
|
||||
nRet += len(uncompressed)
|
||||
}
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
// Flush flushes the Writer to its underlying io.Writer.
|
||||
func (w *Writer) Flush() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if len(w.ibuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
w.write(w.ibuf)
|
||||
w.ibuf = w.ibuf[:0]
|
||||
return w.err
|
||||
}
|
||||
|
||||
// Close calls Flush and then closes the Writer.
|
||||
func (w *Writer) Close() error {
|
||||
w.Flush()
|
||||
ret := w.err
|
||||
if w.err == nil {
|
||||
w.err = errClosed
|
||||
}
|
||||
return ret
|
||||
}
|
||||
+730
@@ -0,0 +1,730 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a
|
||||
// Go toolchain regression. See https://github.com/golang/go/issues/15426 and
|
||||
// https://github.com/golang/snappy/issues/29
|
||||
//
|
||||
// As a workaround, the package was built with a known good assembler, and
|
||||
// those instructions were disassembled by "objdump -d" to yield the
|
||||
// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
// style comments, in AT&T asm syntax. Note that rsp here is a physical
|
||||
// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm).
|
||||
// The instructions were then encoded as "BYTE $0x.." sequences, which assemble
|
||||
// fine on Go 1.6.
|
||||
|
||||
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitLiteral(dst, lit []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX len(lit)
|
||||
// - BX n
|
||||
// - DX return value
|
||||
// - DI &dst[i]
|
||||
// - R10 &lit[0]
|
||||
//
|
||||
// The 24 bytes of stack space is to call runtime·memmove.
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R10 for the
|
||||
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||
// which makes it easier to manually inline this function.
|
||||
TEXT ·emitLiteral(SB), NOSPLIT, $24-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ lit_base+24(FP), R10
|
||||
MOVQ lit_len+32(FP), AX
|
||||
MOVQ AX, DX
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT oneByte
|
||||
CMPL BX, $256
|
||||
JLT twoBytes
|
||||
|
||||
threeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
ADDQ $3, DX
|
||||
JMP memmove
|
||||
|
||||
twoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
ADDQ $2, DX
|
||||
JMP memmove
|
||||
|
||||
oneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
ADDQ $1, DX
|
||||
|
||||
memmove:
|
||||
MOVQ DX, ret+48(FP)
|
||||
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitCopy(dst []byte, offset, length int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX length
|
||||
// - SI &dst[0]
|
||||
// - DI &dst[i]
|
||||
// - R11 offset
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R11 for the
|
||||
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ DI, SI
|
||||
MOVQ offset+24(FP), R11
|
||||
MOVQ length+32(FP), AX
|
||||
|
||||
loop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT step1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP loop0
|
||||
|
||||
step1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE step2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
step2:
|
||||
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||
CMPL AX, $12
|
||||
JGE step3
|
||||
CMPL R11, $2048
|
||||
JGE step3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
step3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func extendMatch(src []byte, i, j int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - DX &src[0]
|
||||
// - SI &src[j]
|
||||
// - R13 &src[len(src) - 8]
|
||||
// - R14 &src[len(src)]
|
||||
// - R15 &src[i]
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R15 for a source
|
||||
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||
MOVQ src_base+0(FP), DX
|
||||
MOVQ src_len+8(FP), R14
|
||||
MOVQ i+24(FP), R15
|
||||
MOVQ j+32(FP), SI
|
||||
ADDQ DX, R14
|
||||
ADDQ DX, R15
|
||||
ADDQ DX, SI
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
cmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA cmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE bsf
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP cmp8
|
||||
|
||||
bsf:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
cmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE extendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE extendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP cmp1
|
||||
|
||||
extendMatchEnd:
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func encodeBlock(dst, src []byte) (d int)
|
||||
//
|
||||
// All local variables fit into registers, other than "var table". The register
|
||||
// allocation:
|
||||
// - AX . .
|
||||
// - BX . .
|
||||
// - CX 56 shift (note that amd64 shifts by non-immediates must use CX).
|
||||
// - DX 64 &src[0], tableSize
|
||||
// - SI 72 &src[s]
|
||||
// - DI 80 &dst[d]
|
||||
// - R9 88 sLimit
|
||||
// - R10 . &src[nextEmit]
|
||||
// - R11 96 prevHash, currHash, nextHash, offset
|
||||
// - R12 104 &src[base], skip
|
||||
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||
// - R15 112 candidate
|
||||
//
|
||||
// The second column (56, 64, etc) is the stack offset to spill the registers
|
||||
// when calling other functions. We could pack this slightly tighter, but it's
|
||||
// simpler to have a dedicated spill map independent of the function called.
|
||||
//
|
||||
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||
// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||
// local variables (registers) during calls gives 32768 + 56 + 64 = 32888.
|
||||
TEXT ·encodeBlock(SB), 0, $32888-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), R14
|
||||
|
||||
// shift, tableSize := uint32(32-8), 1<<8
|
||||
MOVQ $24, CX
|
||||
MOVQ $256, DX
|
||||
|
||||
calcShift:
|
||||
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
// shift--
|
||||
// }
|
||||
CMPQ DX, $16384
|
||||
JGE varTable
|
||||
CMPQ DX, R14
|
||||
JGE varTable
|
||||
SUBQ $1, CX
|
||||
SHLQ $1, DX
|
||||
JMP calcShift
|
||||
|
||||
varTable:
|
||||
// var table [maxTableSize]uint16
|
||||
//
|
||||
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||
// first tableSize elements. Each uint16 element is 2 bytes and each MOVOU
|
||||
// writes 16 bytes, so we can do only tableSize/8 writes instead of the
|
||||
// 2048 writes that would zero-initialize all of table's 32768 bytes.
|
||||
SHRQ $3, DX
|
||||
LEAQ table-32768(SP), BX
|
||||
PXOR X0, X0
|
||||
|
||||
memclr:
|
||||
MOVOU X0, 0(BX)
|
||||
ADDQ $16, BX
|
||||
SUBQ $1, DX
|
||||
JNZ memclr
|
||||
|
||||
// !!! DX = &src[0]
|
||||
MOVQ SI, DX
|
||||
|
||||
// sLimit := len(src) - inputMargin
|
||||
MOVQ R14, R9
|
||||
SUBQ $15, R9
|
||||
|
||||
// !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't
|
||||
// change for the rest of the function.
|
||||
MOVQ CX, 56(SP)
|
||||
MOVQ DX, 64(SP)
|
||||
MOVQ R9, 88(SP)
|
||||
|
||||
// nextEmit := 0
|
||||
MOVQ DX, R10
|
||||
|
||||
// s := 1
|
||||
ADDQ $1, SI
|
||||
|
||||
// nextHash := hash(load32(src, s), shift)
|
||||
MOVL 0(SI), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
outer:
|
||||
// for { etc }
|
||||
|
||||
// skip := 32
|
||||
MOVQ $32, R12
|
||||
|
||||
// nextS := s
|
||||
MOVQ SI, R13
|
||||
|
||||
// candidate := 0
|
||||
MOVQ $0, R15
|
||||
|
||||
inner0:
|
||||
// for { etc }
|
||||
|
||||
// s := nextS
|
||||
MOVQ R13, SI
|
||||
|
||||
// bytesBetweenHashLookups := skip >> 5
|
||||
MOVQ R12, R14
|
||||
SHRQ $5, R14
|
||||
|
||||
// nextS = s + bytesBetweenHashLookups
|
||||
ADDQ R14, R13
|
||||
|
||||
// skip += bytesBetweenHashLookups
|
||||
ADDQ R14, R12
|
||||
|
||||
// if nextS > sLimit { goto emitRemainder }
|
||||
MOVQ R13, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JA emitRemainder
|
||||
|
||||
// candidate = int(table[nextHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[nextHash] = uint16(s)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// nextHash = hash(load32(src, nextS), shift)
|
||||
MOVL 0(R13), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||
MOVL 0(SI), AX
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL AX, BX
|
||||
JNE inner0
|
||||
|
||||
fourByteMatch:
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// A 4-byte match has been found. We'll later see etc.
|
||||
|
||||
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||
// on inputMargin in encode.go.
|
||||
MOVQ SI, AX
|
||||
SUBQ R10, AX
|
||||
CMPQ AX, $16
|
||||
JLE emitLiteralFastPath
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitLiteral call.
|
||||
//
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT inlineEmitLiteralOneByte
|
||||
CMPL BX, $256
|
||||
JLT inlineEmitLiteralTwoBytes
|
||||
|
||||
inlineEmitLiteralThreeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralTwoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralOneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
inlineEmitLiteralMemmove:
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
//
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
MOVQ SI, 72(SP)
|
||||
MOVQ DI, 80(SP)
|
||||
MOVQ R15, 112(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
MOVQ 56(SP), CX
|
||||
MOVQ 64(SP), DX
|
||||
MOVQ 72(SP), SI
|
||||
MOVQ 80(SP), DI
|
||||
MOVQ 88(SP), R9
|
||||
MOVQ 112(SP), R15
|
||||
JMP inner1
|
||||
|
||||
inlineEmitLiteralEnd:
|
||||
// End inline of the emitLiteral call.
|
||||
// ----------------------------------------
|
||||
|
||||
emitLiteralFastPath:
|
||||
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||
MOVB AX, BX
|
||||
SUBB $1, BX
|
||||
SHLB $2, BX
|
||||
MOVB BX, (DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||
// (Encode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||
// OK. Subsequent iterations will fix up the overrun.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(R10), X0
|
||||
MOVOU X0, 0(DI)
|
||||
ADDQ AX, DI
|
||||
|
||||
inner1:
|
||||
// for { etc }
|
||||
|
||||
// base := s
|
||||
MOVQ SI, R12
|
||||
|
||||
// !!! offset := base - candidate
|
||||
MOVQ R12, R11
|
||||
SUBQ R15, R11
|
||||
SUBQ DX, R11
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the extendMatch call.
|
||||
//
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
|
||||
// !!! R14 = &src[len(src)]
|
||||
MOVQ src_len+32(FP), R14
|
||||
ADDQ DX, R14
|
||||
|
||||
// !!! R13 = &src[len(src) - 8]
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
// !!! R15 = &src[candidate + 4]
|
||||
ADDQ $4, R15
|
||||
ADDQ DX, R15
|
||||
|
||||
// !!! s += 4
|
||||
ADDQ $4, SI
|
||||
|
||||
inlineExtendMatchCmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA inlineExtendMatchCmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE inlineExtendMatchBSF
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP inlineExtendMatchCmp8
|
||||
|
||||
inlineExtendMatchBSF:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
JMP inlineExtendMatchEnd
|
||||
|
||||
inlineExtendMatchCmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE inlineExtendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE inlineExtendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP inlineExtendMatchCmp1
|
||||
|
||||
inlineExtendMatchEnd:
|
||||
// End inline of the extendMatch call.
|
||||
// ----------------------------------------
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitCopy call.
|
||||
//
|
||||
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
|
||||
// !!! length := s - base
|
||||
MOVQ SI, AX
|
||||
SUBQ R12, AX
|
||||
|
||||
inlineEmitCopyLoop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT inlineEmitCopyStep1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP inlineEmitCopyLoop0
|
||||
|
||||
inlineEmitCopyStep1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE inlineEmitCopyStep2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
inlineEmitCopyStep2:
|
||||
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||
CMPL AX, $12
|
||||
JGE inlineEmitCopyStep3
|
||||
CMPL R11, $2048
|
||||
JGE inlineEmitCopyStep3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitCopyEnd
|
||||
|
||||
inlineEmitCopyStep3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
inlineEmitCopyEnd:
|
||||
// End inline of the emitCopy call.
|
||||
// ----------------------------------------
|
||||
|
||||
// nextEmit = s
|
||||
MOVQ SI, R10
|
||||
|
||||
// if s >= sLimit { goto emitRemainder }
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JAE emitRemainder
|
||||
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// We could immediately etc.
|
||||
|
||||
// x := load64(src, s-1)
|
||||
MOVQ -1(SI), R14
|
||||
|
||||
// prevHash := hash(uint32(x>>0), shift)
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// table[prevHash] = uint16(s-1)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
SUBQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// currHash := hash(uint32(x>>8), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// candidate = int(table[currHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[currHash] = uint16(s)
|
||||
ADDQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL R14, BX
|
||||
JEQ inner1
|
||||
|
||||
// nextHash = hash(uint32(x>>16), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// s++
|
||||
ADDQ $1, SI
|
||||
|
||||
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||
JMP outer
|
||||
|
||||
emitRemainder:
|
||||
// if nextEmit < len(src) { etc }
|
||||
MOVQ src_len+32(FP), AX
|
||||
ADDQ DX, AX
|
||||
CMPQ R10, AX
|
||||
JEQ encodeBlockEnd
|
||||
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
//
|
||||
// Push args.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ R10, 24(SP)
|
||||
SUBQ R10, AX
|
||||
MOVQ AX, 32(SP)
|
||||
MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
MOVQ DI, 80(SP)
|
||||
CALL ·emitLiteral(SB)
|
||||
MOVQ 80(SP), DI
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
ADDQ 48(SP), DI
|
||||
|
||||
encodeBlockEnd:
|
||||
MOVQ dst_base+0(FP), AX
|
||||
SUBQ AX, DI
|
||||
MOVQ DI, d+48(FP)
|
||||
RET
|
||||
+722
@@ -0,0 +1,722 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitLiteral(dst, lit []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R3 len(lit)
|
||||
// - R4 n
|
||||
// - R6 return value
|
||||
// - R8 &dst[i]
|
||||
// - R10 &lit[0]
|
||||
//
|
||||
// The 32 bytes of stack space is to call runtime·memmove.
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R10 for the
|
||||
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||
// which makes it easier to manually inline this function.
|
||||
TEXT ·emitLiteral(SB), NOSPLIT, $32-56
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD lit_base+24(FP), R10
|
||||
MOVD lit_len+32(FP), R3
|
||||
MOVD R3, R6
|
||||
MOVW R3, R4
|
||||
SUBW $1, R4, R4
|
||||
|
||||
CMPW $60, R4
|
||||
BLT oneByte
|
||||
CMPW $256, R4
|
||||
BLT twoBytes
|
||||
|
||||
threeBytes:
|
||||
MOVD $0xf4, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R4, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
ADD $3, R6, R6
|
||||
B memmove
|
||||
|
||||
twoBytes:
|
||||
MOVD $0xf0, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVB R4, 1(R8)
|
||||
ADD $2, R8, R8
|
||||
ADD $2, R6, R6
|
||||
B memmove
|
||||
|
||||
oneByte:
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, 0(R8)
|
||||
ADD $1, R8, R8
|
||||
ADD $1, R6, R6
|
||||
|
||||
memmove:
|
||||
MOVD R6, ret+48(FP)
|
||||
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// R8, R10 and R3 as arguments.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD R10, 16(RSP)
|
||||
MOVD R3, 24(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitCopy(dst []byte, offset, length int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R3 length
|
||||
// - R7 &dst[0]
|
||||
// - R8 &dst[i]
|
||||
// - R11 offset
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R11 for the
|
||||
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD R8, R7
|
||||
MOVD offset+24(FP), R11
|
||||
MOVD length+32(FP), R3
|
||||
|
||||
loop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPW $68, R3
|
||||
BLT step1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVD $0xfe, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUB $64, R3, R3
|
||||
B loop0
|
||||
|
||||
step1:
|
||||
// if length > 64 { etc }
|
||||
CMP $64, R3
|
||||
BLE step2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVD $0xee, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUB $60, R3, R3
|
||||
|
||||
step2:
|
||||
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||
CMP $12, R3
|
||||
BGE step3
|
||||
CMPW $2048, R11
|
||||
BGE step3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(R8)
|
||||
LSRW $3, R11, R11
|
||||
AND $0xe0, R11, R11
|
||||
SUB $4, R3, R3
|
||||
LSLW $2, R3
|
||||
AND $0xff, R3, R3
|
||||
ORRW R3, R11, R11
|
||||
ORRW $1, R11, R11
|
||||
MOVB R11, 0(R8)
|
||||
ADD $2, R8, R8
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUB R7, R8, R8
|
||||
MOVD R8, ret+40(FP)
|
||||
RET
|
||||
|
||||
step3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUB $1, R3, R3
|
||||
AND $0xff, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW $2, R3, R3
|
||||
MOVB R3, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUB R7, R8, R8
|
||||
MOVD R8, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func extendMatch(src []byte, i, j int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R6 &src[0]
|
||||
// - R7 &src[j]
|
||||
// - R13 &src[len(src) - 8]
|
||||
// - R14 &src[len(src)]
|
||||
// - R15 &src[i]
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R15 for a source
|
||||
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||
MOVD src_base+0(FP), R6
|
||||
MOVD src_len+8(FP), R14
|
||||
MOVD i+24(FP), R15
|
||||
MOVD j+32(FP), R7
|
||||
ADD R6, R14, R14
|
||||
ADD R6, R15, R15
|
||||
ADD R6, R7, R7
|
||||
MOVD R14, R13
|
||||
SUB $8, R13, R13
|
||||
|
||||
cmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMP R13, R7
|
||||
BHI cmp1
|
||||
MOVD (R15), R3
|
||||
MOVD (R7), R4
|
||||
CMP R4, R3
|
||||
BNE bsf
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R7, R7
|
||||
B cmp8
|
||||
|
||||
bsf:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs.
|
||||
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||
// combination of which finds the least significant bit which is set.
|
||||
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||
// a bit index to a byte index.
|
||||
EOR R3, R4, R4
|
||||
RBIT R4, R4
|
||||
CLZ R4, R4
|
||||
ADD R4>>3, R7, R7
|
||||
|
||||
// Convert from &src[ret] to ret.
|
||||
SUB R6, R7, R7
|
||||
MOVD R7, ret+40(FP)
|
||||
RET
|
||||
|
||||
cmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMP R7, R14
|
||||
BLS extendMatchEnd
|
||||
MOVB (R15), R3
|
||||
MOVB (R7), R4
|
||||
CMP R4, R3
|
||||
BNE extendMatchEnd
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
B cmp1
|
||||
|
||||
extendMatchEnd:
|
||||
// Convert from &src[ret] to ret.
|
||||
SUB R6, R7, R7
|
||||
MOVD R7, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func encodeBlock(dst, src []byte) (d int)
|
||||
//
|
||||
// All local variables fit into registers, other than "var table". The register
|
||||
// allocation:
|
||||
// - R3 . .
|
||||
// - R4 . .
|
||||
// - R5 64 shift
|
||||
// - R6 72 &src[0], tableSize
|
||||
// - R7 80 &src[s]
|
||||
// - R8 88 &dst[d]
|
||||
// - R9 96 sLimit
|
||||
// - R10 . &src[nextEmit]
|
||||
// - R11 104 prevHash, currHash, nextHash, offset
|
||||
// - R12 112 &src[base], skip
|
||||
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||
// - R15 120 candidate
|
||||
// - R16 . hash constant, 0x1e35a7bd
|
||||
// - R17 . &table
|
||||
// - . 128 table
|
||||
//
|
||||
// The second column (64, 72, etc) is the stack offset to spill the registers
|
||||
// when calling other functions. We could pack this slightly tighter, but it's
|
||||
// simpler to have a dedicated spill map independent of the function called.
|
||||
//
|
||||
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||
// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||
// local variables (registers) during calls gives 32768 + 64 + 64 = 32896.
|
||||
TEXT ·encodeBlock(SB), 0, $32896-56
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD src_base+24(FP), R7
|
||||
MOVD src_len+32(FP), R14
|
||||
|
||||
// shift, tableSize := uint32(32-8), 1<<8
|
||||
MOVD $24, R5
|
||||
MOVD $256, R6
|
||||
MOVW $0xa7bd, R16
|
||||
MOVKW $(0x1e35<<16), R16
|
||||
|
||||
calcShift:
|
||||
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
// shift--
|
||||
// }
|
||||
MOVD $16384, R2
|
||||
CMP R2, R6
|
||||
BGE varTable
|
||||
CMP R14, R6
|
||||
BGE varTable
|
||||
SUB $1, R5, R5
|
||||
LSL $1, R6, R6
|
||||
B calcShift
|
||||
|
||||
varTable:
|
||||
// var table [maxTableSize]uint16
|
||||
//
|
||||
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||
// first tableSize elements. Each uint16 element is 2 bytes and each
|
||||
// iterations writes 64 bytes, so we can do only tableSize/32 writes
|
||||
// instead of the 2048 writes that would zero-initialize all of table's
|
||||
// 32768 bytes. This clear could overrun the first tableSize elements, but
|
||||
// it won't overrun the allocated stack size.
|
||||
ADD $128, RSP, R17
|
||||
MOVD R17, R4
|
||||
|
||||
// !!! R6 = &src[tableSize]
|
||||
ADD R6<<1, R17, R6
|
||||
|
||||
memclr:
|
||||
STP.P (ZR, ZR), 64(R4)
|
||||
STP (ZR, ZR), -48(R4)
|
||||
STP (ZR, ZR), -32(R4)
|
||||
STP (ZR, ZR), -16(R4)
|
||||
CMP R4, R6
|
||||
BHI memclr
|
||||
|
||||
// !!! R6 = &src[0]
|
||||
MOVD R7, R6
|
||||
|
||||
// sLimit := len(src) - inputMargin
|
||||
MOVD R14, R9
|
||||
SUB $15, R9, R9
|
||||
|
||||
// !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't
|
||||
// change for the rest of the function.
|
||||
MOVD R5, 64(RSP)
|
||||
MOVD R6, 72(RSP)
|
||||
MOVD R9, 96(RSP)
|
||||
|
||||
// nextEmit := 0
|
||||
MOVD R6, R10
|
||||
|
||||
// s := 1
|
||||
ADD $1, R7, R7
|
||||
|
||||
// nextHash := hash(load32(src, s), shift)
|
||||
MOVW 0(R7), R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
outer:
|
||||
// for { etc }
|
||||
|
||||
// skip := 32
|
||||
MOVD $32, R12
|
||||
|
||||
// nextS := s
|
||||
MOVD R7, R13
|
||||
|
||||
// candidate := 0
|
||||
MOVD $0, R15
|
||||
|
||||
inner0:
|
||||
// for { etc }
|
||||
|
||||
// s := nextS
|
||||
MOVD R13, R7
|
||||
|
||||
// bytesBetweenHashLookups := skip >> 5
|
||||
MOVD R12, R14
|
||||
LSR $5, R14, R14
|
||||
|
||||
// nextS = s + bytesBetweenHashLookups
|
||||
ADD R14, R13, R13
|
||||
|
||||
// skip += bytesBetweenHashLookups
|
||||
ADD R14, R12, R12
|
||||
|
||||
// if nextS > sLimit { goto emitRemainder }
|
||||
MOVD R13, R3
|
||||
SUB R6, R3, R3
|
||||
CMP R9, R3
|
||||
BHI emitRemainder
|
||||
|
||||
// candidate = int(table[nextHash])
|
||||
MOVHU 0(R17)(R11<<1), R15
|
||||
|
||||
// table[nextHash] = uint16(s)
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
|
||||
MOVH R3, 0(R17)(R11<<1)
|
||||
|
||||
// nextHash = hash(load32(src, nextS), shift)
|
||||
MOVW 0(R13), R11
|
||||
MULW R16, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||
MOVW 0(R7), R3
|
||||
MOVW (R6)(R15), R4
|
||||
CMPW R4, R3
|
||||
BNE inner0
|
||||
|
||||
fourByteMatch:
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// A 4-byte match has been found. We'll later see etc.
|
||||
|
||||
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||
// on inputMargin in encode.go.
|
||||
MOVD R7, R3
|
||||
SUB R10, R3, R3
|
||||
CMP $16, R3
|
||||
BLE emitLiteralFastPath
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitLiteral call.
|
||||
//
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
MOVW R3, R4
|
||||
SUBW $1, R4, R4
|
||||
|
||||
MOVW $60, R2
|
||||
CMPW R2, R4
|
||||
BLT inlineEmitLiteralOneByte
|
||||
MOVW $256, R2
|
||||
CMPW R2, R4
|
||||
BLT inlineEmitLiteralTwoBytes
|
||||
|
||||
inlineEmitLiteralThreeBytes:
|
||||
MOVD $0xf4, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R4, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
B inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralTwoBytes:
|
||||
MOVD $0xf0, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVB R4, 1(R8)
|
||||
ADD $2, R8, R8
|
||||
B inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralOneByte:
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, 0(R8)
|
||||
ADD $1, R8, R8
|
||||
|
||||
inlineEmitLiteralMemmove:
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
//
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// R8, R10 and R3 as arguments.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD R10, 16(RSP)
|
||||
MOVD R3, 24(RSP)
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
ADD R3, R8, R8
|
||||
MOVD R7, 80(RSP)
|
||||
MOVD R8, 88(RSP)
|
||||
MOVD R15, 120(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
MOVD 64(RSP), R5
|
||||
MOVD 72(RSP), R6
|
||||
MOVD 80(RSP), R7
|
||||
MOVD 88(RSP), R8
|
||||
MOVD 96(RSP), R9
|
||||
MOVD 120(RSP), R15
|
||||
ADD $128, RSP, R17
|
||||
MOVW $0xa7bd, R16
|
||||
MOVKW $(0x1e35<<16), R16
|
||||
B inner1
|
||||
|
||||
inlineEmitLiteralEnd:
|
||||
// End inline of the emitLiteral call.
|
||||
// ----------------------------------------
|
||||
|
||||
emitLiteralFastPath:
|
||||
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||
MOVB R3, R4
|
||||
SUBW $1, R4, R4
|
||||
AND $0xff, R4, R4
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, (R8)
|
||||
ADD $1, R8, R8
|
||||
|
||||
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||
// (Encode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||
// OK. Subsequent iterations will fix up the overrun.
|
||||
//
|
||||
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
LDP 0(R10), (R0, R1)
|
||||
STP (R0, R1), 0(R8)
|
||||
ADD R3, R8, R8
|
||||
|
||||
inner1:
|
||||
// for { etc }
|
||||
|
||||
// base := s
|
||||
MOVD R7, R12
|
||||
|
||||
// !!! offset := base - candidate
|
||||
MOVD R12, R11
|
||||
SUB R15, R11, R11
|
||||
SUB R6, R11, R11
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the extendMatch call.
|
||||
//
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
|
||||
// !!! R14 = &src[len(src)]
|
||||
MOVD src_len+32(FP), R14
|
||||
ADD R6, R14, R14
|
||||
|
||||
// !!! R13 = &src[len(src) - 8]
|
||||
MOVD R14, R13
|
||||
SUB $8, R13, R13
|
||||
|
||||
// !!! R15 = &src[candidate + 4]
|
||||
ADD $4, R15, R15
|
||||
ADD R6, R15, R15
|
||||
|
||||
// !!! s += 4
|
||||
ADD $4, R7, R7
|
||||
|
||||
inlineExtendMatchCmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMP R13, R7
|
||||
BHI inlineExtendMatchCmp1
|
||||
MOVD (R15), R3
|
||||
MOVD (R7), R4
|
||||
CMP R4, R3
|
||||
BNE inlineExtendMatchBSF
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R7, R7
|
||||
B inlineExtendMatchCmp8
|
||||
|
||||
inlineExtendMatchBSF:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs.
|
||||
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||
// combination of which finds the least significant bit which is set.
|
||||
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||
// a bit index to a byte index.
|
||||
EOR R3, R4, R4
|
||||
RBIT R4, R4
|
||||
CLZ R4, R4
|
||||
ADD R4>>3, R7, R7
|
||||
B inlineExtendMatchEnd
|
||||
|
||||
inlineExtendMatchCmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMP R7, R14
|
||||
BLS inlineExtendMatchEnd
|
||||
MOVB (R15), R3
|
||||
MOVB (R7), R4
|
||||
CMP R4, R3
|
||||
BNE inlineExtendMatchEnd
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
B inlineExtendMatchCmp1
|
||||
|
||||
inlineExtendMatchEnd:
|
||||
// End inline of the extendMatch call.
|
||||
// ----------------------------------------
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitCopy call.
|
||||
//
|
||||
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
|
||||
// !!! length := s - base
|
||||
MOVD R7, R3
|
||||
SUB R12, R3, R3
|
||||
|
||||
inlineEmitCopyLoop0:
|
||||
// for length >= 68 { etc }
|
||||
MOVW $68, R2
|
||||
CMPW R2, R3
|
||||
BLT inlineEmitCopyStep1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVD $0xfe, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUBW $64, R3, R3
|
||||
B inlineEmitCopyLoop0
|
||||
|
||||
inlineEmitCopyStep1:
|
||||
// if length > 64 { etc }
|
||||
MOVW $64, R2
|
||||
CMPW R2, R3
|
||||
BLE inlineEmitCopyStep2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVD $0xee, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUBW $60, R3, R3
|
||||
|
||||
inlineEmitCopyStep2:
|
||||
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||
MOVW $12, R2
|
||||
CMPW R2, R3
|
||||
BGE inlineEmitCopyStep3
|
||||
MOVW $2048, R2
|
||||
CMPW R2, R11
|
||||
BGE inlineEmitCopyStep3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(R8)
|
||||
LSRW $8, R11, R11
|
||||
LSLW $5, R11, R11
|
||||
SUBW $4, R3, R3
|
||||
AND $0xff, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW R3, R11, R11
|
||||
ORRW $1, R11, R11
|
||||
MOVB R11, 0(R8)
|
||||
ADD $2, R8, R8
|
||||
B inlineEmitCopyEnd
|
||||
|
||||
inlineEmitCopyStep3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBW $1, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW $2, R3, R3
|
||||
MOVB R3, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
|
||||
inlineEmitCopyEnd:
|
||||
// End inline of the emitCopy call.
|
||||
// ----------------------------------------
|
||||
|
||||
// nextEmit = s
|
||||
MOVD R7, R10
|
||||
|
||||
// if s >= sLimit { goto emitRemainder }
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
CMP R3, R9
|
||||
BLS emitRemainder
|
||||
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// We could immediately etc.
|
||||
|
||||
// x := load64(src, s-1)
|
||||
MOVD -1(R7), R14
|
||||
|
||||
// prevHash := hash(uint32(x>>0), shift)
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// table[prevHash] = uint16(s-1)
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
SUB $1, R3, R3
|
||||
|
||||
MOVHU R3, 0(R17)(R11<<1)
|
||||
|
||||
// currHash := hash(uint32(x>>8), shift)
|
||||
LSR $8, R14, R14
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// candidate = int(table[currHash])
|
||||
MOVHU 0(R17)(R11<<1), R15
|
||||
|
||||
// table[currHash] = uint16(s)
|
||||
ADD $1, R3, R3
|
||||
MOVHU R3, 0(R17)(R11<<1)
|
||||
|
||||
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||
MOVW (R6)(R15), R4
|
||||
CMPW R4, R14
|
||||
BEQ inner1
|
||||
|
||||
// nextHash = hash(uint32(x>>16), shift)
|
||||
LSR $8, R14, R14
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// s++
|
||||
ADD $1, R7, R7
|
||||
|
||||
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||
B outer
|
||||
|
||||
emitRemainder:
|
||||
// if nextEmit < len(src) { etc }
|
||||
MOVD src_len+32(FP), R3
|
||||
ADD R6, R3, R3
|
||||
CMP R3, R10
|
||||
BEQ encodeBlockEnd
|
||||
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
//
|
||||
// Push args.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVD R10, 32(RSP)
|
||||
SUB R10, R3, R3
|
||||
MOVD R3, 40(RSP)
|
||||
MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
MOVD R8, 88(RSP)
|
||||
CALL ·emitLiteral(SB)
|
||||
MOVD 88(RSP), R8
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
MOVD 56(RSP), R1
|
||||
ADD R1, R8, R8
|
||||
|
||||
encodeBlockEnd:
|
||||
MOVD dst_base+0(FP), R3
|
||||
SUB R3, R8, R8
|
||||
MOVD R8, d+48(FP)
|
||||
RET
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
// emitLiteral has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitLiteral(dst, lit []byte) int
|
||||
|
||||
// emitCopy has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitCopy(dst []byte, offset, length int) int
|
||||
|
||||
// extendMatch has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func extendMatch(src []byte, i, j int) int
|
||||
|
||||
// encodeBlock has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func encodeBlock(dst, src []byte) (d int)
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
func load32(b []byte, i int) uint32 {
|
||||
b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func load64(b []byte, i int) uint64 {
|
||||
b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
}
|
||||
|
||||
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= len(lit) && len(lit) <= 65536
|
||||
func emitLiteral(dst, lit []byte) int {
|
||||
i, n := 0, uint(len(lit)-1)
|
||||
switch {
|
||||
case n < 60:
|
||||
dst[0] = uint8(n)<<2 | tagLiteral
|
||||
i = 1
|
||||
case n < 1<<8:
|
||||
dst[0] = 60<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
i = 2
|
||||
default:
|
||||
dst[0] = 61<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
i = 3
|
||||
}
|
||||
return i + copy(dst[i:], lit)
|
||||
}
|
||||
|
||||
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= offset && offset <= 65535
|
||||
// 4 <= length && length <= 65535
|
||||
func emitCopy(dst []byte, offset, length int) int {
|
||||
i := 0
|
||||
// The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The
|
||||
// threshold for this loop is a little higher (at 68 = 64 + 4), and the
|
||||
// length emitted down below is is a little lower (at 60 = 64 - 4), because
|
||||
// it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed
|
||||
// by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as
|
||||
// a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as
|
||||
// 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a
|
||||
// tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an
|
||||
// encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1.
|
||||
for length >= 68 {
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 63<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 64
|
||||
}
|
||||
if length > 64 {
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 59<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 60
|
||||
}
|
||||
if length >= 12 || offset >= 2048 {
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
dst[i+0] = uint8(length-1)<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
return i + 3
|
||||
}
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
|
||||
dst[i+1] = uint8(offset)
|
||||
return i + 2
|
||||
}
|
||||
|
||||
// extendMatch returns the largest k such that k <= len(src) and that
|
||||
// src[i:i+k-j] and src[j:k] have the same contents.
|
||||
//
|
||||
// It assumes that:
|
||||
// 0 <= i && i < j && j <= len(src)
|
||||
func extendMatch(src []byte, i, j int) int {
|
||||
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func hash(u, shift uint32) uint32 {
|
||||
return (u * 0x1e35a7bd) >> shift
|
||||
}
|
||||
|
||||
// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It
|
||||
// assumes that the varint-encoded length of the decompressed bytes has already
|
||||
// been written.
|
||||
//
|
||||
// It also assumes that:
|
||||
// len(dst) >= MaxEncodedLen(len(src)) &&
|
||||
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
|
||||
func encodeBlock(dst, src []byte) (d int) {
|
||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||
// The table element type is uint16, as s < sLimit and sLimit < len(src)
|
||||
// and len(src) <= maxBlockSize and maxBlockSize == 65536.
|
||||
const (
|
||||
maxTableSize = 1 << 14
|
||||
// tableMask is redundant, but helps the compiler eliminate bounds
|
||||
// checks.
|
||||
tableMask = maxTableSize - 1
|
||||
)
|
||||
shift := uint32(32 - 8)
|
||||
for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
shift--
|
||||
}
|
||||
// In Go, all array elements are zero-initialized, so there is no advantage
|
||||
// to a smaller tableSize per se. However, it matches the C++ algorithm,
|
||||
// and in the asm versions of this code, we can get away with zeroing only
|
||||
// the first tableSize elements.
|
||||
var table [maxTableSize]uint16
|
||||
|
||||
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||
// looking for copies.
|
||||
sLimit := len(src) - inputMargin
|
||||
|
||||
// nextEmit is where in src the next emitLiteral should start from.
|
||||
nextEmit := 0
|
||||
|
||||
// The encoded form must start with a literal, as there are no previous
|
||||
// bytes to copy, so we start looking for hash matches at s == 1.
|
||||
s := 1
|
||||
nextHash := hash(load32(src, s), shift)
|
||||
|
||||
for {
|
||||
// Copied from the C++ snappy implementation:
|
||||
//
|
||||
// Heuristic match skipping: If 32 bytes are scanned with no matches
|
||||
// found, start looking only at every other byte. If 32 more bytes are
|
||||
// scanned (or skipped), look at every third byte, etc.. When a match
|
||||
// is found, immediately go back to looking at every byte. This is a
|
||||
// small loss (~5% performance, ~0.1% density) for compressible data
|
||||
// due to more bookkeeping, but for non-compressible data (such as
|
||||
// JPEG) it's a huge win since the compressor quickly "realizes" the
|
||||
// data is incompressible and doesn't bother looking for matches
|
||||
// everywhere.
|
||||
//
|
||||
// The "skip" variable keeps track of how many bytes there are since
|
||||
// the last match; dividing it by 32 (ie. right-shifting by five) gives
|
||||
// the number of bytes to move ahead for each iteration.
|
||||
skip := 32
|
||||
|
||||
nextS := s
|
||||
candidate := 0
|
||||
for {
|
||||
s = nextS
|
||||
bytesBetweenHashLookups := skip >> 5
|
||||
nextS = s + bytesBetweenHashLookups
|
||||
skip += bytesBetweenHashLookups
|
||||
if nextS > sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
candidate = int(table[nextHash&tableMask])
|
||||
table[nextHash&tableMask] = uint16(s)
|
||||
nextHash = hash(load32(src, nextS), shift)
|
||||
if load32(src, s) == load32(src, candidate) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||
// them as literal bytes.
|
||||
d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
// Call emitCopy, and then see if another emitCopy could be our next
|
||||
// move. Repeat until we find no match for the input immediately after
|
||||
// what was consumed by the last emitCopy call.
|
||||
//
|
||||
// If we exit this loop normally then we need to call emitLiteral next,
|
||||
// though we don't yet know how big the literal will be. We handle that
|
||||
// by proceeding to the next iteration of the main loop. We also can
|
||||
// exit this loop via goto if we get close to exhausting the input.
|
||||
for {
|
||||
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||
// literal bytes prior to s.
|
||||
base := s
|
||||
|
||||
// Extend the 4-byte match as long as possible.
|
||||
//
|
||||
// This is an inlined version of:
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
s += 4
|
||||
for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 {
|
||||
}
|
||||
|
||||
d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
nextEmit = s
|
||||
if s >= sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
|
||||
// We could immediately start working at s now, but to improve
|
||||
// compression we first update the hash table at s-1 and at s. If
|
||||
// another emitCopy is not our next move, also calculate nextHash
|
||||
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||
// are faster as one load64 call (with some shifts) instead of
|
||||
// three load32 calls.
|
||||
x := load64(src, s-1)
|
||||
prevHash := hash(uint32(x>>0), shift)
|
||||
table[prevHash&tableMask] = uint16(s - 1)
|
||||
currHash := hash(uint32(x>>8), shift)
|
||||
candidate = int(table[currHash&tableMask])
|
||||
table[currHash&tableMask] = uint16(s)
|
||||
if uint32(x>>8) != load32(src, candidate) {
|
||||
nextHash = hash(uint32(x>>16), shift)
|
||||
s++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitRemainder:
|
||||
if nextEmit < len(src) {
|
||||
d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
}
|
||||
return d
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package snappy implements the Snappy compression format. It aims for very
|
||||
// high speeds and reasonable compression.
|
||||
//
|
||||
// There are actually two Snappy formats: block and stream. They are related,
|
||||
// but different: trying to decompress block-compressed data as a Snappy stream
|
||||
// will fail, and vice versa. The block format is the Decode and Encode
|
||||
// functions and the stream format is the Reader and Writer types.
|
||||
//
|
||||
// The block format, the more common case, is used when the complete size (the
|
||||
// number of bytes) of the original data is known upfront, at the time
|
||||
// compression starts. The stream format, also known as the framing format, is
|
||||
// for when that isn't always true.
|
||||
//
|
||||
// The canonical, C++ implementation is at https://github.com/google/snappy and
|
||||
// it only implements the block format.
|
||||
package snappy // import "github.com/golang/snappy"
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
/*
|
||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||
first byte of each chunk is broken into its 2 least and 6 most significant bits
|
||||
called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
|
||||
Zero means a literal tag. All other values mean a copy tag.
|
||||
|
||||
For literal tags:
|
||||
- If m < 60, the next 1 + m bytes are literal bytes.
|
||||
- Otherwise, let n be the little-endian unsigned integer denoted by the next
|
||||
m - 59 bytes. The next 1 + n bytes after that are literal bytes.
|
||||
|
||||
For copy tags, length bytes are copied from offset bytes ago, in the style of
|
||||
Lempel-Ziv compression algorithms. In particular:
|
||||
- For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
|
||||
The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
|
||||
of the offset. The next byte is bits 0-7 of the offset.
|
||||
- For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
|
||||
The length is 1 + m. The offset is the little-endian unsigned integer
|
||||
denoted by the next 2 bytes.
|
||||
- For l == 3, this tag is a legacy format that is no longer issued by most
|
||||
encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in
|
||||
[1, 65). The length is 1 + m. The offset is the little-endian unsigned
|
||||
integer denoted by the next 4 bytes.
|
||||
*/
|
||||
const (
|
||||
tagLiteral = 0x00
|
||||
tagCopy1 = 0x01
|
||||
tagCopy2 = 0x02
|
||||
tagCopy4 = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
checksumSize = 4
|
||||
chunkHeaderSize = 4
|
||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||
magicBody = "sNaPpY"
|
||||
|
||||
// maxBlockSize is the maximum size of the input to encodeBlock. It is not
|
||||
// part of the wire format per se, but some parts of the encoder assume
|
||||
// that an offset fits into a uint16.
|
||||
//
|
||||
// Also, for the framing format (Writer type instead of Encode function),
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||
// that "the uncompressed data in a chunk must be no longer than 65536
|
||||
// bytes".
|
||||
maxBlockSize = 65536
|
||||
|
||||
// maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
|
||||
// hard coded to be a const instead of a variable, so that obufLen can also
|
||||
// be a const. Their equivalence is confirmed by
|
||||
// TestMaxEncodedLenOfMaxBlockSize.
|
||||
maxEncodedLenOfMaxBlockSize = 76490
|
||||
|
||||
obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
|
||||
obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
|
||||
)
|
||||
|
||||
const (
|
||||
chunkTypeCompressedData = 0x00
|
||||
chunkTypeUncompressedData = 0x01
|
||||
chunkTypePadding = 0xfe
|
||||
chunkTypeStreamIdentifier = 0xff
|
||||
)
|
||||
|
||||
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// crc implements the checksum specified in section 3 of
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func crc(b []byte) uint32 {
|
||||
c := crc32.Update(0, crcTable, b)
|
||||
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
#*
|
||||
*~
|
||||
|
||||
# examples binaries
|
||||
examples/synscan/synscan
|
||||
examples/pfdump/pfdump
|
||||
examples/pcapdump/pcapdump
|
||||
examples/httpassembly/httpassembly
|
||||
examples/statsassembly/statsassembly
|
||||
examples/arpscan/arpscan
|
||||
examples/bidirectional/bidirectional
|
||||
examples/bytediff/bytediff
|
||||
examples/reassemblydump/reassemblydump
|
||||
layers/gen
|
||||
macs/gen
|
||||
pcap/pcap_tester
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
if [ -n "$(go fmt ./...)" ]; then
|
||||
echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
|
||||
exit 1
|
||||
fi
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
|
||||
go get golang.org/x/lint/golint
|
||||
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
|
||||
# Add subdirectories here as we clean up golint on each.
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
if golint |
|
||||
grep -v CannotSetRFMon | # pcap exported error name
|
||||
grep -v DataLost | # tcpassembly/tcpreader exported error name
|
||||
grep .; then
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
done
|
||||
|
||||
pushd layers
|
||||
for file in *.go; do
|
||||
if cat .lint_blacklist | grep -q $file; then
|
||||
echo "Skipping lint of $file due to .lint_blacklist"
|
||||
elif golint $file | grep .; then
|
||||
echo "Lint error in file $file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
popd
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
DIRS=". layers pcap pcapgo tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag"
|
||||
set -e
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
go vet
|
||||
popd
|
||||
done
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ev
|
||||
|
||||
go get github.com/google/gopacket
|
||||
go get github.com/google/gopacket/layers
|
||||
go get github.com/google/gopacket/tcpassembly
|
||||
go get github.com/google/gopacket/reassembly
|
||||
go get github.com/google/gopacket/pcapgo
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ev
|
||||
|
||||
go test github.com/google/gopacket
|
||||
go test github.com/google/gopacket/layers
|
||||
go test github.com/google/gopacket/tcpassembly
|
||||
go test github.com/google/gopacket/reassembly
|
||||
go test github.com/google/gopacket/pcapgo
|
||||
go test github.com/google/gopacket/pcap
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- master
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
libpcap-dev
|
||||
|
||||
# use modules except for older versions (see below)
|
||||
install: true
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
script: ./.travis.script.sh
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: master
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- go: 1.5.x
|
||||
install: ./.travis.install.sh
|
||||
- go: 1.6.x
|
||||
install: ./.travis.install.sh
|
||||
- go: 1.7.x
|
||||
install: ./.travis.install.sh
|
||||
- go: 1.8.x
|
||||
install: ./.travis.install.sh
|
||||
- go: 1.9.x
|
||||
install: ./.travis.install.sh
|
||||
- go: 1.10.x
|
||||
install: ./.travis.install.sh
|
||||
- os: osx
|
||||
go: 1.x
|
||||
# windows doesn't work on travis (package installation just hangs and then errors out)
|
||||
# - os: windows
|
||||
# go: 1.x
|
||||
# # We don't need nmap - but that's the only way to get npcap:
|
||||
# before_install: choco install npcap --version 0.86 -y
|
||||
- stage: style
|
||||
name: "fmt/vet/lint"
|
||||
go: 1.x
|
||||
script:
|
||||
- ./.travis.gofmt.sh
|
||||
- ./.travis.govet.sh
|
||||
- ./.travis.golint.sh
|
||||
|
||||
stages:
|
||||
- style
|
||||
- test
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
AUTHORS AND MAINTAINERS:
|
||||
|
||||
MAIN DEVELOPERS:
|
||||
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
|
||||
|
||||
AUTHORS:
|
||||
Nigel Tao <nigeltao@google.com>
|
||||
Cole Mickens <cole.mickens@gmail.com>
|
||||
Ben Daglish <bdaglish@restorepoint.com>
|
||||
Luis Martinez <martinezlc99@gmail.com>
|
||||
Remco Verhoef <remco@dutchcoders.io>
|
||||
Hiroaki Kawai <Hiroaki.Kawai@gmail.com>
|
||||
Lukas Lueg <lukas.lueg@gmail.com>
|
||||
Laurent Hausermann <laurent.hausermann@gmail.com>
|
||||
Bill Green <bgreen@newrelic.com>
|
||||
Christian Mäder <christian.maeder@nine.ch>
|
||||
Gernot Vormayr <gvormayr@gmail.com>
|
||||
Vitor Garcia Graveto <victor.graveto@gmail.com>
|
||||
Elias Chavarria Reyes <elchavar@cisco.com>
|
||||
Daniel Rittweiler <ripx80@protonmail.com>
|
||||
|
||||
CONTRIBUTORS:
|
||||
Attila Oláh <attila@attilaolah.eu>
|
||||
Vittus Mikiassen <matt.miki.vimik@gmail.com>
|
||||
Matthias Radestock <matthias.radestock@gmail.com>
|
||||
Matthew Sackman <matthew@wellquite.org>
|
||||
Loic Prylli <loicp@google.com>
|
||||
Alexandre Fiori <fiorix@gmail.com>
|
||||
Adrian Tam <adrian.c.m.tam@gmail.com>
|
||||
Satoshi Matsumoto <kaorimatz@gmail.com>
|
||||
David Stainton <dstainton415@gmail.com>
|
||||
Jesse Ward <jesse@jesseward.com>
|
||||
Kane Mathers <kane@kanemathers.name>
|
||||
Jose Selvi <jselvi@pentester.es>
|
||||
Yerden Zhumabekov <yerden.zhumabekov@gmail.com>
|
||||
Jensen Hwa <jensenhwa@gmail.com>
|
||||
|
||||
-----------------------------------------------
|
||||
FORKED FROM github.com/akrennmair/gopcap
|
||||
ALL THE FOLLOWING ARE FOR THAT PROJECT
|
||||
|
||||
MAIN DEVELOPERS:
|
||||
Andreas Krennmair <ak@synflood.at>
|
||||
|
||||
CONTRIBUTORS:
|
||||
Andrea Nall <anall@andreanall.com>
|
||||
Daniel Arndt <danielarndt@gmail.com>
|
||||
Dustin Sallings <dustin@spy.net>
|
||||
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
|
||||
Guillaume Savary <guillaume@savary.name>
|
||||
Mark Smith <mark@qq.is>
|
||||
Miek Gieben <miek@miek.nl>
|
||||
Mike Bell <mike@mikebell.org>
|
||||
Trevor Strohman <strohman@google.com>
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
Contributing To gopacket
|
||||
========================
|
||||
|
||||
So you've got some code and you'd like it to be part of gopacket... wonderful!
|
||||
We're happy to accept contributions, whether they're fixes to old protocols, new
|
||||
protocols entirely, or anything else you think would improve the gopacket
|
||||
library. This document is designed to help you to do just that.
|
||||
|
||||
The first section deals with the plumbing: how to actually get a change
|
||||
submitted.
|
||||
|
||||
The second section deals with coding style... Go is great in that it
|
||||
has a uniform style implemented by 'go fmt', but there's still some decisions
|
||||
we've made that go above and beyond, and if you follow them, they won't come up
|
||||
in your code review.
|
||||
|
||||
The third section deals with some of the implementation decisions we've made,
|
||||
which may help you to understand the current code and which we may ask you to
|
||||
conform to (or provide compelling reasons for ignoring).
|
||||
|
||||
Overall, we hope this document will help you to understand our system and write
|
||||
great code which fits in, and help us to turn around on your code review quickly
|
||||
so the code can make it into the master branch as quickly as possible.
|
||||
|
||||
|
||||
How To Submit Code
|
||||
------------------
|
||||
|
||||
We use github.com's Pull Request feature to receive code contributions from
|
||||
external contributors. See
|
||||
https://help.github.com/articles/creating-a-pull-request/ for details on
|
||||
how to create a request.
|
||||
|
||||
Also, there's a local script `gc` in the base directory of GoPacket that
|
||||
runs a local set of checks, which should give you relatively high confidence
|
||||
that your pull won't fail github pull checks.
|
||||
|
||||
```sh
|
||||
go get github.com/google/gopacket
|
||||
cd $GOROOT/src/pkg/github.com/google/gopacket
|
||||
git checkout -b <mynewfeature> # create a new branch to work from
|
||||
... code code code ...
|
||||
./gc # Run this to do local commits, it performs a number of checks
|
||||
```
|
||||
|
||||
To sum up:
|
||||
|
||||
* DO
|
||||
+ Pull down the latest version.
|
||||
+ Make a feature-specific branch.
|
||||
+ Code using the style and methods discussed in the rest of this document.
|
||||
+ Use the ./gc command to do local commits or check correctness.
|
||||
+ Push your new feature branch up to github.com, as a pull request.
|
||||
+ Handle comments and requests from reviewers, pushing new commits up to
|
||||
your feature branch as problems are addressed.
|
||||
+ Put interesting comments and discussions into commit comments.
|
||||
* DON'T
|
||||
+ Push to someone else's branch without their permission.
|
||||
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
* Go code must be run through `go fmt`, `go vet`, and `golint`
|
||||
* Follow http://golang.org/doc/effective_go.html as much as possible.
|
||||
+ In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums
|
||||
should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs.
|
||||
TcpSourcePort or TCP_SOURCE_PORT).
|
||||
* Bonus points for giving enum types a String() field.
|
||||
* Any exported types or functions should have commentary
|
||||
(http://golang.org/doc/effective_go.html#commentary)
|
||||
|
||||
|
||||
Coding Methods And Implementation Notes
|
||||
---------------------------------------
|
||||
|
||||
### Error Handling
|
||||
|
||||
Many times, you'll be decoding a protocol and run across something bad, a packet
|
||||
corruption or the like. How do you handle this? First off, ALWAYS report the
|
||||
error. You can do this either by returning the error from the decode() function
|
||||
(most common), or if you're up for it you can implement and add an ErrorLayer
|
||||
through the packet builder (the first method is a simple shortcut that does
|
||||
exactly this, then stops any future decoding).
|
||||
|
||||
Often, you'll already have decode some part of your protocol by the time you hit
|
||||
your error. Use your own discretion to determine whether the stuff you've
|
||||
already decoded should be returned to the caller or not:
|
||||
|
||||
```go
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
if len(data) < 10 {
|
||||
// This error occurred before we did ANYTHING, so there's nothing in my
|
||||
// protocol that the caller could possibly want. Just return the error.
|
||||
return fmt.Errorf("Length %d less than 10", len(data))
|
||||
}
|
||||
prot.ImportantField1 = data[:5]
|
||||
prot.ImportantField2 = data[5:10]
|
||||
// At this point, we've already got enough information in 'prot' to
|
||||
// warrant returning it to the caller, so we'll add it now.
|
||||
p.AddLayer(prot)
|
||||
if len(data) < 15 {
|
||||
// We encountered an error later in the packet, but the caller already
|
||||
// has the important info we've gleaned so far.
|
||||
return fmt.Errorf("Length %d less than 15", len(data))
|
||||
}
|
||||
prot.ImportantField3 = data[10:15]
|
||||
return nil // We've already added the layer, we can just return success.
|
||||
}
|
||||
```
|
||||
|
||||
In general, our code follows the approach of returning the first error it
|
||||
encounters. In general, we don't trust any bytes after the first error we see.
|
||||
|
||||
### What Is A Layer?
|
||||
|
||||
The definition of a layer is up to the discretion of the coder. It should be
|
||||
something important enough that it's actually useful to the caller (IE: every
|
||||
TLV value should probably NOT be a layer). However, it can be more granular
|
||||
than a single protocol... IPv6 and SCTP both implement many layers to handle the
|
||||
various parts of the protocol. Use your best judgement, and prepare to defend
|
||||
your decisions during code review. ;)
|
||||
|
||||
### Performance
|
||||
|
||||
We strive to make gopacket as fast as possible while still providing lots of
|
||||
features. In general, this means:
|
||||
|
||||
* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize
|
||||
others on an as-needed basis (tons of MPLS on your network? Time to optimize
|
||||
MPLS!)
|
||||
* Use fast operations. See the toplevel benchmark_test for benchmarks of some
|
||||
of Go's underlying features and types.
|
||||
* Test your performance changes! You should use the ./gc script's --benchmark
|
||||
flag to submit any performance-related changes. Use pcap/gopacket_benchmark
|
||||
to test your change against a PCAP file based on your traffic patterns.
|
||||
* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes
|
||||
a huge performance hit, due to the way that Go currently handles its segmented
|
||||
stack... don't be afraid to clean it up anyway. We'll trust the Go compiler
|
||||
to get good enough over time to handle this. Also, this type of
|
||||
compiler-specific optimization is very fragile; someone adding a field to an
|
||||
entirely different struct elsewhere in the codebase could reverse any gains
|
||||
you might achieve by aligning your allocations.
|
||||
* Try to minimize memory allocations. If possible, use []byte to reference
|
||||
pieces of the input, instead of using string, which requires copying the bytes
|
||||
into a new memory allocation.
|
||||
* Think hard about what should be evaluated lazily vs. not. In general, a
|
||||
layer's struct should almost exactly mirror the layer's frame. Anything
|
||||
that's more interesting should be a function. This may not always be
|
||||
possible, but it's a good rule of thumb.
|
||||
* Don't fear micro-optimizations. With the above in mind, we welcome
|
||||
micro-optimizations that we think will have positive/neutral impacts on the
|
||||
majority of workloads. A prime example of this is pre-allocating certain
|
||||
structs within a larger one:
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
// Most packets have 1-4 of VeryCommon, so we preallocate it here.
|
||||
initialAllocation [4]uint32
|
||||
VeryCommon []uint32
|
||||
}
|
||||
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
prot.VeryCommon = proto.initialAllocation[:0]
|
||||
for len(data) > 4 {
|
||||
field := binary.BigEndian.Uint32(data[:4])
|
||||
data = data[4:]
|
||||
// Since we're using the underlying initialAllocation, we won't need to
|
||||
// allocate new memory for the following append unless we more than 16
|
||||
// bytes of data, which should be the uncommon case.
|
||||
prot.VeryCommon = append(prot.VeryCommon, field)
|
||||
}
|
||||
p.AddLayer(prot)
|
||||
if len(data) > 0 {
|
||||
return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Slices And Data
|
||||
|
||||
If you're pulling a slice from the data you're decoding, don't copy it. Just
|
||||
use the slice itself.
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
A, B net.IP
|
||||
}
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
p.AddLayer(&MyProtocol{
|
||||
A: data[:4],
|
||||
B: data[4:8],
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
The caller has already agreed, by using this library, that they won't modify the
|
||||
set of bytes they pass in to the decoder, or the library has already copied the
|
||||
set of bytes to a read-only location. See DecodeOptions.NoCopy for more
|
||||
information.
|
||||
|
||||
### Enums/Types
|
||||
|
||||
If a protocol has an integer field (uint8, uint16, etc) with a couple of known
|
||||
values that mean something special, make it a type. This allows us to do really
|
||||
nice things like adding a String() function to them, so we can more easily
|
||||
display those to users. Check out layers/enums.go for one example, as well as
|
||||
layers/icmp.go for layer-specific enums.
|
||||
|
||||
When naming things, try for descriptiveness over suscinctness. For example,
|
||||
choose DNSResponseRecord over DNSRR.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2012 Google, Inc. All rights reserved.
|
||||
Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Andreas Krennmair, Google, nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# GoPacket
|
||||
|
||||
This library provides packet decoding capabilities for Go.
|
||||
See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
|
||||
|
||||
[](https://travis-ci.org/google/gopacket)
|
||||
[](https://godoc.org/github.com/google/gopacket)
|
||||
|
||||
Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.9 due to x/sys/unix dependencies.
|
||||
|
||||
Originally forked from the gopcap project written by Andreas
|
||||
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Layer represents a single decoded packet layer (using either the
|
||||
// OSI or TCP/IP definition of a layer). When decoding, a packet's data is
|
||||
// broken up into a number of layers. The caller may call LayerType() to
|
||||
// figure out which type of layer they've received from the packet. Optionally,
|
||||
// they may then use a type assertion to get the actual layer type for deep
|
||||
// inspection of the data.
|
||||
type Layer interface {
|
||||
// LayerType is the gopacket type for this layer.
|
||||
LayerType() LayerType
|
||||
// LayerContents returns the set of bytes that make up this layer.
|
||||
LayerContents() []byte
|
||||
// LayerPayload returns the set of bytes contained within this layer, not
|
||||
// including the layer itself.
|
||||
LayerPayload() []byte
|
||||
}
|
||||
|
||||
// Payload is a Layer containing the payload of a packet. The definition of
|
||||
// what constitutes the payload of a packet depends on previous layers; for
|
||||
// TCP and UDP, we stop decoding above layer 4 and return the remaining
|
||||
// bytes as a Payload. Payload is an ApplicationLayer.
|
||||
type Payload []byte
|
||||
|
||||
// LayerType returns LayerTypePayload
|
||||
func (p Payload) LayerType() LayerType { return LayerTypePayload }
|
||||
|
||||
// LayerContents returns the bytes making up this layer.
|
||||
func (p Payload) LayerContents() []byte { return []byte(p) }
|
||||
|
||||
// LayerPayload returns the payload within this layer.
|
||||
func (p Payload) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as bytes.
|
||||
func (p Payload) Payload() []byte { return []byte(p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) }
|
||||
|
||||
// GoString implements fmt.GoStringer.
|
||||
func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p Payload) CanDecode() LayerClass { return LayerTypePayload }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p Payload) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Payload(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodePayload decodes data by returning it all in a Payload layer.
|
||||
func decodePayload(data []byte, p PacketBuilder) error {
|
||||
payload := &Payload{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fragment is a Layer containing a fragment of a larger frame, used by layers
|
||||
// like IPv4 and IPv6 that allow for fragmentation of their payloads.
|
||||
type Fragment []byte
|
||||
|
||||
// LayerType returns LayerTypeFragment
|
||||
func (p *Fragment) LayerType() LayerType { return LayerTypeFragment }
|
||||
|
||||
// LayerContents implements Layer.
|
||||
func (p *Fragment) LayerContents() []byte { return []byte(*p) }
|
||||
|
||||
// LayerPayload implements Layer.
|
||||
func (p *Fragment) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as a byte slice.
|
||||
func (p *Fragment) Payload() []byte { return []byte(*p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Fragment(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(*p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, *p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeFragment decodes data by returning it all in a Fragment layer.
|
||||
func decodeFragment(data []byte, p PacketBuilder) error {
|
||||
payload := &Fragment{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
|
||||
// corresponding OSI layers, as best as possible.
|
||||
|
||||
// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
|
||||
type LinkLayer interface {
|
||||
Layer
|
||||
LinkFlow() Flow
|
||||
}
|
||||
|
||||
// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
|
||||
// layer 3)
|
||||
type NetworkLayer interface {
|
||||
Layer
|
||||
NetworkFlow() Flow
|
||||
}
|
||||
|
||||
// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
|
||||
// layer 4)
|
||||
type TransportLayer interface {
|
||||
Layer
|
||||
TransportFlow() Flow
|
||||
}
|
||||
|
||||
// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
|
||||
// layer 7), also known as the packet payload.
|
||||
type ApplicationLayer interface {
|
||||
Layer
|
||||
Payload() []byte
|
||||
}
|
||||
|
||||
// ErrorLayer is a packet layer created when decoding of the packet has failed.
|
||||
// Its payload is all the bytes that we were unable to decode, and the returned
|
||||
// error details why the decoding failed.
|
||||
type ErrorLayer interface {
|
||||
Layer
|
||||
Error() error
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata.
|
||||
type DecodeFeedback interface {
|
||||
// SetTruncated should be called if during decoding you notice that a packet
|
||||
// is shorter than internal layer variables (HeaderLength, or the like) say it
|
||||
// should be. It sets packet.Metadata().Truncated.
|
||||
SetTruncated()
|
||||
}
|
||||
|
||||
type nilDecodeFeedback struct{}
|
||||
|
||||
func (nilDecodeFeedback) SetTruncated() {}
|
||||
|
||||
// NilDecodeFeedback implements DecodeFeedback by doing nothing.
|
||||
var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{}
|
||||
|
||||
// PacketBuilder is used by layer decoders to store the layers they've decoded,
|
||||
// and to defer future decoding via NextDecoder.
|
||||
// Typically, the pattern for use is:
|
||||
// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error {
|
||||
// if myLayer, err := myDecodingLogic(data); err != nil {
|
||||
// return err
|
||||
// } else {
|
||||
// p.AddLayer(myLayer)
|
||||
// }
|
||||
// // maybe do this, if myLayer is a LinkLayer
|
||||
// p.SetLinkLayer(myLayer)
|
||||
// return p.NextDecoder(nextDecoder)
|
||||
// }
|
||||
type PacketBuilder interface {
|
||||
DecodeFeedback
|
||||
// AddLayer should be called by a decoder immediately upon successful
|
||||
// decoding of a layer.
|
||||
AddLayer(l Layer)
|
||||
// The following functions set the various specific layers in the final
|
||||
// packet. Note that if many layers call SetX, the first call is kept and all
|
||||
// other calls are ignored.
|
||||
SetLinkLayer(LinkLayer)
|
||||
SetNetworkLayer(NetworkLayer)
|
||||
SetTransportLayer(TransportLayer)
|
||||
SetApplicationLayer(ApplicationLayer)
|
||||
SetErrorLayer(ErrorLayer)
|
||||
// NextDecoder should be called by a decoder when they're done decoding a
|
||||
// packet layer but not done with decoding the entire packet. The next
|
||||
// decoder will be called to decode the last AddLayer's LayerPayload.
|
||||
// Because of this, NextDecoder must only be called once all other
|
||||
// PacketBuilder calls have been made. Set*Layer and AddLayer calls after
|
||||
// NextDecoder calls will behave incorrectly.
|
||||
NextDecoder(next Decoder) error
|
||||
// DumpPacketData is used solely for decoding. If you come across an error
|
||||
// you need to diagnose while processing a packet, call this and your packet's
|
||||
// data will be dumped to stderr so you can create a test. This should never
|
||||
// be called from a production decoder.
|
||||
DumpPacketData()
|
||||
// DecodeOptions returns the decode options
|
||||
DecodeOptions() *DecodeOptions
|
||||
}
|
||||
|
||||
// Decoder is an interface for logic to decode a packet layer. Users may
|
||||
// implement a Decoder to handle their own strange packet types, or may use one
|
||||
// of the many decoders available in the 'layers' subpackage to decode things
|
||||
// for them.
|
||||
type Decoder interface {
|
||||
// Decode decodes the bytes of a packet, sending decoded values and other
|
||||
// information to PacketBuilder, and returning an error if unsuccessful. See
|
||||
// the PacketBuilder documentation for more details.
|
||||
Decode([]byte, PacketBuilder) error
|
||||
}
|
||||
|
||||
// DecodeFunc wraps a function to make it a Decoder.
|
||||
type DecodeFunc func([]byte, PacketBuilder) error
|
||||
|
||||
// Decode implements Decoder by calling itself.
|
||||
func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error {
|
||||
// function, call thyself.
|
||||
return d(data, p)
|
||||
}
|
||||
|
||||
// DecodePayload is a Decoder that returns a Payload layer containing all
|
||||
// remaining bytes.
|
||||
var DecodePayload Decoder = DecodeFunc(decodePayload)
|
||||
|
||||
// DecodeUnknown is a Decoder that returns an Unknown layer containing all
|
||||
// remaining bytes, useful if you run up against a layer that you're unable to
|
||||
// decode yet. This layer is considered an ErrorLayer.
|
||||
var DecodeUnknown Decoder = DecodeFunc(decodeUnknown)
|
||||
|
||||
// DecodeFragment is a Decoder that returns a Fragment layer containing all
|
||||
// remaining bytes.
|
||||
var DecodeFragment Decoder = DecodeFunc(decodeFragment)
|
||||
|
||||
// LayerTypeZero is an invalid layer type, but can be used to determine whether
|
||||
// layer type has actually been set correctly.
|
||||
var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown})
|
||||
|
||||
// LayerTypeDecodeFailure is the layer type for the default error layer.
|
||||
var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown})
|
||||
|
||||
// LayerTypePayload is the layer type for a payload that we don't try to decode
|
||||
// but treat as a success, IE: an application-level payload.
|
||||
var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload})
|
||||
|
||||
// LayerTypeFragment is the layer type for a fragment of a layer transported
|
||||
// by an underlying layer that supports fragmentation.
|
||||
var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment})
|
||||
|
||||
// DecodeFailure is a packet layer created if decoding of the packet data failed
|
||||
// for some reason. It implements ErrorLayer. LayerContents will be the entire
|
||||
// set of bytes that failed to parse, and Error will return the reason parsing
|
||||
// failed.
|
||||
type DecodeFailure struct {
|
||||
data []byte
|
||||
err error
|
||||
stack []byte
|
||||
}
|
||||
|
||||
// Error returns the error encountered during decoding.
|
||||
func (d *DecodeFailure) Error() error { return d.err }
|
||||
|
||||
// LayerContents implements Layer.
|
||||
func (d *DecodeFailure) LayerContents() []byte { return d.data }
|
||||
|
||||
// LayerPayload implements Layer.
|
||||
func (d *DecodeFailure) LayerPayload() []byte { return nil }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d *DecodeFailure) String() string {
|
||||
return "Packet decoding error: " + d.Error().Error()
|
||||
}
|
||||
|
||||
// Dump implements Dumper.
|
||||
func (d *DecodeFailure) Dump() (s string) {
|
||||
if d.stack != nil {
|
||||
s = string(d.stack)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeDecodeFailure
|
||||
func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure }
|
||||
|
||||
// decodeUnknown "decodes" unsupported data types by returning an error.
|
||||
// This decoder will thus always return a DecodeFailure layer.
|
||||
func decodeUnknown(data []byte, p PacketBuilder) error {
|
||||
return errors.New("Layer type not currently supported")
|
||||
}
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
/*
|
||||
Package gopacket provides packet decoding for the Go language.
|
||||
|
||||
gopacket contains many sub-packages with additional functionality you may find
|
||||
useful, including:
|
||||
|
||||
* layers: You'll probably use this every time. This contains of the logic
|
||||
built into gopacket for decoding packet protocols. Note that all example
|
||||
code below assumes that you have imported both gopacket and
|
||||
gopacket/layers.
|
||||
* pcap: C bindings to use libpcap to read packets off the wire.
|
||||
* pfring: C bindings to use PF_RING to read packets off the wire.
|
||||
* afpacket: C bindings for Linux's AF_PACKET to read packets off the wire.
|
||||
* tcpassembly: TCP stream reassembly
|
||||
|
||||
Also, if you're looking to dive right into code, see the examples subdirectory
|
||||
for numerous simple binaries built using gopacket libraries.
|
||||
|
||||
Minimum go version required is 1.5 except for pcapgo/EthernetHandle, afpacket,
|
||||
and bsdbpf which need at least 1.7 due to x/sys/unix dependencies.
|
||||
|
||||
Basic Usage
|
||||
|
||||
gopacket takes in packet data as a []byte and decodes it into a packet with
|
||||
a non-zero number of "layers". Each layer corresponds to a protocol
|
||||
within the bytes. Once a packet has been decoded, the layers of the packet
|
||||
can be requested from the packet.
|
||||
|
||||
// Decode a packet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
|
||||
// Get the TCP layer from this packet
|
||||
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
||||
fmt.Println("This is a TCP packet!")
|
||||
// Get actual TCP data from this layer
|
||||
tcp, _ := tcpLayer.(*layers.TCP)
|
||||
fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort)
|
||||
}
|
||||
// Iterate over all layers, printing out each layer type
|
||||
for _, layer := range packet.Layers() {
|
||||
fmt.Println("PACKET LAYER:", layer.LayerType())
|
||||
}
|
||||
|
||||
Packets can be decoded from a number of starting points. Many of our base
|
||||
types implement Decoder, which allow us to decode packets for which
|
||||
we don't have full data.
|
||||
|
||||
// Decode an ethernet packet
|
||||
ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default)
|
||||
// Decode an IPv6 header and everything it contains
|
||||
ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default)
|
||||
// Decode a TCP header and its payload
|
||||
tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default)
|
||||
|
||||
|
||||
Reading Packets From A Source
|
||||
|
||||
Most of the time, you won't just have a []byte of packet data lying around.
|
||||
Instead, you'll want to read packets in from somewhere (file, interface, etc)
|
||||
and process them. To do that, you'll want to build a PacketSource.
|
||||
|
||||
First, you'll need to construct an object that implements the PacketDataSource
|
||||
interface. There are implementations of this interface bundled with gopacket
|
||||
in the gopacket/pcap and gopacket/pfring subpackages... see their documentation
|
||||
for more information on their usage. Once you have a PacketDataSource, you can
|
||||
pass it into NewPacketSource, along with a Decoder of your choice, to create
|
||||
a PacketSource.
|
||||
|
||||
Once you have a PacketSource, you can read packets from it in multiple ways.
|
||||
See the docs for PacketSource for more details. The easiest method is the
|
||||
Packets function, which returns a channel, then asynchronously writes new
|
||||
packets into that channel, closing the channel if the packetSource hits an
|
||||
end-of-file.
|
||||
|
||||
packetSource := ... // construct using pcap or pfring
|
||||
for packet := range packetSource.Packets() {
|
||||
handlePacket(packet) // do something with each packet
|
||||
}
|
||||
|
||||
You can change the decoding options of the packetSource by setting fields in
|
||||
packetSource.DecodeOptions... see the following sections for more details.
|
||||
|
||||
|
||||
Lazy Decoding
|
||||
|
||||
gopacket optionally decodes packet data lazily, meaning it
|
||||
only decodes a packet layer when it needs to handle a function call.
|
||||
|
||||
// Create a packet, but don't actually decode anything yet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
// Now, decode the packet up to the first IPv4 layer found but no further.
|
||||
// If no IPv4 layer was found, the whole packet will be decoded looking for
|
||||
// it.
|
||||
ip4 := packet.Layer(layers.LayerTypeIPv4)
|
||||
// Decode all layers and return them. The layers up to the first IPv4 layer
|
||||
// are already decoded, and will not require decoding a second time.
|
||||
layers := packet.Layers()
|
||||
|
||||
Lazily-decoded packets are not concurrency-safe. Since layers have not all been
|
||||
decoded, each call to Layer() or Layers() has the potential to mutate the packet
|
||||
in order to decode the next layer. If a packet is used
|
||||
in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket
|
||||
will decode the packet fully, and all future function calls won't mutate the
|
||||
object.
|
||||
|
||||
|
||||
NoCopy Decoding
|
||||
|
||||
By default, gopacket will copy the slice passed to NewPacket and store the
|
||||
copy within the packet, so future mutations to the bytes underlying the slice
|
||||
don't affect the packet and its layers. If you can guarantee that the
|
||||
underlying slice bytes won't be changed, you can use NoCopy to tell
|
||||
gopacket.NewPacket, and it'll use the passed-in slice itself.
|
||||
|
||||
// This channel returns new byte slices, each of which points to a new
|
||||
// memory location that's guaranteed immutable for the duration of the
|
||||
// packet.
|
||||
for data := range myByteSliceChannel {
|
||||
p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
|
||||
doSomethingWithPacket(p)
|
||||
}
|
||||
|
||||
The fastest method of decoding is to use both Lazy and NoCopy, but note from
|
||||
the many caveats above that for some implementations either or both may be
|
||||
dangerous.
|
||||
|
||||
|
||||
Pointers To Known Layers
|
||||
|
||||
During decoding, certain layers are stored in the packet as well-known
|
||||
layer types. For example, IPv4 and IPv6 are both considered NetworkLayer
|
||||
layers, while TCP and UDP are both TransportLayer layers. We support 4
|
||||
layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly
|
||||
anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these,
|
||||
you can use the packet.LinkLayer, packet.NetworkLayer,
|
||||
packet.TransportLayer, and packet.ApplicationLayer functions. Each of
|
||||
these functions returns a corresponding interface
|
||||
(gopacket.{Link,Network,Transport,Application}Layer). The first three
|
||||
provide methods for getting src/dst addresses for that particular layer,
|
||||
while the final layer provides a Payload function to get payload data.
|
||||
This is helpful, for example, to get payloads for all packets regardless
|
||||
of their underlying data type:
|
||||
|
||||
// Get packets from some source
|
||||
for packet := range someSource {
|
||||
if app := packet.ApplicationLayer(); app != nil {
|
||||
if strings.Contains(string(app.Payload()), "magic string") {
|
||||
fmt.Println("Found magic string in a packet!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A particularly useful layer is ErrorLayer, which is set whenever there's
|
||||
an error parsing part of the packet.
|
||||
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
|
||||
if err := packet.ErrorLayer(); err != nil {
|
||||
fmt.Println("Error decoding some part of the packet:", err)
|
||||
}
|
||||
|
||||
Note that we don't return an error from NewPacket because we may have decoded
|
||||
a number of layers successfully before running into our erroneous layer. You
|
||||
may still be able to get your Ethernet and IPv4 layers correctly, even if
|
||||
your TCP layer is malformed.
|
||||
|
||||
|
||||
Flow And Endpoint
|
||||
|
||||
gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol
|
||||
independent manner the fact that a packet is coming from A and going to B.
|
||||
The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide
|
||||
methods for extracting their flow information, without worrying about the type
|
||||
of the underlying Layer.
|
||||
|
||||
A Flow is a simple object made up of a set of two Endpoints, one source and one
|
||||
destination. It details the sender and receiver of the Layer of the Packet.
|
||||
|
||||
An Endpoint is a hashable representation of a source or destination. For
|
||||
example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4
|
||||
IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined
|
||||
into Flows:
|
||||
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
netFlow := packet.NetworkLayer().NetworkFlow()
|
||||
src, dst := netFlow.Endpoints()
|
||||
reverseFlow := gopacket.NewFlow(dst, src)
|
||||
|
||||
Both Endpoint and Flow objects can be used as map keys, and the equality
|
||||
operator can compare them, so you can easily group together all packets
|
||||
based on endpoint criteria:
|
||||
|
||||
flows := map[gopacket.Endpoint]chan gopacket.Packet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
// Send all TCP packets to channels based on their destination port.
|
||||
if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil {
|
||||
flows[tcp.TransportFlow().Dst()] <- packet
|
||||
}
|
||||
// Look for all packets with the same source and destination network address
|
||||
if net := packet.NetworkLayer(); net != nil {
|
||||
src, dst := net.NetworkFlow().Endpoints()
|
||||
if src == dst {
|
||||
fmt.Println("Fishy packet has same network source and dst: %s", src)
|
||||
}
|
||||
}
|
||||
// Find all packets coming from UDP port 1000 to UDP port 500
|
||||
interestingFlow := gopacket.FlowFromEndpoints(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
|
||||
if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow {
|
||||
fmt.Println("Found that UDP flow I was looking for!")
|
||||
}
|
||||
|
||||
For load-balancing purposes, both Flow and Endpoint have FastHash() functions,
|
||||
which provide quick, non-cryptographic hashes of their contents. Of particular
|
||||
importance is the fact that Flow FastHash() is symmetric: A->B will have the same
|
||||
hash as B->A. An example usage could be:
|
||||
|
||||
channels := [8]chan gopacket.Packet
|
||||
for i := 0; i < 8; i++ {
|
||||
channels[i] = make(chan gopacket.Packet)
|
||||
go packetHandler(channels[i])
|
||||
}
|
||||
for packet := range getPackets() {
|
||||
if net := packet.NetworkLayer(); net != nil {
|
||||
channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet
|
||||
}
|
||||
}
|
||||
|
||||
This allows us to split up a packet stream while still making sure that each
|
||||
stream sees all packets for a flow (and its bidirectional opposite).
|
||||
|
||||
|
||||
Implementing Your Own Decoder
|
||||
|
||||
If your network has some strange encapsulation, you can implement your own
|
||||
decoder. In this example, we handle Ethernet packets which are encapsulated
|
||||
in a 4-byte header.
|
||||
|
||||
// Create a layer type, should be unique and high, so it doesn't conflict,
|
||||
// giving it a name and a decoder to use.
|
||||
var MyLayerType = gopacket.RegisterLayerType(12345, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)})
|
||||
|
||||
// Implement my layer
|
||||
type MyLayer struct {
|
||||
StrangeHeader []byte
|
||||
payload []byte
|
||||
}
|
||||
func (m MyLayer) LayerType() gopacket.LayerType { return MyLayerType }
|
||||
func (m MyLayer) LayerContents() []byte { return m.StrangeHeader }
|
||||
func (m MyLayer) LayerPayload() []byte { return m.payload }
|
||||
|
||||
// Now implement a decoder... this one strips off the first 4 bytes of the
|
||||
// packet.
|
||||
func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error {
|
||||
// Create my layer
|
||||
p.AddLayer(&MyLayer{data[:4], data[4:]})
|
||||
// Determine how to handle the rest of the packet
|
||||
return p.NextDecoder(layers.LayerTypeEthernet)
|
||||
}
|
||||
|
||||
// Finally, decode your packets:
|
||||
p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy)
|
||||
|
||||
See the docs for Decoder and PacketBuilder for more details on how coding
|
||||
decoders works, or look at RegisterLayerType and RegisterEndpointType to see how
|
||||
to add layer/endpoint types to gopacket.
|
||||
|
||||
|
||||
Fast Decoding With DecodingLayerParser
|
||||
|
||||
TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode
|
||||
packet data, but only for known packet stacks.
|
||||
|
||||
Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow
|
||||
due to its need to allocate a new packet and every respective layer. It's very
|
||||
versatile and can handle all known layer types, but sometimes you really only
|
||||
care about a specific set of layers regardless, so that versatility is wasted.
|
||||
|
||||
DecodingLayerParser avoids memory allocation altogether by decoding packet
|
||||
layers directly into preallocated objects, which you can then reference to get
|
||||
the packet's information. A quick example:
|
||||
|
||||
func main() {
|
||||
var eth layers.Ethernet
|
||||
var ip4 layers.IPv4
|
||||
var ip6 layers.IPv6
|
||||
var tcp layers.TCP
|
||||
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp)
|
||||
decoded := []gopacket.LayerType{}
|
||||
for packetData := range somehowGetPacketData() {
|
||||
if err := parser.DecodeLayers(packetData, &decoded); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
|
||||
continue
|
||||
}
|
||||
for _, layerType := range decoded {
|
||||
switch layerType {
|
||||
case layers.LayerTypeIPv6:
|
||||
fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP)
|
||||
case layers.LayerTypeIPv4:
|
||||
fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The important thing to note here is that the parser is modifying the passed in
|
||||
layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly
|
||||
speeding up the decoding process. It's even branching based on layer type...
|
||||
it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't
|
||||
handle any other type... since no other decoders were passed in, an (eth, ip4,
|
||||
udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet,
|
||||
LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't
|
||||
decode a UDP packet).
|
||||
|
||||
Unfortunately, not all layers can be used by DecodingLayerParser... only those
|
||||
implementing the DecodingLayer interface are usable. Also, it's possible to
|
||||
create DecodingLayers that are not themselves Layers... see
|
||||
layers.IPv6ExtensionSkipper for an example of this.
|
||||
|
||||
Faster And Customized Decoding with DecodingLayerContainer
|
||||
|
||||
By default, DecodingLayerParser uses native map to store and search for a layer
|
||||
to decode. Though being versatile, in some cases this solution may be not so
|
||||
optimal. For example, if you have only few layers faster operations may be
|
||||
provided by sparse array indexing or linear array scan.
|
||||
|
||||
To accomodate these scenarios, DecodingLayerContainer interface is introduced
|
||||
along with its implementations: DecodingLayerSparse, DecodingLayerArray and
|
||||
DecodingLayerMap. You can specify a container implementation to
|
||||
DecodingLayerParser with SetDecodingLayerContainer method. Example:
|
||||
|
||||
dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet)
|
||||
dlp.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
|
||||
var eth layers.Ethernet
|
||||
dlp.AddDecodingLayer(ð)
|
||||
// ... add layers and use DecodingLayerParser as usual...
|
||||
|
||||
To skip one level of indirection (though sacrificing some capabilities) you may
|
||||
also use DecodingLayerContainer as a decoding tool as it is. In this case you have to
|
||||
handle unknown layer types and layer panics by yourself. Example:
|
||||
|
||||
func main() {
|
||||
var eth layers.Ethernet
|
||||
var ip4 layers.IPv4
|
||||
var ip6 layers.IPv6
|
||||
var tcp layers.TCP
|
||||
dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
|
||||
dlc = dlc.Put(ð)
|
||||
dlc = dlc.Put(&ip4)
|
||||
dlc = dlc.Put(&ip6)
|
||||
dlc = dlc.Put(&tcp)
|
||||
// you may specify some meaningful DecodeFeedback
|
||||
decoder := dlc.LayersDecoder(LayerTypeEthernet, gopacket.NilDecodeFeedback)
|
||||
decoded := make([]gopacket.LayerType, 0, 20)
|
||||
for packetData := range somehowGetPacketData() {
|
||||
lt, err := decoder(packetData, &decoded)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if lt != gopacket.LayerTypeZero {
|
||||
fmt.Fprintf(os.Stderr, "unknown layer type: %v\n", lt)
|
||||
continue
|
||||
}
|
||||
for _, layerType := range decoded {
|
||||
// examine decoded layertypes just as already shown above
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecodingLayerSparse is the fastest but most effective when LayerType values
|
||||
that layers in use can decode are not large because otherwise that would lead
|
||||
to bigger memory footprint. DecodingLayerArray is very compact and primarily
|
||||
usable if the number of decoding layers is not big (up to ~10-15, but please do
|
||||
your own benchmarks). DecodingLayerMap is the most versatile one and used by
|
||||
DecodingLayerParser by default. Please refer to tests and benchmarks in layers
|
||||
subpackage to further examine usage examples and performance measurements.
|
||||
|
||||
You may also choose to implement your own DecodingLayerContainer if you want to
|
||||
make use of your own internal packet decoding logic.
|
||||
|
||||
Creating Packet Data
|
||||
|
||||
As well as offering the ability to decode packet data, gopacket will allow you
|
||||
to create packets from scratch, as well. A number of gopacket layers implement
|
||||
the SerializableLayer interface; these layers can be serialized to a []byte in
|
||||
the following manner:
|
||||
|
||||
ip := &layers.IPv4{
|
||||
SrcIP: net.IP{1, 2, 3, 4},
|
||||
DstIP: net.IP{5, 6, 7, 8},
|
||||
// etc...
|
||||
}
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{} // See SerializeOptions for more details.
|
||||
err := ip.SerializeTo(buf, opts)
|
||||
if err != nil { panic(err) }
|
||||
fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer.
|
||||
|
||||
SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat
|
||||
the current buffer's Bytes() slice as the payload of the serializing layer.
|
||||
Therefore, you can serialize an entire packet by serializing a set of layers in
|
||||
reverse order (Payload, then TCP, then IP, then Ethernet, for example). The
|
||||
SerializeBuffer's SerializeLayers function is a helper that does exactly that.
|
||||
|
||||
To generate a (empty and useless, because no fields are set)
|
||||
Ethernet(IPv4(TCP(Payload))) packet, for example, you can run:
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{}
|
||||
gopacket.SerializeLayers(buf, opts,
|
||||
&layers.Ethernet{},
|
||||
&layers.IPv4{},
|
||||
&layers.TCP{},
|
||||
gopacket.Payload([]byte{1, 2, 3, 4}))
|
||||
packetData := buf.Bytes()
|
||||
|
||||
A Final Note
|
||||
|
||||
If you use gopacket, you'll almost definitely want to make sure gopacket/layers
|
||||
is imported, since when imported it sets all the LayerType variables and fills
|
||||
in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore,
|
||||
it's recommended that even if you don't use any layers functions directly, you still import with:
|
||||
|
||||
import (
|
||||
_ "github.com/google/gopacket/layers"
|
||||
)
|
||||
*/
|
||||
package gopacket
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
|
||||
//
|
||||
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
|
||||
// can't use a byte slice. The two obvious choices are to use a string or a
|
||||
// byte array. Strings work great, but string creation requires memory
|
||||
// allocation, which can be slow. Arrays work great, but have a fixed size. We
|
||||
// originally used the former, now we've switched to the latter. Use of a fixed
|
||||
// byte-array doubles the speed of constructing a flow (due to not needing to
|
||||
// allocate). This is a huge increase... too much for us to pass up.
|
||||
//
|
||||
// The end result of this, though, is that an endpoint/flow can't be created
|
||||
// using more than MaxEndpointSize bytes per address.
|
||||
const MaxEndpointSize = 16
|
||||
|
||||
// Endpoint is the set of bytes used to address packets at various layers.
|
||||
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
|
||||
// Endpoints are usable as map keys.
|
||||
type Endpoint struct {
|
||||
typ EndpointType
|
||||
len int
|
||||
raw [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// EndpointType returns the endpoint type associated with this endpoint.
|
||||
func (a Endpoint) EndpointType() EndpointType { return a.typ }
|
||||
|
||||
// Raw returns the raw bytes of this endpoint. These aren't human-readable
|
||||
// most of the time, but they are faster than calling String.
|
||||
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
|
||||
|
||||
// LessThan provides a stable ordering for all endpoints. It sorts first based
|
||||
// on the EndpointType of an endpoint, then based on the raw bytes of that
|
||||
// endpoint.
|
||||
//
|
||||
// For some endpoints, the actual comparison may not make sense, however this
|
||||
// ordering does provide useful information for most Endpoint types.
|
||||
// Ordering is based first on endpoint type, then on raw endpoint bytes.
|
||||
// Endpoint bytes are sorted lexicographically.
|
||||
func (a Endpoint) LessThan(b Endpoint) bool {
|
||||
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
|
||||
}
|
||||
|
||||
// fnvHash is used by our FastHash functions, and implements the FNV hash
|
||||
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
|
||||
// See http://isthe.com/chongo/tech/comp/fnv/.
|
||||
func fnvHash(s []byte) (h uint64) {
|
||||
h = fnvBasis
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint64(s[i])
|
||||
h *= fnvPrime
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const fnvBasis = 14695981039346656037
|
||||
const fnvPrime = 1099511628211
|
||||
|
||||
// FastHash provides a quick hashing function for an endpoint, useful if you'd
|
||||
// like to split up endpoints by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (a Endpoint) FastHash() (h uint64) {
|
||||
h = fnvHash(a.raw[:a.len])
|
||||
h ^= uint64(a.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new Endpoint object.
|
||||
//
|
||||
// The size of raw must be less than MaxEndpointSize, otherwise this function
|
||||
// will panic.
|
||||
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
|
||||
e.len = len(raw)
|
||||
if e.len > MaxEndpointSize {
|
||||
panic("raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
e.typ = typ
|
||||
copy(e.raw[:], raw)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointTypeMetadata is used to register a new endpoint type.
|
||||
type EndpointTypeMetadata struct {
|
||||
// Name is the string returned by an EndpointType's String function.
|
||||
Name string
|
||||
// Formatter is called from an Endpoint's String function to format the raw
|
||||
// bytes in an Endpoint into a human-readable string.
|
||||
Formatter func([]byte) string
|
||||
}
|
||||
|
||||
// EndpointType is the type of a gopacket Endpoint. This type determines how
|
||||
// the bytes stored in the endpoint should be interpreted.
|
||||
type EndpointType int64
|
||||
|
||||
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
|
||||
|
||||
// RegisterEndpointType creates a new EndpointType and registers it globally.
|
||||
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
|
||||
// reserved for gopacket's use.
|
||||
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
|
||||
t := EndpointType(num)
|
||||
if _, ok := endpointTypes[t]; ok {
|
||||
panic("Endpoint type number already in use")
|
||||
}
|
||||
endpointTypes[t] = meta
|
||||
return t
|
||||
}
|
||||
|
||||
func (e EndpointType) String() string {
|
||||
if t, ok := endpointTypes[e]; ok {
|
||||
return t.Name
|
||||
}
|
||||
return strconv.Itoa(int(e))
|
||||
}
|
||||
|
||||
func (a Endpoint) String() string {
|
||||
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
|
||||
return t.Formatter(a.raw[:a.len])
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", a.typ, a.raw)
|
||||
}
|
||||
|
||||
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
|
||||
// Flows are usable as map keys.
|
||||
type Flow struct {
|
||||
typ EndpointType
|
||||
slen, dlen int
|
||||
src, dst [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
|
||||
// The endpoints must have the same EndpointType, or this function will return
|
||||
// an error.
|
||||
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
|
||||
if src.typ != dst.typ {
|
||||
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
|
||||
return
|
||||
}
|
||||
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
|
||||
}
|
||||
|
||||
// FastHash provides a quick hashing function for a flow, useful if you'd
|
||||
// like to split up flows by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
|
||||
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
|
||||
// B->A.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (f Flow) FastHash() (h uint64) {
|
||||
// This combination must be commutative. We don't use ^, since that would
|
||||
// give the same hash for all A->A flows.
|
||||
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
|
||||
h ^= uint64(f.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of this flow, in the form
|
||||
// "Src->Dst"
|
||||
func (f Flow) String() string {
|
||||
s, d := f.Endpoints()
|
||||
return fmt.Sprintf("%v->%v", s, d)
|
||||
}
|
||||
|
||||
// EndpointType returns the EndpointType for this Flow.
|
||||
func (f Flow) EndpointType() EndpointType {
|
||||
return f.typ
|
||||
}
|
||||
|
||||
// Endpoints returns the two Endpoints for this flow.
|
||||
func (f Flow) Endpoints() (src, dst Endpoint) {
|
||||
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
|
||||
}
|
||||
|
||||
// Src returns the source Endpoint for this flow.
|
||||
func (f Flow) Src() (src Endpoint) {
|
||||
src, _ = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Dst returns the destination Endpoint for this flow.
|
||||
func (f Flow) Dst() (dst Endpoint) {
|
||||
_, dst = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Reverse returns a new flow with endpoints reversed.
|
||||
func (f Flow) Reverse() Flow {
|
||||
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
|
||||
}
|
||||
|
||||
// NewFlow creates a new flow.
|
||||
//
|
||||
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
|
||||
// panic.
|
||||
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
|
||||
f.slen = len(src)
|
||||
f.dlen = len(dst)
|
||||
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
|
||||
panic("flow raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
f.typ = t
|
||||
copy(f.src[:], src)
|
||||
copy(f.dst[:], dst)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
|
||||
// that are specified incorrectly during creation.
|
||||
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
|
||||
return fmt.Sprintf("%v", b)
|
||||
}})
|
||||
|
||||
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
|
||||
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
|
||||
|
||||
// InvalidFlow is a singleton Flow of type EndpointInvalid.
|
||||
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2012 Google, Inc. All rights reserved.
|
||||
|
||||
# This script provides a simple way to run benchmarks against previous code and
|
||||
# keep a log of how benchmarks change over time. When used with the --benchmark
|
||||
# flag, it runs benchmarks from the current code and from the last commit run
|
||||
# with --benchmark, then stores the results in the git commit description. We
|
||||
# rerun the old benchmarks along with the new ones, since there's no guarantee
|
||||
# that git commits will happen on the same machine, so machine differences could
|
||||
# cause wildly inaccurate results.
|
||||
#
|
||||
# If you're making changes to 'gopacket' which could cause performance changes,
|
||||
# you may be requested to use this commit script to make sure your changes don't
|
||||
# have large detrimental effects (or to show off how awesome your performance
|
||||
# improvements are).
|
||||
#
|
||||
# If not run with the --benchmark flag, this script is still very useful... it
|
||||
# makes sure all the correct go formatting, building, and testing work as
|
||||
# expected.
|
||||
|
||||
function Usage {
|
||||
cat <<EOF
|
||||
USAGE: $0 [--benchmark regexp] [--root] [--gen] <git commit flags...>
|
||||
|
||||
--benchmark: Run benchmark comparisons against last benchmark'd commit
|
||||
--root: Run tests that require root priviledges
|
||||
--gen: Generate code for MACs/ports by pulling down external data
|
||||
|
||||
Note, some 'git commit' flags are necessary, if all else fails, pass in -a
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
BENCH=""
|
||||
GEN=""
|
||||
ROOT=""
|
||||
while [ ! -z "$1" ]; do
|
||||
case "$1" in
|
||||
"--benchmark")
|
||||
BENCH="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
"--gen")
|
||||
GEN="yes"
|
||||
shift
|
||||
;;
|
||||
"--root")
|
||||
ROOT="yes"
|
||||
shift
|
||||
;;
|
||||
"--help")
|
||||
Usage
|
||||
;;
|
||||
"-h")
|
||||
Usage
|
||||
;;
|
||||
"help")
|
||||
Usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
function Root {
|
||||
if [ ! -z "$ROOT" ]; then
|
||||
local exec="$1"
|
||||
# Some folks (like me) keep source code in places inaccessible by root (like
|
||||
# NFS), so to make sure things run smoothly we copy them to a /tmp location.
|
||||
local tmpfile="$(mktemp -t gopacket_XXXXXXXX)"
|
||||
echo "Running root test executable $exec as $tmpfile"
|
||||
cp "$exec" "$tmpfile"
|
||||
chmod a+x "$tmpfile"
|
||||
shift
|
||||
sudo "$tmpfile" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -eq "0" ]; then
|
||||
Usage
|
||||
fi
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
# Check for copyright notices.
|
||||
for filename in $(find ./ -type f -name '*.go'); do
|
||||
if ! head -n 1 "$filename" | grep -q Copyright; then
|
||||
echo "File '$filename' may not have copyright notice"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ ! -z "$ROOT" ]; then
|
||||
echo "Running SUDO to get root priviledges for root tests"
|
||||
sudo echo "have root"
|
||||
fi
|
||||
|
||||
if [ ! -z "$GEN" ]; then
|
||||
pushd macs
|
||||
go run gen.go | gofmt > valid_mac_prefixes.go
|
||||
popd
|
||||
pushd layers
|
||||
go run gen.go | gofmt > iana_ports.go
|
||||
go run gen2.go | gofmt > enums_generated.go
|
||||
popd
|
||||
fi
|
||||
|
||||
# Make sure everything is formatted, compiles, and tests pass.
|
||||
go fmt ./...
|
||||
go test -i ./... 2>/dev/null >/dev/null || true
|
||||
go test
|
||||
go build
|
||||
pushd examples/bytediff
|
||||
go build
|
||||
popd
|
||||
if [ -f /usr/include/pcap.h ]; then
|
||||
pushd pcap
|
||||
go test ./...
|
||||
go build ./...
|
||||
go build pcap_tester.go
|
||||
Root pcap_tester --mode=basic
|
||||
Root pcap_tester --mode=filtered
|
||||
Root pcap_tester --mode=timestamp || echo "You might not support timestamp sources"
|
||||
popd
|
||||
pushd examples/afpacket
|
||||
go build
|
||||
popd
|
||||
pushd examples/pcapdump
|
||||
go build
|
||||
popd
|
||||
pushd examples/arpscan
|
||||
go build
|
||||
popd
|
||||
pushd examples/bidirectional
|
||||
go build
|
||||
popd
|
||||
pushd examples/synscan
|
||||
go build
|
||||
popd
|
||||
pushd examples/httpassembly
|
||||
go build
|
||||
popd
|
||||
pushd examples/statsassembly
|
||||
go build
|
||||
popd
|
||||
fi
|
||||
pushd macs
|
||||
go test ./...
|
||||
gofmt -w gen.go
|
||||
go build gen.go
|
||||
popd
|
||||
pushd tcpassembly
|
||||
go test ./...
|
||||
popd
|
||||
pushd reassembly
|
||||
go test ./...
|
||||
popd
|
||||
pushd layers
|
||||
gofmt -w gen.go
|
||||
go build gen.go
|
||||
go test ./...
|
||||
popd
|
||||
pushd pcapgo
|
||||
go test ./...
|
||||
go build ./...
|
||||
popd
|
||||
if [ -f /usr/include/linux/if_packet.h ]; then
|
||||
if grep -q TPACKET_V3 /usr/include/linux/if_packet.h; then
|
||||
pushd afpacket
|
||||
go build ./...
|
||||
go test ./...
|
||||
popd
|
||||
fi
|
||||
fi
|
||||
if [ -f /usr/include/pfring.h ]; then
|
||||
pushd pfring
|
||||
go test ./...
|
||||
go build ./...
|
||||
popd
|
||||
pushd examples/pfdump
|
||||
go build
|
||||
popd
|
||||
fi
|
||||
pushd ip4defrag
|
||||
go test ./...
|
||||
popd
|
||||
pushd defrag
|
||||
go test ./...
|
||||
popd
|
||||
|
||||
for travis_script in `ls .travis.*.sh`; do
|
||||
./$travis_script
|
||||
done
|
||||
|
||||
# Run our initial commit
|
||||
git commit "$@"
|
||||
|
||||
if [ -z "$BENCH" ]; then
|
||||
set +x
|
||||
echo "We're not benchmarking and we've committed... we're done!"
|
||||
exit
|
||||
fi
|
||||
|
||||
### If we get here, we want to run benchmarks from current commit, and compare
|
||||
### then to benchmarks from the last --benchmark commit.
|
||||
|
||||
# Get our current branch.
|
||||
BRANCH="$(git branch | grep '^*' | awk '{print $2}')"
|
||||
|
||||
# File we're going to build our commit description in.
|
||||
COMMIT_FILE="$(mktemp /tmp/tmp.XXXXXXXX)"
|
||||
|
||||
# Add the word "BENCH" to the start of the git commit.
|
||||
echo -n "BENCH " > $COMMIT_FILE
|
||||
|
||||
# Get the current description... there must be an easier way.
|
||||
git log -n 1 | grep '^ ' | sed 's/^ //' >> $COMMIT_FILE
|
||||
|
||||
# Get the commit sha for the last benchmark commit
|
||||
PREV=$(git log -n 1 --grep='BENCHMARK_MARKER_DO_NOT_CHANGE' | head -n 1 | awk '{print $2}')
|
||||
|
||||
## Run current benchmarks
|
||||
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
----------------------------------------------------------
|
||||
BENCHMARK_MARKER_DO_NOT_CHANGE
|
||||
----------------------------------------------------------
|
||||
|
||||
Go version $(go version)
|
||||
|
||||
|
||||
TEST BENCHMARKS "$BENCH"
|
||||
EOF
|
||||
# go seems to have trouble with 'go test --bench=. ./...'
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
pushd layers
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
popd
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
PCAP BENCHMARK
|
||||
EOF
|
||||
if [ "$BENCH" -eq ".*" ]; then
|
||||
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
|
||||
fi
|
||||
|
||||
|
||||
|
||||
## Reset to last benchmark commit, run benchmarks
|
||||
|
||||
git checkout $PREV
|
||||
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
----------------------------------------------------------
|
||||
BENCHMARKING AGAINST COMMIT $PREV
|
||||
----------------------------------------------------------
|
||||
|
||||
|
||||
OLD TEST BENCHMARKS
|
||||
EOF
|
||||
# go seems to have trouble with 'go test --bench=. ./...'
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
pushd layers
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
popd
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
OLD PCAP BENCHMARK
|
||||
EOF
|
||||
if [ "$BENCH" -eq ".*" ]; then
|
||||
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
|
||||
fi
|
||||
|
||||
|
||||
|
||||
## Reset back to the most recent commit, edit the commit message by appending
|
||||
## benchmark results.
|
||||
git checkout $BRANCH
|
||||
git commit --amend -F $COMMIT_FILE
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
// LayerClass is a set of LayerTypes, used for grabbing one of a number of
|
||||
// different types from a packet.
|
||||
type LayerClass interface {
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
Contains(LayerType) bool
|
||||
// LayerTypes returns the set of all layer types in this layer class.
|
||||
// Note that this may not be a fast operation on all LayerClass
|
||||
// implementations.
|
||||
LayerTypes() []LayerType
|
||||
}
|
||||
|
||||
// Contains implements LayerClass.
|
||||
func (l LayerType) Contains(a LayerType) bool {
|
||||
return l == a
|
||||
}
|
||||
|
||||
// LayerTypes implements LayerClass.
|
||||
func (l LayerType) LayerTypes() []LayerType {
|
||||
return []LayerType{l}
|
||||
}
|
||||
|
||||
// LayerClassSlice implements a LayerClass with a slice.
|
||||
type LayerClassSlice []bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (s LayerClassSlice) Contains(t LayerType) bool {
|
||||
return int(t) < len(s) && s[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassSlice.
|
||||
// Because of LayerClassSlice's implementation, this could be quite slow.
|
||||
func (s LayerClassSlice) LayerTypes() (all []LayerType) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] {
|
||||
all = append(all, LayerType(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of
|
||||
// size max(types) and setting slice[t] to true for each type t. Note, if
|
||||
// you implement your own LayerType and give it a high value, this WILL create
|
||||
// a very large slice.
|
||||
func NewLayerClassSlice(types []LayerType) LayerClassSlice {
|
||||
var max LayerType
|
||||
for _, typ := range types {
|
||||
if typ > max {
|
||||
max = typ
|
||||
}
|
||||
}
|
||||
t := make([]bool, int(max+1))
|
||||
for _, typ := range types {
|
||||
t[typ] = true
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// LayerClassMap implements a LayerClass with a map.
|
||||
type LayerClassMap map[LayerType]bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (m LayerClassMap) Contains(t LayerType) bool {
|
||||
return m[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassMap.
|
||||
func (m LayerClassMap) LayerTypes() (all []LayerType) {
|
||||
for t := range m {
|
||||
all = append(all, t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each
|
||||
// type in types.
|
||||
func NewLayerClassMap(types []LayerType) LayerClassMap {
|
||||
m := LayerClassMap{}
|
||||
for _, typ := range types {
|
||||
m[typ] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewLayerClass creates a LayerClass, attempting to be smart about which type
|
||||
// it creates based on which types are passed in.
|
||||
func NewLayerClass(types []LayerType) LayerClass {
|
||||
for _, typ := range types {
|
||||
if typ > maxLayerType {
|
||||
// NewLayerClassSlice could create a very large object, so instead create
|
||||
// a map.
|
||||
return NewLayerClassMap(types)
|
||||
}
|
||||
}
|
||||
return NewLayerClassSlice(types)
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
dot11.go
|
||||
eap.go
|
||||
endpoints.go
|
||||
enums_generated.go
|
||||
enums.go
|
||||
ethernet.go
|
||||
geneve.go
|
||||
icmp4.go
|
||||
icmp6.go
|
||||
igmp.go
|
||||
ip4.go
|
||||
ip6.go
|
||||
layertypes.go
|
||||
linux_sll.go
|
||||
llc.go
|
||||
lldp.go
|
||||
mpls.go
|
||||
ndp.go
|
||||
ntp.go
|
||||
ospf.go
|
||||
pflog.go
|
||||
pppoe.go
|
||||
prism.go
|
||||
radiotap.go
|
||||
rudp.go
|
||||
sctp.go
|
||||
sflow.go
|
||||
tcp.go
|
||||
tcpip.go
|
||||
tls.go
|
||||
tls_alert.go
|
||||
tls_appdata.go
|
||||
tls_cipherspec.go
|
||||
tls_hanshake.go
|
||||
tls_test.go
|
||||
udp.go
|
||||
udplite.go
|
||||
usb.go
|
||||
vrrp.go
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Potential values for ARP.Operation.
|
||||
const (
|
||||
ARPRequest = 1
|
||||
ARPReply = 2
|
||||
)
|
||||
|
||||
// ARP is a ARP packet header.
|
||||
type ARP struct {
|
||||
BaseLayer
|
||||
AddrType LinkType
|
||||
Protocol EthernetType
|
||||
HwAddressSize uint8
|
||||
ProtAddressSize uint8
|
||||
Operation uint16
|
||||
SourceHwAddress []byte
|
||||
SourceProtAddress []byte
|
||||
DstHwAddress []byte
|
||||
DstProtAddress []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeARP
|
||||
func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("ARP length %d too short", len(data))
|
||||
}
|
||||
arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
|
||||
arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
arp.HwAddressSize = data[4]
|
||||
arp.ProtAddressSize = data[5]
|
||||
arp.Operation = binary.BigEndian.Uint16(data[6:8])
|
||||
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
|
||||
if len(data) < int(arpLength) {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength)
|
||||
}
|
||||
arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
|
||||
arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
|
||||
|
||||
arp.Contents = data[:arpLength]
|
||||
arp.Payload = data[arpLength:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.FixLengths {
|
||||
if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
|
||||
return errors.New("mismatched hardware address sizes")
|
||||
}
|
||||
arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
|
||||
if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
|
||||
return errors.New("mismatched prot address sizes")
|
||||
}
|
||||
arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
|
||||
bytes[4] = arp.HwAddressSize
|
||||
bytes[5] = arp.ProtAddressSize
|
||||
binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
|
||||
start := 8
|
||||
for _, addr := range [][]byte{
|
||||
arp.SourceHwAddress,
|
||||
arp.SourceProtAddress,
|
||||
arp.DstHwAddress,
|
||||
arp.DstProtAddress,
|
||||
} {
|
||||
copy(bytes[start:], addr)
|
||||
start += len(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (arp *ARP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeARP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (arp *ARP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeARP(data []byte, p gopacket.PacketBuilder) error {
|
||||
|
||||
arp := &ARP{}
|
||||
return decodingLayerDecoder(arp, data, p)
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file in the root of the source tree.
|
||||
|
||||
package layers
|
||||
|
||||
// This file implements the ASF RMCP payload specified in section 3.2.2.3 of
|
||||
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
// ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP.
|
||||
ASFRMCPEnterprise uint32 = 4542
|
||||
)
|
||||
|
||||
// ASFDataIdentifier encapsulates fields used to uniquely identify the format of
|
||||
// the data block.
|
||||
//
|
||||
// While the enterprise number is almost always 4542 (ASF-RMCP), we support
|
||||
// registering layers using structs of this type as a key in case any users are
|
||||
// using OEM-extensions.
|
||||
type ASFDataIdentifier struct {
|
||||
|
||||
// Enterprise is the IANA Enterprise Number associated with the entity that
|
||||
// defines the message type. A list can be found at
|
||||
// https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
|
||||
// This can be thought of as the namespace for the message type.
|
||||
Enterprise uint32
|
||||
|
||||
// Type is the message type, defined by the entity associated with the
|
||||
// enterprise above. No pressure, but in the context of EN 4542, 1 byte is
|
||||
// the difference between sending a ping and telling a machine to do an
|
||||
// unconditional power down (0x80 and 0x12 respectively).
|
||||
Type uint8
|
||||
}
|
||||
|
||||
// LayerType returns the payload layer type corresponding to an ASF message
|
||||
// type.
|
||||
func (a ASFDataIdentifier) LayerType() gopacket.LayerType {
|
||||
if lt := asfDataLayerTypes[a]; lt != 0 {
|
||||
return lt
|
||||
}
|
||||
|
||||
// some layer types don't have a payload, e.g. ASF-RMCP Presence Ping.
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// RegisterASFLayerType allows specifying that the data block of ASF packets
|
||||
// with a given enterprise number and type should be processed by a given layer
|
||||
// type. This overrides any existing registrations, including defaults.
|
||||
func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) {
|
||||
asfDataLayerTypes[a] = l
|
||||
}
|
||||
|
||||
var (
|
||||
// ASFDataIdentifierPresencePong is the message type of the response to a
|
||||
// Presence Ping message. It indicates the sender is ASF-RMCP-aware.
|
||||
ASFDataIdentifierPresencePong = ASFDataIdentifier{
|
||||
Enterprise: ASFRMCPEnterprise,
|
||||
Type: 0x40,
|
||||
}
|
||||
|
||||
// ASFDataIdentifierPresencePing is a message type sent to a managed client
|
||||
// to solicit a Presence Pong response. Clients may ignore this if the RMCP
|
||||
// version is unsupported. Sending this message with a sequence number <255
|
||||
// is the recommended way of finding out whether an implementation sends
|
||||
// RMCP ACKs (e.g. iDRAC does, Super Micro does not).
|
||||
//
|
||||
// Systems implementing IPMI must respond to this ping to conform to the
|
||||
// spec, so it is a good substitute for an ICMP ping.
|
||||
ASFDataIdentifierPresencePing = ASFDataIdentifier{
|
||||
Enterprise: ASFRMCPEnterprise,
|
||||
Type: 0x80,
|
||||
}
|
||||
|
||||
// asfDataLayerTypes is used to find the next layer for a given ASF header.
|
||||
asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{
|
||||
ASFDataIdentifierPresencePong: LayerTypeASFPresencePong,
|
||||
}
|
||||
)
|
||||
|
||||
// ASF defines ASF's generic RMCP message Data block format. See section
|
||||
// 3.2.2.3.
|
||||
type ASF struct {
|
||||
BaseLayer
|
||||
ASFDataIdentifier
|
||||
|
||||
// Tag is used to match request/response pairs. The tag of a response is set
|
||||
// to that of the message it is responding to. If a message is
|
||||
// unidirectional, i.e. not part of a request/response pair, this is set to
|
||||
// 255.
|
||||
Tag uint8
|
||||
|
||||
// 1 byte reserved, set to 0x00.
|
||||
|
||||
// Length is the length of this layer's payload in bytes.
|
||||
Length uint8
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeASF. It partially satisfies Layer and
|
||||
// SerializableLayer.
|
||||
func (*ASF) LayerType() gopacket.LayerType {
|
||||
return LayerTypeASF
|
||||
}
|
||||
|
||||
// CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer.
|
||||
func (a *ASF) CanDecode() gopacket.LayerClass {
|
||||
return a.LayerType()
|
||||
}
|
||||
|
||||
// DecodeFromBytes makes the layer represent the provided bytes. It partially
|
||||
// satisfies DecodingLayer.
|
||||
func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("invalid ASF data header, length %v less than 8",
|
||||
len(data))
|
||||
}
|
||||
|
||||
a.BaseLayer.Contents = data[:8]
|
||||
a.BaseLayer.Payload = data[8:]
|
||||
|
||||
a.Enterprise = binary.BigEndian.Uint32(data[:4])
|
||||
a.Type = uint8(data[4])
|
||||
a.Tag = uint8(data[5])
|
||||
// 1 byte reserved
|
||||
a.Length = uint8(data[7])
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type corresponding to the message type of
|
||||
// this ASF data layer. This partially satisfies DecodingLayer.
|
||||
func (a *ASF) NextLayerType() gopacket.LayerType {
|
||||
return a.ASFDataIdentifier.LayerType()
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
|
||||
// partially satisfying SerializableLayer.
|
||||
func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
payload := b.Bytes()
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
|
||||
bytes[4] = uint8(a.Type)
|
||||
bytes[5] = a.Tag
|
||||
bytes[6] = 0x00
|
||||
if opts.FixLengths {
|
||||
a.Length = uint8(len(payload))
|
||||
}
|
||||
bytes[7] = a.Length
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeASF decodes the byte slice into an RMCP-ASF data struct.
|
||||
func decodeASF(data []byte, p gopacket.PacketBuilder) error {
|
||||
return decodingLayerDecoder(&ASF{}, data, p)
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file in the root of the source tree.
|
||||
|
||||
package layers
|
||||
|
||||
// This file implements the RMCP ASF Presence Pong message, specified in section
|
||||
// 3.2.4.3 of
|
||||
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf. It
|
||||
// also contains non-competing elements from IPMI v2.0, specified in section
|
||||
// 13.2.4 of
|
||||
// https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf.
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type (
|
||||
// ASFEntity is the type of individual entities that a Presence Pong
|
||||
// response can indicate support of. The entities currently implemented by
|
||||
// the spec are IPMI and ASFv1.
|
||||
ASFEntity uint8
|
||||
|
||||
// ASFInteraction is the type of individual interactions that a Presence
|
||||
// Pong response can indicate support for. The interactions currently
|
||||
// implemented by the spec are RMCP security extensions. Although not
|
||||
// specified, IPMI uses this field to indicate support for DASH, which is
|
||||
// supported as well.
|
||||
ASFInteraction uint8
|
||||
)
|
||||
|
||||
const (
|
||||
// ASFDCMIEnterprise is the IANA-assigned Enterprise Number of the Data
|
||||
// Center Manageability Interface Forum. The Presence Pong response's
|
||||
// Enterprise field being set to this value indicates support for DCMI. The
|
||||
// DCMI spec regards the OEM field as reserved, so these should be null.
|
||||
ASFDCMIEnterprise uint32 = 36465
|
||||
|
||||
// ASFPresencePongEntityIPMI ANDs with Presence Pong's supported entities
|
||||
// field if the managed system supports IPMI.
|
||||
ASFPresencePongEntityIPMI ASFEntity = 1 << 7
|
||||
|
||||
// ASFPresencePongEntityASFv1 ANDs with Presence Pong's supported entities
|
||||
// field if the managed system supports ASF v1.0.
|
||||
ASFPresencePongEntityASFv1 ASFEntity = 1
|
||||
|
||||
// ASFPresencePongInteractionSecurityExtensions ANDs with Presence Pong's
|
||||
// supported interactions field if the managed system supports RMCP v2.0
|
||||
// security extensions. See section 3.2.3.
|
||||
ASFPresencePongInteractionSecurityExtensions ASFInteraction = 1 << 7
|
||||
|
||||
// ASFPresencePongInteractionDASH ANDs with Presence Pong's supported
|
||||
// interactions field if the managed system supports DMTF DASH. See
|
||||
// https://www.dmtf.org/standards/dash.
|
||||
ASFPresencePongInteractionDASH ASFInteraction = 1 << 5
|
||||
)
|
||||
|
||||
// ASFPresencePong defines the structure of a Presence Pong message's payload.
|
||||
// See section 3.2.4.3.
|
||||
type ASFPresencePong struct {
|
||||
BaseLayer
|
||||
|
||||
// Enterprise is the IANA Enterprise Number of an entity that has defined
|
||||
// OEM-specific capabilities for the managed client. If no such capabilities
|
||||
// exist, this is set to ASF's IANA Enterprise Number.
|
||||
Enterprise uint32
|
||||
|
||||
// OEM identifies OEM-specific capabilities. Its structure is defined by the
|
||||
// OEM. This is set to 0s if no OEM-specific capabilities exist. This
|
||||
// implementation does not change byte order from the wire for this field.
|
||||
OEM [4]byte
|
||||
|
||||
// We break out entities and interactions into separate booleans as
|
||||
// discovery is the entire point of this type of message, so we assume they
|
||||
// are accessed. It also makes gopacket's default layer printing more
|
||||
// useful.
|
||||
|
||||
// IPMI is true if IPMI is supported by the managed system. There is no
|
||||
// explicit version in the specification, however given the dates, this is
|
||||
// assumed to be IPMI v1.0. Support for IPMI is contained in the "supported
|
||||
// entities" field of the presence pong payload.
|
||||
IPMI bool
|
||||
|
||||
// ASFv1 indicates support for ASF v1.0. This seems somewhat redundant as
|
||||
// ASF must be supported in order to receive a response. This is contained
|
||||
// in the "supported entities" field of the presence pong payload.
|
||||
ASFv1 bool
|
||||
|
||||
// SecurityExtensions indicates support for RMCP Security Extensions,
|
||||
// specified in ASF v2.0. This will always be false for v1.x
|
||||
// implementations. This is contained in the "supported interactions" field
|
||||
// of the presence pong payload. This field is defined in ASF v1.0, but has
|
||||
// no useful value.
|
||||
SecurityExtensions bool
|
||||
|
||||
// DASH is true if DMTF DASH is supported. This is not specified in ASF
|
||||
// v2.0, but in IPMI v2.0, however the former does not preclude it, so we
|
||||
// support it.
|
||||
DASH bool
|
||||
|
||||
// 6 bytes reserved after the entities and interactions fields, set to 0s.
|
||||
}
|
||||
|
||||
// SupportsDCMI returns whether the Presence Pong message indicates support for
|
||||
// the Data Center Management Interface, which is an extension of IPMI v2.0.
|
||||
func (a *ASFPresencePong) SupportsDCMI() bool {
|
||||
return a.Enterprise == ASFDCMIEnterprise && a.IPMI && a.ASFv1
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeASFPresencePong. It partially satisfies Layer and
|
||||
// SerializableLayer.
|
||||
func (*ASFPresencePong) LayerType() gopacket.LayerType {
|
||||
return LayerTypeASFPresencePong
|
||||
}
|
||||
|
||||
// CanDecode returns LayerTypeASFPresencePong. It partially satisfies
|
||||
// DecodingLayer.
|
||||
func (a *ASFPresencePong) CanDecode() gopacket.LayerClass {
|
||||
return a.LayerType()
|
||||
}
|
||||
|
||||
// DecodeFromBytes makes the layer represent the provided bytes. It partially
|
||||
// satisfies DecodingLayer.
|
||||
func (a *ASFPresencePong) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 16 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("invalid ASF presence pong payload, length %v less than 16",
|
||||
len(data))
|
||||
}
|
||||
|
||||
a.BaseLayer.Contents = data[:16]
|
||||
a.BaseLayer.Payload = data[16:]
|
||||
|
||||
a.Enterprise = binary.BigEndian.Uint32(data[:4])
|
||||
copy(a.OEM[:], data[4:8]) // N.B. no byte order change
|
||||
a.IPMI = data[8]&uint8(ASFPresencePongEntityIPMI) != 0
|
||||
a.ASFv1 = data[8]&uint8(ASFPresencePongEntityASFv1) != 0
|
||||
a.SecurityExtensions = data[9]&uint8(ASFPresencePongInteractionSecurityExtensions) != 0
|
||||
a.DASH = data[9]&uint8(ASFPresencePongInteractionDASH) != 0
|
||||
// ignore remaining 6 bytes; should be set to 0s
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextLayerType returns LayerTypePayload, as there are no further layers to
|
||||
// decode. This partially satisfies DecodingLayer.
|
||||
func (a *ASFPresencePong) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
|
||||
// partially satisfying SerializableLayer.
|
||||
func (a *ASFPresencePong) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
|
||||
|
||||
copy(bytes[4:8], a.OEM[:])
|
||||
|
||||
bytes[8] = 0
|
||||
if a.IPMI {
|
||||
bytes[8] |= uint8(ASFPresencePongEntityIPMI)
|
||||
}
|
||||
if a.ASFv1 {
|
||||
bytes[8] |= uint8(ASFPresencePongEntityASFv1)
|
||||
}
|
||||
|
||||
bytes[9] = 0
|
||||
if a.SecurityExtensions {
|
||||
bytes[9] |= uint8(ASFPresencePongInteractionSecurityExtensions)
|
||||
}
|
||||
if a.DASH {
|
||||
bytes[9] |= uint8(ASFPresencePongInteractionDASH)
|
||||
}
|
||||
|
||||
// zero-out remaining 6 bytes
|
||||
for i := 10; i < len(bytes); i++ {
|
||||
bytes[i] = 0x00
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeASFPresencePong decodes the byte slice into an RMCP-ASF Presence Pong
|
||||
// struct.
|
||||
func decodeASFPresencePong(data []byte, p gopacket.PacketBuilder) error {
|
||||
return decodingLayerDecoder(&ASFPresencePong{}, data, p)
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// BaseLayer is a convenience struct which implements the LayerData and
|
||||
// LayerPayload functions of the Layer interface.
|
||||
type BaseLayer struct {
|
||||
// Contents is the set of bytes that make up this layer. IE: for an
|
||||
// Ethernet packet, this would be the set of bytes making up the
|
||||
// Ethernet frame.
|
||||
Contents []byte
|
||||
// Payload is the set of bytes contained by (but not part of) this
|
||||
// Layer. Again, to take Ethernet as an example, this would be the
|
||||
// set of bytes encapsulated by the Ethernet protocol.
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// LayerContents returns the bytes of the packet layer.
|
||||
func (b *BaseLayer) LayerContents() []byte { return b.Contents }
|
||||
|
||||
// LayerPayload returns the bytes contained within the packet layer.
|
||||
func (b *BaseLayer) LayerPayload() []byte { return b.Payload }
|
||||
|
||||
type layerDecodingLayer interface {
|
||||
gopacket.Layer
|
||||
DecodeFromBytes([]byte, gopacket.DecodeFeedback) error
|
||||
NextLayerType() gopacket.LayerType
|
||||
}
|
||||
|
||||
func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error {
|
||||
err := d.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(d)
|
||||
next := d.NextLayerType()
|
||||
if next == gopacket.LayerTypeZero {
|
||||
return nil
|
||||
}
|
||||
return p.NextDecoder(next)
|
||||
}
|
||||
|
||||
// hacky way to zero out memory... there must be a better way?
|
||||
var lotsOfZeros [1024]byte
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
// Copyright 2017 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
//
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// BFD Control Packet Format
|
||||
// -------------------------
|
||||
// The current version of BFD's RFC (RFC 5880) contains the following
|
||||
// diagram for the BFD Control packet format:
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | My Discriminator |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Your Discriminator |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Desired Min TX Interval |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Required Min RX Interval |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Required Min Echo RX Interval |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// An optional Authentication Section MAY be present:
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Type | Auth Len | Authentication Data... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
//
|
||||
// Simple Password Authentication Section Format
|
||||
// ---------------------------------------------
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Type | Auth Len | Auth Key ID | Password... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
//
|
||||
// Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format
|
||||
// ----------------------------------------------------------------
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Type | Auth Len | Auth Key ID | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Key/Digest... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
//
|
||||
// Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format
|
||||
// ------------------------------------------------------------------
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Type | Auth Len | Auth Key ID | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Auth Key/Hash... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// From https://tools.ietf.org/rfc/rfc5880.txt
|
||||
const bfdMinimumRecordSizeInBytes int = 24
|
||||
|
||||
// BFDVersion represents the version as decoded from the BFD control message
|
||||
type BFDVersion uint8
|
||||
|
||||
// BFDDiagnostic represents diagnostic infomation about a BFD session
|
||||
type BFDDiagnostic uint8
|
||||
|
||||
// constants that define BFDDiagnostic flags
|
||||
const (
|
||||
BFDDiagnosticNone BFDDiagnostic = 0 // No Diagnostic
|
||||
BFDDiagnosticTimeExpired BFDDiagnostic = 1 // Control Detection Time Expired
|
||||
BFDDiagnosticEchoFailed BFDDiagnostic = 2 // Echo Function Failed
|
||||
BFDDiagnosticNeighborSignalDown BFDDiagnostic = 3 // Neighbor Signaled Session Down
|
||||
BFDDiagnosticForwardPlaneReset BFDDiagnostic = 4 // Forwarding Plane Reset
|
||||
BFDDiagnosticPathDown BFDDiagnostic = 5 // Path Down
|
||||
BFDDiagnosticConcatPathDown BFDDiagnostic = 6 // Concatenated Path Down
|
||||
BFDDiagnosticAdminDown BFDDiagnostic = 7 // Administratively Down
|
||||
BFDDiagnosticRevConcatPathDown BFDDiagnostic = 8 // Reverse Concatenated Path Dow
|
||||
)
|
||||
|
||||
// String returns a string version of BFDDiagnostic
|
||||
func (bd BFDDiagnostic) String() string {
|
||||
switch bd {
|
||||
default:
|
||||
return "Unknown"
|
||||
case BFDDiagnosticNone:
|
||||
return "None"
|
||||
case BFDDiagnosticTimeExpired:
|
||||
return "Control Detection Time Expired"
|
||||
case BFDDiagnosticEchoFailed:
|
||||
return "Echo Function Failed"
|
||||
case BFDDiagnosticNeighborSignalDown:
|
||||
return "Neighbor Signaled Session Down"
|
||||
case BFDDiagnosticForwardPlaneReset:
|
||||
return "Forwarding Plane Reset"
|
||||
case BFDDiagnosticPathDown:
|
||||
return "Path Down"
|
||||
case BFDDiagnosticConcatPathDown:
|
||||
return "Concatenated Path Down"
|
||||
case BFDDiagnosticAdminDown:
|
||||
return "Administratively Down"
|
||||
case BFDDiagnosticRevConcatPathDown:
|
||||
return "Reverse Concatenated Path Down"
|
||||
}
|
||||
}
|
||||
|
||||
// BFDState represents the state of a BFD session
|
||||
type BFDState uint8
|
||||
|
||||
// constants that define BFDState
|
||||
const (
|
||||
BFDStateAdminDown BFDState = 0
|
||||
BFDStateDown BFDState = 1
|
||||
BFDStateInit BFDState = 2
|
||||
BFDStateUp BFDState = 3
|
||||
)
|
||||
|
||||
// String returns a string version of BFDState
|
||||
func (s BFDState) String() string {
|
||||
switch s {
|
||||
default:
|
||||
return "Unknown"
|
||||
case BFDStateAdminDown:
|
||||
return "Admin Down"
|
||||
case BFDStateDown:
|
||||
return "Down"
|
||||
case BFDStateInit:
|
||||
return "Init"
|
||||
case BFDStateUp:
|
||||
return "Up"
|
||||
}
|
||||
}
|
||||
|
||||
// BFDDetectMultiplier represents the negotiated transmit interval,
|
||||
// multiplied by this value, provides the Detection Time for the
|
||||
// receiving system in Asynchronous mode.
|
||||
type BFDDetectMultiplier uint8
|
||||
|
||||
// BFDDiscriminator is a unique, nonzero discriminator value used
|
||||
// to demultiplex multiple BFD sessions between the same pair of systems.
|
||||
type BFDDiscriminator uint32
|
||||
|
||||
// BFDTimeInterval represents a time interval in microseconds
|
||||
type BFDTimeInterval uint32
|
||||
|
||||
// BFDAuthType represents the authentication used in the BFD session
|
||||
type BFDAuthType uint8
|
||||
|
||||
// constants that define the BFDAuthType
|
||||
const (
|
||||
BFDAuthTypeNone BFDAuthType = 0 // No Auth
|
||||
BFDAuthTypePassword BFDAuthType = 1 // Simple Password
|
||||
BFDAuthTypeKeyedMD5 BFDAuthType = 2 // Keyed MD5
|
||||
BFDAuthTypeMeticulousKeyedMD5 BFDAuthType = 3 // Meticulous Keyed MD5
|
||||
BFDAuthTypeKeyedSHA1 BFDAuthType = 4 // Keyed SHA1
|
||||
BFDAuthTypeMeticulousKeyedSHA1 BFDAuthType = 5 // Meticulous Keyed SHA1
|
||||
)
|
||||
|
||||
// String returns a string version of BFDAuthType
|
||||
func (at BFDAuthType) String() string {
|
||||
switch at {
|
||||
default:
|
||||
return "Unknown"
|
||||
case BFDAuthTypeNone:
|
||||
return "No Authentication"
|
||||
case BFDAuthTypePassword:
|
||||
return "Simple Password"
|
||||
case BFDAuthTypeKeyedMD5:
|
||||
return "Keyed MD5"
|
||||
case BFDAuthTypeMeticulousKeyedMD5:
|
||||
return "Meticulous Keyed MD5"
|
||||
case BFDAuthTypeKeyedSHA1:
|
||||
return "Keyed SHA1"
|
||||
case BFDAuthTypeMeticulousKeyedSHA1:
|
||||
return "Meticulous Keyed SHA1"
|
||||
}
|
||||
}
|
||||
|
||||
// BFDAuthKeyID represents the authentication key ID in use for
|
||||
// this packet. This allows multiple keys to be active simultaneously.
|
||||
type BFDAuthKeyID uint8
|
||||
|
||||
// BFDAuthSequenceNumber represents the sequence number for this packet.
|
||||
// For Keyed Authentication, this value is incremented occasionally. For
|
||||
// Meticulous Keyed Authentication, this value is incremented for each
|
||||
// successive packet transmitted for a session. This provides protection
|
||||
// against replay attacks.
|
||||
type BFDAuthSequenceNumber uint32
|
||||
|
||||
// BFDAuthData represents the authentication key or digest
|
||||
type BFDAuthData []byte
|
||||
|
||||
// BFDAuthHeader represents authentication data used in the BFD session
|
||||
type BFDAuthHeader struct {
|
||||
AuthType BFDAuthType
|
||||
KeyID BFDAuthKeyID
|
||||
SequenceNumber BFDAuthSequenceNumber
|
||||
Data BFDAuthData
|
||||
}
|
||||
|
||||
// Length returns the data length of the BFDAuthHeader based on the
|
||||
// authentication type
|
||||
func (h *BFDAuthHeader) Length() int {
|
||||
switch h.AuthType {
|
||||
case BFDAuthTypePassword:
|
||||
return 3 + len(h.Data)
|
||||
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
|
||||
return 8 + len(h.Data)
|
||||
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
|
||||
return 8 + len(h.Data)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// BFD represents a BFD control message packet whose payload contains
|
||||
// the control information required to for a BFD session.
|
||||
//
|
||||
// References
|
||||
// ----------
|
||||
//
|
||||
// Wikipedia's BFD entry:
|
||||
// https://en.wikipedia.org/wiki/Bidirectional_Forwarding_Detection
|
||||
// This is the best place to get an overview of BFD.
|
||||
//
|
||||
// RFC 5880 "Bidirectional Forwarding Detection (BFD)" (2010)
|
||||
// https://tools.ietf.org/html/rfc5880
|
||||
// This is the original BFD specification.
|
||||
//
|
||||
// RFC 5881 "Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop)" (2010)
|
||||
// https://tools.ietf.org/html/rfc5881
|
||||
// Describes the use of the Bidirectional Forwarding Detection (BFD)
|
||||
// protocol over IPv4 and IPv6 for single IP hops.
|
||||
type BFD struct {
|
||||
BaseLayer // Stores the packet bytes and payload bytes.
|
||||
|
||||
Version BFDVersion // Version of the BFD protocol.
|
||||
Diagnostic BFDDiagnostic // Diagnostic code for last state change
|
||||
State BFDState // Current state
|
||||
Poll bool // Requesting verification
|
||||
Final bool // Responding to a received BFD Control packet that had the Poll (P) bit set.
|
||||
ControlPlaneIndependent bool // BFD implementation does not share fate with its control plane
|
||||
AuthPresent bool // Authentication Section is present and the session is to be authenticated
|
||||
Demand bool // Demand mode is active
|
||||
Multipoint bool // For future point-to-multipoint extensions. Must always be zero
|
||||
DetectMultiplier BFDDetectMultiplier // Detection time multiplier
|
||||
MyDiscriminator BFDDiscriminator // A unique, nonzero discriminator value
|
||||
YourDiscriminator BFDDiscriminator // discriminator received from the remote system.
|
||||
DesiredMinTxInterval BFDTimeInterval // Minimum interval, in microseconds, the local system would like to use when transmitting BFD Control packets
|
||||
RequiredMinRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Control packets that this system is capable of supporting
|
||||
RequiredMinEchoRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Echo packets that this system is capable of supporting
|
||||
AuthHeader *BFDAuthHeader // Authentication data, variable length.
|
||||
}
|
||||
|
||||
// Length returns the data length of a BFD Control message which
|
||||
// changes based on the presence and type of authentication
|
||||
// contained in the message
|
||||
func (d *BFD) Length() int {
|
||||
if d.AuthPresent && (d.AuthHeader != nil) {
|
||||
return bfdMinimumRecordSizeInBytes + d.AuthHeader.Length()
|
||||
}
|
||||
|
||||
return bfdMinimumRecordSizeInBytes
|
||||
}
|
||||
|
||||
// LayerType returns the layer type of the BFD object, which is LayerTypeBFD.
|
||||
func (d *BFD) LayerType() gopacket.LayerType {
|
||||
return LayerTypeBFD
|
||||
}
|
||||
|
||||
// decodeBFD analyses a byte slice and attempts to decode it as a BFD
|
||||
// control packet
|
||||
//
|
||||
// If it succeeds, it loads p with information about the packet and returns nil.
|
||||
// If it fails, it returns an error (non nil).
|
||||
//
|
||||
// This function is employed in layertypes.go to register the BFD layer.
|
||||
func decodeBFD(data []byte, p gopacket.PacketBuilder) error {
|
||||
|
||||
// Attempt to decode the byte slice.
|
||||
d := &BFD{}
|
||||
err := d.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the decoding worked, add the layer to the packet and set it
|
||||
// as the application layer too, if there isn't already one.
|
||||
p.AddLayer(d)
|
||||
p.SetApplicationLayer(d)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeFromBytes analyses a byte slice and attempts to decode it as a BFD
|
||||
// control packet.
|
||||
//
|
||||
// Upon succeeds, it loads the BFD object with information about the packet
|
||||
// and returns nil.
|
||||
// Upon failure, it returns an error (non nil).
|
||||
func (d *BFD) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
|
||||
// If the data block is too short to be a BFD record, then return an error.
|
||||
if len(data) < bfdMinimumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return errors.New("BFD packet too short")
|
||||
}
|
||||
|
||||
pLen := uint8(data[3])
|
||||
if len(data) != int(pLen) {
|
||||
return errors.New("BFD packet length does not match")
|
||||
}
|
||||
|
||||
// BFD type embeds type BaseLayer which contains two fields:
|
||||
// Contents is supposed to contain the bytes of the data at this level.
|
||||
// Payload is supposed to contain the payload of this level.
|
||||
// Here we set the baselayer to be the bytes of the BFD record.
|
||||
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
|
||||
|
||||
// Extract the fields from the block of bytes.
|
||||
// To make sense of this, refer to the packet diagram
|
||||
// above and the section on endian conventions.
|
||||
|
||||
// The first few fields are all packed into the first 32 bits. Unpack them.
|
||||
d.Version = BFDVersion(((data[0] & 0xE0) >> 5))
|
||||
d.Diagnostic = BFDDiagnostic(data[0] & 0x1F)
|
||||
data = data[1:]
|
||||
|
||||
d.State = BFDState((data[0] & 0xC0) >> 6)
|
||||
d.Poll = data[0]&0x20 != 0
|
||||
d.Final = data[0]&0x10 != 0
|
||||
d.ControlPlaneIndependent = data[0]&0x08 != 0
|
||||
d.AuthPresent = data[0]&0x04 != 0
|
||||
d.Demand = data[0]&0x02 != 0
|
||||
d.Multipoint = data[0]&0x01 != 0
|
||||
data = data[1:]
|
||||
|
||||
data, d.DetectMultiplier = data[1:], BFDDetectMultiplier(data[0])
|
||||
data, _ = data[1:], uint8(data[0]) // Consume length
|
||||
|
||||
// The remaining fields can just be copied in big endian order.
|
||||
data, d.MyDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
|
||||
data, d.YourDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
|
||||
data, d.DesiredMinTxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
|
||||
data, d.RequiredMinRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
|
||||
data, d.RequiredMinEchoRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
|
||||
|
||||
if d.AuthPresent && (len(data) > 2) {
|
||||
d.AuthHeader = &BFDAuthHeader{}
|
||||
data, d.AuthHeader.AuthType = data[1:], BFDAuthType(data[0])
|
||||
data, _ = data[1:], uint8(data[0]) // Consume length
|
||||
data, d.AuthHeader.KeyID = data[1:], BFDAuthKeyID(data[0])
|
||||
|
||||
switch d.AuthHeader.AuthType {
|
||||
case BFDAuthTypePassword:
|
||||
d.AuthHeader.Data = BFDAuthData(data)
|
||||
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
|
||||
// Skipped reserved byte
|
||||
data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
|
||||
d.AuthHeader.Data = BFDAuthData(data)
|
||||
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
|
||||
// Skipped reserved byte
|
||||
data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
|
||||
d.AuthHeader.Data = BFDAuthData(data)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *BFD) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
data, err := b.PrependBytes(bfdMinimumRecordSizeInBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pack the first few fields into the first 32 bits.
|
||||
data[0] = byte(byte(d.Version<<5) | byte(d.Diagnostic))
|
||||
h := uint8(0)
|
||||
h |= (uint8(d.State) << 6)
|
||||
h |= (uint8(bool2uint8(d.Poll)) << 5)
|
||||
h |= (uint8(bool2uint8(d.Final)) << 4)
|
||||
h |= (uint8(bool2uint8(d.ControlPlaneIndependent)) << 3)
|
||||
h |= (uint8(bool2uint8(d.AuthPresent)) << 2)
|
||||
h |= (uint8(bool2uint8(d.Demand)) << 1)
|
||||
h |= uint8(bool2uint8(d.Multipoint))
|
||||
data[1] = byte(h)
|
||||
data[2] = byte(d.DetectMultiplier)
|
||||
data[3] = byte(d.Length())
|
||||
|
||||
// The remaining fields can just be copied in big endian order.
|
||||
binary.BigEndian.PutUint32(data[4:], uint32(d.MyDiscriminator))
|
||||
binary.BigEndian.PutUint32(data[8:], uint32(d.YourDiscriminator))
|
||||
binary.BigEndian.PutUint32(data[12:], uint32(d.DesiredMinTxInterval))
|
||||
binary.BigEndian.PutUint32(data[16:], uint32(d.RequiredMinRxInterval))
|
||||
binary.BigEndian.PutUint32(data[20:], uint32(d.RequiredMinEchoRxInterval))
|
||||
|
||||
if d.AuthPresent && (d.AuthHeader != nil) {
|
||||
auth, err := b.AppendBytes(int(d.AuthHeader.Length()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth[0] = byte(d.AuthHeader.AuthType)
|
||||
auth[1] = byte(d.AuthHeader.Length())
|
||||
auth[2] = byte(d.AuthHeader.KeyID)
|
||||
|
||||
switch d.AuthHeader.AuthType {
|
||||
case BFDAuthTypePassword:
|
||||
copy(auth[3:], d.AuthHeader.Data)
|
||||
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
|
||||
auth[3] = byte(0)
|
||||
binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
|
||||
copy(auth[8:], d.AuthHeader.Data)
|
||||
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
|
||||
auth[3] = byte(0)
|
||||
binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
|
||||
copy(auth[8:], d.AuthHeader.Data)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns a set of layers that BFD objects can decode.
|
||||
// As BFD objects can only decide the BFD layer, we can return just that layer.
|
||||
// Apparently a single layer type implements LayerClass.
|
||||
func (d *BFD) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeBFD
|
||||
}
|
||||
|
||||
// NextLayerType specifies the next layer that GoPacket should attempt to
|
||||
// analyse after this (BFD) layer. As BFD packets do not contain any payload
|
||||
// bytes, there are no further layers to analyse.
|
||||
func (d *BFD) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
// Payload returns an empty byte slice as BFD packets do not carry a payload
|
||||
func (d *BFD) Payload() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bool2uint8 converts a bool to uint8
|
||||
func bool2uint8(b bool) uint8 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+659
@@ -0,0 +1,659 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
// Enum types courtesy of...
|
||||
// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm
|
||||
// https://code.google.com/p/ladvd/
|
||||
// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
|
||||
type CDPTLVType uint16
|
||||
|
||||
// CDPTLVType values.
|
||||
const (
|
||||
CDPTLVDevID CDPTLVType = 0x0001
|
||||
CDPTLVAddress CDPTLVType = 0x0002
|
||||
CDPTLVPortID CDPTLVType = 0x0003
|
||||
CDPTLVCapabilities CDPTLVType = 0x0004
|
||||
CDPTLVVersion CDPTLVType = 0x0005
|
||||
CDPTLVPlatform CDPTLVType = 0x0006
|
||||
CDPTLVIPPrefix CDPTLVType = 0x0007
|
||||
CDPTLVHello CDPTLVType = 0x0008
|
||||
CDPTLVVTPDomain CDPTLVType = 0x0009
|
||||
CDPTLVNativeVLAN CDPTLVType = 0x000a
|
||||
CDPTLVFullDuplex CDPTLVType = 0x000b
|
||||
CDPTLVVLANReply CDPTLVType = 0x000e
|
||||
CDPTLVVLANQuery CDPTLVType = 0x000f
|
||||
CDPTLVPower CDPTLVType = 0x0010
|
||||
CDPTLVMTU CDPTLVType = 0x0011
|
||||
CDPTLVExtendedTrust CDPTLVType = 0x0012
|
||||
CDPTLVUntrustedCOS CDPTLVType = 0x0013
|
||||
CDPTLVSysName CDPTLVType = 0x0014
|
||||
CDPTLVSysOID CDPTLVType = 0x0015
|
||||
CDPTLVMgmtAddresses CDPTLVType = 0x0016
|
||||
CDPTLVLocation CDPTLVType = 0x0017
|
||||
CDPTLVExternalPortID CDPTLVType = 0x0018
|
||||
CDPTLVPowerRequested CDPTLVType = 0x0019
|
||||
CDPTLVPowerAvailable CDPTLVType = 0x001a
|
||||
CDPTLVPortUnidirectional CDPTLVType = 0x001b
|
||||
CDPTLVEnergyWise CDPTLVType = 0x001d
|
||||
CDPTLVSparePairPOE CDPTLVType = 0x001f
|
||||
)
|
||||
|
||||
// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
|
||||
type CiscoDiscoveryValue struct {
|
||||
Type CDPTLVType
|
||||
Length uint16
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
|
||||
// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
|
||||
type CiscoDiscovery struct {
|
||||
BaseLayer
|
||||
Version byte
|
||||
TTL byte
|
||||
Checksum uint16
|
||||
Values []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// CDPCapability is the set of capabilities advertised by a CDP device.
|
||||
type CDPCapability uint32
|
||||
|
||||
// CDPCapability values.
|
||||
const (
|
||||
CDPCapMaskRouter CDPCapability = 0x0001
|
||||
CDPCapMaskTBBridge CDPCapability = 0x0002
|
||||
CDPCapMaskSPBridge CDPCapability = 0x0004
|
||||
CDPCapMaskSwitch CDPCapability = 0x0008
|
||||
CDPCapMaskHost CDPCapability = 0x0010
|
||||
CDPCapMaskIGMPFilter CDPCapability = 0x0020
|
||||
CDPCapMaskRepeater CDPCapability = 0x0040
|
||||
CDPCapMaskPhone CDPCapability = 0x0080
|
||||
CDPCapMaskRemote CDPCapability = 0x0100
|
||||
)
|
||||
|
||||
// CDPCapabilities represents the capabilities of a device
|
||||
type CDPCapabilities struct {
|
||||
L3Router bool
|
||||
TBBridge bool
|
||||
SPBridge bool
|
||||
L2Switch bool
|
||||
IsHost bool
|
||||
IGMPFilter bool
|
||||
L1Repeater bool
|
||||
IsPhone bool
|
||||
RemotelyManaged bool
|
||||
}
|
||||
|
||||
// CDP Power-over-Ethernet values.
|
||||
const (
|
||||
CDPPoEFourWire byte = 0x01
|
||||
CDPPoEPDArch byte = 0x02
|
||||
CDPPoEPDRequest byte = 0x04
|
||||
CDPPoEPSE byte = 0x08
|
||||
)
|
||||
|
||||
// CDPSparePairPoE provides information on PoE.
|
||||
type CDPSparePairPoE struct {
|
||||
PSEFourWire bool // Supported / Not supported
|
||||
PDArchShared bool // Shared / Independent
|
||||
PDRequestOn bool // On / Off
|
||||
PSEOn bool // On / Off
|
||||
}
|
||||
|
||||
// CDPVLANDialogue encapsulates a VLAN Query/Reply
|
||||
type CDPVLANDialogue struct {
|
||||
ID uint8
|
||||
VLAN uint16
|
||||
}
|
||||
|
||||
// CDPPowerDialogue encapsulates a Power Query/Reply
|
||||
type CDPPowerDialogue struct {
|
||||
ID uint16
|
||||
MgmtID uint16
|
||||
Values []uint32
|
||||
}
|
||||
|
||||
// CDPLocation provides location information for a CDP device.
|
||||
type CDPLocation struct {
|
||||
Type uint8 // Undocumented
|
||||
Location string
|
||||
}
|
||||
|
||||
// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
|
||||
type CDPHello struct {
|
||||
OUI []byte
|
||||
ProtocolID uint16
|
||||
ClusterMaster net.IP
|
||||
Unknown1 net.IP
|
||||
Version byte
|
||||
SubVersion byte
|
||||
Status byte
|
||||
Unknown2 byte
|
||||
ClusterCommander net.HardwareAddr
|
||||
SwitchMAC net.HardwareAddr
|
||||
Unknown3 byte
|
||||
ManagementVLAN uint16
|
||||
}
|
||||
|
||||
// CDPEnergyWiseSubtype is used within CDP to define TLV values.
|
||||
type CDPEnergyWiseSubtype uint32
|
||||
|
||||
// CDPEnergyWiseSubtype values.
|
||||
const (
|
||||
CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007
|
||||
CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008
|
||||
CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009
|
||||
CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
|
||||
)
|
||||
|
||||
// CDPEnergyWise is used by CDP to monitor and control power usage.
|
||||
type CDPEnergyWise struct {
|
||||
EncryptedData []byte
|
||||
Unknown1 uint32
|
||||
SequenceNumber uint32
|
||||
ModelNumber string
|
||||
Unknown2 uint16
|
||||
HardwareID string
|
||||
SerialNum string
|
||||
Unknown3 []byte
|
||||
Role string
|
||||
Domain string
|
||||
Name string
|
||||
ReplyUnknown1 []byte
|
||||
ReplyPort []byte
|
||||
ReplyAddress []byte
|
||||
ReplyUnknown2 []byte
|
||||
ReplyUnknown3 []byte
|
||||
}
|
||||
|
||||
// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
|
||||
type CiscoDiscoveryInfo struct {
|
||||
BaseLayer
|
||||
CDPHello
|
||||
DeviceID string
|
||||
Addresses []net.IP
|
||||
PortID string
|
||||
Capabilities CDPCapabilities
|
||||
Version string
|
||||
Platform string
|
||||
IPPrefixes []net.IPNet
|
||||
VTPDomain string
|
||||
NativeVLAN uint16
|
||||
FullDuplex bool
|
||||
VLANReply CDPVLANDialogue
|
||||
VLANQuery CDPVLANDialogue
|
||||
PowerConsumption uint16
|
||||
MTU uint32
|
||||
ExtendedTrust uint8
|
||||
UntrustedCOS uint8
|
||||
SysName string
|
||||
SysOID string
|
||||
MgmtAddresses []net.IP
|
||||
Location CDPLocation
|
||||
PowerRequest CDPPowerDialogue
|
||||
PowerAvailable CDPPowerDialogue
|
||||
SparePairPoe CDPSparePairPoE
|
||||
EnergyWise CDPEnergyWise
|
||||
Unknown []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscovery.
|
||||
func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscovery
|
||||
}
|
||||
|
||||
func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
c := &CiscoDiscovery{
|
||||
Version: data[0],
|
||||
TTL: data[1],
|
||||
Checksum: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if c.Version != 1 && c.Version != 2 {
|
||||
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
|
||||
}
|
||||
var err error
|
||||
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:], p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Contents = data[0:4]
|
||||
c.Payload = data[4:]
|
||||
p.AddLayer(c)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
|
||||
func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscoveryInfo
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryTLVs(data []byte, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) {
|
||||
for len(data) > 0 {
|
||||
if len(data) < 4 {
|
||||
p.SetTruncated()
|
||||
return nil, errors.New("CDP TLV < 4 bytes")
|
||||
}
|
||||
val := CiscoDiscoveryValue{
|
||||
Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
|
||||
Length: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if val.Length < 4 {
|
||||
err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
|
||||
break
|
||||
} else if len(data) < int(val.Length) {
|
||||
p.SetTruncated()
|
||||
return nil, fmt.Errorf("CDP TLV < length %d", val.Length)
|
||||
}
|
||||
val.Value = data[4:val.Length]
|
||||
values = append(values, val)
|
||||
data = data[val.Length:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
|
||||
var err error
|
||||
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
|
||||
p.AddLayer(info)
|
||||
values, err := decodeCiscoDiscoveryTLVs(data, p)
|
||||
if err != nil { // Unlikely, as parent decode will fail, but better safe...
|
||||
return err
|
||||
}
|
||||
for _, val := range values {
|
||||
switch val.Type {
|
||||
case CDPTLVDevID:
|
||||
info.DeviceID = string(val.Value)
|
||||
case CDPTLVAddress:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Addresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVPortID:
|
||||
info.PortID = string(val.Value)
|
||||
case CDPTLVCapabilities:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
|
||||
info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
|
||||
info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
|
||||
info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
|
||||
info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
|
||||
info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
|
||||
info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
|
||||
info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
|
||||
info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
|
||||
info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
|
||||
case CDPTLVVersion:
|
||||
info.Version = string(val.Value)
|
||||
case CDPTLVPlatform:
|
||||
info.Platform = string(val.Value)
|
||||
case CDPTLVIPPrefix:
|
||||
v := val.Value
|
||||
l := len(v)
|
||||
if l%5 == 0 && l >= 5 {
|
||||
for len(v) > 0 {
|
||||
_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
|
||||
info.IPPrefixes = append(info.IPPrefixes, *ipnet)
|
||||
v = v[5:]
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
|
||||
}
|
||||
case CDPTLVHello:
|
||||
if err = checkCDPTLVLen(val, 32); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value
|
||||
info.CDPHello.OUI = v[0:3]
|
||||
info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
|
||||
info.CDPHello.ClusterMaster = v[5:9]
|
||||
info.CDPHello.Unknown1 = v[9:13]
|
||||
info.CDPHello.Version = v[13]
|
||||
info.CDPHello.SubVersion = v[14]
|
||||
info.CDPHello.Status = v[15]
|
||||
info.CDPHello.Unknown2 = v[16]
|
||||
info.CDPHello.ClusterCommander = v[17:23]
|
||||
info.CDPHello.SwitchMAC = v[23:29]
|
||||
info.CDPHello.Unknown3 = v[29]
|
||||
info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
|
||||
case CDPTLVVTPDomain:
|
||||
info.VTPDomain = string(val.Value)
|
||||
case CDPTLVNativeVLAN:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVFullDuplex:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.FullDuplex = (val.Value[0] == 1)
|
||||
case CDPTLVVLANReply:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANReply.ID = uint8(val.Value[0])
|
||||
info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVVLANQuery:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANQuery.ID = uint8(val.Value[0])
|
||||
info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVPower:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVMTU:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
|
||||
case CDPTLVExtendedTrust:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.ExtendedTrust = uint8(val.Value[0])
|
||||
case CDPTLVUntrustedCOS:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.UntrustedCOS = uint8(val.Value[0])
|
||||
case CDPTLVSysName:
|
||||
info.SysName = string(val.Value)
|
||||
case CDPTLVSysOID:
|
||||
info.SysOID = string(val.Value)
|
||||
case CDPTLVMgmtAddresses:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MgmtAddresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVLocation:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Location.Type = uint8(val.Value[0])
|
||||
info.Location.Location = string(val.Value[1:])
|
||||
|
||||
// case CDPTLVLExternalPortID:
|
||||
// Undocumented
|
||||
case CDPTLVPowerRequested:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
case CDPTLVPowerAvailable:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
// case CDPTLVPortUnidirectional
|
||||
// Undocumented
|
||||
case CDPTLVEnergyWise:
|
||||
if err = checkCDPTLVLen(val, 72); err != nil {
|
||||
return err
|
||||
}
|
||||
info.EnergyWise.EncryptedData = val.Value[0:20]
|
||||
info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
|
||||
info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
|
||||
info.EnergyWise.ModelNumber = string(val.Value[28:44])
|
||||
info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
|
||||
info.EnergyWise.HardwareID = string(val.Value[46:49])
|
||||
info.EnergyWise.SerialNum = string(val.Value[49:60])
|
||||
info.EnergyWise.Unknown3 = val.Value[60:68]
|
||||
tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
|
||||
tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
|
||||
data := val.Value[72:]
|
||||
if len(data) < int(tlvLen) {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
|
||||
}
|
||||
numSeen := 0
|
||||
for len(data) > 8 {
|
||||
numSeen++
|
||||
if numSeen > int(tlvNum) { // Too many TLV's ?
|
||||
return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
|
||||
}
|
||||
tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
|
||||
tLen := int(binary.BigEndian.Uint32(data[4:8]))
|
||||
if tLen > len(data)-8 {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
|
||||
}
|
||||
data = data[8:]
|
||||
switch tType {
|
||||
case CDPEnergyWiseRole:
|
||||
info.EnergyWise.Role = string(data[:])
|
||||
case CDPEnergyWiseDomain:
|
||||
info.EnergyWise.Domain = string(data[:])
|
||||
case CDPEnergyWiseName:
|
||||
info.EnergyWise.Name = string(data[:])
|
||||
case CDPEnergyWiseReplyTo:
|
||||
if len(data) >= 18 {
|
||||
info.EnergyWise.ReplyUnknown1 = data[0:2]
|
||||
info.EnergyWise.ReplyPort = data[2:4]
|
||||
info.EnergyWise.ReplyAddress = data[4:8]
|
||||
info.EnergyWise.ReplyUnknown2 = data[8:10]
|
||||
info.EnergyWise.ReplyUnknown3 = data[10:14]
|
||||
}
|
||||
}
|
||||
data = data[tLen:]
|
||||
}
|
||||
case CDPTLVSparePairPOE:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value[0]
|
||||
info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
|
||||
info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
|
||||
info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
|
||||
info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
|
||||
default:
|
||||
info.Unknown = append(info.Unknown, val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CDP Protocol Types
|
||||
const (
|
||||
CDPProtocolTypeNLPID byte = 1
|
||||
CDPProtocolType802_2 byte = 2
|
||||
)
|
||||
|
||||
// CDPAddressType is used to define TLV values within CDP addresses.
|
||||
type CDPAddressType uint64
|
||||
|
||||
// CDP Address types.
|
||||
const (
|
||||
CDPAddressTypeCLNP CDPAddressType = 0x81
|
||||
CDPAddressTypeIPV4 CDPAddressType = 0xcc
|
||||
CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800
|
||||
CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003
|
||||
CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
|
||||
CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137
|
||||
CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4
|
||||
CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600
|
||||
CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019
|
||||
)
|
||||
|
||||
func decodeAddresses(v []byte) (addresses []net.IP, err error) {
|
||||
numaddr := int(binary.BigEndian.Uint32(v[0:4]))
|
||||
if numaddr < 1 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
|
||||
}
|
||||
v = v[4:]
|
||||
if len(v) < numaddr*8 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
|
||||
}
|
||||
for i := 0; i < numaddr; i++ {
|
||||
prottype := v[0]
|
||||
if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
|
||||
return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
|
||||
}
|
||||
protlen := int(v[1])
|
||||
if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
|
||||
(prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
|
||||
return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
|
||||
}
|
||||
plen := make([]byte, 8)
|
||||
copy(plen[8-protlen:], v[2:2+protlen])
|
||||
protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
|
||||
v = v[2+protlen:]
|
||||
addrlen := binary.BigEndian.Uint16(v[0:2])
|
||||
ab := v[2 : 2+addrlen]
|
||||
if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
|
||||
addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
|
||||
} else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
|
||||
addresses = append(addresses, net.IP(ab))
|
||||
} else {
|
||||
// only handle IPV4 & IPV6 for now
|
||||
}
|
||||
v = v[2+addrlen:]
|
||||
if len(v) < 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPTLVType) String() (s string) {
|
||||
switch t {
|
||||
case CDPTLVDevID:
|
||||
s = "Device ID"
|
||||
case CDPTLVAddress:
|
||||
s = "Addresses"
|
||||
case CDPTLVPortID:
|
||||
s = "Port ID"
|
||||
case CDPTLVCapabilities:
|
||||
s = "Capabilities"
|
||||
case CDPTLVVersion:
|
||||
s = "Software Version"
|
||||
case CDPTLVPlatform:
|
||||
s = "Platform"
|
||||
case CDPTLVIPPrefix:
|
||||
s = "IP Prefix"
|
||||
case CDPTLVHello:
|
||||
s = "Protocol Hello"
|
||||
case CDPTLVVTPDomain:
|
||||
s = "VTP Management Domain"
|
||||
case CDPTLVNativeVLAN:
|
||||
s = "Native VLAN"
|
||||
case CDPTLVFullDuplex:
|
||||
s = "Full Duplex"
|
||||
case CDPTLVVLANReply:
|
||||
s = "VoIP VLAN Reply"
|
||||
case CDPTLVVLANQuery:
|
||||
s = "VLANQuery"
|
||||
case CDPTLVPower:
|
||||
s = "Power consumption"
|
||||
case CDPTLVMTU:
|
||||
s = "MTU"
|
||||
case CDPTLVExtendedTrust:
|
||||
s = "Extended Trust Bitmap"
|
||||
case CDPTLVUntrustedCOS:
|
||||
s = "Untrusted Port CoS"
|
||||
case CDPTLVSysName:
|
||||
s = "System Name"
|
||||
case CDPTLVSysOID:
|
||||
s = "System OID"
|
||||
case CDPTLVMgmtAddresses:
|
||||
s = "Management Addresses"
|
||||
case CDPTLVLocation:
|
||||
s = "Location"
|
||||
case CDPTLVExternalPortID:
|
||||
s = "External Port ID"
|
||||
case CDPTLVPowerRequested:
|
||||
s = "Power Requested"
|
||||
case CDPTLVPowerAvailable:
|
||||
s = "Power Available"
|
||||
case CDPTLVPortUnidirectional:
|
||||
s = "Port Unidirectional"
|
||||
case CDPTLVEnergyWise:
|
||||
s = "Energy Wise"
|
||||
case CDPTLVSparePairPOE:
|
||||
s = "Spare Pair POE"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (a CDPAddressType) String() (s string) {
|
||||
switch a {
|
||||
case CDPAddressTypeCLNP:
|
||||
s = "Connectionless Network Protocol"
|
||||
case CDPAddressTypeIPV4:
|
||||
s = "IPv4"
|
||||
case CDPAddressTypeIPV6:
|
||||
s = "IPv6"
|
||||
case CDPAddressTypeDECNET:
|
||||
s = "DECnet Phase IV"
|
||||
case CDPAddressTypeAPPLETALK:
|
||||
s = "Apple Talk"
|
||||
case CDPAddressTypeIPX:
|
||||
s = "Novell IPX"
|
||||
case CDPAddressTypeVINES:
|
||||
s = "Banyan VINES"
|
||||
case CDPAddressTypeXNS:
|
||||
s = "Xerox Network Systems"
|
||||
case CDPAddressTypeAPOLLO:
|
||||
s = "Apollo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPEnergyWiseSubtype) String() (s string) {
|
||||
switch t {
|
||||
case CDPEnergyWiseRole:
|
||||
s = "Role"
|
||||
case CDPEnergyWiseDomain:
|
||||
s = "Domain"
|
||||
case CDPEnergyWiseName:
|
||||
s = "Name"
|
||||
case CDPEnergyWiseReplyTo:
|
||||
s = "ReplyTo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
|
||||
if len(v.Value) < l {
|
||||
err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))
|
||||
}
|
||||
return
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each
|
||||
// EthernetCTP layer.
|
||||
type EthernetCTPFunction uint16
|
||||
|
||||
// EthernetCTPFunction values.
|
||||
const (
|
||||
EthernetCTPFunctionReply EthernetCTPFunction = 1
|
||||
EthernetCTPFunctionForwardData EthernetCTPFunction = 2
|
||||
)
|
||||
|
||||
// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html.
|
||||
// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more
|
||||
// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer.
|
||||
type EthernetCTP struct {
|
||||
BaseLayer
|
||||
SkipCount uint16
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTP.
|
||||
func (c *EthernetCTP) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTP
|
||||
}
|
||||
|
||||
// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more
|
||||
// details.
|
||||
type EthernetCTPForwardData struct {
|
||||
BaseLayer
|
||||
Function EthernetCTPFunction
|
||||
ForwardAddress []byte
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTPForwardData.
|
||||
func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTPForwardData
|
||||
}
|
||||
|
||||
// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint.
|
||||
func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress)
|
||||
}
|
||||
|
||||
// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details.
|
||||
type EthernetCTPReply struct {
|
||||
BaseLayer
|
||||
Function EthernetCTPFunction
|
||||
ReceiptNumber uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTPReply.
|
||||
func (c *EthernetCTPReply) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTPReply
|
||||
}
|
||||
|
||||
// Payload returns the EthernetCTP reply's Data bytes.
|
||||
func (c *EthernetCTPReply) Payload() []byte { return c.Data }
|
||||
|
||||
func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error {
|
||||
c := &EthernetCTP{
|
||||
SkipCount: binary.LittleEndian.Uint16(data[:2]),
|
||||
BaseLayer: BaseLayer{data[:2], data[2:]},
|
||||
}
|
||||
if c.SkipCount%2 != 0 {
|
||||
return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount)
|
||||
}
|
||||
p.AddLayer(c)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||
}
|
||||
|
||||
// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP
|
||||
// layer type to decode next, then decodes based on that.
|
||||
func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error {
|
||||
function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2]))
|
||||
switch function {
|
||||
case EthernetCTPFunctionReply:
|
||||
reply := &EthernetCTPReply{
|
||||
Function: function,
|
||||
ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]),
|
||||
Data: data[4:],
|
||||
BaseLayer: BaseLayer{data, nil},
|
||||
}
|
||||
p.AddLayer(reply)
|
||||
p.SetApplicationLayer(reply)
|
||||
return nil
|
||||
case EthernetCTPFunctionForwardData:
|
||||
forward := &EthernetCTPForwardData{
|
||||
Function: function,
|
||||
ForwardAddress: data[2:8],
|
||||
BaseLayer: BaseLayer{data[:8], data[8:]},
|
||||
}
|
||||
p.AddLayer(forward)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||
}
|
||||
return fmt.Errorf("Unknown EthernetCTP function type %v", function)
|
||||
}
|
||||
+592
@@ -0,0 +1,592 @@
|
||||
// Copyright 2016 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DHCPOp rerprents a bootp operation
|
||||
type DHCPOp byte
|
||||
|
||||
// bootp operations
|
||||
const (
|
||||
DHCPOpRequest DHCPOp = 1
|
||||
DHCPOpReply DHCPOp = 2
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPOp.
|
||||
func (o DHCPOp) String() string {
|
||||
switch o {
|
||||
case DHCPOpRequest:
|
||||
return "Request"
|
||||
case DHCPOpReply:
|
||||
return "Reply"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPMsgType represents a DHCP operation
|
||||
type DHCPMsgType byte
|
||||
|
||||
// Constants that represent DHCP operations
|
||||
const (
|
||||
DHCPMsgTypeUnspecified DHCPMsgType = iota
|
||||
DHCPMsgTypeDiscover
|
||||
DHCPMsgTypeOffer
|
||||
DHCPMsgTypeRequest
|
||||
DHCPMsgTypeDecline
|
||||
DHCPMsgTypeAck
|
||||
DHCPMsgTypeNak
|
||||
DHCPMsgTypeRelease
|
||||
DHCPMsgTypeInform
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPMsgType.
|
||||
func (o DHCPMsgType) String() string {
|
||||
switch o {
|
||||
case DHCPMsgTypeUnspecified:
|
||||
return "Unspecified"
|
||||
case DHCPMsgTypeDiscover:
|
||||
return "Discover"
|
||||
case DHCPMsgTypeOffer:
|
||||
return "Offer"
|
||||
case DHCPMsgTypeRequest:
|
||||
return "Request"
|
||||
case DHCPMsgTypeDecline:
|
||||
return "Decline"
|
||||
case DHCPMsgTypeAck:
|
||||
return "Ack"
|
||||
case DHCPMsgTypeNak:
|
||||
return "Nak"
|
||||
case DHCPMsgTypeRelease:
|
||||
return "Release"
|
||||
case DHCPMsgTypeInform:
|
||||
return "Inform"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
//DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
|
||||
var DHCPMagic uint32 = 0x63825363
|
||||
|
||||
// DHCPv4 contains data for a single DHCP packet.
|
||||
type DHCPv4 struct {
|
||||
BaseLayer
|
||||
Operation DHCPOp
|
||||
HardwareType LinkType
|
||||
HardwareLen uint8
|
||||
HardwareOpts uint8
|
||||
Xid uint32
|
||||
Secs uint16
|
||||
Flags uint16
|
||||
ClientIP net.IP
|
||||
YourClientIP net.IP
|
||||
NextServerIP net.IP
|
||||
RelayAgentIP net.IP
|
||||
ClientHWAddr net.HardwareAddr
|
||||
ServerName []byte
|
||||
File []byte
|
||||
Options DHCPOptions
|
||||
}
|
||||
|
||||
// DHCPOptions is used to get nicely printed option lists which would normally
|
||||
// be cut off after 5 options.
|
||||
type DHCPOptions []DHCPOption
|
||||
|
||||
// String returns a string version of the options list.
|
||||
func (o DHCPOptions) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteByte('[')
|
||||
for i, opt := range o {
|
||||
buf.WriteString(opt.String())
|
||||
if i+1 != len(o) {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDHCPv4
|
||||
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 240 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("DHCPv4 length %d too short", len(data))
|
||||
}
|
||||
d.Options = d.Options[:0]
|
||||
d.Operation = DHCPOp(data[0])
|
||||
d.HardwareType = LinkType(data[1])
|
||||
d.HardwareLen = data[2]
|
||||
d.HardwareOpts = data[3]
|
||||
d.Xid = binary.BigEndian.Uint32(data[4:8])
|
||||
d.Secs = binary.BigEndian.Uint16(data[8:10])
|
||||
d.Flags = binary.BigEndian.Uint16(data[10:12])
|
||||
d.ClientIP = net.IP(data[12:16])
|
||||
d.YourClientIP = net.IP(data[16:20])
|
||||
d.NextServerIP = net.IP(data[20:24])
|
||||
d.RelayAgentIP = net.IP(data[24:28])
|
||||
d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
|
||||
d.ServerName = data[44:108]
|
||||
d.File = data[108:236]
|
||||
if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
|
||||
return InvalidMagicCookie
|
||||
}
|
||||
|
||||
if len(data) <= 240 {
|
||||
// DHCP Packet could have no option (??)
|
||||
return nil
|
||||
}
|
||||
|
||||
options := data[240:]
|
||||
|
||||
stop := len(options)
|
||||
start := 0
|
||||
for start < stop {
|
||||
o := DHCPOption{}
|
||||
if err := o.decode(options[start:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if o.Type == DHCPOptEnd {
|
||||
break
|
||||
}
|
||||
d.Options = append(d.Options, o)
|
||||
// Check if the option is a single byte pad
|
||||
if o.Type == DHCPOptPad {
|
||||
start++
|
||||
} else {
|
||||
start += int(o.Length) + 2
|
||||
}
|
||||
}
|
||||
|
||||
d.Contents = data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the length of a DHCPv4 packet.
|
||||
func (d *DHCPv4) Len() uint16 {
|
||||
n := uint16(240)
|
||||
for _, o := range d.Options {
|
||||
if o.Type == DHCPOptPad {
|
||||
n++
|
||||
} else {
|
||||
n += uint16(o.Length) + 2
|
||||
}
|
||||
}
|
||||
n++ // for opt end
|
||||
return n
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
plen := int(d.Len())
|
||||
|
||||
data, err := b.PrependBytes(plen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data[0] = byte(d.Operation)
|
||||
data[1] = byte(d.HardwareType)
|
||||
if opts.FixLengths {
|
||||
d.HardwareLen = uint8(len(d.ClientHWAddr))
|
||||
}
|
||||
data[2] = d.HardwareLen
|
||||
data[3] = d.HardwareOpts
|
||||
binary.BigEndian.PutUint32(data[4:8], d.Xid)
|
||||
binary.BigEndian.PutUint16(data[8:10], d.Secs)
|
||||
binary.BigEndian.PutUint16(data[10:12], d.Flags)
|
||||
copy(data[12:16], d.ClientIP.To4())
|
||||
copy(data[16:20], d.YourClientIP.To4())
|
||||
copy(data[20:24], d.NextServerIP.To4())
|
||||
copy(data[24:28], d.RelayAgentIP.To4())
|
||||
copy(data[28:44], d.ClientHWAddr)
|
||||
copy(data[44:108], d.ServerName)
|
||||
copy(data[108:236], d.File)
|
||||
binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
|
||||
|
||||
if len(d.Options) > 0 {
|
||||
offset := 240
|
||||
for _, o := range d.Options {
|
||||
if err := o.encode(data[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
// A pad option is only a single byte
|
||||
if o.Type == DHCPOptPad {
|
||||
offset++
|
||||
} else {
|
||||
offset += 2 + len(o.Data)
|
||||
}
|
||||
}
|
||||
optend := NewDHCPOption(DHCPOptEnd, nil)
|
||||
if err := optend.encode(data[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *DHCPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDHCPv4
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *DHCPv4) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
dhcp := &DHCPv4{}
|
||||
err := dhcp.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(dhcp)
|
||||
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||
}
|
||||
|
||||
// DHCPOpt represents a DHCP option or parameter from RFC-2132
|
||||
type DHCPOpt byte
|
||||
|
||||
// Constants for the DHCPOpt options.
|
||||
const (
|
||||
DHCPOptPad DHCPOpt = 0
|
||||
DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP
|
||||
DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC)
|
||||
DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP
|
||||
DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP
|
||||
DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP
|
||||
DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP
|
||||
DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP
|
||||
DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP
|
||||
DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP
|
||||
DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP
|
||||
DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP
|
||||
DHCPOptHostname DHCPOpt = 12 // n, string
|
||||
DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16
|
||||
DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string
|
||||
DHCPOptDomainName DHCPOpt = 15 // n, string
|
||||
DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP
|
||||
DHCPOptRootPath DHCPOpt = 17 // n, string
|
||||
DHCPOptExtensionsPath DHCPOpt = 18 // n, string
|
||||
DHCPOptIPForwarding DHCPOpt = 19 // 1, bool
|
||||
DHCPOptSourceRouting DHCPOpt = 20 // 1, bool
|
||||
DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP}
|
||||
DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16
|
||||
DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte
|
||||
DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32
|
||||
DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16
|
||||
DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16
|
||||
DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool
|
||||
DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP
|
||||
DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool
|
||||
DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool
|
||||
DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool
|
||||
DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP
|
||||
DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
|
||||
DHCPOptARPTrailers DHCPOpt = 34 // 1, bool
|
||||
DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32
|
||||
DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool
|
||||
DHCPOptTCPTTL DHCPOpt = 37 // 1, byte
|
||||
DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32
|
||||
DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool
|
||||
DHCPOptNISDomain DHCPOpt = 40 // n, string
|
||||
DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP
|
||||
DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP
|
||||
DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated.
|
||||
DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP
|
||||
DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP
|
||||
DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte
|
||||
DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string
|
||||
DHCPOptXFontServer DHCPOpt = 48 // n, string
|
||||
DHCPOptXDisplayManager DHCPOpt = 49 // n, string
|
||||
DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP
|
||||
DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32
|
||||
DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3
|
||||
DHCPOptMessageType DHCPOpt = 53 // 1, 1-7
|
||||
DHCPOptServerID DHCPOpt = 54 // 4, net.IP
|
||||
DHCPOptParamsRequest DHCPOpt = 55 // n, []byte
|
||||
DHCPOptMessage DHCPOpt = 56 // n, 3
|
||||
DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16
|
||||
DHCPOptT1 DHCPOpt = 58 // 4, uint32
|
||||
DHCPOptT2 DHCPOpt = 59 // 4, uint32
|
||||
DHCPOptClassID DHCPOpt = 60 // n, []byte
|
||||
DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte
|
||||
DHCPOptDomainSearch DHCPOpt = 119 // n, string
|
||||
DHCPOptSIPServers DHCPOpt = 120 // n, url
|
||||
DHCPOptClasslessStaticRoute DHCPOpt = 121 //
|
||||
DHCPOptEnd DHCPOpt = 255
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPOpt.
|
||||
func (o DHCPOpt) String() string {
|
||||
switch o {
|
||||
case DHCPOptPad:
|
||||
return "(padding)"
|
||||
case DHCPOptSubnetMask:
|
||||
return "SubnetMask"
|
||||
case DHCPOptTimeOffset:
|
||||
return "TimeOffset"
|
||||
case DHCPOptRouter:
|
||||
return "Router"
|
||||
case DHCPOptTimeServer:
|
||||
return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
|
||||
case DHCPOptNameServer:
|
||||
return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
|
||||
case DHCPOptDNS:
|
||||
return "DNS"
|
||||
case DHCPOptLogServer:
|
||||
return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
|
||||
case DHCPOptCookieServer:
|
||||
return "CookieServer"
|
||||
case DHCPOptLPRServer:
|
||||
return "LPRServer"
|
||||
case DHCPOptImpressServer:
|
||||
return "ImpressServer"
|
||||
case DHCPOptResLocServer:
|
||||
return "ResourceLocationServer"
|
||||
case DHCPOptHostname:
|
||||
return "Hostname"
|
||||
case DHCPOptBootfileSize:
|
||||
return "BootfileSize"
|
||||
case DHCPOptMeritDumpFile:
|
||||
return "MeritDumpFile"
|
||||
case DHCPOptDomainName:
|
||||
return "DomainName"
|
||||
case DHCPOptSwapServer:
|
||||
return "SwapServer"
|
||||
case DHCPOptRootPath:
|
||||
return "RootPath"
|
||||
case DHCPOptExtensionsPath:
|
||||
return "ExtensionsPath"
|
||||
case DHCPOptIPForwarding:
|
||||
return "IPForwarding"
|
||||
case DHCPOptSourceRouting:
|
||||
return "SourceRouting"
|
||||
case DHCPOptPolicyFilter:
|
||||
return "PolicyFilter"
|
||||
case DHCPOptDatagramMTU:
|
||||
return "DatagramMTU"
|
||||
case DHCPOptDefaultTTL:
|
||||
return "DefaultTTL"
|
||||
case DHCPOptPathMTUAgingTimeout:
|
||||
return "PathMTUAgingTimeout"
|
||||
case DHCPOptPathPlateuTableOption:
|
||||
return "PathPlateuTableOption"
|
||||
case DHCPOptInterfaceMTU:
|
||||
return "InterfaceMTU"
|
||||
case DHCPOptAllSubsLocal:
|
||||
return "AllSubsLocal"
|
||||
case DHCPOptBroadcastAddr:
|
||||
return "BroadcastAddress"
|
||||
case DHCPOptMaskDiscovery:
|
||||
return "MaskDiscovery"
|
||||
case DHCPOptMaskSupplier:
|
||||
return "MaskSupplier"
|
||||
case DHCPOptRouterDiscovery:
|
||||
return "RouterDiscovery"
|
||||
case DHCPOptSolicitAddr:
|
||||
return "SolicitAddr"
|
||||
case DHCPOptStaticRoute:
|
||||
return "StaticRoute"
|
||||
case DHCPOptARPTrailers:
|
||||
return "ARPTrailers"
|
||||
case DHCPOptARPTimeout:
|
||||
return "ARPTimeout"
|
||||
case DHCPOptEthernetEncap:
|
||||
return "EthernetEncap"
|
||||
case DHCPOptTCPTTL:
|
||||
return "TCPTTL"
|
||||
case DHCPOptTCPKeepAliveInt:
|
||||
return "TCPKeepAliveInt"
|
||||
case DHCPOptTCPKeepAliveGarbage:
|
||||
return "TCPKeepAliveGarbage"
|
||||
case DHCPOptNISDomain:
|
||||
return "NISDomain"
|
||||
case DHCPOptNISServers:
|
||||
return "NISServers"
|
||||
case DHCPOptNTPServers:
|
||||
return "NTPServers"
|
||||
case DHCPOptVendorOption:
|
||||
return "VendorOption"
|
||||
case DHCPOptNetBIOSTCPNS:
|
||||
return "NetBIOSOverTCPNS"
|
||||
case DHCPOptNetBIOSTCPDDS:
|
||||
return "NetBiosOverTCPDDS"
|
||||
case DHCPOptNETBIOSTCPNodeType:
|
||||
return "NetBIOSOverTCPNodeType"
|
||||
case DHCPOptNetBIOSTCPScope:
|
||||
return "NetBIOSOverTCPScope"
|
||||
case DHCPOptXFontServer:
|
||||
return "XFontServer"
|
||||
case DHCPOptXDisplayManager:
|
||||
return "XDisplayManager"
|
||||
case DHCPOptEnd:
|
||||
return "(end)"
|
||||
case DHCPOptSIPServers:
|
||||
return "SipServers"
|
||||
case DHCPOptRequestIP:
|
||||
return "RequestIP"
|
||||
case DHCPOptLeaseTime:
|
||||
return "LeaseTime"
|
||||
case DHCPOptExtOptions:
|
||||
return "ExtOpts"
|
||||
case DHCPOptMessageType:
|
||||
return "MessageType"
|
||||
case DHCPOptServerID:
|
||||
return "ServerID"
|
||||
case DHCPOptParamsRequest:
|
||||
return "ParamsRequest"
|
||||
case DHCPOptMessage:
|
||||
return "Message"
|
||||
case DHCPOptMaxMessageSize:
|
||||
return "MaxDHCPSize"
|
||||
case DHCPOptT1:
|
||||
return "Timer1"
|
||||
case DHCPOptT2:
|
||||
return "Timer2"
|
||||
case DHCPOptClassID:
|
||||
return "ClassID"
|
||||
case DHCPOptClientID:
|
||||
return "ClientID"
|
||||
case DHCPOptDomainSearch:
|
||||
return "DomainSearch"
|
||||
case DHCPOptClasslessStaticRoute:
|
||||
return "ClasslessStaticRoute"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPOption rerpresents a DHCP option.
|
||||
type DHCPOption struct {
|
||||
Type DHCPOpt
|
||||
Length uint8
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// String returns a string version of a DHCP Option.
|
||||
func (o DHCPOption) String() string {
|
||||
switch o.Type {
|
||||
|
||||
case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
|
||||
DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
|
||||
DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
|
||||
|
||||
case DHCPOptMessageType:
|
||||
if len(o.Data) != 1 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
|
||||
|
||||
case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
|
||||
DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
|
||||
if len(o.Data) < 4 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
|
||||
|
||||
case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
|
||||
DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
|
||||
if len(o.Data) != 4 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%d)", o.Type,
|
||||
uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
|
||||
|
||||
case DHCPOptParamsRequest:
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
|
||||
for i, v := range o.Data {
|
||||
buf.WriteString(DHCPOpt(v).String())
|
||||
if i+1 != len(o.Data) {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
return buf.String()
|
||||
|
||||
default:
|
||||
return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDHCPOption constructs a new DHCPOption with a given type and data.
|
||||
func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
|
||||
o := DHCPOption{Type: t}
|
||||
if data != nil {
|
||||
o.Data = data
|
||||
o.Length = uint8(len(data))
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *DHCPOption) encode(b []byte) error {
|
||||
switch o.Type {
|
||||
case DHCPOptPad, DHCPOptEnd:
|
||||
b[0] = byte(o.Type)
|
||||
default:
|
||||
b[0] = byte(o.Type)
|
||||
b[1] = o.Length
|
||||
copy(b[2:], o.Data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DHCPOption) decode(data []byte) error {
|
||||
if len(data) < 1 {
|
||||
// Pad/End have a length of 1
|
||||
return DecOptionNotEnoughData
|
||||
}
|
||||
o.Type = DHCPOpt(data[0])
|
||||
switch o.Type {
|
||||
case DHCPOptPad, DHCPOptEnd:
|
||||
o.Data = nil
|
||||
default:
|
||||
if len(data) < 2 {
|
||||
return DecOptionNotEnoughData
|
||||
}
|
||||
o.Length = data[1]
|
||||
if int(o.Length) > len(data[2:]) {
|
||||
return DecOptionMalformed
|
||||
}
|
||||
o.Data = data[2 : 2+int(o.Length)]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
|
||||
type DHCPv4Error string
|
||||
|
||||
// DHCPv4Error implements error interface.
|
||||
func (d DHCPv4Error) Error() string {
|
||||
return string(d)
|
||||
}
|
||||
|
||||
const (
|
||||
// DecOptionNotEnoughData is returned when there is not enough data during option's decode process
|
||||
DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
|
||||
// DecOptionMalformed is returned when the option is malformed
|
||||
DecOptionMalformed = DHCPv4Error("Option is malformed")
|
||||
// InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
|
||||
InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
|
||||
)
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
// Copyright 2018 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DHCPv6MsgType represents a DHCPv6 operation
|
||||
type DHCPv6MsgType byte
|
||||
|
||||
// Constants that represent DHCP operations
|
||||
const (
|
||||
DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
|
||||
DHCPv6MsgTypeSolicit
|
||||
DHCPv6MsgTypeAdverstise
|
||||
DHCPv6MsgTypeRequest
|
||||
DHCPv6MsgTypeConfirm
|
||||
DHCPv6MsgTypeRenew
|
||||
DHCPv6MsgTypeRebind
|
||||
DHCPv6MsgTypeReply
|
||||
DHCPv6MsgTypeRelease
|
||||
DHCPv6MsgTypeDecline
|
||||
DHCPv6MsgTypeReconfigure
|
||||
DHCPv6MsgTypeInformationRequest
|
||||
DHCPv6MsgTypeRelayForward
|
||||
DHCPv6MsgTypeRelayReply
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPv6MsgType.
|
||||
func (o DHCPv6MsgType) String() string {
|
||||
switch o {
|
||||
case DHCPv6MsgTypeUnspecified:
|
||||
return "Unspecified"
|
||||
case DHCPv6MsgTypeSolicit:
|
||||
return "Solicit"
|
||||
case DHCPv6MsgTypeAdverstise:
|
||||
return "Adverstise"
|
||||
case DHCPv6MsgTypeRequest:
|
||||
return "Request"
|
||||
case DHCPv6MsgTypeConfirm:
|
||||
return "Confirm"
|
||||
case DHCPv6MsgTypeRenew:
|
||||
return "Renew"
|
||||
case DHCPv6MsgTypeRebind:
|
||||
return "Rebind"
|
||||
case DHCPv6MsgTypeReply:
|
||||
return "Reply"
|
||||
case DHCPv6MsgTypeRelease:
|
||||
return "Release"
|
||||
case DHCPv6MsgTypeDecline:
|
||||
return "Decline"
|
||||
case DHCPv6MsgTypeReconfigure:
|
||||
return "Reconfigure"
|
||||
case DHCPv6MsgTypeInformationRequest:
|
||||
return "InformationRequest"
|
||||
case DHCPv6MsgTypeRelayForward:
|
||||
return "RelayForward"
|
||||
case DHCPv6MsgTypeRelayReply:
|
||||
return "RelayReply"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPv6 contains data for a single DHCP packet.
|
||||
type DHCPv6 struct {
|
||||
BaseLayer
|
||||
MsgType DHCPv6MsgType
|
||||
HopCount uint8
|
||||
LinkAddr net.IP
|
||||
PeerAddr net.IP
|
||||
TransactionID []byte
|
||||
Options DHCPv6Options
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDHCPv6
|
||||
func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("DHCPv6 length %d too short", len(data))
|
||||
}
|
||||
d.BaseLayer = BaseLayer{Contents: data}
|
||||
d.Options = d.Options[:0]
|
||||
d.MsgType = DHCPv6MsgType(data[0])
|
||||
|
||||
offset := 0
|
||||
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
|
||||
if len(data) < 34 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType)
|
||||
}
|
||||
d.HopCount = data[1]
|
||||
d.LinkAddr = net.IP(data[2:18])
|
||||
d.PeerAddr = net.IP(data[18:34])
|
||||
offset = 34
|
||||
} else {
|
||||
d.TransactionID = data[1:4]
|
||||
offset = 4
|
||||
}
|
||||
|
||||
stop := len(data)
|
||||
for offset < stop {
|
||||
o := DHCPv6Option{}
|
||||
if err := o.decode(data[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
d.Options = append(d.Options, o)
|
||||
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the length of a DHCPv6 packet.
|
||||
func (d *DHCPv6) Len() int {
|
||||
n := 1
|
||||
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
|
||||
n += 33
|
||||
} else {
|
||||
n += 3
|
||||
}
|
||||
|
||||
for _, o := range d.Options {
|
||||
n += int(o.Length) + 4 // 2 from option code, 2 from option length
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
plen := int(d.Len())
|
||||
|
||||
data, err := b.PrependBytes(plen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := 0
|
||||
data[0] = byte(d.MsgType)
|
||||
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
|
||||
data[1] = byte(d.HopCount)
|
||||
copy(data[2:18], d.LinkAddr.To16())
|
||||
copy(data[18:34], d.PeerAddr.To16())
|
||||
offset = 34
|
||||
} else {
|
||||
copy(data[1:4], d.TransactionID)
|
||||
offset = 4
|
||||
}
|
||||
|
||||
if len(d.Options) > 0 {
|
||||
for _, o := range d.Options {
|
||||
if err := o.encode(data[offset:], opts); err != nil {
|
||||
return err
|
||||
}
|
||||
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *DHCPv6) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDHCPv6
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *DHCPv6) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
|
||||
dhcp := &DHCPv6{}
|
||||
err := dhcp.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(dhcp)
|
||||
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||
}
|
||||
|
||||
// DHCPv6StatusCode represents a DHCP status code - RFC-3315
|
||||
type DHCPv6StatusCode uint16
|
||||
|
||||
// Constants for the DHCPv6StatusCode.
|
||||
const (
|
||||
DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
|
||||
DHCPv6StatusCodeUnspecFail
|
||||
DHCPv6StatusCodeNoAddrsAvail
|
||||
DHCPv6StatusCodeNoBinding
|
||||
DHCPv6StatusCodeNotOnLink
|
||||
DHCPv6StatusCodeUseMulticast
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPv6StatusCode.
|
||||
func (o DHCPv6StatusCode) String() string {
|
||||
switch o {
|
||||
case DHCPv6StatusCodeSuccess:
|
||||
return "Success"
|
||||
case DHCPv6StatusCodeUnspecFail:
|
||||
return "UnspecifiedFailure"
|
||||
case DHCPv6StatusCodeNoAddrsAvail:
|
||||
return "NoAddressAvailable"
|
||||
case DHCPv6StatusCodeNoBinding:
|
||||
return "NoBinding"
|
||||
case DHCPv6StatusCodeNotOnLink:
|
||||
return "NotOnLink"
|
||||
case DHCPv6StatusCodeUseMulticast:
|
||||
return "UseMulticast"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
|
||||
type DHCPv6DUIDType uint16
|
||||
|
||||
// Constants for the DHCPv6DUIDType.
|
||||
const (
|
||||
DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
|
||||
DHCPv6DUIDTypeEN
|
||||
DHCPv6DUIDTypeLL
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPv6DUIDType.
|
||||
func (o DHCPv6DUIDType) String() string {
|
||||
switch o {
|
||||
case DHCPv6DUIDTypeLLT:
|
||||
return "LLT"
|
||||
case DHCPv6DUIDTypeEN:
|
||||
return "EN"
|
||||
case DHCPv6DUIDTypeLL:
|
||||
return "LL"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
|
||||
type DHCPv6DUID struct {
|
||||
Type DHCPv6DUIDType
|
||||
// LLT, LL
|
||||
HardwareType []byte
|
||||
// EN
|
||||
EnterpriseNumber []byte
|
||||
// LLT
|
||||
Time []byte
|
||||
// LLT, LL
|
||||
LinkLayerAddress net.HardwareAddr
|
||||
// EN
|
||||
Identifier []byte
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
|
||||
func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
|
||||
if len(data) < 2 {
|
||||
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
|
||||
}
|
||||
|
||||
d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
|
||||
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
|
||||
if len(data) < 4 {
|
||||
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
|
||||
}
|
||||
d.HardwareType = data[2:4]
|
||||
}
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
if len(data) < 8 {
|
||||
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
|
||||
}
|
||||
d.Time = data[4:8]
|
||||
d.LinkLayerAddress = net.HardwareAddr(data[8:])
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
if len(data) < 6 {
|
||||
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
|
||||
}
|
||||
d.EnterpriseNumber = data[2:6]
|
||||
d.Identifier = data[6:]
|
||||
} else { // DHCPv6DUIDTypeLL
|
||||
if len(data) < 4 {
|
||||
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
|
||||
}
|
||||
d.LinkLayerAddress = net.HardwareAddr(data[4:])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes the DHCPv6DUID in a slice of bytes
|
||||
func (d *DHCPv6DUID) Encode() []byte {
|
||||
length := d.Len()
|
||||
data := make([]byte, length)
|
||||
binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
|
||||
copy(data[2:4], d.HardwareType)
|
||||
}
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
copy(data[4:8], d.Time)
|
||||
copy(data[8:], d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
copy(data[2:6], d.EnterpriseNumber)
|
||||
copy(data[6:], d.Identifier)
|
||||
} else {
|
||||
copy(data[4:], d.LinkLayerAddress)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Len returns the length of the DHCPv6DUID, respecting the type
|
||||
func (d *DHCPv6DUID) Len() int {
|
||||
length := 2 // d.Type
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
|
||||
} else { // LL
|
||||
length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
func (d *DHCPv6DUID) String() string {
|
||||
duid := "Type: " + d.Type.String() + ", "
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
|
||||
} else { // DHCPv6DUIDTypeLL
|
||||
duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
|
||||
}
|
||||
return duid
|
||||
}
|
||||
|
||||
func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
|
||||
duid := &DHCPv6DUID{}
|
||||
err := duid.DecodeFromBytes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return duid, nil
|
||||
}
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
|
||||
type DHCPv6Opt uint16
|
||||
|
||||
// Constants for the DHCPv6Opt options.
|
||||
const (
|
||||
DHCPv6OptClientID DHCPv6Opt = 1
|
||||
DHCPv6OptServerID DHCPv6Opt = 2
|
||||
DHCPv6OptIANA DHCPv6Opt = 3
|
||||
DHCPv6OptIATA DHCPv6Opt = 4
|
||||
DHCPv6OptIAAddr DHCPv6Opt = 5
|
||||
DHCPv6OptOro DHCPv6Opt = 6
|
||||
DHCPv6OptPreference DHCPv6Opt = 7
|
||||
DHCPv6OptElapsedTime DHCPv6Opt = 8
|
||||
DHCPv6OptRelayMessage DHCPv6Opt = 9
|
||||
DHCPv6OptAuth DHCPv6Opt = 11
|
||||
DHCPv6OptUnicast DHCPv6Opt = 12
|
||||
DHCPv6OptStatusCode DHCPv6Opt = 13
|
||||
DHCPv6OptRapidCommit DHCPv6Opt = 14
|
||||
DHCPv6OptUserClass DHCPv6Opt = 15
|
||||
DHCPv6OptVendorClass DHCPv6Opt = 16
|
||||
DHCPv6OptVendorOpts DHCPv6Opt = 17
|
||||
DHCPv6OptInterfaceID DHCPv6Opt = 18
|
||||
DHCPv6OptReconfigureMessage DHCPv6Opt = 19
|
||||
DHCPv6OptReconfigureAccept DHCPv6Opt = 20
|
||||
|
||||
// RFC 3319 Session Initiation Protocol (SIP)
|
||||
DHCPv6OptSIPServersDomainList DHCPv6Opt = 21
|
||||
DHCPv6OptSIPServersAddressList DHCPv6Opt = 22
|
||||
|
||||
// RFC 3646 DNS Configuration
|
||||
DHCPv6OptDNSServers DHCPv6Opt = 23
|
||||
DHCPv6OptDomainList DHCPv6Opt = 24
|
||||
|
||||
// RFC 3633 Prefix Delegation
|
||||
DHCPv6OptIAPD DHCPv6Opt = 25
|
||||
DHCPv6OptIAPrefix DHCPv6Opt = 26
|
||||
|
||||
// RFC 3898 Network Information Service (NIS)
|
||||
DHCPv6OptNISServers DHCPv6Opt = 27
|
||||
DHCPv6OptNISPServers DHCPv6Opt = 28
|
||||
DHCPv6OptNISDomainName DHCPv6Opt = 29
|
||||
DHCPv6OptNISPDomainName DHCPv6Opt = 30
|
||||
|
||||
// RFC 4075 Simple Network Time Protocol (SNTP)
|
||||
DHCPv6OptSNTPServers DHCPv6Opt = 31
|
||||
|
||||
// RFC 4242 Information Refresh Time Option
|
||||
DHCPv6OptInformationRefreshTime DHCPv6Opt = 32
|
||||
|
||||
// RFC 4280 Broadcast and Multicast Control Servers
|
||||
DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33
|
||||
DHCPv6OptBCMCSServerAddressList DHCPv6Opt = 34
|
||||
|
||||
// RFC 4776 Civic Address ConfigurationOption
|
||||
DHCPv6OptGeoconfCivic DHCPv6Opt = 36
|
||||
|
||||
// RFC 4649 Relay Agent Remote-ID
|
||||
DHCPv6OptRemoteID DHCPv6Opt = 37
|
||||
|
||||
// RFC 4580 Relay Agent Subscriber-ID
|
||||
DHCPv6OptSubscriberID DHCPv6Opt = 38
|
||||
|
||||
// RFC 4704 Client Full Qualified Domain Name (FQDN)
|
||||
DHCPv6OptClientFQDN DHCPv6Opt = 39
|
||||
|
||||
// RFC 5192 Protocol for Carrying Authentication for Network Access (PANA)
|
||||
DHCPv6OptPanaAgent DHCPv6Opt = 40
|
||||
|
||||
// RFC 4833 Timezone Options
|
||||
DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41
|
||||
DHCPv6OptNewTZDBTimezone DHCPv6Opt = 42
|
||||
|
||||
// RFC 4994 Relay Agent Echo Request
|
||||
DHCPv6OptEchoRequestOption DHCPv6Opt = 43
|
||||
|
||||
// RFC 5007 Leasequery
|
||||
DHCPv6OptLQQuery DHCPv6Opt = 44
|
||||
DHCPv6OptCLTTime DHCPv6Opt = 45
|
||||
DHCPv6OptClientData DHCPv6Opt = 46
|
||||
DHCPv6OptLQRelayData DHCPv6Opt = 47
|
||||
DHCPv6OptLQClientLink DHCPv6Opt = 48
|
||||
|
||||
// RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6)
|
||||
DHCPv6OptMIP6HNIDF DHCPv6Opt = 49
|
||||
DHCPv6OptMIP6VDINF DHCPv6Opt = 50
|
||||
DHCPv6OptMIP6IDINF DHCPv6Opt = 69
|
||||
DHCPv6OptMIP6UDINF DHCPv6Opt = 70
|
||||
DHCPv6OptMIP6HNP DHCPv6Opt = 71
|
||||
DHCPv6OptMIP6HAA DHCPv6Opt = 72
|
||||
DHCPv6OptMIP6HAF DHCPv6Opt = 73
|
||||
|
||||
// RFC 5223 Discovering Location-to-Service Translation (LoST) Servers
|
||||
DHCPv6OptV6LOST DHCPv6Opt = 51
|
||||
|
||||
// RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP)
|
||||
DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52
|
||||
|
||||
// RFC 5460 Bulk Leasequery
|
||||
DHCPv6OptRelayID DHCPv6Opt = 53
|
||||
|
||||
// RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery
|
||||
DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54
|
||||
DHCPv6OptIPv6FQDNMoS DHCPv6Opt = 55
|
||||
|
||||
// RFC 5908 NTP Server Option
|
||||
DHCPv6OptNTPServer DHCPv6Opt = 56
|
||||
|
||||
// RFC 5986 Discovering the Local Location Information Server (LIS)
|
||||
DHCPv6OptV6AccessDomain DHCPv6Opt = 57
|
||||
|
||||
// RFC 5986 SIP User Agent
|
||||
DHCPv6OptSIPUACSList DHCPv6Opt = 58
|
||||
|
||||
// RFC 5970 Options for Network Boot
|
||||
DHCPv6OptBootFileURL DHCPv6Opt = 59
|
||||
DHCPv6OptBootFileParam DHCPv6Opt = 60
|
||||
DHCPv6OptClientArchType DHCPv6Opt = 61
|
||||
DHCPv6OptNII DHCPv6Opt = 62
|
||||
|
||||
// RFC 6225 Coordinate-Based Location Configuration Information
|
||||
DHCPv6OptGeolocation DHCPv6Opt = 63
|
||||
|
||||
// RFC 6334 Dual-Stack Lite
|
||||
DHCPv6OptAFTRName DHCPv6Opt = 64
|
||||
|
||||
// RFC 6440 EAP Re-authentication Protocol (ERP)
|
||||
DHCPv6OptERPLocalDomainName DHCPv6Opt = 65
|
||||
|
||||
// RFC 6422 Relay-Supplied DHCP Options
|
||||
DHCPv6OptRSOO DHCPv6Opt = 66
|
||||
|
||||
// RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation
|
||||
DHCPv6OptPDExclude DHCPv6Opt = 67
|
||||
|
||||
// RFC 6607 Virtual Subnet Selection
|
||||
DHCPv6OptVSS DHCPv6Opt = 68
|
||||
|
||||
// RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
|
||||
DHCPv6OptRDNSSSelection DHCPv6Opt = 74
|
||||
|
||||
// RFC 6784 Kerberos Options for DHCPv6
|
||||
DHCPv6OptKRBPrincipalName DHCPv6Opt = 75
|
||||
DHCPv6OptKRBRealmName DHCPv6Opt = 76
|
||||
DHCPv6OptKRBKDC DHCPv6Opt = 77
|
||||
|
||||
// RFC 6939 Client Link-Layer Address Option
|
||||
DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79
|
||||
|
||||
// RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents
|
||||
DHCPv6OptLinkAddress DHCPv6Opt = 80
|
||||
|
||||
// RFC 7037 RADIUS Option for the DHCPv6 Relay Agent
|
||||
DHCPv6OptRADIUS DHCPv6Opt = 81
|
||||
|
||||
// RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT
|
||||
DHCPv6OptSolMaxRt DHCPv6Opt = 82
|
||||
DHCPv6OptInfMaxRt DHCPv6Opt = 83
|
||||
|
||||
// RFC 7078 Distributing Address Selection Policy
|
||||
DHCPv6OptAddrSel DHCPv6Opt = 84
|
||||
DHCPv6OptAddrSelTable DHCPv6Opt = 85
|
||||
|
||||
// RFC 7291 DHCP Options for the Port Control Protocol (PCP)
|
||||
DHCPv6OptV6PCPServer DHCPv6Opt = 86
|
||||
|
||||
// RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport
|
||||
DHCPv6OptDHCPv4Message DHCPv6Opt = 87
|
||||
DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88
|
||||
|
||||
// RFC 7598 Configuration of Softwire Address and Port-Mapped Clients
|
||||
DHCPv6OptS46Rule DHCPv6Opt = 89
|
||||
DHCPv6OptS46BR DHCPv6Opt = 90
|
||||
DHCPv6OptS46DMR DHCPv6Opt = 91
|
||||
DHCPv6OptS46V4V4Bind DHCPv6Opt = 92
|
||||
DHCPv6OptS46PortParameters DHCPv6Opt = 93
|
||||
DHCPv6OptS46ContMAPE DHCPv6Opt = 94
|
||||
DHCPv6OptS46ContMAPT DHCPv6Opt = 95
|
||||
DHCPv6OptS46ContLW DHCPv6Opt = 96
|
||||
|
||||
// RFC 7600 IPv4 Residual Deployment via IPv6
|
||||
DHCPv6Opt4RD DHCPv6Opt = 97
|
||||
DHCPv6Opt4RDMapRule DHCPv6Opt = 98
|
||||
DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99
|
||||
|
||||
// RFC 7653 Active Leasequery
|
||||
DHCPv6OptLQBaseTime DHCPv6Opt = 100
|
||||
DHCPv6OptLQStartTime DHCPv6Opt = 101
|
||||
DHCPv6OptLQEndTime DHCPv6Opt = 102
|
||||
|
||||
// RFC 7710 Captive-Portal Identification
|
||||
DHCPv6OptCaptivePortal DHCPv6Opt = 103
|
||||
|
||||
// RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration
|
||||
DHCPv6OptMPLParameters DHCPv6Opt = 104
|
||||
|
||||
// RFC 7839 Access-Network-Identifier (ANI)
|
||||
DHCPv6OptANIATT DHCPv6Opt = 105
|
||||
DHCPv6OptANINetworkName DHCPv6Opt = 106
|
||||
DHCPv6OptANIAPName DHCPv6Opt = 107
|
||||
DHCPv6OptANIAPBSSID DHCPv6Opt = 108
|
||||
DHCPv6OptANIOperatorID DHCPv6Opt = 109
|
||||
DHCPv6OptANIOperatorRealm DHCPv6Opt = 110
|
||||
|
||||
// RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE)
|
||||
DHCPv6OptS46Priority DHCPv6Opt = 111
|
||||
|
||||
// draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD)
|
||||
DHCPv6OptMUDURLV6 DHCPv6Opt = 112
|
||||
|
||||
// RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes
|
||||
DHCPv6OptV6Prefix64 DHCPv6Opt = 113
|
||||
|
||||
// RFC 8156 DHCPv6 Failover Protocol
|
||||
DHCPv6OptFBindingStatus DHCPv6Opt = 114
|
||||
DHCPv6OptFConnectFlags DHCPv6Opt = 115
|
||||
DHCPv6OptFDNSRemovalInfo DHCPv6Opt = 116
|
||||
DHCPv6OptFDNSHostName DHCPv6Opt = 117
|
||||
DHCPv6OptFDNSZoneName DHCPv6Opt = 118
|
||||
DHCPv6OptFDNSFlags DHCPv6Opt = 119
|
||||
DHCPv6OptFExpirationTime DHCPv6Opt = 120
|
||||
DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121
|
||||
DHCPv6OptFMCLT DHCPv6Opt = 122
|
||||
DHCPv6OptFPartnerLifetime DHCPv6Opt = 123
|
||||
DHCPv6OptFPartnerLifetimeSent DHCPv6Opt = 124
|
||||
DHCPv6OptFPartnerDownTime DHCPv6Opt = 125
|
||||
DHCPv6OptFPartnerRawCltTime DHCPv6Opt = 126
|
||||
DHCPv6OptFProtocolVersion DHCPv6Opt = 127
|
||||
DHCPv6OptFKeepaliveTime DHCPv6Opt = 128
|
||||
DHCPv6OptFReconfigureData DHCPv6Opt = 129
|
||||
DHCPv6OptFRelationshipName DHCPv6Opt = 130
|
||||
DHCPv6OptFServerFlags DHCPv6Opt = 131
|
||||
DHCPv6OptFServerState DHCPv6Opt = 132
|
||||
DHCPv6OptFStartTimeOfState DHCPv6Opt = 133
|
||||
DHCPv6OptFStateExpirationTime DHCPv6Opt = 134
|
||||
|
||||
// RFC 8357 Generalized UDP Source Port for DHCP Relay
|
||||
DHCPv6OptRelayPort DHCPv6Opt = 135
|
||||
|
||||
// draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices
|
||||
DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136
|
||||
|
||||
// RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery
|
||||
DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPv6Opt.
|
||||
func (o DHCPv6Opt) String() string {
|
||||
switch o {
|
||||
case DHCPv6OptClientID:
|
||||
return "ClientID"
|
||||
case DHCPv6OptServerID:
|
||||
return "ServerID"
|
||||
case DHCPv6OptIANA:
|
||||
return "IA_NA"
|
||||
case DHCPv6OptIATA:
|
||||
return "IA_TA"
|
||||
case DHCPv6OptIAAddr:
|
||||
return "IAAddr"
|
||||
case DHCPv6OptOro:
|
||||
return "Oro"
|
||||
case DHCPv6OptPreference:
|
||||
return "Preference"
|
||||
case DHCPv6OptElapsedTime:
|
||||
return "ElapsedTime"
|
||||
case DHCPv6OptRelayMessage:
|
||||
return "RelayMessage"
|
||||
case DHCPv6OptAuth:
|
||||
return "Auth"
|
||||
case DHCPv6OptUnicast:
|
||||
return "Unicast"
|
||||
case DHCPv6OptStatusCode:
|
||||
return "StatusCode"
|
||||
case DHCPv6OptRapidCommit:
|
||||
return "RapidCommit"
|
||||
case DHCPv6OptUserClass:
|
||||
return "UserClass"
|
||||
case DHCPv6OptVendorClass:
|
||||
return "VendorClass"
|
||||
case DHCPv6OptVendorOpts:
|
||||
return "VendorOpts"
|
||||
case DHCPv6OptInterfaceID:
|
||||
return "InterfaceID"
|
||||
case DHCPv6OptReconfigureMessage:
|
||||
return "ReconfigureMessage"
|
||||
case DHCPv6OptReconfigureAccept:
|
||||
return "ReconfigureAccept"
|
||||
case DHCPv6OptSIPServersDomainList:
|
||||
return "SIPServersDomainList"
|
||||
case DHCPv6OptSIPServersAddressList:
|
||||
return "SIPServersAddressList"
|
||||
case DHCPv6OptDNSServers:
|
||||
return "DNSRecursiveNameServer"
|
||||
case DHCPv6OptDomainList:
|
||||
return "DomainSearchList"
|
||||
case DHCPv6OptIAPD:
|
||||
return "IdentityAssociationPrefixDelegation"
|
||||
case DHCPv6OptIAPrefix:
|
||||
return "IAPDPrefix"
|
||||
case DHCPv6OptNISServers:
|
||||
return "NISServers"
|
||||
case DHCPv6OptNISPServers:
|
||||
return "NISv2Servers"
|
||||
case DHCPv6OptNISDomainName:
|
||||
return "NISDomainName"
|
||||
case DHCPv6OptNISPDomainName:
|
||||
return "NISv2DomainName"
|
||||
case DHCPv6OptSNTPServers:
|
||||
return "SNTPServers"
|
||||
case DHCPv6OptInformationRefreshTime:
|
||||
return "InformationRefreshTime"
|
||||
case DHCPv6OptBCMCSServerDomainNameList:
|
||||
return "BCMCSControlServersDomainNameList"
|
||||
case DHCPv6OptBCMCSServerAddressList:
|
||||
return "BCMCSControlServersAddressList"
|
||||
case DHCPv6OptGeoconfCivic:
|
||||
return "CivicAddress"
|
||||
case DHCPv6OptRemoteID:
|
||||
return "RelayAgentRemoteID"
|
||||
case DHCPv6OptSubscriberID:
|
||||
return "RelayAgentSubscriberID"
|
||||
case DHCPv6OptClientFQDN:
|
||||
return "ClientFQDN"
|
||||
case DHCPv6OptPanaAgent:
|
||||
return "PANAAuthenticationAgent"
|
||||
case DHCPv6OptNewPOSIXTimezone:
|
||||
return "NewPOSIXTimezone"
|
||||
case DHCPv6OptNewTZDBTimezone:
|
||||
return "NewTZDBTimezone"
|
||||
case DHCPv6OptEchoRequestOption:
|
||||
return "EchoRequest"
|
||||
case DHCPv6OptLQQuery:
|
||||
return "LeasequeryQuery"
|
||||
case DHCPv6OptClientData:
|
||||
return "LeasequeryClientData"
|
||||
case DHCPv6OptCLTTime:
|
||||
return "LeasequeryClientLastTransactionTime"
|
||||
case DHCPv6OptLQRelayData:
|
||||
return "LeasequeryRelayData"
|
||||
case DHCPv6OptLQClientLink:
|
||||
return "LeasequeryClientLink"
|
||||
case DHCPv6OptMIP6HNIDF:
|
||||
return "MIPv6HomeNetworkIDFQDN"
|
||||
case DHCPv6OptMIP6VDINF:
|
||||
return "MIPv6VisitedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6IDINF:
|
||||
return "MIPv6IdentifiedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6UDINF:
|
||||
return "MIPv6UnrestrictedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6HNP:
|
||||
return "MIPv6HomeNetworkPrefix"
|
||||
case DHCPv6OptMIP6HAA:
|
||||
return "MIPv6HomeAgentAddress"
|
||||
case DHCPv6OptMIP6HAF:
|
||||
return "MIPv6HomeAgentFQDN"
|
||||
case DHCPv6OptV6LOST:
|
||||
return "LoST Server"
|
||||
case DHCPv6OptCAPWAPACV6:
|
||||
return "CAPWAPAccessControllerV6"
|
||||
case DHCPv6OptRelayID:
|
||||
return "LeasequeryRelayID"
|
||||
case DHCPv6OptIPv6AddressMoS:
|
||||
return "MoSIPv6Address"
|
||||
case DHCPv6OptIPv6FQDNMoS:
|
||||
return "MoSDomainNameList"
|
||||
case DHCPv6OptNTPServer:
|
||||
return "NTPServer"
|
||||
case DHCPv6OptV6AccessDomain:
|
||||
return "AccessNetworkDomainName"
|
||||
case DHCPv6OptSIPUACSList:
|
||||
return "SIPUserAgentConfigurationServiceDomains"
|
||||
case DHCPv6OptBootFileURL:
|
||||
return "BootFileURL"
|
||||
case DHCPv6OptBootFileParam:
|
||||
return "BootFileParameters"
|
||||
case DHCPv6OptClientArchType:
|
||||
return "ClientSystemArchitectureType"
|
||||
case DHCPv6OptNII:
|
||||
return "ClientNetworkInterfaceIdentifier"
|
||||
case DHCPv6OptGeolocation:
|
||||
return "Geolocation"
|
||||
case DHCPv6OptAFTRName:
|
||||
return "AFTRName"
|
||||
case DHCPv6OptERPLocalDomainName:
|
||||
return "AFTRName"
|
||||
case DHCPv6OptRSOO:
|
||||
return "RSOOption"
|
||||
case DHCPv6OptPDExclude:
|
||||
return "PrefixExclude"
|
||||
case DHCPv6OptVSS:
|
||||
return "VirtualSubnetSelection"
|
||||
case DHCPv6OptRDNSSSelection:
|
||||
return "RDNSSSelection"
|
||||
case DHCPv6OptKRBPrincipalName:
|
||||
return "KerberosPrincipalName"
|
||||
case DHCPv6OptKRBRealmName:
|
||||
return "KerberosRealmName"
|
||||
case DHCPv6OptKRBKDC:
|
||||
return "KerberosKDC"
|
||||
case DHCPv6OptClientLinkLayerAddress:
|
||||
return "ClientLinkLayerAddress"
|
||||
case DHCPv6OptLinkAddress:
|
||||
return "LinkAddress"
|
||||
case DHCPv6OptRADIUS:
|
||||
return "RADIUS"
|
||||
case DHCPv6OptSolMaxRt:
|
||||
return "SolMaxRt"
|
||||
case DHCPv6OptInfMaxRt:
|
||||
return "InfMaxRt"
|
||||
case DHCPv6OptAddrSel:
|
||||
return "AddressSelection"
|
||||
case DHCPv6OptAddrSelTable:
|
||||
return "AddressSelectionTable"
|
||||
case DHCPv6OptV6PCPServer:
|
||||
return "PCPServer"
|
||||
case DHCPv6OptDHCPv4Message:
|
||||
return "DHCPv4Message"
|
||||
case DHCPv6OptDHCPv4OverDHCPv6Server:
|
||||
return "DHCP4o6ServerAddress"
|
||||
case DHCPv6OptS46Rule:
|
||||
return "S46Rule"
|
||||
case DHCPv6OptS46BR:
|
||||
return "S46BR"
|
||||
case DHCPv6OptS46DMR:
|
||||
return "S46DMR"
|
||||
case DHCPv6OptS46V4V4Bind:
|
||||
return "S46IPv4IPv6AddressBinding"
|
||||
case DHCPv6OptS46PortParameters:
|
||||
return "S46PortParameters"
|
||||
case DHCPv6OptS46ContMAPE:
|
||||
return "S46MAPEContainer"
|
||||
case DHCPv6OptS46ContMAPT:
|
||||
return "S46MAPTContainer"
|
||||
case DHCPv6OptS46ContLW:
|
||||
return "S46Lightweight4Over6Container"
|
||||
case DHCPv6Opt4RD:
|
||||
return "4RD"
|
||||
case DHCPv6Opt4RDMapRule:
|
||||
return "4RDMapRule"
|
||||
case DHCPv6Opt4RDNonMapRule:
|
||||
return "4RDNonMapRule"
|
||||
case DHCPv6OptLQBaseTime:
|
||||
return "LQBaseTime"
|
||||
case DHCPv6OptLQStartTime:
|
||||
return "LQStartTime"
|
||||
case DHCPv6OptLQEndTime:
|
||||
return "LQEndTime"
|
||||
case DHCPv6OptCaptivePortal:
|
||||
return "CaptivePortal"
|
||||
case DHCPv6OptMPLParameters:
|
||||
return "MPLParameterConfiguration"
|
||||
case DHCPv6OptANIATT:
|
||||
return "ANIAccessTechnologyType"
|
||||
case DHCPv6OptANINetworkName:
|
||||
return "ANINetworkName"
|
||||
case DHCPv6OptANIAPName:
|
||||
return "ANIAccessPointName"
|
||||
case DHCPv6OptANIAPBSSID:
|
||||
return "ANIAccessPointBSSID"
|
||||
case DHCPv6OptANIOperatorID:
|
||||
return "ANIOperatorIdentifier"
|
||||
case DHCPv6OptANIOperatorRealm:
|
||||
return "ANIOperatorRealm"
|
||||
case DHCPv6OptS46Priority:
|
||||
return "S64Priority"
|
||||
case DHCPv6OptMUDURLV6:
|
||||
return "ManufacturerUsageDescriptionURL"
|
||||
case DHCPv6OptV6Prefix64:
|
||||
return "V6Prefix64"
|
||||
case DHCPv6OptFBindingStatus:
|
||||
return "FailoverBindingStatus"
|
||||
case DHCPv6OptFConnectFlags:
|
||||
return "FailoverConnectFlags"
|
||||
case DHCPv6OptFDNSRemovalInfo:
|
||||
return "FailoverDNSRemovalInfo"
|
||||
case DHCPv6OptFDNSHostName:
|
||||
return "FailoverDNSHostName"
|
||||
case DHCPv6OptFDNSZoneName:
|
||||
return "FailoverDNSZoneName"
|
||||
case DHCPv6OptFDNSFlags:
|
||||
return "FailoverDNSFlags"
|
||||
case DHCPv6OptFExpirationTime:
|
||||
return "FailoverExpirationTime"
|
||||
case DHCPv6OptFMaxUnacknowledgedBNDUPD:
|
||||
return "FailoverMaxUnacknowledgedBNDUPDMessages"
|
||||
case DHCPv6OptFMCLT:
|
||||
return "FailoverMaximumClientLeadTime"
|
||||
case DHCPv6OptFPartnerLifetime:
|
||||
return "FailoverPartnerLifetime"
|
||||
case DHCPv6OptFPartnerLifetimeSent:
|
||||
return "FailoverPartnerLifetimeSent"
|
||||
case DHCPv6OptFPartnerDownTime:
|
||||
return "FailoverPartnerDownTime"
|
||||
case DHCPv6OptFPartnerRawCltTime:
|
||||
return "FailoverPartnerRawClientLeadTime"
|
||||
case DHCPv6OptFProtocolVersion:
|
||||
return "FailoverProtocolVersion"
|
||||
case DHCPv6OptFKeepaliveTime:
|
||||
return "FailoverKeepaliveTime"
|
||||
case DHCPv6OptFReconfigureData:
|
||||
return "FailoverReconfigureData"
|
||||
case DHCPv6OptFRelationshipName:
|
||||
return "FailoverRelationshipName"
|
||||
case DHCPv6OptFServerFlags:
|
||||
return "FailoverServerFlags"
|
||||
case DHCPv6OptFServerState:
|
||||
return "FailoverServerState"
|
||||
case DHCPv6OptFStartTimeOfState:
|
||||
return "FailoverStartTimeOfState"
|
||||
case DHCPv6OptFStateExpirationTime:
|
||||
return "FailoverStateExpirationTime"
|
||||
case DHCPv6OptRelayPort:
|
||||
return "RelayPort"
|
||||
case DHCPv6OptV6ZeroTouchRedirect:
|
||||
return "ZeroTouch"
|
||||
case DHCPv6OptIPV6AddressANDSF:
|
||||
return "ANDSFIPv6Address"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown(%d)", uint16(o))
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPv6Options is used to get nicely printed option lists which would normally
|
||||
// be cut off after 5 options.
|
||||
type DHCPv6Options []DHCPv6Option
|
||||
|
||||
// String returns a string version of the options list.
|
||||
func (o DHCPv6Options) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteByte('[')
|
||||
for i, opt := range o {
|
||||
buf.WriteString(opt.String())
|
||||
if i+1 != len(o) {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DHCPv6Option rerpresents a DHCP option.
|
||||
type DHCPv6Option struct {
|
||||
Code DHCPv6Opt
|
||||
Length uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// String returns a string version of a DHCP Option.
|
||||
func (o DHCPv6Option) String() string {
|
||||
switch o.Code {
|
||||
case DHCPv6OptClientID, DHCPv6OptServerID:
|
||||
duid, err := decodeDHCPv6DUID(o.Data)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Code)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
|
||||
case DHCPv6OptOro:
|
||||
options := ""
|
||||
for i := 0; i < int(o.Length); i += 2 {
|
||||
if options != "" {
|
||||
options += ","
|
||||
}
|
||||
option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
|
||||
options += option.String()
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
|
||||
default:
|
||||
return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
|
||||
func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
|
||||
o := DHCPv6Option{Code: code}
|
||||
if data != nil {
|
||||
o.Data = data
|
||||
o.Length = uint16(len(data))
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
|
||||
binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
|
||||
if opts.FixLengths {
|
||||
binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data)))
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(b[2:4], o.Length)
|
||||
}
|
||||
copy(b[4:], o.Data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DHCPv6Option) decode(data []byte) error {
|
||||
if len(data) < 4 {
|
||||
return errors.New("not enough data to decode")
|
||||
}
|
||||
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
|
||||
o.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
if len(data) < 4+int(o.Length) {
|
||||
return fmt.Errorf("dhcpv6 option size < length %d", 4+o.Length)
|
||||
}
|
||||
o.Data = data[4 : 4+o.Length]
|
||||
return nil
|
||||
}
|
||||
+1098
File diff suppressed because it is too large
Load Diff
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
/*
|
||||
Package layers provides decoding layers for many common protocols.
|
||||
|
||||
The layers package contains decode implementations for a number of different
|
||||
types of packet layers. Users of gopacket will almost always want to also use
|
||||
layers to actually decode packet data into useful pieces. To see the set of
|
||||
protocols that gopacket/layers is currently able to decode,
|
||||
look at the set of LayerTypes defined in the Variables sections. The
|
||||
layers package also defines endpoints for many of the common packet layers
|
||||
that have source/destination addresses associated with them, for example IPv4/6
|
||||
(IPs) and TCP/UDP (ports).
|
||||
Finally, layers contains a number of useful enumerations (IPProtocol,
|
||||
EthernetType, LinkType, PPPType, etc...). Many of these implement the
|
||||
gopacket.Decoder interface, so they can be passed into gopacket as decoders.
|
||||
|
||||
Most common protocol layers are named using acronyms or other industry-common
|
||||
names (IPv4, TCP, PPP). Some of the less common ones have their names expanded
|
||||
(CiscoDiscoveryProtocol).
|
||||
For certain protocols, sub-parts of the protocol are split out into their own
|
||||
layers (SCTP, for example). This is done mostly in cases where portions of the
|
||||
protocol may fulfill the capabilities of interesting layers (SCTPData implements
|
||||
ApplicationLayer, while base SCTP implements TransportLayer), or possibly
|
||||
because splitting a protocol into a few layers makes decoding easier.
|
||||
|
||||
This package is meant to be used with its parent,
|
||||
http://github.com/google/gopacket.
|
||||
|
||||
Port Types
|
||||
|
||||
Instead of using raw uint16 or uint8 values for ports, we use a different port
|
||||
type for every protocol, for example TCPPort and UDPPort. This allows us to
|
||||
override string behavior for each port, which we do by setting up port name
|
||||
maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with
|
||||
their protocol names, and their String function displays these names:
|
||||
|
||||
p := TCPPort(80)
|
||||
fmt.Printf("Number: %d String: %v", p, p)
|
||||
// Prints: "Number: 80 String: 80(http)"
|
||||
|
||||
Modifying Decode Behavior
|
||||
|
||||
layers links together decoding through its enumerations. For example, after
|
||||
decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder.
|
||||
All enumerations that act as decoders, like EthernetType, can be modified by
|
||||
users depending on their preferences. For example, if you have a spiffy new
|
||||
IPv4 decoder that works way better than the one built into layers, you can do
|
||||
this:
|
||||
|
||||
var mySpiffyIPv4Decoder gopacket.Decoder = ...
|
||||
layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder
|
||||
|
||||
This will make all future ethernet packets use your new decoder to decode IPv4
|
||||
packets, instead of the built-in decoder used by gopacket.
|
||||
*/
|
||||
package layers
|
||||
+2118
File diff suppressed because it is too large
Load Diff
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Dot1Q is the packet layer for 802.1Q VLAN headers.
|
||||
type Dot1Q struct {
|
||||
BaseLayer
|
||||
Priority uint8
|
||||
DropEligible bool
|
||||
VLANIdentifier uint16
|
||||
Type EthernetType
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDot1Q
|
||||
func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("802.1Q tag length %d too short", len(data))
|
||||
}
|
||||
d.Priority = (data[0] & 0xE0) >> 5
|
||||
d.DropEligible = data[0]&0x10 != 0
|
||||
d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
|
||||
d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *Dot1Q) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDot1Q
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *Dot1Q) NextLayerType() gopacket.LayerType {
|
||||
return d.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error {
|
||||
d := &Dot1Q{}
|
||||
return decodingLayerDecoder(d, data, p)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.VLANIdentifier > 0xFFF {
|
||||
return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier)
|
||||
}
|
||||
firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier
|
||||
if d.DropEligible {
|
||||
firstBytes |= 0x1000
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, firstBytes)
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type))
|
||||
return nil
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type EAPCode uint8
|
||||
type EAPType uint8
|
||||
|
||||
const (
|
||||
EAPCodeRequest EAPCode = 1
|
||||
EAPCodeResponse EAPCode = 2
|
||||
EAPCodeSuccess EAPCode = 3
|
||||
EAPCodeFailure EAPCode = 4
|
||||
|
||||
// EAPTypeNone means that this EAP layer has no Type or TypeData.
|
||||
// Success and Failure EAPs will have this set.
|
||||
EAPTypeNone EAPType = 0
|
||||
|
||||
EAPTypeIdentity EAPType = 1
|
||||
EAPTypeNotification EAPType = 2
|
||||
EAPTypeNACK EAPType = 3
|
||||
EAPTypeOTP EAPType = 4
|
||||
EAPTypeTokenCard EAPType = 5
|
||||
)
|
||||
|
||||
// EAP defines an Extensible Authentication Protocol (rfc 3748) layer.
|
||||
type EAP struct {
|
||||
BaseLayer
|
||||
Code EAPCode
|
||||
Id uint8
|
||||
Length uint16
|
||||
Type EAPType
|
||||
TypeData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAP.
|
||||
func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAP length %d too short", len(data))
|
||||
}
|
||||
e.Code = EAPCode(data[0])
|
||||
e.Id = data[1]
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
if len(data) < int(e.Length) {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAP length %d too short, %d expected", len(data), e.Length)
|
||||
}
|
||||
switch {
|
||||
case e.Length > 4:
|
||||
e.Type = EAPType(data[4])
|
||||
e.TypeData = data[5:]
|
||||
case e.Length == 4:
|
||||
e.Type = 0
|
||||
e.TypeData = nil
|
||||
default:
|
||||
return fmt.Errorf("invalid EAP length %d", e.Length)
|
||||
}
|
||||
e.BaseLayer.Contents = data[:e.Length]
|
||||
e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if opts.FixLengths {
|
||||
e.Length = uint16(len(e.TypeData) + 1)
|
||||
}
|
||||
size := len(e.TypeData) + 4
|
||||
if size > 4 {
|
||||
size++
|
||||
}
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = byte(e.Code)
|
||||
bytes[1] = e.Id
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
if size > 4 {
|
||||
bytes[4] = byte(e.Type)
|
||||
copy(bytes[5:], e.TypeData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func decodeEAP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EAPOL defines an EAP over LAN (802.1x) layer.
|
||||
type EAPOL struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
Type EAPOLType
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAPOL.
|
||||
func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAPOL length %d too short", len(data))
|
||||
}
|
||||
e.Version = data[0]
|
||||
e.Type = EAPOLType(data[1])
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
e.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer
|
||||
func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, _ := b.PrependBytes(4)
|
||||
bytes[0] = e.Version
|
||||
bytes[1] = byte(e.Type)
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAPOL) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAPOL
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAPOL) NextLayerType() gopacket.LayerType {
|
||||
return e.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAPOL{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
|
||||
// EAPOLKeyDescriptorType is an enumeration of key descriptor types
|
||||
// as specified by 802.1x in the EAPOL-Key frame
|
||||
type EAPOLKeyDescriptorType uint8
|
||||
|
||||
// Enumeration of EAPOLKeyDescriptorType
|
||||
const (
|
||||
EAPOLKeyDescriptorTypeRC4 EAPOLKeyDescriptorType = 1
|
||||
EAPOLKeyDescriptorTypeDot11 EAPOLKeyDescriptorType = 2
|
||||
EAPOLKeyDescriptorTypeWPA EAPOLKeyDescriptorType = 254
|
||||
)
|
||||
|
||||
func (kdt EAPOLKeyDescriptorType) String() string {
|
||||
switch kdt {
|
||||
case EAPOLKeyDescriptorTypeRC4:
|
||||
return "RC4"
|
||||
case EAPOLKeyDescriptorTypeDot11:
|
||||
return "802.11"
|
||||
case EAPOLKeyDescriptorTypeWPA:
|
||||
return "WPA"
|
||||
default:
|
||||
return fmt.Sprintf("unknown descriptor type %d", kdt)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKeyDescriptorVersion is an enumeration of versions specifying the
|
||||
// encryption algorithm for the key data and the authentication for the
|
||||
// message integrity code (MIC)
|
||||
type EAPOLKeyDescriptorVersion uint8
|
||||
|
||||
// Enumeration of EAPOLKeyDescriptorVersion
|
||||
const (
|
||||
EAPOLKeyDescriptorVersionOther EAPOLKeyDescriptorVersion = 0
|
||||
EAPOLKeyDescriptorVersionRC4HMACMD5 EAPOLKeyDescriptorVersion = 1
|
||||
EAPOLKeyDescriptorVersionAESHMACSHA1 EAPOLKeyDescriptorVersion = 2
|
||||
EAPOLKeyDescriptorVersionAES128CMAC EAPOLKeyDescriptorVersion = 3
|
||||
)
|
||||
|
||||
func (v EAPOLKeyDescriptorVersion) String() string {
|
||||
switch v {
|
||||
case EAPOLKeyDescriptorVersionOther:
|
||||
return "Other"
|
||||
case EAPOLKeyDescriptorVersionRC4HMACMD5:
|
||||
return "RC4-HMAC-MD5"
|
||||
case EAPOLKeyDescriptorVersionAESHMACSHA1:
|
||||
return "AES-HMAC-SHA1-128"
|
||||
case EAPOLKeyDescriptorVersionAES128CMAC:
|
||||
return "AES-128-CMAC"
|
||||
default:
|
||||
return fmt.Sprintf("unknown version %d", v)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKeyType is an enumeration of key derivation types describing
|
||||
// the purpose of the keys being derived.
|
||||
type EAPOLKeyType uint8
|
||||
|
||||
// Enumeration of EAPOLKeyType
|
||||
const (
|
||||
EAPOLKeyTypeGroupSMK EAPOLKeyType = 0
|
||||
EAPOLKeyTypePairwise EAPOLKeyType = 1
|
||||
)
|
||||
|
||||
func (kt EAPOLKeyType) String() string {
|
||||
switch kt {
|
||||
case EAPOLKeyTypeGroupSMK:
|
||||
return "Group/SMK"
|
||||
case EAPOLKeyTypePairwise:
|
||||
return "Pairwise"
|
||||
default:
|
||||
return fmt.Sprintf("unknown key type %d", kt)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKey defines an EAPOL-Key frame for 802.1x authentication
|
||||
type EAPOLKey struct {
|
||||
BaseLayer
|
||||
KeyDescriptorType EAPOLKeyDescriptorType
|
||||
KeyDescriptorVersion EAPOLKeyDescriptorVersion
|
||||
KeyType EAPOLKeyType
|
||||
KeyIndex uint8
|
||||
Install bool
|
||||
KeyACK bool
|
||||
KeyMIC bool
|
||||
Secure bool
|
||||
MICError bool
|
||||
Request bool
|
||||
HasEncryptedKeyData bool
|
||||
SMKMessage bool
|
||||
KeyLength uint16
|
||||
ReplayCounter uint64
|
||||
Nonce []byte
|
||||
IV []byte
|
||||
RSC uint64
|
||||
ID uint64
|
||||
MIC []byte
|
||||
KeyDataLength uint16
|
||||
EncryptedKeyData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAPOLKey.
|
||||
func (ek *EAPOLKey) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEAPOLKey
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (ek *EAPOLKey) CanDecode() gopacket.LayerType {
|
||||
return LayerTypeEAPOLKey
|
||||
}
|
||||
|
||||
// NextLayerType returns layers.LayerTypeDot11InformationElement if the key
|
||||
// data exists and is unencrypted, otherwise it does not expect a next layer.
|
||||
func (ek *EAPOLKey) NextLayerType() gopacket.LayerType {
|
||||
if !ek.HasEncryptedKeyData && ek.KeyDataLength > 0 {
|
||||
return LayerTypeDot11InformationElement
|
||||
}
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
const eapolKeyFrameLen = 95
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (ek *EAPOLKey) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < eapolKeyFrameLen {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAPOLKey length %v too short, %v required",
|
||||
len(data), eapolKeyFrameLen)
|
||||
}
|
||||
|
||||
ek.KeyDescriptorType = EAPOLKeyDescriptorType(data[0])
|
||||
|
||||
info := binary.BigEndian.Uint16(data[1:3])
|
||||
ek.KeyDescriptorVersion = EAPOLKeyDescriptorVersion(info & 0x0007)
|
||||
ek.KeyType = EAPOLKeyType((info & 0x0008) >> 3)
|
||||
ek.KeyIndex = uint8((info & 0x0030) >> 4)
|
||||
ek.Install = (info & 0x0040) != 0
|
||||
ek.KeyACK = (info & 0x0080) != 0
|
||||
ek.KeyMIC = (info & 0x0100) != 0
|
||||
ek.Secure = (info & 0x0200) != 0
|
||||
ek.MICError = (info & 0x0400) != 0
|
||||
ek.Request = (info & 0x0800) != 0
|
||||
ek.HasEncryptedKeyData = (info & 0x1000) != 0
|
||||
ek.SMKMessage = (info & 0x2000) != 0
|
||||
|
||||
ek.KeyLength = binary.BigEndian.Uint16(data[3:5])
|
||||
ek.ReplayCounter = binary.BigEndian.Uint64(data[5:13])
|
||||
|
||||
ek.Nonce = data[13:45]
|
||||
ek.IV = data[45:61]
|
||||
ek.RSC = binary.BigEndian.Uint64(data[61:69])
|
||||
ek.ID = binary.BigEndian.Uint64(data[69:77])
|
||||
ek.MIC = data[77:93]
|
||||
|
||||
ek.KeyDataLength = binary.BigEndian.Uint16(data[93:95])
|
||||
|
||||
totalLength := eapolKeyFrameLen + int(ek.KeyDataLength)
|
||||
if len(data) < totalLength {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAPOLKey data length %d too short, %d required",
|
||||
len(data)-eapolKeyFrameLen, ek.KeyDataLength)
|
||||
}
|
||||
|
||||
if ek.HasEncryptedKeyData {
|
||||
ek.EncryptedKeyData = data[eapolKeyFrameLen:totalLength]
|
||||
ek.BaseLayer = BaseLayer{
|
||||
Contents: data[:totalLength],
|
||||
Payload: data[totalLength:],
|
||||
}
|
||||
} else {
|
||||
ek.BaseLayer = BaseLayer{
|
||||
Contents: data[:eapolKeyFrameLen],
|
||||
Payload: data[eapolKeyFrameLen:],
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (ek *EAPOLKey) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
buf, err := b.PrependBytes(eapolKeyFrameLen + len(ek.EncryptedKeyData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(ek.KeyDescriptorType)
|
||||
|
||||
var info uint16
|
||||
info |= uint16(ek.KeyDescriptorVersion)
|
||||
info |= uint16(ek.KeyType) << 3
|
||||
info |= uint16(ek.KeyIndex) << 4
|
||||
if ek.Install {
|
||||
info |= 0x0040
|
||||
}
|
||||
if ek.KeyACK {
|
||||
info |= 0x0080
|
||||
}
|
||||
if ek.KeyMIC {
|
||||
info |= 0x0100
|
||||
}
|
||||
if ek.Secure {
|
||||
info |= 0x0200
|
||||
}
|
||||
if ek.MICError {
|
||||
info |= 0x0400
|
||||
}
|
||||
if ek.Request {
|
||||
info |= 0x0800
|
||||
}
|
||||
if ek.HasEncryptedKeyData {
|
||||
info |= 0x1000
|
||||
}
|
||||
if ek.SMKMessage {
|
||||
info |= 0x2000
|
||||
}
|
||||
binary.BigEndian.PutUint16(buf[1:3], info)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[3:5], ek.KeyLength)
|
||||
binary.BigEndian.PutUint64(buf[5:13], ek.ReplayCounter)
|
||||
|
||||
copy(buf[13:45], ek.Nonce)
|
||||
copy(buf[45:61], ek.IV)
|
||||
binary.BigEndian.PutUint64(buf[61:69], ek.RSC)
|
||||
binary.BigEndian.PutUint64(buf[69:77], ek.ID)
|
||||
copy(buf[77:93], ek.MIC)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[93:95], ek.KeyDataLength)
|
||||
if len(ek.EncryptedKeyData) > 0 {
|
||||
copy(buf[95:95+len(ek.EncryptedKeyData)], ek.EncryptedKeyData)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeEAPOLKey(data []byte, p gopacket.PacketBuilder) error {
|
||||
ek := &EAPOLKey{}
|
||||
return decodingLayerDecoder(ek, data, p)
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
// We use two different endpoint types for IPv4 vs IPv6 addresses, so that
|
||||
// ordering with endpointA.LessThan(endpointB) sanely groups all IPv4
|
||||
// addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses.
|
||||
EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string {
|
||||
return net.IP(b).String()
|
||||
}})
|
||||
EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string {
|
||||
return net.IP(b).String()
|
||||
}})
|
||||
|
||||
EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string {
|
||||
return net.HardwareAddr(b).String()
|
||||
}})
|
||||
EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(b[0]))
|
||||
}})
|
||||
EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string {
|
||||
return "point"
|
||||
}})
|
||||
)
|
||||
|
||||
// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address.
|
||||
// It returns gopacket.InvalidEndpoint if the IP address is invalid.
|
||||
func NewIPEndpoint(a net.IP) gopacket.Endpoint {
|
||||
ipv4 := a.To4()
|
||||
if ipv4 != nil {
|
||||
return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4))
|
||||
}
|
||||
|
||||
ipv6 := a.To16()
|
||||
if ipv6 != nil {
|
||||
return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6))
|
||||
}
|
||||
|
||||
return gopacket.InvalidEndpoint
|
||||
}
|
||||
|
||||
// NewMACEndpoint returns a new MAC address endpoint.
|
||||
func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointMAC, []byte(a))
|
||||
}
|
||||
func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)})
|
||||
}
|
||||
|
||||
// NewTCPPortEndpoint returns an endpoint based on a TCP port.
|
||||
func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointTCPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewUDPPortEndpoint returns an endpoint based on a UDP port.
|
||||
func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointUDPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewSCTPPortEndpoint returns an endpoint based on a SCTP port.
|
||||
func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointSCTPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewRUDPPortEndpoint returns an endpoint based on a RUDP port.
|
||||
func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)})
|
||||
}
|
||||
|
||||
// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port.
|
||||
func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointUDPLitePort, uint16(p))
|
||||
}
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EnumMetadata keeps track of a set of metadata for each enumeration value
|
||||
// for protocol enumerations.
|
||||
type EnumMetadata struct {
|
||||
// DecodeWith is the decoder to use to decode this protocol's data.
|
||||
DecodeWith gopacket.Decoder
|
||||
// Name is the name of the enumeration value.
|
||||
Name string
|
||||
// LayerType is the layer type implied by the given enum.
|
||||
LayerType gopacket.LayerType
|
||||
}
|
||||
|
||||
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type EthernetType uint16
|
||||
|
||||
const (
|
||||
// EthernetTypeLLC is not an actual ethernet type. It is instead a
|
||||
// placeholder we use in Ethernet frames that use the 802.3 standard of
|
||||
// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
|
||||
EthernetTypeLLC EthernetType = 0
|
||||
EthernetTypeIPv4 EthernetType = 0x0800
|
||||
EthernetTypeARP EthernetType = 0x0806
|
||||
EthernetTypeIPv6 EthernetType = 0x86DD
|
||||
EthernetTypeCiscoDiscovery EthernetType = 0x2000
|
||||
EthernetTypeNortelDiscovery EthernetType = 0x01a2
|
||||
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
|
||||
EthernetTypeDot1Q EthernetType = 0x8100
|
||||
EthernetTypePPP EthernetType = 0x880b
|
||||
EthernetTypePPPoEDiscovery EthernetType = 0x8863
|
||||
EthernetTypePPPoESession EthernetType = 0x8864
|
||||
EthernetTypeMPLSUnicast EthernetType = 0x8847
|
||||
EthernetTypeMPLSMulticast EthernetType = 0x8848
|
||||
EthernetTypeEAPOL EthernetType = 0x888e
|
||||
EthernetTypeERSPAN EthernetType = 0x88be
|
||||
EthernetTypeQinQ EthernetType = 0x88a8
|
||||
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
|
||||
EthernetTypeEthernetCTP EthernetType = 0x9000
|
||||
)
|
||||
|
||||
// IPProtocol is an enumeration of IP protocol values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type IPProtocol uint8
|
||||
|
||||
const (
|
||||
IPProtocolIPv6HopByHop IPProtocol = 0
|
||||
IPProtocolICMPv4 IPProtocol = 1
|
||||
IPProtocolIGMP IPProtocol = 2
|
||||
IPProtocolIPv4 IPProtocol = 4
|
||||
IPProtocolTCP IPProtocol = 6
|
||||
IPProtocolUDP IPProtocol = 17
|
||||
IPProtocolRUDP IPProtocol = 27
|
||||
IPProtocolIPv6 IPProtocol = 41
|
||||
IPProtocolIPv6Routing IPProtocol = 43
|
||||
IPProtocolIPv6Fragment IPProtocol = 44
|
||||
IPProtocolGRE IPProtocol = 47
|
||||
IPProtocolESP IPProtocol = 50
|
||||
IPProtocolAH IPProtocol = 51
|
||||
IPProtocolICMPv6 IPProtocol = 58
|
||||
IPProtocolNoNextHeader IPProtocol = 59
|
||||
IPProtocolIPv6Destination IPProtocol = 60
|
||||
IPProtocolOSPF IPProtocol = 89
|
||||
IPProtocolIPIP IPProtocol = 94
|
||||
IPProtocolEtherIP IPProtocol = 97
|
||||
IPProtocolVRRP IPProtocol = 112
|
||||
IPProtocolSCTP IPProtocol = 132
|
||||
IPProtocolUDPLite IPProtocol = 136
|
||||
IPProtocolMPLSInIP IPProtocol = 137
|
||||
)
|
||||
|
||||
// LinkType is an enumeration of link types, and acts as a decoder for any
|
||||
// link type it supports.
|
||||
type LinkType uint8
|
||||
|
||||
const (
|
||||
// According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html
|
||||
LinkTypeNull LinkType = 0
|
||||
LinkTypeEthernet LinkType = 1
|
||||
LinkTypeAX25 LinkType = 3
|
||||
LinkTypeTokenRing LinkType = 6
|
||||
LinkTypeArcNet LinkType = 7
|
||||
LinkTypeSLIP LinkType = 8
|
||||
LinkTypePPP LinkType = 9
|
||||
LinkTypeFDDI LinkType = 10
|
||||
LinkTypePPP_HDLC LinkType = 50
|
||||
LinkTypePPPEthernet LinkType = 51
|
||||
LinkTypeATM_RFC1483 LinkType = 100
|
||||
LinkTypeRaw LinkType = 101
|
||||
LinkTypeC_HDLC LinkType = 104
|
||||
LinkTypeIEEE802_11 LinkType = 105
|
||||
LinkTypeFRelay LinkType = 107
|
||||
LinkTypeLoop LinkType = 108
|
||||
LinkTypeLinuxSLL LinkType = 113
|
||||
LinkTypeLTalk LinkType = 114
|
||||
LinkTypePFLog LinkType = 117
|
||||
LinkTypePrismHeader LinkType = 119
|
||||
LinkTypeIPOverFC LinkType = 122
|
||||
LinkTypeSunATM LinkType = 123
|
||||
LinkTypeIEEE80211Radio LinkType = 127
|
||||
LinkTypeARCNetLinux LinkType = 129
|
||||
LinkTypeIPOver1394 LinkType = 138
|
||||
LinkTypeMTP2Phdr LinkType = 139
|
||||
LinkTypeMTP2 LinkType = 140
|
||||
LinkTypeMTP3 LinkType = 141
|
||||
LinkTypeSCCP LinkType = 142
|
||||
LinkTypeDOCSIS LinkType = 143
|
||||
LinkTypeLinuxIRDA LinkType = 144
|
||||
LinkTypeLinuxLAPD LinkType = 177
|
||||
LinkTypeLinuxUSB LinkType = 220
|
||||
LinkTypeFC2 LinkType = 224
|
||||
LinkTypeFC2Framed LinkType = 225
|
||||
LinkTypeIPv4 LinkType = 228
|
||||
LinkTypeIPv6 LinkType = 229
|
||||
)
|
||||
|
||||
// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516
|
||||
type PPPoECode uint8
|
||||
|
||||
const (
|
||||
PPPoECodePADI PPPoECode = 0x09
|
||||
PPPoECodePADO PPPoECode = 0x07
|
||||
PPPoECodePADR PPPoECode = 0x19
|
||||
PPPoECodePADS PPPoECode = 0x65
|
||||
PPPoECodePADT PPPoECode = 0xA7
|
||||
PPPoECodeSession PPPoECode = 0x00
|
||||
)
|
||||
|
||||
// PPPType is an enumeration of PPP type values, and acts as a decoder for any
|
||||
// type it supports.
|
||||
type PPPType uint16
|
||||
|
||||
const (
|
||||
PPPTypeIPv4 PPPType = 0x0021
|
||||
PPPTypeIPv6 PPPType = 0x0057
|
||||
PPPTypeMPLSUnicast PPPType = 0x0281
|
||||
PPPTypeMPLSMulticast PPPType = 0x0283
|
||||
)
|
||||
|
||||
// SCTPChunkType is an enumeration of chunk types inside SCTP packets.
|
||||
type SCTPChunkType uint8
|
||||
|
||||
const (
|
||||
SCTPChunkTypeData SCTPChunkType = 0
|
||||
SCTPChunkTypeInit SCTPChunkType = 1
|
||||
SCTPChunkTypeInitAck SCTPChunkType = 2
|
||||
SCTPChunkTypeSack SCTPChunkType = 3
|
||||
SCTPChunkTypeHeartbeat SCTPChunkType = 4
|
||||
SCTPChunkTypeHeartbeatAck SCTPChunkType = 5
|
||||
SCTPChunkTypeAbort SCTPChunkType = 6
|
||||
SCTPChunkTypeShutdown SCTPChunkType = 7
|
||||
SCTPChunkTypeShutdownAck SCTPChunkType = 8
|
||||
SCTPChunkTypeError SCTPChunkType = 9
|
||||
SCTPChunkTypeCookieEcho SCTPChunkType = 10
|
||||
SCTPChunkTypeCookieAck SCTPChunkType = 11
|
||||
SCTPChunkTypeShutdownComplete SCTPChunkType = 14
|
||||
)
|
||||
|
||||
// FDDIFrameControl is an enumeration of FDDI frame control bytes.
|
||||
type FDDIFrameControl uint8
|
||||
|
||||
const (
|
||||
FDDIFrameControlLLC FDDIFrameControl = 0x50
|
||||
)
|
||||
|
||||
// EAPOLType is an enumeration of EAPOL packet types.
|
||||
type EAPOLType uint8
|
||||
|
||||
const (
|
||||
EAPOLTypeEAP EAPOLType = 0
|
||||
EAPOLTypeStart EAPOLType = 1
|
||||
EAPOLTypeLogOff EAPOLType = 2
|
||||
EAPOLTypeKey EAPOLType = 3
|
||||
EAPOLTypeASFAlert EAPOLType = 4
|
||||
)
|
||||
|
||||
// ProtocolFamily is the set of values defined as PF_* in sys/socket.h
|
||||
type ProtocolFamily uint8
|
||||
|
||||
const (
|
||||
ProtocolFamilyIPv4 ProtocolFamily = 2
|
||||
// BSDs use different values for INET6... glory be. These values taken from
|
||||
// tcpdump 4.3.0.
|
||||
ProtocolFamilyIPv6BSD ProtocolFamily = 24
|
||||
ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28
|
||||
ProtocolFamilyIPv6Darwin ProtocolFamily = 30
|
||||
ProtocolFamilyIPv6Linux ProtocolFamily = 10
|
||||
)
|
||||
|
||||
// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields.
|
||||
// By combining these two fields together into a single type, we're able to
|
||||
// provide a String function that correctly displays the subtype given the
|
||||
// top-level type.
|
||||
//
|
||||
// If you just care about the top-level type, use the MainType function.
|
||||
type Dot11Type uint8
|
||||
|
||||
// MainType strips the subtype information from the given type,
|
||||
// returning just the overarching type (Mgmt, Ctrl, Data, Reserved).
|
||||
func (d Dot11Type) MainType() Dot11Type {
|
||||
return d & dot11TypeMask
|
||||
}
|
||||
|
||||
func (d Dot11Type) QOS() bool {
|
||||
return d&dot11QOSMask == Dot11TypeDataQOSData
|
||||
}
|
||||
|
||||
const (
|
||||
Dot11TypeMgmt Dot11Type = 0x00
|
||||
Dot11TypeCtrl Dot11Type = 0x01
|
||||
Dot11TypeData Dot11Type = 0x02
|
||||
Dot11TypeReserved Dot11Type = 0x03
|
||||
dot11TypeMask = 0x03
|
||||
dot11QOSMask = 0x23
|
||||
|
||||
// The following are type/subtype conglomerations.
|
||||
|
||||
// Management
|
||||
Dot11TypeMgmtAssociationReq Dot11Type = 0x00
|
||||
Dot11TypeMgmtAssociationResp Dot11Type = 0x04
|
||||
Dot11TypeMgmtReassociationReq Dot11Type = 0x08
|
||||
Dot11TypeMgmtReassociationResp Dot11Type = 0x0c
|
||||
Dot11TypeMgmtProbeReq Dot11Type = 0x10
|
||||
Dot11TypeMgmtProbeResp Dot11Type = 0x14
|
||||
Dot11TypeMgmtMeasurementPilot Dot11Type = 0x18
|
||||
Dot11TypeMgmtBeacon Dot11Type = 0x20
|
||||
Dot11TypeMgmtATIM Dot11Type = 0x24
|
||||
Dot11TypeMgmtDisassociation Dot11Type = 0x28
|
||||
Dot11TypeMgmtAuthentication Dot11Type = 0x2c
|
||||
Dot11TypeMgmtDeauthentication Dot11Type = 0x30
|
||||
Dot11TypeMgmtAction Dot11Type = 0x34
|
||||
Dot11TypeMgmtActionNoAck Dot11Type = 0x38
|
||||
|
||||
// Control
|
||||
Dot11TypeCtrlWrapper Dot11Type = 0x1d
|
||||
Dot11TypeCtrlBlockAckReq Dot11Type = 0x21
|
||||
Dot11TypeCtrlBlockAck Dot11Type = 0x25
|
||||
Dot11TypeCtrlPowersavePoll Dot11Type = 0x29
|
||||
Dot11TypeCtrlRTS Dot11Type = 0x2d
|
||||
Dot11TypeCtrlCTS Dot11Type = 0x31
|
||||
Dot11TypeCtrlAck Dot11Type = 0x35
|
||||
Dot11TypeCtrlCFEnd Dot11Type = 0x39
|
||||
Dot11TypeCtrlCFEndAck Dot11Type = 0x3d
|
||||
|
||||
// Data
|
||||
Dot11TypeDataCFAck Dot11Type = 0x06
|
||||
Dot11TypeDataCFPoll Dot11Type = 0x0a
|
||||
Dot11TypeDataCFAckPoll Dot11Type = 0x0e
|
||||
Dot11TypeDataNull Dot11Type = 0x12
|
||||
Dot11TypeDataCFAckNoData Dot11Type = 0x16
|
||||
Dot11TypeDataCFPollNoData Dot11Type = 0x1a
|
||||
Dot11TypeDataCFAckPollNoData Dot11Type = 0x1e
|
||||
Dot11TypeDataQOSData Dot11Type = 0x22
|
||||
Dot11TypeDataQOSDataCFAck Dot11Type = 0x26
|
||||
Dot11TypeDataQOSDataCFPoll Dot11Type = 0x2a
|
||||
Dot11TypeDataQOSDataCFAckPoll Dot11Type = 0x2e
|
||||
Dot11TypeDataQOSNull Dot11Type = 0x32
|
||||
Dot11TypeDataQOSCFPollNoData Dot11Type = 0x3a
|
||||
Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e
|
||||
)
|
||||
|
||||
// Decode a raw v4 or v6 IP packet.
|
||||
func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error {
|
||||
version := data[0] >> 4
|
||||
switch version {
|
||||
case 4:
|
||||
return decodeIPv4(data, p)
|
||||
case 6:
|
||||
return decodeIPv6(data, p)
|
||||
}
|
||||
return fmt.Errorf("Invalid IP packet version %v", version)
|
||||
}
|
||||
|
||||
func initActualTypeData() {
|
||||
// Each of the XXXTypeMetadata arrays contains mappings of how to handle enum
|
||||
// values for various enum types in gopacket/layers.
|
||||
// These arrays are actually created by gen2.go and stored in
|
||||
// enums_generated.go.
|
||||
//
|
||||
// So, EthernetTypeMetadata[2] contains information on how to handle EthernetType
|
||||
// 2, including which name to give it and which decoder to use to decode
|
||||
// packet data of that type. These arrays are filled by default with all of the
|
||||
// protocols gopacket/layers knows how to handle, but users of the library can
|
||||
// add new decoders or override existing ones. For example, if you write a better
|
||||
// TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith
|
||||
// with your new decoder, and all gopacket/layers decoding will use your new
|
||||
// decoder whenever they encounter that IPProtocol.
|
||||
|
||||
// Here we link up all enumerations with their respective names and decoders.
|
||||
EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC}
|
||||
EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP}
|
||||
EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
|
||||
EthernetTypeMetadata[EthernetTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP", LayerType: LayerTypePPP}
|
||||
EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE}
|
||||
EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE}
|
||||
EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP}
|
||||
EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS}
|
||||
EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS}
|
||||
EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
|
||||
EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
|
||||
EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
|
||||
EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII}
|
||||
|
||||
IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
|
||||
IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP}
|
||||
IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4}
|
||||
IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6}
|
||||
IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP}
|
||||
IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP}
|
||||
IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP}
|
||||
IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE}
|
||||
IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop}
|
||||
IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing}
|
||||
IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment}
|
||||
IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination}
|
||||
IPProtocolMetadata[IPProtocolOSPF] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeOSPF), Name: "OSPF", LayerType: LayerTypeOSPF}
|
||||
IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH}
|
||||
IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP}
|
||||
IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite}
|
||||
IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS}
|
||||
IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload}
|
||||
IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP}
|
||||
IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP}
|
||||
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"}
|
||||
|
||||
PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"}
|
||||
PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"}
|
||||
PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"}
|
||||
PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"}
|
||||
|
||||
PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
|
||||
LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
|
||||
LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"}
|
||||
LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"}
|
||||
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"}
|
||||
LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"}
|
||||
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"}
|
||||
LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
// See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85
|
||||
// Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508
|
||||
if runtime.GOOS == "openbsd" {
|
||||
LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
} else {
|
||||
LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
}
|
||||
LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
|
||||
LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
|
||||
LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}
|
||||
LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"}
|
||||
LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"}
|
||||
|
||||
FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"}
|
||||
|
||||
EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP}
|
||||
EAPOLTypeMetadata[EAPOLTypeKey] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOLKey), Name: "EAPOLKey", LayerType: LayerTypeEAPOLKey}
|
||||
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck}
|
||||
Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData}
|
||||
|
||||
USBTransportTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
|
||||
USBTransportTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
|
||||
USBTransportTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
|
||||
}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
|
||||
package layers
|
||||
|
||||
// Created by gen2.go, don't edit manually
|
||||
// Generated at 2017-10-23 10:20:24.458771856 -0600 MDT m=+0.001159033
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initUnknownTypesForLinkType()
|
||||
initUnknownTypesForEthernetType()
|
||||
initUnknownTypesForPPPType()
|
||||
initUnknownTypesForIPProtocol()
|
||||
initUnknownTypesForSCTPChunkType()
|
||||
initUnknownTypesForPPPoECode()
|
||||
initUnknownTypesForFDDIFrameControl()
|
||||
initUnknownTypesForEAPOLType()
|
||||
initUnknownTypesForProtocolFamily()
|
||||
initUnknownTypesForDot11Type()
|
||||
initUnknownTypesForUSBTransportType()
|
||||
initActualTypeData()
|
||||
}
|
||||
|
||||
// Decoder calls LinkTypeMetadata.DecodeWith's decoder.
|
||||
func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns LinkTypeMetadata.Name.
|
||||
func (a LinkType) String() string {
|
||||
return LinkTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns LinkTypeMetadata.LayerType.
|
||||
func (a LinkType) LayerType() gopacket.LayerType {
|
||||
return LinkTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForLinkType int
|
||||
|
||||
func (a *errorDecoderForLinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForLinkType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode LinkType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForLinkType [256]errorDecoderForLinkType
|
||||
var LinkTypeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForLinkType() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForLinkType[i] = errorDecoderForLinkType(i)
|
||||
LinkTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForLinkType[i],
|
||||
Name: "UnknownLinkType",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls EthernetTypeMetadata.DecodeWith's decoder.
|
||||
func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return EthernetTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns EthernetTypeMetadata.Name.
|
||||
func (a EthernetType) String() string {
|
||||
return EthernetTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns EthernetTypeMetadata.LayerType.
|
||||
func (a EthernetType) LayerType() gopacket.LayerType {
|
||||
return EthernetTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForEthernetType int
|
||||
|
||||
func (a *errorDecoderForEthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForEthernetType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode EthernetType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForEthernetType [65536]errorDecoderForEthernetType
|
||||
var EthernetTypeMetadata [65536]EnumMetadata
|
||||
|
||||
func initUnknownTypesForEthernetType() {
|
||||
for i := 0; i < 65536; i++ {
|
||||
errorDecodersForEthernetType[i] = errorDecoderForEthernetType(i)
|
||||
EthernetTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForEthernetType[i],
|
||||
Name: "UnknownEthernetType",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls PPPTypeMetadata.DecodeWith's decoder.
|
||||
func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return PPPTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns PPPTypeMetadata.Name.
|
||||
func (a PPPType) String() string {
|
||||
return PPPTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns PPPTypeMetadata.LayerType.
|
||||
func (a PPPType) LayerType() gopacket.LayerType {
|
||||
return PPPTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForPPPType int
|
||||
|
||||
func (a *errorDecoderForPPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForPPPType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode PPPType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForPPPType [65536]errorDecoderForPPPType
|
||||
var PPPTypeMetadata [65536]EnumMetadata
|
||||
|
||||
func initUnknownTypesForPPPType() {
|
||||
for i := 0; i < 65536; i++ {
|
||||
errorDecodersForPPPType[i] = errorDecoderForPPPType(i)
|
||||
PPPTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForPPPType[i],
|
||||
Name: "UnknownPPPType",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls IPProtocolMetadata.DecodeWith's decoder.
|
||||
func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return IPProtocolMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns IPProtocolMetadata.Name.
|
||||
func (a IPProtocol) String() string {
|
||||
return IPProtocolMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns IPProtocolMetadata.LayerType.
|
||||
func (a IPProtocol) LayerType() gopacket.LayerType {
|
||||
return IPProtocolMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForIPProtocol int
|
||||
|
||||
func (a *errorDecoderForIPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForIPProtocol) Error() string {
|
||||
return fmt.Sprintf("Unable to decode IPProtocol %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForIPProtocol [256]errorDecoderForIPProtocol
|
||||
var IPProtocolMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForIPProtocol() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForIPProtocol[i] = errorDecoderForIPProtocol(i)
|
||||
IPProtocolMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForIPProtocol[i],
|
||||
Name: "UnknownIPProtocol",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls SCTPChunkTypeMetadata.DecodeWith's decoder.
|
||||
func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns SCTPChunkTypeMetadata.Name.
|
||||
func (a SCTPChunkType) String() string {
|
||||
return SCTPChunkTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns SCTPChunkTypeMetadata.LayerType.
|
||||
func (a SCTPChunkType) LayerType() gopacket.LayerType {
|
||||
return SCTPChunkTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForSCTPChunkType int
|
||||
|
||||
func (a *errorDecoderForSCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForSCTPChunkType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode SCTPChunkType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForSCTPChunkType [256]errorDecoderForSCTPChunkType
|
||||
var SCTPChunkTypeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForSCTPChunkType() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForSCTPChunkType[i] = errorDecoderForSCTPChunkType(i)
|
||||
SCTPChunkTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForSCTPChunkType[i],
|
||||
Name: "UnknownSCTPChunkType",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls PPPoECodeMetadata.DecodeWith's decoder.
|
||||
func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return PPPoECodeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns PPPoECodeMetadata.Name.
|
||||
func (a PPPoECode) String() string {
|
||||
return PPPoECodeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns PPPoECodeMetadata.LayerType.
|
||||
func (a PPPoECode) LayerType() gopacket.LayerType {
|
||||
return PPPoECodeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForPPPoECode int
|
||||
|
||||
func (a *errorDecoderForPPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForPPPoECode) Error() string {
|
||||
return fmt.Sprintf("Unable to decode PPPoECode %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForPPPoECode [256]errorDecoderForPPPoECode
|
||||
var PPPoECodeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForPPPoECode() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForPPPoECode[i] = errorDecoderForPPPoECode(i)
|
||||
PPPoECodeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForPPPoECode[i],
|
||||
Name: "UnknownPPPoECode",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls FDDIFrameControlMetadata.DecodeWith's decoder.
|
||||
func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns FDDIFrameControlMetadata.Name.
|
||||
func (a FDDIFrameControl) String() string {
|
||||
return FDDIFrameControlMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns FDDIFrameControlMetadata.LayerType.
|
||||
func (a FDDIFrameControl) LayerType() gopacket.LayerType {
|
||||
return FDDIFrameControlMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForFDDIFrameControl int
|
||||
|
||||
func (a *errorDecoderForFDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForFDDIFrameControl) Error() string {
|
||||
return fmt.Sprintf("Unable to decode FDDIFrameControl %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForFDDIFrameControl [256]errorDecoderForFDDIFrameControl
|
||||
var FDDIFrameControlMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForFDDIFrameControl() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForFDDIFrameControl[i] = errorDecoderForFDDIFrameControl(i)
|
||||
FDDIFrameControlMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForFDDIFrameControl[i],
|
||||
Name: "UnknownFDDIFrameControl",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls EAPOLTypeMetadata.DecodeWith's decoder.
|
||||
func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns EAPOLTypeMetadata.Name.
|
||||
func (a EAPOLType) String() string {
|
||||
return EAPOLTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns EAPOLTypeMetadata.LayerType.
|
||||
func (a EAPOLType) LayerType() gopacket.LayerType {
|
||||
return EAPOLTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForEAPOLType int
|
||||
|
||||
func (a *errorDecoderForEAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForEAPOLType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode EAPOLType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForEAPOLType [256]errorDecoderForEAPOLType
|
||||
var EAPOLTypeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForEAPOLType() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForEAPOLType[i] = errorDecoderForEAPOLType(i)
|
||||
EAPOLTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForEAPOLType[i],
|
||||
Name: "UnknownEAPOLType",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls ProtocolFamilyMetadata.DecodeWith's decoder.
|
||||
func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns ProtocolFamilyMetadata.Name.
|
||||
func (a ProtocolFamily) String() string {
|
||||
return ProtocolFamilyMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns ProtocolFamilyMetadata.LayerType.
|
||||
func (a ProtocolFamily) LayerType() gopacket.LayerType {
|
||||
return ProtocolFamilyMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForProtocolFamily int
|
||||
|
||||
func (a *errorDecoderForProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForProtocolFamily) Error() string {
|
||||
return fmt.Sprintf("Unable to decode ProtocolFamily %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForProtocolFamily [256]errorDecoderForProtocolFamily
|
||||
var ProtocolFamilyMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForProtocolFamily() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForProtocolFamily[i] = errorDecoderForProtocolFamily(i)
|
||||
ProtocolFamilyMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForProtocolFamily[i],
|
||||
Name: "UnknownProtocolFamily",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls Dot11TypeMetadata.DecodeWith's decoder.
|
||||
func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return Dot11TypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns Dot11TypeMetadata.Name.
|
||||
func (a Dot11Type) String() string {
|
||||
return Dot11TypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns Dot11TypeMetadata.LayerType.
|
||||
func (a Dot11Type) LayerType() gopacket.LayerType {
|
||||
return Dot11TypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForDot11Type int
|
||||
|
||||
func (a *errorDecoderForDot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForDot11Type) Error() string {
|
||||
return fmt.Sprintf("Unable to decode Dot11Type %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForDot11Type [256]errorDecoderForDot11Type
|
||||
var Dot11TypeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForDot11Type() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForDot11Type[i] = errorDecoderForDot11Type(i)
|
||||
Dot11TypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForDot11Type[i],
|
||||
Name: "UnknownDot11Type",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder calls USBTransportTypeMetadata.DecodeWith's decoder.
|
||||
func (a USBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return USBTransportTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
|
||||
// String returns USBTransportTypeMetadata.Name.
|
||||
func (a USBTransportType) String() string {
|
||||
return USBTransportTypeMetadata[a].Name
|
||||
}
|
||||
|
||||
// LayerType returns USBTransportTypeMetadata.LayerType.
|
||||
func (a USBTransportType) LayerType() gopacket.LayerType {
|
||||
return USBTransportTypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
type errorDecoderForUSBTransportType int
|
||||
|
||||
func (a *errorDecoderForUSBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return a
|
||||
}
|
||||
func (a *errorDecoderForUSBTransportType) Error() string {
|
||||
return fmt.Sprintf("Unable to decode USBTransportType %d", int(*a))
|
||||
}
|
||||
|
||||
var errorDecodersForUSBTransportType [256]errorDecoderForUSBTransportType
|
||||
var USBTransportTypeMetadata [256]EnumMetadata
|
||||
|
||||
func initUnknownTypesForUSBTransportType() {
|
||||
for i := 0; i < 256; i++ {
|
||||
errorDecodersForUSBTransportType[i] = errorDecoderForUSBTransportType(i)
|
||||
USBTransportTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: &errorDecodersForUSBTransportType[i],
|
||||
Name: "UnknownUSBTransportType",
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2018 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
//ERSPANIIVersionObsolete - The obsolete value for the version field
|
||||
ERSPANIIVersionObsolete = 0x0
|
||||
// ERSPANIIVersion - The current value for the version field
|
||||
ERSPANIIVersion = 0x1
|
||||
)
|
||||
|
||||
// ERSPANII contains all of the fields found in an ERSPAN Type II header
|
||||
// https://tools.ietf.org/html/draft-foschiano-erspan-03
|
||||
type ERSPANII struct {
|
||||
BaseLayer
|
||||
IsTruncated bool
|
||||
Version, CoS, TrunkEncap uint8
|
||||
VLANIdentifier, SessionID, Reserved uint16
|
||||
Index uint32
|
||||
}
|
||||
|
||||
func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPANII }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
erspan2Length := 8
|
||||
erspan2.Version = data[0] & 0xF0 >> 4
|
||||
erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
|
||||
erspan2.CoS = data[2] & 0xE0 >> 5
|
||||
erspan2.TrunkEncap = data[2] & 0x18 >> 3
|
||||
erspan2.IsTruncated = data[2]&0x4>>2 != 0
|
||||
erspan2.SessionID = binary.BigEndian.Uint16(data[2:4]) & 0x03FF
|
||||
erspan2.Reserved = binary.BigEndian.Uint16(data[4:6]) & 0xFFF0 >> 4
|
||||
erspan2.Index = binary.BigEndian.Uint32(data[4:8]) & 0x000FFFFF
|
||||
erspan2.Contents = data[:erspan2Length]
|
||||
erspan2.Payload = data[erspan2Length:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (erspan2 *ERSPANII) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
twoByteInt := uint16(erspan2.Version&0xF)<<12 | erspan2.VLANIdentifier&0x0FFF
|
||||
binary.BigEndian.PutUint16(bytes, twoByteInt)
|
||||
|
||||
twoByteInt = uint16(erspan2.CoS&0x7)<<13 | uint16(erspan2.TrunkEncap&0x3)<<11 | erspan2.SessionID&0x03FF
|
||||
if erspan2.IsTruncated {
|
||||
twoByteInt |= 0x400
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], twoByteInt)
|
||||
|
||||
fourByteInt := uint32(erspan2.Reserved&0x0FFF)<<20 | erspan2.Index&0x000FFFFF
|
||||
binary.BigEndian.PutUint32(bytes[4:], fourByteInt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (erspan2 *ERSPANII) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeERSPANII
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (erspan2 *ERSPANII) NextLayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func decodeERSPANII(data []byte, p gopacket.PacketBuilder) error {
|
||||
erspan2 := &ERSPANII{}
|
||||
return decodingLayerDecoder(erspan2, data, p)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
|
||||
type EtherIP struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
Reserved uint16
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEtherIP.
|
||||
func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Version = data[0] >> 4
|
||||
e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
|
||||
e.BaseLayer = BaseLayer{data[:2], data[2:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EtherIP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEtherIP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EtherIP) NextLayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EtherIP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// EthernetBroadcast is the broadcast MAC address used by Ethernet.
|
||||
var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
|
||||
// Ethernet is the layer for Ethernet frame headers.
|
||||
type Ethernet struct {
|
||||
BaseLayer
|
||||
SrcMAC, DstMAC net.HardwareAddr
|
||||
EthernetType EthernetType
|
||||
// Length is only set if a length field exists within this header. Ethernet
|
||||
// headers follow two different standards, one that uses an EthernetType, the
|
||||
// other which defines a length the follows with a LLC header (802.3). If the
|
||||
// former is the case, we set EthernetType and Length stays 0. In the latter
|
||||
// case, we set Length and EthernetType = EthernetTypeLLC.
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEthernet
|
||||
func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet }
|
||||
|
||||
func (e *Ethernet) LinkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC)
|
||||
}
|
||||
|
||||
func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 14 {
|
||||
return errors.New("Ethernet packet too small")
|
||||
}
|
||||
eth.DstMAC = net.HardwareAddr(data[0:6])
|
||||
eth.SrcMAC = net.HardwareAddr(data[6:12])
|
||||
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
|
||||
eth.BaseLayer = BaseLayer{data[:14], data[14:]}
|
||||
eth.Length = 0
|
||||
if eth.EthernetType < 0x0600 {
|
||||
eth.Length = uint16(eth.EthernetType)
|
||||
eth.EthernetType = EthernetTypeLLC
|
||||
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
|
||||
df.SetTruncated()
|
||||
} else if cmp > 0 {
|
||||
// Strip off bytes at the end, since we have too many bytes
|
||||
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
|
||||
}
|
||||
// fmt.Println(eth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if len(eth.DstMAC) != 6 {
|
||||
return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC)
|
||||
}
|
||||
if len(eth.SrcMAC) != 6 {
|
||||
return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC)
|
||||
}
|
||||
payload := b.Bytes()
|
||||
bytes, err := b.PrependBytes(14)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, eth.DstMAC)
|
||||
copy(bytes[6:], eth.SrcMAC)
|
||||
if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC {
|
||||
if opts.FixLengths {
|
||||
eth.Length = uint16(len(payload))
|
||||
}
|
||||
if eth.EthernetType != EthernetTypeLLC {
|
||||
return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length)
|
||||
} else if eth.Length > 0x0600 {
|
||||
return fmt.Errorf("invalid ethernet length %v", eth.Length)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[12:], eth.Length)
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType))
|
||||
}
|
||||
length := len(b.Bytes())
|
||||
if length < 60 {
|
||||
// Pad out to 60 bytes.
|
||||
padding, err := b.AppendBytes(60 - length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(padding, lotsOfZeros[:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eth *Ethernet) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func (eth *Ethernet) NextLayerType() gopacket.LayerType {
|
||||
return eth.EthernetType.LayerType()
|
||||
}
|
||||
|
||||
func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
|
||||
eth := &Ethernet{}
|
||||
err := eth.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(eth)
|
||||
p.SetLinkLayer(eth)
|
||||
return p.NextDecoder(eth.EthernetType)
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// FDDI contains the header for FDDI frames.
|
||||
type FDDI struct {
|
||||
BaseLayer
|
||||
FrameControl FDDIFrameControl
|
||||
Priority uint8
|
||||
SrcMAC, DstMAC net.HardwareAddr
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeFDDI.
|
||||
func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI }
|
||||
|
||||
// LinkFlow returns a new flow of type EndpointMAC.
|
||||
func (f *FDDI) LinkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC)
|
||||
}
|
||||
|
||||
func decodeFDDI(data []byte, p gopacket.PacketBuilder) error {
|
||||
f := &FDDI{
|
||||
FrameControl: FDDIFrameControl(data[0] & 0xF8),
|
||||
Priority: data[0] & 0x07,
|
||||
SrcMAC: net.HardwareAddr(data[1:7]),
|
||||
DstMAC: net.HardwareAddr(data[7:13]),
|
||||
BaseLayer: BaseLayer{data[:13], data[13:]},
|
||||
}
|
||||
p.SetLinkLayer(f)
|
||||
p.AddLayer(f)
|
||||
return p.NextDecoder(f.FrameControl)
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file in the root of the source tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// FuzzLayer is a fuzz target for the layers package of gopacket
|
||||
// A fuzz target is a function processing a binary blob (byte slice)
|
||||
// The process here is to interpret this data as a packet, and print the layers contents.
|
||||
// The decoding options and the starting layer are encoded in the first bytes.
|
||||
// The function returns 1 if this is a valid packet (no error layer)
|
||||
func FuzzLayer(data []byte) int {
|
||||
if len(data) < 3 {
|
||||
return 0
|
||||
}
|
||||
// use the first two bytes to choose the top level layer
|
||||
startLayer := binary.BigEndian.Uint16(data[:2])
|
||||
var fuzzOpts = gopacket.DecodeOptions{
|
||||
Lazy: data[2]&0x1 != 0,
|
||||
NoCopy: data[2]&0x2 != 0,
|
||||
SkipDecodeRecovery: data[2]&0x4 != 0,
|
||||
DecodeStreamsAsDatagrams: data[2]&0x8 != 0,
|
||||
}
|
||||
p := gopacket.NewPacket(data[3:], gopacket.LayerType(startLayer), fuzzOpts)
|
||||
for _, l := range p.Layers() {
|
||||
gopacket.LayerString(l)
|
||||
}
|
||||
if p.ErrorLayer() != nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright 2016 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
|
||||
// Geneve Header:
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Virtual Network Identifier (VNI) | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Variable Length Options |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type Geneve struct {
|
||||
BaseLayer
|
||||
Version uint8 // 2 bits
|
||||
OptionsLength uint8 // 6 bits
|
||||
OAMPacket bool // 1 bits
|
||||
CriticalOption bool // 1 bits
|
||||
Protocol EthernetType // 16 bits
|
||||
VNI uint32 // 24bits
|
||||
Options []*GeneveOption
|
||||
}
|
||||
|
||||
// Geneve Tunnel Options
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Option Class | Type |R|R|R| Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Variable Option Data |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type GeneveOption struct {
|
||||
Class uint16 // 16 bits
|
||||
Type uint8 // 8 bits
|
||||
Flags uint8 // 3 bits
|
||||
Length uint8 // 5 bits
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeGeneve
|
||||
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
|
||||
|
||||
func decodeGeneveOption(data []byte, gn *Geneve, df gopacket.DecodeFeedback) (*GeneveOption, uint8, error) {
|
||||
if len(data) < 3 {
|
||||
df.SetTruncated()
|
||||
return nil, 0, errors.New("geneve option too small")
|
||||
}
|
||||
opt := &GeneveOption{}
|
||||
|
||||
opt.Class = binary.BigEndian.Uint16(data[0:2])
|
||||
opt.Type = data[2]
|
||||
opt.Flags = data[3] >> 4
|
||||
opt.Length = (data[3]&0xf)*4 + 4
|
||||
|
||||
if len(data) < int(opt.Length) {
|
||||
df.SetTruncated()
|
||||
return nil, 0, errors.New("geneve option too small")
|
||||
}
|
||||
opt.Data = make([]byte, opt.Length-4)
|
||||
copy(opt.Data, data[4:opt.Length])
|
||||
|
||||
return opt, opt.Length, nil
|
||||
}
|
||||
|
||||
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 7 {
|
||||
df.SetTruncated()
|
||||
return errors.New("geneve packet too short")
|
||||
}
|
||||
|
||||
gn.Version = data[0] >> 7
|
||||
gn.OptionsLength = (data[0] & 0x3f) * 4
|
||||
|
||||
gn.OAMPacket = data[1]&0x80 > 0
|
||||
gn.CriticalOption = data[1]&0x40 > 0
|
||||
gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
|
||||
var buf [4]byte
|
||||
copy(buf[1:], data[4:7])
|
||||
gn.VNI = binary.BigEndian.Uint32(buf[:])
|
||||
|
||||
offset, length := uint8(8), int32(gn.OptionsLength)
|
||||
if len(data) < int(length+7) {
|
||||
df.SetTruncated()
|
||||
return errors.New("geneve packet too short")
|
||||
}
|
||||
|
||||
for length > 0 {
|
||||
opt, len, err := decodeGeneveOption(data[offset:], gn, df)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gn.Options = append(gn.Options, opt)
|
||||
|
||||
length -= int32(len)
|
||||
offset += len
|
||||
}
|
||||
|
||||
gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gn *Geneve) NextLayerType() gopacket.LayerType {
|
||||
return gn.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
|
||||
gn := &Geneve{}
|
||||
return decodingLayerDecoder(gn, data, p)
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// GRE is a Generic Routing Encapsulation header.
|
||||
type GRE struct {
|
||||
BaseLayer
|
||||
ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute, AckPresent bool
|
||||
RecursionControl, Flags, Version uint8
|
||||
Protocol EthernetType
|
||||
Checksum, Offset uint16
|
||||
Key, Seq, Ack uint32
|
||||
*GRERouting
|
||||
}
|
||||
|
||||
// GRERouting is GRE routing information, present if the RoutingPresent flag is
|
||||
// set.
|
||||
type GRERouting struct {
|
||||
AddressFamily uint16
|
||||
SREOffset, SRELength uint8
|
||||
RoutingInformation []byte
|
||||
Next *GRERouting
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeGRE.
|
||||
func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
g.ChecksumPresent = data[0]&0x80 != 0
|
||||
g.RoutingPresent = data[0]&0x40 != 0
|
||||
g.KeyPresent = data[0]&0x20 != 0
|
||||
g.SeqPresent = data[0]&0x10 != 0
|
||||
g.StrictSourceRoute = data[0]&0x08 != 0
|
||||
g.AckPresent = data[1]&0x80 != 0
|
||||
g.RecursionControl = data[0] & 0x7
|
||||
g.Flags = data[1] >> 3
|
||||
g.Version = data[1] & 0x7
|
||||
g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
offset := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
g.Key = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
g.Seq = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
tail := &g.GRERouting
|
||||
for {
|
||||
sre := &GRERouting{
|
||||
AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]),
|
||||
SREOffset: data[offset+2],
|
||||
SRELength: data[offset+3],
|
||||
}
|
||||
sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)]
|
||||
offset += 4 + int(sre.SRELength)
|
||||
if sre.AddressFamily == 0 && sre.SRELength == 0 {
|
||||
break
|
||||
}
|
||||
(*tail) = sre
|
||||
tail = &sre.Next
|
||||
}
|
||||
}
|
||||
if g.AckPresent {
|
||||
g.Ack = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
g.BaseLayer = BaseLayer{data[:offset], data[offset:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the SerializationBuffer,
|
||||
// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info.
|
||||
func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
size := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
r := g.GRERouting
|
||||
for r != nil {
|
||||
size += 4 + int(r.SRELength)
|
||||
r = r.Next
|
||||
}
|
||||
size += 4
|
||||
}
|
||||
if g.AckPresent {
|
||||
size += 4
|
||||
}
|
||||
buf, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags.
|
||||
buf[0] = 0
|
||||
buf[1] = 0
|
||||
if g.ChecksumPresent {
|
||||
buf[0] |= 0x80
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
buf[0] |= 0x40
|
||||
}
|
||||
if g.KeyPresent {
|
||||
buf[0] |= 0x20
|
||||
}
|
||||
if g.SeqPresent {
|
||||
buf[0] |= 0x10
|
||||
}
|
||||
if g.StrictSourceRoute {
|
||||
buf[0] |= 0x08
|
||||
}
|
||||
if g.AckPresent {
|
||||
buf[1] |= 0x80
|
||||
}
|
||||
buf[0] |= g.RecursionControl
|
||||
buf[1] |= g.Flags << 3
|
||||
buf[1] |= g.Version
|
||||
binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol))
|
||||
offset := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
// Don't write the checksum value yet, as we may need to compute it,
|
||||
// which requires the entire header be complete.
|
||||
// Instead we zeroize the memory in case it is dirty.
|
||||
buf[offset] = 0
|
||||
buf[offset+1] = 0
|
||||
binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset)
|
||||
offset += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key)
|
||||
offset += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq)
|
||||
offset += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
sre := g.GRERouting
|
||||
for sre != nil {
|
||||
binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily)
|
||||
buf[offset+2] = sre.SREOffset
|
||||
buf[offset+3] = sre.SRELength
|
||||
copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation)
|
||||
offset += 4 + int(sre.SRELength)
|
||||
sre = sre.Next
|
||||
}
|
||||
// Terminate routing field with a "NULL" SRE.
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], 0)
|
||||
}
|
||||
if g.AckPresent {
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Ack)
|
||||
offset += 4
|
||||
}
|
||||
if g.ChecksumPresent {
|
||||
if opts.ComputeChecksums {
|
||||
g.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[4:6], g.Checksum)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (g *GRE) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeGRE
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (g *GRE) NextLayerType() gopacket.LayerType {
|
||||
return g.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeGRE(data []byte, p gopacket.PacketBuilder) error {
|
||||
g := &GRE{}
|
||||
return decodingLayerDecoder(g, data, p)
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// Copyright 2017 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
//
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const gtpMinimumSizeInBytes int = 8
|
||||
|
||||
// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number.
|
||||
type GTPExtensionHeader struct {
|
||||
Type uint8
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
|
||||
// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
|
||||
type GTPv1U struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
ProtocolType uint8
|
||||
Reserved uint8
|
||||
ExtensionHeaderFlag bool
|
||||
SequenceNumberFlag bool
|
||||
NPDUFlag bool
|
||||
MessageType uint8
|
||||
MessageLength uint16
|
||||
TEID uint32
|
||||
SequenceNumber uint16
|
||||
NPDU uint8
|
||||
GTPExtensionHeaders []GTPExtensionHeader
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeGTPV1U
|
||||
func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
|
||||
|
||||
// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
|
||||
func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
hLen := gtpMinimumSizeInBytes
|
||||
dLen := len(data)
|
||||
if dLen < hLen {
|
||||
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||||
}
|
||||
g.Version = (data[0] >> 5) & 0x07
|
||||
g.ProtocolType = (data[0] >> 4) & 0x01
|
||||
g.Reserved = (data[0] >> 3) & 0x01
|
||||
g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
|
||||
g.NPDUFlag = (data[0] & 0x01) == 1
|
||||
g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
|
||||
g.MessageType = data[1]
|
||||
g.MessageLength = binary.BigEndian.Uint16(data[2:4])
|
||||
pLen := 8 + g.MessageLength
|
||||
if uint16(dLen) < pLen {
|
||||
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||||
}
|
||||
// Field used to multiplex different connections in the same GTP tunnel.
|
||||
g.TEID = binary.BigEndian.Uint32(data[4:8])
|
||||
cIndex := uint16(hLen)
|
||||
if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
|
||||
hLen += 4
|
||||
cIndex += 4
|
||||
if dLen < hLen {
|
||||
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
|
||||
}
|
||||
if g.SequenceNumberFlag {
|
||||
g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
|
||||
}
|
||||
if g.NPDUFlag {
|
||||
g.NPDU = data[10]
|
||||
}
|
||||
if g.ExtensionHeaderFlag {
|
||||
extensionFlag := true
|
||||
for extensionFlag {
|
||||
extensionType := uint8(data[cIndex-1])
|
||||
extensionLength := uint(data[cIndex])
|
||||
if extensionLength == 0 {
|
||||
return fmt.Errorf("GTP packet with invalid extension header")
|
||||
}
|
||||
// extensionLength is in 4-octet units
|
||||
lIndex := cIndex + (uint16(extensionLength) * 4)
|
||||
if uint16(dLen) < lIndex {
|
||||
fmt.Println(dLen, lIndex)
|
||||
return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen)
|
||||
}
|
||||
content := data[cIndex+1 : lIndex-1]
|
||||
eh := GTPExtensionHeader{Type: extensionType, Content: content}
|
||||
g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh)
|
||||
cIndex = lIndex
|
||||
// Check if coming bytes are from an extension header
|
||||
extensionFlag = data[cIndex-1] != 0
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (g *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
data, err := b.PrependBytes(gtpMinimumSizeInBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[0] |= (g.Version << 5)
|
||||
data[0] |= (1 << 4)
|
||||
if len(g.GTPExtensionHeaders) > 0 {
|
||||
data[0] |= 0x04
|
||||
g.ExtensionHeaderFlag = true
|
||||
}
|
||||
if g.SequenceNumberFlag {
|
||||
data[0] |= 0x02
|
||||
}
|
||||
if g.NPDUFlag {
|
||||
data[0] |= 0x01
|
||||
}
|
||||
data[1] = g.MessageType
|
||||
binary.BigEndian.PutUint16(data[2:4], g.MessageLength)
|
||||
binary.BigEndian.PutUint32(data[4:8], g.TEID)
|
||||
if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag {
|
||||
data, err := b.AppendBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binary.BigEndian.PutUint16(data[:2], g.SequenceNumber)
|
||||
data[2] = g.NPDU
|
||||
for _, eh := range g.GTPExtensionHeaders {
|
||||
data[len(data)-1] = eh.Type
|
||||
lContent := len(eh.Content)
|
||||
// extensionLength is in 4-octet units
|
||||
extensionLength := (lContent + 2) / 4
|
||||
// Get two extra byte for the next extension header type and length
|
||||
data, err = b.AppendBytes(lContent + 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[0] = byte(extensionLength)
|
||||
copy(data[1:lContent+1], eh.Content)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// CanDecode returns a set of layers that GTP objects can decode.
|
||||
func (g *GTPv1U) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeGTPv1U
|
||||
}
|
||||
|
||||
// NextLayerType specifies the next layer that GoPacket should attempt to
|
||||
func (g *GTPv1U) NextLayerType() gopacket.LayerType {
|
||||
if len(g.LayerPayload()) == 0 {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
version := uint8(g.LayerPayload()[0]) >> 4
|
||||
if version == 4 {
|
||||
return LayerTypeIPv4
|
||||
} else if version == 6 {
|
||||
return LayerTypeIPv6
|
||||
} else {
|
||||
return LayerTypePPP
|
||||
}
|
||||
}
|
||||
|
||||
func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
|
||||
gtp := >Pv1U{}
|
||||
err := gtp.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(gtp)
|
||||
return p.NextDecoder(gtp.NextLayerType())
|
||||
}
|
||||
+11351
File diff suppressed because it is too large
Load Diff
+267
@@ -0,0 +1,267 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
ICMPv4TypeEchoReply = 0
|
||||
ICMPv4TypeDestinationUnreachable = 3
|
||||
ICMPv4TypeSourceQuench = 4
|
||||
ICMPv4TypeRedirect = 5
|
||||
ICMPv4TypeEchoRequest = 8
|
||||
ICMPv4TypeRouterAdvertisement = 9
|
||||
ICMPv4TypeRouterSolicitation = 10
|
||||
ICMPv4TypeTimeExceeded = 11
|
||||
ICMPv4TypeParameterProblem = 12
|
||||
ICMPv4TypeTimestampRequest = 13
|
||||
ICMPv4TypeTimestampReply = 14
|
||||
ICMPv4TypeInfoRequest = 15
|
||||
ICMPv4TypeInfoReply = 16
|
||||
ICMPv4TypeAddressMaskRequest = 17
|
||||
ICMPv4TypeAddressMaskReply = 18
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv4CodeNet = 0
|
||||
ICMPv4CodeHost = 1
|
||||
ICMPv4CodeProtocol = 2
|
||||
ICMPv4CodePort = 3
|
||||
ICMPv4CodeFragmentationNeeded = 4
|
||||
ICMPv4CodeSourceRoutingFailed = 5
|
||||
ICMPv4CodeNetUnknown = 6
|
||||
ICMPv4CodeHostUnknown = 7
|
||||
ICMPv4CodeSourceIsolated = 8
|
||||
ICMPv4CodeNetAdminProhibited = 9
|
||||
ICMPv4CodeHostAdminProhibited = 10
|
||||
ICMPv4CodeNetTOS = 11
|
||||
ICMPv4CodeHostTOS = 12
|
||||
ICMPv4CodeCommAdminProhibited = 13
|
||||
ICMPv4CodeHostPrecedence = 14
|
||||
ICMPv4CodePrecedenceCutoff = 15
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv4CodeTTLExceeded = 0
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv4CodePointerIndicatesError = 0
|
||||
ICMPv4CodeMissingOption = 1
|
||||
ICMPv4CodeBadLength = 2
|
||||
|
||||
// Redirect
|
||||
// ICMPv4CodeNet = same as for DestinationUnreachable
|
||||
// ICMPv4CodeHost = same as for DestinationUnreachable
|
||||
ICMPv4CodeTOSNet = 2
|
||||
ICMPv4CodeTOSHost = 3
|
||||
)
|
||||
|
||||
type icmpv4TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
|
||||
ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeProtocol: "Protocol",
|
||||
ICMPv4CodePort: "Port",
|
||||
ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
|
||||
ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
|
||||
ICMPv4CodeNetUnknown: "NetUnknown",
|
||||
ICMPv4CodeHostUnknown: "HostUnknown",
|
||||
ICMPv4CodeSourceIsolated: "SourceIsolated",
|
||||
ICMPv4CodeNetAdminProhibited: "NetAdminProhibited",
|
||||
ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
|
||||
ICMPv4CodeNetTOS: "NetTOS",
|
||||
ICMPv4CodeHostTOS: "HostTOS",
|
||||
ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
|
||||
ICMPv4CodeHostPrecedence: "HostPrecedence",
|
||||
ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv4CodeTTLExceeded: "TTLExceeded",
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
|
||||
ICMPv4CodeMissingOption: "MissingOption",
|
||||
ICMPv4CodeBadLength: "BadLength",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
|
||||
"SourceQuench", nil,
|
||||
},
|
||||
ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
|
||||
"Redirect", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeTOSNet: "TOS+Net",
|
||||
ICMPv4CodeTOSHost: "TOS+Host",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampRequest", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampReply", nil,
|
||||
},
|
||||
ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"InfoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
|
||||
"InfoReply", nil,
|
||||
},
|
||||
ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskRequest", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskReply", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv4TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv4 type field.
|
||||
func (a ICMPv4TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv4 code field.
|
||||
func (a ICMPv4TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv4TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv4 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv4 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv4 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv4 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
|
||||
// gopacket type from the ICMPv4 type and code values.
|
||||
func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
|
||||
return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv4 is the layer for IPv4 ICMP packet data.
|
||||
type ICMPv4 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv4TypeCode
|
||||
Checksum uint16
|
||||
Id uint16
|
||||
Seq uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv4.
|
||||
func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.Id = binary.BigEndian.Uint16(data[4:6])
|
||||
i.Seq = binary.BigEndian.Uint16(data[6:8])
|
||||
i.BaseLayer = BaseLayer{data[:8], data[8:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
binary.BigEndian.PutUint16(bytes[4:], i.Id)
|
||||
binary.BigEndian.PutUint16(bytes[6:], i.Seq)
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
i.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv4
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv4) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv4{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following are from RFC 4443
|
||||
ICMPv6TypeDestinationUnreachable = 1
|
||||
ICMPv6TypePacketTooBig = 2
|
||||
ICMPv6TypeTimeExceeded = 3
|
||||
ICMPv6TypeParameterProblem = 4
|
||||
ICMPv6TypeEchoRequest = 128
|
||||
ICMPv6TypeEchoReply = 129
|
||||
|
||||
// The following are from RFC 4861
|
||||
ICMPv6TypeRouterSolicitation = 133
|
||||
ICMPv6TypeRouterAdvertisement = 134
|
||||
ICMPv6TypeNeighborSolicitation = 135
|
||||
ICMPv6TypeNeighborAdvertisement = 136
|
||||
ICMPv6TypeRedirect = 137
|
||||
|
||||
// The following are from RFC 2710
|
||||
ICMPv6TypeMLDv1MulticastListenerQueryMessage = 130
|
||||
ICMPv6TypeMLDv1MulticastListenerReportMessage = 131
|
||||
ICMPv6TypeMLDv1MulticastListenerDoneMessage = 132
|
||||
|
||||
// The following are from RFC 3810
|
||||
ICMPv6TypeMLDv2MulticastListenerReportMessageV2 = 143
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv6CodeNoRouteToDst = 0
|
||||
ICMPv6CodeAdminProhibited = 1
|
||||
ICMPv6CodeBeyondScopeOfSrc = 2
|
||||
ICMPv6CodeAddressUnreachable = 3
|
||||
ICMPv6CodePortUnreachable = 4
|
||||
ICMPv6CodeSrcAddressFailedPolicy = 5
|
||||
ICMPv6CodeRejectRouteToDst = 6
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv6CodeHopLimitExceeded = 0
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv6CodeErroneousHeaderField = 0
|
||||
ICMPv6CodeUnrecognizedNextHeader = 1
|
||||
ICMPv6CodeUnrecognizedIPv6Option = 2
|
||||
)
|
||||
|
||||
type icmpv6TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{
|
||||
ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv6CodeNoRouteToDst: "NoRouteToDst",
|
||||
ICMPv6CodeAdminProhibited: "AdminProhibited",
|
||||
ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc",
|
||||
ICMPv6CodeAddressUnreachable: "AddressUnreachable",
|
||||
ICMPv6CodePortUnreachable: "PortUnreachable",
|
||||
ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy",
|
||||
ICMPv6CodeRejectRouteToDst: "RejectRouteToDst",
|
||||
},
|
||||
},
|
||||
ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{
|
||||
"PacketTooBig", nil,
|
||||
},
|
||||
ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv6CodeHopLimitExceeded: "HopLimitExceeded",
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField",
|
||||
ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader",
|
||||
ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{
|
||||
"Redirect", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv6TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv6 type field.
|
||||
func (a ICMPv6TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv6 code field.
|
||||
func (a ICMPv6TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv6TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv6 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv6 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv6 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv6 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv6TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode
|
||||
// gopacket type from the ICMPv6 type and code values.
|
||||
func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode {
|
||||
return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv6 is the layer for IPv6 ICMP packet data
|
||||
type ICMPv6 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv6TypeCode
|
||||
Checksum uint16
|
||||
// TypeBytes is deprecated and always nil. See the different ICMPv6 message types
|
||||
// instead (e.g. ICMPv6TypeRouterSolicitation).
|
||||
TypeBytes []byte
|
||||
tcpipchecksum
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6.
|
||||
func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Checksum = csum
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6) NextLayerType() gopacket.LayerType {
|
||||
switch i.TypeCode.Type() {
|
||||
case ICMPv6TypeEchoRequest:
|
||||
return LayerTypeICMPv6Echo
|
||||
case ICMPv6TypeEchoReply:
|
||||
return LayerTypeICMPv6Echo
|
||||
case ICMPv6TypeRouterSolicitation:
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
case ICMPv6TypeRouterAdvertisement:
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
case ICMPv6TypeNeighborSolicitation:
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
case ICMPv6TypeNeighborAdvertisement:
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
case ICMPv6TypeRedirect:
|
||||
return LayerTypeICMPv6Redirect
|
||||
case ICMPv6TypeMLDv1MulticastListenerQueryMessage: // Same Code for MLDv1 Query and MLDv2 Query
|
||||
if len(i.Payload) > 20 { // Only payload size differs
|
||||
return LayerTypeMLDv2MulticastListenerQuery
|
||||
} else {
|
||||
return LayerTypeMLDv1MulticastListenerQuery
|
||||
}
|
||||
case ICMPv6TypeMLDv1MulticastListenerDoneMessage:
|
||||
return LayerTypeMLDv1MulticastListenerDone
|
||||
case ICMPv6TypeMLDv1MulticastListenerReportMessage:
|
||||
return LayerTypeMLDv1MulticastListenerReport
|
||||
case ICMPv6TypeMLDv2MulticastListenerReportMessageV2:
|
||||
return LayerTypeMLDv2MulticastListenerReport
|
||||
}
|
||||
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+578
@@ -0,0 +1,578 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Based on RFC 4861
|
||||
|
||||
// ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option.
|
||||
type ICMPv6Opt uint8
|
||||
|
||||
const (
|
||||
_ ICMPv6Opt = iota
|
||||
|
||||
// ICMPv6OptSourceAddress contains the link-layer address of the sender of
|
||||
// the packet. It is used in the Neighbor Solicitation, Router
|
||||
// Solicitation, and Router Advertisement packets. Must be ignored for other
|
||||
// Neighbor discovery messages.
|
||||
ICMPv6OptSourceAddress
|
||||
|
||||
// ICMPv6OptTargetAddress contains the link-layer address of the target. It
|
||||
// is used in Neighbor Advertisement and Redirect packets. Must be ignored
|
||||
// for other Neighbor discovery messages.
|
||||
ICMPv6OptTargetAddress
|
||||
|
||||
// ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes
|
||||
// for Address Autoconfiguration. The Prefix Information option appears in
|
||||
// Router Advertisement packets and MUST be silently ignored for other
|
||||
// messages.
|
||||
ICMPv6OptPrefixInfo
|
||||
|
||||
// ICMPv6OptRedirectedHeader is used in Redirect messages and contains all
|
||||
// or part of the packet that is being redirected.
|
||||
ICMPv6OptRedirectedHeader
|
||||
|
||||
// ICMPv6OptMTU is used in Router Advertisement messages to ensure that all
|
||||
// nodes on a link use the same MTU value in those cases where the link MTU
|
||||
// is not well known. This option MUST be silently ignored for other
|
||||
// Neighbor Discovery messages.
|
||||
ICMPv6OptMTU
|
||||
)
|
||||
|
||||
// ICMPv6Echo represents the structure of a ping.
|
||||
type ICMPv6Echo struct {
|
||||
BaseLayer
|
||||
Identifier uint16
|
||||
SeqNumber uint16
|
||||
}
|
||||
|
||||
// ICMPv6RouterSolicitation is sent by hosts to find routers.
|
||||
type ICMPv6RouterSolicitation struct {
|
||||
BaseLayer
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6RouterAdvertisement is sent by routers in response to Solicitation.
|
||||
type ICMPv6RouterAdvertisement struct {
|
||||
BaseLayer
|
||||
HopLimit uint8
|
||||
Flags uint8
|
||||
RouterLifetime uint16
|
||||
ReachableTime uint32
|
||||
RetransTimer uint32
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6NeighborSolicitation is sent to request the link-layer address of a
|
||||
// target node.
|
||||
type ICMPv6NeighborSolicitation struct {
|
||||
BaseLayer
|
||||
TargetAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation.
|
||||
type ICMPv6NeighborAdvertisement struct {
|
||||
BaseLayer
|
||||
Flags uint8
|
||||
TargetAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node
|
||||
// on the path to a destination.
|
||||
type ICMPv6Redirect struct {
|
||||
BaseLayer
|
||||
TargetAddress net.IP
|
||||
DestinationAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6Option contains the type and data for a single option.
|
||||
type ICMPv6Option struct {
|
||||
Type ICMPv6Opt
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ICMPv6Options is a slice of ICMPv6Option.
|
||||
type ICMPv6Options []ICMPv6Option
|
||||
|
||||
func (i ICMPv6Opt) String() string {
|
||||
switch i {
|
||||
case ICMPv6OptSourceAddress:
|
||||
return "SourceAddress"
|
||||
case ICMPv6OptTargetAddress:
|
||||
return "TargetAddress"
|
||||
case ICMPv6OptPrefixInfo:
|
||||
return "PrefixInfo"
|
||||
case ICMPv6OptRedirectedHeader:
|
||||
return "RedirectedHeader"
|
||||
case ICMPv6OptMTU:
|
||||
return "MTU"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown(%d)", i)
|
||||
}
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6Echo
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6Echo.
|
||||
func (i *ICMPv6Echo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6Echo
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6Echo) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Echo) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 Echo")
|
||||
}
|
||||
i.Identifier = binary.BigEndian.Uint16(data[0:2])
|
||||
i.SeqNumber = binary.BigEndian.Uint16(data[2:4])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Echo) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
buf, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf, i.Identifier)
|
||||
binary.BigEndian.PutUint16(buf[2:], i.SeqNumber)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6.
|
||||
func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
// first 4 bytes are reserved followed by options
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation")
|
||||
}
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[4:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6RouterSolicitation) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6RouterAdvertisement.
|
||||
func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 12 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement")
|
||||
}
|
||||
|
||||
i.HopLimit = uint8(data[0])
|
||||
// M, O bit followed by 6 reserved bits
|
||||
i.Flags = uint8(data[1])
|
||||
i.RouterLifetime = binary.BigEndian.Uint16(data[2:4])
|
||||
i.ReachableTime = binary.BigEndian.Uint32(data[4:8])
|
||||
i.RetransTimer = binary.BigEndian.Uint32(data[8:12])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[12:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(12)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(i.HopLimit)
|
||||
buf[1] = byte(i.Flags)
|
||||
binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime)
|
||||
binary.BigEndian.PutUint32(buf[4:], i.ReachableTime)
|
||||
binary.BigEndian.PutUint32(buf[8:], i.RetransTimer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6RouterAdvertisement) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
}
|
||||
|
||||
// ManagedAddressConfig is true when addresses are available via DHCPv6. If
|
||||
// set, the OtherConfig flag is redundant.
|
||||
func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool {
|
||||
return i.Flags&0x80 != 0
|
||||
}
|
||||
|
||||
// OtherConfig is true when there is other configuration information available
|
||||
// via DHCPv6. For example, DNS-related information.
|
||||
func (i *ICMPv6RouterAdvertisement) OtherConfig() bool {
|
||||
return i.Flags&0x40 != 0
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6NeighborSolicitation.
|
||||
func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 20 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation")
|
||||
}
|
||||
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[20:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(20)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6NeighborSolicitation) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6NeighborAdvertisement.
|
||||
func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 20 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement")
|
||||
}
|
||||
|
||||
i.Flags = uint8(data[0])
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[20:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(20)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(i.Flags)
|
||||
copy(buf[1:], lotsOfZeros[:3])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6NeighborAdvertisement) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
}
|
||||
|
||||
// Router indicates whether the sender is a router or not.
|
||||
func (i *ICMPv6NeighborAdvertisement) Router() bool {
|
||||
return i.Flags&0x80 != 0
|
||||
}
|
||||
|
||||
// Solicited indicates whether the advertisement was solicited or not.
|
||||
func (i *ICMPv6NeighborAdvertisement) Solicited() bool {
|
||||
return i.Flags&0x40 != 0
|
||||
}
|
||||
|
||||
// Override indicates whether the advertisement should Override an existing
|
||||
// cache entry.
|
||||
func (i *ICMPv6NeighborAdvertisement) Override() bool {
|
||||
return i.Flags&0x20 != 0
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6Redirect.
|
||||
func (i *ICMPv6Redirect) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6Redirect
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 36 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect")
|
||||
}
|
||||
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.DestinationAddress = net.IP(data[20:36])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[36:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(36)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
copy(buf[20:], i.DestinationAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6Redirect) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6Redirect
|
||||
}
|
||||
|
||||
func (i ICMPv6Option) String() string {
|
||||
hd := hex.EncodeToString(i.Data)
|
||||
if len(hd) > 0 {
|
||||
hd = " 0x" + hd
|
||||
}
|
||||
|
||||
switch i.Type {
|
||||
case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress:
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v)",
|
||||
i.Type,
|
||||
net.HardwareAddr(i.Data))
|
||||
case ICMPv6OptPrefixInfo:
|
||||
if len(i.Data) == 30 {
|
||||
prefixLen := uint8(i.Data[0])
|
||||
onLink := (i.Data[1]&0x80 != 0)
|
||||
autonomous := (i.Data[1]&0x40 != 0)
|
||||
validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second
|
||||
preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second
|
||||
|
||||
prefix := net.IP(i.Data[14:])
|
||||
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)",
|
||||
i.Type,
|
||||
prefix, prefixLen,
|
||||
onLink, autonomous,
|
||||
validLifetime, preferredLifetime)
|
||||
}
|
||||
case ICMPv6OptRedirectedHeader:
|
||||
// could invoke IP decoder on data... probably best not to
|
||||
break
|
||||
case ICMPv6OptMTU:
|
||||
if len(i.Data) == 6 {
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v)",
|
||||
i.Type,
|
||||
binary.BigEndian.Uint32(i.Data[2:]))
|
||||
}
|
||||
|
||||
}
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd)
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
for len(data) > 0 {
|
||||
if len(data) < 2 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option")
|
||||
}
|
||||
|
||||
// unit is 8 octets, convert to bytes
|
||||
length := int(data[1]) * 8
|
||||
|
||||
if length == 0 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMPv6 message option with length 0")
|
||||
}
|
||||
|
||||
if len(data) < length {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length)
|
||||
}
|
||||
|
||||
o := ICMPv6Option{
|
||||
Type: ICMPv6Opt(data[0]),
|
||||
Data: data[2:length],
|
||||
}
|
||||
|
||||
// chop off option we just consumed
|
||||
data = data[length:]
|
||||
|
||||
*i = append(*i, o)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
for _, opt := range []ICMPv6Option(*i) {
|
||||
length := len(opt.Data) + 2
|
||||
buf, err := b.PrependBytes(length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(opt.Type)
|
||||
buf[1] = byte(length / 8)
|
||||
copy(buf[2:], opt.Data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeICMPv6Echo(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6Echo{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6RouterSolicitation{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6RouterAdvertisement{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6NeighborSolicitation{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6NeighborAdvertisement{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6Redirect{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type IGMPType uint8
|
||||
|
||||
const (
|
||||
IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
|
||||
IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
|
||||
IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
|
||||
IGMPLeaveGroup IGMPType = 0x17 // Leave Group
|
||||
IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
|
||||
)
|
||||
|
||||
// String conversions for IGMP message types
|
||||
func (i IGMPType) String() string {
|
||||
switch i {
|
||||
case IGMPMembershipQuery:
|
||||
return "IGMP Membership Query"
|
||||
case IGMPMembershipReportV1:
|
||||
return "IGMPv1 Membership Report"
|
||||
case IGMPMembershipReportV2:
|
||||
return "IGMPv2 Membership Report"
|
||||
case IGMPMembershipReportV3:
|
||||
return "IGMPv3 Membership Report"
|
||||
case IGMPLeaveGroup:
|
||||
return "Leave Group"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type IGMPv3GroupRecordType uint8
|
||||
|
||||
const (
|
||||
IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
|
||||
IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
|
||||
IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
|
||||
IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
|
||||
IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
|
||||
IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
|
||||
)
|
||||
|
||||
func (i IGMPv3GroupRecordType) String() string {
|
||||
switch i {
|
||||
case IGMPIsIn:
|
||||
return "MODE_IS_INCLUDE"
|
||||
case IGMPIsEx:
|
||||
return "MODE_IS_EXCLUDE"
|
||||
case IGMPToIn:
|
||||
return "CHANGE_TO_INCLUDE_MODE"
|
||||
case IGMPToEx:
|
||||
return "CHANGE_TO_EXCLUDE_MODE"
|
||||
case IGMPAllow:
|
||||
return "ALLOW_NEW_SOURCES"
|
||||
case IGMPBlock:
|
||||
return "BLOCK_OLD_SOURCES"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// IGMP represents an IGMPv3 message.
|
||||
type IGMP struct {
|
||||
BaseLayer
|
||||
Type IGMPType
|
||||
MaxResponseTime time.Duration
|
||||
Checksum uint16
|
||||
GroupAddress net.IP
|
||||
SupressRouterProcessing bool
|
||||
RobustnessValue uint8
|
||||
IntervalTime time.Duration
|
||||
SourceAddresses []net.IP
|
||||
NumberOfGroupRecords uint16
|
||||
NumberOfSources uint16
|
||||
GroupRecords []IGMPv3GroupRecord
|
||||
Version uint8 // IGMP protocol version
|
||||
}
|
||||
|
||||
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Max Resp Time | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type IGMPv1or2 struct {
|
||||
BaseLayer
|
||||
Type IGMPType // IGMP message type
|
||||
MaxResponseTime time.Duration // meaningful only in Membership Query messages
|
||||
Checksum uint16 // 16-bit checksum of entire ip payload
|
||||
GroupAddress net.IP // either 0 or an IP multicast address
|
||||
Version uint8
|
||||
}
|
||||
|
||||
// decodeResponse dissects IGMPv1 or IGMPv2 packet.
|
||||
func (i *IGMPv1or2) decodeResponse(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP packet too small")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x22 | Reserved | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Reserved | Number of Group Records (M) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [1] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [2] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [M] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Record Type | Aux Data Len | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Multicast Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Auxiliary Data .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
|
||||
type IGMPv3GroupRecord struct {
|
||||
Type IGMPv3GroupRecordType
|
||||
AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
|
||||
NumberOfSources uint16
|
||||
MulticastAddress net.IP
|
||||
SourceAddresses []net.IP
|
||||
AuxData uint32 // NOT USED
|
||||
}
|
||||
|
||||
func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #1")
|
||||
}
|
||||
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
|
||||
|
||||
recordOffset := 8
|
||||
for j := 0; j < int(i.NumberOfGroupRecords); j++ {
|
||||
if len(data) < recordOffset+8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #2")
|
||||
}
|
||||
|
||||
var gr IGMPv3GroupRecord
|
||||
gr.Type = IGMPv3GroupRecordType(data[recordOffset])
|
||||
gr.AuxDataLen = data[recordOffset+1]
|
||||
gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
|
||||
gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
|
||||
|
||||
if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Report too small #3")
|
||||
}
|
||||
|
||||
// append source address records.
|
||||
for i := 0; i < int(gr.NumberOfSources); i++ {
|
||||
sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
|
||||
gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
|
||||
}
|
||||
|
||||
i.GroupRecords = append(i.GroupRecords, gr)
|
||||
recordOffset += 8 + 4*int(gr.NumberOfSources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x11 | Max Resp Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- . -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
|
||||
func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
|
||||
if len(data) < 12 {
|
||||
return errors.New("IGMPv3 Membership Query too small #1")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.SupressRouterProcessing = data[8]&0x8 != 0
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
i.RobustnessValue = data[8] & 0x7
|
||||
i.IntervalTime = igmpTimeDecode(data[9])
|
||||
i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
|
||||
|
||||
if len(data) < 12+int(i.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Query too small #2")
|
||||
}
|
||||
|
||||
for j := 0; j < int(i.NumberOfSources); j++ {
|
||||
i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// igmpTimeDecode decodes the duration created by the given byte, using the
|
||||
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
|
||||
func igmpTimeDecode(t uint8) time.Duration {
|
||||
if t&0x80 == 0 {
|
||||
return time.Millisecond * 100 * time.Duration(t)
|
||||
}
|
||||
mant := (t & 0x70) >> 4
|
||||
exp := t & 0x0F
|
||||
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
|
||||
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
|
||||
func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP Packet too small")
|
||||
}
|
||||
|
||||
i.Type = IGMPType(data[0])
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// common IGMP header values between versions 1..3 of IGMP specification..
|
||||
i.Type = IGMPType(data[0])
|
||||
|
||||
switch i.Type {
|
||||
case IGMPMembershipQuery:
|
||||
i.decodeIGMPv3MembershipQuery(data)
|
||||
case IGMPMembershipReportV3:
|
||||
i.decodeIGMPv3MembershipReport(data)
|
||||
default:
|
||||
return errors.New("unsupported IGMP type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *IGMP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *IGMP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
|
||||
// IGMP type are performed against byte[0], logic then iniitalizes and
|
||||
// passes the appropriate struct (IGMP or IGMPv1or2) to
|
||||
// decodingLayerDecoder.
|
||||
func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// byte 0 contains IGMP message type.
|
||||
switch IGMPType(data[0]) {
|
||||
case IGMPMembershipQuery:
|
||||
// IGMPv3 Membership Query payload is >= 12
|
||||
if len(data) >= 12 {
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
} else if len(data) == 8 {
|
||||
i := &IGMPv1or2{}
|
||||
if data[1] == 0x00 {
|
||||
i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
|
||||
} else {
|
||||
i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
|
||||
}
|
||||
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
case IGMPMembershipReportV3:
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPMembershipReportV1:
|
||||
i := &IGMPv1or2{Version: 1}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPLeaveGroup, IGMPMembershipReportV2:
|
||||
// leave group and Query Report v2 used in IGMPv2 only.
|
||||
i := &IGMPv1or2{Version: 2}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
default:
|
||||
}
|
||||
|
||||
return errors.New("Unable to determine IGMP type.")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user