Migrate to Go modules (#980)
* Remove 'vendor/' directory * Remove Go dep, add Go modules * Travis: remove Go 1.10/GOPATH, add Go modules
This commit is contained in:
parent
b13675009d
commit
5825da9f4d
|
@ -2,15 +2,12 @@ language: go
|
|||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
# don't use the miekg/dns when testing forks
|
||||
- mkdir -p $GOPATH/src/github.com/miekg
|
||||
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
script:
|
||||
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ed25519","ed25519/internal/edwards25519"]
|
||||
revision = "20be4c3c3ed52bfccdb2d59a412ee1a936d175a7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
|
||||
revision = "60506f45cf65977eb3a9c6e30f995f54a721c271"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
revision = "112230192c580c3556b8cee6403af37a4fc5f28c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "4c4f7f33c9ed00de01c4c741d2177abfcfe19307"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "fc70ece982d3660454fe1b9ccd5c91162a1cc080b58ad82aa065ad4a2ec93ce9"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
38
Gopkg.toml
38
Gopkg.toml
|
@ -1,38 +0,0 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
|
@ -0,0 +1,10 @@
|
|||
module github.com/miekg/dns
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4
|
||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
|
||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0=
|
||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
|
||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
@ -1,10 +0,0 @@
|
|||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
|
@ -1,2 +0,0 @@
|
|||
# Add no patterns to .hgignore except for files generated by the build.
|
||||
last-change
|
|
@ -1,3 +0,0 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/AUTHORS.
|
|
@ -1,26 +0,0 @@
|
|||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
|
@ -1,3 +0,0 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/CONTRIBUTORS.
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -1,21 +0,0 @@
|
|||
# Go Cryptography
|
||||
|
||||
This repository holds supplementary Go cryptography libraries.
|
||||
|
||||
## Download/Install
|
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/crypto/...`. You
|
||||
can also manually git clone the repository to `$GOPATH/src/golang.org/x/crypto`.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
|
||||
The main issue tracker for the crypto repository is located at
|
||||
https://github.com/golang/go/issues. Prefix your issue with "x/crypto:" in the
|
||||
subject line, so it is easy to find.
|
||||
|
||||
Note that contributions to the cryptography package receive additional scrutiny
|
||||
due to their sensitive nature. Patches may take longer than normal to receive
|
||||
feedback.
|
|
@ -1 +0,0 @@
|
|||
issuerepo: golang/go
|
|
@ -1,217 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
package ed25519
|
||||
|
||||
// This code is a port of the public domain, “ref10” implementation of ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
type PublicKey []byte
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
type PrivateKey []byte
|
||||
|
||||
// Public returns the PublicKey corresponding to priv.
|
||||
func (priv PrivateKey) Public() crypto.PublicKey {
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, priv[32:])
|
||||
return PublicKey(publicKey)
|
||||
}
|
||||
|
||||
// Seed returns the private key seed corresponding to priv. It is provided for
|
||||
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
|
||||
// in this package.
|
||||
func (priv PrivateKey) Seed() []byte {
|
||||
seed := make([]byte, SeedSize)
|
||||
copy(seed, priv[:32])
|
||||
return seed
|
||||
}
|
||||
|
||||
// Sign signs the given message with priv.
|
||||
// Ed25519 performs two passes over messages to be signed and therefore cannot
|
||||
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
|
||||
// indicate the message hasn't been hashed. This can be achieved by passing
|
||||
// crypto.Hash(0) as the value for opts.
|
||||
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
if opts.HashFunc() != crypto.Hash(0) {
|
||||
return nil, errors.New("ed25519: cannot sign hashed message")
|
||||
}
|
||||
|
||||
return Sign(priv, message), nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
if rand == nil {
|
||||
rand = cryptorand.Reader
|
||||
}
|
||||
|
||||
seed := make([]byte, SeedSize)
|
||||
if _, err := io.ReadFull(rand, seed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
privateKey := NewKeyFromSeed(seed)
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, privateKey[32:])
|
||||
|
||||
return publicKey, privateKey, nil
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey {
|
||||
if l := len(seed); l != SeedSize {
|
||||
panic("ed25519: bad seed length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
digest := sha512.Sum512(seed)
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var hBytes [32]byte
|
||||
copy(hBytes[:], digest[:])
|
||||
edwards25519.GeScalarMultBase(&A, &hBytes)
|
||||
var publicKeyBytes [32]byte
|
||||
A.ToBytes(&publicKeyBytes)
|
||||
|
||||
privateKey := make([]byte, PrivateKeySize)
|
||||
copy(privateKey, seed)
|
||||
copy(privateKey[32:], publicKeyBytes[:])
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
if l := len(privateKey); l != PrivateKeySize {
|
||||
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(privateKey[:32])
|
||||
|
||||
var digest1, messageDigest, hramDigest [64]byte
|
||||
var expandedSecretKey [32]byte
|
||||
h.Sum(digest1[:0])
|
||||
copy(expandedSecretKey[:], digest1[:])
|
||||
expandedSecretKey[0] &= 248
|
||||
expandedSecretKey[31] &= 63
|
||||
expandedSecretKey[31] |= 64
|
||||
|
||||
h.Reset()
|
||||
h.Write(digest1[32:])
|
||||
h.Write(message)
|
||||
h.Sum(messageDigest[:0])
|
||||
|
||||
var messageDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
||||
var R edwards25519.ExtendedGroupElement
|
||||
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
||||
|
||||
var encodedR [32]byte
|
||||
R.ToBytes(&encodedR)
|
||||
|
||||
h.Reset()
|
||||
h.Write(encodedR[:])
|
||||
h.Write(privateKey[32:])
|
||||
h.Write(message)
|
||||
h.Sum(hramDigest[:0])
|
||||
var hramDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
||||
|
||||
var s [32]byte
|
||||
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
||||
|
||||
signature := make([]byte, SignatureSize)
|
||||
copy(signature[:], encodedR[:])
|
||||
copy(signature[32:], s[:])
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
if l := len(publicKey); l != PublicKeySize {
|
||||
panic("ed25519: bad public key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
if len(sig) != SignatureSize || sig[63]&224 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var publicKeyBytes [32]byte
|
||||
copy(publicKeyBytes[:], publicKey)
|
||||
if !A.FromBytes(&publicKeyBytes) {
|
||||
return false
|
||||
}
|
||||
edwards25519.FeNeg(&A.X, &A.X)
|
||||
edwards25519.FeNeg(&A.T, &A.T)
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(sig[:32])
|
||||
h.Write(publicKey[:])
|
||||
h.Write(message)
|
||||
var digest [64]byte
|
||||
h.Sum(digest[:0])
|
||||
|
||||
var hReduced [32]byte
|
||||
edwards25519.ScReduce(&hReduced, &digest)
|
||||
|
||||
var R edwards25519.ProjectiveGroupElement
|
||||
var s [32]byte
|
||||
copy(s[:], sig[32:])
|
||||
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
|
||||
// the range [0, order) in order to prevent signature malleability.
|
||||
if !edwards25519.ScMinimal(&s) {
|
||||
return false
|
||||
}
|
||||
|
||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
|
||||
|
||||
var checkR [32]byte
|
||||
R.ToBytes(&checkR)
|
||||
return bytes.Equal(sig[:32], checkR[:])
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
type zeroReader struct{}
|
||||
|
||||
func (zeroReader) Read(buf []byte) (int, error) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func TestUnmarshalMarshal(t *testing.T) {
|
||||
pub, _, _ := GenerateKey(rand.Reader)
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var pubBytes [32]byte
|
||||
copy(pubBytes[:], pub)
|
||||
if !A.FromBytes(&pubBytes) {
|
||||
t.Fatalf("ExtendedGroupElement.FromBytes failed")
|
||||
}
|
||||
|
||||
var pub2 [32]byte
|
||||
A.ToBytes(&pub2)
|
||||
|
||||
if pubBytes != pub2 {
|
||||
t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
var zero zeroReader
|
||||
public, private, _ := GenerateKey(zero)
|
||||
|
||||
message := []byte("test message")
|
||||
sig := Sign(private, message)
|
||||
if !Verify(public, message, sig) {
|
||||
t.Errorf("valid signature rejected")
|
||||
}
|
||||
|
||||
wrongMessage := []byte("wrong message")
|
||||
if Verify(public, wrongMessage, sig) {
|
||||
t.Errorf("signature of different message accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCryptoSigner(t *testing.T) {
|
||||
var zero zeroReader
|
||||
public, private, _ := GenerateKey(zero)
|
||||
|
||||
signer := crypto.Signer(private)
|
||||
|
||||
publicInterface := signer.Public()
|
||||
public2, ok := publicInterface.(PublicKey)
|
||||
if !ok {
|
||||
t.Fatalf("expected PublicKey from Public() but got %T", publicInterface)
|
||||
}
|
||||
|
||||
if !bytes.Equal(public, public2) {
|
||||
t.Errorf("public keys do not match: original:%x vs Public():%x", public, public2)
|
||||
}
|
||||
|
||||
message := []byte("message")
|
||||
var noHash crypto.Hash
|
||||
signature, err := signer.Sign(zero, message, noHash)
|
||||
if err != nil {
|
||||
t.Fatalf("error from Sign(): %s", err)
|
||||
}
|
||||
|
||||
if !Verify(public, message, signature) {
|
||||
t.Errorf("Verify failed on signature from Sign()")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGolden(t *testing.T) {
|
||||
// sign.input.gz is a selection of test cases from
|
||||
// https://ed25519.cr.yp.to/python/sign.input
|
||||
testDataZ, err := os.Open("testdata/sign.input.gz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testDataZ.Close()
|
||||
testData, err := gzip.NewReader(testDataZ)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testData.Close()
|
||||
|
||||
scanner := bufio.NewScanner(testData)
|
||||
lineNo := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
lineNo++
|
||||
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 5 {
|
||||
t.Fatalf("bad number of parts on line %d", lineNo)
|
||||
}
|
||||
|
||||
privBytes, _ := hex.DecodeString(parts[0])
|
||||
pubKey, _ := hex.DecodeString(parts[1])
|
||||
msg, _ := hex.DecodeString(parts[2])
|
||||
sig, _ := hex.DecodeString(parts[3])
|
||||
// The signatures in the test vectors also include the message
|
||||
// at the end, but we just want R and S.
|
||||
sig = sig[:SignatureSize]
|
||||
|
||||
if l := len(pubKey); l != PublicKeySize {
|
||||
t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
|
||||
}
|
||||
|
||||
var priv [PrivateKeySize]byte
|
||||
copy(priv[:], privBytes)
|
||||
copy(priv[32:], pubKey)
|
||||
|
||||
sig2 := Sign(priv[:], msg)
|
||||
if !bytes.Equal(sig, sig2[:]) {
|
||||
t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
|
||||
}
|
||||
|
||||
if !Verify(pubKey, msg, sig2) {
|
||||
t.Errorf("signature failed to verify on line %d", lineNo)
|
||||
}
|
||||
|
||||
priv2 := NewKeyFromSeed(priv[:32])
|
||||
if !bytes.Equal(priv[:], priv2) {
|
||||
t.Errorf("recreating key pair gave different private key on line %d: %x vs %x", lineNo, priv[:], priv2)
|
||||
}
|
||||
|
||||
if pubKey2 := priv2.Public().(PublicKey); !bytes.Equal(pubKey, pubKey2) {
|
||||
t.Errorf("recreating key pair gave different public key on line %d: %x vs %x", lineNo, pubKey, pubKey2)
|
||||
}
|
||||
|
||||
if seed := priv2.Seed(); !bytes.Equal(priv[:32], seed) {
|
||||
t.Errorf("recreating key pair gave different seed on line %d: %x vs %x", lineNo, priv[:32], seed)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
t.Fatalf("error reading test data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMalleability(t *testing.T) {
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
|
||||
// that s be in [0, order). This prevents someone from adding a multiple of
|
||||
// order to s and obtaining a second valid signature for the same message.
|
||||
msg := []byte{0x54, 0x65, 0x73, 0x74}
|
||||
sig := []byte{
|
||||
0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
|
||||
0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
|
||||
0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
|
||||
0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
|
||||
0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
|
||||
0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
|
||||
}
|
||||
publicKey := []byte{
|
||||
0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
|
||||
0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
|
||||
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
|
||||
}
|
||||
|
||||
if Verify(publicKey, msg, sig) {
|
||||
t.Fatal("non-canonical signature accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkKeyGeneration(b *testing.B) {
|
||||
var zero zeroReader
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, _, err := GenerateKey(zero); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSigning(b *testing.B) {
|
||||
var zero zeroReader
|
||||
_, priv, err := GenerateKey(zero)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
message := []byte("Hello, world!")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sign(priv, message)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkVerification(b *testing.B) {
|
||||
var zero zeroReader
|
||||
pub, priv, err := GenerateKey(zero)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
message := []byte("Hello, world!")
|
||||
signature := Sign(priv, message)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Verify(pub, message, signature)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
|||
module golang.org/x/crypto
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d
|
||||
)
|
|
@ -1,8 +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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
@ -1,10 +0,0 @@
|
|||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
|
@ -1,2 +0,0 @@
|
|||
# Add no patterns to .hgignore except for files generated by the build.
|
||||
last-change
|
|
@ -1,3 +0,0 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
|
@ -1,26 +0,0 @@
|
|||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
|
@ -1,3 +0,0 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -1,16 +0,0 @@
|
|||
# Go Networking
|
||||
|
||||
This repository holds supplementary Go networking libraries.
|
||||
|
||||
## Download/Install
|
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/net`. You can
|
||||
also manually git clone the repository to `$GOPATH/src/golang.org/x/net`.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit
|
||||
changes to this repository, see https://golang.org/doc/contribute.html.
|
||||
The main issue tracker for the net repository is located at
|
||||
https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the
|
||||
subject line, so it is easy to find.
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Assemble converts insts into raw instructions suitable for loading
|
||||
// into a BPF virtual machine.
|
||||
//
|
||||
// Currently, no optimization is attempted, the assembled program flow
|
||||
// is exactly as provided.
|
||||
func Assemble(insts []Instruction) ([]RawInstruction, error) {
|
||||
ret := make([]RawInstruction, len(insts))
|
||||
var err error
|
||||
for i, inst := range insts {
|
||||
ret[i], err = inst.Assemble()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Disassemble attempts to parse raw back into
|
||||
// Instructions. Unrecognized RawInstructions are assumed to be an
|
||||
// extension not implemented by this package, and are passed through
|
||||
// unchanged to the output. The allDecoded value reports whether insts
|
||||
// contains no RawInstructions.
|
||||
func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) {
|
||||
insts = make([]Instruction, len(raw))
|
||||
allDecoded = true
|
||||
for i, r := range raw {
|
||||
insts[i] = r.Disassemble()
|
||||
if _, ok := insts[i].(RawInstruction); ok {
|
||||
allDecoded = false
|
||||
}
|
||||
}
|
||||
return insts, allDecoded
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
// A Register is a register of the BPF virtual machine.
|
||||
type Register uint16
|
||||
|
||||
const (
|
||||
// RegA is the accumulator register. RegA is always the
|
||||
// destination register of ALU operations.
|
||||
RegA Register = iota
|
||||
// RegX is the indirection register, used by LoadIndirect
|
||||
// operations.
|
||||
RegX
|
||||
)
|
||||
|
||||
// An ALUOp is an arithmetic or logic operation.
|
||||
type ALUOp uint16
|
||||
|
||||
// ALU binary operation types.
|
||||
const (
|
||||
ALUOpAdd ALUOp = iota << 4
|
||||
ALUOpSub
|
||||
ALUOpMul
|
||||
ALUOpDiv
|
||||
ALUOpOr
|
||||
ALUOpAnd
|
||||
ALUOpShiftLeft
|
||||
ALUOpShiftRight
|
||||
aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type.
|
||||
ALUOpMod
|
||||
ALUOpXor
|
||||
)
|
||||
|
||||
// A JumpTest is a comparison operator used in conditional jumps.
|
||||
type JumpTest uint16
|
||||
|
||||
// Supported operators for conditional jumps.
|
||||
// K can be RegX for JumpIfX
|
||||
const (
|
||||
// K == A
|
||||
JumpEqual JumpTest = iota
|
||||
// K != A
|
||||
JumpNotEqual
|
||||
// K > A
|
||||
JumpGreaterThan
|
||||
// K < A
|
||||
JumpLessThan
|
||||
// K >= A
|
||||
JumpGreaterOrEqual
|
||||
// K <= A
|
||||
JumpLessOrEqual
|
||||
// K & A != 0
|
||||
JumpBitsSet
|
||||
// K & A == 0
|
||||
JumpBitsNotSet
|
||||
)
|
||||
|
||||
// An Extension is a function call provided by the kernel that
|
||||
// performs advanced operations that are expensive or impossible
|
||||
// within the BPF virtual machine.
|
||||
//
|
||||
// Extensions are only implemented by the Linux kernel.
|
||||
//
|
||||
// TODO: should we prune this list? Some of these extensions seem
|
||||
// either broken or near-impossible to use correctly, whereas other
|
||||
// (len, random, ifindex) are quite useful.
|
||||
type Extension int
|
||||
|
||||
// Extension functions available in the Linux kernel.
|
||||
const (
|
||||
// extOffset is the negative maximum number of instructions used
|
||||
// to load instructions by overloading the K argument.
|
||||
extOffset = -0x1000
|
||||
// ExtLen returns the length of the packet.
|
||||
ExtLen Extension = 1
|
||||
// ExtProto returns the packet's L3 protocol type.
|
||||
ExtProto Extension = 0
|
||||
// ExtType returns the packet's type (skb->pkt_type in the kernel)
|
||||
//
|
||||
// TODO: better documentation. How nice an API do we want to
|
||||
// provide for these esoteric extensions?
|
||||
ExtType Extension = 4
|
||||
// ExtPayloadOffset returns the offset of the packet payload, or
|
||||
// the first protocol header that the kernel does not know how to
|
||||
// parse.
|
||||
ExtPayloadOffset Extension = 52
|
||||
// ExtInterfaceIndex returns the index of the interface on which
|
||||
// the packet was received.
|
||||
ExtInterfaceIndex Extension = 8
|
||||
// ExtNetlinkAttr returns the netlink attribute of type X at
|
||||
// offset A.
|
||||
ExtNetlinkAttr Extension = 12
|
||||
// ExtNetlinkAttrNested returns the nested netlink attribute of
|
||||
// type X at offset A.
|
||||
ExtNetlinkAttrNested Extension = 16
|
||||
// ExtMark returns the packet's mark value.
|
||||
ExtMark Extension = 20
|
||||
// ExtQueue returns the packet's assigned hardware queue.
|
||||
ExtQueue Extension = 24
|
||||
// ExtLinkLayerType returns the packet's hardware address type
|
||||
// (e.g. Ethernet, Infiniband).
|
||||
ExtLinkLayerType Extension = 28
|
||||
// ExtRXHash returns the packets receive hash.
|
||||
//
|
||||
// TODO: figure out what this rxhash actually is.
|
||||
ExtRXHash Extension = 32
|
||||
// ExtCPUID returns the ID of the CPU processing the current
|
||||
// packet.
|
||||
ExtCPUID Extension = 36
|
||||
// ExtVLANTag returns the packet's VLAN tag.
|
||||
ExtVLANTag Extension = 44
|
||||
// ExtVLANTagPresent returns non-zero if the packet has a VLAN
|
||||
// tag.
|
||||
//
|
||||
// TODO: I think this might be a lie: it reads bit 0x1000 of the
|
||||
// VLAN header, which changed meaning in recent revisions of the
|
||||
// spec - this extension may now return meaningless information.
|
||||
ExtVLANTagPresent Extension = 48
|
||||
// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
|
||||
// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
|
||||
// other value if no VLAN information is present.
|
||||
ExtVLANProto Extension = 60
|
||||
// ExtRand returns a uniformly random uint32.
|
||||
ExtRand Extension = 56
|
||||
)
|
||||
|
||||
// The following gives names to various bit patterns used in opcode construction.
|
||||
|
||||
const (
|
||||
opMaskCls uint16 = 0x7
|
||||
// opClsLoad masks
|
||||
opMaskLoadDest = 0x01
|
||||
opMaskLoadWidth = 0x18
|
||||
opMaskLoadMode = 0xe0
|
||||
// opClsALU & opClsJump
|
||||
opMaskOperand = 0x08
|
||||
opMaskOperator = 0xf0
|
||||
)
|
||||
|
||||
const (
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsLoadA uint16 = iota
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsLoadX
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
opClsStoreA
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
opClsStoreX
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsALU
|
||||
// +-----------------------------+---+---+---+---+
|
||||
// | TestOperator (4b) | 0 | 1 | 0 | 1 |
|
||||
// +-----------------------------+---+---+---+---+
|
||||
opClsJump
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
// | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 |
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
opClsReturn
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
// | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 |
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
opClsMisc
|
||||
)
|
||||
|
||||
const (
|
||||
opAddrModeImmediate uint16 = iota << 5
|
||||
opAddrModeAbsolute
|
||||
opAddrModeIndirect
|
||||
opAddrModeScratch
|
||||
opAddrModePacketLen // actually an extension, not an addressing mode.
|
||||
opAddrModeMemShift
|
||||
)
|
||||
|
||||
const (
|
||||
opLoadWidth4 uint16 = iota << 3
|
||||
opLoadWidth2
|
||||
opLoadWidth1
|
||||
)
|
||||
|
||||
// Operand for ALU and Jump instructions
|
||||
type opOperand uint16
|
||||
|
||||
// Supported operand sources.
|
||||
const (
|
||||
opOperandConstant opOperand = iota << 3
|
||||
opOperandX
|
||||
)
|
||||
|
||||
// An jumpOp is a conditional jump condition.
|
||||
type jumpOp uint16
|
||||
|
||||
// Supported jump conditions.
|
||||
const (
|
||||
opJumpAlways jumpOp = iota << 4
|
||||
opJumpEqual
|
||||
opJumpGT
|
||||
opJumpGE
|
||||
opJumpSet
|
||||
)
|
||||
|
||||
const (
|
||||
opRetSrcConstant uint16 = iota << 4
|
||||
opRetSrcA
|
||||
)
|
||||
|
||||
const (
|
||||
opMiscTAX = 0x00
|
||||
opMiscTXA = 0x80
|
||||
)
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package bpf implements marshaling and unmarshaling of programs for the
|
||||
Berkeley Packet Filter virtual machine, and provides a Go implementation
|
||||
of the virtual machine.
|
||||
|
||||
BPF's main use is to specify a packet filter for network taps, so that
|
||||
the kernel doesn't have to expensively copy every packet it sees to
|
||||
userspace. However, it's been repurposed to other areas where running
|
||||
user code in-kernel is needed. For example, Linux's seccomp uses BPF
|
||||
to apply security policies to system calls. For simplicity, this
|
||||
documentation refers only to packets, but other uses of BPF have their
|
||||
own data payloads.
|
||||
|
||||
BPF programs run in a restricted virtual machine. It has almost no
|
||||
access to kernel functions, and while conditional branches are
|
||||
allowed, they can only jump forwards, to guarantee that there are no
|
||||
infinite loops.
|
||||
|
||||
The virtual machine
|
||||
|
||||
The BPF VM is an accumulator machine. Its main register, called
|
||||
register A, is an implicit source and destination in all arithmetic
|
||||
and logic operations. The machine also has 16 scratch registers for
|
||||
temporary storage, and an indirection register (register X) for
|
||||
indirect memory access. All registers are 32 bits wide.
|
||||
|
||||
Each run of a BPF program is given one packet, which is placed in the
|
||||
VM's read-only "main memory". LoadAbsolute and LoadIndirect
|
||||
instructions can fetch up to 32 bits at a time into register A for
|
||||
examination.
|
||||
|
||||
The goal of a BPF program is to produce and return a verdict (uint32),
|
||||
which tells the kernel what to do with the packet. In the context of
|
||||
packet filtering, the returned value is the number of bytes of the
|
||||
packet to forward to userspace, or 0 to ignore the packet. Other
|
||||
contexts like seccomp define their own return values.
|
||||
|
||||
In order to simplify programs, attempts to read past the end of the
|
||||
packet terminate the program execution with a verdict of 0 (ignore
|
||||
packet). This means that the vast majority of BPF programs don't need
|
||||
to do any explicit bounds checking.
|
||||
|
||||
In addition to the bytes of the packet, some BPF programs have access
|
||||
to extensions, which are essentially calls to kernel utility
|
||||
functions. Currently, the only extensions supported by this package
|
||||
are the Linux packet filter extensions.
|
||||
|
||||
Examples
|
||||
|
||||
This packet filter selects all ARP packets.
|
||||
|
||||
bpf.Assemble([]bpf.Instruction{
|
||||
// Load "EtherType" field from the ethernet header.
|
||||
bpf.LoadAbsolute{Off: 12, Size: 2},
|
||||
// Skip over the next instruction if EtherType is not ARP.
|
||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1},
|
||||
// Verdict is "send up to 4k of the packet to userspace."
|
||||
bpf.RetConstant{Val: 4096},
|
||||
// Verdict is "ignore packet."
|
||||
bpf.RetConstant{Val: 0},
|
||||
})
|
||||
|
||||
This packet filter captures a random 1% sample of traffic.
|
||||
|
||||
bpf.Assemble([]bpf.Instruction{
|
||||
// Get a 32-bit random number from the Linux kernel.
|
||||
bpf.LoadExtension{Num: bpf.ExtRand},
|
||||
// 1% dice roll?
|
||||
bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1},
|
||||
// Capture.
|
||||
bpf.RetConstant{Val: 4096},
|
||||
// Ignore.
|
||||
bpf.RetConstant{Val: 0},
|
||||
})
|
||||
|
||||
*/
|
||||
package bpf // import "golang.org/x/net/bpf"
|
|
@ -1,726 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import "fmt"
|
||||
|
||||
// An Instruction is one instruction executed by the BPF virtual
|
||||
// machine.
|
||||
type Instruction interface {
|
||||
// Assemble assembles the Instruction into a RawInstruction.
|
||||
Assemble() (RawInstruction, error)
|
||||
}
|
||||
|
||||
// A RawInstruction is a raw BPF virtual machine instruction.
|
||||
type RawInstruction struct {
|
||||
// Operation to execute.
|
||||
Op uint16
|
||||
// For conditional jump instructions, the number of instructions
|
||||
// to skip if the condition is true/false.
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
// Constant parameter. The meaning depends on the Op.
|
||||
K uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
|
||||
|
||||
// Disassemble parses ri into an Instruction and returns it. If ri is
|
||||
// not recognized by this package, ri itself is returned.
|
||||
func (ri RawInstruction) Disassemble() Instruction {
|
||||
switch ri.Op & opMaskCls {
|
||||
case opClsLoadA, opClsLoadX:
|
||||
reg := Register(ri.Op & opMaskLoadDest)
|
||||
sz := 0
|
||||
switch ri.Op & opMaskLoadWidth {
|
||||
case opLoadWidth4:
|
||||
sz = 4
|
||||
case opLoadWidth2:
|
||||
sz = 2
|
||||
case opLoadWidth1:
|
||||
sz = 1
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
switch ri.Op & opMaskLoadMode {
|
||||
case opAddrModeImmediate:
|
||||
if sz != 4 {
|
||||
return ri
|
||||
}
|
||||
return LoadConstant{Dst: reg, Val: ri.K}
|
||||
case opAddrModeScratch:
|
||||
if sz != 4 || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return LoadScratch{Dst: reg, N: int(ri.K)}
|
||||
case opAddrModeAbsolute:
|
||||
if ri.K > extOffset+0xffffffff {
|
||||
return LoadExtension{Num: Extension(-extOffset + ri.K)}
|
||||
}
|
||||
return LoadAbsolute{Size: sz, Off: ri.K}
|
||||
case opAddrModeIndirect:
|
||||
return LoadIndirect{Size: sz, Off: ri.K}
|
||||
case opAddrModePacketLen:
|
||||
if sz != 4 {
|
||||
return ri
|
||||
}
|
||||
return LoadExtension{Num: ExtLen}
|
||||
case opAddrModeMemShift:
|
||||
return LoadMemShift{Off: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsStoreA:
|
||||
if ri.Op != opClsStoreA || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return StoreScratch{Src: RegA, N: int(ri.K)}
|
||||
|
||||
case opClsStoreX:
|
||||
if ri.Op != opClsStoreX || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return StoreScratch{Src: RegX, N: int(ri.K)}
|
||||
|
||||
case opClsALU:
|
||||
switch op := ALUOp(ri.Op & opMaskOperator); op {
|
||||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return ALUOpX{Op: op}
|
||||
case opOperandConstant:
|
||||
return ALUOpConstant{Op: op, Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
case aluOpNeg:
|
||||
return NegateA{}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsJump:
|
||||
switch op := jumpOp(ri.Op & opMaskOperator); op {
|
||||
case opJumpAlways:
|
||||
return Jump{Skip: ri.K}
|
||||
case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
|
||||
cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
case opOperandConstant:
|
||||
return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsReturn:
|
||||
switch ri.Op {
|
||||
case opClsReturn | opRetSrcA:
|
||||
return RetA{}
|
||||
case opClsReturn | opRetSrcConstant:
|
||||
return RetConstant{Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsMisc:
|
||||
switch ri.Op {
|
||||
case opClsMisc | opMiscTAX:
|
||||
return TAX{}
|
||||
case opClsMisc | opMiscTXA:
|
||||
return TXA{}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable") // switch is exhaustive on the bit pattern
|
||||
}
|
||||
}
|
||||
|
||||
func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
|
||||
var test JumpTest
|
||||
|
||||
// Decode "fake" jump conditions that don't appear in machine code
|
||||
// Ensures the Assemble -> Disassemble stage recreates the same instructions
|
||||
// See https://github.com/golang/go/issues/18470
|
||||
if skipTrue == 0 {
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpNotEqual
|
||||
case opJumpGT:
|
||||
test = JumpLessOrEqual
|
||||
case opJumpGE:
|
||||
test = JumpLessThan
|
||||
case opJumpSet:
|
||||
test = JumpBitsNotSet
|
||||
}
|
||||
|
||||
return test, skipFalse, 0
|
||||
}
|
||||
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpEqual
|
||||
case opJumpGT:
|
||||
test = JumpGreaterThan
|
||||
case opJumpGE:
|
||||
test = JumpGreaterOrEqual
|
||||
case opJumpSet:
|
||||
test = JumpBitsSet
|
||||
}
|
||||
|
||||
return test, skipTrue, skipFalse
|
||||
}
|
||||
|
||||
// LoadConstant loads Val into register Dst.
|
||||
type LoadConstant struct {
|
||||
Dst Register
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadConstant) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadConstant) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld #%d", a.Val)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadScratch loads scratch[N] into register Dst.
|
||||
type LoadScratch struct {
|
||||
Dst Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadScratch) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAbsolute loads packet[Off:Off+Size] as an integer value into
|
||||
// register A.
|
||||
type LoadAbsolute struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadAbsolute) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadAbsolute) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [%d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [%d]", a.Off)
|
||||
case 4: // word
|
||||
if a.Off > extOffset+0xffffffff {
|
||||
return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
|
||||
}
|
||||
return fmt.Sprintf("ld [%d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
|
||||
// into register A.
|
||||
type LoadIndirect struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadIndirect) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadIndirect) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [x + %d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [x + %d]", a.Off)
|
||||
case 4: // word
|
||||
return fmt.Sprintf("ld [x + %d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
|
||||
// by 4 and stores the result in register X.
|
||||
//
|
||||
// This instruction is mainly useful to load into X the length of an
|
||||
// IPv4 packet header in a single instruction, rather than have to do
|
||||
// the arithmetic on the header's first byte by hand.
|
||||
type LoadMemShift struct {
|
||||
Off uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadMemShift) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadMemShift) String() string {
|
||||
return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
|
||||
}
|
||||
|
||||
// LoadExtension invokes a linux-specific extension and stores the
|
||||
// result in register A.
|
||||
type LoadExtension struct {
|
||||
Num Extension
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadExtension) Assemble() (RawInstruction, error) {
|
||||
if a.Num == ExtLen {
|
||||
return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
|
||||
}
|
||||
return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadExtension) String() string {
|
||||
switch a.Num {
|
||||
case ExtLen:
|
||||
return "ld #len"
|
||||
case ExtProto:
|
||||
return "ld #proto"
|
||||
case ExtType:
|
||||
return "ld #type"
|
||||
case ExtPayloadOffset:
|
||||
return "ld #poff"
|
||||
case ExtInterfaceIndex:
|
||||
return "ld #ifidx"
|
||||
case ExtNetlinkAttr:
|
||||
return "ld #nla"
|
||||
case ExtNetlinkAttrNested:
|
||||
return "ld #nlan"
|
||||
case ExtMark:
|
||||
return "ld #mark"
|
||||
case ExtQueue:
|
||||
return "ld #queue"
|
||||
case ExtLinkLayerType:
|
||||
return "ld #hatype"
|
||||
case ExtRXHash:
|
||||
return "ld #rxhash"
|
||||
case ExtCPUID:
|
||||
return "ld #cpu"
|
||||
case ExtVLANTag:
|
||||
return "ld #vlan_tci"
|
||||
case ExtVLANTagPresent:
|
||||
return "ld #vlan_avail"
|
||||
case ExtVLANProto:
|
||||
return "ld #vlan_tpid"
|
||||
case ExtRand:
|
||||
return "ld #rand"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// StoreScratch stores register Src into scratch[N].
|
||||
type StoreScratch struct {
|
||||
Src Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a StoreScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
var op uint16
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
op = opClsStoreA
|
||||
case RegX:
|
||||
op = opClsStoreX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
|
||||
}
|
||||
|
||||
return RawInstruction{
|
||||
Op: op,
|
||||
K: uint32(a.N),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a StoreScratch) String() string {
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
return fmt.Sprintf("st M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("stx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpConstant executes A = A <Op> Val.
|
||||
type ALUOpConstant struct {
|
||||
Op ALUOp
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpConstant) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return fmt.Sprintf("add #%d", a.Val)
|
||||
case ALUOpSub:
|
||||
return fmt.Sprintf("sub #%d", a.Val)
|
||||
case ALUOpMul:
|
||||
return fmt.Sprintf("mul #%d", a.Val)
|
||||
case ALUOpDiv:
|
||||
return fmt.Sprintf("div #%d", a.Val)
|
||||
case ALUOpMod:
|
||||
return fmt.Sprintf("mod #%d", a.Val)
|
||||
case ALUOpAnd:
|
||||
return fmt.Sprintf("and #%d", a.Val)
|
||||
case ALUOpOr:
|
||||
return fmt.Sprintf("or #%d", a.Val)
|
||||
case ALUOpXor:
|
||||
return fmt.Sprintf("xor #%d", a.Val)
|
||||
case ALUOpShiftLeft:
|
||||
return fmt.Sprintf("lsh #%d", a.Val)
|
||||
case ALUOpShiftRight:
|
||||
return fmt.Sprintf("rsh #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpX executes A = A <Op> X
|
||||
type ALUOpX struct {
|
||||
Op ALUOp
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpX) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return "add x"
|
||||
case ALUOpSub:
|
||||
return "sub x"
|
||||
case ALUOpMul:
|
||||
return "mul x"
|
||||
case ALUOpDiv:
|
||||
return "div x"
|
||||
case ALUOpMod:
|
||||
return "mod x"
|
||||
case ALUOpAnd:
|
||||
return "and x"
|
||||
case ALUOpOr:
|
||||
return "or x"
|
||||
case ALUOpXor:
|
||||
return "xor x"
|
||||
case ALUOpShiftLeft:
|
||||
return "lsh x"
|
||||
case ALUOpShiftRight:
|
||||
return "rsh x"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// NegateA executes A = -A.
|
||||
type NegateA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a NegateA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(aluOpNeg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a NegateA) String() string {
|
||||
return fmt.Sprintf("neg")
|
||||
}
|
||||
|
||||
// Jump skips the following Skip instructions in the program.
|
||||
type Jump struct {
|
||||
Skip uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a Jump) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsJump | uint16(opJumpAlways),
|
||||
K: a.Skip,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a Jump) String() string {
|
||||
return fmt.Sprintf("ja %d", a.Skip)
|
||||
}
|
||||
|
||||
// JumpIf skips the following Skip instructions in the program if A
|
||||
// <Cond> Val is true.
|
||||
type JumpIf struct {
|
||||
Cond JumpTest
|
||||
Val uint32
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// JumpIfX skips the following Skip instructions in the program if A
|
||||
// <Cond> X is true.
|
||||
type JumpIfX struct {
|
||||
Cond JumpTest
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIfX) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIfX) String() string {
|
||||
return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// jumpToRaw assembles a jump instruction into a RawInstruction
|
||||
func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
|
||||
var (
|
||||
cond jumpOp
|
||||
flip bool
|
||||
)
|
||||
switch test {
|
||||
case JumpEqual:
|
||||
cond = opJumpEqual
|
||||
case JumpNotEqual:
|
||||
cond, flip = opJumpEqual, true
|
||||
case JumpGreaterThan:
|
||||
cond = opJumpGT
|
||||
case JumpLessThan:
|
||||
cond, flip = opJumpGE, true
|
||||
case JumpGreaterOrEqual:
|
||||
cond = opJumpGE
|
||||
case JumpLessOrEqual:
|
||||
cond, flip = opJumpGT, true
|
||||
case JumpBitsSet:
|
||||
cond = opJumpSet
|
||||
case JumpBitsNotSet:
|
||||
cond, flip = opJumpSet, true
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
|
||||
}
|
||||
jt, jf := skipTrue, skipFalse
|
||||
if flip {
|
||||
jt, jf = jf, jt
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: opClsJump | uint16(cond) | uint16(operand),
|
||||
Jt: jt,
|
||||
Jf: jf,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// jumpToString converts a jump instruction to assembler notation
|
||||
func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
|
||||
switch cond {
|
||||
// K == A
|
||||
case JumpEqual:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
|
||||
// K != A
|
||||
case JumpNotEqual:
|
||||
return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
|
||||
// K > A
|
||||
case JumpGreaterThan:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
|
||||
// K < A
|
||||
case JumpLessThan:
|
||||
return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
|
||||
// K >= A
|
||||
case JumpGreaterOrEqual:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
|
||||
// K <= A
|
||||
case JumpLessOrEqual:
|
||||
return fmt.Sprintf("jle %s,%d", operand, skipTrue)
|
||||
// K & A != 0
|
||||
case JumpBitsSet:
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("jset %s,%d", operand, skipTrue)
|
||||
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
|
||||
case JumpBitsNotSet:
|
||||
return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
|
||||
default:
|
||||
return fmt.Sprintf("unknown JumpTest %#v", cond)
|
||||
}
|
||||
}
|
||||
|
||||
func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
|
||||
if skipTrue > 0 {
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
|
||||
}
|
||||
return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
|
||||
}
|
||||
|
||||
// RetA exits the BPF program, returning the value of register A.
|
||||
type RetA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetA) String() string {
|
||||
return fmt.Sprintf("ret a")
|
||||
}
|
||||
|
||||
// RetConstant exits the BPF program, returning a constant value.
|
||||
type RetConstant struct {
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcConstant,
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetConstant) String() string {
|
||||
return fmt.Sprintf("ret #%d", a.Val)
|
||||
}
|
||||
|
||||
// TXA copies the value of register X to register A.
|
||||
type TXA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TXA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTXA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TXA) String() string {
|
||||
return fmt.Sprintf("txa")
|
||||
}
|
||||
|
||||
// TAX copies the value of register A to register X.
|
||||
type TAX struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TAX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTAX,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TAX) String() string {
|
||||
return fmt.Sprintf("tax")
|
||||
}
|
||||
|
||||
func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
|
||||
var (
|
||||
cls uint16
|
||||
sz uint16
|
||||
)
|
||||
switch dst {
|
||||
case RegA:
|
||||
cls = opClsLoadA
|
||||
case RegX:
|
||||
cls = opClsLoadX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
|
||||
}
|
||||
switch loadSize {
|
||||
case 1:
|
||||
sz = opLoadWidth1
|
||||
case 2:
|
||||
sz = opLoadWidth2
|
||||
case 4:
|
||||
sz = opLoadWidth4
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: cls | sz | mode,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
|
@ -1,593 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// This is a direct translation of the program in
|
||||
// testdata/all_instructions.txt.
|
||||
var allInstructions = []Instruction{
|
||||
LoadConstant{Dst: RegA, Val: 42},
|
||||
LoadConstant{Dst: RegX, Val: 42},
|
||||
|
||||
LoadScratch{Dst: RegA, N: 3},
|
||||
LoadScratch{Dst: RegX, N: 3},
|
||||
|
||||
LoadAbsolute{Off: 42, Size: 1},
|
||||
LoadAbsolute{Off: 42, Size: 2},
|
||||
LoadAbsolute{Off: 42, Size: 4},
|
||||
|
||||
LoadIndirect{Off: 42, Size: 1},
|
||||
LoadIndirect{Off: 42, Size: 2},
|
||||
LoadIndirect{Off: 42, Size: 4},
|
||||
|
||||
LoadMemShift{Off: 42},
|
||||
|
||||
LoadExtension{Num: ExtLen},
|
||||
LoadExtension{Num: ExtProto},
|
||||
LoadExtension{Num: ExtType},
|
||||
LoadExtension{Num: ExtRand},
|
||||
|
||||
StoreScratch{Src: RegA, N: 3},
|
||||
StoreScratch{Src: RegX, N: 3},
|
||||
|
||||
ALUOpConstant{Op: ALUOpAdd, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpSub, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpMul, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpDiv, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpOr, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpAnd, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpMod, Val: 42},
|
||||
ALUOpConstant{Op: ALUOpXor, Val: 42},
|
||||
|
||||
ALUOpX{Op: ALUOpAdd},
|
||||
ALUOpX{Op: ALUOpSub},
|
||||
ALUOpX{Op: ALUOpMul},
|
||||
ALUOpX{Op: ALUOpDiv},
|
||||
ALUOpX{Op: ALUOpOr},
|
||||
ALUOpX{Op: ALUOpAnd},
|
||||
ALUOpX{Op: ALUOpShiftLeft},
|
||||
ALUOpX{Op: ALUOpShiftRight},
|
||||
ALUOpX{Op: ALUOpMod},
|
||||
ALUOpX{Op: ALUOpXor},
|
||||
|
||||
NegateA{},
|
||||
|
||||
Jump{Skip: 17},
|
||||
JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 15, SkipFalse: 16},
|
||||
JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 15},
|
||||
JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 14},
|
||||
JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 13},
|
||||
JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 11, SkipFalse: 12},
|
||||
JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 10, SkipFalse: 11},
|
||||
JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 9, SkipFalse: 10},
|
||||
|
||||
JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
|
||||
JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
|
||||
JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
|
||||
JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
|
||||
JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
|
||||
JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
|
||||
JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
|
||||
|
||||
TAX{},
|
||||
TXA{},
|
||||
|
||||
RetA{},
|
||||
RetConstant{Val: 42},
|
||||
}
|
||||
var allInstructionsExpected = "testdata/all_instructions.bpf"
|
||||
|
||||
// Check that we produce the same output as the canonical bpf_asm
|
||||
// linux kernel tool.
|
||||
func TestInterop(t *testing.T) {
|
||||
out, err := Assemble(allInstructions)
|
||||
if err != nil {
|
||||
t.Fatalf("assembly of allInstructions program failed: %s", err)
|
||||
}
|
||||
t.Logf("Assembled program is %d instructions long", len(out))
|
||||
|
||||
bs, err := ioutil.ReadFile(allInstructionsExpected)
|
||||
if err != nil {
|
||||
t.Fatalf("reading %s: %s", allInstructionsExpected, err)
|
||||
}
|
||||
// First statement is the number of statements, last statement is
|
||||
// empty. We just ignore both and rely on slice length.
|
||||
stmts := strings.Split(string(bs), ",")
|
||||
if len(stmts)-2 != len(out) {
|
||||
t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions))
|
||||
}
|
||||
|
||||
for i, stmt := range stmts[1 : len(stmts)-2] {
|
||||
nums := strings.Split(stmt, " ")
|
||||
if len(nums) != 4 {
|
||||
t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt)
|
||||
}
|
||||
|
||||
actual := out[i]
|
||||
|
||||
op, err := strconv.ParseUint(nums[0], 10, 16)
|
||||
if err != nil {
|
||||
t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected)
|
||||
}
|
||||
if actual.Op != uint16(op) {
|
||||
t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op)
|
||||
}
|
||||
|
||||
jt, err := strconv.ParseUint(nums[1], 10, 8)
|
||||
if err != nil {
|
||||
t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected)
|
||||
}
|
||||
if actual.Jt != uint8(jt) {
|
||||
t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt)
|
||||
}
|
||||
|
||||
jf, err := strconv.ParseUint(nums[2], 10, 8)
|
||||
if err != nil {
|
||||
t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected)
|
||||
}
|
||||
if actual.Jf != uint8(jf) {
|
||||
t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf)
|
||||
}
|
||||
|
||||
k, err := strconv.ParseUint(nums[3], 10, 32)
|
||||
if err != nil {
|
||||
t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected)
|
||||
}
|
||||
if actual.K != uint32(k) {
|
||||
t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that assembly and disassembly match each other.
|
||||
func TestAsmDisasm(t *testing.T) {
|
||||
prog1, err := Assemble(allInstructions)
|
||||
if err != nil {
|
||||
t.Fatalf("assembly of allInstructions program failed: %s", err)
|
||||
}
|
||||
t.Logf("Assembled program is %d instructions long", len(prog1))
|
||||
|
||||
got, allDecoded := Disassemble(prog1)
|
||||
if !allDecoded {
|
||||
t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:")
|
||||
for i, inst := range got {
|
||||
if r, ok := inst.(RawInstruction); ok {
|
||||
t.Logf(" insn %d, %#v --> %#v", i+1, allInstructions[i], r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(allInstructions) != len(got) {
|
||||
t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(allInstructions), len(got))
|
||||
}
|
||||
if !reflect.DeepEqual(allInstructions, got) {
|
||||
t.Errorf("program mutated by disassembly:")
|
||||
for i := range got {
|
||||
if !reflect.DeepEqual(allInstructions[i], got[i]) {
|
||||
t.Logf(" insn %d, s: %#v, p1: %#v, got: %#v", i+1, allInstructions[i], prog1[i], got[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type InvalidInstruction struct{}
|
||||
|
||||
func (a InvalidInstruction) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{}, fmt.Errorf("Invalid Instruction")
|
||||
}
|
||||
|
||||
func (a InvalidInstruction) String() string {
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
instruction Instruction
|
||||
assembler string
|
||||
}{
|
||||
{
|
||||
instruction: LoadConstant{Dst: RegA, Val: 42},
|
||||
assembler: "ld #42",
|
||||
},
|
||||
{
|
||||
instruction: LoadConstant{Dst: RegX, Val: 42},
|
||||
assembler: "ldx #42",
|
||||
},
|
||||
{
|
||||
instruction: LoadConstant{Dst: 0xffff, Val: 42},
|
||||
assembler: "unknown instruction: bpf.LoadConstant{Dst:0xffff, Val:0x2a}",
|
||||
},
|
||||
{
|
||||
instruction: LoadScratch{Dst: RegA, N: 3},
|
||||
assembler: "ld M[3]",
|
||||
},
|
||||
{
|
||||
instruction: LoadScratch{Dst: RegX, N: 3},
|
||||
assembler: "ldx M[3]",
|
||||
},
|
||||
{
|
||||
instruction: LoadScratch{Dst: 0xffff, N: 3},
|
||||
assembler: "unknown instruction: bpf.LoadScratch{Dst:0xffff, N:3}",
|
||||
},
|
||||
{
|
||||
instruction: LoadAbsolute{Off: 42, Size: 1},
|
||||
assembler: "ldb [42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadAbsolute{Off: 42, Size: 2},
|
||||
assembler: "ldh [42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadAbsolute{Off: 42, Size: 4},
|
||||
assembler: "ld [42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadAbsolute{Off: 42, Size: -1},
|
||||
assembler: "unknown instruction: bpf.LoadAbsolute{Off:0x2a, Size:-1}",
|
||||
},
|
||||
{
|
||||
instruction: LoadIndirect{Off: 42, Size: 1},
|
||||
assembler: "ldb [x + 42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadIndirect{Off: 42, Size: 2},
|
||||
assembler: "ldh [x + 42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadIndirect{Off: 42, Size: 4},
|
||||
assembler: "ld [x + 42]",
|
||||
},
|
||||
{
|
||||
instruction: LoadIndirect{Off: 42, Size: -1},
|
||||
assembler: "unknown instruction: bpf.LoadIndirect{Off:0x2a, Size:-1}",
|
||||
},
|
||||
{
|
||||
instruction: LoadMemShift{Off: 42},
|
||||
assembler: "ldx 4*([42]&0xf)",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtLen},
|
||||
assembler: "ld #len",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtProto},
|
||||
assembler: "ld #proto",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtType},
|
||||
assembler: "ld #type",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtPayloadOffset},
|
||||
assembler: "ld #poff",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtInterfaceIndex},
|
||||
assembler: "ld #ifidx",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtNetlinkAttr},
|
||||
assembler: "ld #nla",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtNetlinkAttrNested},
|
||||
assembler: "ld #nlan",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtMark},
|
||||
assembler: "ld #mark",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtQueue},
|
||||
assembler: "ld #queue",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtLinkLayerType},
|
||||
assembler: "ld #hatype",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtRXHash},
|
||||
assembler: "ld #rxhash",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtCPUID},
|
||||
assembler: "ld #cpu",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtVLANTag},
|
||||
assembler: "ld #vlan_tci",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtVLANTagPresent},
|
||||
assembler: "ld #vlan_avail",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtVLANProto},
|
||||
assembler: "ld #vlan_tpid",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: ExtRand},
|
||||
assembler: "ld #rand",
|
||||
},
|
||||
{
|
||||
instruction: LoadAbsolute{Off: 0xfffff038, Size: 4},
|
||||
assembler: "ld #rand",
|
||||
},
|
||||
{
|
||||
instruction: LoadExtension{Num: 0xfff},
|
||||
assembler: "unknown instruction: bpf.LoadExtension{Num:4095}",
|
||||
},
|
||||
{
|
||||
instruction: StoreScratch{Src: RegA, N: 3},
|
||||
assembler: "st M[3]",
|
||||
},
|
||||
{
|
||||
instruction: StoreScratch{Src: RegX, N: 3},
|
||||
assembler: "stx M[3]",
|
||||
},
|
||||
{
|
||||
instruction: StoreScratch{Src: 0xffff, N: 3},
|
||||
assembler: "unknown instruction: bpf.StoreScratch{Src:0xffff, N:3}",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpAdd, Val: 42},
|
||||
assembler: "add #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpSub, Val: 42},
|
||||
assembler: "sub #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpMul, Val: 42},
|
||||
assembler: "mul #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpDiv, Val: 42},
|
||||
assembler: "div #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpOr, Val: 42},
|
||||
assembler: "or #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpAnd, Val: 42},
|
||||
assembler: "and #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
|
||||
assembler: "lsh #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
|
||||
assembler: "rsh #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpMod, Val: 42},
|
||||
assembler: "mod #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: ALUOpXor, Val: 42},
|
||||
assembler: "xor #42",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpConstant{Op: 0xffff, Val: 42},
|
||||
assembler: "unknown instruction: bpf.ALUOpConstant{Op:0xffff, Val:0x2a}",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpAdd},
|
||||
assembler: "add x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpSub},
|
||||
assembler: "sub x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpMul},
|
||||
assembler: "mul x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpDiv},
|
||||
assembler: "div x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpOr},
|
||||
assembler: "or x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpAnd},
|
||||
assembler: "and x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpShiftLeft},
|
||||
assembler: "lsh x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpShiftRight},
|
||||
assembler: "rsh x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpMod},
|
||||
assembler: "mod x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: ALUOpXor},
|
||||
assembler: "xor x",
|
||||
},
|
||||
{
|
||||
instruction: ALUOpX{Op: 0xffff},
|
||||
assembler: "unknown instruction: bpf.ALUOpX{Op:0xffff}",
|
||||
},
|
||||
{
|
||||
instruction: NegateA{},
|
||||
assembler: "neg",
|
||||
},
|
||||
{
|
||||
instruction: Jump{Skip: 10},
|
||||
assembler: "ja 10",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9},
|
||||
assembler: "jeq #42,8,9",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8},
|
||||
assembler: "jeq #42,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipFalse: 8},
|
||||
assembler: "jneq #42,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8},
|
||||
assembler: "jneq #42,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7},
|
||||
assembler: "jlt #42,7",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6},
|
||||
assembler: "jle #42,6",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5},
|
||||
assembler: "jgt #42,4,5",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4},
|
||||
assembler: "jgt #42,4",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4},
|
||||
assembler: "jge #42,3,4",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3},
|
||||
assembler: "jge #42,3",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
|
||||
assembler: "jset #42,2,3",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2},
|
||||
assembler: "jset #42,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
|
||||
assembler: "jset #42,3,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2},
|
||||
assembler: "jset #42,0,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2},
|
||||
assembler: "unknown JumpTest 0xffff",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
|
||||
assembler: "jeq x,8,9",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8},
|
||||
assembler: "jeq x,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpEqual, SkipFalse: 8},
|
||||
assembler: "jneq x,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
|
||||
assembler: "jneq x,8",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
|
||||
assembler: "jlt x,7",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
|
||||
assembler: "jle x,6",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
|
||||
assembler: "jgt x,4,5",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4},
|
||||
assembler: "jgt x,4",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
|
||||
assembler: "jge x,3,4",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3},
|
||||
assembler: "jge x,3",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
|
||||
assembler: "jset x,2,3",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2},
|
||||
assembler: "jset x,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2, SkipFalse: 3},
|
||||
assembler: "jset x,3,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2},
|
||||
assembler: "jset x,0,2",
|
||||
},
|
||||
{
|
||||
instruction: JumpIfX{Cond: 0xffff, SkipTrue: 1, SkipFalse: 2},
|
||||
assembler: "unknown JumpTest 0xffff",
|
||||
},
|
||||
{
|
||||
instruction: TAX{},
|
||||
assembler: "tax",
|
||||
},
|
||||
{
|
||||
instruction: TXA{},
|
||||
assembler: "txa",
|
||||
},
|
||||
{
|
||||
instruction: RetA{},
|
||||
assembler: "ret a",
|
||||
},
|
||||
{
|
||||
instruction: RetConstant{Val: 42},
|
||||
assembler: "ret #42",
|
||||
},
|
||||
// Invalid instruction
|
||||
{
|
||||
instruction: InvalidInstruction{},
|
||||
assembler: "unknown instruction: bpf.InvalidInstruction{}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
if input, ok := testCase.instruction.(fmt.Stringer); ok {
|
||||
got := input.String()
|
||||
if got != testCase.assembler {
|
||||
t.Errorf("String did not return expected assembler notation, expected: %s, got: %s", testCase.assembler, got)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Instruction %#v is not a fmt.Stringer", testCase.instruction)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
// A Setter is a type which can attach a compiled BPF filter to itself.
|
||||
type Setter interface {
|
||||
SetBPF(filter []RawInstruction) error
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A VM is an emulated BPF virtual machine.
|
||||
type VM struct {
|
||||
filter []Instruction
|
||||
}
|
||||
|
||||
// NewVM returns a new VM using the input BPF program.
|
||||
func NewVM(filter []Instruction) (*VM, error) {
|
||||
if len(filter) == 0 {
|
||||
return nil, errors.New("one or more Instructions must be specified")
|
||||
}
|
||||
|
||||
for i, ins := range filter {
|
||||
check := len(filter) - (i + 1)
|
||||
switch ins := ins.(type) {
|
||||
// Check for out-of-bounds jumps in instructions
|
||||
case Jump:
|
||||
if check <= int(ins.Skip) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
|
||||
}
|
||||
case JumpIf:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
case JumpIfX:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
// Check for division or modulus by zero
|
||||
case ALUOpConstant:
|
||||
if ins.Val != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return nil, errors.New("cannot divide by zero using ALUOpConstant")
|
||||
}
|
||||
// Check for unknown extensions
|
||||
case LoadExtension:
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
default:
|
||||
return nil, fmt.Errorf("extension %d not implemented", ins.Num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure last instruction is a return instruction
|
||||
switch filter[len(filter)-1].(type) {
|
||||
case RetA, RetConstant:
|
||||
default:
|
||||
return nil, errors.New("BPF program must end with RetA or RetConstant")
|
||||
}
|
||||
|
||||
// Though our VM works using disassembled instructions, we
|
||||
// attempt to assemble the input filter anyway to ensure it is compatible
|
||||
// with an operating system VM.
|
||||
_, err := Assemble(filter)
|
||||
|
||||
return &VM{
|
||||
filter: filter,
|
||||
}, err
|
||||
}
|
||||
|
||||
// Run runs the VM's BPF program against the input bytes.
|
||||
// Run returns the number of bytes accepted by the BPF program, and any errors
|
||||
// which occurred while processing the program.
|
||||
func (v *VM) Run(in []byte) (int, error) {
|
||||
var (
|
||||
// Registers of the virtual machine
|
||||
regA uint32
|
||||
regX uint32
|
||||
regScratch [16]uint32
|
||||
|
||||
// OK is true if the program should continue processing the next
|
||||
// instruction, or false if not, causing the loop to break
|
||||
ok = true
|
||||
)
|
||||
|
||||
// TODO(mdlayher): implement:
|
||||
// - NegateA:
|
||||
// - would require a change from uint32 registers to int32
|
||||
// registers
|
||||
|
||||
// TODO(mdlayher): add interop tests that check signedness of ALU
|
||||
// operations against kernel implementation, and make sure Go
|
||||
// implementation matches behavior
|
||||
|
||||
for i := 0; i < len(v.filter) && ok; i++ {
|
||||
ins := v.filter[i]
|
||||
|
||||
switch ins := ins.(type) {
|
||||
case ALUOpConstant:
|
||||
regA = aluOpConstant(ins, regA)
|
||||
case ALUOpX:
|
||||
regA, ok = aluOpX(ins, regA, regX)
|
||||
case Jump:
|
||||
i += int(ins.Skip)
|
||||
case JumpIf:
|
||||
jump := jumpIf(ins, regA)
|
||||
i += jump
|
||||
case JumpIfX:
|
||||
jump := jumpIfX(ins, regA, regX)
|
||||
i += jump
|
||||
case LoadAbsolute:
|
||||
regA, ok = loadAbsolute(ins, in)
|
||||
case LoadConstant:
|
||||
regA, regX = loadConstant(ins, regA, regX)
|
||||
case LoadExtension:
|
||||
regA = loadExtension(ins, in)
|
||||
case LoadIndirect:
|
||||
regA, ok = loadIndirect(ins, in, regX)
|
||||
case LoadMemShift:
|
||||
regX, ok = loadMemShift(ins, in)
|
||||
case LoadScratch:
|
||||
regA, regX = loadScratch(ins, regScratch, regA, regX)
|
||||
case RetA:
|
||||
return int(regA), nil
|
||||
case RetConstant:
|
||||
return int(ins.Val), nil
|
||||
case StoreScratch:
|
||||
regScratch = storeScratch(ins, regScratch, regA, regX)
|
||||
case TAX:
|
||||
regX = regA
|
||||
case TXA:
|
||||
regA = regX
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
|
@ -1,512 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
func TestVMALUOpAdd(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpAdd,
|
||||
Val: 3,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
8, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 3, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpSub(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.TAX{},
|
||||
bpf.ALUOpX{
|
||||
Op: bpf.ALUOpSub,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpMul(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpMul,
|
||||
Val: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
6, 2, 3, 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpDiv(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpDiv,
|
||||
Val: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
20, 2, 3, 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpDivByZeroALUOpConstant(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpDiv,
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot divide by zero using ALUOpConstant" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpDivByZeroALUOpX(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
// Load byte 0 into X
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.TAX{},
|
||||
// Load byte 1 into A
|
||||
bpf.LoadAbsolute{
|
||||
Off: 9,
|
||||
Size: 1,
|
||||
},
|
||||
// Attempt to perform 1/0
|
||||
bpf.ALUOpX{
|
||||
Op: bpf.ALUOpDiv,
|
||||
},
|
||||
// Return 4 bytes if program does not terminate
|
||||
bpf.LoadConstant{
|
||||
Val: 12,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 3, 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpOr(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpOr,
|
||||
Val: 0x01,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x10, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0xff,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 9, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpAnd(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpAnd,
|
||||
Val: 0x0019,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xaa, 0x09,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpShiftLeft(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpShiftLeft,
|
||||
Val: 0x01,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 0x02,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0xaa,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpShiftRight(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpShiftRight,
|
||||
Val: 0x01,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 0x04,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x08, 0xff, 0xff,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpMod(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpMod,
|
||||
Val: 20,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
30, 0, 0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpModByZeroALUOpConstant(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpMod,
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot divide by zero using ALUOpConstant" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpModByZeroALUOpX(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
// Load byte 0 into X
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.TAX{},
|
||||
// Load byte 1 into A
|
||||
bpf.LoadAbsolute{
|
||||
Off: 9,
|
||||
Size: 1,
|
||||
},
|
||||
// Attempt to perform 1%0
|
||||
bpf.ALUOpX{
|
||||
Op: bpf.ALUOpMod,
|
||||
},
|
||||
// Return 4 bytes if program does not terminate
|
||||
bpf.LoadConstant{
|
||||
Val: 12,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 3, 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpXor(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpXor,
|
||||
Val: 0x0a,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 0x01,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x0b, 0x00, 0x00, 0x00,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMALUOpUnknown(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.ALUOpConstant{
|
||||
Op: bpf.ALUOpAdd,
|
||||
Val: 1,
|
||||
},
|
||||
// Verify that an unknown operation is a no-op
|
||||
bpf.ALUOpConstant{
|
||||
Op: 100,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 0x02,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/net/nettest"
|
||||
)
|
||||
|
||||
// A virtualMachine is a BPF virtual machine which can process an
|
||||
// input packet against a BPF program and render a verdict.
|
||||
type virtualMachine interface {
|
||||
Run(in []byte) (int, error)
|
||||
}
|
||||
|
||||
// canUseOSVM indicates if the OS BPF VM is available on this platform.
|
||||
func canUseOSVM() bool {
|
||||
// OS BPF VM can only be used on platforms where x/net/ipv4 supports
|
||||
// attaching a BPF program to a socket.
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// All BPF tests against both the Go VM and OS VM are assumed to
|
||||
// be used with a UDP socket. As a result, the entire contents
|
||||
// of a UDP datagram is sent through the BPF program, but only
|
||||
// the body after the UDP header will ever be returned in output.
|
||||
|
||||
// testVM sets up a Go BPF VM, and if available, a native OS BPF VM
|
||||
// for integration testing.
|
||||
func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) {
|
||||
goVM, err := bpf.NewVM(filter)
|
||||
if err != nil {
|
||||
// Some tests expect an error, so this error must be returned
|
||||
// instead of fatally exiting the test
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mvm := &multiVirtualMachine{
|
||||
goVM: goVM,
|
||||
|
||||
t: t,
|
||||
}
|
||||
|
||||
// If available, add the OS VM for tests which verify that both the Go
|
||||
// VM and OS VM have exactly the same output for the same input program
|
||||
// and packet.
|
||||
done := func() {}
|
||||
if canUseOSVM() {
|
||||
osVM, osVMDone := testOSVM(t, filter)
|
||||
done = func() { osVMDone() }
|
||||
mvm.osVM = osVM
|
||||
}
|
||||
|
||||
return mvm, done, nil
|
||||
}
|
||||
|
||||
// udpHeaderLen is the length of a UDP header.
|
||||
const udpHeaderLen = 8
|
||||
|
||||
// A multiVirtualMachine is a virtualMachine which can call out to both the Go VM
|
||||
// and the native OS VM, if the OS VM is available.
|
||||
type multiVirtualMachine struct {
|
||||
goVM virtualMachine
|
||||
osVM virtualMachine
|
||||
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (mvm *multiVirtualMachine) Run(in []byte) (int, error) {
|
||||
if len(in) < udpHeaderLen {
|
||||
mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d",
|
||||
udpHeaderLen, len(in))
|
||||
}
|
||||
|
||||
// All tests have a UDP header as part of input, because the OS VM
|
||||
// packets always will. For the Go VM, this output is trimmed before
|
||||
// being sent back to tests.
|
||||
goOut, goErr := mvm.goVM.Run(in)
|
||||
if goOut >= udpHeaderLen {
|
||||
goOut -= udpHeaderLen
|
||||
}
|
||||
|
||||
// If Go output is larger than the size of the packet, packet filtering
|
||||
// interop tests must trim the output bytes to the length of the packet.
|
||||
// The BPF VM should not do this on its own, as other uses of it do
|
||||
// not trim the output byte count.
|
||||
trim := len(in) - udpHeaderLen
|
||||
if goOut > trim {
|
||||
goOut = trim
|
||||
}
|
||||
|
||||
// When the OS VM is not available, process using the Go VM alone
|
||||
if mvm.osVM == nil {
|
||||
return goOut, goErr
|
||||
}
|
||||
|
||||
// The OS VM will apply its own UDP header, so remove the pseudo header
|
||||
// that the Go VM needs.
|
||||
osOut, err := mvm.osVM.Run(in[udpHeaderLen:])
|
||||
if err != nil {
|
||||
mvm.t.Fatalf("error while running OS VM: %v", err)
|
||||
}
|
||||
|
||||
// Verify both VMs return same number of bytes
|
||||
var mismatch bool
|
||||
if goOut != osOut {
|
||||
mismatch = true
|
||||
mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut)
|
||||
}
|
||||
|
||||
if mismatch {
|
||||
mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match")
|
||||
}
|
||||
|
||||
return goOut, goErr
|
||||
}
|
||||
|
||||
// An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for
|
||||
// processing BPF programs.
|
||||
type osVirtualMachine struct {
|
||||
l net.PacketConn
|
||||
s net.Conn
|
||||
}
|
||||
|
||||
// testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting
|
||||
// packets into a UDP listener with a BPF program attached to it.
|
||||
func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
|
||||
l, err := nettest.NewLocalPacketListener("udp")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open OS VM UDP listener: %v", err)
|
||||
}
|
||||
|
||||
prog, err := bpf.Assemble(filter)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile BPF program: %v", err)
|
||||
}
|
||||
|
||||
ip := l.LocalAddr().(*net.UDPAddr).IP
|
||||
if ip.To4() != nil && ip.To16() == nil {
|
||||
err = ipv4.NewPacketConn(l).SetBPF(prog)
|
||||
} else {
|
||||
err = ipv6.NewPacketConn(l).SetBPF(prog)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("failed to attach BPF program to listener: %v", err)
|
||||
}
|
||||
|
||||
s, err := net.Dial(l.LocalAddr().Network(), l.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial connection to listener: %v", err)
|
||||
}
|
||||
|
||||
done := func() {
|
||||
_ = s.Close()
|
||||
_ = l.Close()
|
||||
}
|
||||
|
||||
return &osVirtualMachine{
|
||||
l: l,
|
||||
s: s,
|
||||
}, done
|
||||
}
|
||||
|
||||
// Run sends the input bytes into the OS's BPF VM and returns its verdict.
|
||||
func (vm *osVirtualMachine) Run(in []byte) (int, error) {
|
||||
go func() {
|
||||
_, _ = vm.s.Write(in)
|
||||
}()
|
||||
|
||||
vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond))
|
||||
|
||||
var b [512]byte
|
||||
n, _, err := vm.l.ReadFrom(b[:])
|
||||
if err != nil {
|
||||
// A timeout indicates that BPF filtered out the packet, and thus,
|
||||
// no input should be returned.
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
func TestVMLoadExtensionNotImplemented(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadExtension{
|
||||
Num: 100,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "extension 100 not implemented" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadExtensionExtLen(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadExtension{
|
||||
Num: bpf.ExtLen,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 {
|
||||
return aluOpCommon(ins.Op, regA, ins.Val)
|
||||
}
|
||||
|
||||
func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) {
|
||||
// Guard against division or modulus by zero by terminating
|
||||
// the program, as the OS BPF VM does
|
||||
if regX == 0 {
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return aluOpCommon(ins.Op, regA, regX), true
|
||||
}
|
||||
|
||||
func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
|
||||
switch op {
|
||||
case ALUOpAdd:
|
||||
return regA + value
|
||||
case ALUOpSub:
|
||||
return regA - value
|
||||
case ALUOpMul:
|
||||
return regA * value
|
||||
case ALUOpDiv:
|
||||
// Division by zero not permitted by NewVM and aluOpX checks
|
||||
return regA / value
|
||||
case ALUOpOr:
|
||||
return regA | value
|
||||
case ALUOpAnd:
|
||||
return regA & value
|
||||
case ALUOpShiftLeft:
|
||||
return regA << value
|
||||
case ALUOpShiftRight:
|
||||
return regA >> value
|
||||
case ALUOpMod:
|
||||
// Modulus by zero not permitted by NewVM and aluOpX checks
|
||||
return regA % value
|
||||
case ALUOpXor:
|
||||
return regA ^ value
|
||||
default:
|
||||
return regA
|
||||
}
|
||||
}
|
||||
|
||||
func jumpIf(ins JumpIf, regA uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
|
||||
}
|
||||
|
||||
func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
|
||||
}
|
||||
|
||||
func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
|
||||
var ok bool
|
||||
|
||||
switch cond {
|
||||
case JumpEqual:
|
||||
ok = regA == value
|
||||
case JumpNotEqual:
|
||||
ok = regA != value
|
||||
case JumpGreaterThan:
|
||||
ok = regA > value
|
||||
case JumpLessThan:
|
||||
ok = regA < value
|
||||
case JumpGreaterOrEqual:
|
||||
ok = regA >= value
|
||||
case JumpLessOrEqual:
|
||||
ok = regA <= value
|
||||
case JumpBitsSet:
|
||||
ok = (regA & value) != 0
|
||||
case JumpBitsNotSet:
|
||||
ok = (regA & value) == 0
|
||||
}
|
||||
|
||||
if ok {
|
||||
return int(skipTrue)
|
||||
}
|
||||
|
||||
return int(skipFalse)
|
||||
}
|
||||
|
||||
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
size := int(ins.Size)
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = ins.Val
|
||||
case RegX:
|
||||
regX = ins.Val
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func loadExtension(ins LoadExtension, in []byte) uint32 {
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
return uint32(len(in))
|
||||
default:
|
||||
panic(fmt.Sprintf("unimplemented extension: %d", ins.Num))
|
||||
}
|
||||
}
|
||||
|
||||
func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
|
||||
offset := int(ins.Off) + int(regX)
|
||||
size := int(ins.Size)
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
|
||||
if !inBounds(len(in), offset, 0) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Mask off high 4 bits and multiply low 4 bits by 4
|
||||
return uint32(in[offset]&0x0f) * 4, true
|
||||
}
|
||||
|
||||
func inBounds(inLen int, offset int, size int) bool {
|
||||
return offset+size <= inLen
|
||||
}
|
||||
|
||||
func loadCommon(in []byte, offset int, size int) (uint32, bool) {
|
||||
if !inBounds(len(in), offset, size) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
return uint32(in[offset]), true
|
||||
case 2:
|
||||
return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true
|
||||
case 4:
|
||||
return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid load size: %d", size))
|
||||
}
|
||||
}
|
||||
|
||||
func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = regScratch[ins.N]
|
||||
case RegX:
|
||||
regX = regScratch[ins.N]
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 {
|
||||
switch ins.Src {
|
||||
case RegA:
|
||||
regScratch[ins.N] = regA
|
||||
case RegX:
|
||||
regScratch[ins.N] = regX
|
||||
}
|
||||
|
||||
return regScratch
|
||||
}
|
|
@ -1,726 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
func TestVMJumpOne(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.Jump{
|
||||
Skip: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpOutOfProgram(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.Jump{
|
||||
Skip: 1,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot jump 1 instructions; jumping past program bounds" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfTrueOutOfProgram(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
SkipTrue: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfFalseOutOfProgram(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
SkipFalse: 3,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXTrueOutOfProgram(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpEqual,
|
||||
SkipTrue: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXFalseOutOfProgram(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpEqual,
|
||||
SkipFalse: 3,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 1,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfNotEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpNotEqual,
|
||||
Val: 1,
|
||||
SkipFalse: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfGreaterThan(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpGreaterThan,
|
||||
Val: 0x00010202,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfLessThan(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpLessThan,
|
||||
Val: 0xff010203,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfGreaterOrEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpGreaterOrEqual,
|
||||
Val: 0x00010203,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfLessOrEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpLessOrEqual,
|
||||
Val: 0xff010203,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfBitsSet(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpBitsSet,
|
||||
Val: 0x1122,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 10,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0x02,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfBitsNotSet(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpBitsNotSet,
|
||||
Val: 0x1221,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 10,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0x02,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 1,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpEqual,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXNotEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 1,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpNotEqual,
|
||||
SkipFalse: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXGreaterThan(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0x00010202,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpGreaterThan,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXLessThan(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0xff010203,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpLessThan,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXGreaterOrEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0x00010203,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpGreaterOrEqual,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXLessOrEqual(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 4,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0xff010203,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpLessOrEqual,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 12,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 4, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXBitsSet(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0x1122,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpBitsSet,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 10,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0x02,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMJumpIfXBitsNotSet(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 0x1221,
|
||||
},
|
||||
bpf.JumpIfX{
|
||||
Cond: bpf.JumpBitsNotSet,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
bpf.RetConstant{
|
||||
Val: 10,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0x02,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
func TestVMLoadAbsoluteOffsetOutOfBounds(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 100,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadAbsoluteOffsetPlusSizeOutOfBounds(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadAbsoluteBadInstructionSize(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Size: 5,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid load byte length 0" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadConstantOK(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegX,
|
||||
Val: 9,
|
||||
},
|
||||
bpf.TXA{},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadIndirectOutOfBounds(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadIndirect{
|
||||
Off: 100,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadMemShiftOutOfBounds(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadMemShift{
|
||||
Off: 100,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
dhcp4Port = 53
|
||||
)
|
||||
|
||||
func TestVMLoadMemShiftLoadIndirectNoResult(t *testing.T) {
|
||||
vm, in, done := testDHCPv4(t)
|
||||
defer done()
|
||||
|
||||
// Append mostly empty UDP header with incorrect DHCPv4 port
|
||||
in = append(in, []byte{
|
||||
0, 0,
|
||||
0, dhcp4Port + 1,
|
||||
0, 0,
|
||||
0, 0,
|
||||
}...)
|
||||
|
||||
out, err := vm.Run(in)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 0, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadMemShiftLoadIndirectOK(t *testing.T) {
|
||||
vm, in, done := testDHCPv4(t)
|
||||
defer done()
|
||||
|
||||
// Append mostly empty UDP header with correct DHCPv4 port
|
||||
in = append(in, []byte{
|
||||
0, 0,
|
||||
0, dhcp4Port,
|
||||
0, 0,
|
||||
0, 0,
|
||||
}...)
|
||||
|
||||
out, err := vm.Run(in)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := len(in)-8, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func testDHCPv4(t *testing.T) (virtualMachine, []byte, func()) {
|
||||
// DHCPv4 test data courtesy of David Anderson:
|
||||
// https://github.com/google/netboot/blob/master/dhcp4/conn_linux.go#L59-L70
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
// Load IPv4 packet length
|
||||
bpf.LoadMemShift{Off: 8},
|
||||
// Get UDP dport
|
||||
bpf.LoadIndirect{Off: 8 + 2, Size: 2},
|
||||
// Correct dport?
|
||||
bpf.JumpIf{Cond: bpf.JumpEqual, Val: dhcp4Port, SkipFalse: 1},
|
||||
// Accept
|
||||
bpf.RetConstant{Val: 1500},
|
||||
// Ignore
|
||||
bpf.RetConstant{Val: 0},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
|
||||
// Minimal requirements to make a valid IPv4 header
|
||||
h := &ipv4.Header{
|
||||
Len: ipv4.HeaderLen,
|
||||
Src: net.IPv4(192, 168, 1, 1),
|
||||
Dst: net.IPv4(192, 168, 1, 2),
|
||||
}
|
||||
hb, err := h.Marshal()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal IPv4 header: %v", err)
|
||||
}
|
||||
|
||||
hb = append([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
}, hb...)
|
||||
|
||||
return vm, hb, done
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
func TestVMRetA(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
9,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMRetALargerThanInput(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 2,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 255,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMRetConstant(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.RetConstant{
|
||||
Val: 9,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 1, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMRetConstantLargerThanInput(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.RetConstant{
|
||||
Val: 16,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0, 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
func TestVMStoreScratchInvalidScratchRegisterTooSmall(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: -1,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid scratch slot -1" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMStoreScratchInvalidScratchRegisterTooLarge(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: 16,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid scratch slot 16" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMStoreScratchUnknownSourceRegister(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.StoreScratch{
|
||||
Src: 100,
|
||||
N: 0,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid source register 100" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadScratchInvalidScratchRegisterTooSmall(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegX,
|
||||
N: -1,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid scratch slot -1" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadScratchInvalidScratchRegisterTooLarge(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegX,
|
||||
N: 16,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid scratch slot 16" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMLoadScratchUnknownDestinationRegister(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadScratch{
|
||||
Dst: 100,
|
||||
N: 0,
|
||||
},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if errStr(err) != "assembling instruction 1: invalid target register 100" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMStoreScratchLoadScratchOneValue(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
// Load byte 255
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
// Copy to X and store in scratch[0]
|
||||
bpf.TAX{},
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegX,
|
||||
N: 0,
|
||||
},
|
||||
// Load byte 1
|
||||
bpf.LoadAbsolute{
|
||||
Off: 9,
|
||||
Size: 1,
|
||||
},
|
||||
// Overwrite 1 with 255 from scratch[0]
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegA,
|
||||
N: 0,
|
||||
},
|
||||
// Return 255
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
255, 1, 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 3, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMStoreScratchLoadScratchMultipleValues(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
// Load byte 10
|
||||
bpf.LoadAbsolute{
|
||||
Off: 8,
|
||||
Size: 1,
|
||||
},
|
||||
// Store in scratch[0]
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: 0,
|
||||
},
|
||||
// Load byte 20
|
||||
bpf.LoadAbsolute{
|
||||
Off: 9,
|
||||
Size: 1,
|
||||
},
|
||||
// Store in scratch[1]
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: 1,
|
||||
},
|
||||
// Load byte 30
|
||||
bpf.LoadAbsolute{
|
||||
Off: 10,
|
||||
Size: 1,
|
||||
},
|
||||
// Store in scratch[2]
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: 2,
|
||||
},
|
||||
// Load byte 1
|
||||
bpf.LoadAbsolute{
|
||||
Off: 11,
|
||||
Size: 1,
|
||||
},
|
||||
// Store in scratch[3]
|
||||
bpf.StoreScratch{
|
||||
Src: bpf.RegA,
|
||||
N: 3,
|
||||
},
|
||||
// Load in byte 10 to X
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegX,
|
||||
N: 0,
|
||||
},
|
||||
// Copy X -> A
|
||||
bpf.TXA{},
|
||||
// Verify value is 10
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 10,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
// Fail test if incorrect
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
// Load in byte 20 to A
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegA,
|
||||
N: 1,
|
||||
},
|
||||
// Verify value is 20
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 20,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
// Fail test if incorrect
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
// Load in byte 30 to A
|
||||
bpf.LoadScratch{
|
||||
Dst: bpf.RegA,
|
||||
N: 2,
|
||||
},
|
||||
// Verify value is 30
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: 30,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
// Fail test if incorrect
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
// Return first two bytes on success
|
||||
bpf.RetConstant{
|
||||
Val: 10,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load BPF program: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
out, err := vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
10, 20, 30, 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
if want, got := 2, out; want != got {
|
||||
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
|
||||
want, got)
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
var _ bpf.Instruction = unknown{}
|
||||
|
||||
type unknown struct{}
|
||||
|
||||
func (unknown) Assemble() (bpf.RawInstruction, error) {
|
||||
return bpf.RawInstruction{}, nil
|
||||
}
|
||||
|
||||
func TestVMUnknownInstruction(t *testing.T) {
|
||||
vm, done, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegA,
|
||||
Val: 100,
|
||||
},
|
||||
// Should terminate the program with an error immediately
|
||||
unknown{},
|
||||
bpf.RetA{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer done()
|
||||
|
||||
_, err = vm.Run([]byte{
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00,
|
||||
})
|
||||
if errStr(err) != "unknown Instruction at index 1: bpf_test.unknown" {
|
||||
t.Fatalf("unexpected error while running program: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMNoReturnInstruction(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{
|
||||
bpf.LoadConstant{
|
||||
Dst: bpf.RegA,
|
||||
Val: 1,
|
||||
},
|
||||
})
|
||||
if errStr(err) != "BPF program must end with RetA or RetConstant" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMNoInputInstructions(t *testing.T) {
|
||||
_, _, err := testVM(t, []bpf.Instruction{})
|
||||
if errStr(err) != "one or more Instructions must be specified" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ExampleNewVM demonstrates usage of a VM, using an Ethernet frame
|
||||
// as input and checking its EtherType to determine if it should be accepted.
|
||||
func ExampleNewVM() {
|
||||
// Offset | Length | Comment
|
||||
// -------------------------
|
||||
// 00 | 06 | Ethernet destination MAC address
|
||||
// 06 | 06 | Ethernet source MAC address
|
||||
// 12 | 02 | Ethernet EtherType
|
||||
const (
|
||||
etOff = 12
|
||||
etLen = 2
|
||||
|
||||
etARP = 0x0806
|
||||
)
|
||||
|
||||
// Set up a VM to filter traffic based on if its EtherType
|
||||
// matches the ARP EtherType.
|
||||
vm, err := bpf.NewVM([]bpf.Instruction{
|
||||
// Load EtherType value from Ethernet header
|
||||
bpf.LoadAbsolute{
|
||||
Off: etOff,
|
||||
Size: etLen,
|
||||
},
|
||||
// If EtherType is equal to the ARP EtherType, jump to allow
|
||||
// packet to be accepted
|
||||
bpf.JumpIf{
|
||||
Cond: bpf.JumpEqual,
|
||||
Val: etARP,
|
||||
SkipTrue: 1,
|
||||
},
|
||||
// EtherType does not match the ARP EtherType
|
||||
bpf.RetConstant{
|
||||
Val: 0,
|
||||
},
|
||||
// EtherType matches the ARP EtherType, accept up to 1500
|
||||
// bytes of packet
|
||||
bpf.RetConstant{
|
||||
Val: 1500,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load BPF program: %v", err))
|
||||
}
|
||||
|
||||
// Create an Ethernet frame with the ARP EtherType for testing
|
||||
frame := []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
|
||||
0x08, 0x06,
|
||||
// Payload omitted for brevity
|
||||
}
|
||||
|
||||
// Run our VM's BPF program using the Ethernet frame as input
|
||||
out, err := vm.Run(frame)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to accept Ethernet frame: %v", err))
|
||||
}
|
||||
|
||||
// BPF VM can return a byte count greater than the number of input
|
||||
// bytes, so trim the output to match the input byte length
|
||||
if out > len(frame) {
|
||||
out = len(frame)
|
||||
}
|
||||
|
||||
fmt.Printf("out: %d bytes", out)
|
||||
|
||||
// Output:
|
||||
// out: 14 bytes
|
||||
}
|
||||
|
||||
// errStr returns the string representation of an error, or
|
||||
// "<nil>" if it is nil.
|
||||
func errStr(err error) string {
|
||||
if err == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
return err.Error()
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
issuerepo: golang/go
|
|
@ -1,7 +0,0 @@
|
|||
module golang.org/x/net
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
|
||||
golang.org/x/text v0.3.0
|
||||
)
|
|
@ -1,6 +0,0 @@
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
@ -1,223 +0,0 @@
|
|||
// go generate gen.go
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
|
||||
package iana // import "golang.org/x/net/internal/iana"
|
||||
|
||||
// Differentiated Services Field Codepoints (DSCP), Updated: 2018-05-04
|
||||
const (
|
||||
DiffServCS0 = 0x00 // CS0
|
||||
DiffServCS1 = 0x20 // CS1
|
||||
DiffServCS2 = 0x40 // CS2
|
||||
DiffServCS3 = 0x60 // CS3
|
||||
DiffServCS4 = 0x80 // CS4
|
||||
DiffServCS5 = 0xa0 // CS5
|
||||
DiffServCS6 = 0xc0 // CS6
|
||||
DiffServCS7 = 0xe0 // CS7
|
||||
DiffServAF11 = 0x28 // AF11
|
||||
DiffServAF12 = 0x30 // AF12
|
||||
DiffServAF13 = 0x38 // AF13
|
||||
DiffServAF21 = 0x48 // AF21
|
||||
DiffServAF22 = 0x50 // AF22
|
||||
DiffServAF23 = 0x58 // AF23
|
||||
DiffServAF31 = 0x68 // AF31
|
||||
DiffServAF32 = 0x70 // AF32
|
||||
DiffServAF33 = 0x78 // AF33
|
||||
DiffServAF41 = 0x88 // AF41
|
||||
DiffServAF42 = 0x90 // AF42
|
||||
DiffServAF43 = 0x98 // AF43
|
||||
DiffServEF = 0xb8 // EF
|
||||
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
||||
NotECNTransport = 0x00 // Not-ECT (Not ECN-Capable Transport)
|
||||
ECNTransport1 = 0x01 // ECT(1) (ECN-Capable Transport(1))
|
||||
ECNTransport0 = 0x02 // ECT(0) (ECN-Capable Transport(0))
|
||||
CongestionExperienced = 0x03 // CE (Congestion Experienced)
|
||||
)
|
||||
|
||||
// Protocol Numbers, Updated: 2017-10-13
|
||||
const (
|
||||
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
|
||||
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
||||
ProtocolICMP = 1 // Internet Control Message
|
||||
ProtocolIGMP = 2 // Internet Group Management
|
||||
ProtocolGGP = 3 // Gateway-to-Gateway
|
||||
ProtocolIPv4 = 4 // IPv4 encapsulation
|
||||
ProtocolST = 5 // Stream
|
||||
ProtocolTCP = 6 // Transmission Control
|
||||
ProtocolCBT = 7 // CBT
|
||||
ProtocolEGP = 8 // Exterior Gateway Protocol
|
||||
ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||
ProtocolBBNRCCMON = 10 // BBN RCC Monitoring
|
||||
ProtocolNVPII = 11 // Network Voice Protocol
|
||||
ProtocolPUP = 12 // PUP
|
||||
ProtocolEMCON = 14 // EMCON
|
||||
ProtocolXNET = 15 // Cross Net Debugger
|
||||
ProtocolCHAOS = 16 // Chaos
|
||||
ProtocolUDP = 17 // User Datagram
|
||||
ProtocolMUX = 18 // Multiplexing
|
||||
ProtocolDCNMEAS = 19 // DCN Measurement Subsystems
|
||||
ProtocolHMP = 20 // Host Monitoring
|
||||
ProtocolPRM = 21 // Packet Radio Measurement
|
||||
ProtocolXNSIDP = 22 // XEROX NS IDP
|
||||
ProtocolTRUNK1 = 23 // Trunk-1
|
||||
ProtocolTRUNK2 = 24 // Trunk-2
|
||||
ProtocolLEAF1 = 25 // Leaf-1
|
||||
ProtocolLEAF2 = 26 // Leaf-2
|
||||
ProtocolRDP = 27 // Reliable Data Protocol
|
||||
ProtocolIRTP = 28 // Internet Reliable Transaction
|
||||
ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
|
||||
ProtocolNETBLT = 30 // Bulk Data Transfer Protocol
|
||||
ProtocolMFENSP = 31 // MFE Network Services Protocol
|
||||
ProtocolMERITINP = 32 // MERIT Internodal Protocol
|
||||
ProtocolDCCP = 33 // Datagram Congestion Control Protocol
|
||||
Protocol3PC = 34 // Third Party Connect Protocol
|
||||
ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
|
||||
ProtocolXTP = 36 // XTP
|
||||
ProtocolDDP = 37 // Datagram Delivery Protocol
|
||||
ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
|
||||
ProtocolTPPP = 39 // TP++ Transport Protocol
|
||||
ProtocolIL = 40 // IL Transport Protocol
|
||||
ProtocolIPv6 = 41 // IPv6 encapsulation
|
||||
ProtocolSDRP = 42 // Source Demand Routing Protocol
|
||||
ProtocolIPv6Route = 43 // Routing Header for IPv6
|
||||
ProtocolIPv6Frag = 44 // Fragment Header for IPv6
|
||||
ProtocolIDRP = 45 // Inter-Domain Routing Protocol
|
||||
ProtocolRSVP = 46 // Reservation Protocol
|
||||
ProtocolGRE = 47 // Generic Routing Encapsulation
|
||||
ProtocolDSR = 48 // Dynamic Source Routing Protocol
|
||||
ProtocolBNA = 49 // BNA
|
||||
ProtocolESP = 50 // Encap Security Payload
|
||||
ProtocolAH = 51 // Authentication Header
|
||||
ProtocolINLSP = 52 // Integrated Net Layer Security TUBA
|
||||
ProtocolNARP = 54 // NBMA Address Resolution Protocol
|
||||
ProtocolMOBILE = 55 // IP Mobility
|
||||
ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
|
||||
ProtocolSKIP = 57 // SKIP
|
||||
ProtocolIPv6ICMP = 58 // ICMP for IPv6
|
||||
ProtocolIPv6NoNxt = 59 // No Next Header for IPv6
|
||||
ProtocolIPv6Opts = 60 // Destination Options for IPv6
|
||||
ProtocolCFTP = 62 // CFTP
|
||||
ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
|
||||
ProtocolKRYPTOLAN = 65 // Kryptolan
|
||||
ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
|
||||
ProtocolIPPC = 67 // Internet Pluribus Packet Core
|
||||
ProtocolSATMON = 69 // SATNET Monitoring
|
||||
ProtocolVISA = 70 // VISA Protocol
|
||||
ProtocolIPCV = 71 // Internet Packet Core Utility
|
||||
ProtocolCPNX = 72 // Computer Protocol Network Executive
|
||||
ProtocolCPHB = 73 // Computer Protocol Heart Beat
|
||||
ProtocolWSN = 74 // Wang Span Network
|
||||
ProtocolPVP = 75 // Packet Video Protocol
|
||||
ProtocolBRSATMON = 76 // Backroom SATNET Monitoring
|
||||
ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
|
||||
ProtocolWBMON = 78 // WIDEBAND Monitoring
|
||||
ProtocolWBEXPAK = 79 // WIDEBAND EXPAK
|
||||
ProtocolISOIP = 80 // ISO Internet Protocol
|
||||
ProtocolVMTP = 81 // VMTP
|
||||
ProtocolSECUREVMTP = 82 // SECURE-VMTP
|
||||
ProtocolVINES = 83 // VINES
|
||||
ProtocolTTP = 84 // Transaction Transport Protocol
|
||||
ProtocolIPTM = 84 // Internet Protocol Traffic Manager
|
||||
ProtocolNSFNETIGP = 85 // NSFNET-IGP
|
||||
ProtocolDGP = 86 // Dissimilar Gateway Protocol
|
||||
ProtocolTCF = 87 // TCF
|
||||
ProtocolEIGRP = 88 // EIGRP
|
||||
ProtocolOSPFIGP = 89 // OSPFIGP
|
||||
ProtocolSpriteRPC = 90 // Sprite RPC Protocol
|
||||
ProtocolLARP = 91 // Locus Address Resolution Protocol
|
||||
ProtocolMTP = 92 // Multicast Transport Protocol
|
||||
ProtocolAX25 = 93 // AX.25 Frames
|
||||
ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
|
||||
ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
|
||||
ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
|
||||
ProtocolENCAP = 98 // Encapsulation Header
|
||||
ProtocolGMTP = 100 // GMTP
|
||||
ProtocolIFMP = 101 // Ipsilon Flow Management Protocol
|
||||
ProtocolPNNI = 102 // PNNI over IP
|
||||
ProtocolPIM = 103 // Protocol Independent Multicast
|
||||
ProtocolARIS = 104 // ARIS
|
||||
ProtocolSCPS = 105 // SCPS
|
||||
ProtocolQNX = 106 // QNX
|
||||
ProtocolAN = 107 // Active Networks
|
||||
ProtocolIPComp = 108 // IP Payload Compression Protocol
|
||||
ProtocolSNP = 109 // Sitara Networks Protocol
|
||||
ProtocolCompaqPeer = 110 // Compaq Peer Protocol
|
||||
ProtocolIPXinIP = 111 // IPX in IP
|
||||
ProtocolVRRP = 112 // Virtual Router Redundancy Protocol
|
||||
ProtocolPGM = 113 // PGM Reliable Transport Protocol
|
||||
ProtocolL2TP = 115 // Layer Two Tunneling Protocol
|
||||
ProtocolDDX = 116 // D-II Data Exchange (DDX)
|
||||
ProtocolIATP = 117 // Interactive Agent Transfer Protocol
|
||||
ProtocolSTP = 118 // Schedule Transfer Protocol
|
||||
ProtocolSRP = 119 // SpectraLink Radio Protocol
|
||||
ProtocolUTI = 120 // UTI
|
||||
ProtocolSMP = 121 // Simple Message Protocol
|
||||
ProtocolPTP = 123 // Performance Transparency Protocol
|
||||
ProtocolISIS = 124 // ISIS over IPv4
|
||||
ProtocolFIRE = 125 // FIRE
|
||||
ProtocolCRTP = 126 // Combat Radio Transport Protocol
|
||||
ProtocolCRUDP = 127 // Combat Radio User Datagram
|
||||
ProtocolSSCOPMCE = 128 // SSCOPMCE
|
||||
ProtocolIPLT = 129 // IPLT
|
||||
ProtocolSPS = 130 // Secure Packet Shield
|
||||
ProtocolPIPE = 131 // Private IP Encapsulation within IP
|
||||
ProtocolSCTP = 132 // Stream Control Transmission Protocol
|
||||
ProtocolFC = 133 // Fibre Channel
|
||||
ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
|
||||
ProtocolMobilityHeader = 135 // Mobility Header
|
||||
ProtocolUDPLite = 136 // UDPLite
|
||||
ProtocolMPLSinIP = 137 // MPLS-in-IP
|
||||
ProtocolMANET = 138 // MANET Protocols
|
||||
ProtocolHIP = 139 // Host Identity Protocol
|
||||
ProtocolShim6 = 140 // Shim6 Protocol
|
||||
ProtocolWESP = 141 // Wrapped Encapsulating Security Payload
|
||||
ProtocolROHC = 142 // Robust Header Compression
|
||||
ProtocolReserved = 255 // Reserved
|
||||
)
|
||||
|
||||
// Address Family Numbers, Updated: 2018-04-02
|
||||
const (
|
||||
AddrFamilyIPv4 = 1 // IP (IP version 4)
|
||||
AddrFamilyIPv6 = 2 // IP6 (IP version 6)
|
||||
AddrFamilyNSAP = 3 // NSAP
|
||||
AddrFamilyHDLC = 4 // HDLC (8-bit multidrop)
|
||||
AddrFamilyBBN1822 = 5 // BBN 1822
|
||||
AddrFamily802 = 6 // 802 (includes all 802 media plus Ethernet "canonical format")
|
||||
AddrFamilyE163 = 7 // E.163
|
||||
AddrFamilyE164 = 8 // E.164 (SMDS, Frame Relay, ATM)
|
||||
AddrFamilyF69 = 9 // F.69 (Telex)
|
||||
AddrFamilyX121 = 10 // X.121 (X.25, Frame Relay)
|
||||
AddrFamilyIPX = 11 // IPX
|
||||
AddrFamilyAppletalk = 12 // Appletalk
|
||||
AddrFamilyDecnetIV = 13 // Decnet IV
|
||||
AddrFamilyBanyanVines = 14 // Banyan Vines
|
||||
AddrFamilyE164withSubaddress = 15 // E.164 with NSAP format subaddress
|
||||
AddrFamilyDNS = 16 // DNS (Domain Name System)
|
||||
AddrFamilyDistinguishedName = 17 // Distinguished Name
|
||||
AddrFamilyASNumber = 18 // AS Number
|
||||
AddrFamilyXTPoverIPv4 = 19 // XTP over IP version 4
|
||||
AddrFamilyXTPoverIPv6 = 20 // XTP over IP version 6
|
||||
AddrFamilyXTPnativemodeXTP = 21 // XTP native mode XTP
|
||||
AddrFamilyFibreChannelWorldWidePortName = 22 // Fibre Channel World-Wide Port Name
|
||||
AddrFamilyFibreChannelWorldWideNodeName = 23 // Fibre Channel World-Wide Node Name
|
||||
AddrFamilyGWID = 24 // GWID
|
||||
AddrFamilyL2VPN = 25 // AFI for L2VPN information
|
||||
AddrFamilyMPLSTPSectionEndpointID = 26 // MPLS-TP Section Endpoint Identifier
|
||||
AddrFamilyMPLSTPLSPEndpointID = 27 // MPLS-TP LSP Endpoint Identifier
|
||||
AddrFamilyMPLSTPPseudowireEndpointID = 28 // MPLS-TP Pseudowire Endpoint Identifier
|
||||
AddrFamilyMTIPv4 = 29 // MT IP: Multi-Topology IP version 4
|
||||
AddrFamilyMTIPv6 = 30 // MT IPv6: Multi-Topology IP version 6
|
||||
AddrFamilyEIGRPCommonServiceFamily = 16384 // EIGRP Common Service Family
|
||||
AddrFamilyEIGRPIPv4ServiceFamily = 16385 // EIGRP IPv4 Service Family
|
||||
AddrFamilyEIGRPIPv6ServiceFamily = 16386 // EIGRP IPv6 Service Family
|
||||
AddrFamilyLISPCanonicalAddressFormat = 16387 // LISP Canonical Address Format (LCAF)
|
||||
AddrFamilyBGPLS = 16388 // BGP-LS
|
||||
AddrFamily48bitMAC = 16389 // 48-bit MAC
|
||||
AddrFamily64bitMAC = 16390 // 64-bit MAC
|
||||
AddrFamilyOUI = 16391 // OUI
|
||||
AddrFamilyMACFinal24bits = 16392 // MAC/24
|
||||
AddrFamilyMACFinal40bits = 16393 // MAC/40
|
||||
AddrFamilyIPv6Initial64bits = 16394 // IPv6/64
|
||||
AddrFamilyRBridgePortID = 16395 // RBridge Port ID
|
||||
AddrFamilyTRILLNickname = 16396 // TRILL Nickname
|
||||
)
|
|
@ -1,383 +0,0 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// This program generates internet protocol constants and tables by
|
||||
// reading IANA protocol registries.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var registries = []struct {
|
||||
url string
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
parseDSCPRegistry,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
||||
parseAddrFamilyNumbers,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||
fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
|
||||
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := r.parse(&bb, resp.Body); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(&bb, "\n")
|
||||
}
|
||||
b, err := format.Source(bb.Bytes())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var dr dscpRegistry
|
||||
if err := dec.Decode(&dr); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, dr := range dr.escapeDSCP() {
|
||||
fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
||||
}
|
||||
for _, er := range dr.escapeECN() {
|
||||
fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value)
|
||||
fmt.Fprintf(w, "// %s\n", er.OrigDescr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type dscpRegistry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Records []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
type canonDSCPRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord {
|
||||
var drs []canonDSCPRecord
|
||||
for _, preg := range drr.Registries {
|
||||
if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") {
|
||||
continue
|
||||
}
|
||||
for _, reg := range preg.Registries {
|
||||
if !strings.Contains(reg.Title, "Pool 1 Codepoints") {
|
||||
continue
|
||||
}
|
||||
drs = make([]canonDSCPRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, dr := range reg.Records {
|
||||
s := strings.TrimSpace(dr.Name)
|
||||
drs[i].OrigName = s
|
||||
drs[i].Name = sr.Replace(s)
|
||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
drs[i].Value = int(n) << 2
|
||||
}
|
||||
}
|
||||
}
|
||||
return drs
|
||||
}
|
||||
|
||||
type canonECNRecord struct {
|
||||
OrigDescr string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escapeECN() []canonECNRecord {
|
||||
var ers []canonECNRecord
|
||||
for _, reg := range drr.Registries {
|
||||
if !strings.Contains(reg.Title, "ECN Field") {
|
||||
continue
|
||||
}
|
||||
ers = make([]canonECNRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Capable", "",
|
||||
"Not-ECT", "",
|
||||
"ECT(1)", "",
|
||||
"ECT(0)", "",
|
||||
"CE", "",
|
||||
"(", "",
|
||||
")", "",
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, er := range reg.Records {
|
||||
s := strings.TrimSpace(er.Descr)
|
||||
ers[i].OrigDescr = s
|
||||
ss := strings.Split(s, " ")
|
||||
if len(ss) > 1 {
|
||||
ers[i].Descr = strings.Join(ss[1:], " ")
|
||||
} else {
|
||||
ers[i].Descr = ss[0]
|
||||
}
|
||||
ers[i].Descr = sr.Replace(er.Descr)
|
||||
n, err := strconv.ParseUint(er.Value, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ers[i].Value = int(n)
|
||||
}
|
||||
}
|
||||
return ers
|
||||
}
|
||||
|
||||
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var pn protocolNumbers
|
||||
if err := dec.Decode(&pn); err != nil {
|
||||
return err
|
||||
}
|
||||
prs := pn.escape()
|
||||
prs = append([]canonProtocolRecord{{
|
||||
Name: "IP",
|
||||
Descr: "IPv4 encapsulation, pseudo protocol number",
|
||||
Value: 0,
|
||||
}}, prs...)
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, pr := range prs {
|
||||
if pr.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
|
||||
s := pr.Descr
|
||||
if s == "" {
|
||||
s = pr.OrigName
|
||||
}
|
||||
fmt.Fprintf(w, "// %s\n", s)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type protocolNumbers struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Note string `xml:"registry>note"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Name string `xml:"name"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonProtocolRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
||||
prs := make([]canonProtocolRecord, len(pn.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"-in-", "in",
|
||||
"-within-", "within",
|
||||
"-over-", "over",
|
||||
"+", "P",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, pr := range pn.Records {
|
||||
if strings.Contains(pr.Name, "Deprecated") ||
|
||||
strings.Contains(pr.Name, "deprecated") {
|
||||
continue
|
||||
}
|
||||
prs[i].OrigName = pr.Name
|
||||
s := strings.TrimSpace(pr.Name)
|
||||
switch pr.Name {
|
||||
case "ISIS over IPv4":
|
||||
prs[i].Name = "ISIS"
|
||||
case "manet":
|
||||
prs[i].Name = "MANET"
|
||||
default:
|
||||
prs[i].Name = sr.Replace(s)
|
||||
}
|
||||
ss := strings.Split(pr.Descr, "\n")
|
||||
for i := range ss {
|
||||
ss[i] = strings.TrimSpace(ss[i])
|
||||
}
|
||||
if len(ss) > 1 {
|
||||
prs[i].Descr = strings.Join(ss, " ")
|
||||
} else {
|
||||
prs[i].Descr = ss[0]
|
||||
}
|
||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||
}
|
||||
return prs
|
||||
}
|
||||
|
||||
func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var afn addrFamilylNumbers
|
||||
if err := dec.Decode(&afn); err != nil {
|
||||
return err
|
||||
}
|
||||
afrs := afn.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, afr := range afrs {
|
||||
if afr.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", afr.Descr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type addrFamilylNumbers struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Note string `xml:"registry>note"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonAddrFamilyRecord struct {
|
||||
Name string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord {
|
||||
afrs := make([]canonAddrFamilyRecord, len(afn.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"IP version 4", "IPv4",
|
||||
"IP version 6", "IPv6",
|
||||
"Identifier", "ID",
|
||||
"-", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, afr := range afn.Records {
|
||||
if strings.Contains(afr.Descr, "Unassigned") ||
|
||||
strings.Contains(afr.Descr, "Reserved") {
|
||||
continue
|
||||
}
|
||||
afrs[i].Descr = afr.Descr
|
||||
s := strings.TrimSpace(afr.Descr)
|
||||
switch s {
|
||||
case "IP (IP version 4)":
|
||||
afrs[i].Name = "IPv4"
|
||||
case "IP6 (IP version 6)":
|
||||
afrs[i].Name = "IPv6"
|
||||
case "AFI for L2VPN information":
|
||||
afrs[i].Name = "L2VPN"
|
||||
case "E.164 with NSAP format subaddress":
|
||||
afrs[i].Name = "E164withSubaddress"
|
||||
case "MT IP: Multi-Topology IP version 4":
|
||||
afrs[i].Name = "MTIPv4"
|
||||
case "MAC/24":
|
||||
afrs[i].Name = "MACFinal24bits"
|
||||
case "MAC/40":
|
||||
afrs[i].Name = "MACFinal40bits"
|
||||
case "IPv6/64":
|
||||
afrs[i].Name = "IPv6Initial64bits"
|
||||
default:
|
||||
n := strings.Index(s, "(")
|
||||
if n > 0 {
|
||||
s = s[:n]
|
||||
}
|
||||
n = strings.Index(s, ":")
|
||||
if n > 0 {
|
||||
s = s[:n]
|
||||
}
|
||||
afrs[i].Name = sr.Replace(s)
|
||||
}
|
||||
afrs[i].Value, _ = strconv.Atoi(afr.Value)
|
||||
}
|
||||
return afrs
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) len() int { return int(h.Len) }
|
||||
func (h *cmsghdr) lvl() int { return int(h.Level) }
|
||||
func (h *cmsghdr) typ() int { return int(h.Type) }
|
|
@ -1,13 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||
h.Len = uint32(l)
|
||||
h.Level = int32(lvl)
|
||||
h.Type = int32(typ)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2017 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 arm mips mipsle 386
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||
h.Len = uint32(l)
|
||||
h.Level = int32(lvl)
|
||||
h.Type = int32(typ)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||
h.Len = uint64(l)
|
||||
h.Level = int32(lvl)
|
||||
h.Type = int32(typ)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2017 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 amd64
|
||||
// +build solaris
|
||||
|
||||
package socket
|
||||
|
||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||
h.Len = uint32(l)
|
||||
h.Level = int32(lvl)
|
||||
h.Type = int32(typ)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2017 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package socket
|
||||
|
||||
type cmsghdr struct{}
|
||||
|
||||
const sizeofCmsghdr = 0
|
||||
|
||||
func (h *cmsghdr) len() int { return 0 }
|
||||
func (h *cmsghdr) lvl() int { return 0 }
|
||||
func (h *cmsghdr) typ() int { return 0 }
|
||||
|
||||
func (h *cmsghdr) set(l, lvl, typ int) {}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2019 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type mmsghdr C.struct_mmsghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/socket.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type mmsghdr C.struct_mmsghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type mmsghdr C.struct_mmsghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
|
@ -1,7 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// +build darwin,go1.12
|
||||
|
||||
// This exists solely so we can linkname in symbols from syscall.
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package socket
|
||||
|
||||
import "syscall"
|
||||
|
||||
var (
|
||||
errEAGAIN error = syscall.EAGAIN
|
||||
errEINVAL error = syscall.EINVAL
|
||||
errENOENT error = syscall.ENOENT
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent allocations
|
||||
// at runtime.
|
||||
func errnoErr(errno syscall.Errno) error {
|
||||
switch errno {
|
||||
case 0:
|
||||
return nil
|
||||
case syscall.EAGAIN:
|
||||
return errEAGAIN
|
||||
case syscall.EINVAL:
|
||||
return errEINVAL
|
||||
case syscall.ENOENT:
|
||||
return errENOENT
|
||||
}
|
||||
return errno
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
import "syscall"
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.ERROR_IO_PENDING
|
||||
errEINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent allocations
|
||||
// at runtime.
|
||||
func errnoErr(errno syscall.Errno) error {
|
||||
switch errno {
|
||||
case 0:
|
||||
return nil
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
case syscall.EINVAL:
|
||||
return errEINVAL
|
||||
}
|
||||
return errno
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2017 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 arm mips mipsle 386
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (v *iovec) set(b []byte) {
|
||||
l := len(b)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
v.Base = (*byte)(unsafe.Pointer(&b[0]))
|
||||
v.Len = uint32(l)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (v *iovec) set(b []byte) {
|
||||
l := len(b)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
v.Base = (*byte)(unsafe.Pointer(&b[0]))
|
||||
v.Len = uint64(l)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2017 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 amd64
|
||||
// +build solaris
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (v *iovec) set(b []byte) {
|
||||
l := len(b)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
v.Base = (*int8)(unsafe.Pointer(&b[0]))
|
||||
v.Len = uint64(l)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2017 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package socket
|
||||
|
||||
type iovec struct{}
|
||||
|
||||
func (v *iovec) set(b []byte) {}
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2017 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 !aix,!linux,!netbsd
|
||||
|
||||
package socket
|
||||
|
||||
import "net"
|
||||
|
||||
type mmsghdr struct{}
|
||||
|
||||
type mmsghdrs []mmsghdr
|
||||
|
||||
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
|
||||
return nil
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright 2017 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 aix linux netbsd
|
||||
|
||||
package socket
|
||||
|
||||
import "net"
|
||||
|
||||
type mmsghdrs []mmsghdr
|
||||
|
||||
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
|
||||
for i := range hs {
|
||||
vs := make([]iovec, len(ms[i].Buffers))
|
||||
var sa []byte
|
||||
if parseFn != nil {
|
||||
sa = make([]byte, sizeofSockaddrInet6)
|
||||
}
|
||||
if marshalFn != nil {
|
||||
sa = marshalFn(ms[i].Addr)
|
||||
}
|
||||
hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
|
||||
for i := range hs {
|
||||
ms[i].N = int(hs[i].Len)
|
||||
ms[i].NN = hs[i].Hdr.controllen()
|
||||
ms[i].Flags = hs[i].Hdr.flags()
|
||||
if parseFn != nil {
|
||||
var err error
|
||||
ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
|
||||
for i := range vs {
|
||||
vs[i].set(bs[i])
|
||||
}
|
||||
h.setIov(vs)
|
||||
if len(oob) > 0 {
|
||||
h.Control = (*byte)(unsafe.Pointer(&oob[0]))
|
||||
h.Controllen = uint32(len(oob))
|
||||
}
|
||||
if sa != nil {
|
||||
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
|
||||
h.Namelen = uint32(len(sa))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *msghdr) name() []byte {
|
||||
if h.Name != nil && h.Namelen > 0 {
|
||||
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *msghdr) controllen() int {
|
||||
return int(h.Controllen)
|
||||
}
|
||||
|
||||
func (h *msghdr) flags() int {
|
||||
return int(h.Flags)
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd netbsd
|
||||
|
||||
package socket
|
||||
|
||||
func (h *msghdr) setIov(vs []iovec) {
|
||||
l := len(vs)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
h.Iov = &vs[0]
|
||||
h.Iovlen = int32(l)
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
|
||||
for i := range vs {
|
||||
vs[i].set(bs[i])
|
||||
}
|
||||
h.setIov(vs)
|
||||
if len(oob) > 0 {
|
||||
h.setControl(oob)
|
||||
}
|
||||
if sa != nil {
|
||||
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
|
||||
h.Namelen = uint32(len(sa))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *msghdr) name() []byte {
|
||||
if h.Name != nil && h.Namelen > 0 {
|
||||
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *msghdr) controllen() int {
|
||||
return int(h.Controllen)
|
||||
}
|
||||
|
||||
func (h *msghdr) flags() int {
|
||||
return int(h.Flags)
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2017 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 arm mips mipsle 386
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (h *msghdr) setIov(vs []iovec) {
|
||||
l := len(vs)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
h.Iov = &vs[0]
|
||||
h.Iovlen = uint32(l)
|
||||
}
|
||||
|
||||
func (h *msghdr) setControl(b []byte) {
|
||||
h.Control = (*byte)(unsafe.Pointer(&b[0]))
|
||||
h.Controllen = uint32(len(b))
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (h *msghdr) setIov(vs []iovec) {
|
||||
l := len(vs)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
h.Iov = &vs[0]
|
||||
h.Iovlen = uint64(l)
|
||||
}
|
||||
|
||||
func (h *msghdr) setControl(b []byte) {
|
||||
h.Control = (*byte)(unsafe.Pointer(&b[0]))
|
||||
h.Controllen = uint64(len(b))
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
func (h *msghdr) setIov(vs []iovec) {
|
||||
l := len(vs)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
h.Iov = &vs[0]
|
||||
h.Iovlen = uint32(l)
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2017 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 amd64
|
||||
// +build solaris
|
||||
|
||||
package socket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
|
||||
for i := range vs {
|
||||
vs[i].set(bs[i])
|
||||
}
|
||||
if len(vs) > 0 {
|
||||
h.Iov = &vs[0]
|
||||
h.Iovlen = int32(len(vs))
|
||||
}
|
||||
if len(oob) > 0 {
|
||||
h.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
|
||||
h.Accrightslen = int32(len(oob))
|
||||
}
|
||||
if sa != nil {
|
||||
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
|
||||
h.Namelen = uint32(len(sa))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *msghdr) controllen() int {
|
||||
return int(h.Accrightslen)
|
||||
}
|
||||
|
||||
func (h *msghdr) flags() int {
|
||||
return int(NativeEndian.Uint32(h.Pad_cgo_2[:]))
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2017 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package socket
|
||||
|
||||
type msghdr struct{}
|
||||
|
||||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {}
|
||||
func (h *msghdr) name() []byte { return nil }
|
||||
func (h *msghdr) controllen() int { return 0 }
|
||||
func (h *msghdr) flags() int { return 0 }
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// A Conn represents a raw connection.
|
||||
type Conn struct {
|
||||
network string
|
||||
c syscall.RawConn
|
||||
}
|
||||
|
||||
// NewConn returns a new raw connection.
|
||||
func NewConn(c net.Conn) (*Conn, error) {
|
||||
var err error
|
||||
var cc Conn
|
||||
switch c := c.(type) {
|
||||
case *net.TCPConn:
|
||||
cc.network = "tcp"
|
||||
cc.c, err = c.SyscallConn()
|
||||
case *net.UDPConn:
|
||||
cc.network = "udp"
|
||||
cc.c, err = c.SyscallConn()
|
||||
case *net.IPConn:
|
||||
cc.network = "ip"
|
||||
cc.c, err = c.SyscallConn()
|
||||
default:
|
||||
return nil, errors.New("unknown connection type")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cc, nil
|
||||
}
|
||||
|
||||
func (o *Option) get(c *Conn, b []byte) (int, error) {
|
||||
var operr error
|
||||
var n int
|
||||
fn := func(s uintptr) {
|
||||
n, operr = getsockopt(s, o.Level, o.Name, b)
|
||||
}
|
||||
if err := c.c.Control(fn); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n, os.NewSyscallError("getsockopt", operr)
|
||||
}
|
||||
|
||||
func (o *Option) set(c *Conn, b []byte) error {
|
||||
var operr error
|
||||
fn := func(s uintptr) {
|
||||
operr = setsockopt(s, o.Level, o.Name, b)
|
||||
}
|
||||
if err := c.c.Control(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.NewSyscallError("setsockopt", operr)
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var parseFn func([]byte, string) (net.Addr, error)
|
||||
if c.network != "tcp" {
|
||||
parseFn = parseInetAddr
|
||||
}
|
||||
if err := hs.pack(ms, parseFn, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var operr error
|
||||
var n int
|
||||
fn := func(s uintptr) bool {
|
||||
n, operr = recvmmsg(s, hs, flags)
|
||||
if operr == syscall.EAGAIN {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err := c.c.Read(fn); err != nil {
|
||||
return n, err
|
||||
}
|
||||
if operr != nil {
|
||||
return n, os.NewSyscallError("recvmmsg", operr)
|
||||
}
|
||||
if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var marshalFn func(net.Addr) []byte
|
||||
if c.network != "tcp" {
|
||||
marshalFn = marshalInetAddr
|
||||
}
|
||||
if err := hs.pack(ms, nil, marshalFn); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var operr error
|
||||
var n int
|
||||
fn := func(s uintptr) bool {
|
||||
n, operr = sendmmsg(s, hs, flags)
|
||||
if operr == syscall.EAGAIN {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err := c.c.Write(fn); err != nil {
|
||||
return n, err
|
||||
}
|
||||
if operr != nil {
|
||||
return n, os.NewSyscallError("sendmmsg", operr)
|
||||
}
|
||||
if err := hs[:n].unpack(ms[:n], nil, ""); err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
if c.network != "tcp" {
|
||||
sa = make([]byte, sizeofSockaddrInet6)
|
||||
}
|
||||
h.pack(vs, m.Buffers, m.OOB, sa)
|
||||
var operr error
|
||||
var n int
|
||||
fn := func(s uintptr) bool {
|
||||
n, operr = recvmsg(s, &h, flags)
|
||||
if operr == syscall.EAGAIN {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err := c.c.Read(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
if operr != nil {
|
||||
return os.NewSyscallError("recvmsg", operr)
|
||||
}
|
||||
if c.network != "tcp" {
|
||||
var err error
|
||||
m.Addr, err = parseInetAddr(sa[:], c.network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m.N = n
|
||||
m.NN = h.controllen()
|
||||
m.Flags = h.flags()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsg(m *Message, flags int) error {
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
if m.Addr != nil {
|
||||
sa = marshalInetAddr(m.Addr)
|
||||
}
|
||||
h.pack(vs, m.Buffers, m.OOB, sa)
|
||||
var operr error
|
||||
var n int
|
||||
fn := func(s uintptr) bool {
|
||||
n, operr = sendmsg(s, &h, flags)
|
||||
if operr == syscall.EAGAIN {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err := c.c.Write(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
if operr != nil {
|
||||
return os.NewSyscallError("sendmsg", operr)
|
||||
}
|
||||
m.N = n
|
||||
m.NN = len(m.OOB)
|
||||
return nil
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||
return 0, errNotImplemented
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||
return 0, errNotImplemented
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2017 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
|
||||
|
||||
package socket
|
||||
|
||||
func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsg(m *Message, flags int) error {
|
||||
return errNotImplemented
|
||||
}
|
|
@ -1,288 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socket provides a portable interface for socket system
|
||||
// calls.
|
||||
package socket // import "golang.org/x/net/internal/socket"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
|
||||
// An Option represents a sticky socket option.
|
||||
type Option struct {
|
||||
Level int // level
|
||||
Name int // name; must be equal or greater than 1
|
||||
Len int // length of value in bytes; must be equal or greater than 1
|
||||
}
|
||||
|
||||
// Get reads a value for the option from the kernel.
|
||||
// It returns the number of bytes written into b.
|
||||
func (o *Option) Get(c *Conn, b []byte) (int, error) {
|
||||
if o.Name < 1 || o.Len < 1 {
|
||||
return 0, errors.New("invalid option")
|
||||
}
|
||||
if len(b) < o.Len {
|
||||
return 0, errors.New("short buffer")
|
||||
}
|
||||
return o.get(c, b)
|
||||
}
|
||||
|
||||
// GetInt returns an integer value for the option.
|
||||
//
|
||||
// The Len field of Option must be either 1 or 4.
|
||||
func (o *Option) GetInt(c *Conn) (int, error) {
|
||||
if o.Len != 1 && o.Len != 4 {
|
||||
return 0, errors.New("invalid option")
|
||||
}
|
||||
var b []byte
|
||||
var bb [4]byte
|
||||
if o.Len == 1 {
|
||||
b = bb[:1]
|
||||
} else {
|
||||
b = bb[:4]
|
||||
}
|
||||
n, err := o.get(c, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != o.Len {
|
||||
return 0, errors.New("invalid option length")
|
||||
}
|
||||
if o.Len == 1 {
|
||||
return int(b[0]), nil
|
||||
}
|
||||
return int(NativeEndian.Uint32(b[:4])), nil
|
||||
}
|
||||
|
||||
// Set writes the option and value to the kernel.
|
||||
func (o *Option) Set(c *Conn, b []byte) error {
|
||||
if o.Name < 1 || o.Len < 1 {
|
||||
return errors.New("invalid option")
|
||||
}
|
||||
if len(b) < o.Len {
|
||||
return errors.New("short buffer")
|
||||
}
|
||||
return o.set(c, b)
|
||||
}
|
||||
|
||||
// SetInt writes the option and value to the kernel.
|
||||
//
|
||||
// The Len field of Option must be either 1 or 4.
|
||||
func (o *Option) SetInt(c *Conn, v int) error {
|
||||
if o.Len != 1 && o.Len != 4 {
|
||||
return errors.New("invalid option")
|
||||
}
|
||||
var b []byte
|
||||
if o.Len == 1 {
|
||||
b = []byte{byte(v)}
|
||||
} else {
|
||||
var bb [4]byte
|
||||
NativeEndian.PutUint32(bb[:o.Len], uint32(v))
|
||||
b = bb[:4]
|
||||
}
|
||||
return o.set(c, b)
|
||||
}
|
||||
|
||||
func controlHeaderLen() int {
|
||||
return roundup(sizeofCmsghdr)
|
||||
}
|
||||
|
||||
func controlMessageLen(dataLen int) int {
|
||||
return roundup(sizeofCmsghdr) + dataLen
|
||||
}
|
||||
|
||||
// ControlMessageSpace returns the whole length of control message.
|
||||
func ControlMessageSpace(dataLen int) int {
|
||||
return roundup(sizeofCmsghdr) + roundup(dataLen)
|
||||
}
|
||||
|
||||
// A ControlMessage represents the head message in a stream of control
|
||||
// messages.
|
||||
//
|
||||
// A control message comprises of a header, data and a few padding
|
||||
// fields to conform to the interface to the kernel.
|
||||
//
|
||||
// See RFC 3542 for further information.
|
||||
type ControlMessage []byte
|
||||
|
||||
// Data returns the data field of the control message at the head on
|
||||
// m.
|
||||
func (m ControlMessage) Data(dataLen int) []byte {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l || len(m) < l+dataLen {
|
||||
return nil
|
||||
}
|
||||
return m[l : l+dataLen]
|
||||
}
|
||||
|
||||
// Next returns the control message at the next on m.
|
||||
//
|
||||
// Next works only for standard control messages.
|
||||
func (m ControlMessage) Next(dataLen int) ControlMessage {
|
||||
l := ControlMessageSpace(dataLen)
|
||||
if len(m) < l {
|
||||
return nil
|
||||
}
|
||||
return m[l:]
|
||||
}
|
||||
|
||||
// MarshalHeader marshals the header fields of the control message at
|
||||
// the head on m.
|
||||
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
|
||||
if len(m) < controlHeaderLen() {
|
||||
return errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
h.set(controlMessageLen(dataLen), lvl, typ)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseHeader parses and returns the header fields of the control
|
||||
// message at the head on m.
|
||||
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l {
|
||||
return 0, 0, 0, errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
|
||||
}
|
||||
|
||||
// Marshal marshals the control message at the head on m, and returns
|
||||
// the next control message.
|
||||
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
|
||||
l := len(data)
|
||||
if len(m) < ControlMessageSpace(l) {
|
||||
return nil, errors.New("short message")
|
||||
}
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
h.set(controlMessageLen(l), lvl, typ)
|
||||
if l > 0 {
|
||||
copy(m.Data(l), data)
|
||||
}
|
||||
return m.Next(l), nil
|
||||
}
|
||||
|
||||
// Parse parses m as a single or multiple control messages.
|
||||
//
|
||||
// Parse works for both standard and compatible messages.
|
||||
func (m ControlMessage) Parse() ([]ControlMessage, error) {
|
||||
var ms []ControlMessage
|
||||
for len(m) >= controlHeaderLen() {
|
||||
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
|
||||
l := h.len()
|
||||
if l <= 0 {
|
||||
return nil, errors.New("invalid header length")
|
||||
}
|
||||
if uint64(l) < uint64(controlHeaderLen()) {
|
||||
return nil, errors.New("invalid message length")
|
||||
}
|
||||
if uint64(l) > uint64(len(m)) {
|
||||
return nil, errors.New("short buffer")
|
||||
}
|
||||
// On message reception:
|
||||
//
|
||||
// |<- ControlMessageSpace --------------->|
|
||||
// |<- controlMessageLen ---------->| |
|
||||
// |<- controlHeaderLen ->| | |
|
||||
// +---------------+------+---------+------+
|
||||
// | Header | PadH | Data | PadD |
|
||||
// +---------------+------+---------+------+
|
||||
//
|
||||
// On compatible message reception:
|
||||
//
|
||||
// | ... |<- controlMessageLen ----------->|
|
||||
// | ... |<- controlHeaderLen ->| |
|
||||
// +-----+---------------+------+----------+
|
||||
// | ... | Header | PadH | Data |
|
||||
// +-----+---------------+------+----------+
|
||||
ms = append(ms, ControlMessage(m[:l]))
|
||||
ll := l - controlHeaderLen()
|
||||
if len(m) >= ControlMessageSpace(ll) {
|
||||
m = m[ControlMessageSpace(ll):]
|
||||
} else {
|
||||
m = m[controlMessageLen(ll):]
|
||||
}
|
||||
}
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
// NewControlMessage returns a new stream of control messages.
|
||||
func NewControlMessage(dataLen []int) ControlMessage {
|
||||
var l int
|
||||
for i := range dataLen {
|
||||
l += ControlMessageSpace(dataLen[i])
|
||||
}
|
||||
return make([]byte, l)
|
||||
}
|
||||
|
||||
// A Message represents an IO message.
|
||||
type Message struct {
|
||||
// When writing, the Buffers field must contain at least one
|
||||
// byte to write.
|
||||
// When reading, the Buffers field will always contain a byte
|
||||
// to read.
|
||||
Buffers [][]byte
|
||||
|
||||
// OOB contains protocol-specific control or miscellaneous
|
||||
// ancillary data known as out-of-band data.
|
||||
OOB []byte
|
||||
|
||||
// Addr specifies a destination address when writing.
|
||||
// It can be nil when the underlying protocol of the raw
|
||||
// connection uses connection-oriented communication.
|
||||
// After a successful read, it may contain the source address
|
||||
// on the received packet.
|
||||
Addr net.Addr
|
||||
|
||||
N int // # of bytes read or written from/to Buffers
|
||||
NN int // # of bytes read or written from/to OOB
|
||||
Flags int // protocol-specific information on the received message
|
||||
}
|
||||
|
||||
// RecvMsg wraps recvmsg system call.
|
||||
//
|
||||
// The provided flags is a set of platform-dependent flags, such as
|
||||
// syscall.MSG_PEEK.
|
||||
func (c *Conn) RecvMsg(m *Message, flags int) error {
|
||||
return c.recvMsg(m, flags)
|
||||
}
|
||||
|
||||
// SendMsg wraps sendmsg system call.
|
||||
//
|
||||
// The provided flags is a set of platform-dependent flags, such as
|
||||
// syscall.MSG_DONTROUTE.
|
||||
func (c *Conn) SendMsg(m *Message, flags int) error {
|
||||
return c.sendMsg(m, flags)
|
||||
}
|
||||
|
||||
// RecvMsgs wraps recvmmsg system call.
|
||||
//
|
||||
// It returns the number of processed messages.
|
||||
//
|
||||
// The provided flags is a set of platform-dependent flags, such as
|
||||
// syscall.MSG_PEEK.
|
||||
//
|
||||
// Only Linux supports this.
|
||||
func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) {
|
||||
return c.recvMsgs(ms, flags)
|
||||
}
|
||||
|
||||
// SendMsgs wraps sendmmsg system call.
|
||||
//
|
||||
// It returns the number of processed messages.
|
||||
//
|
||||
// The provided flags is a set of platform-dependent flags, such as
|
||||
// syscall.MSG_DONTROUTE.
|
||||
//
|
||||
// Only Linux supports this.
|
||||
func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) {
|
||||
return c.sendMsgs(ms, flags)
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package socket_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/net/nettest"
|
||||
)
|
||||
|
||||
func TestSocket(t *testing.T) {
|
||||
t.Run("Option", func(t *testing.T) {
|
||||
testSocketOption(t, &socket.Option{Level: syscall.SOL_SOCKET, Name: syscall.SO_RCVBUF, Len: 4})
|
||||
})
|
||||
}
|
||||
|
||||
func testSocketOption(t *testing.T, so *socket.Option) {
|
||||
c, err := nettest.NewLocalPacketListener("udp")
|
||||
if err != nil {
|
||||
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
|
||||
}
|
||||
defer c.Close()
|
||||
cc, err := socket.NewConn(c.(net.Conn))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const N = 2048
|
||||
if err := so.SetInt(cc, N); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err := so.GetInt(cc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n < N {
|
||||
t.Fatalf("got %d; want greater than or equal to %d", n, N)
|
||||
}
|
||||
}
|
||||
|
||||
type mockControl struct {
|
||||
Level int
|
||||
Type int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func TestControlMessage(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
t.Skipf("not supported on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
cs []mockControl
|
||||
}{
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 1, Type: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 2, Type: 2, Data: []byte{0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]mockControl{
|
||||
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
|
||||
{Level: 2, Type: 2, Data: []byte{0xfe}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
var w []byte
|
||||
var tailPadLen int
|
||||
mm := socket.NewControlMessage([]int{0})
|
||||
for i, c := range tt.cs {
|
||||
m := socket.NewControlMessage([]int{len(c.Data)})
|
||||
l := len(m) - len(mm)
|
||||
if i == len(tt.cs)-1 && l > len(c.Data) {
|
||||
tailPadLen = l - len(c.Data)
|
||||
}
|
||||
w = append(w, m...)
|
||||
}
|
||||
|
||||
var err error
|
||||
ww := make([]byte, len(w))
|
||||
copy(ww, w)
|
||||
m := socket.ControlMessage(ww)
|
||||
for _, c := range tt.cs {
|
||||
if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
|
||||
t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
|
||||
}
|
||||
copy(m.Data(len(c.Data)), c.Data)
|
||||
m = m.Next(len(c.Data))
|
||||
}
|
||||
m = socket.ControlMessage(w)
|
||||
for _, c := range tt.cs {
|
||||
m, err = m.Marshal(c.Level, c.Type, c.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(ww, w) {
|
||||
t.Fatalf("got %#v; want %#v", ww, w)
|
||||
}
|
||||
|
||||
ws := [][]byte{w}
|
||||
if tailPadLen > 0 {
|
||||
// Test a message with no tail padding.
|
||||
nopad := w[:len(w)-tailPadLen]
|
||||
ws = append(ws, [][]byte{nopad}...)
|
||||
}
|
||||
for _, w := range ws {
|
||||
ms, err := socket.ControlMessage(w).Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).Parse() = %v", tt.cs, err)
|
||||
}
|
||||
for i, m := range ms {
|
||||
lvl, typ, dataLen, err := m.ParseHeader()
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
|
||||
}
|
||||
if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
|
||||
t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDP(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
t.Skipf("not supported on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
c, err := nettest.NewLocalPacketListener("udp")
|
||||
if err != nil {
|
||||
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
|
||||
}
|
||||
defer c.Close()
|
||||
cc, err := socket.NewConn(c.(net.Conn))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("Message", func(t *testing.T) {
|
||||
data := []byte("HELLO-R-U-THERE")
|
||||
wm := socket.Message{
|
||||
Buffers: bytes.SplitAfter(data, []byte("-")),
|
||||
Addr: c.LocalAddr(),
|
||||
}
|
||||
if err := cc.SendMsg(&wm, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := make([]byte, 32)
|
||||
rm := socket.Message{
|
||||
Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]},
|
||||
}
|
||||
if err := cc.RecvMsg(&rm, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(b[:rm.N], data) {
|
||||
t.Fatalf("got %#v; want %#v", b[:rm.N], data)
|
||||
}
|
||||
})
|
||||
switch runtime.GOOS {
|
||||
case "android", "linux":
|
||||
t.Run("Messages", func(t *testing.T) {
|
||||
data := []byte("HELLO-R-U-THERE")
|
||||
wmbs := bytes.SplitAfter(data, []byte("-"))
|
||||
wms := []socket.Message{
|
||||
{Buffers: wmbs[:1], Addr: c.LocalAddr()},
|
||||
{Buffers: wmbs[1:], Addr: c.LocalAddr()},
|
||||
}
|
||||
n, err := cc.SendMsgs(wms, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != len(wms) {
|
||||
t.Fatalf("got %d; want %d", n, len(wms))
|
||||
}
|
||||
b := make([]byte, 32)
|
||||
rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
|
||||
rms := []socket.Message{
|
||||
{Buffers: rmbs[0]},
|
||||
{Buffers: rmbs[1]},
|
||||
}
|
||||
n, err = cc.RecvMsgs(rms, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != len(rms) {
|
||||
t.Fatalf("got %d; want %d", n, len(rms))
|
||||
}
|
||||
nn := 0
|
||||
for i := 0; i < n; i++ {
|
||||
nn += rms[i].N
|
||||
}
|
||||
if !bytes.Equal(b[:nn], data) {
|
||||
t.Fatalf("got %#v; want %#v", b[:nn], data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The behavior of transmission for zero byte paylaod depends
|
||||
// on each platform implementation. Some may transmit only
|
||||
// protocol header and options, other may transmit nothing.
|
||||
// We test only that SendMsg and SendMsgs will not crash with
|
||||
// empty buffers.
|
||||
wm := socket.Message{
|
||||
Buffers: [][]byte{{}},
|
||||
Addr: c.LocalAddr(),
|
||||
}
|
||||
cc.SendMsg(&wm, 0)
|
||||
wms := []socket.Message{
|
||||
{Buffers: [][]byte{{}}, Addr: c.LocalAddr()},
|
||||
}
|
||||
cc.SendMsgs(wms, 0)
|
||||
}
|
||||
|
||||
func BenchmarkUDP(b *testing.B) {
|
||||
c, err := nettest.NewLocalPacketListener("udp")
|
||||
if err != nil {
|
||||
b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
|
||||
}
|
||||
defer c.Close()
|
||||
cc, err := socket.NewConn(c.(net.Conn))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
data := []byte("HELLO-R-U-THERE")
|
||||
wm := socket.Message{
|
||||
Buffers: [][]byte{data},
|
||||
Addr: c.LocalAddr(),
|
||||
}
|
||||
rm := socket.Message{
|
||||
Buffers: [][]byte{make([]byte, 128)},
|
||||
OOB: make([]byte, 128),
|
||||
}
|
||||
|
||||
for M := 1; M <= 1<<9; M = M << 1 {
|
||||
b.Run(fmt.Sprintf("Iter-%d", M), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < M; j++ {
|
||||
if err := cc.SendMsg(&wm, 0); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := cc.RecvMsg(&rm, 0); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
switch runtime.GOOS {
|
||||
case "android", "linux":
|
||||
wms := make([]socket.Message, M)
|
||||
for i := range wms {
|
||||
wms[i].Buffers = [][]byte{data}
|
||||
wms[i].Addr = c.LocalAddr()
|
||||
}
|
||||
rms := make([]socket.Message, M)
|
||||
for i := range rms {
|
||||
rms[i].Buffers = [][]byte{make([]byte, 128)}
|
||||
rms[i].OOB = make([]byte, 128)
|
||||
}
|
||||
b.Run(fmt.Sprintf("Batch-%d", M), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := cc.SendMsgs(wms, 0); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if _, err := cc.RecvMsgs(rms, 0); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
// NativeEndian is the machine native endian implementation of
|
||||
// ByteOrder.
|
||||
NativeEndian binary.ByteOrder
|
||||
|
||||
kernelAlign int
|
||||
)
|
||||
|
||||
func init() {
|
||||
i := uint32(1)
|
||||
b := (*[4]byte)(unsafe.Pointer(&i))
|
||||
if b[0] == 1 {
|
||||
NativeEndian = binary.LittleEndian
|
||||
} else {
|
||||
NativeEndian = binary.BigEndian
|
||||
}
|
||||
kernelAlign = probeProtocolStack()
|
||||
}
|
||||
|
||||
func roundup(l int) int {
|
||||
return (l + kernelAlign - 1) &^ (kernelAlign - 1)
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2017 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 aix darwin dragonfly freebsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
return 0, errNotImplemented
|
||||
}
|
||||
|
||||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
return 0, errNotImplemented
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2017 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 aix freebsd netbsd openbsd
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func probeProtocolStack() int {
|
||||
if (runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd") && runtime.GOARCH == "arm" {
|
||||
return 8
|
||||
}
|
||||
if runtime.GOOS == "aix" {
|
||||
return 1
|
||||
}
|
||||
var p uintptr
|
||||
return int(unsafe.Sizeof(p))
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2019 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = unix.AF_UNSPEC
|
||||
sysAF_INET = unix.AF_INET
|
||||
sysAF_INET6 = unix.AF_INET6
|
||||
|
||||
sysSOCK_RAW = unix.SOCK_RAW
|
||||
)
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
func probeProtocolStack() int { return 4 }
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
func probeProtocolStack() int { return 4 }
|
|
@ -1,33 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// +build !go1.12
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
|
||||
l := uint32(len(b))
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
|
||||
return int(l), errnoErr(errno)
|
||||
}
|
||||
|
||||
func setsockopt(s uintptr, level, name int, b []byte) error {
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
n, _, errno := syscall.Syscall(syscall.SYS_RECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
||||
|
||||
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
n, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// +build aix go1.12,darwin
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname syscall_getsockopt syscall.getsockopt
|
||||
func syscall_getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *uint32) error
|
||||
|
||||
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
|
||||
l := uint32(len(b))
|
||||
err := syscall_getsockopt(int(s), level, name, unsafe.Pointer(&b[0]), &l)
|
||||
return int(l), err
|
||||
}
|
||||
|
||||
//go:linkname syscall_setsockopt syscall.setsockopt
|
||||
func syscall_setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) error
|
||||
|
||||
func setsockopt(s uintptr, level, name int, b []byte) error {
|
||||
return syscall_setsockopt(int(s), level, name, unsafe.Pointer(&b[0]), uintptr(len(b)))
|
||||
}
|
||||
|
||||
//go:linkname syscall_recvmsg syscall.recvmsg
|
||||
func syscall_recvmsg(s int, msg *syscall.Msghdr, flags int) (n int, err error)
|
||||
|
||||
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
return syscall_recvmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags)
|
||||
}
|
||||
|
||||
//go:linkname syscall_sendmsg syscall.sendmsg
|
||||
func syscall_sendmsg(s int, msg *syscall.Msghdr, flags int) (n int, err error)
|
||||
|
||||
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
return syscall_sendmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,!s390x,!386
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func probeProtocolStack() int {
|
||||
var p uintptr
|
||||
return int(unsafe.Sizeof(p))
|
||||
}
|
||||
|
||||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
||||
|
||||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func probeProtocolStack() int { return 4 }
|
||||
|
||||
const (
|
||||
sysSETSOCKOPT = 0xe
|
||||
sysGETSOCKOPT = 0xf
|
||||
sysSENDMSG = 0x10
|
||||
sysRECVMSG = 0x11
|
||||
sysRECVMMSG = 0x13
|
||||
sysSENDMMSG = 0x14
|
||||
)
|
||||
|
||||
func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
|
||||
func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
|
||||
|
||||
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
|
||||
l := uint32(len(b))
|
||||
_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
|
||||
return int(l), errnoErr(errno)
|
||||
}
|
||||
|
||||
func setsockopt(s uintptr, level, name int, b []byte) error {
|
||||
_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
||||
|
||||
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
|
||||
n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
||||
|
||||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
||||
|
||||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||
n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
||||
return int(n), errnoErr(errno)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||
JMP syscall·socketcall(SB)
|
||||
|
||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
||||
JMP syscall·rawsocketcall(SB)
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
const (
|
||||
sysRECVMMSG = 0x12b
|
||||
sysSENDMMSG = 0x133
|
||||
)
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socket
|
||||
|
||||
const (
|
||||
sysRECVMMSG = 0x16d
|
||||
sysSENDMMSG = 0x176
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue