<commit_before>// +build evmjit

package vm

/*

void* evmjit_create();
int   evmjit_run(void* _jit, void* _data, void* _env);
void  evmjit_destroy(void* _jit);

// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
// More: https://github.com/ethereum/evmjit
#cgo LDFLAGS: -levmjit
*/
import "C"

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/state"
	"math/big"
	"unsafe"
)

type JitVm struct {
	env        Environment
	me         ContextRef
	callerAddr []byte
	price      *big.Int
	data       RuntimeData
}

type i256 [32]byte

type RuntimeData struct {
	gas          int64
	gasPrice     int64
	callData     *byte
	callDataSize uint64
	address      i256
	caller       i256
	origin       i256
	callValue    i256
	coinBase     i256
	difficulty   i256
	gasLimit     i256
	number       uint64
	timestamp    int64
	code         *byte
	codeSize     uint64
	codeHash     i256
}

func hash2llvm(h []byte) i256 {
	var m i256
	copy(m[len(m)-len(h):], h) // right aligned copy
	return m
}

func llvm2hash(m *i256) []byte {
	return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
}

func llvm2hashRef(m *i256) []byte {
	return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
}

func address2llvm(addr []byte) i256 {
	n := hash2llvm(addr)
	bswap(&n)
	return n
}

// bswap swap bytes of the 256-bit integer on LLVM side
// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
func bswap(m *i256) *i256 {
	for i, l := 0, len(m); i < l/2; i++ {
		m[i], m[l-i-1] = m[l-i-1], m[i]
	}
	return m
}

func trim(m []byte) []byte {
	skip := 0
	for i := 0; i < len(m); i++ {
		if m[i] == 0 {
			skip++
		} else {
			break
		}
	}
	return m[skip:]
}

func getDataPtr(m []byte) *byte {
	var p *byte
	if len(m) > 0 {
		p = &m[0]
	}
	return p
}

func big2llvm(n *big.Int) i256 {
	m := hash2llvm(n.Bytes())
	bswap(&m)
	return m
}

func llvm2big(m *i256) *big.Int {
	n := big.NewInt(0)
	for i := 0; i < len(m); i++ {
		b := big.NewInt(int64(m[i]))
		b.Lsh(b, uint(i)*8)
		n.Add(n, b)
	}
	return n
}

// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
// User must asure that referenced memory is available to Go until the data is copied or not needed any more
func llvm2bytesRef(data *byte, length uint64) []byte {
	if length == 0 {
		return nil
	}
	if data == nil {
		panic("Unexpected nil data pointer")
	}
	return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
}

func untested(condition bool, message string) {
	if condition {
		panic("Condition `" + message + "` tested. Remove assert.")
	}
}

func assert(condition bool, message string) {
	if !condition {
		panic("Assert `" + message + "` failed!")
	}
}

func NewJitVm(env Environment) *JitVm {
	return &JitVm{env: env}
}

func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
	// TODO: depth is increased but never checked by VM. VM should not know about it at all.
	self.env.SetDepth(self.env.Depth() + 1)

	// TODO: Move it to Env.Call() or sth
	if Precompiled[string(me.Address())] != nil {
		// if it's address of precopiled contract
		// fallback to standard VM
		stdVm := New(self.env)
		return stdVm.Run(me, caller, code, value, gas, price, callData)
	}

	if self.me != nil {
		panic("JitVm.Run() can be called only once per JitVm instance")
	}

	self.me = me
	self.callerAddr = caller.Address()
	self.price = price

	self.data.gas = gas.Int64()
	self.data.gasPrice = price.Int64()
	self.data.callData = getDataPtr(callData)
	self.data.callDataSize = uint64(len(callData))
	self.data.address = address2llvm(self.me.Address())
	self.data.caller = address2llvm(caller.Address())
	self.data.origin = address2llvm(self.env.Origin())
	self.data.callValue = big2llvm(value)
	self.data.coinBase = address2llvm(self.env.Coinbase())
	self.data.difficulty = big2llvm(self.env.Difficulty())
	self.data.gasLimit = big2llvm(self.env.GasLimit())
	self.data.number = self.env.BlockNumber().Uint64()
	self.data.timestamp = self.env.Time()
	self.data.code = getDataPtr(code)
	self.data.codeSize = uint64(len(code))
	self.data.codeHash = hash2llvm(crypto.Sha3(code)) // TODO: Get already computed hash?

	jit := C.evmjit_create()
	retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))

	if retCode < 0 {
		err = errors.New("OOG from JIT")
		gas.SetInt64(0) // Set gas to 0, JIT does not bother
	} else {
		gas.SetInt64(self.data.gas)
		if retCode == 1 { // RETURN
			ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
		} else if retCode == 2 { // SUICIDE
			// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
			state := self.Env().State()
			receiverAddr := llvm2hashRef(bswap(&self.data.address))
			receiver := state.GetOrNewStateObject(receiverAddr)
			balance := state.GetBalance(me.Address())
			receiver.AddAmount(balance)
			state.Delete(me.Address())
		}
	}

	C.evmjit_destroy(jit)
	return
}

func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
	return self
}

func (self *JitVm) Endl() VirtualMachine {
	return self
}

func (self *JitVm) Env() Environment {
	return self.env
}

//export env_sha3
func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
	data := llvm2bytesRef(dataPtr, length)
	hash := crypto.Sha3(data)
	result := (*i256)(resultPtr)
	*result = hash2llvm(hash)
}

//export env_sstore
func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
	vm := (*JitVm)(vmPtr)
	index := llvm2hash(bswap((*i256)(indexPtr)))
	value := llvm2hash(bswap((*i256)(valuePtr)))
	value = trim(value)
	if len(value) == 0 {
		prevValue := vm.env.State().GetState(vm.me.Address(), index)
		if len(prevValue) != 0 {
			vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
		}
	}

	vm.env.State().SetState(vm.me.Address(), index, value)
}

//export env_sload
func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
	vm := (*JitVm)(vmPtr)
	index := llvm2hash(bswap((*i256)(indexPtr)))
	value := vm.env.State().GetState(vm.me.Address(), index)
	result := (*i256)(resultPtr)
	*result = hash2llvm(value)
	bswap(result)
}

//export env_balance
func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)
	addr := llvm2hash((*i256)(_addr))
	balance := vm.Env().State().GetBalance(addr)
	result := (*i256)(_result)
	*result = big2llvm(balance)
}

//export env_blockhash
func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)
	number := llvm2big((*i256)(_number))
	result := (*i256)(_result)

	currNumber := vm.Env().BlockNumber()
	limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
	if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
		hash := vm.Env().GetHash(uint64(number.Int64()))
		*result = hash2llvm(hash)
	} else {
		*result = i256{}
	}
}

//export env_call
func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
	vm := (*JitVm)(_vm)

	//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
		}
	}()

	balance := vm.Env().State().GetBalance(vm.me.Address())
	value := llvm2big((*i256)(_value))

	if balance.Cmp(value) >= 0 {
		receiveAddr := llvm2hash((*i256)(_receiveAddr))
		inData := C.GoBytes(inDataPtr, C.int(inDataLen))
		outData := llvm2bytesRef(outDataPtr, outDataLen)
		codeAddr := llvm2hash((*i256)(_codeAddr))
		gas := big.NewInt(*_gas)
		var out []byte
		var err error
		if bytes.Equal(codeAddr, receiveAddr) {
			out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
		} else {
			out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
		}
		*_gas = gas.Int64()
		if err == nil {
			copy(outData, out)
			return true
		}
	}

	return false
}

//export env_create
func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)

	value := llvm2big((*i256)(_value))
	initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
	result := (*i256)(_result)
	*result = i256{}

	gas := big.NewInt(*_gas)
	ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
	if suberr == nil {
		dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
		dataGas.Mul(dataGas, GasCreateByte)
		gas.Sub(gas, dataGas)
		*result = hash2llvm(ref.Address())
	}
	*_gas = gas.Int64()
}

//export env_log
func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
	vm := (*JitVm)(_vm)

	data := C.GoBytes(dataPtr, C.int(dataLen))

	topics := make([][]byte, 0, 4)
	if _topic1 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic1)))
	}
	if _topic2 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic2)))
	}
	if _topic3 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic3)))
	}
	if _topic4 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic4)))
	}

	vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64()))
}

//export env_extcode
func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
	vm := (*JitVm)(_vm)
	addr := llvm2hash((*i256)(_addr))
	code := vm.Env().State().GetCode(addr)
	*o_size = uint64(len(code))
	return getDataPtr(code)
}
<commit_msg>Fix JitVm build<commit_after>// +build evmjit

package vm

/*

void* evmjit_create();
int   evmjit_run(void* _jit, void* _data, void* _env);
void  evmjit_destroy(void* _jit);

// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
// More: https://github.com/ethereum/evmjit
#cgo LDFLAGS: -levmjit
*/
import "C"

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/state"
	"math/big"
	"unsafe"
)

