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  }