init
This commit is contained in:
commit
4e4210a43f
62
cmd/parse/main.go
Normal file
62
cmd/parse/main.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
iface "git.tcp.direct/kayos/ifupdown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
eth0 := &iface.NetworkInterface{}
|
||||
switch {
|
||||
case len(os.Args) < 2:
|
||||
buf := &bytes.Buffer{}
|
||||
var empty = 0
|
||||
for {
|
||||
n, err := buf.ReadFrom(os.Stdin)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if n == 0 {
|
||||
empty++
|
||||
}
|
||||
if empty > 100 {
|
||||
break
|
||||
}
|
||||
}
|
||||
n, err := eth0.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if n != len(buf.Bytes()) {
|
||||
panic("short write")
|
||||
}
|
||||
default:
|
||||
dat, err := os.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n, err := eth0.Write(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if n != len(dat) {
|
||||
panic("short write")
|
||||
}
|
||||
}
|
||||
dat, err := json.MarshalIndent(eth0, "", "\t")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = eth0.Validate(); err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
_, _ = os.Stdout.Write(dat)
|
||||
}
|
BIN
cmd/parse/parse
Executable file
BIN
cmd/parse/parse
Executable file
Binary file not shown.
19
errors.go
Normal file
19
errors.go
Normal file
@ -0,0 +1,19 @@
|
||||
package iface
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidAddress = errors.New("invalid address")
|
||||
ErrInvalidMask = errors.New("invalid mask")
|
||||
ErrInvalidBroadcast = errors.New("invalid broadcast")
|
||||
ErrInvalidGateway = errors.New("invalid gateway")
|
||||
ErrInvalidAddressVersion = errors.New("invalid address version")
|
||||
ErrAddressSetWhenDHCP = errors.New("address set when DHCP enabled")
|
||||
ErrAddressNotSetStatic = errors.New("address not set with static config")
|
||||
ErrMaskNotSetStatic = errors.New("mask not set with static config")
|
||||
ErrAdressNotLoopback = errors.New("address must be loopback when config is loopback")
|
||||
ErrInterfaceHasErrors = errors.New("interface has errors")
|
||||
ErrUnallocatedInterface = errors.New("unallocated interface")
|
||||
ErrInvalidIfaceData = errors.New("invalid interface data provided")
|
||||
ErrMultipleInterfaces = errors.New("multiple interfaces in data provided")
|
||||
)
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module git.tcp.direct/kayos/ifupdown
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
git.tcp.direct/kayos/common v0.9.6
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
)
|
||||
|
||||
require github.com/hashicorp/errwrap v1.0.0 // indirect
|
8
go.sum
Normal file
8
go.sum
Normal file
@ -0,0 +1,8 @@
|
||||
git.tcp.direct/kayos/common v0.9.6 h1:EITtktxZF/zkzqAhZZxvm6cZpFYoZ0P/gLB9RPatKUY=
|
||||
git.tcp.direct/kayos/common v0.9.6/go.mod h1:8y9b+PN1+ZVaQ/VugD9dkKe+uqhE8jH7a64RyF7h2rM=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
102
ifaces.go
Normal file
102
ifaces.go
Normal file
@ -0,0 +1,102 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"git.tcp.direct/kayos/common/pool"
|
||||
)
|
||||
|
||||
type poolGroup struct {
|
||||
Buffers pool.BufferFactory
|
||||
Strs pool.StringFactory
|
||||
}
|
||||
|
||||
var pools = poolGroup{Buffers: pool.NewBufferFactory(), Strs: pool.NewStringFactory()}
|
||||
|
||||
type MultiParser struct {
|
||||
Interfaces map[string]*NetworkInterface
|
||||
Errs []error
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func NewMultiParser() *MultiParser {
|
||||
return &MultiParser{
|
||||
Interfaces: make(map[string]*NetworkInterface),
|
||||
Errs: make([]error, 0),
|
||||
buf: make([]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *MultiParser) Write(data []byte) (int, error) {
|
||||
p.buf = append(p.buf, data...)
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (p *MultiParser) Parse() error {
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(p.buf)))
|
||||
|
||||
index := 0
|
||||
currentIfaceName := ""
|
||||
|
||||
buf := pools.Buffers.Get()
|
||||
defer pools.Buffers.MustPut(buf)
|
||||
|
||||
flush := func(name string) (*NetworkInterface, bool) {
|
||||
if len(buf.Bytes()) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
defer buf.MustReset()
|
||||
newIface := NewNetworkInterface(name)
|
||||
if _, err := buf.WriteTo(newIface); err != nil {
|
||||
p.Errs = append(p.Errs, err)
|
||||
return nil, false
|
||||
}
|
||||
p.Interfaces[newIface.Name] = newIface
|
||||
return newIface, true
|
||||
}
|
||||
|
||||
w := func(s string) {
|
||||
_, _ = buf.WriteString(s)
|
||||
_ = buf.WriteByte('\n')
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
upForChange := currentIfaceName == "" ||
|
||||
(currentIfaceName != "" && !strings.Contains(line, currentIfaceName)) ||
|
||||
index == 0
|
||||
startDetected := len(strings.Fields(line)) > 1 &&
|
||||
(strings.HasPrefix(line, "auto") ||
|
||||
strings.HasPrefix(line, "allow-") ||
|
||||
strings.HasPrefix(line, "iface"))
|
||||
|
||||
switch {
|
||||
case line == "", strings.HasPrefix(line, "#"):
|
||||
continue
|
||||
case upForChange && startDetected:
|
||||
newName := strings.Fields(line)[1]
|
||||
if ifa, ok := flush(newName); ok {
|
||||
currentIfaceName = ifa.Name
|
||||
index++
|
||||
}
|
||||
w(line)
|
||||
continue
|
||||
default:
|
||||
w(line)
|
||||
}
|
||||
}
|
||||
|
||||
if len(buf.Bytes()) > 0 {
|
||||
flush("unknown")
|
||||
}
|
||||
me := &multierror.Error{}
|
||||
for _, err := range p.Errs {
|
||||
if err != nil {
|
||||
me = multierror.Append(me, err)
|
||||
}
|
||||
}
|
||||
return me.ErrorOrNil()
|
||||
}
|
42
ifaces_test.go
Normal file
42
ifaces_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse_SimpleValidData(t *testing.T) {
|
||||
mp := NewMultiParser()
|
||||
data := []byte(`# The loopback network interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
# The primary network interface
|
||||
allow-hotplug eth0
|
||||
iface eth0 inet dhcp
|
||||
`)
|
||||
_, err := mp.Write(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Write failed: %v", err)
|
||||
}
|
||||
|
||||
err = mp.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
if len(mp.Interfaces) != 2 {
|
||||
t.Fatalf("Expected 2 interfaces, got: %d", len(mp.Interfaces))
|
||||
}
|
||||
|
||||
loIface, ok := mp.Interfaces["lo"]
|
||||
if !ok || loIface.Name != "lo" {
|
||||
t.Fatalf("Expected to find interface 'lo', got: %+v", loIface)
|
||||
}
|
||||
|
||||
eth0Iface, ok := mp.Interfaces["eth0"]
|
||||
if !ok || eth0Iface.Name != "eth0" {
|
||||
t.Fatalf("Expected to find interface 'eth0', got: %+v", eth0Iface)
|
||||
}
|
||||
}
|
||||
|
||||
// Add more test functions here to check other aspects and edge cases.
|
712
netif.go
Normal file
712
netif.go
Normal file
@ -0,0 +1,712 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Hooks struct {
|
||||
// PreUp of the interface
|
||||
PreUp []string
|
||||
// PostUp of the interface
|
||||
PostUp []string
|
||||
// PreDown of the interface
|
||||
PreDown []string
|
||||
// PostDown of the interface
|
||||
PostDown []string
|
||||
}
|
||||
|
||||
type AddressConfig uint8
|
||||
|
||||
const (
|
||||
AddressConfigUnset AddressConfig = iota
|
||||
AddressConfigLoopback
|
||||
AddressConfigDHCP
|
||||
AddressConfigStatic
|
||||
AddressConfigManual
|
||||
)
|
||||
|
||||
var addressConfigMap = map[AddressConfig]string{
|
||||
AddressConfigLoopback: "loopback",
|
||||
AddressConfigDHCP: "dhcp",
|
||||
AddressConfigStatic: "static",
|
||||
AddressConfigManual: "manual",
|
||||
}
|
||||
|
||||
func (ac AddressConfig) String() string {
|
||||
return addressConfigMap[ac]
|
||||
}
|
||||
|
||||
type AddressVersion uint8
|
||||
|
||||
const (
|
||||
AddressVersionNil AddressVersion = iota
|
||||
AddressVersion4
|
||||
AddressVersion6
|
||||
)
|
||||
|
||||
func (at AddressVersion) String() string {
|
||||
switch at {
|
||||
case AddressVersion4:
|
||||
return "inet"
|
||||
case AddressVersion6:
|
||||
return "inet6"
|
||||
default:
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NetworkInterface follows the format of ifupdown /etc/network/interfaces
|
||||
type NetworkInterface struct {
|
||||
// Name of the interface.
|
||||
Name string `json:"name"`
|
||||
// Hotplug determines if hot plugging is allowed.
|
||||
Hotplug bool `json:"hotplug,omitempty"`
|
||||
// Auto determines if the interface is automatically brought up.
|
||||
Auto bool `json:"auto,omitempty"`
|
||||
// Address determines the static IP address of the interface.
|
||||
Address net.IP `json:"address,omitempty"`
|
||||
|
||||
// Netmask determines the netmask of the interface.
|
||||
Netmask net.IPMask `json:"netmask,omitempty"`
|
||||
|
||||
Broadcast net.IP `json:"broadcast,omitempty"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Config AddressConfig `json:"config,omitempty"`
|
||||
Version AddressVersion `json:"version,omitempty"`
|
||||
|
||||
// DNSServers of the interface.
|
||||
DNSServers []net.IP `json:"dns_servers,omitempty"`
|
||||
// DNSSearch of the interface.
|
||||
DNSSearch []string `json:"dns_search,omitempty"`
|
||||
// MACAddress of the interface.
|
||||
MACAddress net.HardwareAddr `json:"mac_address,omitempty"`
|
||||
// Hooks contains the pre/post up/down hooks.
|
||||
Hooks Hooks `json:"hooks,omitempty"`
|
||||
|
||||
dirty bool
|
||||
allocated bool
|
||||
errs []error
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func NewNetworkInterface(name string) *NetworkInterface {
|
||||
return &NetworkInterface{
|
||||
allocated: false,
|
||||
Name: name,
|
||||
Auto: true,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) allocate() {
|
||||
if iface.RWMutex == nil {
|
||||
iface.RWMutex = &sync.RWMutex{}
|
||||
}
|
||||
iface.Lock()
|
||||
iface.dirty = true
|
||||
iface.allocated = true
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) err() error {
|
||||
if len(iface.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
err := ErrInterfaceHasErrors
|
||||
for i, e := range iface.errs {
|
||||
if err != nil {
|
||||
if i == 0 {
|
||||
err = fmt.Errorf("%w: %w", err, e)
|
||||
continue
|
||||
}
|
||||
err = fmt.Errorf("%w, %w", err, e)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) Validate() error {
|
||||
if !iface.dirty && len(iface.errs) > 0 {
|
||||
if err := iface.err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if iface.RWMutex == nil {
|
||||
iface.RWMutex = &sync.RWMutex{}
|
||||
}
|
||||
|
||||
iface.RLock()
|
||||
defer iface.RUnlock()
|
||||
|
||||
iface.errs = iface.errs[:0]
|
||||
|
||||
if iface.allocated != true {
|
||||
iface.errs = append(iface.errs, ErrUnallocatedInterface)
|
||||
return iface.err()
|
||||
}
|
||||
|
||||
if iface.Config == AddressConfigUnset {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("address config not set"))
|
||||
}
|
||||
|
||||
switch iface.Config {
|
||||
case AddressConfigDHCP:
|
||||
if iface.Address != nil {
|
||||
iface.errs = append(iface.errs, ErrAddressSetWhenDHCP)
|
||||
}
|
||||
case AddressConfigStatic:
|
||||
switch {
|
||||
case iface.Address == nil:
|
||||
iface.errs = append(iface.errs, ErrAddressNotSetStatic)
|
||||
case iface.Netmask == nil && iface.Version == AddressVersion4:
|
||||
iface.errs = append(iface.errs, ErrMaskNotSetStatic)
|
||||
case iface.Address.IsUnspecified():
|
||||
iface.errs = append(iface.errs, ErrInvalidAddress)
|
||||
}
|
||||
case AddressConfigLoopback:
|
||||
if iface.Address != nil && !iface.Address.IsLoopback() {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("%w: %v", ErrAdressNotLoopback, iface.Address))
|
||||
}
|
||||
}
|
||||
|
||||
switch iface.Version {
|
||||
case AddressVersion4, AddressVersion6:
|
||||
break
|
||||
default:
|
||||
iface.errs = append(iface.errs, fmt.Errorf(
|
||||
"[%s] %w: %v",
|
||||
iface.Name, ErrInvalidAddressVersion, iface.Version,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
iface.dirty = false
|
||||
return iface.err()
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithAddress(address string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
_, ipn, err := net.ParseCIDR(address)
|
||||
if err == nil {
|
||||
iface = iface.WithNetmask(ipn.Mask.Size())
|
||||
}
|
||||
iface.Address = net.ParseIP(address)
|
||||
if iface.Address == nil {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid address: %s", address))
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithLoopback() *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = AddressConfigLoopback
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithDHCP() *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = AddressConfigDHCP
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithStatic() *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = AddressConfigStatic
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithManual() *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = AddressConfigManual
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithAddressConfig(config AddressConfig) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = config
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithAddressVersion(version AddressVersion) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Version = version
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithNetmask(mask, bits int) *NetworkInterface {
|
||||
iface.allocate()
|
||||
var parsedMask net.IPMask
|
||||
if iface.Address != nil {
|
||||
if iface.Address.To4() != nil || iface.Version == AddressVersion4 {
|
||||
parsedMask = net.CIDRMask(mask, bits)
|
||||
} else {
|
||||
parsedMask = net.CIDRMask(mask, bits)
|
||||
}
|
||||
if parsedMask == nil {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid mask: %d", mask))
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
}
|
||||
iface.Netmask = parsedMask
|
||||
// iface.Netmask = netmask
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithBroadcast(broadcast string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Broadcast = net.ParseIP(broadcast)
|
||||
if iface.Broadcast == nil {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid broadcast: %s", broadcast))
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithGateway(gateway string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Gateway = net.ParseIP(gateway)
|
||||
if iface.Gateway == nil {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid gateway: %s", gateway))
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithConfigMethod(config AddressConfig) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Config = config
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithVersion(version AddressVersion) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.Version = version
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithDNS(dnsServers []string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
for _, dns := range dnsServers {
|
||||
if nsIP := net.ParseIP(dns); nsIP != nil {
|
||||
iface.DNSServers = append(iface.DNSServers, nsIP)
|
||||
} else {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid dns server: %s", dns))
|
||||
}
|
||||
}
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithDNSSearch(dnsSearch []string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
iface.DNSSearch = dnsSearch
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) WithMACAddress(macAddress string) *NetworkInterface {
|
||||
iface.allocate()
|
||||
var err error
|
||||
iface.MACAddress, err = net.ParseMAC(macAddress)
|
||||
if err != nil {
|
||||
iface.errs = append(iface.errs, fmt.Errorf("invalid mac address: %s", macAddress))
|
||||
}
|
||||
iface.Unlock()
|
||||
return iface
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) netMaskString(mask net.IPMask) string {
|
||||
if mask == nil {
|
||||
return ""
|
||||
}
|
||||
var m netip.Addr
|
||||
if iface.Version == AddressVersion4 {
|
||||
m = netip.AddrFrom4([4]byte{mask[0], mask[1], mask[2], mask[3]})
|
||||
} else {
|
||||
m = netip.AddrFrom16([16]byte{
|
||||
mask[0], mask[1], mask[2], mask[3],
|
||||
mask[4], mask[5], mask[6], mask[7],
|
||||
mask[8], mask[9], mask[10], mask[11],
|
||||
mask[12], mask[13], mask[14], mask[15],
|
||||
})
|
||||
}
|
||||
if !m.IsValid() {
|
||||
return ""
|
||||
}
|
||||
return m.String()
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) String() string {
|
||||
if iface.RWMutex == nil {
|
||||
iface.RWMutex = new(sync.RWMutex)
|
||||
}
|
||||
iface.RLock()
|
||||
defer iface.RUnlock()
|
||||
if !iface.allocated {
|
||||
return ""
|
||||
}
|
||||
if iface.Validate() != nil {
|
||||
return ""
|
||||
}
|
||||
str := pools.Strs.Get()
|
||||
defer pools.Strs.MustPut(str)
|
||||
w := func(s string) {
|
||||
if len(s) > 0 {
|
||||
str.MustWriteString(s)
|
||||
}
|
||||
}
|
||||
if err := iface.write(w); err != nil && !errors.Is(err, io.EOF) {
|
||||
return ""
|
||||
}
|
||||
return str.String()
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) Write(p []byte) (int, error) {
|
||||
iface.allocate()
|
||||
defer iface.Unlock()
|
||||
xerox := bufio.NewScanner(bytes.NewReader(p))
|
||||
numIfaces := strings.Count(string(p), "iface")
|
||||
if numIfaces > 1 {
|
||||
return 0, ErrMultipleInterfaces
|
||||
}
|
||||
for xerox.Scan() {
|
||||
normalized := strings.TrimSpace(xerox.Text())
|
||||
switch {
|
||||
case strings.HasPrefix(normalized, "#"):
|
||||
continue
|
||||
case strings.HasPrefix(normalized, "auto"):
|
||||
iface.Auto = true
|
||||
continue
|
||||
case strings.HasPrefix(normalized, "allow-hotplug"):
|
||||
iface.Hotplug = true
|
||||
case strings.HasPrefix(normalized, "iface"):
|
||||
for i, fragment := range strings.Fields(normalized) {
|
||||
// println(i, fragment)
|
||||
switch i {
|
||||
case 0:
|
||||
if fragment != "iface" {
|
||||
return 0, fmt.Errorf("%w: %s", ErrInvalidIfaceData, normalized)
|
||||
}
|
||||
case 1:
|
||||
iface.Name = fragment
|
||||
case 2:
|
||||
switch fragment {
|
||||
case "inet":
|
||||
iface.Version = AddressVersion4
|
||||
// println("version 4")
|
||||
case "inet6":
|
||||
iface.Version = AddressVersion6
|
||||
// println("version 6")
|
||||
default:
|
||||
}
|
||||
case 3:
|
||||
switch fragment {
|
||||
case "static":
|
||||
iface.Config = AddressConfigStatic
|
||||
case "dhcp":
|
||||
iface.Config = AddressConfigDHCP
|
||||
case "manual":
|
||||
iface.Config = AddressConfigManual
|
||||
case "loopback":
|
||||
iface.Config = AddressConfigLoopback
|
||||
default:
|
||||
return 0, fmt.Errorf("%w: %s", ErrInvalidIfaceData, normalized)
|
||||
}
|
||||
default:
|
||||
//
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "address"):
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
if strings.Contains(fragment, "/") {
|
||||
prfx, _ := netip.ParsePrefix(fragment)
|
||||
iface.Address = net.ParseIP(prfx.Addr().String())
|
||||
if iface.Address == nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
iface.Netmask = net.CIDRMask(prfx.Bits(), 8*len(iface.Address))
|
||||
continue
|
||||
}
|
||||
iface.Address = net.ParseIP(fragment)
|
||||
if iface.Address == nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
default:
|
||||
//
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "netmask"):
|
||||
if iface.Netmask != nil || iface.Version != AddressVersion4 {
|
||||
continue
|
||||
}
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
maskBytes := net.ParseIP(fragment)
|
||||
if maskBytes == nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
iface.Netmask = net.IPv4Mask(maskBytes[12], maskBytes[13], maskBytes[14], maskBytes[15])
|
||||
if iface.Netmask == nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
default:
|
||||
//
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "gateway"):
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
iface.Gateway = net.ParseIP(fragment)
|
||||
if iface.Gateway == nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
default:
|
||||
//
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "dns-nameservers"):
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
default:
|
||||
iface.DNSServers = append(iface.DNSServers, net.ParseIP(fragment))
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "dns-search"):
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
default:
|
||||
iface.DNSSearch = append(iface.DNSSearch, fragment)
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(normalized, "pre-up"):
|
||||
hook := strings.TrimPrefix(normalized, "pre-up ")
|
||||
if len(hook) == 0 {
|
||||
continue
|
||||
}
|
||||
iface.Hooks.PreUp = append(iface.Hooks.PreUp, hook)
|
||||
case strings.HasPrefix(normalized, "post-up"):
|
||||
hook := strings.TrimPrefix(normalized, "post-up ")
|
||||
if len(hook) == 0 {
|
||||
continue
|
||||
}
|
||||
iface.Hooks.PostUp = append(iface.Hooks.PostUp, hook)
|
||||
case strings.HasPrefix(normalized, "pre-down"):
|
||||
hook := strings.TrimPrefix(normalized, "pre-down ")
|
||||
if len(hook) == 0 {
|
||||
continue
|
||||
}
|
||||
iface.Hooks.PreDown = append(iface.Hooks.PreDown, hook)
|
||||
case strings.HasPrefix(normalized, "post-down"):
|
||||
hook := strings.TrimPrefix(normalized, "post-down ")
|
||||
if len(hook) == 0 {
|
||||
continue
|
||||
}
|
||||
iface.Hooks.PostDown = append(iface.Hooks.PostDown, hook)
|
||||
case strings.HasPrefix(normalized, "hwaddress"):
|
||||
for i, fragment := range strings.Split(normalized, " ") {
|
||||
switch i {
|
||||
case 0:
|
||||
continue
|
||||
case 2:
|
||||
var err error
|
||||
iface.MACAddress, err = net.ParseMAC(fragment)
|
||||
if err != nil {
|
||||
return 0, ErrInvalidIfaceData
|
||||
}
|
||||
continue
|
||||
default:
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) Read(p []byte) (int, error) {
|
||||
if err := iface.Validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var (
|
||||
count = 0
|
||||
wChan = make(chan string)
|
||||
errChan = make(chan error)
|
||||
doneChan = make(chan bool)
|
||||
)
|
||||
|
||||
w := func(s string) {
|
||||
switch {
|
||||
case len(s) == 0:
|
||||
return
|
||||
case len(s) > len(p),
|
||||
count+len(s) > len(p):
|
||||
errChan <- io.ErrShortBuffer
|
||||
default:
|
||||
wChan <- s
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
errChan <- iface.write(w)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-doneChan:
|
||||
return count, nil
|
||||
case err := <-errChan:
|
||||
if errors.Is(err, io.EOF) || err == nil {
|
||||
return count, nil
|
||||
}
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
case s := <-wChan:
|
||||
count += copy(p[count:], s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iface *NetworkInterface) write(w func(s string)) error {
|
||||
if err := iface.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if iface.Auto {
|
||||
w("auto ")
|
||||
w(iface.Name)
|
||||
w("\n")
|
||||
}
|
||||
if iface.Hotplug {
|
||||
w("allow-hotplug ")
|
||||
w(iface.Name)
|
||||
w("\n")
|
||||
}
|
||||
|
||||
w("iface ")
|
||||
w(iface.Name)
|
||||
w(" ")
|
||||
w(iface.Version.String())
|
||||
w(" ")
|
||||
w(iface.Config.String())
|
||||
w("\n")
|
||||
|
||||
if (iface.Address != nil && iface.Netmask != nil && !iface.Address.IsUnspecified()) &&
|
||||
(iface.Config == AddressConfigStatic || iface.Config == AddressConfigManual) {
|
||||
w("\taddress ")
|
||||
w(iface.Address.String())
|
||||
w("\n")
|
||||
w("\tnetmask ")
|
||||
w(iface.netMaskString(iface.Netmask))
|
||||
w("\n")
|
||||
if iface.Broadcast != nil {
|
||||
w("\tbroadcast ")
|
||||
w(iface.Broadcast.String())
|
||||
w("\n")
|
||||
}
|
||||
if iface.Gateway != nil {
|
||||
w("\tgateway ")
|
||||
w(iface.Gateway.String())
|
||||
w("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(iface.DNSServers) > 0 {
|
||||
w("\tdns-nameservers")
|
||||
for _, dns := range iface.DNSServers {
|
||||
w(" ")
|
||||
w(dns.String())
|
||||
}
|
||||
w("\n")
|
||||
}
|
||||
|
||||
if len(iface.DNSSearch) > 0 {
|
||||
w("\tdns-search")
|
||||
for _, dns := range iface.DNSSearch {
|
||||
w(" ")
|
||||
w(dns)
|
||||
}
|
||||
w("\n")
|
||||
}
|
||||
|
||||
if iface.MACAddress != nil {
|
||||
w("\thwaddress ether ")
|
||||
w(iface.MACAddress.String())
|
||||
w("\n")
|
||||
}
|
||||
|
||||
if len(iface.Hooks.PreUp) > 0 {
|
||||
for _, hook := range iface.Hooks.PreUp {
|
||||
w("\tpre-up ")
|
||||
w(hook)
|
||||
w("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(iface.Hooks.PostUp) > 0 {
|
||||
for _, hook := range iface.Hooks.PostUp {
|
||||
w("\tpost-up ")
|
||||
w(hook)
|
||||
w("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(iface.Hooks.PreDown) > 0 {
|
||||
for _, hook := range iface.Hooks.PreDown {
|
||||
w("\tpre-down ")
|
||||
w(hook)
|
||||
w("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(iface.Hooks.PostDown) > 0 {
|
||||
for _, hook := range iface.Hooks.PostDown {
|
||||
w("\tpost-down ")
|
||||
w(hook)
|
||||
w("\n")
|
||||
}
|
||||
}
|
||||
return io.EOF
|
||||
}
|
396
netif_test.go
Normal file
396
netif_test.go
Normal file
@ -0,0 +1,396 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func TestAddressConfig_String(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
ac AddressConfig
|
||||
want string
|
||||
}
|
||||
|
||||
tests := []test{{
|
||||
name: "static",
|
||||
ac: AddressConfigStatic,
|
||||
want: "static",
|
||||
},
|
||||
{
|
||||
name: "dhcp",
|
||||
ac: AddressConfigDHCP,
|
||||
want: "dhcp",
|
||||
},
|
||||
{
|
||||
name: "manual",
|
||||
ac: AddressConfigManual,
|
||||
want: "manual",
|
||||
},
|
||||
{
|
||||
name: "loopback",
|
||||
ac: AddressConfigLoopback,
|
||||
want: "loopback",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.ac.String(); got != tt.want {
|
||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddressType_String(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
at AddressVersion
|
||||
want string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "ipv4",
|
||||
at: AddressVersion4,
|
||||
want: "inet",
|
||||
},
|
||||
{
|
||||
name: "ipv6",
|
||||
at: AddressVersion6,
|
||||
want: "inet6",
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
at: AddressVersionNil,
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.at.String(); got != tt.want {
|
||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkInterface_String(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
builder func() *NetworkInterface
|
||||
wantErrors []error
|
||||
wantString string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "static ipv4 #1",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithStatic().
|
||||
WithAddressVersion(AddressVersion4).
|
||||
WithAddress("10.0.0.5").
|
||||
WithNetmask(8, 32).
|
||||
WithGateway("10.0.0.1")
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet static
|
||||
address 10.0.0.5
|
||||
netmask 255.0.0.0
|
||||
gateway 10.0.0.1
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "static ipv6 #1",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithStatic().
|
||||
WithAddressVersion(AddressVersion6).
|
||||
WithAddress("2001:db8::1").
|
||||
WithNetmask(64, 128).
|
||||
WithGateway("2001:db8::2")
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet6 static
|
||||
address 2001:db8::1
|
||||
netmask ffff:ffff:ffff:ffff::
|
||||
gateway 2001:db8::2
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "static ipv6 #2",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithStatic().
|
||||
WithAddressVersion(AddressVersion6).
|
||||
WithAddress("2001:db8::2").
|
||||
WithNetmask(48, 128).
|
||||
WithGateway("2001:db8::1")
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet6 static
|
||||
address 2001:db8::2
|
||||
netmask ffff:ffff:ffff::
|
||||
gateway 2001:db8::1
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "static ipv6 #3",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithStatic().
|
||||
WithAddressVersion(AddressVersion6).
|
||||
WithAddress("fc00:bbbb:bbbb:bb01::31:1927").
|
||||
WithNetmask(128, 128).
|
||||
WithGateway("fc00:bbbb:bbbb:bb01::1")
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet6 static
|
||||
address fc00:bbbb:bbbb:bb01::31:1927
|
||||
netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
gateway fc00:bbbb:bbbb:bb01::1
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "dhcp ipv4",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithDHCP().
|
||||
WithAddressVersion(AddressVersion4)
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet dhcp
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "dhcp ipv6",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithDHCP().
|
||||
WithAddressVersion(AddressVersion6)
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet6 dhcp
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "manual ipv4",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithManual().
|
||||
WithAddressVersion(AddressVersion4)
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet manual
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "manual ipv6",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").WithManual().WithAddressVersion(AddressVersion6)
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet6 manual
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "loopback ipv4",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("lo").WithLoopback().WithAddressVersion(AddressVersion4)
|
||||
},
|
||||
wantString: `auto lo
|
||||
iface lo inet loopback
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "loopback ipv6",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("lo").
|
||||
WithAddressConfig(AddressConfigLoopback).
|
||||
WithAddressVersion(AddressVersion6)
|
||||
},
|
||||
wantString: `auto lo
|
||||
iface lo inet6 loopback
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid address config",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithAddress("yeeterson")
|
||||
},
|
||||
wantString: ``,
|
||||
wantErrors: []error{fmt.Errorf(": %s", "yeeterson")},
|
||||
},
|
||||
{
|
||||
name: "unallocated interface",
|
||||
builder: func() *NetworkInterface {
|
||||
return &NetworkInterface{}
|
||||
},
|
||||
wantString: ``,
|
||||
wantErrors: []error{ErrUnallocatedInterface},
|
||||
},
|
||||
{
|
||||
name: "invalid address version",
|
||||
builder: func() *NetworkInterface {
|
||||
return NewNetworkInterface("eth0").
|
||||
WithAddressVersion(3)
|
||||
},
|
||||
wantString: ``,
|
||||
wantErrors: []error{ErrInvalidAddressVersion},
|
||||
},
|
||||
{
|
||||
name: "dirty config",
|
||||
builder: func() *NetworkInterface {
|
||||
ifa := NewNetworkInterface("eth0").
|
||||
WithAddressVersion(3)
|
||||
if ifa.Validate() == nil {
|
||||
t.Errorf("invalid interface passed validation")
|
||||
}
|
||||
ifa = ifa.WithAddressVersion(AddressVersion4).
|
||||
WithStatic().WithAddress("1.1.1.1").
|
||||
WithNetmask(32, 32)
|
||||
return ifa
|
||||
},
|
||||
wantString: `auto eth0
|
||||
iface eth0 inet static
|
||||
address 1.1.1.1
|
||||
netmask 255.255.255.255
|
||||
`,
|
||||
wantErrors: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
iface := tt.builder()
|
||||
|
||||
if iface == nil {
|
||||
t.Fatal("nil interface returned")
|
||||
}
|
||||
|
||||
validationErrs := iface.Validate()
|
||||
|
||||
switch {
|
||||
case len(tt.wantErrors) < 1 && validationErrs != nil:
|
||||
t.Errorf("Validate(): %v", validationErrs)
|
||||
spew.Dump(iface)
|
||||
case len(tt.wantErrors) != 0 && len(iface.errs) == 0:
|
||||
t.Errorf("interface error: %v, wanted %+v", iface.errs, tt.wantErrors)
|
||||
t.Logf("%s", spew.Sdump(iface))
|
||||
case len(tt.wantErrors) != 0 && len(iface.errs) != 0:
|
||||
need := len(tt.wantErrors)
|
||||
for _, err := range iface.errs {
|
||||
if need == 0 {
|
||||
t.Errorf("interface has extra error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, wantErr := range tt.wantErrors {
|
||||
if errors.Is(err, wantErr) {
|
||||
need--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xeroxWant := bufio.NewScanner(strings.NewReader(tt.wantString))
|
||||
xeroxGot := bufio.NewScanner(strings.NewReader(iface.String()))
|
||||
for xeroxWant.Scan() {
|
||||
if !xeroxGot.Scan() {
|
||||
t.Errorf("xerox: got %s, want %s", xeroxGot.Text(), xeroxWant.Text())
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(xeroxGot.Text()) == "" {
|
||||
xeroxGot.Scan()
|
||||
}
|
||||
if strings.TrimSpace(xeroxGot.Text()) != strings.TrimSpace(xeroxWant.Text()) {
|
||||
t.Errorf("xerox: got %s, want %s", xeroxGot.Text(), xeroxWant.Text())
|
||||
}
|
||||
}
|
||||
|
||||
if len(tt.wantErrors) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b := make([]byte, len(iface.String()))
|
||||
n, err := iface.Read(b)
|
||||
if err != nil {
|
||||
t.Errorf("Read(): = %v", err)
|
||||
}
|
||||
if n != len(iface.String()) {
|
||||
t.Errorf("Read() = %v, want %v", n, len(tt.wantString))
|
||||
}
|
||||
xeroxWant = bufio.NewScanner(strings.NewReader(tt.wantString))
|
||||
xeroxGot = bufio.NewScanner(strings.NewReader(string(b)))
|
||||
for xeroxWant.Scan() {
|
||||
if !xeroxGot.Scan() {
|
||||
t.Errorf("xerox: got %s, want %s", xeroxGot.Text(), xeroxWant.Text())
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(xeroxGot.Text()) == "" {
|
||||
xeroxGot.Scan()
|
||||
}
|
||||
if strings.TrimSpace(xeroxGot.Text()) != strings.TrimSpace(xeroxWant.Text()) {
|
||||
t.Errorf("xerox: got %s, want %s", xeroxGot.Text(), xeroxWant.Text())
|
||||
}
|
||||
}
|
||||
|
||||
newIface := &NetworkInterface{}
|
||||
if n, err = newIface.Write(b); err != nil {
|
||||
t.Errorf("Write(): = %v", err)
|
||||
}
|
||||
if n != len(b) {
|
||||
t.Errorf("Write() = %v, want %v", n, len(tt.wantString))
|
||||
}
|
||||
newipstr, err := newIface.Address.MarshalText()
|
||||
if err != nil {
|
||||
t.Errorf("Write() = %v", err)
|
||||
}
|
||||
ipstr, err := iface.Address.MarshalText()
|
||||
if err != nil {
|
||||
t.Errorf("Write() = %v", err)
|
||||
}
|
||||
if !strings.EqualFold(string(newipstr), string(ipstr)) {
|
||||
t.Errorf("Write() = %v, want %v", newipstr, ipstr)
|
||||
}
|
||||
if newIface.Version != iface.Version {
|
||||
t.Errorf("Write() = %v, want %v", newIface.Version, iface.Version)
|
||||
}
|
||||
if newIface.Config != iface.Config {
|
||||
t.Errorf("Write() = %v, want %v", newIface.Config, iface.Config)
|
||||
}
|
||||
if iface.Version == AddressVersion4 && !bytes.Equal(newIface.Netmask, iface.Netmask) {
|
||||
t.Errorf("Write() netmask = %v, want %v", newIface.Netmask, iface.Netmask)
|
||||
}
|
||||
if newIface.Gateway.String() != iface.Gateway.String() {
|
||||
t.Errorf("Write() = %v, want %v", newIface.Gateway, iface.Gateway)
|
||||
}
|
||||
oldIfaceIsValidated := iface.Validate() == nil
|
||||
err = newIface.Validate()
|
||||
if oldIfaceIsValidated && err != nil {
|
||||
t.Errorf("Write() Validate = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user