type JitVm struct {
	env        Environment
	me         ContextRef
	callerAddr []byte
	price      *big.Int
	data       RuntimeData
}

type i256 [32]byte

type RuntimeData struct {
	gas          int64
	gasPrice     int64
	callData     *byte
	callDataSize uint64
	address      i256
	caller       i256
	origin       i256
	callValue    i256
	coinBase     i256
	difficulty   i256
	gasLimit     i256
	number       uint64
	timestamp    int64
	code         *byte
	codeSize     uint64
	codeHash     i256
}

func hash2llvm(h []byte) i256 {
	var m i256
	copy(m[len(m)-len(h):], h) // right aligned copy
	return m
}

func llvm2hash(m *i256) []byte {
	return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
}

func llvm2hashRef(m *i256) []byte {
	return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
}

func address2llvm(addr []byte) i256 {
	n := hash2llvm(addr)
	bswap(&n)
	return n
}

// bswap swap bytes of the 256-bit integer on LLVM side
// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
func bswap(m *i256) *i256 {
	for i, l := 0, len(m); i < l/2; i++ {
		m[i], m[l-i-1] = m[l-i-1], m[i]
	}
	return m
}

func trim(m []byte) []byte {
	skip := 0
	for i := 0; i < len(m); i++ {
		if m[i] == 0 {
			skip++
		} else {
			break
		}
	}
	return m[skip:]
}

func getDataPtr(m []byte) *byte {
	var p *byte
	if len(m) > 0 {
		p = &m[0]
	}
	return p
}

func big2llvm(n *big.Int) i256 {
	m := hash2llvm(n.Bytes())
	bswap(&m)
	return m
}

func llvm2big(m *i256) *big.Int {
	n := big.NewInt(0)
	for i := 0; i < len(m); i++ {
		b := big.NewInt(int64(m[i]))
		b.Lsh(b, uint(i)*8)
		n.Add(n, b)
	}
	return n
}

// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
// User must asure that referenced memory is available to Go until the data is copied or not needed any more
func llvm2bytesRef(data *byte, length uint64) []byte {
	if length == 0 {
		return nil
	}
	if data == nil {
		panic("Unexpected nil data pointer")
	}
	return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
}

func untested(condition bool, message string) {
	if condition {
		panic("Condition `" + message + "` tested. Remove assert.")
	}
}

func assert(condition bool, message string) {
	if !condition {
		panic("Assert `" + message + "` failed!")
	}
}

func NewJitVm(env Environment) *JitVm {
	return &JitVm{env: env}
}

func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
	// TODO: depth is increased but never checked by VM. VM should not know about it at all.
	self.env.SetDepth(self.env.Depth() + 1)

	// TODO: Move it to Env.Call() or sth
	if Precompiled[string(me.Address())] != nil {
		// if it's address of precopiled contract
		// fallback to standard VM
		stdVm := New(self.env)
		return stdVm.Run(me, caller, code, value, gas, price, callData)
	}

	if self.me != nil {
		panic("JitVm.Run() can be called only once per JitVm instance")
	}

	self.me = me
	self.callerAddr = caller.Address()
	self.price = price

	self.data.gas = gas.Int64()
	self.data.gasPrice = price.Int64()
	self.data.callData = getDataPtr(callData)
	self.data.callDataSize = uint64(len(callData))
	self.data.address = address2llvm(self.me.Address())
	self.data.caller = address2llvm(caller.Address())
	self.data.origin = address2llvm(self.env.Origin())
	self.data.callValue = big2llvm(value)
	self.data.coinBase = address2llvm(self.env.Coinbase())
	self.data.difficulty = big2llvm(self.env.Difficulty())
	self.data.gasLimit = big2llvm(self.env.GasLimit())
	self.data.number = self.env.BlockNumber().Uint64()
	self.data.timestamp = self.env.Time()
	self.data.code = getDataPtr(code)
	self.data.codeSize = uint64(len(code))
	self.data.codeHash = hash2llvm(crypto.Sha3(code)) // TODO: Get already computed hash?

	jit := C.evmjit_create()
	retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))

	if retCode < 0 {
		err = errors.New("OOG from JIT")
		gas.SetInt64(0) // Set gas to 0, JIT does not bother
	} else {
		gas.SetInt64(self.data.gas)
		if retCode == 1 { // RETURN
			ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
		} else if retCode == 2 { // SUICIDE
			// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
			state := self.Env().State()
			receiverAddr := llvm2hashRef(bswap(&self.data.address))
			receiver := state.GetOrNewStateObject(receiverAddr)
			balance := state.GetBalance(me.Address())
			receiver.AddBalance(balance)
			state.Delete(me.Address())
		}
	}

	C.evmjit_destroy(jit)
	return
}

func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
	return self
}

func (self *JitVm) Endl() VirtualMachine {
	return self
}

func (self *JitVm) Env() Environment {
	return self.env
}

//export env_sha3
func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
	data := llvm2bytesRef(dataPtr, length)
	hash := crypto.Sha3(data)
	result := (*i256)(resultPtr)
	*result = hash2llvm(hash)
}

//export env_sstore
func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
	vm := (*JitVm)(vmPtr)
	index := llvm2hash(bswap((*i256)(indexPtr)))
	value := llvm2hash(bswap((*i256)(valuePtr)))
	value = trim(value)
	if len(value) == 0 {
		prevValue := vm.env.State().GetState(vm.me.Address(), index)
		if len(prevValue) != 0 {
			vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
		}
	}

	vm.env.State().SetState(vm.me.Address(), index, value)
}

//export env_sload
func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
	vm := (*JitVm)(vmPtr)
	index := llvm2hash(bswap((*i256)(indexPtr)))
	value := vm.env.State().GetState(vm.me.Address(), index)
	result := (*i256)(resultPtr)
	*result = hash2llvm(value)
	bswap(result)
}

//export env_balance
func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)
	addr := llvm2hash((*i256)(_addr))
	balance := vm.Env().State().GetBalance(addr)
	result := (*i256)(_result)
	*result = big2llvm(balance)
}

//export env_blockhash
func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)
	number := llvm2big((*i256)(_number))
	result := (*i256)(_result)

	currNumber := vm.Env().BlockNumber()
	limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
	if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
		hash := vm.Env().GetHash(uint64(number.Int64()))
		*result = hash2llvm(hash)
	} else {
		*result = i256{}
	}
}

//export env_call
func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
	vm := (*JitVm)(_vm)

	//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
		}
	}()

	balance := vm.Env().State().GetBalance(vm.me.Address())
	value := llvm2big((*i256)(_value))

	if balance.Cmp(value) >= 0 {
		receiveAddr := llvm2hash((*i256)(_receiveAddr))
		inData := C.GoBytes(inDataPtr, C.int(inDataLen))
		outData := llvm2bytesRef(outDataPtr, outDataLen)
		codeAddr := llvm2hash((*i256)(_codeAddr))
		gas := big.NewInt(*_gas)
		var out []byte
		var err error
		if bytes.Equal(codeAddr, receiveAddr) {
			out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
		} else {
			out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
		}
		*_gas = gas.Int64()
		if err == nil {
			copy(outData, out)
			return true
		}
	}

	return false
}

//export env_create
func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
	vm := (*JitVm)(_vm)

	value := llvm2big((*i256)(_value))
	initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
	result := (*i256)(_result)
	*result = i256{}

	gas := big.NewInt(*_gas)
	ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
	if suberr == nil {
		dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
		dataGas.Mul(dataGas, GasCreateByte)
		gas.Sub(gas, dataGas)
		*result = hash2llvm(ref.Address())
	}
	*_gas = gas.Int64()
}

//export env_log
func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
	vm := (*JitVm)(_vm)

	data := C.GoBytes(dataPtr, C.int(dataLen))

	topics := make([][]byte, 0, 4)
	if _topic1 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic1)))
	}
	if _topic2 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic2)))
	}
	if _topic3 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic3)))
	}
	if _topic4 != nil {
		topics = append(topics, llvm2hash((*i256)(_topic4)))
	}

	vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64()))
}

//export env_extcode
func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
	vm := (*JitVm)(_vm)
	addr := llvm2hash((*i256)(_addr))
	code := vm.Env().State().GetCode(addr)
	*o_size = uint64(len(code))
	return getDataPtr(code)
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ut_test

import (
	"net"
	"testing"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	. "github.com/onsi/gomega"

	"github.com/projectcalico/felix/bpf/nat"
	"github.com/projectcalico/felix/bpf/routes"
	"github.com/projectcalico/felix/ip"
	"github.com/projectcalico/felix/proto"
)

var rulesAllowUDP = [][][]*proto.Rule{{{{
	Action:   "Allow",
	Protocol: &proto.Protocol{NumberOrName: &proto.Protocol_Name{Name: "udp"}},
}}}}

func TestICMPRelatedPlain(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	icmpUNreachable := makeICMPError(ipv4, udp, 3 /* Unreachable */, 1 /*Host unreachable error */)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// there is no normal CT record yet, must be denied
		Expect(res.Retval).To(Equal(resTC_ACT_SHOT))
	})

	// Insert a reverse route for the source workload.
	rtKey := routes.NewKey(srcV4CIDR).AsBytes()
	rtVal := routes.NewValueWithIfIndex(routes.FlagsLocalWorkload, 1).AsBytes()
	err = rtMap.Update(rtKey, rtVal)
	Expect(err).NotTo(HaveOccurred())

	runBpfTest(t, "calico_from_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	// fake icmp echo reply, we do not really care about the payload, just the type and code
	icmpEchoResp := makeICMPError(ipv4, udp, 0 /* Echo reply */, 0)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpEchoResp)
		Expect(err).NotTo(HaveOccurred())
		// echo is unrelated, must be denied
		Expect(res.Retval).To(Equal(resTC_ACT_SHOT))
	})
}

func TestICMPRelatedNATPodPod(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	// Insert a reverse route for the source workload.
	rtKey := routes.NewKey(srcV4CIDR).AsBytes()
	rtVal := routes.NewValueWithIfIndex(routes.FlagsLocalWorkload, 1).AsBytes()
	err = rtMap.Update(rtKey, rtVal)
	Expect(err).NotTo(HaveOccurred())

	err = natMap.Update(
		nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes(),
		nat.NewNATValue(0, 1, 0, 0).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natMap.Delete(nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	natIP := net.IPv4(8, 8, 8, 8)
	natPort := uint16(666)

	err = natBEMap.Update(
		nat.NewNATBackendKey(0, 0).AsBytes(),
		nat.NewNATBackendValue(natIP, natPort).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natBEMap.Delete(nat.NewNATBackendKey(0, 0).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	var natPkt gopacket.Packet

	runBpfTest(t, "calico_from_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		natPkt = gopacket.NewPacket(res.dataOut, layers.LayerTypeEthernet, gopacket.Default)
	})

	natIPv4L := natPkt.Layer(layers.LayerTypeIPv4)
	Expect(natIPv4L).NotTo(BeNil())
	natUDPL := natPkt.Layer(layers.LayerTypeUDP)
	Expect(natUDPL).NotTo(BeNil())

	icmpUNreachable := makeICMPError(natIPv4L.(*layers.IPv4), natUDPL.(*layers.UDP), 3, 1)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowe
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func TestICMPRelatedFromHost(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	runBpfTest(t, "calico_from_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	icmpTTLExceeded := makeICMPError(ipv4, udp, 11 /* Time Exceeded */, 0 /* TTL expired */)

	runBpfTest(t, "calico_to_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpTTLExceeded)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func TestICMPRelatedFromHostBeforeNAT(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	err = natMap.Update(
		nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes(),
		nat.NewNATValue(0, 1, 0, 0).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natMap.Delete(nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	natIP := net.IPv4(8, 8, 8, 8)
	natPort := uint16(666)

	err = natBEMap.Update(
		nat.NewNATBackendKey(0, 0).AsBytes(),
		nat.NewNATBackendValue(natIP, natPort).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natBEMap.Delete(nat.NewNATBackendKey(0, 0).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	// NP route
	node2IP := net.IPv4(3, 3, 3, 3)
	node2wCIDR := net.IPNet{
		IP:   natIP,
		Mask: net.IPv4Mask(255, 255, 255, 0),
	}

	err = rtMap.Update(
		routes.NewKey(ip.CIDRFromIPNet(&node2wCIDR).(ip.V4CIDR)).AsBytes(),
		routes.NewValueWithNextHop(routes.FlagsRemoteWorkload, ip.FromNetIP(node2IP).(ip.V4Addr)).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())

	// this will create NAT tracking entries for a nodeport
	runBpfTest(t, "calico_from_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	// we base the packet on the orignal packet before NAT as if we let the original packet through
	// before we do the actual NAT as that is where we check for TTL as doing it for the tunneled
	// packet would be complicated
	icmpTTLExceeded := makeICMPError(ipv4, udp, 11 /* Time Exceeded */, 0 /* TTL expired */)

	runBpfTest(t, "calico_to_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpTTLExceeded)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func makeICMPError(ipInner *layers.IPv4, l4 gopacket.SerializableLayer, icmpType, icmpCode uint8) []byte {
	payloadBuf := gopacket.NewSerializeBuffer()
	err := gopacket.SerializeLayers(payloadBuf, gopacket.SerializeOptions{}, ipInner, l4)
	Expect(err).NotTo(HaveOccurred())
	payload := payloadBuf.Bytes()

	eth := &layers.Ethernet{
		SrcMAC:       []byte{0xee, 0, 0, 0, 0, 1},
		DstMAC:       []byte{0xfe, 0, 0, 0, 0, 2},
		EthernetType: layers.EthernetTypeIPv4,
	}

	ipv4 := &layers.IPv4{
		Version:  4,
		IHL:      5,
		TTL:      64,
		Flags:    layers.IPv4DontFragment,
		SrcIP:    hostIP,
		DstIP:    ipInner.SrcIP,
		Protocol: layers.IPProtocolICMPv4,
		Length:   uint16(20 + 8 + len(payload)),
	}

	icmp := &layers.ICMPv4{
		TypeCode: layers.CreateICMPv4TypeCode(icmpType, icmpCode),
	}

	pkt := gopacket.NewSerializeBuffer()
	err = gopacket.SerializeLayers(pkt, gopacket.SerializeOptions{ComputeChecksums: true},
		eth, ipv4, icmp, gopacket.Payload(payload))
	Expect(err).NotTo(HaveOccurred())

	return pkt.Bytes()
}

func checkICMP(bytes []byte, origSrc, innerSrc, innerDst net.IP,
	innerProto layers.IPProtocol, innerPortSrc, innerPortDst uint16) {

	icmpPkt := gopacket.NewPacket(bytes, layers.LayerTypeEthernet, gopacket.Default)

	ipv4L := icmpPkt.Layer(layers.LayerTypeIPv4)
	Expect(ipv4L).NotTo(BeNil())
	ipv4R := ipv4L.(*layers.IPv4)

	Expect(ipv4R.DstIP.String()).To(Equal(origSrc.String()))

	payloadL := icmpPkt.ApplicationLayer()
	Expect(payloadL).NotTo(BeNil())
	origPkt := gopacket.NewPacket(payloadL.Payload(), layers.LayerTypeIPv4, gopacket.Default)
	Expect(origPkt).NotTo(BeNil())

	ipv4L = origPkt.Layer(layers.LayerTypeIPv4)
	Expect(ipv4L).NotTo(BeNil())
	ipv4R = ipv4L.(*layers.IPv4)

	Expect(ipv4R.SrcIP.String()).To(Equal(innerSrc.String()))
	Expect(ipv4R.DstIP.String()).To(Equal(innerDst.String()))
	Expect(ipv4R.Protocol).To(Equal(innerProto))

	switch innerProto {
	case layers.IPProtocolUDP:
		udpL := origPkt.Layer(layers.LayerTypeUDP)
		Expect(udpL).NotTo(BeNil())
		udpR := udpL.(*layers.UDP)

		Expect(uint16(udpR.SrcPort)).To(Equal(innerPortSrc))
		Expect(uint16(udpR.DstPort)).To(Equal(innerPortDst))
	case layers.IPProtocolTCP:
		tcpL := origPkt.Layer(layers.LayerTypeTCP)
		Expect(tcpL).NotTo(BeNil())
		tcpR := tcpL.(*layers.TCP)

		Expect(uint16(tcpR.SrcPort)).To(Equal(innerPortSrc))
		Expect(uint16(tcpR.DstPort)).To(Equal(innerPortDst))
	}
}
<commit_msg>fv/bpf: check icmp related outer IP header<commit_after>// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ut_test

import (
	"net"
	"testing"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	. "github.com/onsi/gomega"

	"github.com/projectcalico/felix/bpf/nat"
	"github.com/projectcalico/felix/bpf/routes"
	"github.com/projectcalico/felix/ip"
	"github.com/projectcalico/felix/proto"
)

var rulesAllowUDP = [][][]*proto.Rule{{{{
	Action:   "Allow",
	Protocol: &proto.Protocol{NumberOrName: &proto.Protocol_Name{Name: "udp"}},
}}}}

func TestICMPRelatedPlain(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	icmpUNreachable := makeICMPError(ipv4, udp, 3 /* Unreachable */, 1 /*Host unreachable error */)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// there is no normal CT record yet, must be denied
		Expect(res.Retval).To(Equal(resTC_ACT_SHOT))
	})

	// Insert a reverse route for the source workload.
	rtKey := routes.NewKey(srcV4CIDR).AsBytes()
	rtVal := routes.NewValueWithIfIndex(routes.FlagsLocalWorkload, 1).AsBytes()
	err = rtMap.Update(rtKey, rtVal)
	Expect(err).NotTo(HaveOccurred())

	runBpfTest(t, "calico_from_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	// fake icmp echo reply, we do not really care about the payload, just the type and code
	icmpEchoResp := makeICMPError(ipv4, udp, 0 /* Echo reply */, 0)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpEchoResp)
		Expect(err).NotTo(HaveOccurred())
		// echo is unrelated, must be denied
		Expect(res.Retval).To(Equal(resTC_ACT_SHOT))
	})
}

func TestICMPRelatedNATPodPod(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	// Insert a reverse route for the source workload.
	rtKey := routes.NewKey(srcV4CIDR).AsBytes()
	rtVal := routes.NewValueWithIfIndex(routes.FlagsLocalWorkload, 1).AsBytes()
	err = rtMap.Update(rtKey, rtVal)
	Expect(err).NotTo(HaveOccurred())

	err = natMap.Update(
		nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes(),
		nat.NewNATValue(0, 1, 0, 0).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natMap.Delete(nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	natIP := net.IPv4(8, 8, 8, 8)
	natPort := uint16(666)

	err = natBEMap.Update(
		nat.NewNATBackendKey(0, 0).AsBytes(),
		nat.NewNATBackendValue(natIP, natPort).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natBEMap.Delete(nat.NewNATBackendKey(0, 0).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	var natPkt gopacket.Packet

	runBpfTest(t, "calico_from_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		natPkt = gopacket.NewPacket(res.dataOut, layers.LayerTypeEthernet, gopacket.Default)
	})

	natIPv4L := natPkt.Layer(layers.LayerTypeIPv4)
	Expect(natIPv4L).NotTo(BeNil())
	natUDPL := natPkt.Layer(layers.LayerTypeUDP)
	Expect(natUDPL).NotTo(BeNil())

	icmpUNreachable := makeICMPError(natIPv4L.(*layers.IPv4), natUDPL.(*layers.UDP), 3, 1)

	runBpfTest(t, "calico_to_workload_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpUNreachable)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, hostIP, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func TestICMPRelatedFromHost(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	runBpfTest(t, "calico_from_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	icmpTTLExceeded := makeICMPError(ipv4, udp, 11 /* Time Exceeded */, 0 /* TTL expired */)

	runBpfTest(t, "calico_to_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpTTLExceeded)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, hostIP, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func TestICMPRelatedFromHostBeforeNAT(t *testing.T) {
	RegisterTestingT(t)

	defer resetBPFMaps()

	_, ipv4, l4, _, pktBytes, err := testPacketUDPDefault()
	Expect(err).NotTo(HaveOccurred())
	udp := l4.(*layers.UDP)

	err = natMap.Update(
		nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes(),
		nat.NewNATValue(0, 1, 0, 0).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natMap.Delete(nat.NewNATKey(ipv4.DstIP, uint16(udp.DstPort), uint8(ipv4.Protocol)).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	natIP := net.IPv4(8, 8, 8, 8)
	natPort := uint16(666)

	err = natBEMap.Update(
		nat.NewNATBackendKey(0, 0).AsBytes(),
		nat.NewNATBackendValue(natIP, natPort).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())
	defer func() {
		err := natBEMap.Delete(nat.NewNATBackendKey(0, 0).AsBytes())
		Expect(err).NotTo(HaveOccurred())
	}()

	// NP route
	node2IP := net.IPv4(3, 3, 3, 3)
	node2wCIDR := net.IPNet{
		IP:   natIP,
		Mask: net.IPv4Mask(255, 255, 255, 0),
	}

	err = rtMap.Update(
		routes.NewKey(ip.CIDRFromIPNet(&node2wCIDR).(ip.V4CIDR)).AsBytes(),
		routes.NewValueWithNextHop(routes.FlagsRemoteWorkload, ip.FromNetIP(node2IP).(ip.V4Addr)).AsBytes(),
	)
	Expect(err).NotTo(HaveOccurred())

	// this will create NAT tracking entries for a nodeport
	runBpfTest(t, "calico_from_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(pktBytes)
		Expect(err).NotTo(HaveOccurred())
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))
	})

	// we base the packet on the orignal packet before NAT as if we let the original packet through
	// before we do the actual NAT as that is where we check for TTL as doing it for the tunneled
	// packet would be complicated
	icmpTTLExceeded := makeICMPError(ipv4, udp, 11 /* Time Exceeded */, 0 /* TTL expired */)

	runBpfTest(t, "calico_to_host_ep", rulesAllowUDP, func(bpfrun bpfProgRunFn) {
		res, err := bpfrun(icmpTTLExceeded)
		Expect(err).NotTo(HaveOccurred())
		// we have a normal ct record, it is related, must be allowed
		Expect(res.Retval).To(Equal(resTC_ACT_UNSPEC))

		checkICMP(res.dataOut, hostIP, ipv4.SrcIP, ipv4.SrcIP, ipv4.DstIP, ipv4.Protocol,
			uint16(udp.SrcPort), uint16(udp.DstPort))
	})
}

func makeICMPError(ipInner *layers.IPv4, l4 gopacket.SerializableLayer, icmpType, icmpCode uint8) []byte {
	payloadBuf := gopacket.NewSerializeBuffer()
	err := gopacket.SerializeLayers(payloadBuf, gopacket.SerializeOptions{}, ipInner, l4)
	Expect(err).NotTo(HaveOccurred())
	payload := payloadBuf.Bytes()

	eth := &layers.Ethernet{
		SrcMAC:       []byte{0xee, 0, 0, 0, 0, 1},
		DstMAC:       []byte{0xfe, 0, 0, 0, 0, 2},
		EthernetType: layers.EthernetTypeIPv4,
	}

	ipv4 := &layers.IPv4{
		Version:  4,
		IHL:      5,
		TTL:      64,
		Flags:    layers.IPv4DontFragment,
		SrcIP:    hostIP,
		DstIP:    ipInner.SrcIP,
		Protocol: layers.IPProtocolICMPv4,
		Length:   uint16(20 + 8 + len(payload)),
	}

	icmp := &layers.ICMPv4{
		TypeCode: layers.CreateICMPv4TypeCode(icmpType, icmpCode),
	}

	pkt := gopacket.NewSerializeBuffer()
	err = gopacket.SerializeLayers(pkt, gopacket.SerializeOptions{ComputeChecksums: true},
		eth, ipv4, icmp, gopacket.Payload(payload))
	Expect(err).NotTo(HaveOccurred())

	return pkt.Bytes()
}

func checkICMP(bytes []byte, outSrc, outDst, innerSrc, innerDst net.IP,
	innerProto layers.IPProtocol, innerPortSrc, innerPortDst uint16) {

	icmpPkt := gopacket.NewPacket(bytes, layers.LayerTypeEthernet, gopacket.Default)

	ipv4L := icmpPkt.Layer(layers.LayerTypeIPv4)
	Expect(ipv4L).NotTo(BeNil())
	ipv4R := ipv4L.(*layers.IPv4)

	Expect(ipv4R.SrcIP.String()).To(Equal(outSrc.String()))
	Expect(ipv4R.DstIP.String()).To(Equal(outDst.String()))

	payloadL := icmpPkt.ApplicationLayer()
	Expect(payloadL).NotTo(BeNil())
	origPkt := gopacket.NewPacket(payloadL.Payload(), layers.LayerTypeIPv4, gopacket.Default)
	Expect(origPkt).NotTo(BeNil())

	ipv4L = origPkt.Layer(layers.LayerTypeIPv4)
	Expect(ipv4L).NotTo(BeNil())
	ipv4R = ipv4L.(*layers.IPv4)

	Expect(ipv4R.SrcIP.String()).To(Equal(innerSrc.String()))
	Expect(ipv4R.DstIP.String()).To(Equal(innerDst.String()))
	Expect(ipv4R.Protocol).To(Equal(innerProto))

	switch innerProto {
	case layers.IPProtocolUDP:
		udpL := origPkt.Layer(layers.LayerTypeUDP)
		Expect(udpL).NotTo(BeNil())
		udpR := udpL.(*layers.UDP)

		Expect(uint16(udpR.SrcPort)).To(Equal(innerPortSrc))
		Expect(uint16(udpR.DstPort)).To(Equal(innerPortDst))
	case layers.IPProtocolTCP:
		tcpL := origPkt.Layer(layers.LayerTypeTCP)
		Expect(tcpL).NotTo(BeNil())
		tcpR := tcpL.(*layers.TCP)

		Expect(uint16(tcpR.SrcPort)).To(Equal(innerPortSrc))
		Expect(uint16(tcpR.DstPort)).To(Equal(innerPortDst))
	}
}
<|endoftext|>


<commit_before>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.FromSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.FromSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}
	return os.Symlink(absDir, newname)
}

func TestWalkSkipDir(t *testing.T) {
	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		osDirname = filepath.Join(rootDir, osDirname)
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := filepath.Join(rootDir, "dir3")
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			filepath.Join(rootDir, "dir3"),
			filepath.Join(rootDir, "dir3/aaa.txt"),
			filepath.Join(rootDir, "dir3/zzz"),
			filepath.Join(rootDir, "dir3/zzz/aaa.txt"),
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkNoAccess(t *testing.T) {
	var actual []string

	err := Walk(rootDir, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		ErrorCallback: func(osChildname string, err error) ErrorAction {
			actual = append(actual, osChildname)
			return SkipNode
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(rootDir, "dir6/noaccess"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir4")
	symlink := filepath.Join(rootDir, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(rootDir, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir4"),
		filepath.Join(rootDir, "dir4/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToFile"),
		filepath.Join(rootDir, "dir4/zzz"),
		filepath.Join(rootDir, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir4")
	symlink := filepath.Join(rootDir, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(rootDir, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir4"),
		filepath.Join(rootDir, "dir4/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToFile"),
		filepath.Join(rootDir, "dir4/zzz"),
		filepath.Join(rootDir, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkSymbolicRelativeLinkChain(t *testing.T) {
	var actual []string
	err := Walk(filepath.Join(rootDir, "dir7"), &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir7"),
		filepath.Join(rootDir, "dir7", "a"),
		filepath.Join(rootDir, "dir7", "a", "x"),
		filepath.Join(rootDir, "dir7", "a", "x", "y"),
		filepath.Join(rootDir, "dir7", "b"),
		filepath.Join(rootDir, "dir7", "b", "y"),
		filepath.Join(rootDir, "dir7", "z"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir5")

	var actual []string

	err := Walk(osDirname, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *Dirent) error {
			// t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(rootDir, "dir5/a2/a2a"),
		filepath.Join(rootDir, "dir5/a2"),
		filepath.Join(rootDir, "dir5"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<commit_msg>TestWalkNoAccess<commit_after>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.FromSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.FromSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}
	return os.Symlink(absDir, newname)
}

func TestWalkSkipDir(t *testing.T) {
	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		osDirname = filepath.Join(rootDir, osDirname)
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := filepath.Join(rootDir, "dir3")
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			filepath.Join(rootDir, "dir3"),
			filepath.Join(rootDir, "dir3/aaa.txt"),
			filepath.Join(rootDir, "dir3/zzz"),
			filepath.Join(rootDir, "dir3/zzz/aaa.txt"),
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkNoAccess(t *testing.T) {
	var actual []string

	err := Walk(rootDir, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		ErrorCallback: func(osChildname string, err error) ErrorAction {
			actual = append(actual, filepath.FromSlash(osChildname))
			return SkipNode
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(rootDir, "dir6/noaccess"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir4")
	symlink := filepath.Join(rootDir, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(rootDir, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir4"),
		filepath.Join(rootDir, "dir4/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToFile"),
		filepath.Join(rootDir, "dir4/zzz"),
		filepath.Join(rootDir, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir4")
	symlink := filepath.Join(rootDir, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(rootDir, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir4"),
		filepath.Join(rootDir, "dir4/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToAbsDirectory/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory"),
		filepath.Join(rootDir, "dir4/symlinkToDirectory/aaa.txt"),
		filepath.Join(rootDir, "dir4/symlinkToFile"),
		filepath.Join(rootDir, "dir4/zzz"),
		filepath.Join(rootDir, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkSymbolicRelativeLinkChain(t *testing.T) {
	var actual []string
	err := Walk(filepath.Join(rootDir, "dir7"), &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			actual = append(actual, filepath.FromSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(rootDir, "dir7"),
		filepath.Join(rootDir, "dir7", "a"),
		filepath.Join(rootDir, "dir7", "a", "x"),
		filepath.Join(rootDir, "dir7", "a", "x", "y"),
		filepath.Join(rootDir, "dir7", "b"),
		filepath.Join(rootDir, "dir7", "b", "y"),
		filepath.Join(rootDir, "dir7", "z"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	osDirname := filepath.Join(rootDir, "dir5")

	var actual []string

	err := Walk(osDirname, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *Dirent) error {
			// t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(rootDir, "dir5/a2/a2a"),
		filepath.Join(rootDir, "dir5/a2"),
		filepath.Join(rootDir, "dir5"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"strings"
)

func GetScadaOEMHeader() (headerResult []string, fieldResult []string) {
	oldHeader := `TimeStamp
Turbine
AI intern ActivPower
AI intern WindSpeed
AI intern NacellePos
AI intern WindDirection
AI intern PitchAngle1
AI intern PitchAngle2
AI intern PitchAngle3
C intern SpeedGenerator
C intern SpeedRotor
AI intern ReactivPower
AI intern Frequency Grid
AI GearOilPressure
Temp Outdoor
Temp Nacelle
Temp GearBox HSS NDE
Temp GearBox HSS DE
Temp GearBox IMS DE
Temp GearOilSump
Temp GearBox IMS NDE
Temp GeneratorBearing DE
Temp GeneratorBearing NDE
Temp MainBearing
Temp YawBrake 1
Temp YawBrake 2
Temp G1L1
Temp G1L2
Temp G1L3
Temp YawBrake 4
AI HydrSystemPressure
Temp BottomControlSection Low
AI intern TempConv1
AI intern TempConv2
AI intern TempConv3
AI intern R PidAngleOut
AI intern I1
AI intern I2
AI intern I3
AI intern NacelleDrill
AI intern PitchAkku V1
AI intern PitchAkku V2
AI intern PitchAkku V3
AI intern PitchConv Current1
AI intern PitchConv Current2
AI intern PitchConv Current3
AI intern PitchAngleSP Diff1
AI intern PitchAngleSP Diff2
AI intern PitchAngleSP Diff3
AI intern RpmDiff
AI intern U1
AI intern U2
AI intern U3
AI Intern WindSpeedDif
AI speed RotFR
AI WindSpeed1
AI WindSpeed2
AI WindVane1
AI WindVane2
AI internCurrentAsym
AI intern WindVaneDiff
AI intern PitchSpeed2
AI intern Speed RPMDiff FR1 RotCNT
AI DrTrVibValue
AI intern InLastErrorConv1
AI intern InLastErrorConv2
AI intern InLastErrorConv3
AI TowerVibValueAxial
AI intern DiffGenSpeedSPToAct
Temp YawBrake 5
AI intern SpeedGenerator Proximity
AI intern SpeedDiff Encoder Proximity
Temp CabinetTopBox Low
Temp CabinetTopBox
Temp BottomControlSection
Temp BottomPowerSection
Temp BottomPowerSection Low
AI intern Pitch1 Status High
AI intern Pitch2 Status High
AI intern Pitch3 Status High
AI intern InPosition1 ch3
AI intern InPosition2 ch3
AI intern InPosition3 ch3
AI intern Temp Brake Blade1
AI intern Temp Brake Blade2
AI intern Temp Brake Blade3
AI intern Temp PitchMotor Blade1
AI intern Temp PitchMotor Blade2
AI intern Temp PitchMotor Blade3
AI intern Temp Hub Additional1
AI intern Temp Hub Additional2
AI intern Temp Hub Additional3
AI intern Pitch1 Status Low
AI intern Pitch2 Status Low
AI intern Pitch3 Status Low
AI intern Battery VoltageBlade1 center
AI intern Battery VoltageBlade2 center
AI intern Battery VoltageBlade3 center
AI intern Battery ChargingCur Blade1
AI intern Battery ChargingCur Blade2
AI intern Battery ChargingCur Blade3
AI intern Battery DischargingCur Blade1
AI intern Battery DischargingCur Blade2
AI intern Battery DischargingCur Blade3
AI intern PitchMotor BrakeVoltage Blade1
AI intern PitchMotor BrakeVoltage Blade2
AI intern PitchMotor BrakeVoltage Blade3
AI intern PitchMotor BrakeCurrent Blade1
AI intern PitchMotor BrakeCurrent Blade2
AI intern PitchMotor BrakeCurrent Blade3
AI intern Temp HubBox Blade1
AI intern Temp HubBox Blade2
AI intern Temp HubBox Blade3
AI intern Temp Pitch1 HeatSink
AI intern Temp Pitch2 HeatSink
AI intern Temp Pitch3 HeatSink
AI intern ErrorStackBlade1
AI intern ErrorStackBlade2
AI intern ErrorStackBlade3
AI intern Temp BatteryBox Blade1
AI intern Temp BatteryBox Blade2
AI intern Temp BatteryBox Blade3
AI intern DC LinkVoltage1
AI intern DC LinkVoltage2
AI intern DC LinkVoltage3
Temp Yaw Motor1
Temp Yaw Motor2
Temp Yaw Motor3
Temp Yaw Motor4
AO DFIG Power Setpiont
AO DFIG Q Setpoint
AI DFIG Torque actual
AI DFIG SpeedGenerator Encoder
AI intern DFIG DC Link Voltage actual
AI intern DFIG MSC current
AI intern DFIG Main voltage
AI intern DFIG Main current
AI intern DFIG active power actual
AI intern DFIG reactive power actual
AI intern DFIG active power actual LSC
AI intern DFIG LSC current
AI intern DFIG Data log number
AI intern Damper OscMagnitude
AI intern Damper PassbandFullLoad
AI YawBrake TempRise1
AI YawBrake TempRise2
AI YawBrake TempRise3
AI YawBrake TempRise4
AI intern NacelleDrill at NorthPosSensor`

	newHeader := `TimeStamp
Turbine
Active Power
Wind Speed
Nacelle Pos
Wind Direction
Pitch Angle1
Pitch Angle2
Pitch Angle3
Generator Speed
Rotor Speed
Reactive Power
Frequency Grid
Gear Oil Pressure
Ambient Temp
Temp Nacelle
Temp GearBox HSS NDE
Temp GearBox HSS DE
Temp GearBox IMS DE
Temp Gear Oil Sump
Temp GearBox IMS NDE
Temp Generator Bearing DE
Temp Generator Bearing NDE
Temp Main Bearing
Temp Yaw Brake 1
Temp Yaw Brake 2
Temp G1L1
Temp G1L2
Temp G1L3
Temp Yaw Brake 4
Hydr System Pressure
Temp Bottom Control Section Low
Temp Conv1
Temp Conv2
Temp Conv3
R Pid Angle Out
I1
I2
I3
Nacelle Drill
Pitch Akku V1
Pitch Akku V2
Pitch Akku V3
Pitch Conv Current1
Pitch Conv Current2
Pitch Conv Current3
Pitch Angle SP Diff1
Pitch Angle SP Diff2
Pitch Angle SP Diff3
Rpm Diff
U1
U2
U3
Wind Speed Dif
Speed Rot FR
Wind Speed1
Wind Speed2
Wind Vane1
Wind Vane2
Current Asym
Wind Vane Diff
Pitch Speed2
Speed RPM Diff FR1 Rot CNT
Dr Tr Vib Value
In Last Error Conv1
In Last Error Conv2
In Last Error Conv3
AI Tower Vib Value Axial
Diff Gen Speed SP To Act
Temp YawBrake 5
Speed Generator Proximity
Speed Diff Encoder Proximity
Temp CabinetTopBox Low
Temp CabinetTopBox
Temp Bottom Control Section
Temp Bottom Power Section
Temp Bottom Power Section Low
Pitch1 Status High
Pitch2 Status High
Pitch3 Status High
In Position1 ch3
In Position2 ch3
In Position3 ch3
Temp Brake Blade1
Temp Brake Blade2
Temp Brake Blade3
Temp Pitch Motor Blade1
Temp Pitch Motor Blade2
Temp Pitch Motor Blade3
Temp Hub Additional1
Temp Hub Additional2
Temp Hub Additional3
Pitch1 Status Low
Pitch2 Status Low
Pitch3 Status Low
Battery Voltage Blade1 center
Battery Voltage Blade2 center
Battery Voltage Blade3 center
Battery Charging Cur Blade1
Battery Charging Cur Blade2
Battery Charging Cur Blade3
Battery Discharging Cur Blade1
Battery Discharging Cur Blade2
Battery Discharging Cur Blade3
Pitch Motor Brake Voltage Blade1
Pitch Motor Brake Voltage Blade2
Pitch Motor Brake Voltage Blade3
Pitch Motor Brake Current Blade1
Pitch Motor Brake Current Blade2
Pitch Motor Brake Current Blade3
Temp HubBox Blade1
Temp HubBox Blade2
Temp HubBox Blade3
Temp Pitch1 HeatSink
Temp Pitch2 HeatSink
Temp Pitch3 HeatSink
Error Stack Blade1
Error Stack Blade2
Error Stack Blade3
Temp Battery Box Blade1
Temp Battery Box Blade2
Temp Battery Box Blade3
DC Link Voltage1
DC Link Voltage2
DC Link Voltage3
Temp Yaw Motor1
Temp Yaw Motor2
Temp Yaw Motor3
Temp Yaw Motor4
AO DFIG Power Setpiont
AO DFIG Q Setpoint
AI DFIG Torque actual
AI DFIG Speed Generator Encoder
DFIG DC Link Voltage actual
DFIG MSC current
DFIG Main voltage
DFIG Main current
DFIG Active power actual
DFIG reactive power actual
DFIG active power actual LSC
DFIG LSC current
DFIG Data log number
Damper Osc Magnitude
Damper Pass band FullLoad
AI Yaw Brake Temp Rise1
AI Yaw Brake Temp Rise2
AI Yaw Brake Temp Rise3
AI Yaw Brake Temp Rise4
Nacelle Drill at North Pos Sensor`

	oldHeaderList := strings.Split(oldHeader, "\n")
	newHeaderList := strings.Split(newHeader, "\n")
	for idx, val := range oldHeaderList {
		fieldResult = append(fieldResult, strings.ToLower(strings.Replace(strings.TrimSuffix(val, " "), " ", "_", -69)))
		headerResult = append(headerResult, newHeaderList[idx])
	}

	return
}

func GetScadaHFDHeader() (headerResult []string, fieldResult []string) {
	// vArrRealtime := []string{"TimeStamp", "Turbine", "ActivePower_kW", "WindSpeed_ms", "NacellePos", "WindDirection",
	// 	"PitchAngle", "PitchAngle1", "PitchAngle2", "PitchAngle3", "GenSpeed_RPM", "RotorSpeed_RPM",
	// 	"ReactivePower_kVAr", "Frequency_Hz", "TempOutdoor", "TempNacelle", "TempGearBoxHSSNDE", "TempGearBoxHSSDE",
	// 	"TempGearBoxIMSDE", "TempGearBoxOilSump", "TempGearBoxIMSNDE", "TempGeneratorBearingDE", "TempGeneratorBearingNDE",
	// 	"TempHubBearing", "TempG1L1", "TempG1L2", "TempG1L3", "TempBottomControlSection", "TempConv1",
	// 	"TempConv2", "TempConv3", "PitchAccuV1", "PitchAccuV2", "PitchAccuV3", "PowerFactor",
	// 	"Total_Prod_Day_kWh"}

	fieldResult = []string{"TimeStamp", "Turbine", "Fast_ActivePower_kW", "Fast_WindSpeed_ms", "Slow_NacellePos", "Slow_WindDirection",
		"Fast_PitchAngle", "Fast_PitchAngle1", "Fast_PitchAngle2", "Fast_PitchAngle3", "Fast_GenSpeed_RPM", "Fast_RotorSpeed_RPM",
		"Fast_ReactivePower_kVAr", "Fast_Frequency_Hz", "Slow_TempOutdoor", "Slow_TempNacelle", "Slow_TempGearBoxHSSNDE", "Slow_TempGearBoxHSSDE",
		"Slow_TempGearBoxIMSDE", "Slow_TempGearBoxOilSump", "Slow_TempGearBoxIMSNDE", "Slow_TempGeneratorBearingDE", "Slow_TempGeneratorBearingNDE",
		"Slow_TempHubBearing", "Slow_TempG1L1", "Slow_TempG1L2", "Slow_TempG1L3", "Slow_TempBottomControlSection", "Slow_TempConv1",
		"Slow_TempConv2", "Slow_TempConv3", "Fast_PitchAccuV1", "Fast_PitchAccuV2", "Fast_PitchAccuV3", "Fast_PowerFactor",
		"Fast_Total_Prod_Day_kWh"}

	headerResult = []string{"TimeStamp", "Turbine", "Active Power", "Wind Speed", "Nacelle Pos", "Wind Direction",
		"Pitch Angle", "Pitch Angle1", "Pitch Angle2", "Pitch Angle3", "Generator Speed", "Rotor Speed",
		"Reactive Power", "Frequency Grid", "Ambient Temp", "Temp Nacelle", "Temp GearBox HSS NDE", "Temp GearBox HSS DE",
		"Temp GearBox IMS DE", "Temp GearBox Oil Sump", "Temp GearBox IMS NDE", "Temp Generator Bearing DE", "Temp Generator Bearing NDE",
		"Temp Main Bearing", "Temp G1L1", "Temp G1L2", "Temp G1L3", "Temp Bottom Control Section", "Temp Conv1",
		"Temp Conv2", "Temp Conv3", "Pitch Accu V1", "Pitch Accu V2", "Pitch Accu V3", "Power Factor",
		"Total Production Day"}

	return
}
<commit_msg>adding count field into scada hfd databrowser<commit_after>package controller

import (
	"strings"
)

func GetScadaOEMHeader() (headerResult []string, fieldResult []string) {
	oldHeader := `TimeStamp
Turbine
AI intern ActivPower
AI intern WindSpeed
AI intern NacellePos
AI intern WindDirection
AI intern PitchAngle1
AI intern PitchAngle2
AI intern PitchAngle3
C intern SpeedGenerator
C intern SpeedRotor
AI intern ReactivPower
AI intern Frequency Grid
AI GearOilPressure
Temp Outdoor
Temp Nacelle
Temp GearBox HSS NDE
Temp GearBox HSS DE
Temp GearBox IMS DE
Temp GearOilSump
Temp GearBox IMS NDE
Temp GeneratorBearing DE
Temp GeneratorBearing NDE
Temp MainBearing
Temp YawBrake 1
Temp YawBrake 2
Temp G1L1
Temp G1L2
Temp G1L3
Temp YawBrake 4
AI HydrSystemPressure
Temp BottomControlSection Low
AI intern TempConv1
AI intern TempConv2
AI intern TempConv3
AI intern R PidAngleOut
AI intern I1
AI intern I2
AI intern I3
AI intern NacelleDrill
AI intern PitchAkku V1
AI intern PitchAkku V2
AI intern PitchAkku V3
AI intern PitchConv Current1
AI intern PitchConv Current2
AI intern PitchConv Current3
AI intern PitchAngleSP Diff1
AI intern PitchAngleSP Diff2
AI intern PitchAngleSP Diff3
AI intern RpmDiff
AI intern U1
AI intern U2
AI intern U3
AI Intern WindSpeedDif
AI speed RotFR
AI WindSpeed1
AI WindSpeed2
AI WindVane1
AI WindVane2
AI internCurrentAsym
AI intern WindVaneDiff
AI intern PitchSpeed2
AI intern Speed RPMDiff FR1 RotCNT
AI DrTrVibValue
AI intern InLastErrorConv1
AI intern InLastErrorConv2
AI intern InLastErrorConv3
AI TowerVibValueAxial
AI intern DiffGenSpeedSPToAct
Temp YawBrake 5
AI intern SpeedGenerator Proximity
AI intern SpeedDiff Encoder Proximity
Temp CabinetTopBox Low
Temp CabinetTopBox
Temp BottomControlSection
Temp BottomPowerSection
Temp BottomPowerSection Low
AI intern Pitch1 Status High
AI intern Pitch2 Status High
AI intern Pitch3 Status High
AI intern InPosition1 ch3
AI intern InPosition2 ch3
AI intern InPosition3 ch3
AI intern Temp Brake Blade1
AI intern Temp Brake Blade2
AI intern Temp Brake Blade3
AI intern Temp PitchMotor Blade1
AI intern Temp PitchMotor Blade2
AI intern Temp PitchMotor Blade3
AI intern Temp Hub Additional1
AI intern Temp Hub Additional2
AI intern Temp Hub Additional3
AI intern Pitch1 Status Low
AI intern Pitch2 Status Low
AI intern Pitch3 Status Low
AI intern Battery VoltageBlade1 center
AI intern Battery VoltageBlade2 center
AI intern Battery VoltageBlade3 center
AI intern Battery ChargingCur Blade1
AI intern Battery ChargingCur Blade2
AI intern Battery ChargingCur Blade3
AI intern Battery DischargingCur Blade1
AI intern Battery DischargingCur Blade2
AI intern Battery DischargingCur Blade3
AI intern PitchMotor BrakeVoltage Blade1
AI intern PitchMotor BrakeVoltage Blade2
AI intern PitchMotor BrakeVoltage Blade3
AI intern PitchMotor BrakeCurrent Blade1
AI intern PitchMotor BrakeCurrent Blade2
AI intern PitchMotor BrakeCurrent Blade3
AI intern Temp HubBox Blade1
AI intern Temp HubBox Blade2
AI intern Temp HubBox Blade3
AI intern Temp Pitch1 HeatSink
AI intern Temp Pitch2 HeatSink
AI intern Temp Pitch3 HeatSink
AI intern ErrorStackBlade1
AI intern ErrorStackBlade2
AI intern ErrorStackBlade3
AI intern Temp BatteryBox Blade1
AI intern Temp BatteryBox Blade2
AI intern Temp BatteryBox Blade3
AI intern DC LinkVoltage1
AI intern DC LinkVoltage2
AI intern DC LinkVoltage3
Temp Yaw Motor1
Temp Yaw Motor2
Temp Yaw Motor3
Temp Yaw Motor4
AO DFIG Power Setpiont
AO DFIG Q Setpoint
AI DFIG Torque actual
AI DFIG SpeedGenerator Encoder
AI intern DFIG DC Link Voltage actual
AI intern DFIG MSC current
AI intern DFIG Main voltage
AI intern DFIG Main current
AI intern DFIG active power actual
AI intern DFIG reactive power actual
AI intern DFIG active power actual LSC
AI intern DFIG LSC current
AI intern DFIG Data log number
AI intern Damper OscMagnitude
AI intern Damper PassbandFullLoad
AI YawBrake TempRise1
AI YawBrake TempRise2
AI YawBrake TempRise3
AI YawBrake TempRise4
AI intern NacelleDrill at NorthPosSensor`

	newHeader := `TimeStamp
Turbine
Active Power
Wind Speed
Nacelle Pos
Wind Direction
Pitch Angle1
Pitch Angle2
Pitch Angle3
Generator Speed
Rotor Speed
Reactive Power
Frequency Grid
Gear Oil Pressure
Ambient Temp
Temp Nacelle
Temp GearBox HSS NDE
Temp GearBox HSS DE
Temp GearBox IMS DE
Temp Gear Oil Sump
Temp GearBox IMS NDE
Temp Generator Bearing DE
Temp Generator Bearing NDE
Temp Main Bearing
Temp Yaw Brake 1
Temp Yaw Brake 2
Temp G1L1
Temp G1L2
Temp G1L3
Temp Yaw Brake 4
Hydr System Pressure
Temp Bottom Control Section Low
Temp Conv1
Temp Conv2
Temp Conv3
R Pid Angle Out
I1
I2
I3
Nacelle Drill
Pitch Akku V1
Pitch Akku V2
Pitch Akku V3
Pitch Conv Current1
Pitch Conv Current2
Pitch Conv Current3
Pitch Angle SP Diff1
Pitch Angle SP Diff2
Pitch Angle SP Diff3
Rpm Diff
U1
U2
U3
Wind Speed Dif
Speed Rot FR
Wind Speed1
Wind Speed2
Wind Vane1
Wind Vane2
Current Asym
Wind Vane Diff
Pitch Speed2
Speed RPM Diff FR1 Rot CNT
Dr Tr Vib Value
In Last Error Conv1
In Last Error Conv2
In Last Error Conv3
AI Tower Vib Value Axial
Diff Gen Speed SP To Act
Temp YawBrake 5
Speed Generator Proximity
Speed Diff Encoder Proximity
Temp CabinetTopBox Low
Temp CabinetTopBox
Temp Bottom Control Section
Temp Bottom Power Section
Temp Bottom Power Section Low
Pitch1 Status High
Pitch2 Status High
Pitch3 Status High
In Position1 ch3
In Position2 ch3
In Position3 ch3
Temp Brake Blade1
Temp Brake Blade2
Temp Brake Blade3
Temp Pitch Motor Blade1
Temp Pitch Motor Blade2
Temp Pitch Motor Blade3
Temp Hub Additional1
Temp Hub Additional2
Temp Hub Additional3
Pitch1 Status Low
Pitch2 Status Low
Pitch3 Status Low
Battery Voltage Blade1 center
Battery Voltage Blade2 center
Battery Voltage Blade3 center
Battery Charging Cur Blade1
Battery Charging Cur Blade2
Battery Charging Cur Blade3
Battery Discharging Cur Blade1
Battery Discharging Cur Blade2
Battery Discharging Cur Blade3
Pitch Motor Brake Voltage Blade1
Pitch Motor Brake Voltage Blade2
Pitch Motor Brake Voltage Blade3
Pitch Motor Brake Current Blade1
Pitch Motor Brake Current Blade2
Pitch Motor Brake Current Blade3
Temp HubBox Blade1
Temp HubBox Blade2
Temp HubBox Blade3
Temp Pitch1 HeatSink
Temp Pitch2 HeatSink
Temp Pitch3 HeatSink
Error Stack Blade1
Error Stack Blade2
Error Stack Blade3
Temp Battery Box Blade1
Temp Battery Box Blade2
Temp Battery Box Blade3
DC Link Voltage1
DC Link Voltage2
DC Link Voltage3
Temp Yaw Motor1
Temp Yaw Motor2
Temp Yaw Motor3
Temp Yaw Motor4
AO DFIG Power Setpiont
AO DFIG Q Setpoint
AI DFIG Torque actual
AI DFIG Speed Generator Encoder
DFIG DC Link Voltage actual
DFIG MSC current
DFIG Main voltage
DFIG Main current
DFIG Active power actual
DFIG reactive power actual
DFIG active power actual LSC
DFIG LSC current
DFIG Data log number
Damper Osc Magnitude
Damper Pass band FullLoad
AI Yaw Brake Temp Rise1
AI Yaw Brake Temp Rise2
AI Yaw Brake Temp Rise3
AI Yaw Brake Temp Rise4
Nacelle Drill at North Pos Sensor`

	oldHeaderList := strings.Split(oldHeader, "\n")
	newHeaderList := strings.Split(newHeader, "\n")
	for idx, val := range oldHeaderList {
		fieldResult = append(fieldResult, strings.ToLower(strings.Replace(strings.TrimSuffix(val, " "), " ", "_", -69)))
		headerResult = append(headerResult, newHeaderList[idx])
	}

	return
}

func GetScadaHFDHeader() (headerResult []string, fieldResult []string) {
	// vArrRealtime := []string{"TimeStamp", "Turbine", "ActivePower_kW", "WindSpeed_ms", "NacellePos", "WindDirection",
	// 	"PitchAngle", "PitchAngle1", "PitchAngle2", "PitchAngle3", "GenSpeed_RPM", "RotorSpeed_RPM",
	// 	"ReactivePower_kVAr", "Frequency_Hz", "TempOutdoor", "TempNacelle", "TempGearBoxHSSNDE", "TempGearBoxHSSDE",
	// 	"TempGearBoxIMSDE", "TempGearBoxOilSump", "TempGearBoxIMSNDE", "TempGeneratorBearingDE", "TempGeneratorBearingNDE",
	// 	"TempHubBearing", "TempG1L1", "TempG1L2", "TempG1L3", "TempBottomControlSection", "TempConv1",
	// 	"TempConv2", "TempConv3", "PitchAccuV1", "PitchAccuV2", "PitchAccuV3", "PowerFactor",
	// 	"Total_Prod_Day_kWh"}

	fieldResult = []string{"TimeStamp", "Turbine", "Fast_ActivePower_kW", "Fast_WindSpeed_ms", "Slow_NacellePos", "Slow_WindDirection",
		"Fast_PitchAngle", "Fast_PitchAngle1", "Fast_PitchAngle2", "Fast_PitchAngle3", "Fast_GenSpeed_RPM", "Fast_RotorSpeed_RPM",
		"Fast_ReactivePower_kVAr", "Fast_Frequency_Hz", "Slow_TempOutdoor", "Slow_TempNacelle", "Slow_TempGearBoxHSSNDE", "Slow_TempGearBoxHSSDE",
		"Slow_TempGearBoxIMSDE", "Slow_TempGearBoxOilSump", "Slow_TempGearBoxIMSNDE", "Slow_TempGeneratorBearingDE", "Slow_TempGeneratorBearingNDE",
		"Slow_TempHubBearing", "Slow_TempG1L1", "Slow_TempG1L2", "Slow_TempG1L3", "Slow_TempBottomControlSection", "Slow_TempConv1",
		"Slow_TempConv2", "Slow_TempConv3", "Fast_PitchAccuV1", "Fast_PitchAccuV2", "Fast_PitchAccuV3", "Fast_PowerFactor",
		"Fast_Total_Prod_Day_kWh",
		"Fast_ActivePower_kW_Count", "Fast_WindSpeed_ms_Count", "Slow_NacellePos_Count", "Slow_WindDirection_Count",
		"Fast_PitchAngle_Count", "Fast_PitchAngle1_Count", "Fast_PitchAngle2_Count", "Fast_PitchAngle3_Count", "Fast_GenSpeed_RPM_Count", "Fast_RotorSpeed_RPM_Count",
		"Fast_ReactivePower_kVAr_Count", "Fast_Frequency_Hz_Count", "Slow_TempOutdoor_Count", "Slow_TempNacelle_Count", "Slow_TempGearBoxHSSNDE_Count", "Slow_TempGearBoxHSSDE_Count",
		"Slow_TempGearBoxIMSDE_Count", "Slow_TempGearBoxOilSump_Count", "Slow_TempGearBoxIMSNDE_Count", "Slow_TempGeneratorBearingDE_Count", "Slow_TempGeneratorBearingNDE_Count",
		"Slow_TempHubBearing_Count", "Slow_TempG1L1_Count", "Slow_TempG1L2_Count", "Slow_TempG1L3_Count", "Slow_TempBottomControlSection_Count", "Slow_TempConv1_Count",
		"Slow_TempConv2_Count", "Slow_TempConv3_Count", "Fast_PitchAccuV1_Count", "Fast_PitchAccuV2_Count", "Fast_PitchAccuV3_Count", "Fast_PowerFactor_Count",
		"Fast_Total_Prod_Day_kWh_Count"}

	headerResult = []string{"TimeStamp", "Turbine", "Active Power", "Wind Speed", "Nacelle Pos", "Wind Direction",
		"Pitch Angle", "Pitch Angle1", "Pitch Angle2", "Pitch Angle3", "Generator Speed", "Rotor Speed",
		"Reactive Power", "Frequency Grid", "Ambient Temp", "Temp Nacelle", "Temp GearBox HSS NDE", "Temp GearBox HSS DE",
		"Temp GearBox IMS DE", "Temp GearBox Oil Sump", "Temp GearBox IMS NDE", "Temp Generator Bearing DE", "Temp Generator Bearing NDE",
		"Temp Main Bearing", "Temp G1L1", "Temp G1L2", "Temp G1L3", "Temp Bottom Control Section", "Temp Conv1",
		"Temp Conv2", "Temp Conv3", "Pitch Accu V1", "Pitch Accu V2", "Pitch Accu V3", "Power Factor",
		"Total Production Day",
		"Active Power Count", "Wind Speed Count", "Nacelle Pos Count", "Wind Direction Count",
		"Pitch Angle Count", "Pitch Angle1 Count", "Pitch Angle2 Count", "Pitch Angle3 Count", "Generator Speed Count", "Rotor Speed Count",
		"Reactive Power Count", "Frequency Grid Count", "Ambient Temp Count", "Temp Nacelle Count", "Temp GearBox HSS NDE Count", "Temp GearBox HSS DE Count",
		"Temp GearBox IMS DE Count", "Temp GearBox Oil Sump Count", "Temp GearBox IMS NDE Count", "Temp Generator Bearing DE Count", "Temp Generator Bearing NDE Count",
		"Temp Main Bearing Count", "Temp G1L1 Count", "Temp G1L2 Count", "Temp G1L3 Count", "Temp Bottom Control Section Count", "Temp Conv1 Count",
		"Temp Conv2 Count", "Temp Conv3 Count", "Pitch Accu V1 Count", "Pitch Accu V2 Count", "Pitch Accu V3 Count", "Power Factor Count",
		"Total Production Day Count"}

	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wiring_test

import (
	"testing"

	. "github.com/jacobsa/ogletest"
)

func TestIntegration(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type IntegrationTest struct {
}

func init() { RegisterTestSuite(&IntegrationTest{}) }

func (t *IntegrationTest) SetUp(i *TestInfo) {
	panic("TODO")
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *IntegrationTest) WrongPasswordForRegistry() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) WrongPasswordForDirSaver() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) WrongPasswordForDirRestorer() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) EmptyDirectory() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) SingleFile() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) SingleEmptySubDir() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) DecentHierarchy() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) StableResult() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) Symlinks() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) HardLinks() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) Permissions() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) OwnershipInfo() {
	AssertFalse(true, "TODO")
}

func (t *IntegrationTest) Mtime() {
	AssertFalse(true, "TODO")
}
<commit_msg>Rejiggered test names.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wiring_test

import (
	"testing"

	. "github.com/jacobsa/ogletest"
)

func TestIntegration(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Wiring
////////////////////////////////////////////////////////////////////////

type WiringTest struct {
}

func init() { RegisterTestSuite(&WiringTest{}) }

func (t *WiringTest) WrongPasswordForRegistry() {
	AssertFalse(true, "TODO")
}

func (t *WiringTest) WrongPasswordForDirSaver() {
	AssertFalse(true, "TODO")
}

func (t *WiringTest) WrongPasswordForDirRestorer() {
	AssertFalse(true, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Saving and restoring
////////////////////////////////////////////////////////////////////////

type SaveAndRestoreTest struct {
}

func init() { RegisterTestSuite(&SaveAndRestoreTest{}) }

func (t *SaveAndRestoreTest) SetUp(i *TestInfo) {
	panic("TODO")
}

func (t *SaveAndRestoreTest) EmptyDirectory() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) SingleFile() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) SingleEmptySubDir() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) DecentHierarchy() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) StableResult() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) Symlinks() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) HardLinks() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) Permissions() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) OwnershipInfo() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) Mtime() {
	AssertFalse(true, "TODO")
}

func (t *SaveAndRestoreTest) BackupExclusions() {
	AssertFalse(true, "TODO")
}
<|endoftext|>