如何运行 BASH 脚本来响应网络事件?

如何运行 BASH 脚本来响应网络事件?

我有一个小的 BASH 脚本,我想在几台 Mac 上运行(10.8.5 和 10.9.4 的混合版本)。每当我的 Mac 尝试通过特定的 TCP 端口连接到另一台 Mac 时,我都会触发该脚本。两台计算机的 IP 地址都是已知的,当我通过 nettop 或控制台观看时,我可以看到流量(端口 6472 上的连接)。

我一直没能找到——也许我搜索得不太正确——一个可以在后台运行并监视连接请求,然后在请求发生时运行可执行文件的工具。我想我可以想出一个,但这似乎是别人已经解决的问题。任何现有解决方案的方向都将受到热烈欢迎。

谢谢!Mike

答案1

如何在后台运行这样的程序,使用tcpdump使用正确的过滤器并在每次匹配时启动脚本?

tcpdump -l -i eth0 'dst host other_host and dst port 6472 and tcp[tcpflags] = tcp-syn' 2>/dev/null | while read f ; do your_script; done

eth0用你的网络接口替换other_host另一台机器的 IP 地址

如果你不想使用终端窗口,你可以将其制作成 bash 脚本并在屏幕会议。

答案2

另一种替代方案,可能比 @ultrasawblade 解决方案更简单的解决方案可能是运行被敲.——看来有一个适用于 Mac 的版本。

答案3

完成任务最灵活、最个性化的方法可能是仔细阅读dtrace

要么使用通用套接字或 tcp 提供程序,并以破坏性模式运行脚本以system()从内核调用,要么使用函数边界跟踪 (FBT) 作为提供程序。后者将使您的脚本依赖于 OSX 版本。

经过一番尝试,我想到了一个可以为你使用套接字提供程序的最终解决方案提供足够素材的东西(因此它应该可以在你的两台 MacOSX 机器上工作):

#!/usr/sbin/dtrace -s

#pragma D option quiet
#pragma D option switchrate=10hz
#pragma D option destructive 

/* AF_INET{6} are unknown to dtrace, so replace with numbers */
inline int af_inet  =  2; /* AF_INET  */
inline int af_inet6 = 30; /* AF_INET6 */
inline const string procname = "nc";

dtrace:::BEGIN
{
    /* Add translations as desired from /usr/include/sys/errno.h */
    err[0]            = "Success";
    err[EINTR]        = "Interrupted syscall";
    err[EIO]          = "I/O error";
    err[EACCES]       = "Permission denied";
    err[ENETDOWN]     = "Network is down";
    err[ENETUNREACH]  = "Network unreachable";
    err[ECONNRESET]   = "Connection reset";
    err[ECONNREFUSED] = "Connection refused";
    err[ETIMEDOUT]    = "Timed out";
    err[EHOSTDOWN]    = "Host down";
    err[EHOSTUNREACH] = "No route to host";
    err[EINPROGRESS]  = "In progress";
    err[EADDRNOTAVAIL] = "Can't assign requested address";

    printf("%-6s %-20s %-8s %-21s %-21s %-8s %s\n", 
        "PID", "PROCNAME", "FAMILY", "S_ADDR:S_PORT", "D_ADDR:D_PORT", 
        "LAT(us)", "RESULT");
}

/*  MacOSX connectx() syscall:

    connectx(arg0:int s, arg1:struct sockaddr *src, arg2:socklen_t srclen, 
                         arg3:struct sockaddr *dsts, arg4:socklen_t dstlen, 
                         arg5:uint32_t ifscope, arg6: associd_t aid, 
                         arg7:connid_t *cid);
*/

syscall::connectx:entry
{
    this->s = (struct sockaddr_in *) copyin(arg3, sizeof(struct sockaddr)); 
    this->f = this->s->sin_family;
} 

syscall::connectx:entry 
/ this->f == af_inet && execname == procname / 
{ 
    this->s = (struct sockaddr_in *) copyin(arg1, sizeof(struct sockaddr)); 
    self->address = inet_ntop(this->f, (void *) &this->s->sin_addr);
    self->port = ntohs(this->s->sin_port);
    self->s_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));

    this->d = (struct sockaddr_in *) copyin(arg3, sizeof(struct sockaddr)); 
    self->address = inet_ntop(this->f, (void *) &this->d->sin_addr);
    self->port = ntohs(this->d->sin_port);  
    self->d_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));

    self->ts = timestamp; 
}

syscall::connectx:entry
/ this->f == af_inet6 && execname == procname /
{
    this->s6 = (struct sockaddr_in6 *) copyin(arg1, sizeof(struct sockaddr_in6));
    self->port = ntohs(this->s6->sin6_port);
    self->address = inet_ntop(this->f, (void *) &this->s6->sin6_addr);
    self->s_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));

    this->d6 = (struct sockaddr_in6 *) copyin(arg3, sizeof(struct sockaddr_in6));
    self->port = ntohs(this->d6->sin6_port);
    self->address = inet_ntop(this->f, (void *) &this->d6->sin6_addr);
    self->d_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));

    self->ts = timestamp;
}

syscall::connectx:return 
/ self->ts / 
{ 
    this->delta = (timestamp - self->ts) / 1000; 
    this->errstr = err[errno] != NULL ? err[errno] : lltostr(errno);

    /* Basically anything can be called here */
    system("date");
    printf("%-6d %-20s %-8d %-21s %-21s %-8d %s\n", 
        pid, execname, this->f, self->s_addr, self->d_addr,
        this->delta, this->errstr); 

    self->family = 0; 
    self->ts = 0; 
}

将其保存到文件中(假设./socket_connect_mac_simple.d),然后调用如下代码:

$ sudo ./socket_connect_mac_simple.d

打开另一个终端并启动nc服务器:

$ nc -4 -k -l 127.0.0.1 6472 > /dev/null

并在另一个终端上连接它:

$ while :; do nc -4 -s 192.168.0.19 127.0.0.1 6472 < /usr/bin/true; sleep 0.5; done

您的输出应该类似于以下内容:

$ sudo ./socket_connect_mac_simple.d
PID    PROCNAME         FAMILY   S_ADDR:S_PORT         D_ADDR:D_PORT         LAT(us)  RESULT
45823  nc               2        192.168.0.19:0        127.0.0.1:6472        240      Success
45825  nc               2        192.168.0.19:0        127.0.0.1:6472        234      Success
45827  nc               2        192.168.0.19:0        127.0.0.1:6472        236      Success
45829  nc               2        192.168.0.19:0        127.0.0.1:6472        240      Success
45831  nc               2        192.168.0.19:0        127.0.0.1:6472        241      Success
45833  nc               2        192.168.0.19:0        127.0.0.1:6472        238      Success
45835  nc               2        192.168.0.19:0        127.0.0.1:6472        234      Success
45837  nc               2        192.168.0.19:0        127.0.0.1:6472        241      Success
45839  nc               2        192.168.0.19:0        127.0.0.1:6472        236      Success
45841  nc               2        192.168.0.19:0        127.0.0.1:6472        237      Success

显然,您将用端口 6472 上的守护进程列表进行替换nc,并且您还必须调用我放置system("date")代码片段的 bash 脚本。除此之外,它应该基本上按照您描述的方式工作。

确保您阅读了有关破坏性模式 dtrace 调用的详细文档system()DTrace 内部的破坏性模式调用

相关内容