监控网络使用情况,排除本地流量。

10

我正在开发一款监控网络使用情况的应用程序。然而,我发现很多方法都不能排除本地流量(比如Time Machine)。

我正在寻找一种排除本地流量的方法,只监视直接与互联网相连的使用情况。

更新:感谢您的回复,现在我知道如何查找流量是否为本地流量了,但我仍然不知道如何计算总的输入/输出字节数(如果我之前没有详细说明,对不起)。我无法知道在某个时间段内或自操作系统启动以来发送/接收了多少字节的本地流量(或互联网流量)。这个问题进一步被进程在操作系统运行时启动或关闭所复杂化。

回答如何在Linux/Mac OSX中获取网络适配器统计信息?的答案提供了一种有趣的方法来总结总使用量,但它并没有帮助,因为它总结的使用情况是接口统计信息。

更新2:我已经发布了我的最终解决方案,请向下滚动一点来查看。


查看除了lo接口或默认路由指向的接口之外的流量。不确定如何使用Mac OS X特定调用来实现这一点(否则我会将其发布为答案),但Unix工具可以做到... - derobert
排除每个本地接口的子网应该做得不错。getifaddrs(3)应该可以胜任;您可能想要的唯一的特定于Mac的内容是观察接口的上下线。 - Nicholas Riley
@derobert 谢谢,但是其他接口也应该有本地流量吧? - koo
我在回答你的评论时使用了一个答案,而不是另一个评论,因为我的答案太长了,不适合放在评论中。 - derobert
8个回答

2
回答你有关哪些接口承载本地流量的评论实际上很复杂,因为这取决于您对本地流量的理解。
“本地”的意思最简单的是指不离开生成它的计算机的流量(例如,在同一台计算机上运行的两个程序之间的通信)。所有这些流量都经过lo。这是人们在说“本地流量”时指的一件事情(也是我回答时所想到的)。
下一个最简单的含义是“目标地址在同一子网中的IP流量”。这将是具有本地子网内部目标地址的流量。计算这个最简单的方法将是使用路由表(如果Mac OS X按路由计算流量统计信息,则各个网关上的路由将提供非本地流量),或使用防火墙规则。这可能不是任何人口中的“本地流量”的含义。
另一种含义可能是“发送到此(物理)位置中的机器的IP流量”。例如,在我的办公室中,我们使用了几个子网,并在它们之间拥有路由器,但是从一个子网到另一个子网的流量仍然清晰可见。使用此定义需要网络知识来区分本地和非本地流量。
另一种含义可能是“发送到我的组织中的机器的IP流量”。这是一种合理的含义,取决于您的网络设置(例如,也许您在不同位置之间拥有快速光纤,但是您的Internet连接要慢得多,或按GB计费)。需要深入了解网络才能确定目标是否将是本地的,而且,使用VPN之类的东西,可能随时间而变化。 最后,“互联网流量”并不是任何这些含义的相反面。例如,有时候,看起来在您的以太网段上的本地机器实际上是通过VPN,通过Internet连接到达的(这并不疯狂,在远程用户需要使用各种Windows服务时非常有用)。组织内部的流量可以轻松通过Internet VPN传输。
如果网络非常简单,只有一个内部子网,一个路由器,所有不发送到该内部子网的流量都是Internet流量,则可以欺骗并解决此问题。这可能适用于绝大多数家庭网络,以及许多小型企业网络。
在简单的网络设置中,您可以做出一些假设,并通过计算以下情况来尽可能接近答案:
  • 目标MAC地址为默认网关的MAC地址;和
  • 目标IP地址不是默认网关的IP地址
或者:
  • 目标IP地址不在默认路由所在的网络接口的子网内
您可以创建一个防火墙规则来计算任何一个。至少对于Linux iptables,我很确定BSD pf以及可能是Mac OS X也是如此。
另一种方法:SNMP

最后,如果您无法使用防火墙规则(因为那需要root权限),您可以希望默认网关响应SNMP community public,探索其所有接口,并找到一个不在子网范围内的IP地址,然后假设它是互联网链接。然后,您可以向路由器请求该接口上的流量计数。

当然,您会发现许多SOHO路由器不支持SNMP,而那些支持SNMP的路由器可能没有打开它。


谢谢!那是一个非常好的答案。但我想我应该放弃了。我查看了来自BSD系统管理器手册的ipsw(IP防火墙和流量整形控制程序),但它不能用于提供有用的统计信息。许多现有的Linux解决方案也无法移植,因为它们利用了/proc/net。此外,SNMP也不是我真正寻找的,因为我的应用程序并不面向类似专业人士的用户。 - koo

2

您需要阅读ifconfig(8)的源代码,其中描述了如何获取每个已连接网络接口的状态。

特别注意in_status()函数,该函数获取接口的inet地址和netmask。

当流量中的源或目标地址与本地接口具有相同的主机时:

int is_local =
(src && netmask) == (ifaddr && netmask)
|| (dst && netmask) == (ifaddr && netmask)

