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