gitee.com/aurawing/surguard-go@v0.3.1-0.20240409071558-96509a61ecf3/conn/bind_windows.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package conn
     7  
     8  import (
     9  	"encoding/binary"
    10  	"io"
    11  	"net"
    12  	"net/netip"
    13  	"strconv"
    14  	"sync"
    15  	"sync/atomic"
    16  	"unsafe"
    17  
    18  	"golang.org/x/sys/windows"
    19  
    20  	"gitee.com/aurawing/surguard-go/conn/winrio"
    21  )
    22  
    23  const (
    24  	packetsPerRing = 1024
    25  	bytesPerPacket = 2048 - 32
    26  	receiveSpins   = 15
    27  )
    28  
    29  type ringPacket struct {
    30  	addr WinRingEndpoint
    31  	data [bytesPerPacket]byte
    32  }
    33  
    34  type ringBuffer struct {
    35  	packets    uintptr
    36  	head, tail uint32
    37  	id         winrio.BufferId
    38  	iocp       windows.Handle
    39  	isFull     bool
    40  	cq         winrio.Cq
    41  	mu         sync.Mutex
    42  	overlapped windows.Overlapped
    43  }
    44  
    45  func (rb *ringBuffer) Push() *ringPacket {
    46  	for rb.isFull {
    47  		panic("ring is full")
    48  	}
    49  	ret := (*ringPacket)(unsafe.Pointer(rb.packets + (uintptr(rb.tail%packetsPerRing) * unsafe.Sizeof(ringPacket{}))))
    50  	rb.tail += 1
    51  	if rb.tail%packetsPerRing == rb.head%packetsPerRing {
    52  		rb.isFull = true
    53  	}
    54  	return ret
    55  }
    56  
    57  func (rb *ringBuffer) Return(count uint32) {
    58  	if rb.head%packetsPerRing == rb.tail%packetsPerRing && !rb.isFull {
    59  		return
    60  	}
    61  	rb.head += count
    62  	rb.isFull = false
    63  }
    64  
    65  type afWinRingBind struct {
    66  	sock      windows.Handle
    67  	rx, tx    ringBuffer
    68  	rq        winrio.Rq
    69  	mu        sync.Mutex
    70  	blackhole bool
    71  }
    72  
    73  // WinRingBind uses Windows registered I/O for fast ring buffered networking.
    74  type WinRingBind struct {
    75  	v4, v6 afWinRingBind
    76  	mu     sync.RWMutex
    77  	isOpen atomic.Uint32 // 0, 1, or 2
    78  }
    79  
    80  func NewDefaultBind() Bind { return NewWinRingBind() }
    81  
    82  func NewWinRingBind() Bind {
    83  	if !winrio.Initialize() {
    84  		return NewStdNetBind()
    85  	}
    86  	return new(WinRingBind)
    87  }
    88  
    89  type WinRingEndpoint struct {
    90  	family uint16
    91  	data   [30]byte
    92  }
    93  
    94  var (
    95  	_ Bind     = (*WinRingBind)(nil)
    96  	_ Endpoint = (*WinRingEndpoint)(nil)
    97  )
    98  
    99  func (*WinRingBind) ParseEndpoint(s string) (Endpoint, error) {
   100  	host, port, err := net.SplitHostPort(s)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	host16, err := windows.UTF16PtrFromString(host)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	port16, err := windows.UTF16PtrFromString(port)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	hints := windows.AddrinfoW{
   113  		Flags:    windows.AI_NUMERICHOST,
   114  		Family:   windows.AF_UNSPEC,
   115  		Socktype: windows.SOCK_DGRAM,
   116  		Protocol: windows.IPPROTO_UDP,
   117  	}
   118  	var addrinfo *windows.AddrinfoW
   119  	err = windows.GetAddrInfoW(host16, port16, &hints, &addrinfo)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	defer windows.FreeAddrInfoW(addrinfo)
   124  	if (addrinfo.Family != windows.AF_INET && addrinfo.Family != windows.AF_INET6) || addrinfo.Addrlen > unsafe.Sizeof(WinRingEndpoint{}) {
   125  		return nil, windows.ERROR_INVALID_ADDRESS
   126  	}
   127  	var dst [unsafe.Sizeof(WinRingEndpoint{})]byte
   128  	copy(dst[:], unsafe.Slice((*byte)(unsafe.Pointer(addrinfo.Addr)), addrinfo.Addrlen))
   129  	return (*WinRingEndpoint)(unsafe.Pointer(&dst[0])), nil
   130  }
   131  
   132  func (*WinRingEndpoint) ClearSrc() {}
   133  
   134  func (e *WinRingEndpoint) DstIP() netip.Addr {
   135  	switch e.family {
   136  	case windows.AF_INET:
   137  		return netip.AddrFrom4(*(*[4]byte)(e.data[2:6]))
   138  	case windows.AF_INET6:
   139  		return netip.AddrFrom16(*(*[16]byte)(e.data[6:22]))
   140  	}
   141  	return netip.Addr{}
   142  }
   143  
   144  func (e *WinRingEndpoint) SrcIP() netip.Addr {
   145  	return netip.Addr{} // not supported
   146  }
   147  
   148  func (e *WinRingEndpoint) DstToBytes() []byte {
   149  	switch e.family {
   150  	case windows.AF_INET:
   151  		b := make([]byte, 0, 6)
   152  		b = append(b, e.data[2:6]...)
   153  		b = append(b, e.data[1], e.data[0])
   154  		return b
   155  	case windows.AF_INET6:
   156  		b := make([]byte, 0, 18)
   157  		b = append(b, e.data[6:22]...)
   158  		b = append(b, e.data[1], e.data[0])
   159  		return b
   160  	}
   161  	return nil
   162  }
   163  
   164  func (e *WinRingEndpoint) DstToString() string {
   165  	switch e.family {
   166  	case windows.AF_INET:
   167  		return netip.AddrPortFrom(netip.AddrFrom4(*(*[4]byte)(e.data[2:6])), binary.BigEndian.Uint16(e.data[0:2])).String()
   168  	case windows.AF_INET6:
   169  		var zone string
   170  		if scope := *(*uint32)(unsafe.Pointer(&e.data[22])); scope > 0 {
   171  			zone = strconv.FormatUint(uint64(scope), 10)
   172  		}
   173  		return netip.AddrPortFrom(netip.AddrFrom16(*(*[16]byte)(e.data[6:22])).WithZone(zone), binary.BigEndian.Uint16(e.data[0:2])).String()
   174  	}
   175  	return ""
   176  }
   177  
   178  func (e *WinRingEndpoint) SrcToString() string {
   179  	return ""
   180  }
   181  
   182  func (ring *ringBuffer) CloseAndZero() {
   183  	if ring.cq != 0 {
   184  		winrio.CloseCompletionQueue(ring.cq)
   185  		ring.cq = 0
   186  	}
   187  	if ring.iocp != 0 {
   188  		windows.CloseHandle(ring.iocp)
   189  		ring.iocp = 0
   190  	}
   191  	if ring.id != 0 {
   192  		winrio.DeregisterBuffer(ring.id)
   193  		ring.id = 0
   194  	}
   195  	if ring.packets != 0 {
   196  		windows.VirtualFree(ring.packets, 0, windows.MEM_RELEASE)
   197  		ring.packets = 0
   198  	}
   199  	ring.head = 0
   200  	ring.tail = 0
   201  	ring.isFull = false
   202  }
   203  
   204  func (bind *afWinRingBind) CloseAndZero() {
   205  	bind.rx.CloseAndZero()
   206  	bind.tx.CloseAndZero()
   207  	if bind.sock != 0 {
   208  		windows.CloseHandle(bind.sock)
   209  		bind.sock = 0
   210  	}
   211  	bind.blackhole = false
   212  }
   213  
   214  func (bind *WinRingBind) closeAndZero() {
   215  	bind.isOpen.Store(0)
   216  	bind.v4.CloseAndZero()
   217  	bind.v6.CloseAndZero()
   218  }
   219  
   220  func (ring *ringBuffer) Open() error {
   221  	var err error
   222  	packetsLen := unsafe.Sizeof(ringPacket{}) * packetsPerRing
   223  	ring.packets, err = windows.VirtualAlloc(0, packetsLen, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	ring.id, err = winrio.RegisterPointer(unsafe.Pointer(ring.packets), uint32(packetsLen))
   228  	if err != nil {
   229  		return err
   230  	}
   231  	ring.iocp, err = windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	ring.cq, err = winrio.CreateIOCPCompletionQueue(packetsPerRing, ring.iocp, 0, &ring.overlapped)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	return nil
   240  }
   241  
   242  func (bind *afWinRingBind) Open(family int32, sa windows.Sockaddr) (windows.Sockaddr, error) {
   243  	var err error
   244  	bind.sock, err = winrio.Socket(family, windows.SOCK_DGRAM, windows.IPPROTO_UDP)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	err = bind.rx.Open()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	err = bind.tx.Open()
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	bind.rq, err = winrio.CreateRequestQueue(bind.sock, packetsPerRing, 1, packetsPerRing, 1, bind.rx.cq, bind.tx.cq, 0)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  	err = windows.Bind(bind.sock, sa)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	sa, err = windows.Getsockname(bind.sock)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	return sa, nil
   269  }
   270  
   271  func (bind *WinRingBind) Open(ipv4Addr, ipv6Addr string, port uint16) (recvFns []ReceiveFunc, selectedPort uint16, err error) {
   272  	bind.mu.Lock()
   273  	defer bind.mu.Unlock()
   274  	defer func() {
   275  		if err != nil {
   276  			bind.closeAndZero()
   277  		}
   278  	}()
   279  	if bind.isOpen.Load() != 0 {
   280  		return nil, 0, ErrBindAlreadyOpen
   281  	}
   282  	var sa windows.Sockaddr
   283  	var ipv4b *[4]byte
   284  	if ipv4Addr != "" {
   285  		ipv4b = (*[4]byte)(net.ParseIP(ipv4Addr)[12:])
   286  		sa, err = bind.v4.Open(windows.AF_INET, &windows.SockaddrInet4{Port: int(port), Addr: *ipv4b})
   287  	} else {
   288  		sa, err = bind.v4.Open(windows.AF_INET, &windows.SockaddrInet4{Port: int(port)})
   289  	}
   290  
   291  	if err != nil {
   292  		return nil, 0, err
   293  	}
   294  	var ipv6b *[16]byte
   295  	if ipv6Addr != "" {
   296  		ipv6b = (*[16]byte)(net.ParseIP(ipv6Addr))
   297  		sa, err = bind.v6.Open(windows.AF_INET6, &windows.SockaddrInet6{Port: sa.(*windows.SockaddrInet4).Port, Addr: *ipv6b})
   298  	} else {
   299  		sa, err = bind.v6.Open(windows.AF_INET6, &windows.SockaddrInet6{Port: sa.(*windows.SockaddrInet4).Port})
   300  	}
   301  
   302  	if err != nil {
   303  		return nil, 0, err
   304  	}
   305  	selectedPort = uint16(sa.(*windows.SockaddrInet6).Port)
   306  	for i := 0; i < packetsPerRing; i++ {
   307  		err = bind.v4.InsertReceiveRequest()
   308  		if err != nil {
   309  			return nil, 0, err
   310  		}
   311  		err = bind.v6.InsertReceiveRequest()
   312  		if err != nil {
   313  			return nil, 0, err
   314  		}
   315  	}
   316  	bind.isOpen.Store(1)
   317  	return []ReceiveFunc{bind.receiveIPv4, bind.receiveIPv6}, selectedPort, err
   318  }
   319  
   320  func (bind *WinRingBind) Close() error {
   321  	bind.mu.RLock()
   322  	if bind.isOpen.Load() != 1 {
   323  		bind.mu.RUnlock()
   324  		return nil
   325  	}
   326  	bind.isOpen.Store(2)
   327  	windows.PostQueuedCompletionStatus(bind.v4.rx.iocp, 0, 0, nil)
   328  	windows.PostQueuedCompletionStatus(bind.v4.tx.iocp, 0, 0, nil)
   329  	windows.PostQueuedCompletionStatus(bind.v6.rx.iocp, 0, 0, nil)
   330  	windows.PostQueuedCompletionStatus(bind.v6.tx.iocp, 0, 0, nil)
   331  	bind.mu.RUnlock()
   332  	bind.mu.Lock()
   333  	defer bind.mu.Unlock()
   334  	bind.closeAndZero()
   335  	return nil
   336  }
   337  
   338  // TODO: When all Binds handle IdealBatchSize, remove this dynamic function and
   339  // rename the IdealBatchSize constant to BatchSize.
   340  func (bind *WinRingBind) BatchSize() int {
   341  	// TODO: implement batching in and out of the ring
   342  	return 1
   343  }
   344  
   345  func (bind *WinRingBind) SetMark(mark uint32) error {
   346  	return nil
   347  }
   348  
   349  func (bind *afWinRingBind) InsertReceiveRequest() error {
   350  	packet := bind.rx.Push()
   351  	dataBuffer := &winrio.Buffer{
   352  		Id:     bind.rx.id,
   353  		Offset: uint32(uintptr(unsafe.Pointer(&packet.data[0])) - bind.rx.packets),
   354  		Length: uint32(len(packet.data)),
   355  	}
   356  	addressBuffer := &winrio.Buffer{
   357  		Id:     bind.rx.id,
   358  		Offset: uint32(uintptr(unsafe.Pointer(&packet.addr)) - bind.rx.packets),
   359  		Length: uint32(unsafe.Sizeof(packet.addr)),
   360  	}
   361  	bind.mu.Lock()
   362  	defer bind.mu.Unlock()
   363  	return winrio.ReceiveEx(bind.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, uintptr(unsafe.Pointer(packet)))
   364  }
   365  
   366  //go:linkname procyield runtime.procyield
   367  func procyield(cycles uint32)
   368  
   369  func (bind *afWinRingBind) Receive(buf []byte, isOpen *atomic.Uint32) (int, Endpoint, error) {
   370  	if isOpen.Load() != 1 {
   371  		return 0, nil, net.ErrClosed
   372  	}
   373  	bind.rx.mu.Lock()
   374  	defer bind.rx.mu.Unlock()
   375  
   376  	var err error
   377  	var count uint32
   378  	var results [1]winrio.Result
   379  retry:
   380  	count = 0
   381  	for tries := 0; count == 0 && tries < receiveSpins; tries++ {
   382  		if tries > 0 {
   383  			if isOpen.Load() != 1 {
   384  				return 0, nil, net.ErrClosed
   385  			}
   386  			procyield(1)
   387  		}
   388  		count = winrio.DequeueCompletion(bind.rx.cq, results[:])
   389  	}
   390  	if count == 0 {
   391  		err = winrio.Notify(bind.rx.cq)
   392  		if err != nil {
   393  			return 0, nil, err
   394  		}
   395  		var bytes uint32
   396  		var key uintptr
   397  		var overlapped *windows.Overlapped
   398  		err = windows.GetQueuedCompletionStatus(bind.rx.iocp, &bytes, &key, &overlapped, windows.INFINITE)
   399  		if err != nil {
   400  			return 0, nil, err
   401  		}
   402  		if isOpen.Load() != 1 {
   403  			return 0, nil, net.ErrClosed
   404  		}
   405  		count = winrio.DequeueCompletion(bind.rx.cq, results[:])
   406  		if count == 0 {
   407  			return 0, nil, io.ErrNoProgress
   408  		}
   409  	}
   410  	bind.rx.Return(1)
   411  	err = bind.InsertReceiveRequest()
   412  	if err != nil {
   413  		return 0, nil, err
   414  	}
   415  	// We limit the MTU well below the 65k max for practicality, but this means a remote host can still send us
   416  	// huge packets. Just try again when this happens. The infinite loop this could cause is still limited to
   417  	// attacker bandwidth, just like the rest of the receive path.
   418  	if windows.Errno(results[0].Status) == windows.WSAEMSGSIZE {
   419  		if isOpen.Load() != 1 {
   420  			return 0, nil, net.ErrClosed
   421  		}
   422  		goto retry
   423  	}
   424  	if results[0].Status != 0 {
   425  		return 0, nil, windows.Errno(results[0].Status)
   426  	}
   427  	packet := (*ringPacket)(unsafe.Pointer(uintptr(results[0].RequestContext)))
   428  	ep := packet.addr
   429  	n := copy(buf, packet.data[:results[0].BytesTransferred])
   430  	return n, &ep, nil
   431  }
   432  
   433  func (bind *WinRingBind) receiveIPv4(bufs [][]byte, sizes []int, eps []Endpoint) (int, error) {
   434  	bind.mu.RLock()
   435  	defer bind.mu.RUnlock()
   436  	n, ep, err := bind.v4.Receive(bufs[0], &bind.isOpen)
   437  	sizes[0] = n
   438  	eps[0] = ep
   439  	return 1, err
   440  }
   441  
   442  func (bind *WinRingBind) receiveIPv6(bufs [][]byte, sizes []int, eps []Endpoint) (int, error) {
   443  	bind.mu.RLock()
   444  	defer bind.mu.RUnlock()
   445  	n, ep, err := bind.v6.Receive(bufs[0], &bind.isOpen)
   446  	sizes[0] = n
   447  	eps[0] = ep
   448  	return 1, err
   449  }
   450  
   451  func (bind *afWinRingBind) Send(buf []byte, nend *WinRingEndpoint, isOpen *atomic.Uint32) error {
   452  	if isOpen.Load() != 1 {
   453  		return net.ErrClosed
   454  	}
   455  	if len(buf) > bytesPerPacket {
   456  		return io.ErrShortBuffer
   457  	}
   458  	bind.tx.mu.Lock()
   459  	defer bind.tx.mu.Unlock()
   460  	var results [packetsPerRing]winrio.Result
   461  	count := winrio.DequeueCompletion(bind.tx.cq, results[:])
   462  	if count == 0 && bind.tx.isFull {
   463  		err := winrio.Notify(bind.tx.cq)
   464  		if err != nil {
   465  			return err
   466  		}
   467  		var bytes uint32
   468  		var key uintptr
   469  		var overlapped *windows.Overlapped
   470  		err = windows.GetQueuedCompletionStatus(bind.tx.iocp, &bytes, &key, &overlapped, windows.INFINITE)
   471  		if err != nil {
   472  			return err
   473  		}
   474  		if isOpen.Load() != 1 {
   475  			return net.ErrClosed
   476  		}
   477  		count = winrio.DequeueCompletion(bind.tx.cq, results[:])
   478  		if count == 0 {
   479  			return io.ErrNoProgress
   480  		}
   481  	}
   482  	if count > 0 {
   483  		bind.tx.Return(count)
   484  	}
   485  	packet := bind.tx.Push()
   486  	packet.addr = *nend
   487  	copy(packet.data[:], buf)
   488  	dataBuffer := &winrio.Buffer{
   489  		Id:     bind.tx.id,
   490  		Offset: uint32(uintptr(unsafe.Pointer(&packet.data[0])) - bind.tx.packets),
   491  		Length: uint32(len(buf)),
   492  	}
   493  	addressBuffer := &winrio.Buffer{
   494  		Id:     bind.tx.id,
   495  		Offset: uint32(uintptr(unsafe.Pointer(&packet.addr)) - bind.tx.packets),
   496  		Length: uint32(unsafe.Sizeof(packet.addr)),
   497  	}
   498  	bind.mu.Lock()
   499  	defer bind.mu.Unlock()
   500  	return winrio.SendEx(bind.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, 0)
   501  }
   502  
   503  func (bind *WinRingBind) Send(bufs [][]byte, endpoint Endpoint) error {
   504  	nend, ok := endpoint.(*WinRingEndpoint)
   505  	if !ok {
   506  		return ErrWrongEndpointType
   507  	}
   508  	bind.mu.RLock()
   509  	defer bind.mu.RUnlock()
   510  	for _, buf := range bufs {
   511  		switch nend.family {
   512  		case windows.AF_INET:
   513  			if bind.v4.blackhole {
   514  				continue
   515  			}
   516  			if err := bind.v4.Send(buf, nend, &bind.isOpen); err != nil {
   517  				return err
   518  			}
   519  		case windows.AF_INET6:
   520  			if bind.v6.blackhole {
   521  				continue
   522  			}
   523  			if err := bind.v6.Send(buf, nend, &bind.isOpen); err != nil {
   524  				return err
   525  			}
   526  		}
   527  	}
   528  	return nil
   529  }
   530  
   531  func (s *StdNetBind) BindSocketToInterface4(interfaceIndex uint32, blackhole bool) error {
   532  	s.mu.Lock()
   533  	defer s.mu.Unlock()
   534  	sysconn, err := s.ipv4.SyscallConn()
   535  	if err != nil {
   536  		return err
   537  	}
   538  	err2 := sysconn.Control(func(fd uintptr) {
   539  		err = bindSocketToInterface4(windows.Handle(fd), interfaceIndex)
   540  	})
   541  	if err2 != nil {
   542  		return err2
   543  	}
   544  	if err != nil {
   545  		return err
   546  	}
   547  	s.blackhole4 = blackhole
   548  	return nil
   549  }
   550  
   551  func (s *StdNetBind) BindSocketToInterface6(interfaceIndex uint32, blackhole bool) error {
   552  	s.mu.Lock()
   553  	defer s.mu.Unlock()
   554  	sysconn, err := s.ipv6.SyscallConn()
   555  	if err != nil {
   556  		return err
   557  	}
   558  	err2 := sysconn.Control(func(fd uintptr) {
   559  		err = bindSocketToInterface6(windows.Handle(fd), interfaceIndex)
   560  	})
   561  	if err2 != nil {
   562  		return err2
   563  	}
   564  	if err != nil {
   565  		return err
   566  	}
   567  	s.blackhole6 = blackhole
   568  	return nil
   569  }
   570  
   571  func (bind *WinRingBind) BindSocketToInterface4(interfaceIndex uint32, blackhole bool) error {
   572  	bind.mu.RLock()
   573  	defer bind.mu.RUnlock()
   574  	if bind.isOpen.Load() != 1 {
   575  		return net.ErrClosed
   576  	}
   577  	err := bindSocketToInterface4(bind.v4.sock, interfaceIndex)
   578  	if err != nil {
   579  		return err
   580  	}
   581  	bind.v4.blackhole = blackhole
   582  	return nil
   583  }
   584  
   585  func (bind *WinRingBind) BindSocketToInterface6(interfaceIndex uint32, blackhole bool) error {
   586  	bind.mu.RLock()
   587  	defer bind.mu.RUnlock()
   588  	if bind.isOpen.Load() != 1 {
   589  		return net.ErrClosed
   590  	}
   591  	err := bindSocketToInterface6(bind.v6.sock, interfaceIndex)
   592  	if err != nil {
   593  		return err
   594  	}
   595  	bind.v6.blackhole = blackhole
   596  	return nil
   597  }
   598  
   599  func bindSocketToInterface4(handle windows.Handle, interfaceIndex uint32) error {
   600  	const IP_UNICAST_IF = 31
   601  	/* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
   602  	var bytes [4]byte
   603  	binary.BigEndian.PutUint32(bytes[:], interfaceIndex)
   604  	interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0]))
   605  	err := windows.SetsockoptInt(handle, windows.IPPROTO_IP, IP_UNICAST_IF, int(interfaceIndex))
   606  	if err != nil {
   607  		return err
   608  	}
   609  	return nil
   610  }
   611  
   612  func bindSocketToInterface6(handle windows.Handle, interfaceIndex uint32) error {
   613  	const IPV6_UNICAST_IF = 31
   614  	return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(interfaceIndex))
   615  }