Over-the-wire 


The project is currently under active development.
Overview
over-the-wire
is a Node.js packet manipulation library supporting:
- Packet crafting and parsing
- Capturing network traffic and sending packets in all formats
- Parsing and serializing pcap and pcapng file formats
- Creating custom non-TCP/UDP socket instances
System Requirements
- Libpcap/WinPcap/Npcap library installed (if Wireshark is installed on your system, you are good to go)
- Node.js version 16.10.0 or higher recommended
- C++ compiler, if there are no prebuilt bindings for your system
Installation
npm install over-the-wire --save
Getting started
This example shows a Node.js implementation of traceroute built with nothing but the Node.js standard library and the over-the-wire API. The code is kind of oversimplified, it as a minimal, self-contained demo of the key abstractions in the library:
- Discover the active default gateway
- craft and send ICMP Echo packets hop-by-hop
- Capture the entire exchange to a pcap/pcapng file for later inspection.
const net = require('node:net');
const os = require('node:os');
const fs = require('node:fs');
const otw = require('over-the-wire');
function getMyIp(iface, targetIp) {
const family = net.isIP(targetIp) == 6 ? 'IPv6' : 'IPv4';
return os.networkInterfaces()?.[iface]?.find(e => e.family == family)?.address;
}
async function traceroute(targetIp) {
const { Pcap, Packet } = otw;
// ARP and Routing info
const [
{ iface, ip: gatewayIp, family },
arpTable,
] = await Promise.all([
otw.system.gatewayFor(targetIp),
otw.system.getArpTable(),
]);
if (family != 'AF_INET') {
console.error('Unsupported family');
process.exit(0);
}
const gatewayMac = arpTable[iface].find(e => e.ipAddr == gatewayIp).hwAddr;
// Listen for all ICMP requests
const dev = new Pcap.LiveDevice({
iface,
direction: 'inout',
filter: 'icmp',
});
// Get info about interface
console.log('[*] Using interface: ', dev.iface);
// Save captured packets to a pcapng file
const dump = Pcap.createWriteStream({ format: 'pcapng' });
dump.pipe(fs.createWriteStream('dump.pcapng'));
const myIp = getMyIp(iface, targetIp);
const path = [];
let ttl = 1;
//Send ICMP packet with a specified TTL
let sequence = 1;
const ping = (timeToLive) => {
const pkt = new Packet({ iface: dev.iface })
.Ethernet({ dst: gatewayMac })
.IPv4({ src: myIp, dst: targetIp, timeToLive })
.ICMP({
type: 8,
code: 0,
id: Math.floor(Math.random() * 65535),
sequence,
});
dev.write(pkt);
sequence++;
};
// Print packet info
dev.on('data', pkt => {
try {
// Uncomment for debugging
//console.log(`[*] ${pkt.layers.IPv4.src} -> ${pkt.layers.IPv4.dst} (${pkt.layers.ICMP.type}), ttl: ${ttl}, [${path.join()}]`);
if (pkt.layers.ICMP && pkt.layers.IPv4.dst == myIp) {
const srcIp = pkt.layers.IPv4.src;
if (pkt.layers.ICMP.type == 0) {
path.push(targetIp);
console.log('[*] Traced path');
console.log([...new Set(path)].map(e => `- ${e}`).join('\n'));
process.exit(0);
}
else {
if (path[path.length - 1] != srcIp) {
path.push(srcIp);
ttl++;
}
ping(ttl);
const fixedTtl = ttl;
// Just in case there is no response
let tid = setInterval(() => {
if (ttl == fixedTtl) {
ping(ttl);
}
else {
clearInterval(tid);
}
}, 2e2);
}
}
//Saving all captured traffic
dump.write(pkt);
} catch(err) {
console.error(err);
process.exit(1);
}
});
setTimeout(() => ping(ttl), 1e3);
}
// google.com's IP
traceroute('172.217.168.174').catch(console.error);
Documentation
Questions or Suggestions
Feel free to open any issue in the Issues section of this repository. Currently, there are no restrictions on the format.