github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/cmd/proxy/sctp_proxy.go (about)

     1  package main
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"net"
     7  	"sync"
     8  
     9  	"github.com/ishidawataru/sctp"
    10  )
    11  
    12  // SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to
    13  // handle SCTP traffic forwarding between the frontend and backend addresses.
    14  type SCTPProxy struct {
    15  	listener     *sctp.SCTPListener
    16  	frontendAddr *sctp.SCTPAddr
    17  	backendAddr  *sctp.SCTPAddr
    18  }
    19  
    20  // NewSCTPProxy creates a new SCTPProxy.
    21  func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
    22  	// detect version of hostIP to bind only to correct version
    23  	ipVersion := ipv4
    24  	if frontendAddr.IPAddrs[0].IP.To4() == nil {
    25  		ipVersion = ipv6
    26  	}
    27  	listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	// If the port in frontendAddr was 0 then ListenSCTP will have a picked
    32  	// a port to listen on, hence the call to Addr to get that actual port:
    33  	return &SCTPProxy{
    34  		listener:     listener,
    35  		frontendAddr: listener.Addr().(*sctp.SCTPAddr),
    36  		backendAddr:  backendAddr,
    37  	}, nil
    38  }
    39  
    40  func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) {
    41  	backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr)
    42  	if err != nil {
    43  		log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err)
    44  		client.Close()
    45  		return
    46  	}
    47  	clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client)
    48  	backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend)
    49  
    50  	var wg sync.WaitGroup
    51  	var broker = func(to, from net.Conn) {
    52  		io.Copy(to, from)
    53  		from.Close()
    54  		to.Close()
    55  		wg.Done()
    56  	}
    57  
    58  	wg.Add(2)
    59  	go broker(clientC, backendC)
    60  	go broker(backendC, clientC)
    61  
    62  	finish := make(chan struct{})
    63  	go func() {
    64  		wg.Wait()
    65  		close(finish)
    66  	}()
    67  
    68  	select {
    69  	case <-quit:
    70  	case <-finish:
    71  	}
    72  	clientC.Close()
    73  	backendC.Close()
    74  	<-finish
    75  }
    76  
    77  // Run starts forwarding the traffic using SCTP.
    78  func (proxy *SCTPProxy) Run() {
    79  	quit := make(chan bool)
    80  	defer close(quit)
    81  	for {
    82  		client, err := proxy.listener.Accept()
    83  		if err != nil {
    84  			log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
    85  			return
    86  		}
    87  		go proxy.clientLoop(client.(*sctp.SCTPConn), quit)
    88  	}
    89  }
    90  
    91  // Close stops forwarding the traffic.
    92  func (proxy *SCTPProxy) Close() { proxy.listener.Close() }
    93  
    94  // FrontendAddr returns the SCTP address on which the proxy is listening.
    95  func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
    96  
    97  // BackendAddr returns the SCTP proxied address.
    98  func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr }