mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0440abbc91 | ||
|
|
143991124c | ||
|
|
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 |
+9
-7
@@ -1,13 +1,15 @@
|
||||
FROM golang:1.14.9-alpine3.11 as builder
|
||||
FROM golang:1.21.0-alpine3.18 as builder
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
ENV GO111MODULE=on
|
||||
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
|
||||
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.11
|
||||
FROM alpine:3.18
|
||||
RUN apk add --no-cache iptables
|
||||
COPY --from=builder /go/bin /bin
|
||||
COPY --from=builder /client /bin
|
||||
COPY --from=builder /server /bin
|
||||
EXPOSE 29900/udp
|
||||
EXPOSE 12948
|
||||
|
||||
@@ -106,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*
|
||||
|
||||
@@ -146,7 +146,7 @@ COMMANDS:
|
||||
|
||||
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")
|
||||
@@ -188,7 +188,7 @@ COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--listen value, -l value kcp server listen address (default: ":29900")
|
||||
--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")
|
||||
@@ -216,6 +216,21 @@ GLOBAL OPTIONS:
|
||||
--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.
|
||||
@@ -291,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
|
||||
|
||||
+13
-11
@@ -57,12 +57,6 @@ for os in ${OSES[@]}; do
|
||||
$sum kcptun-${os}-386-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
#Apple M1 device
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_darwin_arm64 github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_darwin_arm64 github.com/xtaci/kcptun/client
|
||||
tar -zcf kcptun-darwin-arm64-$VERSION.tar.gz client_darwin_arm64 server_darwin_arm64
|
||||
$sum kcptun-darwin-arm64-$VERSION.tar.gz
|
||||
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
@@ -74,11 +68,19 @@ $sum kcptun-linux-arm$v-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# ARM64
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -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 -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
|
||||
@@ -37,6 +37,7 @@ type Config struct {
|
||||
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.
+22
-3
@@ -1,18 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
+34
-7
@@ -7,6 +7,8 @@ import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -98,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",
|
||||
@@ -109,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",
|
||||
@@ -243,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{}
|
||||
@@ -276,6 +282,7 @@ func main() {
|
||||
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"))
|
||||
@@ -302,10 +309,22 @@ 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())
|
||||
@@ -329,6 +348,7 @@ func main() {
|
||||
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 {
|
||||
@@ -340,6 +360,8 @@ func main() {
|
||||
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":
|
||||
@@ -429,6 +451,11 @@ func main() {
|
||||
// start snmp logger
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
|
||||
// start pprof
|
||||
if config.Pprof {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
|
||||
// start scavenger
|
||||
chScavenger := make(chan timedSession, 128)
|
||||
go scavenger(chScavenger, &config)
|
||||
@@ -438,7 +465,7 @@ func main() {
|
||||
muxes := make([]timedSession, numconn)
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
p1, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[unit]
|
||||
Description=kcptun
|
||||
|
||||
Wants=network.target
|
||||
|
||||
@@ -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,15 +1,29 @@
|
||||
module github.com/xtaci/kcptun
|
||||
|
||||
require (
|
||||
github.com/coreos/go-iptables v0.4.2 // indirect
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/google/gopacket v1.1.17 // indirect
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/urfave/cli v1.21.0
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||
github.com/xtaci/smux v1.5.16
|
||||
github.com/urfave/cli v1.22.14
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5
|
||||
github.com/xtaci/smux v1.5.24
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
golang.org/x/crypto v0.14.0
|
||||
)
|
||||
|
||||
go 1.14
|
||||
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.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
)
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.0
|
||||
|
||||
@@ -1,89 +1,134 @@
|
||||
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/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
|
||||
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
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/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.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
|
||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
|
||||
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
|
||||
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/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
|
||||
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
|
||||
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
|
||||
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
|
||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
|
||||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92 h1:a9X2tFWnJR0JjYSz2n4PdmTGtDHtlnU7VI5bNjUDsyI=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547 h1:SFJWLwOnjTJCt7JKf2fYH0RWtWJTkvYKIEfLvjJfOqM=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17 h1:bkdaqtER0PMlP05BBHfu6W+71kt/NwbAk93KH7F78Ck=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
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/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q=
|
||||
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.14 h1:1j+zJYDZRv9FHaWqCJfH5RPizIm0fSzJIFbfVn8zsfg=
|
||||
github.com/xtaci/smux v1.5.14/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.15 h1:6hMiXswcleXj5oNfcJc+DXS8Vj36XX2LaX98udog6Kc=
|
||||
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
|
||||
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs=
|
||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 h1:4JSJPND/+4555t1HfXYF4UEqDqiSKCgeV0+hbA8hMs4=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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-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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/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.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
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
|
||||
+30
-15
@@ -133,7 +133,7 @@ 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",
|
||||
@@ -149,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",
|
||||
@@ -361,6 +361,8 @@ func main() {
|
||||
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":
|
||||
@@ -430,22 +432,35 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if config.TCP { // tcp dual stack
|
||||
if conn, err := tcpraw.Listen("tcp", config.Listen); err == nil {
|
||||
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
} else {
|
||||
log.Println(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)
|
||||
}
|
||||
|
||||
// udp stack
|
||||
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
+175
-54
@@ -31,7 +31,6 @@ type Error struct {
|
||||
exec.ExitError
|
||||
cmd exec.Cmd
|
||||
msg string
|
||||
proto Protocol
|
||||
exitStatus *int //for overriding
|
||||
}
|
||||
|
||||
@@ -48,9 +47,13 @@ func (e *Error) Error() string {
|
||||
|
||||
// IsNotExist returns true if the error is due to the chain or rule not existing
|
||||
func (e *Error) IsNotExist() bool {
|
||||
return e.ExitStatus() == 1 &&
|
||||
(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
|
||||
e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
|
||||
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
|
||||
@@ -62,15 +65,17 @@ const (
|
||||
)
|
||||
|
||||
type IPTables struct {
|
||||
path string
|
||||
proto Protocol
|
||||
hasCheck bool
|
||||
hasWait bool
|
||||
hasRandomFully bool
|
||||
v1 int
|
||||
v2 int
|
||||
v3 int
|
||||
mode string // the underlying iptables operating mode, e.g. nf_tables
|
||||
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.
|
||||
@@ -87,36 +92,69 @@ type Stat struct {
|
||||
Options string `json:"options"`
|
||||
}
|
||||
|
||||
// New creates a new IPTables.
|
||||
// For backwards compatibility, this always uses IPv4, i.e. "iptables".
|
||||
func New() (*IPTables, error) {
|
||||
return NewWithProtocol(ProtocolIPv4)
|
||||
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) {
|
||||
path, err := exec.LookPath(getIptablesCommand(proto))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vstring, err := getIptablesVersionString(path)
|
||||
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
|
||||
|
||||
checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
|
||||
|
||||
ipt := IPTables{
|
||||
path: path,
|
||||
proto: proto,
|
||||
hasCheck: checkPresent,
|
||||
hasWait: waitPresent,
|
||||
hasRandomFully: randomFullyPresent,
|
||||
v1: v1,
|
||||
v2: v2,
|
||||
v3: v3,
|
||||
mode: mode,
|
||||
}
|
||||
return &ipt, nil
|
||||
return New(IPFamily(proto), Timeout(0))
|
||||
}
|
||||
|
||||
// Proto returns the protocol used by this IPTables.
|
||||
@@ -149,6 +187,26 @@ func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) er
|
||||
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...)
|
||||
@@ -175,6 +233,24 @@ func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
|
||||
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}
|
||||
@@ -212,6 +288,21 @@ func (ipt *IPTables) ListChains(table string) ([]string, error) {
|
||||
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"}
|
||||
@@ -232,6 +323,11 @@ func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
|
||||
|
||||
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
|
||||
@@ -348,18 +444,6 @@ func (ipt *IPTables) executeList(args []string) ([]string, error) {
|
||||
rules = rules[:len(rules)-1]
|
||||
}
|
||||
|
||||
// nftables mode doesn't return an error code when listing a non-existent
|
||||
// chain. Patch that up.
|
||||
if len(rules) == 0 && ipt.mode == "nf_tables" {
|
||||
v := 1
|
||||
return nil, &Error{
|
||||
cmd: exec.Cmd{Args: args},
|
||||
msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)),
|
||||
proto: ipt.proto,
|
||||
exitStatus: &v,
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range rules {
|
||||
rules[i] = filterRuleOutput(rule)
|
||||
}
|
||||
@@ -403,6 +487,26 @@ 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)
|
||||
@@ -430,6 +534,9 @@ 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 {
|
||||
@@ -437,9 +544,12 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
|
||||
}
|
||||
ul, err := fmu.tryLock()
|
||||
if err != nil {
|
||||
syscall.Close(fmu.fd)
|
||||
return err
|
||||
}
|
||||
defer ul.Unlock()
|
||||
defer func() {
|
||||
_ = ul.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
@@ -453,7 +563,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
|
||||
if err := cmd.Run(); err != nil {
|
||||
switch e := err.(type) {
|
||||
case *exec.ExitError:
|
||||
return &Error{*e, cmd, stderr.String(), ipt.proto, nil}
|
||||
return &Error{*e, cmd, stderr.String(), nil}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
@@ -472,8 +582,8 @@ func getIptablesCommand(proto Protocol) string {
|
||||
}
|
||||
|
||||
// Checks if iptables has the "-C" and "--wait" flag
|
||||
func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) {
|
||||
return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3)
|
||||
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
|
||||
@@ -548,6 +658,17 @@ func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
|
||||
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 {
|
||||
|
||||
+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
|
||||
}
|
||||
}
|
||||
+3
@@ -8,8 +8,11 @@
|
||||
|
||||
# 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>
|
||||
|
||||
+4
@@ -26,9 +26,13 @@
|
||||
|
||||
# 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>
|
||||
|
||||
+57
-30
@@ -52,6 +52,8 @@ const (
|
||||
// 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 {
|
||||
@@ -83,6 +85,8 @@ func NewReader(r io.Reader) *Reader {
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -114,32 +118,23 @@ func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Read satisfies the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
for {
|
||||
if r.i < r.j {
|
||||
n := copy(p, r.decoded[r.i:r.j])
|
||||
r.i += n
|
||||
return n, nil
|
||||
}
|
||||
func (r *Reader) fill() error {
|
||||
for r.i >= r.j {
|
||||
if !r.readFull(r.buf[:4], true) {
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
chunkType := r.buf[0]
|
||||
if !r.readHeader {
|
||||
if chunkType != chunkTypeStreamIdentifier {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
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 0, r.err
|
||||
return r.err
|
||||
}
|
||||
|
||||
// The chunk types are specified at
|
||||
@@ -149,11 +144,11 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||
// Section 4.2. Compressed data (chunk type 0x00).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:chunkLen]
|
||||
if !r.readFull(buf, false) {
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
buf = buf[checksumSize:]
|
||||
@@ -161,19 +156,19 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||
n, err := DecodedLen(buf)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if _, err := Decode(r.decoded, buf); err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
@@ -182,25 +177,25 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:checksumSize]
|
||||
if !r.readFull(buf, false) {
|
||||
return 0, r.err
|
||||
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 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.decoded[:n], false) {
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
@@ -209,15 +204,15 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||
if chunkLen != len(magicBody) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
return r.err
|
||||
}
|
||||
}
|
||||
continue
|
||||
@@ -226,12 +221,44 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||
if chunkType <= 0x7f {
|
||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||
r.err = ErrUnsupported
|
||||
return 0, r.err
|
||||
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 0, r.err
|
||||
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
|
||||
}
|
||||
|
||||
+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
|
||||
Generated
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
+19
-5
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 appengine !gc noasm
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
@@ -85,14 +85,28 @@ func decode(dst, src []byte) int {
|
||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
|
||||
// the built-in copy function, this byte-by-byte copy always runs
|
||||
// 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:])
|
||||
for end := d + length; d != end; d++ {
|
||||
dst[d] = 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
|
||||
|
||||
+4
@@ -15,6 +15,8 @@ import (
|
||||
// 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)
|
||||
@@ -139,6 +141,8 @@ func NewBufferedWriter(w io.Writer) *Writer {
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
+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
|
||||
Generated
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 appengine !gc noasm
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
module github.com/golang/snappy
|
||||
+6
-4
@@ -2,6 +2,7 @@ language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- master
|
||||
|
||||
addons:
|
||||
@@ -38,10 +39,11 @@ jobs:
|
||||
install: ./.travis.install.sh
|
||||
- os: osx
|
||||
go: 1.x
|
||||
- os: windows
|
||||
go: 1.x
|
||||
# winpcap does not work on travis ci - so install nmap to get libpcap
|
||||
before_install: choco install nmap
|
||||
# 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
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ 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>
|
||||
@@ -32,6 +33,7 @@ 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
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ 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.7 due to x/sys/unix dependencies.
|
||||
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).
|
||||
|
||||
+2
-2
@@ -78,7 +78,7 @@ func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
func decodePayload(data []byte, p PacketBuilder) error {
|
||||
payload := &Payload{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
@@ -132,7 +132,7 @@ func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
func decodeFragment(data []byte, p PacketBuilder) error {
|
||||
payload := &Fragment{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
|
||||
+62
-1
@@ -208,7 +208,7 @@ based on endpoint criteria:
|
||||
}
|
||||
}
|
||||
// Find all packets coming from UDP port 1000 to UDP port 500
|
||||
interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(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!")
|
||||
}
|
||||
@@ -320,6 +320,67 @@ 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
|
||||
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
module github.com/google/gopacket
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67
|
||||
)
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
+10
-1
@@ -10,6 +10,7 @@ package layers
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
@@ -39,17 +40,25 @@ 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]
|
||||
|
||||
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
|
||||
arp.Contents = data[:arpLength]
|
||||
arp.Payload = data[arpLength:]
|
||||
return nil
|
||||
|
||||
+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)
|
||||
}
|
||||
+11
-3
@@ -13,6 +13,7 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
@@ -227,7 +228,7 @@ func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
|
||||
}
|
||||
var err error
|
||||
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
|
||||
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:], p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -242,8 +243,12 @@ func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscoveryInfo
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
|
||||
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]),
|
||||
@@ -251,6 +256,9 @@ func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err er
|
||||
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)
|
||||
@@ -263,7 +271,7 @@ func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
|
||||
var err error
|
||||
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
|
||||
p.AddLayer(info)
|
||||
values, err := decodeCiscoDiscoveryTLVs(data)
|
||||
values, err := decodeCiscoDiscoveryTLVs(data, p)
|
||||
if err != nil { // Unlikely, as parent decode will fail, but better safe...
|
||||
return err
|
||||
}
|
||||
|
||||
+7
@@ -124,6 +124,10 @@ 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])
|
||||
@@ -168,6 +172,9 @@ func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
|
||||
start += int(o.Length) + 2
|
||||
}
|
||||
}
|
||||
|
||||
d.Contents = data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+21
-2
@@ -8,7 +8,6 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
@@ -88,12 +87,20 @@ 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])
|
||||
@@ -261,21 +268,33 @@ type DHCPv6DUID struct {
|
||||
// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
|
||||
func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
|
||||
if len(data) < 2 {
|
||||
return errors.New("Not enough bytes to decode: " + string(len(data)))
|
||||
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:])
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -608,14 +608,14 @@ func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
|
||||
}
|
||||
|
||||
func (o *DHCPv6Option) decode(data []byte) error {
|
||||
if len(data) < 2 {
|
||||
if len(data) < 4 {
|
||||
return errors.New("not enough data to decode")
|
||||
}
|
||||
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
|
||||
if len(data) < 3 {
|
||||
return errors.New("not enough data to decode")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
+84
-39
@@ -52,25 +52,26 @@ type DNSType uint16
|
||||
|
||||
// DNSType known values.
|
||||
const (
|
||||
DNSTypeA DNSType = 1 // a host address
|
||||
DNSTypeNS DNSType = 2 // an authoritative name server
|
||||
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
|
||||
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
|
||||
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
|
||||
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
|
||||
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
|
||||
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
|
||||
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
|
||||
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
|
||||
DNSTypeWKS DNSType = 11 // a well known service description
|
||||
DNSTypePTR DNSType = 12 // a domain name pointer
|
||||
DNSTypeHINFO DNSType = 13 // host information
|
||||
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
|
||||
DNSTypeMX DNSType = 15 // mail exchange
|
||||
DNSTypeTXT DNSType = 16 // text strings
|
||||
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
|
||||
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
|
||||
DNSTypeOPT DNSType = 41 // OPT Pseudo-RR [RFC6891]
|
||||
DNSTypeA DNSType = 1 // a host address
|
||||
DNSTypeNS DNSType = 2 // an authoritative name server
|
||||
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
|
||||
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
|
||||
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
|
||||
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
|
||||
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
|
||||
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
|
||||
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
|
||||
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
|
||||
DNSTypeWKS DNSType = 11 // a well known service description
|
||||
DNSTypePTR DNSType = 12 // a domain name pointer
|
||||
DNSTypeHINFO DNSType = 13 // host information
|
||||
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
|
||||
DNSTypeMX DNSType = 15 // mail exchange
|
||||
DNSTypeTXT DNSType = 16 // text strings
|
||||
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
|
||||
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
|
||||
DNSTypeOPT DNSType = 41 // OPT Pseudo-RR [RFC6891]
|
||||
DNSTypeURI DNSType = 256 // URI RR [RFC7553]
|
||||
)
|
||||
|
||||
func (dt DNSType) String() string {
|
||||
@@ -115,6 +116,8 @@ func (dt DNSType) String() string {
|
||||
return "SRV"
|
||||
case DNSTypeOPT:
|
||||
return "OPT"
|
||||
case DNSTypeURI:
|
||||
return "URI"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,25 +126,26 @@ type DNSResponseCode uint8
|
||||
|
||||
// DNSResponseCode known values.
|
||||
const (
|
||||
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
|
||||
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
|
||||
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
|
||||
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
|
||||
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
|
||||
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
|
||||
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
|
||||
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
|
||||
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
|
||||
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
|
||||
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
|
||||
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
|
||||
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
|
||||
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
|
||||
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
|
||||
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
|
||||
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
|
||||
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
|
||||
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
|
||||
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
|
||||
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
|
||||
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
|
||||
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
|
||||
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
|
||||
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
|
||||
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
|
||||
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
|
||||
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
|
||||
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
|
||||
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
|
||||
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
|
||||
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
|
||||
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
|
||||
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
|
||||
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
|
||||
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
|
||||
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
|
||||
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
|
||||
DNSResponseCodeBadCookie DNSResponseCode = 23 // Bad/missing Server Cookie [RFC7873]
|
||||
)
|
||||
|
||||
func (drc DNSResponseCode) String() string {
|
||||
@@ -184,6 +188,8 @@ func (drc DNSResponseCode) String() string {
|
||||
return "Algorithm not supported"
|
||||
case DNSResponseCodeBadTruc:
|
||||
return "Bad Truncation"
|
||||
case DNSResponseCodeBadCookie:
|
||||
return "Bad Cookie"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +373,10 @@ func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
d.Additionals = d.Additionals[:i] // strip off erroneous value
|
||||
return err
|
||||
}
|
||||
// extract extended RCODE from OPT RRs, RFC 6891 section 6.1.3
|
||||
if d.Additionals[i].Type == DNSTypeOPT {
|
||||
d.ResponseCode = DNSResponseCode(uint8(d.ResponseCode) | uint8(d.Additionals[i].TTL>>20&0xF0))
|
||||
}
|
||||
}
|
||||
|
||||
if uint16(len(d.Questions)) != d.QDCount {
|
||||
@@ -427,6 +437,8 @@ func recSize(rr *DNSResourceRecord) int {
|
||||
return l
|
||||
case DNSTypeSRV:
|
||||
return 6 + len(rr.SRV.Name) + 2
|
||||
case DNSTypeURI:
|
||||
return 4 + len(rr.URI.Target)
|
||||
case DNSTypeOPT:
|
||||
l := len(rr.OPT) * 4
|
||||
for _, opt := range rr.OPT {
|
||||
@@ -684,6 +696,7 @@ type DNSResourceRecord struct {
|
||||
SRV DNSSRV
|
||||
MX DNSMX
|
||||
OPT []DNSOPT // See RFC 6891, section 6.1.2
|
||||
URI DNSURI
|
||||
|
||||
// Undecoded TXT for backward compatibility
|
||||
TXT []byte
|
||||
@@ -707,7 +720,7 @@ func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeF
|
||||
}
|
||||
rr.Data = data[endq+10 : end]
|
||||
|
||||
if err = rr.decodeRData(data, endq+10, buffer); err != nil {
|
||||
if err = rr.decodeRData(data[:end], endq+10, buffer); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -781,6 +794,10 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria
|
||||
binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight)
|
||||
binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port)
|
||||
encodeName(rr.SRV.Name, data, noff+16)
|
||||
case DNSTypeURI:
|
||||
binary.BigEndian.PutUint16(data[noff+10:], rr.URI.Priority)
|
||||
binary.BigEndian.PutUint16(data[noff+12:], rr.URI.Weight)
|
||||
copy(data[noff+14:], rr.URI.Target)
|
||||
case DNSTypeOPT:
|
||||
noff2 := noff + 10
|
||||
for _, opt := range rr.OPT {
|
||||
@@ -813,6 +830,9 @@ func (rr *DNSResourceRecord) String() string {
|
||||
}
|
||||
return "OPT " + strings.Join(opts, ",")
|
||||
}
|
||||
if rr.Type == DNSTypeURI {
|
||||
return fmt.Sprintf("URI %d %d %s", rr.URI.Priority, rr.URI.Weight, string(rr.URI.Target))
|
||||
}
|
||||
if rr.Class == DNSClassIN {
|
||||
switch rr.Type {
|
||||
case DNSTypeA, DNSTypeAAAA:
|
||||
@@ -858,6 +878,9 @@ func decodeOPTs(data []byte, offset int) ([]DNSOPT, error) {
|
||||
|
||||
for i := offset; i < end; {
|
||||
opt := DNSOPT{}
|
||||
if len(data) < i+4 {
|
||||
return allOPT, fmt.Errorf("Malformed DNSOPT record. Length %d < %d", len(data), i+4)
|
||||
}
|
||||
opt.Code = DNSOptionCode(binary.BigEndian.Uint16(data[i : i+2]))
|
||||
l := binary.BigEndian.Uint16(data[i+2 : i+4])
|
||||
if i+4+int(l) > end {
|
||||
@@ -911,6 +934,9 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) < endq+20 {
|
||||
return errors.New("SOA too small")
|
||||
}
|
||||
rr.SOA.RName = name
|
||||
rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4])
|
||||
rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8])
|
||||
@@ -918,13 +944,26 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte
|
||||
rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16])
|
||||
rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20])
|
||||
case DNSTypeMX:
|
||||
if len(data) < offset+2 {
|
||||
return errors.New("MX too small")
|
||||
}
|
||||
rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
name, _, err := decodeName(data, offset+2, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.MX.Name = name
|
||||
case DNSTypeURI:
|
||||
if len(rr.Data) < 4 {
|
||||
return errors.New("URI too small")
|
||||
}
|
||||
rr.URI.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
rr.URI.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||
rr.URI.Target = rr.Data[4:]
|
||||
case DNSTypeSRV:
|
||||
if len(data) < offset+6 {
|
||||
return errors.New("SRV too small")
|
||||
}
|
||||
rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||
rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6])
|
||||
@@ -964,6 +1003,12 @@ type DNSMX struct {
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// DNSURI is a URI record, defining a target (URI) of a server/service
|
||||
type DNSURI struct {
|
||||
Priority, Weight uint16
|
||||
Target []byte
|
||||
}
|
||||
|
||||
// DNSOptionCode represents the code of a DNS Option, see RFC6891, section 6.1.2
|
||||
type DNSOptionCode uint16
|
||||
|
||||
|
||||
+18
-5
@@ -922,6 +922,7 @@ func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
}
|
||||
m.Type = Dot11Type((data[0])&0xFC) >> 2
|
||||
|
||||
m.DataLayer = nil
|
||||
m.Proto = uint8(data[0]) & 0x0003
|
||||
m.Flags = Dot11Flags(data[1])
|
||||
m.DurationID = binary.LittleEndian.Uint16(data[2:4])
|
||||
@@ -1064,7 +1065,11 @@ func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
}
|
||||
|
||||
if mainType == Dot11TypeData {
|
||||
l := dataDecodeMap[m.Type]()
|
||||
d := dataDecodeMap[m.Type]
|
||||
if d == nil {
|
||||
return fmt.Errorf("unsupported type: %v", m.Type)
|
||||
}
|
||||
l := d()
|
||||
err := l.DecodeFromBytes(m.BaseLayer.Payload, df)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1279,8 +1284,10 @@ func decodeDot11DataCFPollNoData(data []byte, p gopacket.PacketBuilder) error {
|
||||
return decodingLayerDecoder(d, data, p)
|
||||
}
|
||||
|
||||
func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData }
|
||||
func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPollNoData }
|
||||
func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData }
|
||||
func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDot11DataCFPollNoData
|
||||
}
|
||||
func (m *Dot11DataCFPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
return m.Dot11Data.DecodeFromBytes(data, df)
|
||||
}
|
||||
@@ -1338,8 +1345,10 @@ func decodeDot11DataQOSDataCFAck(data []byte, p gopacket.PacketBuilder) error {
|
||||
return decodingLayerDecoder(d, data, p)
|
||||
}
|
||||
|
||||
func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck }
|
||||
func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSDataCFAck }
|
||||
func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck }
|
||||
func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDot11DataQOSDataCFAck
|
||||
}
|
||||
func (m *Dot11DataQOSDataCFAck) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck }
|
||||
|
||||
type Dot11DataQOSDataCFPoll struct {
|
||||
@@ -1461,6 +1470,10 @@ func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.Decod
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), offset+int(m.Length))
|
||||
}
|
||||
if len(data) < offset+4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("vendor extension size < %d", offset+int(m.Length))
|
||||
}
|
||||
if m.ID == 221 {
|
||||
// Vendor extension
|
||||
m.OUI = data[offset : offset+4]
|
||||
|
||||
+4
@@ -27,6 +27,10 @@ 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
|
||||
|
||||
+8
@@ -47,9 +47,17 @@ 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])
|
||||
|
||||
+4
@@ -25,6 +25,10 @@ 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])
|
||||
|
||||
+4
-9
@@ -8,7 +8,6 @@
|
||||
package layers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
@@ -26,14 +25,6 @@ type EnumMetadata struct {
|
||||
LayerType gopacket.LayerType
|
||||
}
|
||||
|
||||
// errorFunc returns a decoder that spits out a specific error message.
|
||||
func errorFunc(msg string) gopacket.Decoder {
|
||||
var e = errors.New(msg)
|
||||
return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type EthernetType uint16
|
||||
@@ -56,6 +47,7 @@ const (
|
||||
EthernetTypeMPLSUnicast EthernetType = 0x8847
|
||||
EthernetTypeMPLSMulticast EthernetType = 0x8848
|
||||
EthernetTypeEAPOL EthernetType = 0x888e
|
||||
EthernetTypeERSPAN EthernetType = 0x88be
|
||||
EthernetTypeQinQ EthernetType = 0x88a8
|
||||
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
|
||||
EthernetTypeEthernetCTP EthernetType = 0x9000
|
||||
@@ -130,6 +122,8 @@ const (
|
||||
LinkTypeLinuxIRDA LinkType = 144
|
||||
LinkTypeLinuxLAPD LinkType = 177
|
||||
LinkTypeLinuxUSB LinkType = 220
|
||||
LinkTypeFC2 LinkType = 224
|
||||
LinkTypeFC2Framed LinkType = 225
|
||||
LinkTypeIPv4 LinkType = 228
|
||||
LinkTypeIPv6 LinkType = 229
|
||||
)
|
||||
@@ -324,6 +318,7 @@ func initActualTypeData() {
|
||||
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}
|
||||
|
||||
+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)
|
||||
}
|
||||
+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
|
||||
}
|
||||
+14
-3
@@ -50,7 +50,11 @@ type GeneveOption struct {
|
||||
// LayerType returns LayerTypeGeneve
|
||||
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
|
||||
|
||||
func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
|
||||
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])
|
||||
@@ -58,10 +62,14 @@ func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
|
||||
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
|
||||
return opt, opt.Length, nil
|
||||
}
|
||||
|
||||
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
@@ -88,7 +96,10 @@ func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
|
||||
}
|
||||
|
||||
for length > 0 {
|
||||
opt, len := decodeGeneveOption(data[offset:], gn)
|
||||
opt, len, err := decodeGeneveOption(data[offset:], gn, df)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gn.Options = append(gn.Options, opt)
|
||||
|
||||
length -= int32(len)
|
||||
|
||||
+3
@@ -160,6 +160,9 @@ func (g *GTPv1U) CanDecode() gopacket.LayerClass {
|
||||
|
||||
// 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
|
||||
|
||||
+18
-3
@@ -324,7 +324,11 @@ func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun b
|
||||
return length
|
||||
}
|
||||
|
||||
func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
|
||||
func decodeIPv6HeaderTLVOption(data []byte, df gopacket.DecodeFeedback) (h *ipv6HeaderTLVOption, _ error) {
|
||||
if len(data) < 2 {
|
||||
df.SetTruncated()
|
||||
return nil, errors.New("IPv6 header option too small")
|
||||
}
|
||||
h = &ipv6HeaderTLVOption{}
|
||||
if data[0] == 0 {
|
||||
h.ActualLength = 1
|
||||
@@ -333,6 +337,10 @@ func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
|
||||
h.OptionType = data[0]
|
||||
h.OptionLength = data[1]
|
||||
h.ActualLength = int(h.OptionLength) + 2
|
||||
if len(data) < h.ActualLength {
|
||||
df.SetTruncated()
|
||||
return nil, errors.New("IPv6 header TLV option too small")
|
||||
}
|
||||
h.OptionData = data[2:h.ActualLength]
|
||||
return
|
||||
}
|
||||
@@ -504,9 +512,13 @@ func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Options = i.Options[:0]
|
||||
offset := 2
|
||||
for offset < i.ActualLength {
|
||||
opt := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
@@ -633,7 +645,10 @@ func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedbac
|
||||
}
|
||||
offset := 2
|
||||
for offset < i.ActualLength {
|
||||
opt := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
|
||||
+9
@@ -8,6 +8,7 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
@@ -27,6 +28,10 @@ type IPSecAH struct {
|
||||
func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
|
||||
|
||||
func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
|
||||
if len(data) < 12 {
|
||||
p.SetTruncated()
|
||||
return errors.New("IPSec AH packet less than 12 bytes")
|
||||
}
|
||||
i := &IPSecAH{
|
||||
ipv6ExtensionBase: ipv6ExtensionBase{
|
||||
NextHeader: IPProtocol(data[0]),
|
||||
@@ -37,6 +42,10 @@ func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
|
||||
Seq: binary.BigEndian.Uint32(data[8:12]),
|
||||
}
|
||||
i.ActualLength = (int(i.HeaderLength) + 2) * 4
|
||||
if len(data) < i.ActualLength {
|
||||
p.SetTruncated()
|
||||
return errors.New("Truncated AH packet < ActualLength")
|
||||
}
|
||||
i.AuthenticationData = data[12:i.ActualLength]
|
||||
i.Contents = data[:i.ActualLength]
|
||||
i.Payload = data[i.ActualLength:]
|
||||
|
||||
+5
@@ -143,6 +143,11 @@ var (
|
||||
LayerTypeMLDv2MulticastListenerQuery = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)})
|
||||
LayerTypeTLS = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)})
|
||||
LayerTypeModbusTCP = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)})
|
||||
LayerTypeRMCP = gopacket.RegisterLayerType(142, gopacket.LayerTypeMetadata{Name: "RMCP", Decoder: gopacket.DecodeFunc(decodeRMCP)})
|
||||
LayerTypeASF = gopacket.RegisterLayerType(143, gopacket.LayerTypeMetadata{Name: "ASF", Decoder: gopacket.DecodeFunc(decodeASF)})
|
||||
LayerTypeASFPresencePong = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)})
|
||||
LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
|
||||
LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
+5
@@ -8,6 +8,7 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
@@ -114,6 +115,10 @@ func decodeLCM(data []byte, p gopacket.PacketBuilder) error {
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("LCM < 8 bytes")
|
||||
}
|
||||
offset := 0
|
||||
|
||||
lcm.Magic = binary.BigEndian.Uint32(data[offset:4])
|
||||
|
||||
+20
-2
@@ -779,6 +779,16 @@ func (c *LinkLayerDiscovery) SerializeTo(b gopacket.SerializeBuffer, opts gopack
|
||||
binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen:], ttlIDLen)
|
||||
binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen+2:], c.TTL)
|
||||
|
||||
for _, v := range c.Values {
|
||||
vb, err := b.AppendBytes(int(v.Length) + 2) // +2 for TLV type and length; 1 byte for subtype is included in v.Value
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
idLen := ((uint16(v.Type) << 9) | v.Length)
|
||||
binary.BigEndian.PutUint16(vb[0:2], idLen)
|
||||
copy(vb[2:], v.Value)
|
||||
}
|
||||
|
||||
vb, err = b.AppendBytes(2) // End Tlv, 2 bytes
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -792,10 +802,18 @@ func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
var vals []LinkLayerDiscoveryValue
|
||||
vData := data[0:]
|
||||
for len(vData) > 0 {
|
||||
if len(vData) < 2 {
|
||||
p.SetTruncated()
|
||||
return errors.New("LLDP vdata < 2 bytes")
|
||||
}
|
||||
nbit := vData[0] & 0x01
|
||||
t := LLDPTLVType(vData[0] >> 1)
|
||||
val := LinkLayerDiscoveryValue{Type: t, Length: uint16(nbit)<<8 + uint16(vData[1])}
|
||||
if val.Length > 0 {
|
||||
if len(vData) < int(val.Length+2) {
|
||||
p.SetTruncated()
|
||||
return fmt.Errorf("LLDP VData < %d bytes", val.Length+2)
|
||||
}
|
||||
val.Value = vData[2 : val.Length+2]
|
||||
}
|
||||
vals = append(vals, val)
|
||||
@@ -872,10 +890,10 @@ func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1])
|
||||
info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6])
|
||||
olen := v.Value[mlen+6]
|
||||
if err := checkLLDPTLVLen(v, int(mlen+6+olen)); err != nil {
|
||||
if err := checkLLDPTLVLen(v, int(mlen+7+olen)); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MgmtAddress.OID = string(v.Value[mlen+9 : mlen+9+olen])
|
||||
info.MgmtAddress.OID = string(v.Value[mlen+7 : mlen+7+olen])
|
||||
case LLDPTLVOrgSpecific:
|
||||
if err := checkLLDPTLVLen(v, 4); err != nil {
|
||||
return err
|
||||
|
||||
+1
-1
@@ -471,7 +471,7 @@ type MLDv2MulticastAddressRecord struct {
|
||||
|
||||
// decodes a multicast address record from bytes
|
||||
func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
|
||||
if len(data) < 4 {
|
||||
if len(data) < 20 {
|
||||
df.SetTruncated()
|
||||
return 0, errors.New(
|
||||
"Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
|
||||
|
||||
+36
-1
@@ -8,6 +8,7 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
@@ -38,6 +39,7 @@ const (
|
||||
ASExternalLSAtypeV2 = 0x5
|
||||
ASExternalLSAtype = 0x4005
|
||||
NSSALSAtype = 0x2007
|
||||
NSSALSAtypeV2 = 0x7
|
||||
LinkLSAtype = 0x0008
|
||||
IntraAreaPrefixLSAtype = 0x2009
|
||||
)
|
||||
@@ -129,6 +131,12 @@ type NetworkLSA struct {
|
||||
AttachedRouter []uint32
|
||||
}
|
||||
|
||||
// NetworkLSAV2 is the struct from RFC 2328 A.4.3.
|
||||
type NetworkLSAV2 struct {
|
||||
NetworkMask uint32
|
||||
AttachedRouter []uint32
|
||||
}
|
||||
|
||||
// RouterV2 extends RouterLSAV2
|
||||
type RouterV2 struct {
|
||||
Type uint8
|
||||
@@ -288,12 +296,30 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
|
||||
switch lstype {
|
||||
case RouterLSAtypeV2:
|
||||
var routers []RouterV2
|
||||
var j uint32
|
||||
for j = 24; j < uint32(lsalength); j += 12 {
|
||||
if len(data) < int(j+12) {
|
||||
return nil, errors.New("LSAtypeV2 too small")
|
||||
}
|
||||
router := RouterV2{
|
||||
LinkID: binary.BigEndian.Uint32(data[j : j+4]),
|
||||
LinkData: binary.BigEndian.Uint32(data[j+4 : j+8]),
|
||||
Type: uint8(data[j+8]),
|
||||
Metric: binary.BigEndian.Uint16(data[j+10 : j+12]),
|
||||
}
|
||||
routers = append(routers, router)
|
||||
}
|
||||
if len(data) < 24 {
|
||||
return nil, errors.New("LSAtypeV2 too small")
|
||||
}
|
||||
links := binary.BigEndian.Uint16(data[22:24])
|
||||
content = RouterLSAV2{
|
||||
Flags: data[20],
|
||||
Links: links,
|
||||
Routers: routers,
|
||||
}
|
||||
case NSSALSAtypeV2:
|
||||
fallthrough
|
||||
case ASExternalLSAtypeV2:
|
||||
content = ASExternalLSAV2{
|
||||
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
|
||||
@@ -302,6 +328,16 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
|
||||
ForwardingAddress: binary.BigEndian.Uint32(data[28:32]),
|
||||
ExternalRouteTag: binary.BigEndian.Uint32(data[32:36]),
|
||||
}
|
||||
case NetworkLSAtypeV2:
|
||||
var routers []uint32
|
||||
var j uint32
|
||||
for j = 24; j < uint32(lsalength); j += 4 {
|
||||
routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
|
||||
}
|
||||
content = NetworkLSAV2{
|
||||
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
|
||||
AttachedRouter: routers,
|
||||
}
|
||||
case RouterLSAtype:
|
||||
var routers []Router
|
||||
var j uint32
|
||||
@@ -346,7 +382,6 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
|
||||
case ASExternalLSAtype:
|
||||
fallthrough
|
||||
case NSSALSAtype:
|
||||
|
||||
flags := uint8(data[20])
|
||||
prefixLen := uint8(data[24]) / 8
|
||||
var forwardingAddress []byte
|
||||
|
||||
+8
@@ -9,6 +9,7 @@ package layers
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
@@ -39,6 +40,10 @@ type PFLog struct {
|
||||
}
|
||||
|
||||
func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 60 {
|
||||
df.SetTruncated()
|
||||
return errors.New("PFLog data less than 60 bytes")
|
||||
}
|
||||
pf.Length = data[0]
|
||||
pf.Family = ProtocolFamily(data[1])
|
||||
pf.Action = data[2]
|
||||
@@ -56,6 +61,9 @@ func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
|
||||
return errors.New("PFLog header length should be 3 less than multiple of 4")
|
||||
}
|
||||
actualLength := int(pf.Length) + 3
|
||||
if len(data) < actualLength {
|
||||
return fmt.Errorf("PFLog data size < %d", actualLength)
|
||||
}
|
||||
pf.Contents = data[:actualLength]
|
||||
pf.Payload = data[actualLength:]
|
||||
return nil
|
||||
|
||||
+2
@@ -115,6 +115,8 @@ var udpPortLayerType = [65536]gopacket.LayerType{
|
||||
6081: LayerTypeGeneve,
|
||||
3784: LayerTypeBFD,
|
||||
2152: LayerTypeGTPv1U,
|
||||
623: LayerTypeRMCP,
|
||||
1812: LayerTypeRADIUS,
|
||||
}
|
||||
|
||||
// RegisterUDPPortLayerType creates a new mapping between a UDPPort
|
||||
|
||||
+12
-5
@@ -9,6 +9,7 @@ package layers
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"strings"
|
||||
@@ -468,11 +469,13 @@ const (
|
||||
func (self RadioTapAMPDUStatusFlags) ReportZerolen() bool {
|
||||
return self&RadioTapAMPDUStatusFlagsReportZerolen != 0
|
||||
}
|
||||
func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool { return self&RadioTapAMPDUDelimCRCKnown != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 }
|
||||
func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool {
|
||||
return self&RadioTapAMPDUDelimCRCKnown != 0
|
||||
}
|
||||
|
||||
type RadioTapVHT struct {
|
||||
Known RadioTapVHTKnown
|
||||
@@ -727,6 +730,10 @@ type RadioTap struct {
|
||||
func (m *RadioTap) LayerType() gopacket.LayerType { return LayerTypeRadioTap }
|
||||
|
||||
func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("RadioTap too small")
|
||||
}
|
||||
m.Version = uint8(data[0])
|
||||
m.Length = binary.LittleEndian.Uint16(data[2:4])
|
||||
m.Present = RadioTapPresent(binary.LittleEndian.Uint32(data[4:8]))
|
||||
|
||||
+560
@@ -0,0 +1,560 @@
|
||||
// Copyright 2020 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"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
// RFC 2865 3. Packet Format
|
||||
// `The minimum length is 20 and maximum length is 4096.`
|
||||
radiusMinimumRecordSizeInBytes int = 20
|
||||
radiusMaximumRecordSizeInBytes int = 4096
|
||||
|
||||
// RFC 2865 5. Attributes
|
||||
// `The Length field is one octet, and indicates the length of this Attribute including the Type, Length and Value fields.`
|
||||
// `The Value field is zero or more octets and contains information specific to the Attribute.`
|
||||
radiusAttributesMinimumRecordSizeInBytes int = 2
|
||||
)
|
||||
|
||||
// RADIUS represents a Remote Authentication Dial In User Service layer.
|
||||
type RADIUS struct {
|
||||
BaseLayer
|
||||
|
||||
Code RADIUSCode
|
||||
Identifier RADIUSIdentifier
|
||||
Length RADIUSLength
|
||||
Authenticator RADIUSAuthenticator
|
||||
Attributes []RADIUSAttribute
|
||||
}
|
||||
|
||||
// RADIUSCode represents packet type.
|
||||
type RADIUSCode uint8
|
||||
|
||||
// constants that define RADIUSCode.
|
||||
const (
|
||||
RADIUSCodeAccessRequest RADIUSCode = 1 // RFC2865 3. Packet Format
|
||||
RADIUSCodeAccessAccept RADIUSCode = 2 // RFC2865 3. Packet Format
|
||||
RADIUSCodeAccessReject RADIUSCode = 3 // RFC2865 3. Packet Format
|
||||
RADIUSCodeAccountingRequest RADIUSCode = 4 // RFC2865 3. Packet Format
|
||||
RADIUSCodeAccountingResponse RADIUSCode = 5 // RFC2865 3. Packet Format
|
||||
RADIUSCodeAccessChallenge RADIUSCode = 11 // RFC2865 3. Packet Format
|
||||
RADIUSCodeStatusServer RADIUSCode = 12 // RFC2865 3. Packet Format (experimental)
|
||||
RADIUSCodeStatusClient RADIUSCode = 13 // RFC2865 3. Packet Format (experimental)
|
||||
RADIUSCodeReserved RADIUSCode = 255 // RFC2865 3. Packet Format
|
||||
)
|
||||
|
||||
// String returns a string version of a RADIUSCode.
|
||||
func (t RADIUSCode) String() (s string) {
|
||||
switch t {
|
||||
case RADIUSCodeAccessRequest:
|
||||
s = "Access-Request"
|
||||
case RADIUSCodeAccessAccept:
|
||||
s = "Access-Accept"
|
||||
case RADIUSCodeAccessReject:
|
||||
s = "Access-Reject"
|
||||
case RADIUSCodeAccountingRequest:
|
||||
s = "Accounting-Request"
|
||||
case RADIUSCodeAccountingResponse:
|
||||
s = "Accounting-Response"
|
||||
case RADIUSCodeAccessChallenge:
|
||||
s = "Access-Challenge"
|
||||
case RADIUSCodeStatusServer:
|
||||
s = "Status-Server"
|
||||
case RADIUSCodeStatusClient:
|
||||
s = "Status-Client"
|
||||
case RADIUSCodeReserved:
|
||||
s = "Reserved"
|
||||
default:
|
||||
s = fmt.Sprintf("Unknown(%d)", t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RADIUSIdentifier represents packet identifier.
|
||||
type RADIUSIdentifier uint8
|
||||
|
||||
// RADIUSLength represents packet length.
|
||||
type RADIUSLength uint16
|
||||
|
||||
// RADIUSAuthenticator represents authenticator.
|
||||
type RADIUSAuthenticator [16]byte
|
||||
|
||||
// RADIUSAttribute represents attributes.
|
||||
type RADIUSAttribute struct {
|
||||
Type RADIUSAttributeType
|
||||
Length RADIUSAttributeLength
|
||||
Value RADIUSAttributeValue
|
||||
}
|
||||
|
||||
// RADIUSAttributeType represents attribute type.
|
||||
type RADIUSAttributeType uint8
|
||||
|
||||
// constants that define RADIUSAttributeType.
|
||||
const (
|
||||
RADIUSAttributeTypeUserName RADIUSAttributeType = 1 // RFC2865 5.1. User-Name
|
||||
RADIUSAttributeTypeUserPassword RADIUSAttributeType = 2 // RFC2865 5.2. User-Password
|
||||
RADIUSAttributeTypeCHAPPassword RADIUSAttributeType = 3 // RFC2865 5.3. CHAP-Password
|
||||
RADIUSAttributeTypeNASIPAddress RADIUSAttributeType = 4 // RFC2865 5.4. NAS-IP-Address
|
||||
RADIUSAttributeTypeNASPort RADIUSAttributeType = 5 // RFC2865 5.5. NAS-Port
|
||||
RADIUSAttributeTypeServiceType RADIUSAttributeType = 6 // RFC2865 5.6. Service-Type
|
||||
RADIUSAttributeTypeFramedProtocol RADIUSAttributeType = 7 // RFC2865 5.7. Framed-Protocol
|
||||
RADIUSAttributeTypeFramedIPAddress RADIUSAttributeType = 8 // RFC2865 5.8. Framed-IP-Address
|
||||
RADIUSAttributeTypeFramedIPNetmask RADIUSAttributeType = 9 // RFC2865 5.9. Framed-IP-Netmask
|
||||
RADIUSAttributeTypeFramedRouting RADIUSAttributeType = 10 // RFC2865 5.10. Framed-Routing
|
||||
RADIUSAttributeTypeFilterId RADIUSAttributeType = 11 // RFC2865 5.11. Filter-Id
|
||||
RADIUSAttributeTypeFramedMTU RADIUSAttributeType = 12 // RFC2865 5.12. Framed-MTU
|
||||
RADIUSAttributeTypeFramedCompression RADIUSAttributeType = 13 // RFC2865 5.13. Framed-Compression
|
||||
RADIUSAttributeTypeLoginIPHost RADIUSAttributeType = 14 // RFC2865 5.14. Login-IP-Host
|
||||
RADIUSAttributeTypeLoginService RADIUSAttributeType = 15 // RFC2865 5.15. Login-Service
|
||||
RADIUSAttributeTypeLoginTCPPort RADIUSAttributeType = 16 // RFC2865 5.16. Login-TCP-Port
|
||||
RADIUSAttributeTypeReplyMessage RADIUSAttributeType = 18 // RFC2865 5.18. Reply-Message
|
||||
RADIUSAttributeTypeCallbackNumber RADIUSAttributeType = 19 // RFC2865 5.19. Callback-Number
|
||||
RADIUSAttributeTypeCallbackId RADIUSAttributeType = 20 // RFC2865 5.20. Callback-Id
|
||||
RADIUSAttributeTypeFramedRoute RADIUSAttributeType = 22 // RFC2865 5.22. Framed-Route
|
||||
RADIUSAttributeTypeFramedIPXNetwork RADIUSAttributeType = 23 // RFC2865 5.23. Framed-IPX-Network
|
||||
RADIUSAttributeTypeState RADIUSAttributeType = 24 // RFC2865 5.24. State
|
||||
RADIUSAttributeTypeClass RADIUSAttributeType = 25 // RFC2865 5.25. Class
|
||||
RADIUSAttributeTypeVendorSpecific RADIUSAttributeType = 26 // RFC2865 5.26. Vendor-Specific
|
||||
RADIUSAttributeTypeSessionTimeout RADIUSAttributeType = 27 // RFC2865 5.27. Session-Timeout
|
||||
RADIUSAttributeTypeIdleTimeout RADIUSAttributeType = 28 // RFC2865 5.28. Idle-Timeout
|
||||
RADIUSAttributeTypeTerminationAction RADIUSAttributeType = 29 // RFC2865 5.29. Termination-Action
|
||||
RADIUSAttributeTypeCalledStationId RADIUSAttributeType = 30 // RFC2865 5.30. Called-Station-Id
|
||||
RADIUSAttributeTypeCallingStationId RADIUSAttributeType = 31 // RFC2865 5.31. Calling-Station-Id
|
||||
RADIUSAttributeTypeNASIdentifier RADIUSAttributeType = 32 // RFC2865 5.32. NAS-Identifier
|
||||
RADIUSAttributeTypeProxyState RADIUSAttributeType = 33 // RFC2865 5.33. Proxy-State
|
||||
RADIUSAttributeTypeLoginLATService RADIUSAttributeType = 34 // RFC2865 5.34. Login-LAT-Service
|
||||
RADIUSAttributeTypeLoginLATNode RADIUSAttributeType = 35 // RFC2865 5.35. Login-LAT-Node
|
||||
RADIUSAttributeTypeLoginLATGroup RADIUSAttributeType = 36 // RFC2865 5.36. Login-LAT-Group
|
||||
RADIUSAttributeTypeFramedAppleTalkLink RADIUSAttributeType = 37 // RFC2865 5.37. Framed-AppleTalk-Link
|
||||
RADIUSAttributeTypeFramedAppleTalkNetwork RADIUSAttributeType = 38 // RFC2865 5.38. Framed-AppleTalk-Network
|
||||
RADIUSAttributeTypeFramedAppleTalkZone RADIUSAttributeType = 39 // RFC2865 5.39. Framed-AppleTalk-Zone
|
||||
RADIUSAttributeTypeAcctStatusType RADIUSAttributeType = 40 // RFC2866 5.1. Acct-Status-Type
|
||||
RADIUSAttributeTypeAcctDelayTime RADIUSAttributeType = 41 // RFC2866 5.2. Acct-Delay-Time
|
||||
RADIUSAttributeTypeAcctInputOctets RADIUSAttributeType = 42 // RFC2866 5.3. Acct-Input-Octets
|
||||
RADIUSAttributeTypeAcctOutputOctets RADIUSAttributeType = 43 // RFC2866 5.4. Acct-Output-Octets
|
||||
RADIUSAttributeTypeAcctSessionId RADIUSAttributeType = 44 // RFC2866 5.5. Acct-Session-Id
|
||||
RADIUSAttributeTypeAcctAuthentic RADIUSAttributeType = 45 // RFC2866 5.6. Acct-Authentic
|
||||
RADIUSAttributeTypeAcctSessionTime RADIUSAttributeType = 46 // RFC2866 5.7. Acct-Session-Time
|
||||
RADIUSAttributeTypeAcctInputPackets RADIUSAttributeType = 47 // RFC2866 5.8. Acct-Input-Packets
|
||||
RADIUSAttributeTypeAcctOutputPackets RADIUSAttributeType = 48 // RFC2866 5.9. Acct-Output-Packets
|
||||
RADIUSAttributeTypeAcctTerminateCause RADIUSAttributeType = 49 // RFC2866 5.10. Acct-Terminate-Cause
|
||||
RADIUSAttributeTypeAcctMultiSessionId RADIUSAttributeType = 50 // RFC2866 5.11. Acct-Multi-Session-Id
|
||||
RADIUSAttributeTypeAcctLinkCount RADIUSAttributeType = 51 // RFC2866 5.12. Acct-Link-Count
|
||||
RADIUSAttributeTypeAcctInputGigawords RADIUSAttributeType = 52 // RFC2869 5.1. Acct-Input-Gigawords
|
||||
RADIUSAttributeTypeAcctOutputGigawords RADIUSAttributeType = 53 // RFC2869 5.2. Acct-Output-Gigawords
|
||||
RADIUSAttributeTypeEventTimestamp RADIUSAttributeType = 55 // RFC2869 5.3. Event-Timestamp
|
||||
RADIUSAttributeTypeCHAPChallenge RADIUSAttributeType = 60 // RFC2865 5.40. CHAP-Challenge
|
||||
RADIUSAttributeTypeNASPortType RADIUSAttributeType = 61 // RFC2865 5.41. NAS-Port-Type
|
||||
RADIUSAttributeTypePortLimit RADIUSAttributeType = 62 // RFC2865 5.42. Port-Limit
|
||||
RADIUSAttributeTypeLoginLATPort RADIUSAttributeType = 63 // RFC2865 5.43. Login-LAT-Port
|
||||
RADIUSAttributeTypeTunnelType RADIUSAttributeType = 64 // RFC2868 3.1. Tunnel-Type
|
||||
RADIUSAttributeTypeTunnelMediumType RADIUSAttributeType = 65 // RFC2868 3.2. Tunnel-Medium-Type
|
||||
RADIUSAttributeTypeTunnelClientEndpoint RADIUSAttributeType = 66 // RFC2868 3.3. Tunnel-Client-Endpoint
|
||||
RADIUSAttributeTypeTunnelServerEndpoint RADIUSAttributeType = 67 // RFC2868 3.4. Tunnel-Server-Endpoint
|
||||
RADIUSAttributeTypeAcctTunnelConnection RADIUSAttributeType = 68 // RFC2867 4.1. Acct-Tunnel-Connection
|
||||
RADIUSAttributeTypeTunnelPassword RADIUSAttributeType = 69 // RFC2868 3.5. Tunnel-Password
|
||||
RADIUSAttributeTypeARAPPassword RADIUSAttributeType = 70 // RFC2869 5.4. ARAP-Password
|
||||
RADIUSAttributeTypeARAPFeatures RADIUSAttributeType = 71 // RFC2869 5.5. ARAP-Features
|
||||
RADIUSAttributeTypeARAPZoneAccess RADIUSAttributeType = 72 // RFC2869 5.6. ARAP-Zone-Access
|
||||
RADIUSAttributeTypeARAPSecurity RADIUSAttributeType = 73 // RFC2869 5.7. ARAP-Security
|
||||
RADIUSAttributeTypeARAPSecurityData RADIUSAttributeType = 74 // RFC2869 5.8. ARAP-Security-Data
|
||||
RADIUSAttributeTypePasswordRetry RADIUSAttributeType = 75 // RFC2869 5.9. Password-Retry
|
||||
RADIUSAttributeTypePrompt RADIUSAttributeType = 76 // RFC2869 5.10. Prompt
|
||||
RADIUSAttributeTypeConnectInfo RADIUSAttributeType = 77 // RFC2869 5.11. Connect-Info
|
||||
RADIUSAttributeTypeConfigurationToken RADIUSAttributeType = 78 // RFC2869 5.12. Configuration-Token
|
||||
RADIUSAttributeTypeEAPMessage RADIUSAttributeType = 79 // RFC2869 5.13. EAP-Message
|
||||
RADIUSAttributeTypeMessageAuthenticator RADIUSAttributeType = 80 // RFC2869 5.14. Message-Authenticator
|
||||
RADIUSAttributeTypeTunnelPrivateGroupID RADIUSAttributeType = 81 // RFC2868 3.6. Tunnel-Private-Group-ID
|
||||
RADIUSAttributeTypeTunnelAssignmentID RADIUSAttributeType = 82 // RFC2868 3.7. Tunnel-Assignment-ID
|
||||
RADIUSAttributeTypeTunnelPreference RADIUSAttributeType = 83 // RFC2868 3.8. Tunnel-Preference
|
||||
RADIUSAttributeTypeARAPChallengeResponse RADIUSAttributeType = 84 // RFC2869 5.15. ARAP-Challenge-Response
|
||||
RADIUSAttributeTypeAcctInterimInterval RADIUSAttributeType = 85 // RFC2869 5.16. Acct-Interim-Interval
|
||||
RADIUSAttributeTypeAcctTunnelPacketsLost RADIUSAttributeType = 86 // RFC2867 4.2. Acct-Tunnel-Packets-Lost
|
||||
RADIUSAttributeTypeNASPortId RADIUSAttributeType = 87 // RFC2869 5.17. NAS-Port-Id
|
||||
RADIUSAttributeTypeFramedPool RADIUSAttributeType = 88 // RFC2869 5.18. Framed-Pool
|
||||
RADIUSAttributeTypeTunnelClientAuthID RADIUSAttributeType = 90 // RFC2868 3.9. Tunnel-Client-Auth-ID
|
||||
RADIUSAttributeTypeTunnelServerAuthID RADIUSAttributeType = 91 // RFC2868 3.10. Tunnel-Server-Auth-ID
|
||||
)
|
||||
|
||||
// RADIUSAttributeType represents attribute length.
|
||||
type RADIUSAttributeLength uint8
|
||||
|
||||
// RADIUSAttributeType represents attribute value.
|
||||
type RADIUSAttributeValue []byte
|
||||
|
||||
// String returns a string version of a RADIUSAttributeType.
|
||||
func (t RADIUSAttributeType) String() (s string) {
|
||||
switch t {
|
||||
case RADIUSAttributeTypeUserName:
|
||||
s = "User-Name"
|
||||
case RADIUSAttributeTypeUserPassword:
|
||||
s = "User-Password"
|
||||
case RADIUSAttributeTypeCHAPPassword:
|
||||
s = "CHAP-Password"
|
||||
case RADIUSAttributeTypeNASIPAddress:
|
||||
s = "NAS-IP-Address"
|
||||
case RADIUSAttributeTypeNASPort:
|
||||
s = "NAS-Port"
|
||||
case RADIUSAttributeTypeServiceType:
|
||||
s = "Service-Type"
|
||||
case RADIUSAttributeTypeFramedProtocol:
|
||||
s = "Framed-Protocol"
|
||||
case RADIUSAttributeTypeFramedIPAddress:
|
||||
s = "Framed-IP-Address"
|
||||
case RADIUSAttributeTypeFramedIPNetmask:
|
||||
s = "Framed-IP-Netmask"
|
||||
case RADIUSAttributeTypeFramedRouting:
|
||||
s = "Framed-Routing"
|
||||
case RADIUSAttributeTypeFilterId:
|
||||
s = "Filter-Id"
|
||||
case RADIUSAttributeTypeFramedMTU:
|
||||
s = "Framed-MTU"
|
||||
case RADIUSAttributeTypeFramedCompression:
|
||||
s = "Framed-Compression"
|
||||
case RADIUSAttributeTypeLoginIPHost:
|
||||
s = "Login-IP-Host"
|
||||
case RADIUSAttributeTypeLoginService:
|
||||
s = "Login-Service"
|
||||
case RADIUSAttributeTypeLoginTCPPort:
|
||||
s = "Login-TCP-Port"
|
||||
case RADIUSAttributeTypeReplyMessage:
|
||||
s = "Reply-Message"
|
||||
case RADIUSAttributeTypeCallbackNumber:
|
||||
s = "Callback-Number"
|
||||
case RADIUSAttributeTypeCallbackId:
|
||||
s = "Callback-Id"
|
||||
case RADIUSAttributeTypeFramedRoute:
|
||||
s = "Framed-Route"
|
||||
case RADIUSAttributeTypeFramedIPXNetwork:
|
||||
s = "Framed-IPX-Network"
|
||||
case RADIUSAttributeTypeState:
|
||||
s = "State"
|
||||
case RADIUSAttributeTypeClass:
|
||||
s = "Class"
|
||||
case RADIUSAttributeTypeVendorSpecific:
|
||||
s = "Vendor-Specific"
|
||||
case RADIUSAttributeTypeSessionTimeout:
|
||||
s = "Session-Timeout"
|
||||
case RADIUSAttributeTypeIdleTimeout:
|
||||
s = "Idle-Timeout"
|
||||
case RADIUSAttributeTypeTerminationAction:
|
||||
s = "Termination-Action"
|
||||
case RADIUSAttributeTypeCalledStationId:
|
||||
s = "Called-Station-Id"
|
||||
case RADIUSAttributeTypeCallingStationId:
|
||||
s = "Calling-Station-Id"
|
||||
case RADIUSAttributeTypeNASIdentifier:
|
||||
s = "NAS-Identifier"
|
||||
case RADIUSAttributeTypeProxyState:
|
||||
s = "Proxy-State"
|
||||
case RADIUSAttributeTypeLoginLATService:
|
||||
s = "Login-LAT-Service"
|
||||
case RADIUSAttributeTypeLoginLATNode:
|
||||
s = "Login-LAT-Node"
|
||||
case RADIUSAttributeTypeLoginLATGroup:
|
||||
s = "Login-LAT-Group"
|
||||
case RADIUSAttributeTypeFramedAppleTalkLink:
|
||||
s = "Framed-AppleTalk-Link"
|
||||
case RADIUSAttributeTypeFramedAppleTalkNetwork:
|
||||
s = "Framed-AppleTalk-Network"
|
||||
case RADIUSAttributeTypeFramedAppleTalkZone:
|
||||
s = "Framed-AppleTalk-Zone"
|
||||
case RADIUSAttributeTypeAcctStatusType:
|
||||
s = "Acct-Status-Type"
|
||||
case RADIUSAttributeTypeAcctDelayTime:
|
||||
s = "Acct-Delay-Time"
|
||||
case RADIUSAttributeTypeAcctInputOctets:
|
||||
s = "Acct-Input-Octets"
|
||||
case RADIUSAttributeTypeAcctOutputOctets:
|
||||
s = "Acct-Output-Octets"
|
||||
case RADIUSAttributeTypeAcctSessionId:
|
||||
s = "Acct-Session-Id"
|
||||
case RADIUSAttributeTypeAcctAuthentic:
|
||||
s = "Acct-Authentic"
|
||||
case RADIUSAttributeTypeAcctSessionTime:
|
||||
s = "Acct-Session-Time"
|
||||
case RADIUSAttributeTypeAcctInputPackets:
|
||||
s = "Acct-Input-Packets"
|
||||
case RADIUSAttributeTypeAcctOutputPackets:
|
||||
s = "Acct-Output-Packets"
|
||||
case RADIUSAttributeTypeAcctTerminateCause:
|
||||
s = "Acct-Terminate-Cause"
|
||||
case RADIUSAttributeTypeAcctMultiSessionId:
|
||||
s = "Acct-Multi-Session-Id"
|
||||
case RADIUSAttributeTypeAcctLinkCount:
|
||||
s = "Acct-Link-Count"
|
||||
case RADIUSAttributeTypeAcctInputGigawords:
|
||||
s = "Acct-Input-Gigawords"
|
||||
case RADIUSAttributeTypeAcctOutputGigawords:
|
||||
s = "Acct-Output-Gigawords"
|
||||
case RADIUSAttributeTypeEventTimestamp:
|
||||
s = "Event-Timestamp"
|
||||
case RADIUSAttributeTypeCHAPChallenge:
|
||||
s = "CHAP-Challenge"
|
||||
case RADIUSAttributeTypeNASPortType:
|
||||
s = "NAS-Port-Type"
|
||||
case RADIUSAttributeTypePortLimit:
|
||||
s = "Port-Limit"
|
||||
case RADIUSAttributeTypeLoginLATPort:
|
||||
s = "Login-LAT-Port"
|
||||
case RADIUSAttributeTypeTunnelType:
|
||||
s = "Tunnel-Type"
|
||||
case RADIUSAttributeTypeTunnelMediumType:
|
||||
s = "Tunnel-Medium-Type"
|
||||
case RADIUSAttributeTypeTunnelClientEndpoint:
|
||||
s = "Tunnel-Client-Endpoint"
|
||||
case RADIUSAttributeTypeTunnelServerEndpoint:
|
||||
s = "Tunnel-Server-Endpoint"
|
||||
case RADIUSAttributeTypeAcctTunnelConnection:
|
||||
s = "Acct-Tunnel-Connection"
|
||||
case RADIUSAttributeTypeTunnelPassword:
|
||||
s = "Tunnel-Password"
|
||||
case RADIUSAttributeTypeARAPPassword:
|
||||
s = "ARAP-Password"
|
||||
case RADIUSAttributeTypeARAPFeatures:
|
||||
s = "ARAP-Features"
|
||||
case RADIUSAttributeTypeARAPZoneAccess:
|
||||
s = "ARAP-Zone-Access"
|
||||
case RADIUSAttributeTypeARAPSecurity:
|
||||
s = "ARAP-Security"
|
||||
case RADIUSAttributeTypeARAPSecurityData:
|
||||
s = "ARAP-Security-Data"
|
||||
case RADIUSAttributeTypePasswordRetry:
|
||||
s = "Password-Retry"
|
||||
case RADIUSAttributeTypePrompt:
|
||||
s = "Prompt"
|
||||
case RADIUSAttributeTypeConnectInfo:
|
||||
s = "Connect-Info"
|
||||
case RADIUSAttributeTypeConfigurationToken:
|
||||
s = "Configuration-Token"
|
||||
case RADIUSAttributeTypeEAPMessage:
|
||||
s = "EAP-Message"
|
||||
case RADIUSAttributeTypeMessageAuthenticator:
|
||||
s = "Message-Authenticator"
|
||||
case RADIUSAttributeTypeTunnelPrivateGroupID:
|
||||
s = "Tunnel-Private-Group-ID"
|
||||
case RADIUSAttributeTypeTunnelAssignmentID:
|
||||
s = "Tunnel-Assignment-ID"
|
||||
case RADIUSAttributeTypeTunnelPreference:
|
||||
s = "Tunnel-Preference"
|
||||
case RADIUSAttributeTypeARAPChallengeResponse:
|
||||
s = "ARAP-Challenge-Response"
|
||||
case RADIUSAttributeTypeAcctInterimInterval:
|
||||
s = "Acct-Interim-Interval"
|
||||
case RADIUSAttributeTypeAcctTunnelPacketsLost:
|
||||
s = "Acct-Tunnel-Packets-Lost"
|
||||
case RADIUSAttributeTypeNASPortId:
|
||||
s = "NAS-Port-Id"
|
||||
case RADIUSAttributeTypeFramedPool:
|
||||
s = "Framed-Pool"
|
||||
case RADIUSAttributeTypeTunnelClientAuthID:
|
||||
s = "Tunnel-Client-Auth-ID"
|
||||
case RADIUSAttributeTypeTunnelServerAuthID:
|
||||
s = "Tunnel-Server-Auth-ID"
|
||||
default:
|
||||
s = fmt.Sprintf("Unknown(%d)", t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the length of a RADIUS packet.
|
||||
func (radius *RADIUS) Len() (int, error) {
|
||||
n := radiusMinimumRecordSizeInBytes
|
||||
for _, v := range radius.Attributes {
|
||||
alen, err := attributeValueLength(v.Value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += int(alen) + 2 // Added Type and Length
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeRADIUS.
|
||||
func (radius *RADIUS) LayerType() gopacket.LayerType {
|
||||
return LayerTypeRADIUS
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (radius *RADIUS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) > radiusMaximumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS length %d too big", len(data))
|
||||
}
|
||||
|
||||
if len(data) < radiusMinimumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS length %d too short", len(data))
|
||||
}
|
||||
|
||||
radius.BaseLayer = BaseLayer{Contents: data}
|
||||
|
||||
radius.Code = RADIUSCode(data[0])
|
||||
radius.Identifier = RADIUSIdentifier(data[1])
|
||||
radius.Length = RADIUSLength(binary.BigEndian.Uint16(data[2:4]))
|
||||
|
||||
if int(radius.Length) > radiusMaximumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS length %d too big", radius.Length)
|
||||
}
|
||||
|
||||
if int(radius.Length) < radiusMinimumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS length %d too short", radius.Length)
|
||||
}
|
||||
|
||||
// RFC 2865 3. Packet Format
|
||||
// `If the packet is shorter than the Length field indicates, it MUST be silently discarded.`
|
||||
if int(radius.Length) > len(data) {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS length %d too big", radius.Length)
|
||||
}
|
||||
|
||||
// RFC 2865 3. Packet Format
|
||||
// `Octets outside the range of the Length field MUST be treated as padding and ignored on reception.`
|
||||
if int(radius.Length) < len(data) {
|
||||
df.SetTruncated()
|
||||
data = data[:radius.Length]
|
||||
}
|
||||
|
||||
copy(radius.Authenticator[:], data[4:20])
|
||||
|
||||
if len(data) == radiusMinimumRecordSizeInBytes {
|
||||
return nil
|
||||
}
|
||||
|
||||
pos := radiusMinimumRecordSizeInBytes
|
||||
for {
|
||||
if len(data) == pos {
|
||||
break
|
||||
}
|
||||
|
||||
if len(data[pos:]) < radiusAttributesMinimumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS attributes length %d too short", len(data[pos:]))
|
||||
}
|
||||
|
||||
attr := RADIUSAttribute{}
|
||||
attr.Type = RADIUSAttributeType(data[pos])
|
||||
attr.Length = RADIUSAttributeLength(data[pos+1])
|
||||
|
||||
if int(attr.Length) > len(data[pos:]) {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS attributes length %d too big", attr.Length)
|
||||
}
|
||||
|
||||
if int(attr.Length) < radiusAttributesMinimumRecordSizeInBytes {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("RADIUS attributes length %d too short", attr.Length)
|
||||
}
|
||||
|
||||
if int(attr.Length) > radiusAttributesMinimumRecordSizeInBytes {
|
||||
attr.Value = make([]byte, attr.Length-2)
|
||||
copy(attr.Value[:], data[pos+2:pos+int(attr.Length)])
|
||||
radius.Attributes = append(radius.Attributes, attr)
|
||||
}
|
||||
|
||||
pos += int(attr.Length)
|
||||
}
|
||||
|
||||
for _, v := range radius.Attributes {
|
||||
if v.Type == RADIUSAttributeTypeEAPMessage {
|
||||
radius.BaseLayer.Payload = append(radius.BaseLayer.Payload, v.Value...)
|
||||
}
|
||||
}
|
||||
|
||||
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 (radius *RADIUS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
plen, err := radius.Len()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.FixLengths {
|
||||
radius.Length = RADIUSLength(plen)
|
||||
}
|
||||
|
||||
data, err := b.PrependBytes(plen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data[0] = byte(radius.Code)
|
||||
data[1] = byte(radius.Identifier)
|
||||
binary.BigEndian.PutUint16(data[2:], uint16(radius.Length))
|
||||
copy(data[4:20], radius.Authenticator[:])
|
||||
|
||||
pos := radiusMinimumRecordSizeInBytes
|
||||
for _, v := range radius.Attributes {
|
||||
if opts.FixLengths {
|
||||
v.Length, err = attributeValueLength(v.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
data[pos] = byte(v.Type)
|
||||
data[pos+1] = byte(v.Length)
|
||||
copy(data[pos+2:], v.Value[:])
|
||||
|
||||
pos += len(v.Value) + 2 // Added Type and Length
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (radius *RADIUS) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeRADIUS
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (radius *RADIUS) NextLayerType() gopacket.LayerType {
|
||||
if len(radius.BaseLayer.Payload) > 0 {
|
||||
return LayerTypeEAP
|
||||
} else {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
}
|
||||
|
||||
// Payload returns the EAP Type-Data for EAP-Message attributes.
|
||||
func (radius *RADIUS) Payload() []byte {
|
||||
return radius.BaseLayer.Payload
|
||||
}
|
||||
|
||||
func decodeRADIUS(data []byte, p gopacket.PacketBuilder) error {
|
||||
radius := &RADIUS{}
|
||||
err := radius.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(radius)
|
||||
p.SetApplicationLayer(radius)
|
||||
next := radius.NextLayerType()
|
||||
if next == gopacket.LayerTypeZero {
|
||||
return nil
|
||||
}
|
||||
return p.NextDecoder(next)
|
||||
}
|
||||
|
||||
func attributeValueLength(v []byte) (RADIUSAttributeLength, error) {
|
||||
n := len(v)
|
||||
if n > 255 {
|
||||
return 0, fmt.Errorf("RADIUS attribute value length %d too long", n)
|
||||
} else {
|
||||
return RADIUSAttributeLength(n), nil
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
// 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 header specified in section 3.2.2.2 of
|
||||
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a
|
||||
// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined)
|
||||
// are currently reserved.
|
||||
type RMCPClass uint8
|
||||
|
||||
// LayerType returns the payload layer type corresponding to a RMCP class.
|
||||
func (c RMCPClass) LayerType() gopacket.LayerType {
|
||||
if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 {
|
||||
return lt
|
||||
}
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func (c RMCPClass) String() string {
|
||||
return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType())
|
||||
}
|
||||
|
||||
const (
|
||||
// RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower
|
||||
// values are considered legacy, while higher values are reserved by the
|
||||
// specification.
|
||||
RMCPVersion1 uint8 = 0x06
|
||||
|
||||
// RMCPNormal indicates a "normal" message, i.e. not an acknowledgement.
|
||||
RMCPNormal uint8 = 0
|
||||
|
||||
// RMCPAck indicates a message is acknowledging a received normal message.
|
||||
RMCPAck uint8 = 1 << 7
|
||||
|
||||
// RMCPClassASF identifies an RMCP message as containing an ASF-RMCP
|
||||
// payload.
|
||||
RMCPClassASF RMCPClass = 0x06
|
||||
|
||||
// RMCPClassIPMI identifies an RMCP message as containing an IPMI payload.
|
||||
RMCPClassIPMI RMCPClass = 0x07
|
||||
|
||||
// RMCPClassOEM identifies an RMCP message as containing an OEM-defined
|
||||
// payload.
|
||||
RMCPClassOEM RMCPClass = 0x08
|
||||
)
|
||||
|
||||
var (
|
||||
rmcpClassLayerTypes = [16]gopacket.LayerType{
|
||||
RMCPClassASF: LayerTypeASF,
|
||||
// RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not
|
||||
// implemented, so we return LayerTypePayload
|
||||
}
|
||||
)
|
||||
|
||||
// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of
|
||||
// a certain class should processed by the provided layer type. This overrides
|
||||
// any existing registrations, including defaults.
|
||||
func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) {
|
||||
rmcpClassLayerTypes[c] = l
|
||||
}
|
||||
|
||||
// RMCP describes the format of an RMCP header, which forms a UDP payload. See
|
||||
// section 3.2.2.2.
|
||||
type RMCP struct {
|
||||
BaseLayer
|
||||
|
||||
// Version identifies the version of the RMCP header. 0x06 indicates RMCP
|
||||
// v1.0; lower values are legacy, higher values are reserved.
|
||||
Version uint8
|
||||
|
||||
// Sequence is the sequence number assicated with the message. Note that
|
||||
// this rolls over to 0 after 254, not 255. Seq num 255 indicates the
|
||||
// receiver must not send an ACK.
|
||||
Sequence uint8
|
||||
|
||||
// Ack indicates whether this packet is an acknowledgement. If it is, the
|
||||
// payload will be empty.
|
||||
Ack bool
|
||||
|
||||
// Class idicates the structure of the payload. There are only 2^4 valid
|
||||
// values, however there is no uint4 data type. N.B. the Ack bit has been
|
||||
// split off into another field. The most significant 4 bits of this field
|
||||
// will always be 0.
|
||||
Class RMCPClass
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeRMCP. It partially satisfies Layer and
|
||||
// SerializableLayer.
|
||||
func (*RMCP) LayerType() gopacket.LayerType {
|
||||
return LayerTypeRMCP
|
||||
}
|
||||
|
||||
// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer.
|
||||
func (r *RMCP) CanDecode() gopacket.LayerClass {
|
||||
return r.LayerType()
|
||||
}
|
||||
|
||||
// DecodeFromBytes makes the layer represent the provided bytes. It partially
|
||||
// satisfies DecodingLayer.
|
||||
func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("invalid RMCP header, length %v less than 4",
|
||||
len(data))
|
||||
}
|
||||
|
||||
r.BaseLayer.Contents = data[:4]
|
||||
r.BaseLayer.Payload = data[4:]
|
||||
|
||||
r.Version = uint8(data[0])
|
||||
// 1 byte reserved
|
||||
r.Sequence = uint8(data[2])
|
||||
r.Ack = data[3]&RMCPAck != 0
|
||||
r.Class = RMCPClass(data[3] & 0xF)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextLayerType returns the data layer of this RMCP layer. This partially
|
||||
// satisfies DecodingLayer.
|
||||
func (r *RMCP) NextLayerType() gopacket.LayerType {
|
||||
return r.Class.LayerType()
|
||||
}
|
||||
|
||||
// Payload returns the data layer. It partially satisfies ApplicationLayer.
|
||||
func (r *RMCP) Payload() []byte {
|
||||
return r.BaseLayer.Payload
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
|
||||
// partially satisfying SerializableLayer.
|
||||
func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
|
||||
// The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths
|
||||
// to work around issues in LAN chips. This is no longer necessary as of
|
||||
// IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The
|
||||
// same approach is taken by FreeIPMI:
|
||||
// http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = r.Version
|
||||
bytes[1] = 0x00
|
||||
bytes[2] = r.Sequence
|
||||
bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeRMCP decodes the byte slice into an RMCP type, and sets the application
|
||||
// layer to it.
|
||||
func decodeRMCP(data []byte, p gopacket.PacketBuilder) error {
|
||||
rmcp := &RMCP{}
|
||||
err := rmcp.DecodeFromBytes(data, p)
|
||||
p.AddLayer(rmcp)
|
||||
p.SetApplicationLayer(rmcp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(rmcp.NextLayerType())
|
||||
}
|
||||
+88
-1
@@ -25,7 +25,7 @@ Specification has this to say:
|
||||
be used for all interfaces.
|
||||
|
||||
This decoder only supports the compact form, because that is the only
|
||||
one for which data was avaialble.
|
||||
one for which data was available.
|
||||
|
||||
The datagram is composed of one or more samples of type flow or counter,
|
||||
and each sample is composed of one or more records describing the sample.
|
||||
@@ -460,28 +460,73 @@ func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) {
|
||||
var sdc SFlowDataSource
|
||||
|
||||
s.EnterpriseID, s.Format = sdf.decode()
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if expanded {
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4]))
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4]))
|
||||
} else {
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4]))
|
||||
s.SourceIDClass, s.SourceIDIndex = sdc.decode()
|
||||
}
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
|
||||
if expanded {
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
} else {
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
}
|
||||
if len(*data) < 4 {
|
||||
return SFlowFlowSample{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
|
||||
for i := uint32(0); i < s.RecordCount; i++ {
|
||||
@@ -2184,19 +2229,61 @@ func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) {
|
||||
|
||||
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
|
||||
ec.EnterpriseID, ec.Format = cdf.decode()
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
if len(*data) < 4 {
|
||||
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
|
||||
}
|
||||
*data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
|
||||
return ec, nil
|
||||
}
|
||||
|
||||
+14
-18
@@ -245,14 +245,11 @@ func (s *SIP) NextLayerType() gopacket.LayerType {
|
||||
|
||||
// DecodeFromBytes decodes the slice into the SIP struct.
|
||||
func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
|
||||
// Init some vars for parsing follow-up
|
||||
var countLines int
|
||||
var line []byte
|
||||
var err error
|
||||
|
||||
// Clean leading new line
|
||||
data = bytes.Trim(data, "\n")
|
||||
var offset int
|
||||
|
||||
// Iterate on all lines of the SIP Headers
|
||||
// and stop when we reach the SDP (aka when the new line
|
||||
@@ -265,19 +262,21 @@ func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
line, err = buffer.ReadBytes(byte('\n'))
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if len(bytes.Trim(line, "\r\n")) > 0 {
|
||||
df.SetTruncated()
|
||||
}
|
||||
break
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
offset += len(line)
|
||||
|
||||
// Trim the new line delimiters
|
||||
line = bytes.Trim(line, "\r\n")
|
||||
|
||||
// Empty line, we hit Body
|
||||
// Putting packet remain in Paypload
|
||||
if len(line) == 0 {
|
||||
s.BaseLayer.Payload = buffer.Bytes()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -298,6 +297,7 @@ func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
|
||||
countLines++
|
||||
}
|
||||
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -469,11 +469,11 @@ func (s *SIP) GetHeader(headerName string) []string {
|
||||
headerName = strings.ToLower(headerName)
|
||||
h := make([]string, 0)
|
||||
if _, ok := s.Headers[headerName]; ok {
|
||||
if len(s.Headers[headerName]) > 0 {
|
||||
return s.Headers[headerName]
|
||||
} else if len(s.Headers[compactSipHeadersCorrespondance[headerName]]) > 0 {
|
||||
return s.Headers[compactSipHeadersCorrespondance[headerName]]
|
||||
}
|
||||
return s.Headers[headerName]
|
||||
}
|
||||
compactHeader := compactSipHeadersCorrespondance[headerName]
|
||||
if _, ok := s.Headers[compactHeader]; ok {
|
||||
return s.Headers[compactHeader]
|
||||
}
|
||||
return h
|
||||
}
|
||||
@@ -482,13 +482,9 @@ func (s *SIP) GetHeader(headerName string) []string {
|
||||
// the specified name. If the current SIP packet has multiple
|
||||
// headers with the same name, it returns the first.
|
||||
func (s *SIP) GetFirstHeader(headerName string) string {
|
||||
headerName = strings.ToLower(headerName)
|
||||
if _, ok := s.Headers[headerName]; ok {
|
||||
if len(s.Headers[headerName]) > 0 {
|
||||
return s.Headers[headerName][0]
|
||||
} else if len(s.Headers[compactSipHeadersCorrespondance[headerName]]) > 0 {
|
||||
return s.Headers[compactSipHeadersCorrespondance[headerName]][0]
|
||||
}
|
||||
headers := s.GetHeader(headerName)
|
||||
if len(headers) > 0 {
|
||||
return headers[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
+9
-5
@@ -108,10 +108,12 @@ func (t TCPOption) String() string {
|
||||
}
|
||||
switch t.OptionType {
|
||||
case TCPOptionKindMSS:
|
||||
return fmt.Sprintf("TCPOption(%s:%v%s)",
|
||||
t.OptionType,
|
||||
binary.BigEndian.Uint16(t.OptionData),
|
||||
hd)
|
||||
if len(t.OptionData) >= 2 {
|
||||
return fmt.Sprintf("TCPOption(%s:%v%s)",
|
||||
t.OptionType,
|
||||
binary.BigEndian.Uint16(t.OptionData),
|
||||
hd)
|
||||
}
|
||||
|
||||
case TCPOptionKindTimestamps:
|
||||
if len(t.OptionData) == 8 {
|
||||
@@ -254,6 +256,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
} else {
|
||||
tcp.Options = tcp.Options[:0]
|
||||
}
|
||||
tcp.Padding = tcp.Padding[:0]
|
||||
if tcp.DataOffset < 5 {
|
||||
return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
|
||||
}
|
||||
@@ -268,6 +271,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
tcp.Payload = data[dataStart:]
|
||||
// From here on, data points just to the header options.
|
||||
data = data[20:dataStart]
|
||||
OPTIONS:
|
||||
for len(data) > 0 {
|
||||
tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
|
||||
opt := &tcp.Options[len(tcp.Options)-1]
|
||||
@@ -275,7 +279,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
case TCPOptionKindEndList: // End of options
|
||||
opt.OptionLength = 1
|
||||
tcp.Padding = data[1:]
|
||||
break
|
||||
break OPTIONS
|
||||
case TCPOptionKindNop: // 1 byte padding
|
||||
opt.OptionLength = 1
|
||||
default:
|
||||
|
||||
+75
@@ -135,6 +135,7 @@ func (t *TLS) decodeTLSRecords(data []byte, df gopacket.DecodeFeedback) error {
|
||||
|
||||
// since there are no further layers, the baselayer's content is
|
||||
// pointing to this layer
|
||||
// TODO: Consider removing this
|
||||
t.BaseLayer = BaseLayer{Contents: data[:len(data)]}
|
||||
|
||||
var h TLSRecordHeader
|
||||
@@ -206,3 +207,77 @@ func (t *TLS) NextLayerType() gopacket.LayerType {
|
||||
func (t *TLS) Payload() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
func (t *TLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
totalLength := 0
|
||||
for _, record := range t.ChangeCipherSpec {
|
||||
if opts.FixLengths {
|
||||
record.Length = 1
|
||||
}
|
||||
totalLength += 5 + 1 // length of header + record
|
||||
}
|
||||
for range t.Handshake {
|
||||
totalLength += 5
|
||||
// TODO
|
||||
}
|
||||
for _, record := range t.AppData {
|
||||
if opts.FixLengths {
|
||||
record.Length = uint16(len(record.Payload))
|
||||
}
|
||||
totalLength += 5 + len(record.Payload)
|
||||
}
|
||||
for _, record := range t.Alert {
|
||||
if len(record.EncryptedMsg) == 0 {
|
||||
if opts.FixLengths {
|
||||
record.Length = 2
|
||||
}
|
||||
totalLength += 5 + 2
|
||||
} else {
|
||||
if opts.FixLengths {
|
||||
record.Length = uint16(len(record.EncryptedMsg))
|
||||
}
|
||||
totalLength += 5 + len(record.EncryptedMsg)
|
||||
}
|
||||
}
|
||||
data, err := b.PrependBytes(totalLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
off := 0
|
||||
for _, record := range t.ChangeCipherSpec {
|
||||
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||
data[off] = byte(record.Message)
|
||||
off++
|
||||
}
|
||||
for _, record := range t.Handshake {
|
||||
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||
// TODO
|
||||
}
|
||||
for _, record := range t.AppData {
|
||||
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||
copy(data[off:], record.Payload)
|
||||
off += len(record.Payload)
|
||||
}
|
||||
for _, record := range t.Alert {
|
||||
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||
if len(record.EncryptedMsg) == 0 {
|
||||
data[off] = byte(record.Level)
|
||||
data[off+1] = byte(record.Description)
|
||||
off += 2
|
||||
} else {
|
||||
copy(data[off:], record.EncryptedMsg)
|
||||
off += len(record.EncryptedMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeHeader(header TLSRecordHeader, data []byte, offset int) int {
|
||||
data[offset] = byte(header.ContentType)
|
||||
binary.BigEndian.PutUint16(data[offset+1:], uint16(header.Version))
|
||||
binary.BigEndian.PutUint16(data[offset+3:], header.Length)
|
||||
|
||||
return offset + 5
|
||||
}
|
||||
|
||||
+5
@@ -8,6 +8,7 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
@@ -142,6 +143,10 @@ func decodeUSB(data []byte, p gopacket.PacketBuilder) error {
|
||||
}
|
||||
|
||||
func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 40 {
|
||||
df.SetTruncated()
|
||||
return errors.New("USB < 40 bytes")
|
||||
}
|
||||
m.ID = binary.LittleEndian.Uint64(data[0:8])
|
||||
m.EventType = USBEventType(data[8])
|
||||
m.TransferType = USBTransportType(data[9])
|
||||
|
||||
+27
-2
@@ -8,7 +8,9 @@ package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
@@ -37,9 +39,21 @@ type VXLAN struct {
|
||||
// LayerType returns LayerTypeVXLAN
|
||||
func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN }
|
||||
|
||||
func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
|
||||
vx := &VXLAN{}
|
||||
// CanDecode returns the layer type this DecodingLayer can decode
|
||||
func (vx *VXLAN) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeVXLAN
|
||||
}
|
||||
|
||||
// NextLayerType retuns the next layer we should see after vxlan
|
||||
func (vx *VXLAN) NextLayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
// DecodeFromBytes takes a byte buffer and decodes
|
||||
func (vx *VXLAN) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("vxlan packet too small")
|
||||
}
|
||||
// VNI is a 24bit number, Uint32 requires 32 bits
|
||||
var buf [4]byte
|
||||
copy(buf[1:], data[4:7])
|
||||
@@ -59,6 +73,17 @@ func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
|
||||
vx.Contents = data[:vxlanLength]
|
||||
vx.Payload = data[vxlanLength:]
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
|
||||
vx := &VXLAN{}
|
||||
err := vx.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.AddLayer(vx)
|
||||
return p.NextDecoder(LinkTypeEthernet)
|
||||
}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||
|
||||
package gopacket
|
||||
|
||||
// Created by gen.go, don't edit manually
|
||||
// Generated at 2019-06-18 11:37:31.308731293 +0600 +06 m=+0.000842599
|
||||
|
||||
// LayersDecoder returns DecodingLayerFunc for specified
|
||||
// DecodingLayerContainer, LayerType value to start decoding with and
|
||||
// some DecodeFeedback.
|
||||
func LayersDecoder(dl DecodingLayerContainer, first LayerType, df DecodeFeedback) DecodingLayerFunc {
|
||||
firstDec, ok := dl.Decoder(first)
|
||||
if !ok {
|
||||
return func([]byte, *[]LayerType) (LayerType, error) {
|
||||
return first, nil
|
||||
}
|
||||
}
|
||||
if dlc, ok := dl.(DecodingLayerSparse); ok {
|
||||
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
|
||||
*decoded = (*decoded)[:0] // Truncated decoded layers.
|
||||
typ := first
|
||||
decoder := firstDec
|
||||
for {
|
||||
if err := decoder.DecodeFromBytes(data, df); err != nil {
|
||||
return LayerTypeZero, err
|
||||
}
|
||||
*decoded = append(*decoded, typ)
|
||||
typ = decoder.NextLayerType()
|
||||
if data = decoder.LayerPayload(); len(data) == 0 {
|
||||
break
|
||||
}
|
||||
if decoder, ok = dlc.Decoder(typ); !ok {
|
||||
return typ, nil
|
||||
}
|
||||
}
|
||||
return LayerTypeZero, nil
|
||||
}
|
||||
}
|
||||
if dlc, ok := dl.(DecodingLayerArray); ok {
|
||||
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
|
||||
*decoded = (*decoded)[:0] // Truncated decoded layers.
|
||||
typ := first
|
||||
decoder := firstDec
|
||||
for {
|
||||
if err := decoder.DecodeFromBytes(data, df); err != nil {
|
||||
return LayerTypeZero, err
|
||||
}
|
||||
*decoded = append(*decoded, typ)
|
||||
typ = decoder.NextLayerType()
|
||||
if data = decoder.LayerPayload(); len(data) == 0 {
|
||||
break
|
||||
}
|
||||
if decoder, ok = dlc.Decoder(typ); !ok {
|
||||
return typ, nil
|
||||
}
|
||||
}
|
||||
return LayerTypeZero, nil
|
||||
}
|
||||
}
|
||||
if dlc, ok := dl.(DecodingLayerMap); ok {
|
||||
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
|
||||
*decoded = (*decoded)[:0] // Truncated decoded layers.
|
||||
typ := first
|
||||
decoder := firstDec
|
||||
for {
|
||||
if err := decoder.DecodeFromBytes(data, df); err != nil {
|
||||
return LayerTypeZero, err
|
||||
}
|
||||
*decoded = append(*decoded, typ)
|
||||
typ = decoder.NextLayerType()
|
||||
if data = decoder.LayerPayload(); len(data) == 0 {
|
||||
break
|
||||
}
|
||||
if decoder, ok = dlc.Decoder(typ); !ok {
|
||||
return typ, nil
|
||||
}
|
||||
}
|
||||
return LayerTypeZero, nil
|
||||
}
|
||||
}
|
||||
dlc := dl
|
||||
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
|
||||
*decoded = (*decoded)[:0] // Truncated decoded layers.
|
||||
typ := first
|
||||
decoder := firstDec
|
||||
for {
|
||||
if err := decoder.DecodeFromBytes(data, df); err != nil {
|
||||
return LayerTypeZero, err
|
||||
}
|
||||
*decoded = append(*decoded, typ)
|
||||
typ = decoder.NextLayerType()
|
||||
if data = decoder.LayerPayload(); len(data) == 0 {
|
||||
break
|
||||
}
|
||||
if decoder, ok = dlc.Decoder(typ); !ok {
|
||||
return typ, nil
|
||||
}
|
||||
}
|
||||
return LayerTypeZero, nil
|
||||
}
|
||||
}
|
||||
+169
-26
@@ -10,6 +10,12 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A container for single LayerType->DecodingLayer mapping.
|
||||
type decodingLayerElem struct {
|
||||
typ LayerType
|
||||
dec DecodingLayer
|
||||
}
|
||||
|
||||
// DecodingLayer is an interface for packet layers that can decode themselves.
|
||||
//
|
||||
// The important part of DecodingLayer is that they decode themselves in-place.
|
||||
@@ -39,15 +45,150 @@ type DecodingLayer interface {
|
||||
LayerPayload() []byte
|
||||
}
|
||||
|
||||
// DecodingLayerFunc decodes given packet and stores decoded LayerType
|
||||
// values into specified slice. Returns either first encountered
|
||||
// unsupported LayerType value or decoding error. In case of success,
|
||||
// returns (LayerTypeZero, nil).
|
||||
type DecodingLayerFunc func([]byte, *[]LayerType) (LayerType, error)
|
||||
|
||||
// DecodingLayerContainer stores all DecodingLayer-s and serves as a
|
||||
// searching tool for DecodingLayerParser.
|
||||
type DecodingLayerContainer interface {
|
||||
// Put adds new DecodingLayer to container. The new instance of
|
||||
// the same DecodingLayerContainer is returned so it may be
|
||||
// implemented as a value receiver.
|
||||
Put(DecodingLayer) DecodingLayerContainer
|
||||
// Decoder returns DecodingLayer to decode given LayerType and
|
||||
// true if it was found. If no decoder found, return false.
|
||||
Decoder(LayerType) (DecodingLayer, bool)
|
||||
// LayersDecoder returns DecodingLayerFunc which decodes given
|
||||
// packet, starting with specified LayerType and DecodeFeedback.
|
||||
LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc
|
||||
}
|
||||
|
||||
// DecodingLayerSparse is a sparse array-based implementation of
|
||||
// DecodingLayerContainer. Each DecodingLayer is addressed in an
|
||||
// allocated slice by LayerType value itself. Though this is the
|
||||
// fastest container it may be memory-consuming if used with big
|
||||
// LayerType values.
|
||||
type DecodingLayerSparse []DecodingLayer
|
||||
|
||||
// Put implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerSparse) Put(d DecodingLayer) DecodingLayerContainer {
|
||||
maxLayerType := LayerType(len(dl) - 1)
|
||||
for _, typ := range d.CanDecode().LayerTypes() {
|
||||
if typ > maxLayerType {
|
||||
maxLayerType = typ
|
||||
}
|
||||
}
|
||||
|
||||
if extra := maxLayerType - LayerType(len(dl)) + 1; extra > 0 {
|
||||
dl = append(dl, make([]DecodingLayer, extra)...)
|
||||
}
|
||||
|
||||
for _, typ := range d.CanDecode().LayerTypes() {
|
||||
dl[typ] = d
|
||||
}
|
||||
return dl
|
||||
}
|
||||
|
||||
// LayersDecoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerSparse) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
|
||||
return LayersDecoder(dl, first, df)
|
||||
}
|
||||
|
||||
// Decoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerSparse) Decoder(typ LayerType) (DecodingLayer, bool) {
|
||||
if int64(typ) < int64(len(dl)) {
|
||||
decoder := dl[typ]
|
||||
return decoder, decoder != nil
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// DecodingLayerArray is an array-based implementation of
|
||||
// DecodingLayerContainer. Each DecodingLayer is searched linearly in
|
||||
// an allocated slice in one-by-one fashion.
|
||||
type DecodingLayerArray []decodingLayerElem
|
||||
|
||||
// Put implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerArray) Put(d DecodingLayer) DecodingLayerContainer {
|
||||
TYPES:
|
||||
for _, typ := range d.CanDecode().LayerTypes() {
|
||||
for i := range dl {
|
||||
if dl[i].typ == typ {
|
||||
dl[i].dec = d
|
||||
continue TYPES
|
||||
}
|
||||
}
|
||||
dl = append(dl, decodingLayerElem{typ, d})
|
||||
}
|
||||
return dl
|
||||
}
|
||||
|
||||
// Decoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerArray) Decoder(typ LayerType) (DecodingLayer, bool) {
|
||||
for i := range dl {
|
||||
if dl[i].typ == typ {
|
||||
return dl[i].dec, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// LayersDecoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerArray) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
|
||||
return LayersDecoder(dl, first, df)
|
||||
}
|
||||
|
||||
// DecodingLayerMap is an map-based implementation of
|
||||
// DecodingLayerContainer. Each DecodingLayer is searched in a map
|
||||
// hashed by LayerType value.
|
||||
type DecodingLayerMap map[LayerType]DecodingLayer
|
||||
|
||||
// Put implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerMap) Put(d DecodingLayer) DecodingLayerContainer {
|
||||
for _, typ := range d.CanDecode().LayerTypes() {
|
||||
if dl == nil {
|
||||
dl = make(map[LayerType]DecodingLayer)
|
||||
}
|
||||
dl[typ] = d
|
||||
}
|
||||
return dl
|
||||
}
|
||||
|
||||
// Decoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerMap) Decoder(typ LayerType) (DecodingLayer, bool) {
|
||||
d, ok := dl[typ]
|
||||
return d, ok
|
||||
}
|
||||
|
||||
// LayersDecoder implements DecodingLayerContainer interface.
|
||||
func (dl DecodingLayerMap) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
|
||||
return LayersDecoder(dl, first, df)
|
||||
}
|
||||
|
||||
// Static code check.
|
||||
var (
|
||||
_ = []DecodingLayerContainer{
|
||||
DecodingLayerSparse(nil),
|
||||
DecodingLayerMap(nil),
|
||||
DecodingLayerArray(nil),
|
||||
}
|
||||
)
|
||||
|
||||
// DecodingLayerParser parses a given set of layer types. See DecodeLayers for
|
||||
// more information on how DecodingLayerParser should be used.
|
||||
type DecodingLayerParser struct {
|
||||
// DecodingLayerParserOptions is the set of options available to the
|
||||
// user to define the parser's behavior.
|
||||
DecodingLayerParserOptions
|
||||
first LayerType
|
||||
decoders map[LayerType]DecodingLayer
|
||||
df DecodeFeedback
|
||||
dlc DecodingLayerContainer
|
||||
first LayerType
|
||||
df DecodeFeedback
|
||||
|
||||
decodeFunc DecodingLayerFunc
|
||||
|
||||
// Truncated is set when a decode layer detects that the packet has been
|
||||
// truncated.
|
||||
Truncated bool
|
||||
@@ -57,9 +198,7 @@ type DecodingLayerParser struct {
|
||||
// the decoding layer's CanDecode layers to the parser... should they be
|
||||
// encountered, they'll be parsed.
|
||||
func (l *DecodingLayerParser) AddDecodingLayer(d DecodingLayer) {
|
||||
for _, typ := range d.CanDecode().LayerTypes() {
|
||||
l.decoders[typ] = d
|
||||
}
|
||||
l.SetDecodingLayerContainer(l.dlc.Put(d))
|
||||
}
|
||||
|
||||
// SetTruncated is used by DecodingLayers to set the Truncated boolean in the
|
||||
@@ -77,18 +216,30 @@ func (l *DecodingLayerParser) SetTruncated() {
|
||||
// subsequently decoded layers to find the next relevant decoder. Should a
|
||||
// deoder not be available for the layer type returned by NextLayerType,
|
||||
// decoding will stop.
|
||||
//
|
||||
// NewDecodingLayerParser uses DecodingLayerMap container by
|
||||
// default.
|
||||
func NewDecodingLayerParser(first LayerType, decoders ...DecodingLayer) *DecodingLayerParser {
|
||||
dlp := &DecodingLayerParser{
|
||||
decoders: make(map[LayerType]DecodingLayer),
|
||||
first: first,
|
||||
}
|
||||
dlp := &DecodingLayerParser{first: first}
|
||||
dlp.df = dlp // Cast this once to the interface
|
||||
// default container
|
||||
dlc := DecodingLayerContainer(DecodingLayerMap(make(map[LayerType]DecodingLayer)))
|
||||
for _, d := range decoders {
|
||||
dlp.AddDecodingLayer(d)
|
||||
dlc = dlc.Put(d)
|
||||
}
|
||||
|
||||
dlp.SetDecodingLayerContainer(dlc)
|
||||
return dlp
|
||||
}
|
||||
|
||||
// SetDecodingLayerContainer specifies container with decoders. This
|
||||
// call replaces all decoders already registered in given instance of
|
||||
// DecodingLayerParser.
|
||||
func (l *DecodingLayerParser) SetDecodingLayerContainer(dlc DecodingLayerContainer) {
|
||||
l.dlc = dlc
|
||||
l.decodeFunc = l.dlc.LayersDecoder(l.first, l.df)
|
||||
}
|
||||
|
||||
// DecodeLayers decodes as many layers as possible from the given data. It
|
||||
// initially treats the data as layer type 'typ', then uses NextLayerType on
|
||||
// each subsequent decoded layer until it gets to a layer type it doesn't know
|
||||
@@ -153,23 +304,15 @@ func (l *DecodingLayerParser) DecodeLayers(data []byte, decoded *[]LayerType) (e
|
||||
if !l.IgnorePanic {
|
||||
defer panicToError(&err)
|
||||
}
|
||||
typ := l.first
|
||||
*decoded = (*decoded)[:0] // Truncated decoded layers.
|
||||
for len(data) > 0 {
|
||||
decoder, ok := l.decoders[typ]
|
||||
if !ok {
|
||||
if l.IgnoreUnsupported {
|
||||
return nil
|
||||
}
|
||||
return UnsupportedLayerType(typ)
|
||||
} else if err = decoder.DecodeFromBytes(data, l.df); err != nil {
|
||||
return err
|
||||
typ, err := l.decodeFunc(data, decoded)
|
||||
if typ != LayerTypeZero {
|
||||
// no decoder
|
||||
if l.IgnoreUnsupported {
|
||||
return nil
|
||||
}
|
||||
*decoded = append(*decoded, typ)
|
||||
typ = decoder.NextLayerType()
|
||||
data = decoder.LayerPayload()
|
||||
return UnsupportedLayerType(typ)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UnsupportedLayerType is returned by DecodingLayerParser if DecodeLayers
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- master
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test -race ./...
|
||||
- go test -tags=noasm ./...
|
||||
|
||||
stages:
|
||||
- gofmt
|
||||
- test
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: 'master'
|
||||
fast_finish: true
|
||||
include:
|
||||
- stage: gofmt
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- diff <(gofmt -d .) <(printf "")
|
||||
- diff <(gofmt -d ./private) <(printf "")
|
||||
- go install github.com/klauspost/asmfmt/cmd/asmfmt
|
||||
- diff <(asmfmt -d .) <(printf "")
|
||||
- stage: i386
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- GOOS=linux GOARCH=386 go test .
|
||||
-191
@@ -1,191 +0,0 @@
|
||||
# cpuid
|
||||
Package cpuid provides information about the CPU running the current program.
|
||||
|
||||
CPU features are detected on startup, and kept for fast access through the life of the application.
|
||||
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
|
||||
|
||||
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
|
||||
|
||||
Package home: https://github.com/klauspost/cpuid
|
||||
|
||||
[![GoDoc][1]][2] [![Build Status][3]][4]
|
||||
|
||||
[1]: https://godoc.org/github.com/klauspost/cpuid?status.svg
|
||||
[2]: https://godoc.org/github.com/klauspost/cpuid
|
||||
[3]: https://travis-ci.org/klauspost/cpuid.svg?branch=master
|
||||
[4]: https://travis-ci.org/klauspost/cpuid
|
||||
|
||||
# features
|
||||
|
||||
## x86 CPU Instructions
|
||||
* **CMOV** (i686 CMOV)
|
||||
* **NX** (NX (No-Execute) bit)
|
||||
* **AMD3DNOW** (AMD 3DNOW)
|
||||
* **AMD3DNOWEXT** (AMD 3DNowExt)
|
||||
* **MMX** (standard MMX)
|
||||
* **MMXEXT** (SSE integer functions or AMD MMX ext)
|
||||
* **SSE** (SSE functions)
|
||||
* **SSE2** (P4 SSE functions)
|
||||
* **SSE3** (Prescott SSE3 functions)
|
||||
* **SSSE3** (Conroe SSSE3 functions)
|
||||
* **SSE4** (Penryn SSE4.1 functions)
|
||||
* **SSE4A** (AMD Barcelona microarchitecture SSE4a instructions)
|
||||
* **SSE42** (Nehalem SSE4.2 functions)
|
||||
* **AVX** (AVX functions)
|
||||
* **AVX2** (AVX2 functions)
|
||||
* **FMA3** (Intel FMA 3)
|
||||
* **FMA4** (Bulldozer FMA4 functions)
|
||||
* **XOP** (Bulldozer XOP functions)
|
||||
* **F16C** (Half-precision floating-point conversion)
|
||||
* **BMI1** (Bit Manipulation Instruction Set 1)
|
||||
* **BMI2** (Bit Manipulation Instruction Set 2)
|
||||
* **TBM** (AMD Trailing Bit Manipulation)
|
||||
* **LZCNT** (LZCNT instruction)
|
||||
* **POPCNT** (POPCNT instruction)
|
||||
* **AESNI** (Advanced Encryption Standard New Instructions)
|
||||
* **CLMUL** (Carry-less Multiplication)
|
||||
* **HTT** (Hyperthreading (enabled))
|
||||
* **HLE** (Hardware Lock Elision)
|
||||
* **RTM** (Restricted Transactional Memory)
|
||||
* **RDRAND** (RDRAND instruction is available)
|
||||
* **RDSEED** (RDSEED instruction is available)
|
||||
* **ADX** (Intel ADX (Multi-Precision Add-Carry Instruction Extensions))
|
||||
* **SHA** (Intel SHA Extensions)
|
||||
* **AVX512F** (AVX-512 Foundation)
|
||||
* **AVX512DQ** (AVX-512 Doubleword and Quadword Instructions)
|
||||
* **AVX512IFMA** (AVX-512 Integer Fused Multiply-Add Instructions)
|
||||
* **AVX512PF** (AVX-512 Prefetch Instructions)
|
||||
* **AVX512ER** (AVX-512 Exponential and Reciprocal Instructions)
|
||||
* **AVX512CD** (AVX-512 Conflict Detection Instructions)
|
||||
* **AVX512BW** (AVX-512 Byte and Word Instructions)
|
||||
* **AVX512VL** (AVX-512 Vector Length Extensions)
|
||||
* **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions)
|
||||
* **AVX512VBMI2** (AVX-512 Vector Bit Manipulation Instructions, Version 2)
|
||||
* **AVX512VNNI** (AVX-512 Vector Neural Network Instructions)
|
||||
* **AVX512VPOPCNTDQ** (AVX-512 Vector Population Count Doubleword and Quadword)
|
||||
* **GFNI** (Galois Field New Instructions)
|
||||
* **VAES** (Vector AES)
|
||||
* **AVX512BITALG** (AVX-512 Bit Algorithms)
|
||||
* **VPCLMULQDQ** (Carry-Less Multiplication Quadword)
|
||||
* **AVX512BF16** (AVX-512 BFLOAT16 Instructions)
|
||||
* **AVX512VP2INTERSECT** (AVX-512 Intersect for D/Q)
|
||||
* **MPX** (Intel MPX (Memory Protection Extensions))
|
||||
* **ERMS** (Enhanced REP MOVSB/STOSB)
|
||||
* **RDTSCP** (RDTSCP Instruction)
|
||||
* **CX16** (CMPXCHG16B Instruction)
|
||||
* **SGX** (Software Guard Extensions, with activation details)
|
||||
* **VMX** (Virtual Machine Extensions)
|
||||
|
||||
## Performance
|
||||
* **RDTSCP()** Returns current cycle count. Can be used for benchmarking.
|
||||
* **SSE2SLOW** (SSE2 is supported, but usually not faster)
|
||||
* **SSE3SLOW** (SSE3 is supported, but usually not faster)
|
||||
* **ATOM** (Atom processor, some SSSE3 instructions are slower)
|
||||
* **Cache line** (Probable size of a cache line).
|
||||
* **L1, L2, L3 Cache size** on newer Intel/AMD CPUs.
|
||||
|
||||
## ARM CPU features
|
||||
|
||||
# ARM FEATURE DETECTION DISABLED!
|
||||
|
||||
See [#52](https://github.com/klauspost/cpuid/issues/52).
|
||||
|
||||
Currently only `arm64` platforms are implemented.
|
||||
|
||||
* **FP** Single-precision and double-precision floating point
|
||||
* **ASIMD** Advanced SIMD
|
||||
* **EVTSTRM** Generic timer
|
||||
* **AES** AES instructions
|
||||
* **PMULL** Polynomial Multiply instructions (PMULL/PMULL2)
|
||||
* **SHA1** SHA-1 instructions (SHA1C, etc)
|
||||
* **SHA2** SHA-2 instructions (SHA256H, etc)
|
||||
* **CRC32** CRC32/CRC32C instructions
|
||||
* **ATOMICS** Large System Extensions (LSE)
|
||||
* **FPHP** Half-precision floating point
|
||||
* **ASIMDHP** Advanced SIMD half-precision floating point
|
||||
* **ARMCPUID** Some CPU ID registers readable at user-level
|
||||
* **ASIMDRDM** Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH)
|
||||
* **JSCVT** Javascript-style double->int convert (FJCVTZS)
|
||||
* **FCMA** Floating point complex number addition and multiplication
|
||||
* **LRCPC** Weaker release consistency (LDAPR, etc)
|
||||
* **DCPOP** Data cache clean to Point of Persistence (DC CVAP)
|
||||
* **SHA3** SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
|
||||
* **SM3** SM3 instructions
|
||||
* **SM4** SM4 instructions
|
||||
* **ASIMDDP** SIMD Dot Product
|
||||
* **SHA512** SHA512 instructions
|
||||
* **SVE** Scalable Vector Extension
|
||||
* **GPA** Generic Pointer Authentication
|
||||
|
||||
## Cpu Vendor/VM
|
||||
* **Intel**
|
||||
* **AMD**
|
||||
* **VIA**
|
||||
* **Transmeta**
|
||||
* **NSC**
|
||||
* **KVM** (Kernel-based Virtual Machine)
|
||||
* **MSVM** (Microsoft Hyper-V or Windows Virtual PC)
|
||||
* **VMware**
|
||||
* **XenHVM**
|
||||
* **Bhyve**
|
||||
* **Hygon**
|
||||
|
||||
# installing
|
||||
|
||||
```go get github.com/klauspost/cpuid```
|
||||
|
||||
# example
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/klauspost/cpuid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Print basic CPU information:
|
||||
fmt.Println("Name:", cpuid.CPU.BrandName)
|
||||
fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores)
|
||||
fmt.Println("ThreadsPerCore:", cpuid.CPU.ThreadsPerCore)
|
||||
fmt.Println("LogicalCores:", cpuid.CPU.LogicalCores)
|
||||
fmt.Println("Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model)
|
||||
fmt.Println("Features:", cpuid.CPU.Features)
|
||||
fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine)
|
||||
fmt.Println("L1 Data Cache:", cpuid.CPU.Cache.L1D, "bytes")
|
||||
fmt.Println("L1 Instruction Cache:", cpuid.CPU.Cache.L1D, "bytes")
|
||||
fmt.Println("L2 Cache:", cpuid.CPU.Cache.L2, "bytes")
|
||||
fmt.Println("L3 Cache:", cpuid.CPU.Cache.L3, "bytes")
|
||||
|
||||
// Test if we have a specific feature:
|
||||
if cpuid.CPU.SSE() {
|
||||
fmt.Println("We have Streaming SIMD Extensions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sample output:
|
||||
```
|
||||
>go run main.go
|
||||
Name: Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz
|
||||
PhysicalCores: 2
|
||||
ThreadsPerCore: 2
|
||||
LogicalCores: 4
|
||||
Family 6 Model: 42
|
||||
Features: CMOV,MMX,MMXEXT,SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2,AVX,AESNI,CLMUL
|
||||
Cacheline bytes: 64
|
||||
We have Streaming SIMD Extensions
|
||||
```
|
||||
|
||||
# private package
|
||||
|
||||
In the "private" folder you can find an autogenerated version of the library you can include in your own packages.
|
||||
|
||||
For this purpose all exports are removed, and functions and constants are lowercased.
|
||||
|
||||
This is not a recommended way of using the library, but provided for convenience, if it is difficult for you to use external packages.
|
||||
|
||||
# license
|
||||
|
||||
This code is published under an MIT license. See LICENSE file for more information.
|
||||
-1504
File diff suppressed because it is too large
Load Diff
-3
@@ -1,3 +0,0 @@
|
||||
module github.com/klauspost/cpuid
|
||||
|
||||
go 1.12
|
||||
Generated
+74
@@ -0,0 +1,74 @@
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
|
||||
builds:
|
||||
-
|
||||
id: "cpuid"
|
||||
binary: cpuid
|
||||
main: ./cmd/cpuid/main.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -ldflags=-s -w
|
||||
goos:
|
||||
- aix
|
||||
- linux
|
||||
- freebsd
|
||||
- netbsd
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
|
||||
archives:
|
||||
-
|
||||
id: cpuid
|
||||
name_template: "cpuid-{{ .Os }}_{{ .Arch }}_{{ .Version }}"
|
||||
replacements:
|
||||
aix: AIX
|
||||
darwin: OSX
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
freebsd: FreeBSD
|
||||
netbsd: NetBSD
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^doc:'
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^tests:'
|
||||
- '^Update\sREADME.md'
|
||||
|
||||
nfpms:
|
||||
-
|
||||
file_name_template: "cpuid_package_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
vendor: Klaus Post
|
||||
homepage: https://github.com/klauspost/cpuid
|
||||
maintainer: Klaus Post <klauspost@gmail.com>
|
||||
description: CPUID Tool
|
||||
license: BSD 3-Clause
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
freebsd: FreeBSD
|
||||
amd64: x86_64
|
||||
Generated
Vendored
Generated
Vendored
+493
@@ -0,0 +1,493 @@
|
||||
# cpuid
|
||||
Package cpuid provides information about the CPU running the current program.
|
||||
|
||||
CPU features are detected on startup, and kept for fast access through the life of the application.
|
||||
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
|
||||
|
||||
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
|
||||
|
||||
Package home: https://github.com/klauspost/cpuid
|
||||
|
||||
[](https://pkg.go.dev/github.com/klauspost/cpuid/v2)
|
||||
[![Build Status][3]][4]
|
||||
|
||||
[3]: https://travis-ci.org/klauspost/cpuid.svg?branch=master
|
||||
[4]: https://travis-ci.org/klauspost/cpuid
|
||||
|
||||
## installing
|
||||
|
||||
`go get -u github.com/klauspost/cpuid/v2` using modules.
|
||||
Drop `v2` for others.
|
||||
|
||||
Installing binary:
|
||||
|
||||
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
|
||||
|
||||
Or download binaries from release page: https://github.com/klauspost/cpuid/releases
|
||||
|
||||
### Homebrew
|
||||
|
||||
For macOS/Linux users, you can install via [brew](https://brew.sh/)
|
||||
|
||||
```sh
|
||||
$ brew install cpuid
|
||||
```
|
||||
|
||||
## example
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Print basic CPU information:
|
||||
fmt.Println("Name:", CPU.BrandName)
|
||||
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
|
||||
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
|
||||
fmt.Println("LogicalCores:", CPU.LogicalCores)
|
||||
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
|
||||
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
|
||||
fmt.Println("Cacheline bytes:", CPU.CacheLine)
|
||||
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
|
||||
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
|
||||
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
|
||||
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
|
||||
fmt.Println("Frequency", CPU.Hz, "hz")
|
||||
|
||||
// Test if we have these specific features:
|
||||
if CPU.Supports(SSE, SSE2) {
|
||||
fmt.Println("We have Streaming SIMD 2 Extensions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sample output:
|
||||
```
|
||||
>go run main.go
|
||||
Name: AMD Ryzen 9 3950X 16-Core Processor
|
||||
PhysicalCores: 16
|
||||
ThreadsPerCore: 2
|
||||
LogicalCores: 32
|
||||
Family 23 Model: 113 Vendor ID: AMD
|
||||
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CMOV,CX16,F16C,FMA3,HTT,HYPERVISOR,LZCNT,MMX,MMXEXT,NX,POPCNT,RDRAND,RDSEED,RDTSCP,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3
|
||||
Cacheline bytes: 64
|
||||
L1 Data Cache: 32768 bytes
|
||||
L1 Instruction Cache: 32768 bytes
|
||||
L2 Cache: 524288 bytes
|
||||
L3 Cache: 16777216 bytes
|
||||
Frequency 0 hz
|
||||
We have Streaming SIMD 2 Extensions
|
||||
```
|
||||
|
||||
# usage
|
||||
|
||||
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
|
||||
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
|
||||
|
||||
To test a larger number of features, they can be combined using `f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)`, etc.
|
||||
This can be using with `cpuid.CPU.HasAll(f)` to quickly test if all features are supported.
|
||||
|
||||
Note that for some cpu/os combinations some features will not be detected.
|
||||
`amd64` has rather good support and should work reliably on all platforms.
|
||||
|
||||
Note that hypervisors may not pass through all CPU features through to the guest OS,
|
||||
so even if your host supports a feature it may not be visible on guests.
|
||||
|
||||
## arm64 feature detection
|
||||
|
||||
Not all operating systems provide ARM features directly
|
||||
and there is no safe way to do so for the rest.
|
||||
|
||||
Currently `arm64/linux` and `arm64/freebsd` should be quite reliable.
|
||||
`arm64/darwin` adds features expected from the M1 processor, but a lot remains undetected.
|
||||
|
||||
A `DetectARM()` can be used if you are able to control your deployment,
|
||||
it will detect CPU features, but may crash if the OS doesn't intercept the calls.
|
||||
A `-cpu.arm` flag for detecting unsafe ARM features can be added. See below.
|
||||
|
||||
Note that currently only features are detected on ARM,
|
||||
no additional information is currently available.
|
||||
|
||||
## flags
|
||||
|
||||
It is possible to add flags that affects cpu detection.
|
||||
|
||||
For this the `Flags()` command is provided.
|
||||
|
||||
This must be called *before* `flag.Parse()` AND after the flags have been parsed `Detect()` must be called.
|
||||
|
||||
This means that any detection used in `init()` functions will not contain these flags.
|
||||
|
||||
Example:
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cpuid.Flags()
|
||||
flag.Parse()
|
||||
cpuid.Detect()
|
||||
|
||||
// Test if we have these specific features:
|
||||
if cpuid.CPU.Supports(cpuid.SSE, cpuid.SSE2) {
|
||||
fmt.Println("We have Streaming SIMD 2 Extensions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## commandline
|
||||
|
||||
Download as binary from: https://github.com/klauspost/cpuid/releases
|
||||
|
||||
Install from source:
|
||||
|
||||
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
λ cpuid
|
||||
Name: AMD Ryzen 9 3950X 16-Core Processor
|
||||
Vendor String: AuthenticAMD
|
||||
Vendor ID: AMD
|
||||
PhysicalCores: 16
|
||||
Threads Per Core: 2
|
||||
Logical Cores: 32
|
||||
CPU Family 23 Model: 113
|
||||
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
|
||||
Microarchitecture level: 3
|
||||
Cacheline bytes: 64
|
||||
L1 Instruction Cache: 32768 bytes
|
||||
L1 Data Cache: 32768 bytes
|
||||
L2 Cache: 524288 bytes
|
||||
L3 Cache: 16777216 bytes
|
||||
|
||||
```
|
||||
### JSON Output:
|
||||
|
||||
```
|
||||
λ cpuid --json
|
||||
{
|
||||
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
|
||||
"VendorID": 2,
|
||||
"VendorString": "AuthenticAMD",
|
||||
"PhysicalCores": 16,
|
||||
"ThreadsPerCore": 2,
|
||||
"LogicalCores": 32,
|
||||
"Family": 23,
|
||||
"Model": 113,
|
||||
"CacheLine": 64,
|
||||
"Hz": 0,
|
||||
"BoostFreq": 0,
|
||||
"Cache": {
|
||||
"L1I": 32768,
|
||||
"L1D": 32768,
|
||||
"L2": 524288,
|
||||
"L3": 16777216
|
||||
},
|
||||
"SGX": {
|
||||
"Available": false,
|
||||
"LaunchControl": false,
|
||||
"SGX1Supported": false,
|
||||
"SGX2Supported": false,
|
||||
"MaxEnclaveSizeNot64": 0,
|
||||
"MaxEnclaveSize64": 0,
|
||||
"EPCSections": null
|
||||
},
|
||||
"Features": [
|
||||
"ADX",
|
||||
"AESNI",
|
||||
"AVX",
|
||||
"AVX2",
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CLZERO",
|
||||
"CMOV",
|
||||
"CMPXCHG8",
|
||||
"CPBOOST",
|
||||
"CX16",
|
||||
"F16C",
|
||||
"FMA3",
|
||||
"FXSR",
|
||||
"FXSROPT",
|
||||
"HTT",
|
||||
"HYPERVISOR",
|
||||
"LAHF",
|
||||
"LZCNT",
|
||||
"MCAOVERFLOW",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"MOVBE",
|
||||
"NX",
|
||||
"OSXSAVE",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SCE",
|
||||
"SHA",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4",
|
||||
"SSE42",
|
||||
"SSE4A",
|
||||
"SSSE3",
|
||||
"SUCCOR",
|
||||
"X87",
|
||||
"XSAVE"
|
||||
],
|
||||
"X64Level": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Check CPU microarch level
|
||||
|
||||
```
|
||||
λ cpuid --check-level=3
|
||||
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
|
||||
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
|
||||
Exit Code 0
|
||||
|
||||
λ cpuid --check-level=4
|
||||
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
|
||||
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
|
||||
Exit Code 1
|
||||
```
|
||||
|
||||
|
||||
## Available flags
|
||||
|
||||
### x86 & amd64
|
||||
|
||||
| Feature Flag | Description |
|
||||
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ADX | Intel ADX (Multi-Precision Add-Carry Instruction Extensions) |
|
||||
| AESNI | Advanced Encryption Standard New Instructions |
|
||||
| AMD3DNOW | AMD 3DNOW |
|
||||
| AMD3DNOWEXT | AMD 3DNowExt |
|
||||
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
|
||||
| AMXINT8 | Tile computational operations on 8-bit integers |
|
||||
| AMXFP16 | Tile computational operations on FP16 numbers |
|
||||
| AMXTILE | Tile architecture |
|
||||
| AVX | AVX functions |
|
||||
| AVX2 | AVX2 functions |
|
||||
| AVX512BF16 | AVX-512 BFLOAT16 Instructions |
|
||||
| AVX512BITALG | AVX-512 Bit Algorithms |
|
||||
| AVX512BW | AVX-512 Byte and Word Instructions |
|
||||
| AVX512CD | AVX-512 Conflict Detection Instructions |
|
||||
| AVX512DQ | AVX-512 Doubleword and Quadword Instructions |
|
||||
| AVX512ER | AVX-512 Exponential and Reciprocal Instructions |
|
||||
| AVX512F | AVX-512 Foundation |
|
||||
| AVX512FP16 | AVX-512 FP16 Instructions |
|
||||
| AVX512IFMA | AVX-512 Integer Fused Multiply-Add Instructions |
|
||||
| AVX512PF | AVX-512 Prefetch Instructions |
|
||||
| AVX512VBMI | AVX-512 Vector Bit Manipulation Instructions |
|
||||
| AVX512VBMI2 | AVX-512 Vector Bit Manipulation Instructions, Version 2 |
|
||||
| AVX512VL | AVX-512 Vector Length Extensions |
|
||||
| AVX512VNNI | AVX-512 Vector Neural Network Instructions |
|
||||
| AVX512VP2INTERSECT | AVX-512 Intersect for D/Q |
|
||||
| AVX512VPOPCNTDQ | AVX-512 Vector Population Count Doubleword and Quadword |
|
||||
| AVXIFMA | AVX-IFMA instructions |
|
||||
| AVXNECONVERT | AVX-NE-CONVERT instructions |
|
||||
| AVXSLOW | Indicates the CPU performs 2 128 bit operations instead of one |
|
||||
| AVXVNNI | AVX (VEX encoded) VNNI neural network instructions |
|
||||
| AVXVNNIINT8 | AVX-VNNI-INT8 instructions |
|
||||
| BHI_CTRL | Branch History Injection and Intra-mode Branch Target Injection / CVE-2022-0001, CVE-2022-0002 / INTEL-SA-00598 |
|
||||
| BMI1 | Bit Manipulation Instruction Set 1 |
|
||||
| BMI2 | Bit Manipulation Instruction Set 2 |
|
||||
| CETIBT | Intel CET Indirect Branch Tracking |
|
||||
| CETSS | Intel CET Shadow Stack |
|
||||
| CLDEMOTE | Cache Line Demote |
|
||||
| CLMUL | Carry-less Multiplication |
|
||||
| CLZERO | CLZERO instruction supported |
|
||||
| CMOV | i686 CMOV |
|
||||
| CMPCCXADD | CMPCCXADD instructions |
|
||||
| CMPSB_SCADBS_SHORT | Fast short CMPSB and SCASB |
|
||||
| CMPXCHG8 | CMPXCHG8 instruction |
|
||||
| CPBOOST | Core Performance Boost |
|
||||
| CPPC | AMD: Collaborative Processor Performance Control |
|
||||
| CX16 | CMPXCHG16B Instruction |
|
||||
| EFER_LMSLE_UNS | AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ |
|
||||
| ENQCMD | Enqueue Command |
|
||||
| ERMS | Enhanced REP MOVSB/STOSB |
|
||||
| F16C | Half-precision floating-point conversion |
|
||||
| FLUSH_L1D | Flush L1D cache |
|
||||
| FMA3 | Intel FMA 3. Does not imply AVX. |
|
||||
| FMA4 | Bulldozer FMA4 functions |
|
||||
| FP128 | AMD: When set, the internal FP/SIMD execution datapath is 128-bits wide |
|
||||
| FP256 | AMD: When set, the internal FP/SIMD execution datapath is 256-bits wide |
|
||||
| FSRM | Fast Short Rep Mov |
|
||||
| FXSR | FXSAVE, FXRESTOR instructions, CR4 bit 9 |
|
||||
| FXSROPT | FXSAVE/FXRSTOR optimizations |
|
||||
| GFNI | Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage. |
|
||||
| HLE | Hardware Lock Elision |
|
||||
| HRESET | If set CPU supports history reset and the IA32_HRESET_ENABLE MSR |
|
||||
| HTT | Hyperthreading (enabled) |
|
||||
| HWA | Hardware assert supported. Indicates support for MSRC001_10 |
|
||||
| HYBRID_CPU | This part has CPUs of more than one type. |
|
||||
| HYPERVISOR | This bit has been reserved by Intel & AMD for use by hypervisors |
|
||||
| IA32_ARCH_CAP | IA32_ARCH_CAPABILITIES MSR (Intel) |
|
||||
| IA32_CORE_CAP | IA32_CORE_CAPABILITIES MSR |
|
||||
| IBPB | Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) |
|
||||
| IBRS | AMD: Indirect Branch Restricted Speculation |
|
||||
| IBRS_PREFERRED | AMD: IBRS is preferred over software solution |
|
||||
| IBRS_PROVIDES_SMP | AMD: IBRS provides Same Mode Protection |
|
||||
| IBS | Instruction Based Sampling (AMD) |
|
||||
| IBSBRNTRGT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSFETCHSAM | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSFFV | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPCNT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPCNTEXT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPSAM | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSRDWROPCNT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSRIPINVALIDCHK | Instruction Based Sampling Feature (AMD) |
|
||||
| IBS_FETCH_CTLX | AMD: IBS fetch control extended MSR supported |
|
||||
| IBS_OPDATA4 | AMD: IBS op data 4 MSR supported |
|
||||
| IBS_OPFUSE | AMD: Indicates support for IbsOpFuse |
|
||||
| IBS_PREVENTHOST | Disallowing IBS use by the host supported |
|
||||
| IBS_ZEN4 | Fetch and Op IBS support IBS extensions added with Zen4 |
|
||||
| IDPRED_CTRL | IPRED_DIS |
|
||||
| INT_WBINVD | WBINVD/WBNOINVD are interruptible. |
|
||||
| INVLPGB | NVLPGB and TLBSYNC instruction supported |
|
||||
| LAHF | LAHF/SAHF in long mode |
|
||||
| LAM | If set, CPU supports Linear Address Masking |
|
||||
| LBRVIRT | LBR virtualization |
|
||||
| LZCNT | LZCNT instruction |
|
||||
| MCAOVERFLOW | MCA overflow recovery support. |
|
||||
| MCDT_NO | Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it. |
|
||||
| MCOMMIT | MCOMMIT instruction supported |
|
||||
| MD_CLEAR | VERW clears CPU buffers |
|
||||
| MMX | standard MMX |
|
||||
| MMXEXT | SSE integer functions or AMD MMX ext |
|
||||
| MOVBE | MOVBE instruction (big-endian) |
|
||||
| MOVDIR64B | Move 64 Bytes as Direct Store |
|
||||
| MOVDIRI | Move Doubleword as Direct Store |
|
||||
| MOVSB_ZL | Fast Zero-Length MOVSB |
|
||||
| MPX | Intel MPX (Memory Protection Extensions) |
|
||||
| MOVU | MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD |
|
||||
| MSRIRC | Instruction Retired Counter MSR available |
|
||||
| MSRLIST | Read/Write List of Model Specific Registers |
|
||||
| MSR_PAGEFLUSH | Page Flush MSR available |
|
||||
| NRIPS | Indicates support for NRIP save on VMEXIT |
|
||||
| NX | NX (No-Execute) bit |
|
||||
| OSXSAVE | XSAVE enabled by OS |
|
||||
| PCONFIG | PCONFIG for Intel Multi-Key Total Memory Encryption |
|
||||
| POPCNT | POPCNT instruction |
|
||||
| PPIN | AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled |
|
||||
| PREFETCHI | PREFETCHIT0/1 instructions |
|
||||
| PSFD | Predictive Store Forward Disable |
|
||||
| RDPRU | RDPRU instruction supported |
|
||||
| RDRAND | RDRAND instruction is available |
|
||||
| RDSEED | RDSEED instruction is available |
|
||||
| RDTSCP | RDTSCP Instruction |
|
||||
| RRSBA_CTRL | Restricted RSB Alternate |
|
||||
| RTM | Restricted Transactional Memory |
|
||||
| RTM_ALWAYS_ABORT | Indicates that the loaded microcode is forcing RTM abort. |
|
||||
| SERIALIZE | Serialize Instruction Execution |
|
||||
| SEV | AMD Secure Encrypted Virtualization supported |
|
||||
| SEV_64BIT | AMD SEV guest execution only allowed from a 64-bit host |
|
||||
| SEV_ALTERNATIVE | AMD SEV Alternate Injection supported |
|
||||
| SEV_DEBUGSWAP | Full debug state swap supported for SEV-ES guests |
|
||||
| SEV_ES | AMD SEV Encrypted State supported |
|
||||
| SEV_RESTRICTED | AMD SEV Restricted Injection supported |
|
||||
| SEV_SNP | AMD SEV Secure Nested Paging supported |
|
||||
| SGX | Software Guard Extensions |
|
||||
| SGXLC | Software Guard Extensions Launch Control |
|
||||
| SHA | Intel SHA Extensions |
|
||||
| SME | AMD Secure Memory Encryption supported |
|
||||
| SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced |
|
||||
| SPEC_CTRL_SSBD | Speculative Store Bypass Disable |
|
||||
| SRBDS_CTRL | SRBDS mitigation MSR available |
|
||||
| SSE | SSE functions |
|
||||
| SSE2 | P4 SSE functions |
|
||||
| SSE3 | Prescott SSE3 functions |
|
||||
| SSE4 | Penryn SSE4.1 functions |
|
||||
| SSE42 | Nehalem SSE4.2 functions |
|
||||
| SSE4A | AMD Barcelona microarchitecture SSE4a instructions |
|
||||
| SSSE3 | Conroe SSSE3 functions |
|
||||
| STIBP | Single Thread Indirect Branch Predictors |
|
||||
| STIBP_ALWAYSON | AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On |
|
||||
| STOSB_SHORT | Fast short STOSB |
|
||||
| SUCCOR | Software uncorrectable error containment and recovery capability. |
|
||||
| SVM | AMD Secure Virtual Machine |
|
||||
| SVMDA | Indicates support for the SVM decode assists. |
|
||||
| SVMFBASID | SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control |
|
||||
| SVML | AMD SVM lock. Indicates support for SVM-Lock. |
|
||||
| SVMNP | AMD SVM nested paging |
|
||||
| SVMPF | SVM pause intercept filter. Indicates support for the pause intercept filter |
|
||||
| SVMPFT | SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold |
|
||||
| SYSCALL | System-Call Extension (SCE): SYSCALL and SYSRET instructions. |
|
||||
| SYSEE | SYSENTER and SYSEXIT instructions |
|
||||
| TBM | AMD Trailing Bit Manipulation |
|
||||
| TDX_GUEST | Intel Trust Domain Extensions Guest |
|
||||
| TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations |
|
||||
| TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. |
|
||||
| TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. |
|
||||
| TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 |
|
||||
| TSXLDTRK | Intel TSX Suspend Load Address Tracking |
|
||||
| VAES | Vector AES. AVX(512) versions requires additional checks. |
|
||||
| VMCBCLEAN | VMCB clean bits. Indicates support for VMCB clean bits. |
|
||||
| VMPL | AMD VM Permission Levels supported |
|
||||
| VMSA_REGPROT | AMD VMSA Register Protection supported |
|
||||
| VMX | Virtual Machine Extensions |
|
||||
| VPCLMULQDQ | Carry-Less Multiplication Quadword. Requires AVX for 3 register versions. |
|
||||
| VTE | AMD Virtual Transparent Encryption supported |
|
||||
| WAITPKG | TPAUSE, UMONITOR, UMWAIT |
|
||||
| WBNOINVD | Write Back and Do Not Invalidate Cache |
|
||||
| WRMSRNS | Non-Serializing Write to Model Specific Register |
|
||||
| X87 | FPU |
|
||||
| XGETBV1 | Supports XGETBV with ECX = 1 |
|
||||
| XOP | Bulldozer XOP functions |
|
||||
| XSAVE | XSAVE, XRESTOR, XSETBV, XGETBV |
|
||||
| XSAVEC | Supports XSAVEC and the compacted form of XRSTOR. |
|
||||
| XSAVEOPT | XSAVEOPT available |
|
||||
| XSAVES | Supports XSAVES/XRSTORS and IA32_XSS |
|
||||
|
||||
# ARM features:
|
||||
|
||||
| Feature Flag | Description |
|
||||
|--------------|------------------------------------------------------------------|
|
||||
| AESARM | AES instructions |
|
||||
| ARMCPUID | Some CPU ID registers readable at user-level |
|
||||
| ASIMD | Advanced SIMD |
|
||||
| ASIMDDP | SIMD Dot Product |
|
||||
| ASIMDHP | Advanced SIMD half-precision floating point |
|
||||
| ASIMDRDM | Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH) |
|
||||
| ATOMICS | Large System Extensions (LSE) |
|
||||
| CRC32 | CRC32/CRC32C instructions |
|
||||
| DCPOP | Data cache clean to Point of Persistence (DC CVAP) |
|
||||
| EVTSTRM | Generic timer |
|
||||
| FCMA | Floatin point complex number addition and multiplication |
|
||||
| FP | Single-precision and double-precision floating point |
|
||||
| FPHP | Half-precision floating point |
|
||||
| GPA | Generic Pointer Authentication |
|
||||
| JSCVT | Javascript-style double->int convert (FJCVTZS) |
|
||||
| LRCPC | Weaker release consistency (LDAPR, etc) |
|
||||
| PMULL | Polynomial Multiply instructions (PMULL/PMULL2) |
|
||||
| SHA1 | SHA-1 instructions (SHA1C, etc) |
|
||||
| SHA2 | SHA-2 instructions (SHA256H, etc) |
|
||||
| SHA3 | SHA-3 instructions (EOR3, RAXI, XAR, BCAX) |
|
||||
| SHA512 | SHA512 instructions |
|
||||
| SM3 | SM3 instructions |
|
||||
| SM4 | SM4 instructions |
|
||||
| SVE | Scalable Vector Extension |
|
||||
|
||||
# license
|
||||
|
||||
This code is published under an MIT license. See LICENSE file for more information.
|
||||
+1427
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+5
@@ -40,3 +40,8 @@ TEXT ·asmRdtscpAsm(SB), 7, $0
|
||||
MOVL CX, ecx+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// func asmDarwinHasAVX512() bool
|
||||
TEXT ·asmDarwinHasAVX512(SB), 7, $0
|
||||
MOVL $0, eax+0(FP)
|
||||
RET
|
||||
vendor/github.com/klauspost/cpuid/cpuid_amd64.s → vendor/github.com/klauspost/cpuid/v2/cpuid_amd64.s
Generated
Vendored
+30
@@ -40,3 +40,33 @@ TEXT ·asmRdtscpAsm(SB), 7, $0
|
||||
MOVL CX, ecx+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// From https://go-review.googlesource.com/c/sys/+/285572/
|
||||
// func asmDarwinHasAVX512() bool
|
||||
TEXT ·asmDarwinHasAVX512(SB), 7, $0-1
|
||||
MOVB $0, ret+0(FP) // default to false
|
||||
|
||||
#ifdef GOOS_darwin // return if not darwin
|
||||
#ifdef GOARCH_amd64 // return if not amd64
|
||||
// These values from:
|
||||
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
|
||||
#define commpage64_base_address 0x00007fffffe00000
|
||||
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
|
||||
#define commpage64_version (commpage64_base_address+0x01E)
|
||||
#define hasAVX512F 0x0000004000000000
|
||||
MOVQ $commpage64_version, BX
|
||||
MOVW (BX), AX
|
||||
CMPW AX, $13 // versions < 13 do not support AVX512
|
||||
JL no_avx512
|
||||
MOVQ $commpage64_cpu_capabilities64, BX
|
||||
MOVQ (BX), AX
|
||||
MOVQ $hasAVX512F, CX
|
||||
ANDQ CX, AX
|
||||
JZ no_avx512
|
||||
MOVB $1, ret+0(FP)
|
||||
|
||||
no_avx512:
|
||||
#endif
|
||||
#endif
|
||||
RET
|
||||
|
||||
vendor/github.com/klauspost/cpuid/cpuid_arm64.s → vendor/github.com/klauspost/cpuid/v2/cpuid_arm64.s
Generated
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build arm64,!gccgo
|
||||
//+build arm64,!gccgo,!noasm,!appengine
|
||||
|
||||
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
|
||||
|
||||
Generated
Vendored
+108
-80
@@ -1,9 +1,12 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build arm64,!gccgo,!noasm,!appengine
|
||||
//go:build arm64 && !gccgo && !noasm && !appengine
|
||||
// +build arm64,!gccgo,!noasm,!appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
import "runtime"
|
||||
|
||||
func getMidr() (midr uint64)
|
||||
func getProcFeatures() (procFeatures uint64)
|
||||
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
|
||||
@@ -15,14 +18,19 @@ func initCPU() {
|
||||
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
}
|
||||
|
||||
func addInfo(c *CPUInfo) {
|
||||
// ARM64 disabled for now.
|
||||
if true {
|
||||
func addInfo(c *CPUInfo, safe bool) {
|
||||
// Seems to be safe to assume on ARM64
|
||||
c.CacheLine = 64
|
||||
detectOS(c)
|
||||
|
||||
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
|
||||
if safe && !c.Supports(ARMCPUID) && runtime.GOOS != "freebsd" {
|
||||
return
|
||||
}
|
||||
// midr := getMidr()
|
||||
midr := getMidr()
|
||||
|
||||
// MIDR_EL1 - Main ID Register
|
||||
// https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/midr_el1
|
||||
// x--------------------------------------------------x
|
||||
// | Name | bits | visible |
|
||||
// |--------------------------------------------------|
|
||||
@@ -37,11 +45,70 @@ func addInfo(c *CPUInfo) {
|
||||
// | Revision | [3-0] | y |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
// fmt.Printf(" implementer: 0x%02x\n", (midr>>24)&0xff)
|
||||
// fmt.Printf(" variant: 0x%01x\n", (midr>>20)&0xf)
|
||||
// fmt.Printf("architecture: 0x%01x\n", (midr>>16)&0xf)
|
||||
// fmt.Printf(" part num: 0x%03x\n", (midr>>4)&0xfff)
|
||||
// fmt.Printf(" revision: 0x%01x\n", (midr>>0)&0xf)
|
||||
switch (midr >> 24) & 0xff {
|
||||
case 0xC0:
|
||||
c.VendorString = "Ampere Computing"
|
||||
c.VendorID = Ampere
|
||||
case 0x41:
|
||||
c.VendorString = "Arm Limited"
|
||||
c.VendorID = ARM
|
||||
case 0x42:
|
||||
c.VendorString = "Broadcom Corporation"
|
||||
c.VendorID = Broadcom
|
||||
case 0x43:
|
||||
c.VendorString = "Cavium Inc"
|
||||
c.VendorID = Cavium
|
||||
case 0x44:
|
||||
c.VendorString = "Digital Equipment Corporation"
|
||||
c.VendorID = DEC
|
||||
case 0x46:
|
||||
c.VendorString = "Fujitsu Ltd"
|
||||
c.VendorID = Fujitsu
|
||||
case 0x49:
|
||||
c.VendorString = "Infineon Technologies AG"
|
||||
c.VendorID = Infineon
|
||||
case 0x4D:
|
||||
c.VendorString = "Motorola or Freescale Semiconductor Inc"
|
||||
c.VendorID = Motorola
|
||||
case 0x4E:
|
||||
c.VendorString = "NVIDIA Corporation"
|
||||
c.VendorID = NVIDIA
|
||||
case 0x50:
|
||||
c.VendorString = "Applied Micro Circuits Corporation"
|
||||
c.VendorID = AMCC
|
||||
case 0x51:
|
||||
c.VendorString = "Qualcomm Inc"
|
||||
c.VendorID = Qualcomm
|
||||
case 0x56:
|
||||
c.VendorString = "Marvell International Ltd"
|
||||
c.VendorID = Marvell
|
||||
case 0x69:
|
||||
c.VendorString = "Intel Corporation"
|
||||
c.VendorID = Intel
|
||||
}
|
||||
|
||||
// Lower 4 bits: Architecture
|
||||
// Architecture Meaning
|
||||
// 0b0001 Armv4.
|
||||
// 0b0010 Armv4T.
|
||||
// 0b0011 Armv5 (obsolete).
|
||||
// 0b0100 Armv5T.
|
||||
// 0b0101 Armv5TE.
|
||||
// 0b0110 Armv5TEJ.
|
||||
// 0b0111 Armv6.
|
||||
// 0b1111 Architectural features are individually identified in the ID_* registers, see 'ID registers'.
|
||||
// Upper 4 bit: Variant
|
||||
// An IMPLEMENTATION DEFINED variant number.
|
||||
// Typically, this field is used to distinguish between different product variants, or major revisions of a product.
|
||||
c.Family = int(midr>>16) & 0xff
|
||||
|
||||
// PartNum, bits [15:4]
|
||||
// An IMPLEMENTATION DEFINED primary part number for the device.
|
||||
// On processors implemented by Arm, if the top four bits of the primary
|
||||
// part number are 0x0 or 0x7, the variant and architecture are encoded differently.
|
||||
// Revision, bits [3:0]
|
||||
// An IMPLEMENTATION DEFINED revision number for the device.
|
||||
c.Model = int(midr) & 0xffff
|
||||
|
||||
procFeatures := getProcFeatures()
|
||||
|
||||
@@ -68,25 +135,18 @@ func addInfo(c *CPUInfo) {
|
||||
// | EL0 | [3-0] | n |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
var f ArmFlags
|
||||
var f flagSet
|
||||
// if procFeatures&(0xf<<48) != 0 {
|
||||
// fmt.Println("DIT")
|
||||
// }
|
||||
if procFeatures&(0xf<<32) != 0 {
|
||||
f |= SVE
|
||||
}
|
||||
f.setIf(procFeatures&(0xf<<32) != 0, SVE)
|
||||
if procFeatures&(0xf<<20) != 15<<20 {
|
||||
f |= ASIMD
|
||||
if procFeatures&(0xf<<20) == 1<<20 {
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
|
||||
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
|
||||
f |= FPHP
|
||||
f |= ASIMDHP
|
||||
}
|
||||
}
|
||||
if procFeatures&(0xf<<16) != 0 {
|
||||
f |= FP
|
||||
f.set(ASIMD)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
|
||||
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
|
||||
f.setIf(procFeatures&(0xf<<20) == 1<<20, FPHP, ASIMDHP)
|
||||
}
|
||||
f.setIf(procFeatures&(0xf<<16) != 0, FP)
|
||||
|
||||
instAttrReg0, instAttrReg1 := getInstAttributes()
|
||||
|
||||
@@ -127,46 +187,22 @@ func addInfo(c *CPUInfo) {
|
||||
// if instAttrReg0&(0xf<<48) != 0 {
|
||||
// fmt.Println("FHM")
|
||||
// }
|
||||
if instAttrReg0&(0xf<<44) != 0 {
|
||||
f |= ASIMDDP
|
||||
}
|
||||
if instAttrReg0&(0xf<<40) != 0 {
|
||||
f |= SM4
|
||||
}
|
||||
if instAttrReg0&(0xf<<36) != 0 {
|
||||
f |= SM3
|
||||
}
|
||||
if instAttrReg0&(0xf<<32) != 0 {
|
||||
f |= SHA3
|
||||
}
|
||||
if instAttrReg0&(0xf<<28) != 0 {
|
||||
f |= ASIMDRDM
|
||||
}
|
||||
if instAttrReg0&(0xf<<20) != 0 {
|
||||
f |= ATOMICS
|
||||
}
|
||||
if instAttrReg0&(0xf<<16) != 0 {
|
||||
f |= CRC32
|
||||
}
|
||||
if instAttrReg0&(0xf<<12) != 0 {
|
||||
f |= SHA2
|
||||
}
|
||||
if instAttrReg0&(0xf<<12) == 2<<12 {
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
|
||||
f |= SHA512
|
||||
}
|
||||
if instAttrReg0&(0xf<<8) != 0 {
|
||||
f |= SHA1
|
||||
}
|
||||
if instAttrReg0&(0xf<<4) != 0 {
|
||||
f |= AES
|
||||
}
|
||||
if instAttrReg0&(0xf<<4) == 2<<4 {
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
|
||||
f |= PMULL
|
||||
}
|
||||
f.setIf(instAttrReg0&(0xf<<44) != 0, ASIMDDP)
|
||||
f.setIf(instAttrReg0&(0xf<<40) != 0, SM4)
|
||||
f.setIf(instAttrReg0&(0xf<<36) != 0, SM3)
|
||||
f.setIf(instAttrReg0&(0xf<<32) != 0, SHA3)
|
||||
f.setIf(instAttrReg0&(0xf<<28) != 0, ASIMDRDM)
|
||||
f.setIf(instAttrReg0&(0xf<<20) != 0, ATOMICS)
|
||||
f.setIf(instAttrReg0&(0xf<<16) != 0, CRC32)
|
||||
f.setIf(instAttrReg0&(0xf<<12) != 0, SHA2)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
|
||||
f.setIf(instAttrReg0&(0xf<<12) == 2<<12, SHA512)
|
||||
f.setIf(instAttrReg0&(0xf<<8) != 0, SHA1)
|
||||
f.setIf(instAttrReg0&(0xf<<4) != 0, AESARM)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
|
||||
f.setIf(instAttrReg0&(0xf<<4) == 2<<4, PMULL)
|
||||
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
|
||||
//
|
||||
@@ -194,26 +230,18 @@ func addInfo(c *CPUInfo) {
|
||||
// if instAttrReg1&(0xf<<28) != 0 {
|
||||
// fmt.Println("GPI")
|
||||
// }
|
||||
if instAttrReg1&(0xf<<28) != 24 {
|
||||
f |= GPA
|
||||
}
|
||||
if instAttrReg1&(0xf<<20) != 0 {
|
||||
f |= LRCPC
|
||||
}
|
||||
if instAttrReg1&(0xf<<16) != 0 {
|
||||
f |= FCMA
|
||||
}
|
||||
if instAttrReg1&(0xf<<12) != 0 {
|
||||
f |= JSCVT
|
||||
}
|
||||
f.setIf(instAttrReg1&(0xf<<28) != 24, GPA)
|
||||
f.setIf(instAttrReg1&(0xf<<20) != 0, LRCPC)
|
||||
f.setIf(instAttrReg1&(0xf<<16) != 0, FCMA)
|
||||
f.setIf(instAttrReg1&(0xf<<12) != 0, JSCVT)
|
||||
// if instAttrReg1&(0xf<<8) != 0 {
|
||||
// fmt.Println("API")
|
||||
// }
|
||||
// if instAttrReg1&(0xf<<4) != 0 {
|
||||
// fmt.Println("APA")
|
||||
// }
|
||||
if instAttrReg1&(0xf<<0) != 0 {
|
||||
f |= DCPOP
|
||||
}
|
||||
c.Arm = f
|
||||
f.setIf(instAttrReg1&(0xf<<0) != 0, DCPOP)
|
||||
|
||||
// Store
|
||||
c.featureSet.or(f)
|
||||
}
|
||||
vendor/github.com/klauspost/cpuid/detect_ref.go → vendor/github.com/klauspost/cpuid/v2/detect_ref.go
Generated
Vendored
+3
-2
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build !amd64,!386,!arm64 gccgo noasm appengine
|
||||
//go:build (!amd64 && !386 && !arm64) || gccgo || noasm || appengine
|
||||
// +build !amd64,!386,!arm64 gccgo noasm appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
@@ -11,4 +12,4 @@ func initCPU() {
|
||||
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
}
|
||||
|
||||
func addInfo(info *CPUInfo) {}
|
||||
func addInfo(info *CPUInfo, safe bool) {}
|
||||
Generated
Vendored
+9
-6
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build 386,!gccgo,!noasm amd64,!gccgo,!noasm,!appengine
|
||||
//go:build (386 && !gccgo && !noasm && !appengine) || (amd64 && !gccgo && !noasm && !appengine)
|
||||
// +build 386,!gccgo,!noasm,!appengine amd64,!gccgo,!noasm,!appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
@@ -8,26 +9,28 @@ func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmXgetbv(index uint32) (eax, edx uint32)
|
||||
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
func asmDarwinHasAVX512() bool
|
||||
|
||||
func initCPU() {
|
||||
cpuid = asmCpuid
|
||||
cpuidex = asmCpuidex
|
||||
xgetbv = asmXgetbv
|
||||
rdtscpAsm = asmRdtscpAsm
|
||||
darwinHasAVX512 = asmDarwinHasAVX512
|
||||
}
|
||||
|
||||
func addInfo(c *CPUInfo) {
|
||||
func addInfo(c *CPUInfo, safe bool) {
|
||||
c.maxFunc = maxFunctionID()
|
||||
c.maxExFunc = maxExtendedFunction()
|
||||
c.BrandName = brandName()
|
||||
c.CacheLine = cacheLine()
|
||||
c.Family, c.Model = familyModel()
|
||||
c.Features = support()
|
||||
c.SGX = hasSGX(c.Features&SGX != 0, c.Features&SGXLC != 0)
|
||||
c.Family, c.Model, c.Stepping = familyModel()
|
||||
c.featureSet = support()
|
||||
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
|
||||
c.ThreadsPerCore = threadsPerCore()
|
||||
c.LogicalCores = logicalCores()
|
||||
c.PhysicalCores = physicalCores()
|
||||
c.VendorID, c.VendorString = vendorID()
|
||||
c.Hz = hertz(c.BrandName)
|
||||
c.cacheSize()
|
||||
c.frequencies()
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
// Code generated by "stringer -type=FeatureID,Vendor"; DO NOT EDIT.
|
||||
|
||||
package cpuid
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ADX-1]
|
||||
_ = x[AESNI-2]
|
||||
_ = x[AMD3DNOW-3]
|
||||
_ = x[AMD3DNOWEXT-4]
|
||||
_ = x[AMXBF16-5]
|
||||
_ = x[AMXFP16-6]
|
||||
_ = x[AMXINT8-7]
|
||||
_ = x[AMXTILE-8]
|
||||
_ = x[AVX-9]
|
||||
_ = x[AVX2-10]
|
||||
_ = x[AVX512BF16-11]
|
||||
_ = x[AVX512BITALG-12]
|
||||
_ = x[AVX512BW-13]
|
||||
_ = x[AVX512CD-14]
|
||||
_ = x[AVX512DQ-15]
|
||||
_ = x[AVX512ER-16]
|
||||
_ = x[AVX512F-17]
|
||||
_ = x[AVX512FP16-18]
|
||||
_ = x[AVX512IFMA-19]
|
||||
_ = x[AVX512PF-20]
|
||||
_ = x[AVX512VBMI-21]
|
||||
_ = x[AVX512VBMI2-22]
|
||||
_ = x[AVX512VL-23]
|
||||
_ = x[AVX512VNNI-24]
|
||||
_ = x[AVX512VP2INTERSECT-25]
|
||||
_ = x[AVX512VPOPCNTDQ-26]
|
||||
_ = x[AVXIFMA-27]
|
||||
_ = x[AVXNECONVERT-28]
|
||||
_ = x[AVXSLOW-29]
|
||||
_ = x[AVXVNNI-30]
|
||||
_ = x[AVXVNNIINT8-31]
|
||||
_ = x[BHI_CTRL-32]
|
||||
_ = x[BMI1-33]
|
||||
_ = x[BMI2-34]
|
||||
_ = x[CETIBT-35]
|
||||
_ = x[CETSS-36]
|
||||
_ = x[CLDEMOTE-37]
|
||||
_ = x[CLMUL-38]
|
||||
_ = x[CLZERO-39]
|
||||
_ = x[CMOV-40]
|
||||
_ = x[CMPCCXADD-41]
|
||||
_ = x[CMPSB_SCADBS_SHORT-42]
|
||||
_ = x[CMPXCHG8-43]
|
||||
_ = x[CPBOOST-44]
|
||||
_ = x[CPPC-45]
|
||||
_ = x[CX16-46]
|
||||
_ = x[EFER_LMSLE_UNS-47]
|
||||
_ = x[ENQCMD-48]
|
||||
_ = x[ERMS-49]
|
||||
_ = x[F16C-50]
|
||||
_ = x[FLUSH_L1D-51]
|
||||
_ = x[FMA3-52]
|
||||
_ = x[FMA4-53]
|
||||
_ = x[FP128-54]
|
||||
_ = x[FP256-55]
|
||||
_ = x[FSRM-56]
|
||||
_ = x[FXSR-57]
|
||||
_ = x[FXSROPT-58]
|
||||
_ = x[GFNI-59]
|
||||
_ = x[HLE-60]
|
||||
_ = x[HRESET-61]
|
||||
_ = x[HTT-62]
|
||||
_ = x[HWA-63]
|
||||
_ = x[HYBRID_CPU-64]
|
||||
_ = x[HYPERVISOR-65]
|
||||
_ = x[IA32_ARCH_CAP-66]
|
||||
_ = x[IA32_CORE_CAP-67]
|
||||
_ = x[IBPB-68]
|
||||
_ = x[IBRS-69]
|
||||
_ = x[IBRS_PREFERRED-70]
|
||||
_ = x[IBRS_PROVIDES_SMP-71]
|
||||
_ = x[IBS-72]
|
||||
_ = x[IBSBRNTRGT-73]
|
||||
_ = x[IBSFETCHSAM-74]
|
||||
_ = x[IBSFFV-75]
|
||||
_ = x[IBSOPCNT-76]
|
||||
_ = x[IBSOPCNTEXT-77]
|
||||
_ = x[IBSOPSAM-78]
|
||||
_ = x[IBSRDWROPCNT-79]
|
||||
_ = x[IBSRIPINVALIDCHK-80]
|
||||
_ = x[IBS_FETCH_CTLX-81]
|
||||
_ = x[IBS_OPDATA4-82]
|
||||
_ = x[IBS_OPFUSE-83]
|
||||
_ = x[IBS_PREVENTHOST-84]
|
||||
_ = x[IBS_ZEN4-85]
|
||||
_ = x[IDPRED_CTRL-86]
|
||||
_ = x[INT_WBINVD-87]
|
||||
_ = x[INVLPGB-88]
|
||||
_ = x[LAHF-89]
|
||||
_ = x[LAM-90]
|
||||
_ = x[LBRVIRT-91]
|
||||
_ = x[LZCNT-92]
|
||||
_ = x[MCAOVERFLOW-93]
|
||||
_ = x[MCDT_NO-94]
|
||||
_ = x[MCOMMIT-95]
|
||||
_ = x[MD_CLEAR-96]
|
||||
_ = x[MMX-97]
|
||||
_ = x[MMXEXT-98]
|
||||
_ = x[MOVBE-99]
|
||||
_ = x[MOVDIR64B-100]
|
||||
_ = x[MOVDIRI-101]
|
||||
_ = x[MOVSB_ZL-102]
|
||||
_ = x[MOVU-103]
|
||||
_ = x[MPX-104]
|
||||
_ = x[MSRIRC-105]
|
||||
_ = x[MSRLIST-106]
|
||||
_ = x[MSR_PAGEFLUSH-107]
|
||||
_ = x[NRIPS-108]
|
||||
_ = x[NX-109]
|
||||
_ = x[OSXSAVE-110]
|
||||
_ = x[PCONFIG-111]
|
||||
_ = x[POPCNT-112]
|
||||
_ = x[PPIN-113]
|
||||
_ = x[PREFETCHI-114]
|
||||
_ = x[PSFD-115]
|
||||
_ = x[RDPRU-116]
|
||||
_ = x[RDRAND-117]
|
||||
_ = x[RDSEED-118]
|
||||
_ = x[RDTSCP-119]
|
||||
_ = x[RRSBA_CTRL-120]
|
||||
_ = x[RTM-121]
|
||||
_ = x[RTM_ALWAYS_ABORT-122]
|
||||
_ = x[SERIALIZE-123]
|
||||
_ = x[SEV-124]
|
||||
_ = x[SEV_64BIT-125]
|
||||
_ = x[SEV_ALTERNATIVE-126]
|
||||
_ = x[SEV_DEBUGSWAP-127]
|
||||
_ = x[SEV_ES-128]
|
||||
_ = x[SEV_RESTRICTED-129]
|
||||
_ = x[SEV_SNP-130]
|
||||
_ = x[SGX-131]
|
||||
_ = x[SGXLC-132]
|
||||
_ = x[SHA-133]
|
||||
_ = x[SME-134]
|
||||
_ = x[SME_COHERENT-135]
|
||||
_ = x[SPEC_CTRL_SSBD-136]
|
||||
_ = x[SRBDS_CTRL-137]
|
||||
_ = x[SSE-138]
|
||||
_ = x[SSE2-139]
|
||||
_ = x[SSE3-140]
|
||||
_ = x[SSE4-141]
|
||||
_ = x[SSE42-142]
|
||||
_ = x[SSE4A-143]
|
||||
_ = x[SSSE3-144]
|
||||
_ = x[STIBP-145]
|
||||
_ = x[STIBP_ALWAYSON-146]
|
||||
_ = x[STOSB_SHORT-147]
|
||||
_ = x[SUCCOR-148]
|
||||
_ = x[SVM-149]
|
||||
_ = x[SVMDA-150]
|
||||
_ = x[SVMFBASID-151]
|
||||
_ = x[SVML-152]
|
||||
_ = x[SVMNP-153]
|
||||
_ = x[SVMPF-154]
|
||||
_ = x[SVMPFT-155]
|
||||
_ = x[SYSCALL-156]
|
||||
_ = x[SYSEE-157]
|
||||
_ = x[TBM-158]
|
||||
_ = x[TDX_GUEST-159]
|
||||
_ = x[TLB_FLUSH_NESTED-160]
|
||||
_ = x[TME-161]
|
||||
_ = x[TOPEXT-162]
|
||||
_ = x[TSCRATEMSR-163]
|
||||
_ = x[TSXLDTRK-164]
|
||||
_ = x[VAES-165]
|
||||
_ = x[VMCBCLEAN-166]
|
||||
_ = x[VMPL-167]
|
||||
_ = x[VMSA_REGPROT-168]
|
||||
_ = x[VMX-169]
|
||||
_ = x[VPCLMULQDQ-170]
|
||||
_ = x[VTE-171]
|
||||
_ = x[WAITPKG-172]
|
||||
_ = x[WBNOINVD-173]
|
||||
_ = x[WRMSRNS-174]
|
||||
_ = x[X87-175]
|
||||
_ = x[XGETBV1-176]
|
||||
_ = x[XOP-177]
|
||||
_ = x[XSAVE-178]
|
||||
_ = x[XSAVEC-179]
|
||||
_ = x[XSAVEOPT-180]
|
||||
_ = x[XSAVES-181]
|
||||
_ = x[AESARM-182]
|
||||
_ = x[ARMCPUID-183]
|
||||
_ = x[ASIMD-184]
|
||||
_ = x[ASIMDDP-185]
|
||||
_ = x[ASIMDHP-186]
|
||||
_ = x[ASIMDRDM-187]
|
||||
_ = x[ATOMICS-188]
|
||||
_ = x[CRC32-189]
|
||||
_ = x[DCPOP-190]
|
||||
_ = x[EVTSTRM-191]
|
||||
_ = x[FCMA-192]
|
||||
_ = x[FP-193]
|
||||
_ = x[FPHP-194]
|
||||
_ = x[GPA-195]
|
||||
_ = x[JSCVT-196]
|
||||
_ = x[LRCPC-197]
|
||||
_ = x[PMULL-198]
|
||||
_ = x[SHA1-199]
|
||||
_ = x[SHA2-200]
|
||||
_ = x[SHA3-201]
|
||||
_ = x[SHA512-202]
|
||||
_ = x[SM3-203]
|
||||
_ = x[SM4-204]
|
||||
_ = x[SVE-205]
|
||||
_ = x[lastID-206]
|
||||
_ = x[firstID-0]
|
||||
}
|
||||
|
||||
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSPEC_CTRL_SSBDSRBDS_CTRLSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
|
||||
|
||||
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 62, 65, 69, 79, 91, 99, 107, 115, 123, 130, 140, 150, 158, 168, 179, 187, 197, 215, 230, 237, 249, 256, 263, 274, 282, 286, 290, 296, 301, 309, 314, 320, 324, 333, 351, 359, 366, 370, 374, 388, 394, 398, 402, 411, 415, 419, 424, 429, 433, 437, 444, 448, 451, 457, 460, 463, 473, 483, 496, 509, 513, 517, 531, 548, 551, 561, 572, 578, 586, 597, 605, 617, 633, 647, 658, 668, 683, 691, 702, 712, 719, 723, 726, 733, 738, 749, 756, 763, 771, 774, 780, 785, 794, 801, 809, 813, 816, 822, 829, 842, 847, 849, 856, 863, 869, 873, 882, 886, 891, 897, 903, 909, 919, 922, 938, 947, 950, 959, 974, 987, 993, 1007, 1014, 1017, 1022, 1025, 1028, 1040, 1054, 1064, 1067, 1071, 1075, 1079, 1084, 1089, 1094, 1099, 1113, 1124, 1130, 1133, 1138, 1147, 1151, 1156, 1161, 1167, 1174, 1179, 1182, 1191, 1207, 1210, 1216, 1226, 1234, 1238, 1247, 1251, 1263, 1266, 1276, 1279, 1286, 1294, 1301, 1304, 1311, 1314, 1319, 1325, 1333, 1339, 1345, 1353, 1358, 1365, 1372, 1380, 1387, 1392, 1397, 1404, 1408, 1410, 1414, 1417, 1422, 1427, 1432, 1436, 1440, 1444, 1450, 1453, 1456, 1459, 1465}
|
||||
|
||||
func (i FeatureID) String() string {
|
||||
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
|
||||
return "FeatureID(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FeatureID_name[_FeatureID_index[i]:_FeatureID_index[i+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[VendorUnknown-0]
|
||||
_ = x[Intel-1]
|
||||
_ = x[AMD-2]
|
||||
_ = x[VIA-3]
|
||||
_ = x[Transmeta-4]
|
||||
_ = x[NSC-5]
|
||||
_ = x[KVM-6]
|
||||
_ = x[MSVM-7]
|
||||
_ = x[VMware-8]
|
||||
_ = x[XenHVM-9]
|
||||
_ = x[Bhyve-10]
|
||||
_ = x[Hygon-11]
|
||||
_ = x[SiS-12]
|
||||
_ = x[RDC-13]
|
||||
_ = x[Ampere-14]
|
||||
_ = x[ARM-15]
|
||||
_ = x[Broadcom-16]
|
||||
_ = x[Cavium-17]
|
||||
_ = x[DEC-18]
|
||||
_ = x[Fujitsu-19]
|
||||
_ = x[Infineon-20]
|
||||
_ = x[Motorola-21]
|
||||
_ = x[NVIDIA-22]
|
||||
_ = x[AMCC-23]
|
||||
_ = x[Qualcomm-24]
|
||||
_ = x[Marvell-25]
|
||||
_ = x[lastVendor-26]
|
||||
}
|
||||
|
||||
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvelllastVendor"
|
||||
|
||||
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 155}
|
||||
|
||||
func (i Vendor) String() string {
|
||||
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {
|
||||
return "Vendor(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Vendor_name[_Vendor_index[i]:_Vendor_index[i+1]]
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
if runtime.GOOS != "ios" {
|
||||
tryToFillCPUInfoFomSysctl(c)
|
||||
}
|
||||
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
|
||||
// to detect their supported state dynamically. Assume the CPU features that
|
||||
// Apple Silicon M1 supports to be available as a minimal set of features
|
||||
// to all Go programs running on darwin/arm64.
|
||||
// TODO: Add more if we know them.
|
||||
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func sysctlGetBool(name string) bool {
|
||||
value, err := unix.SysctlUint32(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return value != 0
|
||||
}
|
||||
|
||||
func sysctlGetString(name string) string {
|
||||
value, err := unix.Sysctl(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func sysctlGetInt(unknown int, names ...string) int {
|
||||
for _, name := range names {
|
||||
value, err := unix.SysctlUint32(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if value != 0 {
|
||||
return int(value)
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func sysctlGetInt64(unknown int, names ...string) int {
|
||||
for _, name := range names {
|
||||
value64, err := unix.SysctlUint64(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if int(value64) != unknown {
|
||||
return int(value64)
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func setFeature(c *CPUInfo, name string, feature FeatureID) {
|
||||
c.featureSet.setIf(sysctlGetBool(name), feature)
|
||||
}
|
||||
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
|
||||
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
|
||||
|
||||
if len(c.BrandName) != 0 {
|
||||
c.VendorString = strings.Fields(c.BrandName)[0]
|
||||
}
|
||||
|
||||
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
|
||||
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
|
||||
sysctlGetInt(1, "hw.physicalcpu")
|
||||
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
|
||||
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
|
||||
c.Model = sysctlGetInt(0, "machdep.cpu.model")
|
||||
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
|
||||
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
|
||||
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
|
||||
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
|
||||
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
|
||||
|
||||
// from https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile
|
||||
setFeature(c, "hw.optional.arm.FEAT_AES", AESARM)
|
||||
setFeature(c, "hw.optional.AdvSIMD", ASIMD)
|
||||
setFeature(c, "hw.optional.arm.FEAT_DotProd", ASIMDDP)
|
||||
setFeature(c, "hw.optional.arm.FEAT_RDM", ASIMDRDM)
|
||||
setFeature(c, "hw.optional.FEAT_CRC32", CRC32)
|
||||
setFeature(c, "hw.optional.arm.FEAT_DPB", DCPOP)
|
||||
// setFeature(c, "", EVTSTRM)
|
||||
setFeature(c, "hw.optional.arm.FEAT_FCMA", FCMA)
|
||||
setFeature(c, "hw.optional.arm.FEAT_FP", FP)
|
||||
setFeature(c, "hw.optional.arm.FEAT_FP16", FPHP)
|
||||
setFeature(c, "hw.optional.arm.FEAT_PAuth", GPA)
|
||||
setFeature(c, "hw.optional.arm.FEAT_JSCVT", JSCVT)
|
||||
setFeature(c, "hw.optional.arm.FEAT_LRCPC", LRCPC)
|
||||
setFeature(c, "hw.optional.arm.FEAT_PMULL", PMULL)
|
||||
setFeature(c, "hw.optional.arm.FEAT_SHA1", SHA1)
|
||||
setFeature(c, "hw.optional.arm.FEAT_SHA256", SHA2)
|
||||
setFeature(c, "hw.optional.arm.FEAT_SHA3", SHA3)
|
||||
setFeature(c, "hw.optional.arm.FEAT_SHA512", SHA512)
|
||||
// setFeature(c, "", SM3)
|
||||
// setFeature(c, "", SM4)
|
||||
setFeature(c, "hw.optional.arm.FEAT_SVE", SVE)
|
||||
|
||||
// from empirical observation
|
||||
setFeature(c, "hw.optional.AdvSIMD_HPFPCvt", ASIMDHP)
|
||||
setFeature(c, "hw.optional.armv8_1_atomics", ATOMICS)
|
||||
setFeature(c, "hw.optional.floatingpoint", FP)
|
||||
setFeature(c, "hw.optional.armv8_2_sha3", SHA3)
|
||||
setFeature(c, "hw.optional.armv8_2_sha512", SHA512)
|
||||
setFeature(c, "hw.optional.armv8_3_compnum", FCMA)
|
||||
setFeature(c, "hw.optional.armv8_crc32", CRC32)
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
// Copyright 2018 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 located
|
||||
// here https://github.com/golang/sys/blob/master/LICENSE
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// HWCAP bits.
|
||||
const (
|
||||
hwcap_FP = 1 << 0
|
||||
hwcap_ASIMD = 1 << 1
|
||||
hwcap_EVTSTRM = 1 << 2
|
||||
hwcap_AES = 1 << 3
|
||||
hwcap_PMULL = 1 << 4
|
||||
hwcap_SHA1 = 1 << 5
|
||||
hwcap_SHA2 = 1 << 6
|
||||
hwcap_CRC32 = 1 << 7
|
||||
hwcap_ATOMICS = 1 << 8
|
||||
hwcap_FPHP = 1 << 9
|
||||
hwcap_ASIMDHP = 1 << 10
|
||||
hwcap_CPUID = 1 << 11
|
||||
hwcap_ASIMDRDM = 1 << 12
|
||||
hwcap_JSCVT = 1 << 13
|
||||
hwcap_FCMA = 1 << 14
|
||||
hwcap_LRCPC = 1 << 15
|
||||
hwcap_DCPOP = 1 << 16
|
||||
hwcap_SHA3 = 1 << 17
|
||||
hwcap_SM3 = 1 << 18
|
||||
hwcap_SM4 = 1 << 19
|
||||
hwcap_ASIMDDP = 1 << 20
|
||||
hwcap_SHA512 = 1 << 21
|
||||
hwcap_SVE = 1 << 22
|
||||
hwcap_ASIMDFHM = 1 << 23
|
||||
)
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
// For now assuming no hyperthreading is reasonable.
|
||||
c.LogicalCores = runtime.NumCPU()
|
||||
c.PhysicalCores = c.LogicalCores
|
||||
c.ThreadsPerCore = 1
|
||||
if hwcap == 0 {
|
||||
// We did not get values from the runtime.
|
||||
// Try reading /proc/self/auxv
|
||||
|
||||
// From https://github.com/golang/sys
|
||||
const (
|
||||
_AT_HWCAP = 16
|
||||
_AT_HWCAP2 = 26
|
||||
|
||||
uintSize = int(32 << (^uint(0) >> 63))
|
||||
)
|
||||
|
||||
buf, err := ioutil.ReadFile("/proc/self/auxv")
|
||||
if err != nil {
|
||||
// e.g. on android /proc/self/auxv is not accessible, so silently
|
||||
// ignore the error and leave Initialized = false. On some
|
||||
// architectures (e.g. arm64) doinit() implements a fallback
|
||||
// readout and will set Initialized = true again.
|
||||
return false
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
for len(buf) >= 2*(uintSize/8) {
|
||||
var tag, val uint
|
||||
switch uintSize {
|
||||
case 32:
|
||||
tag = uint(bo.Uint32(buf[0:]))
|
||||
val = uint(bo.Uint32(buf[4:]))
|
||||
buf = buf[8:]
|
||||
case 64:
|
||||
tag = uint(bo.Uint64(buf[0:]))
|
||||
val = uint(bo.Uint64(buf[8:]))
|
||||
buf = buf[16:]
|
||||
}
|
||||
switch tag {
|
||||
case _AT_HWCAP:
|
||||
hwcap = val
|
||||
case _AT_HWCAP2:
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
if hwcap == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// HWCap was populated by the runtime from the auxiliary vector.
|
||||
// Use HWCap information since reading aarch64 system registers
|
||||
// is not supported in user space on older linux kernels.
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
|
||||
|
||||
// The Samsung S9+ kernel reports support for atomics, but not all cores
|
||||
// actually support them, resulting in SIGILL. See issue #28431.
|
||||
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isSet(hwc uint, value uint) bool {
|
||||
return hwc&value != 0
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build arm64 && !linux && !darwin
|
||||
// +build arm64,!linux,!darwin
|
||||
|
||||
package cpuid
|
||||
|
||||
import "runtime"
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
c.PhysicalCores = runtime.NumCPU()
|
||||
// For now assuming 1 thread per core...
|
||||
c.ThreadsPerCore = 1
|
||||
c.LogicalCores = c.PhysicalCores
|
||||
return false
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build nounsafe
|
||||
// +build nounsafe
|
||||
|
||||
package cpuid
|
||||
|
||||
var hwcap uint
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build !nounsafe
|
||||
// +build !nounsafe
|
||||
|
||||
package cpuid
|
||||
|
||||
import _ "unsafe" // needed for go:linkname
|
||||
|
||||
//go:linkname hwcap internal/cpu.HWCap
|
||||
var hwcap uint
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
go tool dist list | while IFS=/ read os arch; do
|
||||
echo "Checking $os/$arch..."
|
||||
echo " normal"
|
||||
GOARCH=$arch GOOS=$os go build -o /dev/null .
|
||||
echo " noasm"
|
||||
GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null .
|
||||
echo " appengine"
|
||||
GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null .
|
||||
echo " noasm,appengine"
|
||||
GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null .
|
||||
done
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- master
|
||||
|
||||
install:
|
||||
- go get ./...
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test -cpu=1,2 .
|
||||
- go test -tags=noasm -cpu=1,2 .
|
||||
- go build examples/simple-decoder.go
|
||||
- go build examples/simple-encoder.go
|
||||
- go build examples/stream-decoder.go
|
||||
- go build examples/stream-encoder.go
|
||||
|
||||
stages:
|
||||
- gofmt
|
||||
- test
|
||||
- deploy
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- go: 'master'
|
||||
- arch: s390x
|
||||
fast_finish: true
|
||||
include:
|
||||
- stage: gofmt
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- diff <(gofmt -d .) <(printf "")
|
||||
- diff <(gofmt -d ./examples) <(printf "")
|
||||
- go install github.com/klauspost/asmfmt/cmd/asmfmt
|
||||
- diff <(asmfmt -d .) <(printf "")
|
||||
- stage: race
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- go test -cpu=1 -short -race .
|
||||
- go test -cpu=2 -short -race .
|
||||
- go test -tags=noasm -cpu=1 -short -race .
|
||||
- go test -tags=noasm -cpu=4 -short -race .
|
||||
- go test -no-avx512 -short -race .
|
||||
- go test -no-avx512 -no-avx2 -short -race .
|
||||
- go test -no-avx512 -no-avx2 -no-ssse3 -short -race .
|
||||
- stage: amd64-noasm
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- go test -no-avx512
|
||||
- go test -no-avx512 -no-avx2
|
||||
- go test -no-avx512 -no-avx2 -no-ssse3
|
||||
- stage: i386
|
||||
go: 1.14.x
|
||||
os: linux
|
||||
arch: amd64
|
||||
script:
|
||||
- GOOS=linux GOARCH=386 go test -short .
|
||||
+206
-50
@@ -1,10 +1,5 @@
|
||||
# Reed-Solomon
|
||||
[![GoDoc][1]][2] [![Build Status][3]][4]
|
||||
|
||||
[1]: https://godoc.org/github.com/klauspost/reedsolomon?status.svg
|
||||
[2]: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
|
||||
[3]: https://travis-ci.org/klauspost/reedsolomon.svg?branch=master
|
||||
[4]: https://travis-ci.org/klauspost/reedsolomon
|
||||
[](https://pkg.go.dev/github.com/klauspost/reedsolomon) [](https://github.com/klauspost/reedsolomon/actions/workflows/go.yml)
|
||||
|
||||
Reed-Solomon Erasure Coding in Go, with speeds exceeding 1GB/s/cpu core implemented in pure Go.
|
||||
|
||||
@@ -13,9 +8,12 @@ This is a Go port of the [JavaReedSolomon](https://github.com/Backblaze/JavaReed
|
||||
|
||||
For an introduction on erasure coding, see the post on the [Backblaze blog](https://www.backblaze.com/blog/reed-solomon/).
|
||||
|
||||
For encoding high shard counts (>256) a Leopard implementation is used.
|
||||
For most platforms this performs close to the original Leopard implementation in terms of speed.
|
||||
|
||||
Package home: https://github.com/klauspost/reedsolomon
|
||||
|
||||
Godoc: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
|
||||
Godoc: https://pkg.go.dev/github.com/klauspost/reedsolomon
|
||||
|
||||
# Installation
|
||||
To get the package use the standard:
|
||||
@@ -23,8 +21,30 @@ To get the package use the standard:
|
||||
go get -u github.com/klauspost/reedsolomon
|
||||
```
|
||||
|
||||
Using Go modules is recommended.
|
||||
|
||||
# Changes
|
||||
|
||||
## 2022
|
||||
|
||||
* [GFNI](https://github.com/klauspost/reedsolomon/pull/224) support for amd64, for up to 3x faster processing.
|
||||
* [Leopard GF8](https://github.com/klauspost/reedsolomon#leopard-gf8) mode added, for faster processing of medium shard counts.
|
||||
* [Leopard GF16](https://github.com/klauspost/reedsolomon#leopard-compatible-gf16) mode added, for up to 65536 shards.
|
||||
* [WithJerasureMatrix](https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc#WithJerasureMatrix) allows constructing a [Jerasure](https://github.com/tsuraan/Jerasure) compatible matrix.
|
||||
|
||||
## 2021
|
||||
|
||||
* Use `GOAMD64=v4` to enable faster AVX2.
|
||||
* Add progressive shard encoding.
|
||||
* Wider AVX2 loops
|
||||
* Limit concurrency on AVX2, since we are likely memory bound.
|
||||
* Allow 0 parity shards.
|
||||
* Allow disabling inversion cache.
|
||||
* Faster AVX2 encoding.
|
||||
|
||||
<details>
|
||||
<summary>See older changes</summary>
|
||||
|
||||
## May 2020
|
||||
|
||||
* ARM64 optimizations, up to 2.5x faster.
|
||||
@@ -87,6 +107,8 @@ The [`StreamEncoder`](https://godoc.org/github.com/klauspost/reedsolomon#StreamE
|
||||
handles this without modifying the interface.
|
||||
This is a good lesson on why returning interfaces is not a good design.
|
||||
|
||||
</details>
|
||||
|
||||
# Usage
|
||||
|
||||
This section assumes you know the basics of Reed-Solomon encoding.
|
||||
@@ -96,23 +118,19 @@ This package performs the calculation of the parity sets. The usage is therefore
|
||||
|
||||
First of all, you need to choose your distribution of data and parity shards.
|
||||
A 'good' distribution is very subjective, and will depend a lot on your usage scenario.
|
||||
A good starting point is above 5 and below 257 data shards (the maximum supported number),
|
||||
and the number of parity shards to be 2 or above, and below the number of data shards.
|
||||
|
||||
To create an encoder with 10 data shards (where your data goes) and 3 parity shards (calculated):
|
||||
```Go
|
||||
enc, err := reedsolomon.New(10, 3)
|
||||
```
|
||||
This encoder will work for all parity sets with this distribution of data and parity shards.
|
||||
The error will only be set if you specify 0 or negative values in any of the parameters,
|
||||
or if you specify more than 256 data shards.
|
||||
|
||||
If you will primarily be using it with one shard size it is recommended to use
|
||||
[`WithAutoGoroutines(shardSize)`](https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc#WithAutoGoroutines)
|
||||
as an additional parameter. This will attempt to calculate the optimal number of goroutines to use for the best speed.
|
||||
It is not required that all shards are this size.
|
||||
|
||||
The you send and receive data is a simple slice of byte slices; `[][]byte`.
|
||||
Then you send and receive data that is a simple slice of byte slices; `[][]byte`.
|
||||
In the example above, the top slice must have a length of 13.
|
||||
|
||||
```Go
|
||||
@@ -128,8 +146,10 @@ but you could for instance also use [mmap](https://github.com/edsrzf/mmap-go) to
|
||||
data[i] := make([]byte, 50000)
|
||||
}
|
||||
|
||||
// The above allocations can also be done by the encoder:
|
||||
// data := enc.(reedsolomon.Extended).AllocAligned(50000)
|
||||
|
||||
// Fill some data into the data shards
|
||||
// Fill some data into the data shards
|
||||
for i, in := range data[:10] {
|
||||
for j:= range in {
|
||||
in[j] = byte((i+j)&0xff)
|
||||
@@ -178,6 +198,17 @@ If you are only interested in the data shards (for reading purposes) you can cal
|
||||
err := enc.ReconstructData(data)
|
||||
```
|
||||
|
||||
If you don't need all data shards you can use `ReconstructSome()`:
|
||||
|
||||
```Go
|
||||
// Delete two data shards
|
||||
data[3] = nil
|
||||
data[7] = nil
|
||||
|
||||
// Reconstruct just the shard 3
|
||||
err := enc.ReconstructSome(data, []bool{false, false, false, true, false, false, false, false})
|
||||
```
|
||||
|
||||
So to sum up reconstruction:
|
||||
* The number of data/parity shards must match the numbers used for encoding.
|
||||
* The order of shards must be the same as used when encoding.
|
||||
@@ -209,6 +240,72 @@ To join a data set, use the `Join()` function, which will join the shards and wr
|
||||
err = enc.Join(io.Discard, data, len(bigfile))
|
||||
```
|
||||
|
||||
## Aligned Allocations
|
||||
|
||||
For AMD64 aligned inputs can make a big speed difference.
|
||||
|
||||
This is an example of the speed difference when inputs are unaligned/aligned:
|
||||
|
||||
```
|
||||
BenchmarkEncode100x20x10000-32 7058 172648 ns/op 6950.57 MB/s
|
||||
BenchmarkEncode100x20x10000-32 8406 137911 ns/op 8701.24 MB/s
|
||||
```
|
||||
|
||||
This is mostly the case when dealing with odd-sized shards.
|
||||
|
||||
To facilitate this the package provides an `AllocAligned(shards, each int) [][]byte`.
|
||||
This will allocate a number of shards, each with the size `each`.
|
||||
Each shard will then be aligned to a 64 byte boundary.
|
||||
|
||||
Each encoder also has a `AllocAligned(each int) [][]byte` as an extended interface which will return the same,
|
||||
but with the shard count configured in the encoder.
|
||||
|
||||
It is not possible to re-aligned already allocated slices, for example when using `Split`.
|
||||
When it is not possible to write to aligned shards, you should not copy to them.
|
||||
|
||||
# Progressive encoding
|
||||
|
||||
It is possible to encode individual shards using EncodeIdx:
|
||||
|
||||
```Go
|
||||
// EncodeIdx will add parity for a single data shard.
|
||||
// Parity shards should start out as 0. The caller must zero them.
|
||||
// Data shards must be delivered exactly once. There is no check for this.
|
||||
// The parity shards will always be updated and the data shards will remain the same.
|
||||
EncodeIdx(dataShard []byte, idx int, parity [][]byte) error
|
||||
```
|
||||
|
||||
This allows progressively encoding the parity by sending individual data shards.
|
||||
There is no requirement on shards being delivered in order,
|
||||
but when sent in order it allows encoding shards one at the time,
|
||||
effectively allowing the operation to be streaming.
|
||||
|
||||
The result will be the same as encoding all shards at once.
|
||||
There is a minor speed penalty using this method, so send
|
||||
shards at once if they are available.
|
||||
|
||||
## Example
|
||||
|
||||
```Go
|
||||
func test() {
|
||||
// Create an encoder with 7 data and 3 parity slices.
|
||||
enc, _ := reedsolomon.New(7, 3)
|
||||
|
||||
// This will be our output parity.
|
||||
parity := make([][]byte, 3)
|
||||
for i := range parity {
|
||||
parity[i] = make([]byte, 10000)
|
||||
}
|
||||
|
||||
for i := 0; i < 7; i++ {
|
||||
// Send data shards one at the time.
|
||||
_ = enc.EncodeIdx(make([]byte, 10000), i, parity)
|
||||
}
|
||||
|
||||
// parity now contains parity, as if all data was sent in one call.
|
||||
}
|
||||
```
|
||||
|
||||
# Streaming/Merging
|
||||
|
||||
It might seem like a limitation that all data should be in memory,
|
||||
@@ -281,6 +378,8 @@ There is no buffering or timeouts/retry specified. If you want to add that, you
|
||||
For complete examples of a streaming encoder and decoder see the
|
||||
[examples folder](https://github.com/klauspost/reedsolomon/tree/master/examples).
|
||||
|
||||
GF16 (more than 256 shards) is not supported by the streaming interface.
|
||||
|
||||
# Advanced Options
|
||||
|
||||
You can modify internal options which affects how jobs are split between and processed by goroutines.
|
||||
@@ -294,35 +393,106 @@ Example of how to supply options:
|
||||
enc, err := reedsolomon.New(10, 3, WithMaxGoroutines(25))
|
||||
```
|
||||
|
||||
# Leopard Compatible GF16
|
||||
|
||||
When you encode more than 256 shards the library will switch to a [Leopard-RS](https://github.com/catid/leopard) implementation.
|
||||
|
||||
This allows encoding up to 65536 shards (data+parity) with the following limitations, similar to leopard:
|
||||
|
||||
* The original and recovery data must not exceed 65536 pieces.
|
||||
* The shard size *must* each be a multiple of 64 bytes.
|
||||
* Each buffer should have the same number of bytes.
|
||||
* Even the last shard must be rounded up to the block size.
|
||||
|
||||
| | Regular | Leopard |
|
||||
|-----------------|---------|---------|
|
||||
| Encode | ✓ | ✓ |
|
||||
| EncodeIdx | ✓ | - |
|
||||
| Verify | ✓ | ✓ |
|
||||
| Reconstruct | ✓ | ✓ |
|
||||
| ReconstructData | ✓ | ✓ |
|
||||
| ReconstructSome | ✓ | ✓ (+) |
|
||||
| Update | ✓ | - |
|
||||
| Split | ✓ | ✓ |
|
||||
| Join | ✓ | ✓ |
|
||||
|
||||
* (+) Same as calling `ReconstructData`.
|
||||
|
||||
The Split/Join functions will help to split an input to the proper sizes.
|
||||
|
||||
Speed can be expected to be `O(N*log(N))`, compared to the `O(N*N)`.
|
||||
Reconstruction matrix calculation is more time-consuming,
|
||||
so be sure to include that as part of any benchmark you run.
|
||||
|
||||
For now SSSE3, AVX2 and AVX512 assembly are available on AMD64 platforms.
|
||||
|
||||
Leopard mode currently always runs as a single goroutine, since multiple
|
||||
goroutines doesn't provide any worthwhile speedup.
|
||||
|
||||
## Leopard GF8
|
||||
|
||||
It is possible to replace the default reed-solomon encoder with a leopard compatible one.
|
||||
This will typically be faster when dealing with more than 20-30 shards.
|
||||
Note that the limitations listed above also applies to this mode.
|
||||
See table below for speed with different number of shards.
|
||||
|
||||
To enable Leopard GF8 mode use `WithLeopardGF(true)`.
|
||||
|
||||
Benchmark Encoding and Reconstructing *1KB* shards with variable number of shards.
|
||||
All implementation use inversion cache when available.
|
||||
Speed is total shard size for each operation. Data shard throughput is speed/2.
|
||||
AVX2 is used.
|
||||
|
||||
| Encoder | Shards | Encode | Recover All | Recover One |
|
||||
|--------------|-------------|----------------|--------------|----------------|
|
||||
| Cauchy | 4+4 | 23076.83 MB/s | 5444.02 MB/s | 10834.67 MB/s |
|
||||
| Cauchy | 8+8 | 15206.87 MB/s | 4223.42 MB/s | 16181.62 MB/s |
|
||||
| Cauchy | 16+16 | 7427.47 MB/s | 3305.84 MB/s | 22480.41 MB/s |
|
||||
| Cauchy | 32+32 | 3785.64 MB/s | 2300.07 MB/s | 26181.31 MB/s |
|
||||
| Cauchy | 64+64 | 1911.93 MB/s | 1368.51 MB/s | 27992.93 MB/s |
|
||||
| Cauchy | 128+128 | 963.83 MB/s | 1327.56 MB/s | 32866.86 MB/s |
|
||||
| Leopard GF8 | 4+4 | 17061.28 MB/s | 3099.06 MB/s | 4096.78 MB/s |
|
||||
| Leopard GF8 | 8+8 | 10546.67 MB/s | 2925.92 MB/s | 3964.00 MB/s |
|
||||
| Leopard GF8 | 16+16 | 10961.37 MB/s | 2328.40 MB/s | 3110.22 MB/s |
|
||||
| Leopard GF8 | 32+32 | 7111.47 MB/s | 2374.61 MB/s | 3220.75 MB/s |
|
||||
| Leopard GF8 | 64+64 | 7468.57 MB/s | 2055.41 MB/s | 3061.81 MB/s |
|
||||
| Leopard GF8 | 128+128 | 5479.99 MB/s | 1953.21 MB/s | 2815.15 MB/s |
|
||||
| Leopard GF16 | 256+256 | 6158.66 MB/s | 454.14 MB/s | 506.70 MB/s |
|
||||
| Leopard GF16 | 512+512 | 4418.58 MB/s | 685.75 MB/s | 801.63 MB/s |
|
||||
| Leopard GF16 | 1024+1024 | 4778.05 MB/s | 814.51 MB/s | 1080.19 MB/s |
|
||||
| Leopard GF16 | 2048+2048 | 3417.05 MB/s | 911.64 MB/s | 1179.48 MB/s |
|
||||
| Leopard GF16 | 4096+4096 | 3209.41 MB/s | 729.13 MB/s | 1135.06 MB/s |
|
||||
| Leopard GF16 | 8192+8192 | 2034.11 MB/s | 604.52 MB/s | 842.13 MB/s |
|
||||
| Leopard GF16 | 16384+16384 | 1525.88 MB/s | 486.74 MB/s | 750.01 MB/s |
|
||||
| Leopard GF16 | 32768+32768 | 1138.67 MB/s | 482.81 MB/s | 712.73 MB/s |
|
||||
|
||||
"Traditional" encoding is faster until somewhere between 16 and 32 shards.
|
||||
Leopard provides fast encoding in all cases, but shows a significant overhead for reconstruction.
|
||||
|
||||
Calculating the reconstruction matrix takes a significant amount of computation.
|
||||
With bigger shards that will be smaller. Arguably, fewer shards typically also means bigger shards.
|
||||
Due to the high shard count caching reconstruction matrices generally isn't feasible for Leopard.
|
||||
|
||||
# Performance
|
||||
|
||||
Performance depends mainly on the number of parity shards.
|
||||
In rough terms, doubling the number of parity shards will double the encoding time.
|
||||
|
||||
Here are the throughput numbers with some different selections of data and parity shards.
|
||||
For reference each shard is 1MB random data, and 2 CPU cores are used for encoding.
|
||||
For reference each shard is 1MB random data, and 16 CPU cores are used for encoding.
|
||||
|
||||
| Data | Parity | Parity | MB/s | SSSE3 MB/s | SSSE3 Speed | Rel. Speed |
|
||||
|------|--------|--------|--------|-------------|-------------|------------|
|
||||
| 5 | 2 | 40% | 576,11 | 2599,2 | 451% | 100,00% |
|
||||
| 10 | 2 | 20% | 587,73 | 3100,28 | 528% | 102,02% |
|
||||
| 10 | 4 | 40% | 298,38 | 2470,97 | 828% | 51,79% |
|
||||
| 50 | 20 | 40% | 59,81 | 713,28 | 1193% | 10,38% |
|
||||
| Data | Parity | Go MB/s | SSSE3 MB/s | AVX2 MB/s |
|
||||
|------|--------|---------|------------|-----------|
|
||||
| 5 | 2 | 20,772 | 66,355 | 108,755 |
|
||||
| 8 | 8 | 6,815 | 38,338 | 70,516 |
|
||||
| 10 | 4 | 9,245 | 48,237 | 93,875 |
|
||||
| 50 | 20 | 2,063 | 12,130 | 22,828 |
|
||||
|
||||
The throughput numbers here is the size of the encoded data and parity shards.
|
||||
|
||||
If `runtime.GOMAXPROCS()` is set to a value higher than 1,
|
||||
the encoder will use multiple goroutines to perform the calculations in `Verify`, `Encode` and `Reconstruct`.
|
||||
|
||||
Example of performance scaling on AMD Ryzen 3950X - 16 physical cores, 32 logical cores, AVX 2.
|
||||
The example uses 10 blocks with 1MB data each and 4 parity blocks.
|
||||
|
||||
| Threads | Speed |
|
||||
|---------|------------|
|
||||
| 1 | 9979 MB/s |
|
||||
| 2 | 18870 MB/s |
|
||||
| 4 | 33697 MB/s |
|
||||
| 8 | 51531 MB/s |
|
||||
| 16 | 59204 MB/s |
|
||||
|
||||
|
||||
Benchmarking `Reconstruct()` followed by a `Verify()` (=`all`) versus just calling `ReconstructData()` (=`data`) gives the following result:
|
||||
```
|
||||
@@ -336,22 +506,10 @@ BenchmarkReconstruct50x20x1M-8 1364.35 4189.79 3.07x
|
||||
BenchmarkReconstruct10x4x16M-8 1484.35 5779.53 3.89x
|
||||
```
|
||||
|
||||
# Performance on AVX512
|
||||
The package will use [GFNI](https://en.wikipedia.org/wiki/AVX-512#GFNI) instructions combined with AVX512 when these are available.
|
||||
This further improves speed by up to 3x over AVX2 code paths.
|
||||
|
||||
The performance on AVX512 has been accelerated for Intel CPUs.
|
||||
This gives speedups on a per-core basis typically up to 2x compared to
|
||||
AVX2 as can be seen in the following table:
|
||||
|
||||
```
|
||||
[...]
|
||||
```
|
||||
|
||||
This speedup has been achieved by computing multiple parity blocks in parallel as opposed to one after the other.
|
||||
In doing so it is possible to minimize the memory bandwidth required for loading all data shards.
|
||||
At the same time the calculations are performed in the 512-bit wide ZMM registers and the surplus of ZMM
|
||||
registers (32 in total) is used to keep more data around (most notably the matrix coefficients).
|
||||
|
||||
# Performance on ARM64 NEON
|
||||
## ARM64 NEON
|
||||
|
||||
By exploiting NEON instructions the performance for ARM has been accelerated.
|
||||
Below are the performance numbers for a single core on an EC2 m6g.16xlarge (Graviton2) instance (Amazon Linux 2):
|
||||
@@ -366,7 +524,7 @@ BenchmarkGaloisXor1M-64 10000 100322 ns/op 10452.13 MB/s
|
||||
# Performance on ppc64le
|
||||
|
||||
The performance for ppc64le has been accelerated.
|
||||
This gives roughly a 10x performance improvement on this architecture as can been seen below:
|
||||
This gives roughly a 10x performance improvement on this architecture as can be seen below:
|
||||
|
||||
```
|
||||
benchmark old MB/s new MB/s speedup
|
||||
@@ -376,9 +534,6 @@ BenchmarkGaloisXor128K-160 862.02 7905.00 9.17x
|
||||
BenchmarkGaloisXor1M-160 784.60 6296.65 8.03x
|
||||
```
|
||||
|
||||
# asm2plan9s
|
||||
|
||||
[asm2plan9s](https://github.com/fwessels/asm2plan9s) is used for assembling the AVX2 instructions into their BYTE/WORD/LONG equivalents.
|
||||
|
||||
# Links
|
||||
* [Backblaze Open Sources Reed-Solomon Erasure Coding Source Code](https://www.backblaze.com/blog/reed-solomon/).
|
||||
@@ -389,6 +544,7 @@ BenchmarkGaloisXor1M-160 784.60 6296.65 8.03x
|
||||
* [reed-solomon-erasure](https://github.com/darrenldl/reed-solomon-erasure). Compatible Rust implementation.
|
||||
* [go-erasure](https://github.com/somethingnew2-0/go-erasure). A similar library using cgo, slower in my tests.
|
||||
* [Screaming Fast Galois Field Arithmetic](http://www.snia.org/sites/default/files2/SDC2013/presentations/NewThinking/EthanMiller_Screaming_Fast_Galois_Field%20Arithmetic_SIMD%20Instructions.pdf). Basis for SSE3 optimizations.
|
||||
* [Leopard-RS](https://github.com/catid/leopard) C library used as basis for GF16 implementation.
|
||||
|
||||
# License
|
||||
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
os: Visual Studio 2015
|
||||
|
||||
platform: x64
|
||||
|
||||
clone_folder: c:\gopath\src\github.com\klauspost\reedsolomon
|
||||
|
||||
# environment variables
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
|
||||
install:
|
||||
- echo %PATH%
|
||||
- echo %GOPATH%
|
||||
- go version
|
||||
- go env
|
||||
- go get -d ./...
|
||||
|
||||
build_script:
|
||||
- go test -v -cpu=2 ./...
|
||||
- go test -cpu=1,2,4 -short -race ./...
|
||||
+53
-8
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user