* Report incorrectly formatted ignores in config file. * Compact IPv6 addresses in iptables log output to match dnsmasq reporting. * Handle dnsmasq "cached" log entries. * Update README.md iptraffic output is much improved.master
@@ -158,7 +158,7 @@ stretches to the next header and will either contain name / value | |||||
pairs or specifically formatted text data. | pairs or specifically formatted text data. | ||||
Remarks are lines that start with a pound sign (#). I am not using | Remarks are lines that start with a pound sign (#). I am not using | ||||
the MS style semicolon (;). Blank lines are also ignored. | |||||
the MS style semicolon (;). Blank lines are ignored. | |||||
Currently there are two sections allowed in the file: | Currently there are two sections allowed in the file: | ||||
@@ -167,13 +167,11 @@ Currently there are two sections allowed in the file: | |||||
outside your network (them). This is used to determine direction | outside your network (them). This is used to determine direction | ||||
of connection (in/out). | of connection (in/out). | ||||
Each line is a single entry. The entries can be in IPv4 & 6 | |||||
notation and should be in a format that matches your iptables log | |||||
output. For IPv4 that means no leading zeros. For IPv6 **all** | |||||
hex digits are usually logged, even the zeros. Address prefixes | |||||
can be used to define networks and they should end with either | |||||
"." (IPv4) or ":" (IPv6). With IPv6 it could be useful to end on | |||||
a boundary other than those denoted by colon. | |||||
Each line is a single entry. The entries can be in IPv4 or 6 | |||||
notation and should be in a minimalistic format. Basically this | |||||
means no leading zeros. For IPv6 make sure to use :: as | |||||
appropriate. Address prefixes can be used to define networks and | |||||
they should end with either "." (IPv4) or ":" (IPv6). | |||||
Example: "192.168.1." matches the 192.168.1.0/24 network. | Example: "192.168.1." matches the 192.168.1.0/24 network. | ||||
@@ -183,9 +181,11 @@ Currently there are two sections allowed in the file: | |||||
TSV records contain the following fields separated by tabs: | TSV records contain the following fields separated by tabs: | ||||
1. Our / internal address or "*" for match all addresses. | |||||
1. Our / internal address. Follows same wild card specification as | |||||
described in "us" above. | |||||
2. Our port number. 0 matches all. | 2. Our port number. 0 matches all. | ||||
3. Outside / their IP address. "*" matches all. | |||||
3. Outside / their IP address. Follows same wild card | |||||
specification as described in "us" above. | |||||
4. Their port number. Zero matches all. | 4. Their port number. Zero matches all. | ||||
5. Domain name associated with the destination. "*" matches all. | 5. Domain name associated with the destination. "*" matches all. | ||||
6. IP protocol used (must be all caps): | 6. IP protocol used (must be all caps): | ||||
@@ -217,6 +217,9 @@ Currently there are two sections allowed in the file: | |||||
So this means things like g00gle analytics, CDNs, external | So this means things like g00gle analytics, CDNs, external | ||||
JavaScript and CSS servers, ... will still be reported. | JavaScript and CSS servers, ... will still be reported. | ||||
Obviously in normal use the list of OK (ignored) content will get | |||||
quite long. Future tools will handle this better. | |||||
iptraffic Command Line | iptraffic Command Line | ||||
@@ -5,6 +5,7 @@ | |||||
// Copyright JF Possibilities, Inc. All rights reserved. | // Copyright JF Possibilities, Inc. All rights reserved. | ||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
#include <fstream> | #include <fstream> | ||||
#include <iostream> | |||||
#include "config.h" | #include "config.h" | ||||
#include "strutil.h" | #include "strutil.h" | ||||
@@ -15,9 +16,11 @@ void Config::load(const std::string &fname) { | |||||
std::ifstream f(fname.c_str()); | std::ifstream f(fname.c_str()); | ||||
TSV tsv; | TSV tsv; | ||||
Conn conn; | Conn conn; | ||||
int ln=0; | |||||
while(std::getline(f, l)) { | while(std::getline(f, l)) { | ||||
ln++; | |||||
l = strip(l); | l = strip(l); | ||||
if(l=="" || l[0]=='#') continue; | if(l=="" || l[0]=='#') continue; | ||||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') { | if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') { | ||||
@@ -28,6 +31,7 @@ heading: | |||||
/// Read in "us" list /// | /// Read in "us" list /// | ||||
while(std::getline(f, l)) { | while(std::getline(f, l)) { | ||||
ln++; | |||||
l=strip(l); | l=strip(l); | ||||
if(l=="" || l[0]=='#') continue; | if(l=="" || l[0]=='#') continue; | ||||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | ||||
@@ -39,9 +43,14 @@ heading: | |||||
/// Read in ignore list /// | /// Read in ignore list /// | ||||
while(std::getline(f, l)) { | while(std::getline(f, l)) { | ||||
ln++; | |||||
if(l=="" || l[0]=='#') continue; | if(l=="" || l[0]=='#') continue; | ||||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | ||||
tsv = l; | tsv = l; | ||||
if(tsv.count!=7) { | |||||
std::cerr << "Incorrrect column count in config file line " << ln << std::endl; | |||||
continue; | |||||
} | |||||
if(tsv.count>6) { | if(tsv.count>6) { | ||||
tsv >> conn; | tsv >> conn; | ||||
ignores.push_back(conn); | ignores.push_back(conn); | ||||
@@ -4,14 +4,48 @@ | |||||
// Started April 23rd, 2021 | // Started April 23rd, 2021 | ||||
// Copyright JF Possibilities, Inc. All rights reserved. | // Copyright JF Possibilities, Inc. All rights reserved. | ||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
#include <arpa/inet.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdexcept> | #include <stdexcept> | ||||
#include <iostream> | |||||
#include "data.h" | #include "data.h" | ||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
// Utils | |||||
////////////////////////////////////////////////////////////////////// | |||||
std::string ipv6opt(const std::string &addr) { | |||||
in6_addr buf; | |||||
char s[256]; | |||||
if(inet_pton(AF_INET6, addr.c_str(), &buf)<1) throw | |||||
std::runtime_error("ipv6opt: inet_pton() says '"+addr+"' is not a valid IPv6 address"); | |||||
if(!inet_ntop(AF_INET6, &buf, s, 255)) throw // should never happen | |||||
std::runtime_error("ipv6opt: inet_ntop() refused to convert the address back"); | |||||
return std::string(s); | |||||
} | |||||
int addr_wild_comp(const std::string &str1, const std::string &str2) { | |||||
int spre1=0, spre2=0; | |||||
if(str1=="*" || str2=="*") return 0; | |||||
if(str1!="" && (str1.end()[-1]=='.' || str1.end()[-1]==':')) spre1=str1.size(); | |||||
if(str2!="" && (str2.end()[-1]=='.' || str2.end()[-1]==':')) spre2=str2.size(); | |||||
if(spre2>spre1) spre1=spre2; | |||||
if(spre1) return strncmp(str1.c_str(), str2.c_str(), spre1); | |||||
else if(str1<str2) return -1; | |||||
else if(str1>str2) return 1; | |||||
return 0; | |||||
} | |||||
////////////////////////////////////////////////////////////////////// | |||||
// Conn | // Conn | ||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
@@ -23,6 +57,13 @@ void Conn::clear() { | |||||
void Conn::compact() { | |||||
if(us.find(':')!=us.npos) us=ipv6opt(us); | |||||
if(them.find(':')!=us.npos) them=ipv6opt(them); | |||||
} | |||||
void Conn::swap() { | void Conn::swap() { | ||||
std::string s; | std::string s; | ||||
int x; | int x; | ||||
@@ -74,19 +115,15 @@ Conn &Conn::operator=(const Splits &sp) { | |||||
// TODO: does < > have any actual meaning in this context? | // TODO: does < > have any actual meaning in this context? | ||||
int Conn::cmp(const Conn >r) const { | int Conn::cmp(const Conn >r) const { | ||||
if(us!="*" && gtr.us!="*") { | |||||
if(us<gtr.us) return -1; | |||||
if(us>gtr.us) return 1; | |||||
} | |||||
int r; | |||||
if(r = addr_wild_comp(us, gtr.us)) return r; | |||||
// TODO: auto-wildcard port based on in? | // TODO: auto-wildcard port based on in? | ||||
if(us_port && gtr.us_port) { // 0 = no comparison wildcard | if(us_port && gtr.us_port) { // 0 = no comparison wildcard | ||||
if(us_port<gtr.us_port) return -1; | if(us_port<gtr.us_port) return -1; | ||||
if(us_port>gtr.us_port) return 1; | if(us_port>gtr.us_port) return 1; | ||||
} | } | ||||
if(them!="*" && gtr.them!="*") { | |||||
if(them<gtr.them) return -1; | |||||
if(them>gtr.them) return 1; | |||||
} | |||||
if(r = addr_wild_comp(them, gtr.them)) return r; | |||||
if(them_port && gtr.them_port) { // 0 = no comparison wildcard | if(them_port && gtr.them_port) { // 0 = no comparison wildcard | ||||
if(them_port<gtr.them_port) return -1; | if(them_port<gtr.them_port) return -1; | ||||
if(them_port>gtr.them_port) return 1; | if(them_port>gtr.them_port) return 1; | ||||
@@ -20,6 +20,28 @@ | |||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
// Reduce IPv6 address string to optimal format. | |||||
// | |||||
// The resultant string is produced by standard routines in libC. Its | |||||
// as optimal as it makes it. *DON'T* run filter strings through this | |||||
// or you'll lose partial match capability. | |||||
////////////////////////////////////////////////////////////////////// | |||||
std::string ipv6opt(const std::string &addr); | |||||
////////////////////////////////////////////////////////////////////// | |||||
// Wild compare of two address strings | |||||
// | |||||
// returns: 0=match, -1 = <, 1 = > | |||||
////////////////////////////////////////////////////////////////////// | |||||
int addr_wild_comp(const std::string &str1, const std::string &str2); | |||||
////////////////////////////////////////////////////////////////////// | |||||
// Network connection between "us" and "them" | // Network connection between "us" and "them" | ||||
////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
typedef unsigned short word; | typedef unsigned short word; | ||||
@@ -35,6 +57,8 @@ struct Conn { | |||||
Conn(): us_port(0), them_port(0), in(false) {} | Conn(): us_port(0), them_port(0), in(false) {} | ||||
// clear data | // clear data | ||||
void clear(); | void clear(); | ||||
// Compact IPv6 address strings | |||||
void compact(); | |||||
// swap polarity of record | // swap polarity of record | ||||
void swap(); | void swap(); | ||||
// scan & copy data from log record in | // scan & copy data from log record in | ||||
@@ -148,8 +148,10 @@ struct IPtraffic: public cBaseApp { | |||||
/// DNS query result /// | /// DNS query result /// | ||||
// TODO: need to get more specific on tying us + them + time to DNS | // TODO: need to get more specific on tying us + them + time to DNS | ||||
// TODO: doesn't seem that CNAMEs are getting attached to requests properly. | |||||
// the logs are cryptic on this front. | |||||
if(ln.count>8 && strncmp(ln.fields[4], "dnsmasq[", 8)==0) { | if(ln.count>8 && strncmp(ln.fields[4], "dnsmasq[", 8)==0) { | ||||
if(ln[5]=="reply") { | |||||
if(ln[5]=="reply" || ln[5]=="cached") { | |||||
name = ln[6]; | name = ln[6]; | ||||
address = ln[8]; | address = ln[8]; | ||||
// Hmm... is this reply an address? | // Hmm... is this reply an address? | ||||
@@ -175,6 +177,7 @@ struct IPtraffic: public cBaseApp { | |||||
&& ln[6]=="ACCEPT") | && ln[6]=="ACCEPT") | ||||
) { | ) { | ||||
conn = ln; | conn = ln; | ||||
conn.compact(); | |||||
if(!pre_match(config.us, conn.us)) conn.swap(); | if(!pre_match(config.us, conn.us)) conn.swap(); | ||||
if((nvp=rdns.find(conn.them))!=rdns.end()) | if((nvp=rdns.find(conn.them))!=rdns.end()) | ||||
conn.name = nvp->second; | conn.name = nvp->second; | ||||