lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/xnet/trace.go (about)

     1  // Copyright (C) 2017-2021  Nexedi SA and Contributors.
     2  //                          Kirill Smelkov <kirr@nexedi.com>
     3  //
     4  // This program is free software: you can Use, Study, Modify and Redistribute
     5  // it under the terms of the GNU General Public License version 3, or (at your
     6  // option) any later version, as published by the Free Software Foundation.
     7  //
     8  // You can also Link and Combine this program with other software covered by
     9  // the terms of any of the Free Software licenses or any of the Open Source
    10  // Initiative approved licenses and Convey the resulting work. Corresponding
    11  // source of such a combination shall include the source code for all other
    12  // software used.
    13  //
    14  // This program is distributed WITHOUT ANY WARRANTY; without even the implied
    15  // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    16  //
    17  // See COPYING file for full licensing terms.
    18  // See https://www.nexedi.com/licensing for rationale and options.
    19  
    20  package xnet
    21  // network tracing
    22  
    23  import (
    24  	"context"
    25  	"net"
    26  	"sync/atomic"
    27  )
    28  
    29  // NetTrace wraps underlying networker with IO tracing layer.
    30  //
    31  // Tracing is done via calling trace func right after corresponding networking
    32  // event happenned.  No synchronization for notification is performed - if one
    33  // is required tracing func must implement such synchronization itself.
    34  //
    35  // only initiation events are traced:
    36  //
    37  // 1. Tx only (no Rx):
    38  //   - because Write, contrary to Read, never writes partial data on non-error
    39  //   - because in case of pipenet tracing writes only is enough to get whole network exchange picture
    40  //
    41  // 2. Dial only (no Accept)
    42  //   - for similar reasons.
    43  //
    44  // WARNING NetTrace functionality is currently very draft.
    45  func NetTrace(inner Networker, tracerx TraceReceiver) *Tracer {
    46  	return &Tracer{inner, tracerx, 1}
    47  }
    48  
    49  // TraceReceiver is the interface that needs to be implemented by network trace receivers.
    50  type TraceReceiver interface {
    51  	TraceNetDial(*TraceDial)
    52  	TraceNetConnect(*TraceConnect)
    53  	TraceNetListen(*TraceListen)
    54  	TraceNetTx(*TraceTx)
    55  	// XXX +TraceNetClose?
    56  }
    57  
    58  // TraceDial is event corresponding to network dial start.
    59  type TraceDial struct {
    60  	// XXX also put networker?
    61  	Dialer, Addr string
    62  }
    63  
    64  // TraceConnect is event corresponding to established network connection.
    65  type TraceConnect struct {
    66  	// XXX also put networker?
    67  	Src, Dst net.Addr
    68  	Dialed   string
    69  }
    70  
    71  // TraceListen is event corresponding to network listening.
    72  type TraceListen struct {
    73  	// XXX also put networker?
    74  	Laddr net.Addr
    75  }
    76  
    77  // TraceTx is event corresponding to network transmission.
    78  type TraceTx struct {
    79  	// XXX also put network somehow?
    80  	Src, Dst net.Addr
    81  	Pkt      []byte
    82  }
    83  
    84  // Tracer wraps underlying Networker to emit events on networking operations.
    85  //
    86  // Create it via NetTrace.
    87  type Tracer struct {
    88  	inner Networker
    89  	rx    TraceReceiver
    90  	on    int32 // atomic (tracing can be enabled/disabled at runtime)
    91  }
    92  
    93  // TraceOn tells the tracer to (re)enable delivery of trace events.
    94  func (t *Tracer) TraceOn() {
    95  	atomic.StoreInt32(&t.on, 1)
    96  }
    97  
    98  // TraceOff tells tracer to disable delivery of trace events.
    99  func (t *Tracer) TraceOff() {
   100  	atomic.StoreInt32(&t.on, 0)
   101  }
   102  
   103  func (t *Tracer) enabled() bool {
   104  	return (atomic.LoadInt32(&t.on) != 0)
   105  }
   106  
   107  // Network implements Networker.
   108  func (t *Tracer) Network() string {
   109  	return t.inner.Network() // XXX + "+trace" ?
   110  }
   111  
   112  // Name implements Networker.
   113  func (t *Tracer) Name() string {
   114  	return t.inner.Name()
   115  }
   116  
   117  // Close implements Networker.
   118  func (t *Tracer) Close() error {
   119  	// XXX +trace?
   120  	return t.inner.Close()
   121  }
   122  
   123  // Dial implements Networker.
   124  func (t *Tracer) Dial(ctx context.Context, addr string) (net.Conn, error) {
   125  	if t.enabled() {
   126  		t.rx.TraceNetDial(&TraceDial{Dialer: t.inner.Name(), Addr: addr})
   127  	}
   128  	c, err := t.inner.Dial(ctx, addr)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	if t.enabled() {
   133  		t.rx.TraceNetConnect(&TraceConnect{Src: c.LocalAddr(), Dst: c.RemoteAddr(), Dialed: addr})
   134  	}
   135  	return &traceConn{t, c}, nil
   136  }
   137  
   138  // Listen implements Networker.
   139  func (t *Tracer) Listen(ctx context.Context, laddr string) (Listener, error) {
   140  	// XXX +TraceNetListenPre ?
   141  	l, err := t.inner.Listen(ctx, laddr)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	if t.enabled() {
   146  		t.rx.TraceNetListen(&TraceListen{Laddr: l.Addr()})
   147  	}
   148  	return &netTraceListener{t, l}, nil
   149  }
   150  
   151  // netTraceListener wraps net.Listener to wrap accepted connections with traceConn.
   152  type netTraceListener struct {
   153  	t        *Tracer
   154  	Listener
   155  }
   156  
   157  func (ntl *netTraceListener) Accept(ctx context.Context) (net.Conn, error) {
   158  	c, err := ntl.Listener.Accept(ctx)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	return &traceConn{ntl.t, c}, nil
   163  }
   164  
   165  // traceConn wraps net.Conn and notifies tracer on Writes.
   166  type traceConn struct {
   167  	t        *Tracer
   168  	net.Conn
   169  }
   170  
   171  func (tc *traceConn) Write(b []byte) (int, error) {
   172  	// XXX +TraceNetTxPre ?
   173  	n, err := tc.Conn.Write(b)
   174  	if err == nil {
   175  		if tc.t.enabled() {
   176  			tc.t.rx.TraceNetTx(&TraceTx{Src: tc.LocalAddr(), Dst: tc.RemoteAddr(), Pkt: b})
   177  		}
   178  	}
   179  	return n, err
   180  }