mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix IPv6 Port Forwarding for the Bridge Driver
1. Allocate either a IPv4 and/or IPv6 Port Binding (HostIP, HostPort, ContainerIP, ContainerPort) based on the input and system parameters 2. Update the userland proxy as well as dummy proxy (inside port mapper) to specifically listen on either the IPv4 or IPv6 network Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>
This commit is contained in:
parent
09be71b900
commit
33a82a26a8
10 changed files with 210 additions and 68 deletions
|
@ -8,6 +8,16 @@ import (
|
||||||
"github.com/ishidawataru/sctp"
|
"github.com/ishidawataru/sctp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ipVersion refers to IP version - v4 or v6
|
||||||
|
type ipVersion string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IPv4 is version 4
|
||||||
|
ipv4 ipVersion = "4"
|
||||||
|
// IPv4 is version 6
|
||||||
|
ipv6 ipVersion = "6"
|
||||||
|
)
|
||||||
|
|
||||||
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
||||||
// between two endpoints : the frontend and the backend.
|
// between two endpoints : the frontend and the backend.
|
||||||
// It can be used to do software port-mapping between two addresses.
|
// It can be used to do software port-mapping between two addresses.
|
||||||
|
|
|
@ -19,7 +19,12 @@ type SCTPProxy struct {
|
||||||
|
|
||||||
// NewSCTPProxy creates a new SCTPProxy.
|
// NewSCTPProxy creates a new SCTPProxy.
|
||||||
func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
|
func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
|
||||||
listener, err := sctp.ListenSCTP("sctp", frontendAddr)
|
// detect version of hostIP to bind only to correct version
|
||||||
|
ipVersion := ipv4
|
||||||
|
if frontendAddr.IPAddrs[0].IP.To4() == nil {
|
||||||
|
ipVersion = ipv6
|
||||||
|
}
|
||||||
|
listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,12 @@ type TCPProxy struct {
|
||||||
|
|
||||||
// NewTCPProxy creates a new TCPProxy.
|
// NewTCPProxy creates a new TCPProxy.
|
||||||
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
|
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
|
||||||
listener, err := net.ListenTCP("tcp", frontendAddr)
|
// detect version of hostIP to bind only to correct version
|
||||||
|
ipVersion := ipv4
|
||||||
|
if frontendAddr.IP.To4() == nil {
|
||||||
|
ipVersion = ipv6
|
||||||
|
}
|
||||||
|
listener, err := net.ListenTCP("tcp"+string(ipVersion), frontendAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,12 @@ type UDPProxy struct {
|
||||||
|
|
||||||
// NewUDPProxy creates a new UDPProxy.
|
// NewUDPProxy creates a new UDPProxy.
|
||||||
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
||||||
listener, err := net.ListenUDP("udp", frontendAddr)
|
// detect version of hostIP to bind only to correct version
|
||||||
|
ipVersion := ipv4
|
||||||
|
if frontendAddr.IP.To4() == nil {
|
||||||
|
ipVersion = ipv6
|
||||||
|
}
|
||||||
|
listener, err := net.ListenUDP("udp"+string(ipVersion), frontendAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,72 +11,114 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
defaultBindingIP = net.IPv4(0, 0, 0, 0)
|
|
||||||
defaultBindingIPV6 = net.ParseIP("::")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
||||||
if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
|
if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defHostIP := defaultBindingIP
|
defHostIP := net.IPv4zero // 0.0.0.0
|
||||||
if reqDefBindIP != nil {
|
if reqDefBindIP != nil {
|
||||||
defHostIP = reqDefBindIP
|
defHostIP = reqDefBindIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPv4 port binding including user land proxy
|
var containerIPv6 net.IP
|
||||||
pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
|
if ep.addrv6 != nil {
|
||||||
if err != nil {
|
containerIPv6 = ep.addrv6.IP
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPv6 port binding excluding user land proxy
|
pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled)
|
||||||
if n.driver.config.EnableIP6Tables && ep.addrv6 != nil {
|
|
||||||
// TODO IPv6 custom default binding IP
|
|
||||||
pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ensure we clear the previous allocated IPv4 ports
|
|
||||||
n.releasePortsInternal(pb)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pb = append(pb, pbv6...)
|
|
||||||
}
|
|
||||||
return pb, nil
|
return pb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
||||||
bs := make([]types.PortBinding, 0, len(bindings))
|
bs := make([]types.PortBinding, 0, len(bindings))
|
||||||
for _, c := range bindings {
|
for _, c := range bindings {
|
||||||
b := c.GetCopy()
|
bIPv4 := c.GetCopy()
|
||||||
if err := n.allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil {
|
bIPv6 := c.GetCopy()
|
||||||
|
// Allocate IPv4 Port mappings
|
||||||
|
if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
|
||||||
|
if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
|
||||||
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
|
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
|
||||||
if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
||||||
logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
|
logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bs = append(bs, b)
|
bs = append(bs, bIPv4)
|
||||||
|
}
|
||||||
|
// Allocate IPv6 Port mappings
|
||||||
|
if ok := n.validatePortBindingIPv6(&bIPv6, containerIPv6, defHostIP); ok {
|
||||||
|
if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil {
|
||||||
|
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
|
||||||
|
if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
||||||
|
logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bs = append(bs, bIPv6)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return bs, nil
|
return bs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
|
// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true
|
||||||
|
// if this is a valid IPv4 binding, else returns false
|
||||||
|
func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool {
|
||||||
|
//Return early if there is a valid Host IP, but its not a IPv6 address
|
||||||
|
if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Adjust the host address in the operational binding
|
||||||
|
if len(bnd.HostIP) == 0 {
|
||||||
|
// Return early if the default binding address is an IPv6 address
|
||||||
|
if defHostIP.To4() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
bnd.HostIP = defHostIP
|
||||||
|
}
|
||||||
|
bnd.IP = containerIPv4
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true
|
||||||
|
// if this is a valid IP6v binding, else returns false
|
||||||
|
func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIPv6, defHostIP net.IP) bool {
|
||||||
|
// Return early if there is no IPv6 container endpoint
|
||||||
|
if containerIPv6 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Return early if there is a valid Host IP, which is a IPv4 address
|
||||||
|
if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0
|
||||||
|
if len(bnd.HostIP) == 0 {
|
||||||
|
if defHostIP.Equal(net.IPv4zero) {
|
||||||
|
bnd.HostIP = net.IPv6zero
|
||||||
|
// If the default binding IP is an IPv6 address, use it
|
||||||
|
} else if defHostIP.To4() == nil {
|
||||||
|
bnd.HostIP = defHostIP
|
||||||
|
// Return false if default binding ip is an IPv4 address
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bnd.IP = containerIPv6
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
|
||||||
var (
|
var (
|
||||||
host net.Addr
|
host net.Addr
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store the container interface address in the operational binding
|
|
||||||
bnd.IP = containerIP
|
|
||||||
|
|
||||||
// Adjust the host address in the operational binding
|
|
||||||
if len(bnd.HostIP) == 0 {
|
|
||||||
bnd.HostIP = defHostIP
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust HostPortEnd if this is not a range.
|
// Adjust HostPortEnd if this is not a range.
|
||||||
if bnd.HostPortEnd == 0 {
|
if bnd.HostPortEnd == 0 {
|
||||||
bnd.HostPortEnd = bnd.HostPort
|
bnd.HostPortEnd = bnd.HostPort
|
||||||
|
@ -90,7 +132,7 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos
|
||||||
|
|
||||||
portmapper := n.portMapper
|
portmapper := n.portMapper
|
||||||
|
|
||||||
if containerIP.To4() == nil {
|
if bnd.IP.To4() == nil {
|
||||||
portmapper = n.portMapperV6
|
portmapper = n.portMapperV6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,3 +95,74 @@ func TestPortMappingConfig(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPortMappingV6Config(t *testing.T) {
|
||||||
|
defer testutils.SetupTestOSContext(t)()
|
||||||
|
d := newDriver()
|
||||||
|
|
||||||
|
config := &configuration{
|
||||||
|
EnableIPTables: true,
|
||||||
|
EnableIP6Tables: true,
|
||||||
|
}
|
||||||
|
genericOption := make(map[string]interface{})
|
||||||
|
genericOption[netlabel.GenericData] = config
|
||||||
|
|
||||||
|
if err := d.configure(genericOption); err != nil {
|
||||||
|
t.Fatalf("Failed to setup driver config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
portBindings := []types.PortBinding{
|
||||||
|
{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)},
|
||||||
|
{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)},
|
||||||
|
{Proto: types.SCTP, Port: uint16(500), HostPort: uint16(65000)},
|
||||||
|
}
|
||||||
|
|
||||||
|
sbOptions := make(map[string]interface{})
|
||||||
|
sbOptions[netlabel.PortMap] = portBindings
|
||||||
|
netConfig := &networkConfiguration{
|
||||||
|
BridgeName: DefaultBridgeName,
|
||||||
|
EnableIPv6: true,
|
||||||
|
}
|
||||||
|
netOptions := make(map[string]interface{})
|
||||||
|
netOptions[netlabel.GenericData] = netConfig
|
||||||
|
|
||||||
|
ipdList := getIPv4Data(t, "")
|
||||||
|
err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create bridge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
te := newTestEndpoint(ipdList[0].Pool, 11)
|
||||||
|
err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil {
|
||||||
|
t.Fatalf("Failed to join the endpoint: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil {
|
||||||
|
t.Fatalf("Failed to program external connectivity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
network, ok := d.networks["dummy"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Cannot find network %s inside driver", "dummy")
|
||||||
|
}
|
||||||
|
ep, _ := network.endpoints["ep1"]
|
||||||
|
if len(ep.portMapping) != 6 {
|
||||||
|
t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
// release host mapped ports
|
||||||
|
err = d.Leave("dummy", "ep1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.RevokeExternalConnectivity("dummy", "ep1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ func TestBridge(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Unexpected format for port mapping in endpoint operational data")
|
t.Fatalf("Unexpected format for port mapping in endpoint operational data")
|
||||||
}
|
}
|
||||||
if len(pm) != 5 {
|
if len(pm) != 10 {
|
||||||
t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm))
|
t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,21 +151,17 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
containerIP, containerPort := getIPAndPort(m.container)
|
containerIP, containerPort := getIPAndPort(m.container)
|
||||||
if pm.checkIP(hostIP) {
|
|
||||||
if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
|
if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cleanup := func() error {
|
cleanup := func() error {
|
||||||
// need to undo the iptables rules before we return
|
// need to undo the iptables rules before we return
|
||||||
m.userlandProxy.Stop()
|
m.userlandProxy.Stop()
|
||||||
if pm.checkIP(hostIP) {
|
|
||||||
pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
|
pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
|
||||||
if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
|
if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,11 +44,3 @@ func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net
|
||||||
}
|
}
|
||||||
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
|
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkIP checks if IP is valid and matching to chain version
|
|
||||||
func (pm *PortMapper) checkIP(ip net.IP) bool {
|
|
||||||
if pm.chain == nil || pm.chain.IPTable.Version == iptables.IPv4 {
|
|
||||||
return ip.To4() != nil
|
|
||||||
}
|
|
||||||
return ip.To16() != nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,6 +19,16 @@ type userlandProxy interface {
|
||||||
Stop() error
|
Stop() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ipVersion refers to IP version - v4 or v6
|
||||||
|
type ipVersion string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IPv4 is version 4
|
||||||
|
ipv4 ipVersion = "4"
|
||||||
|
// IPv4 is version 6
|
||||||
|
ipv6 ipVersion = "6"
|
||||||
|
)
|
||||||
|
|
||||||
// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
|
// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
|
||||||
// proxies as separate processes.
|
// proxies as separate processes.
|
||||||
type proxyCommand struct {
|
type proxyCommand struct {
|
||||||
|
@ -79,19 +89,25 @@ func (p *proxyCommand) Stop() error {
|
||||||
type dummyProxy struct {
|
type dummyProxy struct {
|
||||||
listener io.Closer
|
listener io.Closer
|
||||||
addr net.Addr
|
addr net.Addr
|
||||||
|
ipVersion ipVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) {
|
func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) {
|
||||||
|
// detect version of hostIP to bind only to correct version
|
||||||
|
version := ipv4
|
||||||
|
if hostIP.To4() == nil {
|
||||||
|
version = ipv6
|
||||||
|
}
|
||||||
switch proto {
|
switch proto {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
|
addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
|
||||||
return &dummyProxy{addr: addr}, nil
|
return &dummyProxy{addr: addr, ipVersion: version}, nil
|
||||||
case "udp":
|
case "udp":
|
||||||
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
|
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
|
||||||
return &dummyProxy{addr: addr}, nil
|
return &dummyProxy{addr: addr, ipVersion: version}, nil
|
||||||
case "sctp":
|
case "sctp":
|
||||||
addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
|
addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
|
||||||
return &dummyProxy{addr: addr}, nil
|
return &dummyProxy{addr: addr, ipVersion: version}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Unknown addr type: %s", proto)
|
return nil, fmt.Errorf("Unknown addr type: %s", proto)
|
||||||
}
|
}
|
||||||
|
@ -100,19 +116,19 @@ func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, er
|
||||||
func (p *dummyProxy) Start() error {
|
func (p *dummyProxy) Start() error {
|
||||||
switch addr := p.addr.(type) {
|
switch addr := p.addr.(type) {
|
||||||
case *net.TCPAddr:
|
case *net.TCPAddr:
|
||||||
l, err := net.ListenTCP("tcp", addr)
|
l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.listener = l
|
p.listener = l
|
||||||
case *net.UDPAddr:
|
case *net.UDPAddr:
|
||||||
l, err := net.ListenUDP("udp", addr)
|
l, err := net.ListenUDP("udp"+string(p.ipVersion), addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.listener = l
|
p.listener = l
|
||||||
case *sctp.SCTPAddr:
|
case *sctp.SCTPAddr:
|
||||||
l, err := sctp.ListenSCTP("sctp", addr)
|
l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue