2222
2323const (
2424 errRewriteTCPDestinationPort = "rewrite TCP destination port: %v"
25+
26+ // Port offsets in TCP/UDP headers
27+ sourcePortOffset = 0
28+ destinationPortOffset = 2
2529)
2630
2731// ipv4Checksum calculates IPv4 header checksum.
@@ -748,8 +752,8 @@ func (m *Manager) applyPortDNATRule(packetData []byte, d *decoder, rule portDNAT
748752 return true
749753}
750754
751- // rewriteTCPDestinationPort rewrites the destination port in a TCP packet and updates checksum.
752- func (m * Manager ) rewriteTCPDestinationPort (packetData []byte , d * decoder , newPort uint16 ) error {
755+ // rewriteTCPPort rewrites a TCP port (source or destination) and updates checksum.
756+ func (m * Manager ) rewriteTCPPort (packetData []byte , d * decoder , newPort uint16 , portOffset int ) error {
753757 if len (packetData ) < 20 || d .decoded [0 ] != layers .LayerTypeIPv4 {
754758 return ErrIPv4Only
755759 }
@@ -768,9 +772,9 @@ func (m *Manager) rewriteTCPDestinationPort(packetData []byte, d *decoder, newPo
768772 return fmt .Errorf ("packet too short for TCP header" )
769773 }
770774
771- oldPort := binary . BigEndian . Uint16 ( packetData [ tcpStart + 2 : tcpStart + 4 ])
772-
773- binary .BigEndian .PutUint16 (packetData [tcpStart + 2 : tcpStart + 4 ], newPort )
775+ portStart := tcpStart + portOffset
776+ oldPort := binary . BigEndian . Uint16 ( packetData [ portStart : portStart + 2 ])
777+ binary .BigEndian .PutUint16 (packetData [portStart : portStart + 2 ], newPort )
774778
775779 if len (packetData ) >= tcpStart + 18 {
776780 checksumOffset := tcpStart + 16
@@ -787,45 +791,17 @@ func (m *Manager) rewriteTCPDestinationPort(packetData []byte, d *decoder, newPo
787791 return nil
788792}
789793
794+ // rewriteTCPDestinationPort rewrites the destination port in a TCP packet and updates checksum.
795+ func (m * Manager ) rewriteTCPDestinationPort (packetData []byte , d * decoder , newPort uint16 ) error {
796+ return m .rewriteTCPPort (packetData , d , newPort , destinationPortOffset )
797+ }
798+
790799// rewriteTCPSourcePort rewrites the source port in a TCP packet and updates checksum.
791800func (m * Manager ) rewriteTCPSourcePort (packetData []byte , d * decoder , newPort uint16 ) error {
792- if len (packetData ) < 20 || d .decoded [0 ] != layers .LayerTypeIPv4 {
793- return ErrIPv4Only
794- }
795-
796- if len (d .decoded ) < 2 || d .decoded [1 ] != layers .LayerTypeTCP {
797- return fmt .Errorf ("not a TCP packet" )
798- }
799-
800- ipHeaderLen := int (d .ip4 .IHL ) * 4
801- if ipHeaderLen < 20 || ipHeaderLen > len (packetData ) {
802- return errInvalidIPHeaderLength
803- }
804-
805- tcpStart := ipHeaderLen
806- if len (packetData ) < tcpStart + 4 {
807- return fmt .Errorf ("packet too short for TCP header" )
808- }
809-
810- oldPort := binary .BigEndian .Uint16 (packetData [tcpStart : tcpStart + 2 ])
811-
812- binary .BigEndian .PutUint16 (packetData [tcpStart :tcpStart + 2 ], newPort )
813-
814- if len (packetData ) >= tcpStart + 18 {
815- checksumOffset := tcpStart + 16
816- oldChecksum := binary .BigEndian .Uint16 (packetData [checksumOffset : checksumOffset + 2 ])
817-
818- var oldPortBytes , newPortBytes [2 ]byte
819- binary .BigEndian .PutUint16 (oldPortBytes [:], oldPort )
820- binary .BigEndian .PutUint16 (newPortBytes [:], newPort )
821-
822- newChecksum := incrementalUpdate (oldChecksum , oldPortBytes [:], newPortBytes [:])
823- binary .BigEndian .PutUint16 (packetData [checksumOffset :checksumOffset + 2 ], newChecksum )
824- }
825-
826- return nil
801+ return m .rewriteTCPPort (packetData , d , newPort , sourcePortOffset )
827802}
828803
804+ // applyInboundUDPPortDNAT applies port DNAT to inbound UDP packets.
829805func (m * Manager ) applyInboundUDPPortDNAT (packetData []byte , d * decoder , dstIP netip.Addr , dstPort uint16 ) bool {
830806 m .portDNATMutex .RLock ()
831807 defer m .portDNATMutex .RUnlock ()
@@ -849,7 +825,8 @@ func (m *Manager) applyInboundUDPPortDNAT(packetData []byte, d *decoder, dstIP n
849825 return false
850826}
851827
852- func (m * Manager ) rewriteUDPDestinationPort (packetData []byte , d * decoder , newPort uint16 ) error {
828+ // rewriteUDPPort rewrites a UDP port (source or destination) and updates checksum.
829+ func (m * Manager ) rewriteUDPPort (packetData []byte , d * decoder , newPort uint16 , portOffset int ) error {
853830 if len (packetData ) < 20 || d .decoded [0 ] != layers .LayerTypeIPv4 {
854831 return ErrIPv4Only
855832 }
@@ -868,8 +845,9 @@ func (m *Manager) rewriteUDPDestinationPort(packetData []byte, d *decoder, newPo
868845 return fmt .Errorf ("packet too short for UDP header" )
869846 }
870847
871- oldPort := binary .BigEndian .Uint16 (packetData [udpStart + 2 : udpStart + 4 ])
872- binary .BigEndian .PutUint16 (packetData [udpStart + 2 :udpStart + 4 ], newPort )
848+ portStart := udpStart + portOffset
849+ oldPort := binary .BigEndian .Uint16 (packetData [portStart : portStart + 2 ])
850+ binary .BigEndian .PutUint16 (packetData [portStart :portStart + 2 ], newPort )
873851
874852 checksumOffset := udpStart + 6
875853 if len (packetData ) >= udpStart + 8 {
@@ -887,6 +865,11 @@ func (m *Manager) rewriteUDPDestinationPort(packetData []byte, d *decoder, newPo
887865 return nil
888866}
889867
868+ // rewriteUDPDestinationPort rewrites the destination port in a UDP packet and updates checksum.
869+ func (m * Manager ) rewriteUDPDestinationPort (packetData []byte , d * decoder , newPort uint16 ) error {
870+ return m .rewriteUDPPort (packetData , d , newPort , destinationPortOffset )
871+ }
872+
890873// translateOutboundPortReverse applies reverse port DNAT to outbound return traffic.
891874func (m * Manager ) translateOutboundPortReverse (packetData []byte , d * decoder ) bool {
892875 if ! m .portDNATEnabled .Load () {
@@ -932,6 +915,7 @@ func (m *Manager) translateOutboundPortReverse(packetData []byte, d *decoder) bo
932915 return m .applyOutboundUDPPortReverse (packetData , d , srcIP , srcPort )
933916}
934917
918+ // applyOutboundUDPPortReverse applies reverse port DNAT to outbound UDP packets.
935919func (m * Manager ) applyOutboundUDPPortReverse (packetData []byte , d * decoder , srcIP netip.Addr , srcPort uint16 ) bool {
936920 m .portDNATMutex .RLock ()
937921 defer m .portDNATMutex .RUnlock ()
@@ -955,40 +939,7 @@ func (m *Manager) applyOutboundUDPPortReverse(packetData []byte, d *decoder, src
955939 return false
956940}
957941
942+ // rewriteUDPSourcePort rewrites the source port in a UDP packet and updates checksum.
958943func (m * Manager ) rewriteUDPSourcePort (packetData []byte , d * decoder , newPort uint16 ) error {
959- if len (packetData ) < 20 || d .decoded [0 ] != layers .LayerTypeIPv4 {
960- return ErrIPv4Only
961- }
962-
963- if len (d .decoded ) < 2 || d .decoded [1 ] != layers .LayerTypeUDP {
964- return fmt .Errorf ("not a UDP packet" )
965- }
966-
967- ipHeaderLen := int (d .ip4 .IHL ) * 4
968- if ipHeaderLen < 20 || ipHeaderLen > len (packetData ) {
969- return errInvalidIPHeaderLength
970- }
971-
972- udpStart := ipHeaderLen
973- if len (packetData ) < udpStart + 8 {
974- return fmt .Errorf ("packet too short for UDP header" )
975- }
976-
977- oldPort := binary .BigEndian .Uint16 (packetData [udpStart : udpStart + 2 ])
978- binary .BigEndian .PutUint16 (packetData [udpStart :udpStart + 2 ], newPort )
979-
980- checksumOffset := udpStart + 6
981- if len (packetData ) >= udpStart + 8 {
982- oldChecksum := binary .BigEndian .Uint16 (packetData [checksumOffset : checksumOffset + 2 ])
983- if oldChecksum != 0 {
984- var oldPortBytes , newPortBytes [2 ]byte
985- binary .BigEndian .PutUint16 (oldPortBytes [:], oldPort )
986- binary .BigEndian .PutUint16 (newPortBytes [:], newPort )
987-
988- newChecksum := incrementalUpdate (oldChecksum , oldPortBytes [:], newPortBytes [:])
989- binary .BigEndian .PutUint16 (packetData [checksumOffset :checksumOffset + 2 ], newChecksum )
990- }
991- }
992-
993- return nil
944+ return m .rewriteUDPPort (packetData , d , newPort , sourcePortOffset )
994945}
0 commit comments