mirror of
https://github.com/rwinkhart/sys.git
synced 2026-09-05 16:47:27 -04:00
unix: add functions to encode Inet4Pktinfo and Inet6Pktinfo
It's possible to control the source address of a UDP packet by passing a socket control message of type IP_PKTINFO or IPV6_PKTINFO. This is a somewhat esoteric feature of the network stack, but it's extremely useful feature when you really need it. Change-Id: I8300575f975679f6689d6f1282af253ba62e8f9d Reviewed-on: https://go-review.googlesource.com/c/sys/+/355610 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Tobias Klauser <tobias.klauser@gmail.com>
This commit is contained in:
committed by
Tobias Klauser
parent
9d61738499
commit
8e5104632a
@@ -9,9 +9,11 @@ package unix_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -118,3 +120,78 @@ func TestSCMCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPktInfo(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
network string
|
||||||
|
address *net.UDPAddr
|
||||||
|
}{
|
||||||
|
{"udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1")}},
|
||||||
|
{"udp6", &net.UDPAddr{IP: net.ParseIP("::1")}},
|
||||||
|
}
|
||||||
|
for _, test := range testcases {
|
||||||
|
t.Run(test.network, func(t *testing.T) {
|
||||||
|
conn, err := net.ListenUDP(test.network, test.address)
|
||||||
|
if errors.Is(err, unix.EADDRNOTAVAIL) {
|
||||||
|
t.Skipf("%v is not available", test.address)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Listen:", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
var pktInfo []byte
|
||||||
|
var src net.IP
|
||||||
|
switch test.network {
|
||||||
|
case "udp4":
|
||||||
|
var info4 unix.Inet4Pktinfo
|
||||||
|
src = net.ParseIP("127.0.0.2").To4()
|
||||||
|
copy(info4.Spec_dst[:], src)
|
||||||
|
pktInfo = unix.PktInfo4(&info4)
|
||||||
|
|
||||||
|
case "udp6":
|
||||||
|
var info6 unix.Inet6Pktinfo
|
||||||
|
src = net.ParseIP("2001:0DB8::1")
|
||||||
|
copy(info6.Addr[:], src)
|
||||||
|
pktInfo = unix.PktInfo6(&info6)
|
||||||
|
|
||||||
|
raw, err := conn.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("SyscallConn:", err)
|
||||||
|
}
|
||||||
|
var opErr error
|
||||||
|
err = raw.Control(func(fd uintptr) {
|
||||||
|
opErr = unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_FREEBIND, 1)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Control:", err)
|
||||||
|
}
|
||||||
|
if errors.Is(opErr, unix.ENOPROTOOPT) {
|
||||||
|
// Happens on android-amd64-emu, maybe Android has disabled
|
||||||
|
// IPV6_FREEBIND?
|
||||||
|
t.Skip("IPV6_FREEBIND not supported")
|
||||||
|
}
|
||||||
|
if opErr != nil {
|
||||||
|
t.Fatal("Can't enable IPV6_FREEBIND:", opErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := []byte{1}
|
||||||
|
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
_, _, err = conn.WriteMsgUDP(msg, pktInfo, addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("WriteMsgUDP:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
||||||
|
_, _, _, remote, err := conn.ReadMsgUDP(msg, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadMsgUDP:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !remote.IP.Equal(src) {
|
||||||
|
t.Errorf("Got packet from %v, want %v", remote.IP, src)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,3 +34,25 @@ func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
|
|||||||
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||||
return &ucred, nil
|
return &ucred, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PktInfo4 encodes Inet4Pktinfo into a socket control message of type IP_PKTINFO.
|
||||||
|
func PktInfo4(info *Inet4Pktinfo) []byte {
|
||||||
|
b := make([]byte, CmsgSpace(SizeofInet4Pktinfo))
|
||||||
|
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = SOL_IP
|
||||||
|
h.Type = IP_PKTINFO
|
||||||
|
h.SetLen(CmsgLen(SizeofInet4Pktinfo))
|
||||||
|
*(*Inet4Pktinfo)(h.data(0)) = *info
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// PktInfo6 encodes Inet6Pktinfo into a socket control message of type IPV6_PKTINFO.
|
||||||
|
func PktInfo6(info *Inet6Pktinfo) []byte {
|
||||||
|
b := make([]byte, CmsgSpace(SizeofInet6Pktinfo))
|
||||||
|
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = SOL_IPV6
|
||||||
|
h.Type = IPV6_PKTINFO
|
||||||
|
h.SetLen(CmsgLen(SizeofInet6Pktinfo))
|
||||||
|
*(*Inet6Pktinfo)(h.data(0)) = *info
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user