Skip to content

Commit e93e5ac

Browse files
authored
Better server failure detection and PreserveSourceIPPortWhenDestNATMa… (#3648)
* Better server failure detection and PreserveSourceIPPortWhenDestNATMapping detection * improve PreserveSourceIPPortWhenDestNATMapping detection
1 parent 66c2586 commit e93e5ac

File tree

1 file changed

+56
-17
lines changed

1 file changed

+56
-17
lines changed

common/natTraversal/stun/natTypeTest.go

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,16 @@ func (t *NATTypeTest) TestMappingBehaviour() error {
296296
return nil
297297
}
298298

299+
// Validate OTHER-ADDRESS has both a different IP and port from the primary server.
300+
// The mapping behavior test requires a genuinely distinct endpoint to produce meaningful results.
301+
serverUDPForValidation, ok := t.TestServer.(*net.UDPAddr)
302+
if !ok {
303+
return errors.New("TestServer is not a UDP address")
304+
}
305+
if otherAddr.IP.Equal(serverUDPForValidation.IP) || otherAddr.Port == serverUDPForValidation.Port {
306+
return errors.New("OTHER-ADDRESS from STUN server does not differ in both IP and port from the primary server")
307+
}
308+
299309
// Test II: From same socket, binding to OTHER-ADDRESS (different IP and port)
300310
altAddr := &net.UDPAddr{IP: otherAddr.IP, Port: otherAddr.Port}
301311
resp2, _, err := t.doTransactionWithRetry(conn, localAddr, altAddr, t.Attempts,
@@ -594,32 +604,61 @@ func (t *NATTypeTest) CalcReminderValues() error {
594604
t.SingleSourceIPSourceNATMapping = NATYesOrNoUnknownType_No
595605
}
596606

597-
allSendToMatchRespFrom := true
598-
validPairCount := 0
607+
// PreserveSourceIPPortWhenDestNATMapping: check using RESPONSE-ORIGIN if available,
608+
// otherwise fall back to CHANGE-REQUEST hairpin detection.
609+
allResponseOriginMatchRespFrom := true
610+
responseOriginPairCount := 0
599611
for _, tc := range transcripts {
600612
if tc.Resp == nil || tc.RespFrom == nil || tc.ReqSentTo == nil {
601613
continue
602614
}
603-
if value, ok := tc.Req.Attributes.Get(stun.AttrChangeRequest); ok {
604-
if len(value.Value) != 4 || (value.Value[0] == 0 && value.Value[1] == 0 && value.Value[2] == 0 && value.Value[3] == 0) {
605-
continue
606-
}
607-
} else {
615+
var responseOrigin stun.ResponseOrigin
616+
if err := responseOrigin.GetFrom(tc.Resp); err != nil {
608617
continue
609618
}
610-
validPairCount++
611-
if tc.RespFrom.String() != tc.ReqSentTo.String() {
612-
allSendToMatchRespFrom = false
619+
responseOriginAddr := net.UDPAddr{IP: responseOrigin.IP, Port: responseOrigin.Port}
620+
if responseOriginAddr.String() == tc.ReqSentTo.String() {
621+
continue
622+
}
623+
responseOriginPairCount++
624+
if tc.RespFrom.String() != responseOriginAddr.String() {
625+
allResponseOriginMatchRespFrom = false
613626
break
614627
}
615628
}
616-
switch {
617-
case validPairCount < 1:
618-
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Unknown
619-
case allSendToMatchRespFrom:
620-
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_No
621-
default:
622-
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Yes
629+
if responseOriginPairCount >= 1 {
630+
if allResponseOriginMatchRespFrom {
631+
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Yes
632+
} else {
633+
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_No
634+
}
635+
} else {
636+
// Fallback: detect if we receive our own hairpin packets.
637+
// If RespFrom == ReqSentTo for all responses, the source address was rewritten (not preserved).
638+
allSendToMatchRespFrom := true
639+
respPairCount := 0
640+
for _, tc := range transcripts {
641+
if tc.Resp == nil || tc.RespFrom == nil || tc.ReqSentTo == nil {
642+
continue
643+
}
644+
// Only count hairpin packets: responses that are binding requests (not binding responses)
645+
if tc.Resp.Type != stun.BindingRequest {
646+
continue
647+
}
648+
respPairCount++
649+
if tc.RespFrom.String() != tc.ReqSentTo.String() {
650+
allSendToMatchRespFrom = false
651+
break
652+
}
653+
}
654+
switch {
655+
case respPairCount < 1:
656+
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Unknown
657+
case allSendToMatchRespFrom:
658+
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_No
659+
default:
660+
t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Yes
661+
}
623662
}
624663

625664
// PreserveSourcePortWhenSourceNATMapping: check if mapped port matches local source port

0 commit comments

Comments
 (0)