2019-01-02 01:55:51 +01:00
|
|
|
/* SPDX-License-Identifier: MIT
|
2018-05-03 15:04:00 +02:00
|
|
|
*
|
2021-01-28 17:52:15 +01:00
|
|
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
2018-05-03 15:04:00 +02:00
|
|
|
*/
|
|
|
|
|
2019-11-07 17:13:05 +01:00
|
|
|
package conn
|
2017-08-25 14:53:23 +02:00
|
|
|
|
|
|
|
import (
|
2021-02-09 19:46:57 +01:00
|
|
|
"errors"
|
2017-08-25 14:53:23 +02:00
|
|
|
"net"
|
2022-03-17 00:09:48 +01:00
|
|
|
"net/netip"
|
2021-03-31 22:55:18 +02:00
|
|
|
"sync"
|
2018-06-11 19:04:38 +02:00
|
|
|
"syscall"
|
2017-08-25 14:53:23 +02:00
|
|
|
)
|
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
// StdNetBind is meant to be a temporary solution on platforms for which
|
|
|
|
// the sticky socket / source caching behavior has not yet been implemented.
|
|
|
|
// It uses the Go's net package to implement networking.
|
|
|
|
// See LinuxSocketBind for a proper implementation on the Linux platform.
|
|
|
|
type StdNetBind struct {
|
2021-03-31 22:55:18 +02:00
|
|
|
mu sync.Mutex // protects following fields
|
2019-10-21 13:29:57 +02:00
|
|
|
ipv4 *net.UDPConn
|
|
|
|
ipv6 *net.UDPConn
|
|
|
|
blackhole4 bool
|
|
|
|
blackhole6 bool
|
2017-11-19 00:21:58 +01:00
|
|
|
}
|
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func NewStdNetBind() Bind { return &StdNetBind{} }
|
2017-11-19 00:21:58 +01:00
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
type StdNetEndpoint net.UDPAddr
|
2017-11-19 00:21:58 +01:00
|
|
|
|
2021-12-09 17:55:50 +01:00
|
|
|
var (
|
|
|
|
_ Bind = (*StdNetBind)(nil)
|
|
|
|
_ Endpoint = (*StdNetEndpoint)(nil)
|
|
|
|
)
|
2021-02-22 02:01:50 +01:00
|
|
|
|
|
|
|
func (*StdNetBind) ParseEndpoint(s string) (Endpoint, error) {
|
2021-11-05 01:52:54 +01:00
|
|
|
e, err := netip.ParseAddrPort(s)
|
|
|
|
return (*StdNetEndpoint)(&net.UDPAddr{
|
|
|
|
IP: e.Addr().AsSlice(),
|
|
|
|
Port: int(e.Port()),
|
|
|
|
Zone: e.Addr().Zone(),
|
|
|
|
}), err
|
2017-11-19 00:21:58 +01:00
|
|
|
}
|
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (*StdNetEndpoint) ClearSrc() {}
|
2017-11-19 00:21:58 +01:00
|
|
|
|
2021-11-05 01:52:54 +01:00
|
|
|
func (e *StdNetEndpoint) DstIP() netip.Addr {
|
|
|
|
a, _ := netip.AddrFromSlice((*net.UDPAddr)(e).IP)
|
|
|
|
return a
|
2017-11-19 00:21:58 +01:00
|
|
|
}
|
|
|
|
|
2021-11-05 01:52:54 +01:00
|
|
|
func (e *StdNetEndpoint) SrcIP() netip.Addr {
|
|
|
|
return netip.Addr{} // not supported
|
2017-11-19 00:21:58 +01:00
|
|
|
}
|
2017-11-17 17:25:45 +01:00
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (e *StdNetEndpoint) DstToBytes() []byte {
|
2017-11-19 00:21:58 +01:00
|
|
|
addr := (*net.UDPAddr)(e)
|
2018-05-18 05:02:35 +02:00
|
|
|
out := addr.IP.To4()
|
|
|
|
if out == nil {
|
|
|
|
out = addr.IP
|
|
|
|
}
|
2017-11-19 00:21:58 +01:00
|
|
|
out = append(out, byte(addr.Port&0xff))
|
|
|
|
out = append(out, byte((addr.Port>>8)&0xff))
|
|
|
|
return out
|
|
|
|
}
|
2017-11-17 17:25:45 +01:00
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (e *StdNetEndpoint) DstToString() string {
|
2017-11-19 00:21:58 +01:00
|
|
|
return (*net.UDPAddr)(e).String()
|
|
|
|
}
|
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (e *StdNetEndpoint) SrcToString() string {
|
2017-11-19 00:21:58 +01:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2017-11-19 13:14:15 +01:00
|
|
|
func listenNet(network string, port int) (*net.UDPConn, int, error) {
|
|
|
|
conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port})
|
2017-11-19 00:21:58 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
2019-11-07 17:13:05 +01:00
|
|
|
// Retrieve port.
|
2017-11-19 00:21:58 +01:00
|
|
|
laddr := conn.LocalAddr()
|
2017-11-19 13:14:15 +01:00
|
|
|
uaddr, err := net.ResolveUDPAddr(
|
2017-11-19 00:21:58 +01:00
|
|
|
laddr.Network(),
|
|
|
|
laddr.String(),
|
|
|
|
)
|
2017-11-19 13:14:15 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
2017-11-19 00:21:58 +01:00
|
|
|
return conn, uaddr.Port, nil
|
|
|
|
}
|
|
|
|
|
2021-03-31 22:55:18 +02:00
|
|
|
func (bind *StdNetBind) Open(uport uint16) ([]ReceiveFunc, uint16, error) {
|
|
|
|
bind.mu.Lock()
|
|
|
|
defer bind.mu.Unlock()
|
|
|
|
|
2017-11-19 13:14:15 +01:00
|
|
|
var err error
|
2021-02-09 18:45:12 +01:00
|
|
|
var tries int
|
2017-11-17 17:25:45 +01:00
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
if bind.ipv4 != nil || bind.ipv6 != nil {
|
2021-03-31 22:55:18 +02:00
|
|
|
return nil, 0, ErrBindAlreadyOpen
|
2021-02-22 02:01:50 +01:00
|
|
|
}
|
|
|
|
|
2021-03-29 22:27:21 +02:00
|
|
|
// Attempt to open ipv4 and ipv6 listeners on the same port.
|
|
|
|
// If uport is 0, we can retry on failure.
|
2021-02-09 18:45:12 +01:00
|
|
|
again:
|
2017-11-19 13:14:15 +01:00
|
|
|
port := int(uport)
|
2021-03-29 22:21:06 +02:00
|
|
|
var ipv4, ipv6 *net.UDPConn
|
2017-11-17 17:25:45 +01:00
|
|
|
|
2021-03-29 22:21:06 +02:00
|
|
|
ipv4, port, err = listenNet("udp4", port)
|
2021-02-09 19:46:57 +01:00
|
|
|
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
|
2021-03-31 22:55:18 +02:00
|
|
|
return nil, 0, err
|
2017-11-17 17:25:45 +01:00
|
|
|
}
|
2017-11-19 13:14:15 +01:00
|
|
|
|
2021-03-29 22:27:21 +02:00
|
|
|
// Listen on the same port as we're using for ipv4.
|
2021-03-29 22:21:06 +02:00
|
|
|
ipv6, port, err = listenNet("udp6", port)
|
|
|
|
if uport == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
|
|
|
|
ipv4.Close()
|
2021-02-09 18:45:12 +01:00
|
|
|
tries++
|
|
|
|
goto again
|
|
|
|
}
|
2021-02-09 19:46:57 +01:00
|
|
|
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
|
2021-03-29 22:21:06 +02:00
|
|
|
ipv4.Close()
|
2021-03-31 22:55:18 +02:00
|
|
|
return nil, 0, err
|
2017-11-17 17:25:45 +01:00
|
|
|
}
|
2021-03-31 22:55:18 +02:00
|
|
|
var fns []ReceiveFunc
|
|
|
|
if ipv4 != nil {
|
2021-04-10 01:21:35 +02:00
|
|
|
fns = append(fns, bind.makeReceiveIPv4(ipv4))
|
2021-03-31 22:55:18 +02:00
|
|
|
bind.ipv4 = ipv4
|
2021-02-22 02:01:50 +01:00
|
|
|
}
|
2021-03-31 22:55:18 +02:00
|
|
|
if ipv6 != nil {
|
2021-04-10 01:21:35 +02:00
|
|
|
fns = append(fns, bind.makeReceiveIPv6(ipv6))
|
2021-03-31 22:55:18 +02:00
|
|
|
bind.ipv6 = ipv6
|
|
|
|
}
|
|
|
|
if len(fns) == 0 {
|
|
|
|
return nil, 0, syscall.EAFNOSUPPORT
|
|
|
|
}
|
|
|
|
return fns, uint16(port), nil
|
2017-11-19 13:14:15 +01:00
|
|
|
}
|
2017-11-17 17:25:45 +01:00
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (bind *StdNetBind) Close() error {
|
2021-03-31 22:55:18 +02:00
|
|
|
bind.mu.Lock()
|
|
|
|
defer bind.mu.Unlock()
|
|
|
|
|
2018-06-11 19:04:38 +02:00
|
|
|
var err1, err2 error
|
|
|
|
if bind.ipv4 != nil {
|
|
|
|
err1 = bind.ipv4.Close()
|
2021-02-22 02:01:50 +01:00
|
|
|
bind.ipv4 = nil
|
2018-06-11 19:04:38 +02:00
|
|
|
}
|
|
|
|
if bind.ipv6 != nil {
|
|
|
|
err2 = bind.ipv6.Close()
|
2021-02-22 02:01:50 +01:00
|
|
|
bind.ipv6 = nil
|
2018-06-11 19:04:38 +02:00
|
|
|
}
|
2021-02-22 18:47:41 +01:00
|
|
|
bind.blackhole4 = false
|
|
|
|
bind.blackhole6 = false
|
2017-11-19 13:14:15 +01:00
|
|
|
if err1 != nil {
|
|
|
|
return err1
|
|
|
|
}
|
|
|
|
return err2
|
|
|
|
}
|
2019-11-07 17:13:05 +01:00
|
|
|
|
2021-04-10 01:21:35 +02:00
|
|
|
func (*StdNetBind) makeReceiveIPv4(conn *net.UDPConn) ReceiveFunc {
|
2021-03-31 22:55:18 +02:00
|
|
|
return func(buff []byte) (int, Endpoint, error) {
|
|
|
|
n, endpoint, err := conn.ReadFromUDP(buff)
|
2021-04-10 01:21:35 +02:00
|
|
|
if endpoint != nil {
|
2021-03-31 22:55:18 +02:00
|
|
|
endpoint.IP = endpoint.IP.To4()
|
|
|
|
}
|
|
|
|
return n, (*StdNetEndpoint)(endpoint), err
|
2018-06-11 19:04:38 +02:00
|
|
|
}
|
2017-11-19 13:14:15 +01:00
|
|
|
}
|
|
|
|
|
2021-04-10 01:21:35 +02:00
|
|
|
func (*StdNetBind) makeReceiveIPv6(conn *net.UDPConn) ReceiveFunc {
|
|
|
|
return func(buff []byte) (int, Endpoint, error) {
|
|
|
|
n, endpoint, err := conn.ReadFromUDP(buff)
|
|
|
|
return n, (*StdNetEndpoint)(endpoint), err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 02:01:50 +01:00
|
|
|
func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
2017-11-19 13:14:15 +01:00
|
|
|
var err error
|
2021-02-22 02:01:50 +01:00
|
|
|
nend, ok := endpoint.(*StdNetEndpoint)
|
|
|
|
if !ok {
|
|
|
|
return ErrWrongEndpointType
|
|
|
|
}
|
2021-03-31 22:55:18 +02:00
|
|
|
|
|
|
|
bind.mu.Lock()
|
|
|
|
blackhole := bind.blackhole4
|
|
|
|
conn := bind.ipv4
|
|
|
|
if nend.IP.To4() == nil {
|
2021-03-29 22:11:11 +02:00
|
|
|
blackhole = bind.blackhole6
|
|
|
|
conn = bind.ipv6
|
2017-11-19 13:14:15 +01:00
|
|
|
}
|
2021-03-31 22:55:18 +02:00
|
|
|
bind.mu.Unlock()
|
|
|
|
|
2021-03-29 22:11:11 +02:00
|
|
|
if blackhole {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if conn == nil {
|
|
|
|
return syscall.EAFNOSUPPORT
|
|
|
|
}
|
|
|
|
_, err = conn.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
2017-11-19 13:14:15 +01:00
|
|
|
return err
|
|
|
|
}
|