如何找出进程发送数据包的PID(产生networkingstream量)?

几个星期前,我遇到了一个问题,我在大约300个节点的大型networking中更改了DNS地址。 之后,一些节点仍然继续询问旧的DNS服务器,尽piperesolv.conf正常,而host / nslookup正在查询新的DNS服务器。

看着tcpdump并试图用iptableslogging来logging请求,我确认了一些主机确实仍在向旧的名字服务器发送查询。

我把其中一个主机从生产中拿走,并开始closures服务/支撑过程,试图找出罪魁祸首。

最后 – 它是lldpd守护进程,在启动时明显caching了域名服务器,甚至没有注意到resolv.conf中的更改。

所以,我的问题是 – 是否有一个更聪明的方法来找出哪个PId产生特定types的stream量? 我尝试与auditctl,但没有太大的成功。 CentOS 6有问题,但是如果有任何Linux发行版的解决scheme,我将不胜感激。

netstat有很多选项可以显示通过tcp / udp /两者的监听/打开套接字的组合。 就像是:

 $> sudo netstat -pan Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Addr Foreign Addr State PID/Program name ... tcp 0 1 192.168.66.1:39219 192.168.66.139:2003 SYN_SENT 2045/logstash-forwa 

…会给你很多的输出,但包括拥有这些端口的进程的源,目标,端口号和PID。

auditctl有什么问题?

你会这样做

1)定义你的审计规则来审计sendmsg和sendto系统调用。 这些系统调用在名称parsing期间使用。

 auditctl -a exit,always -F arch=b64 -S sendmsg -S sendto -k send 

2)现在search您的审计logging。 你可以在这里根据远程DNS IP来grep

 ausearch -k send -i|grep -A2 "serv:53" 

在下面的例子中,你可以看到负责系统调用的应用程序被称为dig

 ausearch -k send -i|grep -A2 "serv:53" type=SOCKADDR msg=audit(10/31/2016 15:24:56.264:176998) : saddr=inet host:172.16.0.23 serv:53 type=SYSCALL msg=audit(10/31/2016 15:24:56.264:176998) : arch=x86_64 syscall=sendmsg success=yes exit=29 a0=14 a1=7fa1919f9ac0 a2=0 a3=7fa1919f9780 items=0 ppid=31729 pid=32047 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts5 ses=52 comm=dig exe=/usr/bin/dig subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=send comm=dig exe=/usr/bin/dig 

而区分发送远程DNS请求的方式在这里。 所以你只需要grep一个特定的DNS主机。

 saddr=inet host:172.16.0.23 serv:53 

甚至更好 – 看看使用什么DNS主机(在这个例子中我只有一个)

 ausearch -k send -i|grep "serv:53"|awk '{print $6}'|sort|uniq -c 3 host:172.16.0.23 

然后缩小哪些应用程序正在使用这些特定的主机。

编辑1:其实我只是简单的ping到一个主机strace。 看起来像sendmsg并不总是被使用。 这是我所看到的

 socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4 connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, 16) = 0 gettimeofday({1477929832, 712018}, NULL) = 0 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) sendto(4, "\3\326\1\0\0\1\0\0\0\0\0\0\tvkontakte\2ru\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30 poll([{fd=4, events=POLLIN}], 1, 5000) = 1 ([{fd=4, revents=POLLIN}]) ioctl(4, FIONREAD, [62]) = 0 recvfrom(4, "\3\326\201\200\0\1\0\2\0\0\0\0\tvkontakte\2ru\0\0\1\0\1\300\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, [16]) = 62 close(4) = 0 

我以前的例子是基于挖掘应用程序,在系统调用方面略有不同的路线。
所以在大多数情况下,这就是这个规则

 auditctl -a exit,always -F arch=b64 -S connect -k connect 

紧接着ausearch

 ausearch -k connect -i|grep saddr|grep "serv:53"|awk '{print $6}'|sort|uniq -c 

我前几天也遇到了同样的问题,并提出了一个非常简单的方法。 它基于这样一个事实, 即发送进程将等待DNS响应来自它发送请求的同一个端口

  1. 使用iptables -j LOG查找传出DNS请求的源端口
  2. 使用lsof -i UDP:<source_port>来找出哪个进程正在等待该端口上的响应。

当然,由于响应在毫秒内到达,所以你不能手动完成。 而且,即使在自动化的情况下,也不能保证在DNS响应到达之前您能够查询系统,并且发送进程死亡。 这就是为什么甚至在执行上述步骤之前,我还configuration内核stream量控制器以将传出的数据包延迟到指定的ip / port(使用tc模块netem )。 这允许我控制时间窗口,我必须在步骤1中获取的源UDP端口上查询系统中哪些PID正在等待DNS响应。

