diff --git a/index.js b/index.js index 3d98c04..b0c4d4b 100644 --- a/index.js +++ b/index.js @@ -120,6 +120,45 @@ function buildBPFFilter() { return filters.length > 0 ? filters.join(' and ') : ''; } +/** + * Validate and format IP address for Elasticsearch + * Returns null if invalid + */ +function validateIPAddress(ipString) { + if (!ipString || typeof ipString !== 'string') return null; + + // IPv4 validation regex + const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/; + + // IPv6 validation - check if it contains colons and valid hex characters + const ipv6Regex = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/; + + // Check if it's a valid IPv4 + if (ipv4Regex.test(ipString)) { + const parts = ipString.split('.'); + const allValid = parts.every(part => { + const num = parseInt(part, 10); + return num >= 0 && num <= 255; + }); + return allValid ? ipString : null; + } + + // Check if it's a valid IPv6 + if (ipv6Regex.test(ipString)) { + return ipString; + } + + // Try to convert MAC-like format to IPv6 (fe:80:... -> fe80::...) + // Some decoders might return IPv6 in an incorrect format + if (ipString.includes(':') && ipString.length > 20) { + // This might be a malformed IPv6, try to detect and skip + logger.debug(`Skipping malformed IP address: ${ipString}`); + return null; + } + + return null; +} + /** * Check if content is ASCII/readable */ @@ -321,14 +360,31 @@ async function processPacket(buffer, interfaceInfo) { const ipRet = decoders.IPV4(buffer, ret.offset); if (ipRet) { - packet.ip = { - version: 4, - src: ipRet.info.srcaddr, - dst: ipRet.info.dstaddr, - protocol: ipRet.info.protocol, - ttl: ipRet.info.ttl, - length: ipRet.info.totallen - }; + const srcAddr = validateIPAddress(ipRet.info.srcaddr); + const dstAddr = validateIPAddress(ipRet.info.dstaddr); + + // Only add IP fields if addresses are valid + if (srcAddr && dstAddr) { + packet.ip = { + version: 4, + src: srcAddr, + dst: dstAddr, + protocol: ipRet.info.protocol, + ttl: ipRet.info.ttl, + length: ipRet.info.totallen + }; + } else { + // Store raw addresses if validation fails + packet.ip_raw = { + version: 4, + src: ipRet.info.srcaddr, + dst: ipRet.info.dstaddr, + protocol: ipRet.info.protocol, + ttl: ipRet.info.ttl, + length: ipRet.info.totallen + }; + logger.debug(`Invalid IPv4 addresses: ${ipRet.info.srcaddr} -> ${ipRet.info.dstaddr}`); + } // Decode TCP if (ipRet.info.protocol === PROTOCOL.IP.TCP) { @@ -402,13 +458,29 @@ async function processPacket(buffer, interfaceInfo) { const ipv6Ret = decoders.IPV6(buffer, ret.offset); if (ipv6Ret) { - packet.ip = { - version: 6, - src: ipv6Ret.info.srcaddr, - dst: ipv6Ret.info.dstaddr, - protocol: ipv6Ret.info.protocol, - hop_limit: ipv6Ret.info.hoplimit - }; + const srcAddr = validateIPAddress(ipv6Ret.info.srcaddr); + const dstAddr = validateIPAddress(ipv6Ret.info.dstaddr); + + // Only add IP fields if addresses are valid + if (srcAddr && dstAddr) { + packet.ip = { + version: 6, + src: srcAddr, + dst: dstAddr, + protocol: ipv6Ret.info.protocol, + hop_limit: ipv6Ret.info.hoplimit + }; + } else { + // Store raw addresses if validation fails + packet.ip_raw = { + version: 6, + src: ipv6Ret.info.srcaddr, + dst: ipv6Ret.info.dstaddr, + protocol: ipv6Ret.info.protocol, + hop_limit: ipv6Ret.info.hoplimit + }; + logger.debug(`Invalid IPv6 addresses: ${ipv6Ret.info.srcaddr} -> ${ipv6Ret.info.dstaddr}`); + } } } @@ -510,6 +582,17 @@ async function initializeElasticsearch() { hop_limit: { type: 'integer' } } }, + ip_raw: { + properties: { + version: { type: 'integer' }, + src: { type: 'keyword' }, + dst: { type: 'keyword' }, + protocol: { type: 'integer' }, + ttl: { type: 'integer' }, + length: { type: 'integer' }, + hop_limit: { type: 'integer' } + } + }, tcp: { properties: { src_port: { type: 'integer' },