然后您可以确定它是本地的。

http://www.opensource.apple.com/source/network_cmds/network_cmds-307/ifconfig.tproj/ifconfig.c


1
最好的方法是通过对ifconfig进行系统调用,找到eth0、eth1或其他适配器的“外部”IP地址。然后拉取任何系统(消息、syslog等)的日志,并为该外部IP地址编写过滤器。为了使其更加美观和便携,编写一个正则表达式,仅过滤公共可路由IP,并仅为该“外部”IP地址过滤消息日志。

0

0

有三个不可路由的IP地址范围,它们通常被用作NAT服务的地址范围。任何不在这些不可路由地址范围内的地址都是外部地址。

当然,如果您没有在NAT路由器后面,这项任务会更加困难(从技术上讲,在127.0.0.1以下的所有地址都是外部地址)。

不可路由的IP地址范围为:

10.0.0.0 - 10.255.255.255

172.16.0.0 - 172.31.255.255

192.168.0.0 - 192.168.255.255


这并不是真的;内部地址不一定要使用RFC1918空间。在网络上,有很多时候你会为机器使用非1918空间,例如服务器。同一以太网段上的一个服务器到另一个服务器的流量仍然是本地流量,绝对不是互联网流量。 - derobert

0

谢谢!我看了getifaddrs。但它不是我想要的东西。我需要一种分离本地和互联网流量统计数据的方法,而getifaddrs没有提供这个功能。更糟糕的是,AF_LINK家族的统计数据结构(->ifa_data->ifi_obytes和ifi_ibytes)是32位无符号整数,这意味着在统计数据超过约4 GB后会溢出。 - koo

0

不知道如何在 Objective-C 中实现它,但思路是获取所在网络的地址(可以根据本地 IP 或 netmask 中的位数从网络类别(A、B、C)推断出来),然后只需检查出站连接的地址即可。如果目标不在本地网络中,则计算流量;如果在本地网络中,则什么也不做。


1
网络类自1993年以来就没有被使用过。https://secure.wikimedia.org/wikipedia/en/wiki/Classless_Inter-Domain_Routing - Rolf Rander
但这并不意味着你不能使用它 =) - Viktor
如果您的网络掩码是255.255.255.128,那么它属于哪个网络类? - Rolf Rander
如果您阅读我的答案,它说“或者从子网掩码中的位(如果它不是标准的)”。 - Viktor
是的,但您不能仅通过查看IP地址来假设网络掩码,您必须检查配置的掩码。 - Rolf Rander
不幸的是,这并不能提供互联网流量。不同的子网并不意味着它会穿越互联网;相同的子网也不意味着它不会进入互联网。 - derobert

0

我最终的解决方案是使用 libpcap 来实现。当然,它有一些缺点,包括需要提升权限并捕获所有过滤的数据包以计算统计信息,但至少它能够完美地工作。

libpcap 的许多文档和教程都非常详尽清晰,我建议每个对此解决方案感兴趣的人都可以花费相对较少的时间进行搜索。

另外,可能会有一些人对我的互联网流量过滤器感兴趣,它只是以下内容 -

- (NSString *)_internetFilterStringForInterface:(AKNetworkInterface *)interface
    inOrOut:(BOOL)inYesOutNo
{
    if (![interface net] || ![interface mask] || IsEmpty([interface addresses]))
    {
        return nil;
    }

    NSString *hostType = inYesOutNo ? @"dst" : @"src";
    NSString *host = nil;
    for (NSString *hostComponent in [interface addresses])
    {
        if (IsEmpty(hostComponent)) continue;
        if (!host)
            host = [NSString stringWithFormat:@"(%@ host %@", hostType, hostComponent];
        else
            host = [host stringByAppendingFormat:@" or %@ host %@", hostType, hostComponent];
    }
    host = [host stringByAppendingString:@")"];

    NSString *net = [interface netString];
    net = [net stringByReplacingOccurrencesOfString:@".0" withString:@""];

    NSString *filter = [NSString stringWithFormat:
                        @"ip and (not %@ net %@) and %@",
                        inYesOutNo ? @"src" : @"dst",
                        net, host];
    return filter;
}

筛选器是根据一些关于什么算作“本地流量”的答案设计的,我知道它并不包括一些特殊情况,比如双重NAT配置等等,但我希望能得到关于这个的建议。

我知道net = [net stringByReplacingOccurrencesOfString:@".0" withString:@""];只是一个快速的破解方法,在某些特殊情况下很容易失败,但嘿,至少现在还没有人抱怨。


嗨,亚当,你如何告诉iOS在每次用户使用互联网时调用你的应用程序或代码? - Eduardo Iglesias
我开发的应用程序不是为iOS设计的。目前在iOS上无法实现这一点。 - koo
嗨,@Adam KO,我不知道怎么做,但可以检查数据管理和数据使用情况。无论如何,谢谢 :) - Eduardo Iglesias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接