我已经使用一个名为ptrap的小脚本(这是一种更通用的解决scheme,不限于DNS请求,因此可以使用任何基于TCP / UDP的协议检测进程)自动执行上述步骤,包括tc延迟。 在我的帮助下,我发现,在我的情况下,联系旧的DNS服务器的服务是sendmail。

德米特里上面的答案+1; 这对我很好:

 auditctl -a exit,always -F arch=b64 -F a0=2 -S socket -k SOCKET 

要查看结果条目,我grep该“-k”string的日志文件

 grep SOCKET /var/log/audit/audit.log 

为了得到有趣的领域,

 grep SOCKET /var/log/audit/audit.log | \ cut -d' ' -f 4- | \ sed "s|^|@\n|g;s| |\n|g" | \ grep -E "^((exe|uid|comm)=|@)" | \ tr '\n@' ' \n' |\ sort -u 

(解释: cut -d''-f 4- – >使用空格(-d“')作为分隔符将行切成字段,显示字段倒数第四(4))

(解释: sed“s | ^ | @ \ n | g; s | | \ n | g” – >编辑行,在行首加上'@'char-plus-newline,把空格换成换行符)

(解释: grep -E“^((uid | comm | exe)= | @)” – >当原始行的每个字段现在在它自己的行上时,挑出有趣的字段:user-id,command,executable – 和行开始'@'字符。)

(解释: tr'\ n @''\ n' – >现在只有想要的字段,将换行符转换回空格,并将前缀'@'换回到换行符(将这些字段重新join到一行中)

(解释: sort -u – >sorting行,只显示唯一行)

给我:

 uid=0 comm="atop" exe="/usr/bin/atop" uid=0 comm="http" exe="/usr/lib/apt/methods/http" uid=0 comm="links" exe="/usr/bin/links" uid=0 comm="ntpdate" exe="/usr/sbin/ntpdate" uid=0 comm="ufdbguardd" exe="/usr/local/ufdbguard/bin/ufdbguardd" uid=1000 comm=536F636B657420546872656164 exe="/usr/lib/firefox/firefox" uid=1000 comm="clock-applet" exe="/usr/lib/mate-panel/clock-applet" uid=1000 comm="pool" exe="/usr/lib/mate-panel/clock-applet" uid=105 comm="http" exe="/usr/lib/apt/methods/http" uid=105 comm="https" exe="/usr/lib/apt/methods/https" uid=135 comm="unbound" exe="/usr/sbin/unbound" uid=13 comm="squid" exe="/usr/src/squid-4-master/src/squid" uid=1 comm="debsecan" exe="/usr/bin/python2.7" 

包含空格的命令以简单的ascii-to-hex方法编码(请参阅audit_logging.c )。 要解码,请将“FF”replace为“&#xFF;” 并重新编码从html到ascii:

 grep SOCKET /var/log/audit/audit.log | \ cut -d' ' -f 4- | sed "s|^|@\n|g;s| |\n|g" | \ grep -E "^((exe|uid|comm)=|@)" | tr '\n@' ' \n' | \ sort -u | sed "s|^[^=]*=||g;s| [^ ]*=| |g" | \ while read UCE ; do \ echo "$C" | grep -q '"' || \ { C=\"`echo $C | sed "s|\(..\)|\&#x\1;|g" | recode h4..u8`\" ; } ; \ echo "uid=$U comm=$C exe=$E" ; done 

(解释: sed“s | ^ [^ =] = || g; s | [^] = | | g” – >编辑掉'xxx ='部分的行 – first:line-start(^)由any-char-except – '='replace为空格,然后空格后面加上any-char-except-''replace为空格)

(解释: 在读取UCE时; do … done – >在每一行上循环,将三位数据分别读入U,C,E(userid,command,executable)中)

(解释: echo“$ C”| grep -q'“'|| – >testing命令字段以查看它是否包含双引号 – 如果不是('||')则执行以下操作:)

(解释: {C = \“ echo $C | sed "s|\(..\)|\&#x\1;|g" | recode h4..ascii \”;} – >打印命令string,编辑每一对字符'FF'为'&#xFF;',然后通过gnu'recode'将它们从html实体转换为ascii字符。)

(解释: echo“uid = $ U comm = $ C exe = $ E” – >打印出修改后的行)

这给我输出(只显示解码的行):

 uid=1000 comm="Socket Thread" exe="/usr/lib/firefox/firefox 

/ j