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 }