sticky_linux.go raw

   1  //go:build linux && !android
   2  
   3  /* SPDX-License-Identifier: MIT
   4   *
   5   * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
   6   */
   7  
   8  package conn
   9  
  10  import (
  11  	"net/netip"
  12  	"unsafe"
  13  
  14  	"golang.org/x/sys/unix"
  15  )
  16  
  17  func (e *StdNetEndpoint) SrcIP() netip.Addr {
  18  	switch len(e.src) {
  19  	case unix.CmsgSpace(unix.SizeofInet4Pktinfo):
  20  		info := (*unix.Inet4Pktinfo)(unsafe.Pointer(&e.src[unix.CmsgLen(0)]))
  21  		return netip.AddrFrom4(info.Spec_dst)
  22  	case unix.CmsgSpace(unix.SizeofInet6Pktinfo):
  23  		info := (*unix.Inet6Pktinfo)(unsafe.Pointer(&e.src[unix.CmsgLen(0)]))
  24  		// TODO: set zone. in order to do so we need to check if the address is
  25  		// link local, and if it is perform a syscall to turn the ifindex into a
  26  		// zone string because netip uses string zones.
  27  		return netip.AddrFrom16(info.Addr)
  28  	}
  29  	return netip.Addr{}
  30  }
  31  
  32  func (e *StdNetEndpoint) SrcIfidx() int32 {
  33  	switch len(e.src) {
  34  	case unix.CmsgSpace(unix.SizeofInet4Pktinfo):
  35  		info := (*unix.Inet4Pktinfo)(unsafe.Pointer(&e.src[unix.CmsgLen(0)]))
  36  		return info.Ifindex
  37  	case unix.CmsgSpace(unix.SizeofInet6Pktinfo):
  38  		info := (*unix.Inet6Pktinfo)(unsafe.Pointer(&e.src[unix.CmsgLen(0)]))
  39  		return int32(info.Ifindex)
  40  	}
  41  	return 0
  42  }
  43  
  44  func (e *StdNetEndpoint) SrcToString() string {
  45  	return e.SrcIP().String()
  46  }
  47  
  48  // getSrcFromControl parses the control for PKTINFO and if found updates ep with
  49  // the source information found.
  50  func getSrcFromControl(control []byte, ep *StdNetEndpoint) {
  51  	ep.ClearSrc()
  52  
  53  	var (
  54  		hdr  unix.Cmsghdr
  55  		data []byte
  56  		rem  []byte = control
  57  		err  error
  58  	)
  59  
  60  	for len(rem) > unix.SizeofCmsghdr {
  61  		hdr, data, rem, err = unix.ParseOneSocketControlMessage(rem)
  62  		if err != nil {
  63  			return
  64  		}
  65  
  66  		if hdr.Level == unix.IPPROTO_IP &&
  67  			hdr.Type == unix.IP_PKTINFO {
  68  
  69  			if ep.src == nil || cap(ep.src) < unix.CmsgSpace(unix.SizeofInet4Pktinfo) {
  70  				ep.src = make([]byte, 0, unix.CmsgSpace(unix.SizeofInet4Pktinfo))
  71  			}
  72  			ep.src = ep.src[:unix.CmsgSpace(unix.SizeofInet4Pktinfo)]
  73  
  74  			hdrBuf := unsafe.Slice((*byte)(unsafe.Pointer(&hdr)), unix.SizeofCmsghdr)
  75  			copy(ep.src, hdrBuf)
  76  			copy(ep.src[unix.CmsgLen(0):], data)
  77  			return
  78  		}
  79  
  80  		if hdr.Level == unix.IPPROTO_IPV6 &&
  81  			hdr.Type == unix.IPV6_PKTINFO {
  82  
  83  			if ep.src == nil || cap(ep.src) < unix.CmsgSpace(unix.SizeofInet6Pktinfo) {
  84  				ep.src = make([]byte, 0, unix.CmsgSpace(unix.SizeofInet6Pktinfo))
  85  			}
  86  
  87  			ep.src = ep.src[:unix.CmsgSpace(unix.SizeofInet6Pktinfo)]
  88  
  89  			hdrBuf := unsafe.Slice((*byte)(unsafe.Pointer(&hdr)), unix.SizeofCmsghdr)
  90  			copy(ep.src, hdrBuf)
  91  			copy(ep.src[unix.CmsgLen(0):], data)
  92  			return
  93  		}
  94  	}
  95  }
  96  
  97  // setSrcControl sets an IP{V6}_PKTINFO in control based on the source address
  98  // and source ifindex found in ep. control's len will be set to 0 in the event
  99  // that ep is a default value.
 100  func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
 101  	if cap(*control) < len(ep.src) {
 102  		return
 103  	}
 104  	*control = (*control)[:0]
 105  	*control = append(*control, ep.src...)
 106  }
 107  
 108  // stickyControlSize returns the recommended buffer size for pooling sticky
 109  // offloading control data.
 110  var stickyControlSize = unix.CmsgSpace(unix.SizeofInet6Pktinfo)
 111  
 112  const StdNetSupportsStickySockets = true
 113