aboutsummaryrefslogblamecommitdiffstats
path: root/conn_default.go
blob: 9b2325c14b70593b5edae8645e576c3518927961 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                        
 

                                   
                                                              

   


            
                               
             
            
                 
                 

 






                                                                     











                                                 
                                           










                                         
 

                                              



                             



                                                    
 







                                               
                                                                     


                 
                                                                     






                                  
                                         


                                


                                  


                                    











                                                      
                                                                     

                           
 
                          
 
                                                      
                                                                    
                                  
         

                                                      

                                                                    
                                 
                               


                                  

                                       
 
                                       






                                        






                                                                         


                                                   
                                                       


                                               



                                                                         


                                                   






                                                                    
                                 


                                                   
                                                                         
                


                                                   
                                                                         



                  


                   



                                                   
                                                              
                       





                                                         

                          






















                                                                                                  
         
                  
 
// +build !linux android

/* SPDX-License-Identifier: GPL-2.0
 *
 * Copyright (C) 2017-2018 WireGuard LLC. All Rights Reserved.
 */

package main

import (
	"golang.org/x/sys/unix"
	"net"
	"os"
	"runtime"
	"syscall"
)

/* This code is meant to be a temporary solution
 * on platforms for which the sticky socket / source caching behavior
 * has not yet been implemented.
 *
 * See conn_linux.go for an implementation on the linux platform.
 */

type NativeBind struct {
	ipv4 *net.UDPConn
	ipv6 *net.UDPConn
}

type NativeEndpoint net.UDPAddr

var _ Bind = (*NativeBind)(nil)
var _ Endpoint = (*NativeEndpoint)(nil)

func CreateEndpoint(s string) (Endpoint, error) {
	addr, err := parseEndpoint(s)
	return (*NativeEndpoint)(addr), err
}

func (_ *NativeEndpoint) ClearSrc() {}

func (e *NativeEndpoint) DstIP() net.IP {
	return (*net.UDPAddr)(e).IP
}

func (e *NativeEndpoint) SrcIP() net.IP {
	return nil // not supported
}

func (e *NativeEndpoint) DstToBytes() []byte {
	addr := (*net.UDPAddr)(e)
	out := addr.IP.To4()
	if out == nil {
		out = addr.IP
	}
	out = append(out, byte(addr.Port&0xff))
	out = append(out, byte((addr.Port>>8)&0xff))
	return out
}

func (e *NativeEndpoint) DstToString() string {
	return (*net.UDPAddr)(e).String()
}

func (e *NativeEndpoint) SrcToString() string {
	return ""
}

func listenNet(network string, port int) (*net.UDPConn, int, error) {

	// listen

	conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port})
	if err != nil {
		return nil, 0, err
	}

	// retrieve port

	laddr := conn.LocalAddr()
	uaddr, err := net.ResolveUDPAddr(
		laddr.Network(),
		laddr.String(),
	)
	if err != nil {
		return nil, 0, err
	}
	return conn, uaddr.Port, nil
}

func extractErrno(err error) error {
	opErr, ok := err.(*net.OpError)
	if !ok {
		return nil
	}
	syscallErr, ok := opErr.Err.(*os.SyscallError)
	if !ok {
		return nil
	}
	return syscallErr.Err
}

func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
	var err error
	var bind NativeBind

	port := int(uport)

	bind.ipv4, port, err = listenNet("udp4", port)
	if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
		return nil, 0, err
	}

	bind.ipv6, port, err = listenNet("udp6", port)
	if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
		return nil, 0, err
		bind.ipv4.Close()
		bind.ipv4 = nil
		return nil, 0, err
	}

	return &bind, uint16(port), nil
}

func (bind *NativeBind) Close() error {
	var err1, err2 error
	if bind.ipv4 != nil {
		err1 = bind.ipv4.Close()
	}
	if bind.ipv6 != nil {
		err2 = bind.ipv6.Close()
	}
	if err1 != nil {
		return err1
	}
	return err2
}

func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
	if bind.ipv4 == nil {
		return 0, nil, syscall.EAFNOSUPPORT
	}
	n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
	if endpoint != nil {
		endpoint.IP = endpoint.IP.To4()
	}
	return n, (*NativeEndpoint)(endpoint), err
}

func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
	if bind.ipv6 == nil {
		return 0, nil, syscall.EAFNOSUPPORT
	}
	n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
	return n, (*NativeEndpoint)(endpoint), err
}

func (bind *NativeBind) Send(buff []byte, endpoint Endpoint) error {
	var err error
	nend := endpoint.(*NativeEndpoint)
	if nend.IP.To4() != nil {
		if bind.ipv4 == nil {
			return syscall.EAFNOSUPPORT
		}
		_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
	} else {
		if bind.ipv6 == nil {
			return syscall.EAFNOSUPPORT
		}
		_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
	}
	return err
}

var fwmarkIoctl int

func init() {
	switch runtime.GOOS {
	case "linux", "android":
		fwmarkIoctl = 36 /* unix.SO_MARK */
	case "freebsd":
		fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
	case "openbsd":
		fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
	}
}

func (bind *NativeBind) SetMark(mark uint32) error {
	if fwmarkIoctl == 0 {
		return nil
	}
	if bind.ipv4 != nil {
		fd, err := bind.ipv4.SyscallConn()
		if err != nil {
			return err
		}
		err = fd.Control(func(fd uintptr) {
			err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
		})
		if err != nil {
			return err
		}
	}
	if bind.ipv6 != nil {
		fd, err := bind.ipv6.SyscallConn()
		if err != nil {
			return err
		}
		err = fd.Control(func(fd uintptr) {
			err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
		})
		if err != nil {
			return err
		}
	}
	return nil
}