@@ -21,11 +21,13 @@ import (
2121 "github.com/google/gopacket/layers"
2222 "github.com/pkg/errors"
2323 "github.com/rs/zerolog"
24+ "go.opentelemetry.io/otel/attribute"
2425 "golang.org/x/net/icmp"
2526 "golang.org/x/net/ipv4"
2627 "golang.org/x/net/ipv6"
2728
2829 "github.com/cloudflare/cloudflared/packet"
30+ "github.com/cloudflare/cloudflared/tracing"
2931)
3032
3133const (
@@ -266,33 +268,50 @@ func (ip *icmpProxy) Serve(ctx context.Context) error {
266268// The async version of Win32 APIs take a callback whose memory is not garbage collected, so we use the synchronous version.
267269// It's possible that a slow request will block other requests, so we set the timeout to only 1s.
268270func (ip * icmpProxy ) Request (ctx context.Context , pk * packet.ICMP , responder * packetResponder ) error {
269- if pk == nil {
270- return errPacketNil
271- }
272271 defer func () {
273272 if r := recover (); r != nil {
274273 ip .logger .Error ().Interface ("error" , r ).Msgf ("Recover panic from sending icmp request/response, error %s" , debug .Stack ())
275274 }
276275 }()
277276
277+ _ , requestSpan := responder .requestSpan (ctx , pk )
278+ defer responder .exportSpan ()
279+
278280 echo , err := getICMPEcho (pk .Message )
279281 if err != nil {
280282 return err
281283 }
282- respData , err := ip .icmpEchoRoundtrip (pk .Dst , echo )
284+ requestSpan .SetAttributes (
285+ attribute .Int ("originalEchoID" , echo .ID ),
286+ attribute .Int ("seq" , echo .Seq ),
287+ )
288+
289+ resp , err := ip .icmpEchoRoundtrip (pk .Dst , echo )
283290 if err != nil {
284291 ip .logger .Err (err ).Msg ("ICMP echo roundtrip failed" )
292+ tracing .EndWithErrorStatus (requestSpan , err )
285293 return err
286294 }
287-
288- err = ip .handleEchoReply (pk , echo , respData , responder )
295+ tracing .End (requestSpan )
296+ responder .exportSpan ()
297+
298+ _ , replySpan := responder .replySpan (ctx , ip .logger )
299+ replySpan .SetAttributes (
300+ attribute .Int ("originalEchoID" , echo .ID ),
301+ attribute .Int ("seq" , echo .Seq ),
302+ attribute .Int64 ("rtt" , int64 (resp .rtt ())),
303+ attribute .String ("status" , resp .status ().String ()),
304+ )
305+ err = ip .handleEchoReply (pk , echo , resp , responder )
289306 if err != nil {
307+ tracing .EndWithErrorStatus (replySpan , err )
290308 return errors .Wrap (err , "failed to handle ICMP echo reply" )
291309 }
310+ tracing .End (replySpan )
292311 return nil
293312}
294313
295- func (ip * icmpProxy ) handleEchoReply (request * packet.ICMP , echoReq * icmp.Echo , data [] byte , responder * packetResponder ) error {
314+ func (ip * icmpProxy ) handleEchoReply (request * packet.ICMP , echoReq * icmp.Echo , resp echoResp , responder * packetResponder ) error {
296315 var replyType icmp.Type
297316 if request .Dst .Is4 () {
298317 replyType = ipv4 .ICMPTypeEchoReply
@@ -313,7 +332,7 @@ func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, d
313332 Body : & icmp.Echo {
314333 ID : echoReq .ID ,
315334 Seq : echoReq .Seq ,
316- Data : data ,
335+ Data : resp . payload () ,
317336 },
318337 },
319338 }
@@ -334,16 +353,17 @@ func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, d
334353 return responder .returnPacket (serializedPacket )
335354}
336355
337- func (ip * icmpProxy ) icmpEchoRoundtrip (dst netip.Addr , echo * icmp.Echo ) ([] byte , error ) {
356+ func (ip * icmpProxy ) icmpEchoRoundtrip (dst netip.Addr , echo * icmp.Echo ) (echoResp , error ) {
338357 if dst .Is6 () {
339358 if ip .srcSocketAddr == nil {
340359 return nil , fmt .Errorf ("cannot send ICMPv6 using ICMPv4 proxy" )
341360 }
342361 resp , err := ip .icmp6SendEcho (dst , echo )
343362 if err != nil {
363+
344364 return nil , errors .Wrap (err , "failed to send/receive ICMPv6 echo" )
345365 }
346- return resp . data , nil
366+ return resp , nil
347367 }
348368 if ip .srcSocketAddr != nil {
349369 return nil , fmt .Errorf ("cannot send ICMPv4 using ICMPv6 proxy" )
@@ -352,7 +372,7 @@ func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte,
352372 if err != nil {
353373 return nil , errors .Wrap (err , "failed to send/receive ICMPv4 echo" )
354374 }
355- return resp . data , nil
375+ return resp , nil
356376}
357377
358378/*
@@ -371,7 +391,7 @@ func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte,
371391 To retain the reference allocated objects, conversion from pointer to uintptr must happen as arguments to the
372392 syscall function
373393*/
374- func (ip * icmpProxy ) icmpSendEcho (dst netip.Addr , echo * icmp.Echo ) (* echoResp , error ) {
394+ func (ip * icmpProxy ) icmpSendEcho (dst netip.Addr , echo * icmp.Echo ) (* echoV4Resp , error ) {
375395 dataSize := len (echo .Data )
376396 replySize := echoReplySize + uintptr (dataSize )
377397 replyBuf := make ([]byte , replySize )
@@ -399,7 +419,7 @@ func (ip *icmpProxy) icmpSendEcho(dst netip.Addr, echo *icmp.Echo) (*echoResp, e
399419 } else if replyCount > 1 {
400420 ip .logger .Warn ().Msgf ("Received %d ICMP echo replies, only sending 1 back" , replyCount )
401421 }
402- return newEchoResp (replyBuf )
422+ return newEchoV4Resp (replyBuf )
403423}
404424
405425// Third definition of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax is address in uint32
@@ -411,12 +431,30 @@ func inAddrV4(ip netip.Addr) (uint32, error) {
411431 return endian .Uint32 (v4 [:]), nil
412432}
413433
414- type echoResp struct {
434+ type echoResp interface {
435+ status () ipStatus
436+ rtt () uint32
437+ payload () []byte
438+ }
439+
440+ type echoV4Resp struct {
415441 reply * echoReply
416442 data []byte
417443}
418444
419- func newEchoResp (replyBuf []byte ) (* echoResp , error ) {
445+ func (r * echoV4Resp ) status () ipStatus {
446+ return r .reply .Status
447+ }
448+
449+ func (r * echoV4Resp ) rtt () uint32 {
450+ return r .reply .RoundTripTime
451+ }
452+
453+ func (r * echoV4Resp ) payload () []byte {
454+ return r .data
455+ }
456+
457+ func newEchoV4Resp (replyBuf []byte ) (* echoV4Resp , error ) {
420458 if len (replyBuf ) == 0 {
421459 return nil , fmt .Errorf ("reply buffer is empty" )
422460 }
@@ -430,7 +468,7 @@ func newEchoResp(replyBuf []byte) (*echoResp, error) {
430468 if dataBufStart < int (echoReplySize ) {
431469 return nil , fmt .Errorf ("reply buffer size %d is too small to hold data of size %d" , len (replyBuf ), int (reply .DataSize ))
432470 }
433- return & echoResp {
471+ return & echoV4Resp {
434472 reply : & reply ,
435473 data : replyBuf [dataBufStart :],
436474 }, nil
@@ -502,6 +540,18 @@ type echoV6Resp struct {
502540 data []byte
503541}
504542
543+ func (r * echoV6Resp ) status () ipStatus {
544+ return r .reply .Status
545+ }
546+
547+ func (r * echoV6Resp ) rtt () uint32 {
548+ return r .reply .RoundTripTime
549+ }
550+
551+ func (r * echoV6Resp ) payload () []byte {
552+ return r .data
553+ }
554+
505555func newEchoV6Resp (replyBuf []byte , dataSize int ) (* echoV6Resp , error ) {
506556 if len (replyBuf ) == 0 {
507557 return nil , fmt .Errorf ("reply buffer is empty" )
0 commit comments