V3 release

This commit is contained in:
Eliezer Croitoru 2026-09-09 16:09:08 +03:00
parent 4d67f8be82
commit db9f887958
4 changed files with 33 additions and 17 deletions

View File

@ -6,12 +6,9 @@ COPY src/ .
RUN go mod init ngtech.co.il/network/livtrace/v2 && go mod tidy RUN go mod init ngtech.co.il/network/livtrace/v2 && go mod tidy
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /out/livetrace-linux-amd64 .
go build -ldflags="-s -w" -o /out/livetrace-linux-amd64 .
RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 \ RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o /out/livetrace-windows-amd64.exe .
go build -ldflags="-s -w" -o /out/livetrace-windows-amd64.exe .
# Stage 2: Export target
FROM scratch AS bin FROM scratch AS bin
COPY --from=builder /out/ / COPY --from=builder /out/ /

Binary file not shown.

Binary file not shown.

View File

@ -295,12 +295,16 @@ func NewTracer(
func (t *Tracer) Done() <-chan struct{} { return t.stopCh } func (t *Tracer) Done() <-chan struct{} { return t.stopCh }
func (t *Tracer) Start() error { func (t *Tracer) Start() error {
var err error if t.proto == "winapi" {
// Windows ICMP API manages its own sockets per probe.
// No raw socket, no receive loop, no cleanup loop needed.
go t.probeLoop()
return nil
}
// ICMP socket — always required (receiving ICMP errors, and sending in ICMP mode). var err error
t.icmpConn, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") t.icmpConn, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil { if err != nil {
// Fallback: unprivileged ICMP dgram (Linux ≥5.x with ping_group_range).
t.icmpConn, err = icmp.ListenPacket("udp4", "0.0.0.0:0") t.icmpConn, err = icmp.ListenPacket("udp4", "0.0.0.0:0")
if err != nil { if err != nil {
hint := "try: sudo ./livetrace" hint := "try: sudo ./livetrace"
@ -312,7 +316,6 @@ func (t *Tracer) Start() error {
} }
t.icmpPC = t.icmpConn.IPv4PacketConn() t.icmpPC = t.icmpConn.IPv4PacketConn()
// UDP sending socket — only needed in UDP mode.
if t.proto == "udp" { if t.proto == "udp" {
t.udpSend, err = net.ListenPacket("udp4", ":0") t.udpSend, err = net.ListenPacket("udp4", ":0")
if err != nil { if err != nil {
@ -334,7 +337,9 @@ func (t *Tracer) Stop() {
default: default:
close(t.stopCh) close(t.stopCh)
} }
_ = t.icmpConn.Close() if t.icmpConn != nil {
_ = t.icmpConn.Close()
}
if t.udpSend != nil { if t.udpSend != nil {
_ = t.udpSend.Close() _ = t.udpSend.Close()
} }
@ -386,9 +391,12 @@ func (t *Tracer) makeUDPBytes() []byte {
// ─── Sending ────────────────────────────────────────────────────────────────── // ─── Sending ──────────────────────────────────────────────────────────────────
func (t *Tracer) sendProbe(ttl int) { func (t *Tracer) sendProbe(ttl int) {
if t.proto == "udp" { switch t.proto {
case "udp":
t.sendProbeUDP(ttl) t.sendProbeUDP(ttl)
} else { case "winapi":
t.sendProbeWinAPI(ttl)
default:
t.sendProbeICMP(ttl) t.sendProbeICMP(ttl)
} }
} }
@ -449,6 +457,12 @@ func (t *Tracer) sendProbeUDP(ttl int) {
} }
} }
func winAPIProbe(_ net.IP, _ int, _ time.Duration, _ []byte) (float64, string, uint32, error) {
return 0, "", 0, fmt.Errorf("winapi probe not supported on this platform")
}
func (t *Tracer) sendProbeWinAPI(_ int) {}
// ─── Probe loop ─────────────────────────────────────────────────────────────── // ─── Probe loop ───────────────────────────────────────────────────────────────
func (t *Tracer) probeLoop() { func (t *Tracer) probeLoop() {
@ -898,7 +912,7 @@ func main() {
maxHops := flag.Int("m", 30, "maximum hops") maxHops := flag.Int("m", 30, "maximum hops")
intervalMS := flag.Int("i", 1000, "probe interval in milliseconds") intervalMS := flag.Int("i", 1000, "probe interval in milliseconds")
timeoutMS := flag.Int("t", 3000, "probe timeout in milliseconds") timeoutMS := flag.Int("t", 3000, "probe timeout in milliseconds")
proto := flag.String("proto", "icmp", "probe protocol: icmp or udp") proto := flag.String("proto", "icmp", "probe protocol: icmp, udp, or winapi (Windows ICMP API, Windows only)")
port := flag.Int("port", 33434, "base destination port (UDP mode only)") port := flag.Int("port", 33434, "base destination port (UDP mode only)")
size := flag.Int("size", defPktSize, "total IP packet size in bytes (min 28); use 1500 for MTU test") size := flag.Int("size", defPktSize, "total IP packet size in bytes (min 28); use 1500 for MTU test")
noDNS := flag.Bool("n", false, "disable reverse DNS (show numeric IPs only)") noDNS := flag.Bool("n", false, "disable reverse DNS (show numeric IPs only)")
@ -930,10 +944,15 @@ Examples:
} }
p := strings.ToLower(*proto) p := strings.ToLower(*proto)
if p != "icmp" && p != "udp" {
fmt.Fprintln(os.Stderr, "Error: -proto must be icmp or udp") if p != "icmp" && p != "udp" && p != "winapi" {
os.Exit(1) fmt.Fprintln(os.Stderr, "Error: -proto must be icmp, udp, or winapi")
} os.Exit(1)
}
if p == "winapi" && runtime.GOOS != "windows" {
fmt.Fprintln(os.Stderr, "Error: -proto winapi is only available on Windows")
os.Exit(1)
}
tracer, err := NewTracer( tracer, err := NewTracer(
flag.Arg(0), flag.Arg(0),