github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/tunnel/connid.go (about) 1 package tunnel 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "net" 7 8 "go.opentelemetry.io/otel/attribute" 9 "go.opentelemetry.io/otel/trace" 10 11 "github.com/telepresenceio/telepresence/v2/pkg/ipproto" 12 "github.com/telepresenceio/telepresence/v2/pkg/iputil" 13 ) 14 15 // A ConnID is a compact and immutable representation of protocol, source IP, source port, destination IP and destination port which 16 // is suitable as a map key. 17 type ConnID string 18 19 func ConnIDFromUDP(src, dst *net.UDPAddr) ConnID { 20 return NewConnID(ipproto.UDP, src.IP, dst.IP, uint16(src.Port), uint16(dst.Port)) 21 } 22 23 // NewConnID returns a new ConnID for the given values. 24 func NewConnID(proto int, src, dst net.IP, srcPort, dstPort uint16) ConnID { 25 src4 := src.To4() 26 dst4 := dst.To4() 27 if src4 != nil && dst4 != nil { 28 // These are not NOOPs because a IPv4 can be represented using a 16 byte net.IP. Here 29 // we ensure that the 4 byte form is used. 30 src = src4 31 dst = dst4 32 } else { 33 src = src.To16() 34 dst = dst.To16() 35 } 36 ls := len(src) 37 ld := len(dst) 38 if ls == 0 { 39 panic("invalid source IP") 40 } 41 if ld == 0 { 42 panic("invalid destination IP") 43 } 44 bs := make([]byte, ls+ld+5) 45 copy(bs, src) 46 binary.BigEndian.PutUint16(bs[ls:], srcPort) 47 ls += 2 48 copy(bs[ls:], dst) 49 ls += ld 50 binary.BigEndian.PutUint16(bs[ls:], dstPort) 51 ls += 2 52 bs[ls] = byte(proto) 53 return ConnID(bs) 54 } 55 56 func NewZeroID() ConnID { 57 return ConnID(make([]byte, 13)) 58 } 59 60 // areBothIPv4 returns true if the source and destination of this ConnID are both IPv4. 61 func (id ConnID) areBothIPv4() bool { 62 return len(id) == 13 63 } 64 65 // IsSourceIPv4 returns true if the source of this ConnID is IPv4. 66 func (id ConnID) IsSourceIPv4() bool { 67 return id.areBothIPv4() || net.IP(id[0:16]).To4() != nil 68 } 69 70 // IsDestinationIPv4 returns true if the destination of this ConnID is IPv4. 71 func (id ConnID) IsDestinationIPv4() bool { 72 return id.areBothIPv4() || net.IP(id[18:34]).To4() != nil 73 } 74 75 // Source returns the source IP. 76 func (id ConnID) Source() net.IP { 77 if id.areBothIPv4() { 78 return net.IP(id[0:4]) 79 } 80 return iputil.Normalize(net.IP(id[0:16])) 81 } 82 83 // SourceAddr returns the *net.TCPAddr or *net.UDPAddr that corresponds to the 84 // source IP and port of this instance. 85 func (id ConnID) SourceAddr() net.Addr { 86 if id.Protocol() == ipproto.TCP { 87 return &net.TCPAddr{IP: id.Source(), Port: int(id.SourcePort())} 88 } 89 return &net.UDPAddr{IP: id.Source(), Port: int(id.SourcePort())} 90 } 91 92 // SourcePort returns the source port. 93 func (id ConnID) SourcePort() uint16 { 94 if id.areBothIPv4() { 95 return binary.BigEndian.Uint16([]byte(id)[4:]) 96 } 97 return binary.BigEndian.Uint16([]byte(id)[16:]) 98 } 99 100 // Destination returns the destination IP. 101 func (id ConnID) Destination() net.IP { 102 if id.areBothIPv4() { 103 return net.IP(id[6:10]) 104 } 105 return iputil.Normalize(net.IP(id[18:34])) 106 } 107 108 // DestinationAddr returns the *net.TCPAddr or *net.UDPAddr that corresponds to the 109 // destination IP and port of this instance. 110 func (id ConnID) DestinationAddr() net.Addr { 111 if id.Protocol() == ipproto.TCP { 112 return &net.TCPAddr{IP: id.Destination(), Port: int(id.DestinationPort())} 113 } 114 return &net.UDPAddr{IP: id.Destination(), Port: int(id.DestinationPort())} 115 } 116 117 // DestinationPort returns the destination port. 118 func (id ConnID) DestinationPort() uint16 { 119 if id.areBothIPv4() { 120 return binary.BigEndian.Uint16([]byte(id)[10:]) 121 } 122 return binary.BigEndian.Uint16([]byte(id)[34:]) 123 } 124 125 // Protocol returns the protocol, e.g. ipproto.TCP. 126 func (id ConnID) Protocol() int { 127 return int(id[len(id)-1]) 128 } 129 130 // SourceProtocolString returns the protocol string for the source, e.g. "tcp4". 131 func (id ConnID) SourceProtocolString() (proto string) { 132 p := id.Protocol() 133 switch p { 134 case ipproto.TCP: 135 if id.IsSourceIPv4() { 136 proto = "tcp4" 137 } else { 138 proto = "tcp6" 139 } 140 case ipproto.UDP: 141 if id.IsSourceIPv4() { 142 proto = "udp4" 143 } else { 144 proto = "udp6" 145 } 146 default: 147 proto = fmt.Sprintf("unknown-%d", p) 148 } 149 return proto 150 } 151 152 // DestinationProtocolString returns the protocol string for the source, e.g. "tcp4". 153 func (id ConnID) DestinationProtocolString() (proto string) { 154 p := id.Protocol() 155 switch p { 156 case ipproto.TCP: 157 if id.IsDestinationIPv4() { 158 proto = "tcp4" 159 } else { 160 proto = "tcp6" 161 } 162 case ipproto.UDP: 163 if id.IsDestinationIPv4() { 164 proto = "udp4" 165 } else { 166 proto = "udp6" 167 } 168 default: 169 proto = fmt.Sprintf("unknown-%d", p) 170 } 171 return proto 172 } 173 174 // SourceNetwork returns either "ip4" or "ip6". 175 func (id ConnID) SourceNetwork() string { 176 if id.IsSourceIPv4() { 177 return "ip4" 178 } 179 return "ip6" 180 } 181 182 // DestinationNetwork returns either "ip4" or "ip6". 183 func (id ConnID) DestinationNetwork() string { 184 if id.IsDestinationIPv4() { 185 return "ip4" 186 } 187 return "ip6" 188 } 189 190 func (id ConnID) SpanRecord(span trace.Span) { 191 span.SetAttributes(attribute.String("tel2.conn-id", id.String())) 192 } 193 194 // Reply returns a copy of this ConnID with swapped source and destination properties. 195 func (id ConnID) Reply() ConnID { 196 return NewConnID(id.Protocol(), id.Destination(), id.Source(), id.DestinationPort(), id.SourcePort()) 197 } 198 199 // ReplyString returns a formatted string suitable for logging showing the destination:destinationPort -> source:sourcePort. 200 func (id ConnID) ReplyString() string { 201 return fmt.Sprintf("%s %s -> %s", 202 ipproto.String(id.Protocol()), 203 iputil.JoinIpPort(id.Destination(), id.DestinationPort()), 204 iputil.JoinIpPort(id.Source(), id.SourcePort())) 205 } 206 207 // String returns a formatted string suitable for logging showing the source:sourcePort -> destination:destinationPort. 208 func (id ConnID) String() string { 209 if len(id) < 13 { 210 return "bogus ConnID" 211 } 212 return fmt.Sprintf("%s %s -> %s", 213 ipproto.String(id.Protocol()), 214 iputil.JoinIpPort(id.Source(), id.SourcePort()), 215 iputil.JoinIpPort(id.Destination(), id.DestinationPort())) 216 }