mirror of
https://github.com/btcsuite/btcd.git
synced 2026-08-13 12:32:51 +02:00
btcutil: vendor anet instead of depending on a personal fork
In this commit, we vendor the Android networking shim that backs InterfaceAddrs on android into the btcutil module, rather than importing it from github.com/kcalvinalvin/anet. The shim works around golang/go#40569, where net.InterfaceAddrs fails with "route ip+net: netlinkrib: permission denied" on Android 11 and later because of the tightened restrictions on NETLINK sockets. btcutil only ever calls anet.InterfaceAddrs, wired in from net_android.go under the android build tag. The motivation is dependency hygiene. The previous import pointed at a fork hosted under an individual contributor's account, which is not a home we want a release dependency to live in. By copying the source into internal/anet we drop the external module entirely: the package is now unimportable from outside btcutil, and go.mod no longer references the fork. The source originates from github.com/wlynxg/anet, and its BSD 3-Clause LICENSE travels with it. Unlike upstream, this copy does not reach into the standard library's net package via //go:linkname; it carries its own ipv6ZoneCache, so no -ldflags "-checklinkname=0" linker flag is needed to build it. When golang/go#40569 is resolved upstream, both this package and the net_android.go shim can be removed.
This commit is contained in:
parent
280d4d2db6
commit
ce4e0f53af
10 changed files with 738 additions and 4 deletions
|
|
@ -12,7 +12,6 @@ require (
|
|||
github.com/btcsuite/btcd/wire/v2 v2.0.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
github.com/kcalvinalvin/anet v0.0.0-20251112173137-d8ddc1f6dbee
|
||||
github.com/kkdai/bstream v1.0.0
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U
|
|||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/kcalvinalvin/anet v0.0.0-20251112173137-d8ddc1f6dbee h1:FPP9HDkBbPyniu+u7FHZg+kKFX1WW0gxOGteJ0h3AJk=
|
||||
github.com/kcalvinalvin/anet v0.0.0-20251112173137-d8ddc1f6dbee/go.mod h1:N6sz6HwJAenJ6d+/xmSl0ikfV05ZrVGmjt1ryy/WOtE=
|
||||
github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
|
||||
github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
|
|
|||
28
btcutil/internal/anet/LICENSE
Normal file
28
btcutil/internal/anet/LICENSE
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2023, wlynxg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
24
btcutil/internal/anet/README.md
Normal file
24
btcutil/internal/anet/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# anet (vendored)
|
||||
|
||||
This package is a vendored copy of an Android networking shim. It works around
|
||||
[golang/go#40569](https://github.com/golang/go/issues/40569), where
|
||||
`net.Interfaces()` and `net.InterfaceAddrs()` fail with
|
||||
`route ip+net: netlinkrib: permission denied` on Android 11 and later due to the
|
||||
tightened restrictions on `NETLINK` sockets (no `bind`, no `RTM_GETLINK`).
|
||||
|
||||
`btcutil` only relies on `anet.InterfaceAddrs()`, which is wired in from
|
||||
`net_android.go` under the `android` build tag.
|
||||
|
||||
## Provenance
|
||||
|
||||
The source originates from `github.com/wlynxg/anet` (BSD 3-Clause, see
|
||||
`LICENSE`). It was vendored here, rather than imported as a module dependency,
|
||||
so that `btcd` does not depend on a fork hosted under a personal account.
|
||||
|
||||
Unlike upstream, this copy does **not** use the `//go:linkname` mechanism to
|
||||
reach into the standard library's `net` package; it carries its own
|
||||
`ipv6ZoneCache`. As a result, no `-ldflags "-checklinkname=0"` linker flag is
|
||||
required to build it.
|
||||
|
||||
When [golang/go#40569](https://github.com/golang/go/issues/40569) is resolved
|
||||
upstream, this package and the `net_android.go` shim can be removed.
|
||||
7
btcutil/internal/anet/android_api_level.go
Normal file
7
btcutil/internal/anet/android_api_level.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//go:build !(android && cgo)
|
||||
|
||||
package anet
|
||||
|
||||
func androidDeviceApiLevel() int {
|
||||
return -1
|
||||
}
|
||||
23
btcutil/internal/anet/android_api_level_cgo.go
Normal file
23
btcutil/internal/anet/android_api_level_cgo.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//go:build android && cgo
|
||||
|
||||
package anet
|
||||
|
||||
// #include <android/api-level.h>
|
||||
import "C"
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
apiLevel int
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Returns the API level of the device we're actually running on, or -1 on failure.
|
||||
// The returned value is equivalent to the Java Build.VERSION.SDK_INT API.
|
||||
func androidDeviceApiLevel() int {
|
||||
once.Do(func() {
|
||||
apiLevel = int(C.android_get_device_api_level())
|
||||
})
|
||||
|
||||
return apiLevel
|
||||
}
|
||||
30
btcutil/internal/anet/interface.go
Normal file
30
btcutil/internal/anet/interface.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//go:build !android
|
||||
// +build !android
|
||||
|
||||
package anet
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Interfaces returns a list of the system's network interfaces.
|
||||
func Interfaces() ([]net.Interface, error) {
|
||||
return net.Interfaces()
|
||||
}
|
||||
|
||||
// InterfaceAddrs returns a list of the system's unicast interface
|
||||
// addresses.
|
||||
//
|
||||
// The returned list does not identify the associated interface; use
|
||||
// Interfaces and Interface.Addrs for more detail.
|
||||
func InterfaceAddrs() ([]net.Addr, error) {
|
||||
return net.InterfaceAddrs()
|
||||
}
|
||||
|
||||
// InterfaceAddrsByInterface returns a list of the system's unicast
|
||||
// interface addresses by specific interface.
|
||||
func InterfaceAddrsByInterface(ifi *net.Interface) ([]net.Addr, error) {
|
||||
return ifi.Addrs()
|
||||
}
|
||||
|
||||
func SetAndroidVersion(version uint) {}
|
||||
446
btcutil/internal/anet/interface_android.go
Normal file
446
btcutil/internal/anet/interface_android.go
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
package anet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
android11ApiLevel = 30
|
||||
)
|
||||
|
||||
var (
|
||||
customAndroidApiLevel = -1
|
||||
errInvalidInterface = errors.New("invalid network interface")
|
||||
errInvalidInterfaceIndex = errors.New("invalid network interface index")
|
||||
errInvalidInterfaceName = errors.New("invalid network interface name")
|
||||
errNoSuchInterface = errors.New("no such network interface")
|
||||
errNoSuchMulticastInterface = errors.New("no such multicast network interface")
|
||||
)
|
||||
|
||||
type ifReq [40]byte
|
||||
|
||||
// Interfaces returns a list of the system's network interfaces.
|
||||
func Interfaces() ([]net.Interface, error) {
|
||||
if androidApiLevel() < android11ApiLevel {
|
||||
return net.Interfaces()
|
||||
}
|
||||
|
||||
ift, err := interfaceTable(0)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
if len(ift) != 0 {
|
||||
zoneCache.update(ift, true)
|
||||
zoneCacheX.update(ift, true)
|
||||
}
|
||||
return ift, nil
|
||||
}
|
||||
|
||||
// InterfaceAddrs returns a list of the system's unicast interface
|
||||
// addresses.
|
||||
//
|
||||
// The returned list does not identify the associated interface; use
|
||||
// Interfaces and Interface.Addrs for more detail.
|
||||
func InterfaceAddrs() ([]net.Addr, error) {
|
||||
if androidApiLevel() < android11ApiLevel {
|
||||
return net.InterfaceAddrs()
|
||||
}
|
||||
|
||||
ifat, err := interfaceAddrTable(nil)
|
||||
if err != nil {
|
||||
err = &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
return ifat, err
|
||||
}
|
||||
|
||||
// InterfaceByIndex returns the interface specified by index.
|
||||
//
|
||||
// On Solaris, it returns one of the logical network interfaces
|
||||
// sharing the logical data link; for more precision use
|
||||
// InterfaceByName.
|
||||
func InterfaceByIndex(index int) (*net.Interface, error) {
|
||||
if androidApiLevel() < android11ApiLevel {
|
||||
return net.InterfaceByIndex(index)
|
||||
}
|
||||
|
||||
if index <= 0 {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex}
|
||||
}
|
||||
ift, err := interfaceTable(index)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
ifi, err := interfaceByIndex(ift, index)
|
||||
if err != nil {
|
||||
err = &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
return ifi, err
|
||||
}
|
||||
|
||||
// InterfaceByName returns the interface specified by name.
|
||||
func InterfaceByName(name string) (*net.Interface, error) {
|
||||
if name == "" {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName}
|
||||
}
|
||||
ift, err := interfaceTable(0)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
if len(ift) != 0 {
|
||||
zoneCache.update(ift, true)
|
||||
zoneCacheX.update(ift, true)
|
||||
}
|
||||
for _, ifi := range ift {
|
||||
if name == ifi.Name {
|
||||
return &ifi, nil
|
||||
}
|
||||
}
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
|
||||
}
|
||||
|
||||
// InterfaceAddrsByInterface returns a list of the system's unicast
|
||||
// interface addresses by specific interface.
|
||||
func InterfaceAddrsByInterface(ifi *net.Interface) ([]net.Addr, error) {
|
||||
if ifi == nil {
|
||||
return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
|
||||
}
|
||||
|
||||
if androidApiLevel() < android11ApiLevel {
|
||||
return ifi.Addrs()
|
||||
}
|
||||
|
||||
ifat, err := interfaceAddrTable(ifi)
|
||||
if err != nil {
|
||||
err = &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
return ifat, err
|
||||
}
|
||||
|
||||
// SetAndroidVersion set the Android environment in which the program runs.
|
||||
// The Android system version number can be obtained through
|
||||
// `android.os.Build.VERSION.RELEASE` of the Android framework.
|
||||
// If version is 0 the actual version will be detected automatically if possible.
|
||||
func SetAndroidVersion(version uint) {
|
||||
switch {
|
||||
case version == 0:
|
||||
customAndroidApiLevel = -1
|
||||
case version >= 11:
|
||||
customAndroidApiLevel = android11ApiLevel
|
||||
default:
|
||||
customAndroidApiLevel = 0
|
||||
}
|
||||
}
|
||||
|
||||
func androidApiLevel() int {
|
||||
if customAndroidApiLevel != -1 {
|
||||
// user-provided api level should be used
|
||||
return customAndroidApiLevel
|
||||
}
|
||||
|
||||
// try to autodetect api level
|
||||
return androidDeviceApiLevel()
|
||||
}
|
||||
|
||||
// An ipv6ZoneCache represents a cache holding partial network
|
||||
// interface information. It is used for reducing the cost of IPv6
|
||||
// addressing scope zone resolution.
|
||||
//
|
||||
// Multiple names sharing the index are managed by first-come
|
||||
// first-served basis for consistency.
|
||||
type ipv6ZoneCache struct {
|
||||
sync.RWMutex // guard the following
|
||||
lastFetched time.Time // last time routing information was fetched
|
||||
toIndex map[string]int // interface name to its index
|
||||
toName map[int]string // interface index to its name
|
||||
}
|
||||
|
||||
var zoneCache = ipv6ZoneCache{
|
||||
toIndex: make(map[string]int),
|
||||
toName: make(map[int]string),
|
||||
}
|
||||
|
||||
var zoneCacheX = ipv6ZoneCache{
|
||||
toIndex: make(map[string]int),
|
||||
toName: make(map[int]string),
|
||||
}
|
||||
|
||||
// update refreshes the network interface information if the cache was last
|
||||
// updated more than 1 minute ago, or if force is set. It reports whether the
|
||||
// cache was updated.
|
||||
func (zc *ipv6ZoneCache) update(ift []net.Interface, force bool) (updated bool) {
|
||||
zc.Lock()
|
||||
defer zc.Unlock()
|
||||
now := time.Now()
|
||||
if !force && zc.lastFetched.After(now.Add(-60*time.Second)) {
|
||||
return false
|
||||
}
|
||||
zc.lastFetched = now
|
||||
if len(ift) == 0 {
|
||||
var err error
|
||||
if ift, err = interfaceTable(0); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
zc.toIndex = make(map[string]int, len(ift))
|
||||
zc.toName = make(map[int]string, len(ift))
|
||||
for _, ifi := range ift {
|
||||
zc.toIndex[ifi.Name] = ifi.Index
|
||||
if _, ok := zc.toName[ifi.Index]; !ok {
|
||||
zc.toName[ifi.Index] = ifi.Name
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// If the ifindex is zero, interfaceTable returns mappings of all
|
||||
// network interfaces. Otherwise it returns a mapping of a specific
|
||||
// interface.
|
||||
func interfaceTable(ifindex int) ([]net.Interface, error) {
|
||||
tab, err := NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("netlinkrib", err)
|
||||
}
|
||||
msgs, err := syscall.ParseNetlinkMessage(tab)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("parsenetlinkmessage", err)
|
||||
}
|
||||
|
||||
var ift []net.Interface
|
||||
im := make(map[uint32]struct{})
|
||||
loop:
|
||||
for _, m := range msgs {
|
||||
switch m.Header.Type {
|
||||
case syscall.NLMSG_DONE:
|
||||
break loop
|
||||
case syscall.RTM_NEWADDR:
|
||||
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
|
||||
if _, ok := im[ifam.Index]; ok {
|
||||
continue
|
||||
} else {
|
||||
im[ifam.Index] = struct{}{}
|
||||
}
|
||||
|
||||
if ifindex == 0 || ifindex == int(ifam.Index) {
|
||||
ifi := newLink(ifam)
|
||||
if ifi != nil {
|
||||
ift = append(ift, *ifi)
|
||||
}
|
||||
if ifindex == int(ifam.Index) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ift, nil
|
||||
}
|
||||
|
||||
func newLink(ifam *syscall.IfAddrmsg) *net.Interface {
|
||||
ift := &net.Interface{Index: int(ifam.Index)}
|
||||
|
||||
name, err := indexToName(ifam.Index)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
ift.Name = name
|
||||
|
||||
mtu, err := nameToMTU(name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
ift.MTU = mtu
|
||||
|
||||
flags, err := nameToFlags(name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
ift.Flags = flags
|
||||
return ift
|
||||
}
|
||||
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
var f net.Flags
|
||||
if rawFlags&syscall.IFF_UP != 0 {
|
||||
f |= net.FlagUp
|
||||
}
|
||||
if rawFlags&syscall.IFF_RUNNING != 0 {
|
||||
f |= net.FlagRunning
|
||||
}
|
||||
if rawFlags&syscall.IFF_BROADCAST != 0 {
|
||||
f |= net.FlagBroadcast
|
||||
}
|
||||
if rawFlags&syscall.IFF_LOOPBACK != 0 {
|
||||
f |= net.FlagLoopback
|
||||
}
|
||||
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
|
||||
f |= net.FlagPointToPoint
|
||||
}
|
||||
if rawFlags&syscall.IFF_MULTICAST != 0 {
|
||||
f |= net.FlagMulticast
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// If the ifi is nil, interfaceAddrTable returns addresses for all
|
||||
// network interfaces. Otherwise it returns addresses for a specific
|
||||
// interface.
|
||||
func interfaceAddrTable(ifi *net.Interface) ([]net.Addr, error) {
|
||||
tab, err := NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("netlinkrib", err)
|
||||
}
|
||||
msgs, err := syscall.ParseNetlinkMessage(tab)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("parsenetlinkmessage", err)
|
||||
}
|
||||
|
||||
var ift []net.Interface
|
||||
if ifi == nil {
|
||||
var err error
|
||||
ift, err = interfaceTable(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ifat, err := addrTable(ift, ifi, msgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ifat, nil
|
||||
}
|
||||
|
||||
func addrTable(ift []net.Interface, ifi *net.Interface, msgs []syscall.NetlinkMessage) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
loop:
|
||||
for _, m := range msgs {
|
||||
switch m.Header.Type {
|
||||
case syscall.NLMSG_DONE:
|
||||
break loop
|
||||
case syscall.RTM_NEWADDR:
|
||||
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
|
||||
if len(ift) != 0 || ifi.Index == int(ifam.Index) {
|
||||
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
|
||||
}
|
||||
ifa := newAddr(ifam, attrs)
|
||||
if ifa != nil {
|
||||
ifat = append(ifat, ifa)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ifat, nil
|
||||
}
|
||||
|
||||
func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) net.Addr {
|
||||
var ipPointToPoint bool
|
||||
// Seems like we need to make sure whether the IP interface
|
||||
// stack consists of IP point-to-point numbered or unnumbered
|
||||
// addressing.
|
||||
for _, a := range attrs {
|
||||
if a.Attr.Type == syscall.IFA_LOCAL {
|
||||
ipPointToPoint = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, a := range attrs {
|
||||
if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS {
|
||||
continue
|
||||
}
|
||||
switch ifam.Family {
|
||||
case syscall.AF_INET:
|
||||
return &net.IPNet{IP: net.IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv4len)}
|
||||
case syscall.AF_INET6:
|
||||
ifa := &net.IPNet{IP: make(net.IP, net.IPv6len), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv6len)}
|
||||
copy(ifa.IP, a.Value[:])
|
||||
return ifa
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func interfaceByIndex(ift []net.Interface, index int) (*net.Interface, error) {
|
||||
for _, ifi := range ift {
|
||||
if index == ifi.Index {
|
||||
return &ifi, nil
|
||||
}
|
||||
}
|
||||
return nil, errNoSuchInterface
|
||||
}
|
||||
|
||||
func ioctl(fd int, req uint, arg unsafe.Pointer) error {
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||
if e1 != 0 {
|
||||
return e1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func indexToName(index uint32) (string, error) {
|
||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
var ifr ifReq
|
||||
*(*uint32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ])) = index
|
||||
err = ioctl(fd, syscall.SIOCGIFNAME, unsafe.Pointer(&ifr[0]))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes.Trim(ifr[:syscall.IFNAMSIZ], "\x00")), nil
|
||||
}
|
||||
|
||||
func nameToMTU(name string) (int, error) {
|
||||
// Leave room for terminating NULL byte.
|
||||
if len(name) >= syscall.IFNAMSIZ {
|
||||
return -1, syscall.EINVAL
|
||||
}
|
||||
|
||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
var ifr ifReq
|
||||
copy(ifr[:], name)
|
||||
err = ioctl(fd, syscall.SIOCGIFMTU, unsafe.Pointer(&ifr[0]))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ]))), nil
|
||||
}
|
||||
|
||||
func nameToFlags(name string) (net.Flags, error) {
|
||||
// Leave room for terminating NULL byte.
|
||||
if len(name) >= syscall.IFNAMSIZ {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
|
||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
var ifr ifReq
|
||||
copy(ifr[:], name)
|
||||
err = ioctl(fd, syscall.SIOCGIFFLAGS, unsafe.Pointer(&ifr[0]))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return linkFlags(*(*uint32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ]))), nil
|
||||
}
|
||||
179
btcutil/internal/anet/netlink_android.go
Normal file
179
btcutil/internal/anet/netlink_android.go
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Netlink sockets and messages
|
||||
|
||||
package anet
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Round the length of a netlink message up to align it properly.
|
||||
func nlmAlignOf(msglen int) int {
|
||||
return (msglen + syscall.NLMSG_ALIGNTO - 1) & ^(syscall.NLMSG_ALIGNTO - 1)
|
||||
}
|
||||
|
||||
// Round the length of a netlink route attribute up to align it
|
||||
// properly.
|
||||
func rtaAlignOf(attrlen int) int {
|
||||
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
|
||||
}
|
||||
|
||||
// NetlinkRouteRequest represents a request message to receive routing
|
||||
// and link states from the kernel.
|
||||
type NetlinkRouteRequest struct {
|
||||
Header syscall.NlMsghdr
|
||||
Data syscall.RtGenmsg
|
||||
}
|
||||
|
||||
func (rr *NetlinkRouteRequest) toWireFormat() []byte {
|
||||
b := make([]byte, rr.Header.Len)
|
||||
*(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
|
||||
*(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
|
||||
*(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
|
||||
*(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
|
||||
*(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
|
||||
b[16] = byte(rr.Data.Family)
|
||||
return b
|
||||
}
|
||||
|
||||
func newNetlinkRouteRequest(proto, seq, family int) []byte {
|
||||
rr := &NetlinkRouteRequest{}
|
||||
rr.Header.Len = uint32(syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg)
|
||||
rr.Header.Type = uint16(proto)
|
||||
rr.Header.Flags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST
|
||||
rr.Header.Seq = uint32(seq)
|
||||
rr.Data.Family = uint8(family)
|
||||
return rr.toWireFormat()
|
||||
}
|
||||
|
||||
// NetlinkRIB returns routing information base, as known as RIB, which
|
||||
// consists of network facility information, states and parameters.
|
||||
func NetlinkRIB(proto, family int) ([]byte, error) {
|
||||
s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.NETLINK_ROUTE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.Close(s)
|
||||
sa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
|
||||
|
||||
wb := newNetlinkRouteRequest(proto, 1, family)
|
||||
if err := syscall.Sendto(s, wb, 0, sa); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lsa, err := syscall.Getsockname(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lsanl, ok := lsa.(*syscall.SockaddrNetlink)
|
||||
if !ok {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
var tab []byte
|
||||
rbNew := make([]byte, syscall.Getpagesize())
|
||||
done:
|
||||
for {
|
||||
rb := rbNew
|
||||
nr, _, err := syscall.Recvfrom(s, rb, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nr < syscall.NLMSG_HDRLEN {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
rb = rb[:nr]
|
||||
tab = append(tab, rb...)
|
||||
msgs, err := ParseNetlinkMessage(rb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_DONE {
|
||||
break done
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
}
|
||||
}
|
||||
return tab, nil
|
||||
}
|
||||
|
||||
// NetlinkMessage represents a netlink message.
|
||||
type NetlinkMessage struct {
|
||||
Header syscall.NlMsghdr
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ParseNetlinkMessage parses b as an array of netlink messages and
|
||||
// returns the slice containing the NetlinkMessage structures.
|
||||
func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
|
||||
var msgs []NetlinkMessage
|
||||
for len(b) >= syscall.NLMSG_HDRLEN {
|
||||
h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-syscall.NLMSG_HDRLEN]}
|
||||
msgs = append(msgs, m)
|
||||
b = b[dlen:]
|
||||
}
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
func netlinkMessageHeaderAndData(b []byte) (*syscall.NlMsghdr, []byte, int, error) {
|
||||
h := (*syscall.NlMsghdr)(unsafe.Pointer(&b[0]))
|
||||
l := nlmAlignOf(int(h.Len))
|
||||
if int(h.Len) < syscall.NLMSG_HDRLEN || l > len(b) {
|
||||
return nil, nil, 0, syscall.EINVAL
|
||||
}
|
||||
return h, b[syscall.NLMSG_HDRLEN:], l, nil
|
||||
}
|
||||
|
||||
// NetlinkRouteAttr represents a netlink route attribute.
|
||||
type NetlinkRouteAttr struct {
|
||||
Attr syscall.RtAttr
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// ParseNetlinkRouteAttr parses m's payload as an array of netlink
|
||||
// route attributes and returns the slice containing the
|
||||
// NetlinkRouteAttr structures.
|
||||
func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
|
||||
var b []byte
|
||||
switch m.Header.Type {
|
||||
case syscall.RTM_NEWLINK, syscall.RTM_DELLINK:
|
||||
b = m.Data[syscall.SizeofIfInfomsg:]
|
||||
case syscall.RTM_NEWADDR, syscall.RTM_DELADDR:
|
||||
b = m.Data[syscall.SizeofIfAddrmsg:]
|
||||
case syscall.RTM_NEWROUTE, syscall.RTM_DELROUTE:
|
||||
b = m.Data[syscall.SizeofRtMsg:]
|
||||
default:
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
var attrs []NetlinkRouteAttr
|
||||
for len(b) >= syscall.SizeofRtAttr {
|
||||
a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-syscall.SizeofRtAttr]}
|
||||
attrs = append(attrs, ra)
|
||||
b = b[alen:]
|
||||
}
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) {
|
||||
a := (*syscall.RtAttr)(unsafe.Pointer(&b[0]))
|
||||
if int(a.Len) < syscall.SizeofRtAttr || int(a.Len) > len(b) {
|
||||
return nil, nil, 0, syscall.EINVAL
|
||||
}
|
||||
return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ package btcutil
|
|||
import (
|
||||
"net"
|
||||
|
||||
"github.com/kcalvinalvin/anet"
|
||||
"github.com/btcsuite/btcd/btcutil/v2/internal/anet"
|
||||
)
|
||||
|
||||
// InterfaceAddrs returns a list of the system's network interface addresses.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue