blob: d8be4668421795ee3ab12b1e7cb084c8db8d70b6 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package netboot
import (
"net"
"os"
"syscall"
"golang.org/x/sys/unix"
)
// UDPConnWithReusablePort creates a new connection that binds to the specified port while allowing it to be reused.
func UDPConnWithReusablePort(port int, zone string, reusable bool) (*net.UDPConn, error) {
syscall.ForkLock.RLock()
fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
if err == nil {
unix.CloseOnExec(fd)
}
syscall.ForkLock.RUnlock()
if err != nil {
return nil, err
}
if reusable {
// SO_REUSEADDR and SO_REUSEPORT allows binding to the same port multiple
// times which is necessary in the case when there are multiple instances.
if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
}
// Bind the socket to the provided port and zone.
sockaddr := &syscall.SockaddrInet6{Port: port}
if zone != "" {
ift, err := net.InterfaceByName(zone)
if err != nil {
return nil, err
}
sockaddr.ZoneId = uint32(ift.Index)
}
if err := syscall.Bind(fd, sockaddr); err != nil {
syscall.Close(fd)
return nil, err
}
f := os.NewFile(uintptr(fd), "")
conn, err := net.FilePacketConn(f)
f.Close()
if err != nil {
return nil, err
}
return conn.(*net.UDPConn), err
}