前一篇博客说了nethogs的一些问题,因为nethogs无法满足我的需求,所以自己写了一个,因为对libpcap库和网络协议了解有限,所以可能存在一些问题。目前该程序只支持指定PID的进程网络流量监控,支持TCP和UDP,输出的信息包括:1s的平均流量、10s的每秒平均流量、60s的每秒平均流量、从监控开始的总的流量。代码如下:
decpcap.c和decpcap.h是nethogs封装的接口,直接拿来用了。。。。
decpcap.c
#include "decpcap.h"
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <pcap.h>
#include <string.h> // for memcpy
#include <sys/types.h>
#define DP_DEBUG 0
bool catchall = false;
/* functions to set up a handle (which is basically just a pcap handle) */
struct dp_handle *dp_fillhandle(pcap_t *phandle) {
struct dp_handle *retval =
(struct dp_handle *)malloc(sizeof(struct dp_handle));
int i;
retval->pcap_handle = phandle;
for (i = 0; i < dp_n_packet_types; i++) {
retval->callback[i] = NULL;
}
retval->linktype = pcap_datalink(retval->pcap_handle);
switch (retval->linktype) {
case (DLT_EN10MB):
fprintf(stdout, "Ethernet link detected\n");
break;
case (DLT_PPP):
fprintf(stdout, "PPP link detected\n");
break;
case (DLT_LINUX_SLL):
fprintf(stdout, "Linux Cooked Socket link detected\n");
break;
default:
fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype);
// TODO maybe error? or 'other' callback?
break;
}
return retval;
}
struct dp_handle *dp_open_offline(char *fname, char *ebuf) {
pcap_t *temp = pcap_open_offline(fname, ebuf);
if (temp == NULL) {
return NULL;
}
return dp_fillhandle(temp);
}
struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc,
int to_ms, char *filter, char *errbuf) {
struct bpf_program fp; // compiled filter program
bpf_u_int32 maskp; // subnet mask
bpf_u_int32 netp; // interface IP
pcap_t *temp = pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
if (temp == NULL) {
return NULL;
}
if (filter != NULL) {
pcap_lookupnet(device, &netp, &maskp, errbuf);
/* Compile the filter */
if (pcap_compile(temp, &fp, filter, 1, netp) == -1) {
fprintf(stderr,
"Error calling pcap_compile for filter on device %s: %s\n",
device, pcap_geterr(temp));
return NULL;
}
/* set the filter */
if (pcap_setfilter(temp, &fp) == -1) {
fprintf(stderr, "Error setting capture filter on device %s: %s\n", device,
pcap_geterr(temp));
return NULL;
}
}
return dp_fillhandle(temp);
}
/* functions to add callbacks */
void dp_addcb(struct dp_handle *handle, enum dp_packet_type type,
dp_callback callback) {
handle->callback[type] = callback;
}
/* functions for parsing the payloads */
void dp_parse_tcp(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
// const struct tcphdr * tcp = (struct tcphdr *) packet;
// u_char * payload = (u_char *) packet + sizeof (struct tcphdr);
if (handle->callback[dp_packet_tcp] != NULL) {
int done =
(handle->callback[dp_packet_tcp])(handle->userdata, header, packet);
if (done)
return;
}
// TODO: maybe `pass on' payload to lower-level protocol parsing
}
void dp_parse_udp(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
if (handle->callback[dp_packet_udp] != NULL) {
int done =
(handle->callback[dp_packet_udp])(handle->userdata, header, packet);
if (done)
return;
}
// TODO: maybe `pass on' payload to lower-level protocol parsing
}
void dp_parse_ip(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
const struct ip *ip = (struct ip *)packet;
if (DP_DEBUG) {
fprintf(stdout, "Looking at packet with length %ud\n", header->len);
}
u_char *payload = (u_char *)packet + sizeof(struct ip);
if (handle->callback[dp_packet_ip] != NULL) {
int done =
(handle->callback[dp_packet_ip])(handle->userdata, header, packet);
if (done)
return;
}
switch (ip->ip_p) {
case IPPROTO_TCP:
dp_parse_tcp(handle, header, payload);
break;
case IPPROTO_UDP:
dp_parse_udp(handle, header, payload);
break;
default:
// TODO: maybe support for non-tcp IP packets
break;
}
}
void dp_parse_ip6(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
u_char *payload = (u_char *)packet + sizeof(struct ip6_hdr);
if (handle->callback[dp_packet_ip6] != NULL) {
int done =
(handle->callback[dp_packet_ip6])(handle->userdata, header, packet);
if (done)
return;
}
switch ((ip6->ip6_ctlun).ip6_un1.ip6_un1_nxt) {
case IPPROTO_TCP:
dp_parse_tcp(handle, header, payload);
break;
case IPPROTO_UDP:
if (catchall)
dp_parse_udp(handle, header, payload);
break;
default:
// TODO: maybe support for non-tcp ipv6 packets
break;
}
}
void dp_parse_ethernet(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
const struct ether_header *ethernet = (struct ether_header *)packet;
u_char *payload = (u_char *)packet + sizeof(struct ether_header);
u_int16_t protocol = 0;
/* call handle if it exists */
if (handle->callback[dp_packet_ethernet] != NULL) {
int done = (handle->callback[dp_packet_ethernet])(handle->userdata, header,
packet);
/* return if handle decides we're done */
if (done)
return;
}
/* parse payload */
protocol = ntohs(ethernet->ether_type);
switch (protocol) {
case ETHERTYPE_IP:
dp_parse_ip(handle, header, payload);
break;
case ETHERTYPE_IPV6:
dp_parse_ip6(handle, header, payload);
break;
default:
// TODO: maybe support for other protocols apart from IPv4 and IPv6
break;
}
}
/* ppp header, i hope ;) */
/* glanced from ethereal, it's 16 bytes, and the payload packet type is
* in the last 2 bytes... */
struct ppp_header {
u_int16_t dummy1;
u_int16_t dummy2;
u_int16_t dummy3;
u_int16_t dummy4;
u_int16_t dummy5;
u_int16_t dummy6;
u_int16_t dummy7;
u_int16_t packettype;
};
void dp_parse_ppp(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
const struct ppp_header *ppp = (struct ppp_header *)packet;
u_char *payload = (u_char *)packet + sizeof(struct ppp_header);
u_int16_t protocol = 0;
/* call handle if it exists */
if (handle->callback[dp_packet_ppp] != NULL) {
int done =
(handle->callback[dp_packet_ppp])(handle->userdata, header, packet);
/* return if handle decides we're done */
if (done)
return;
}
/* parse payload */
protocol = ntohs(ppp->packettype);
switch (protocol) {
case ETHERTYPE_IP:
dp_parse_ip(handle, header, payload);
break;
case ETHERTYPE_IPV6:
dp_parse_ip6(handle, header, payload);
break;
default:
// TODO: support for other than IPv4 and IPv6
break;
}
}
/* linux cooked header, i hope ;) */
/* glanced from libpcap/ssl.h */
#define SLL_ADDRLEN 8 /* length of address field */
struct sll_header {
u_int16_t sll_pkttype; /* packet type */
u_int16_t sll_hatype; /* link-layer address type */
u_int16_t sll_halen; /* link-layer address length */
u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */
u_int16_t sll_protocol; /* protocol */
};
void dp_parse_linux_cooked(struct dp_handle *handle, const dp_header *header,
const u_char *packet) {
const struct sll_header *sll = (struct sll_header *)packet;
u_char *payload = (u_char *)packet + sizeof(struct sll_header);
u_int16_t protocol = 0;
/* call handle if it exists */
if (handle->callback[dp_packet_sll] != NULL) {
int done =
(handle->callback[dp_packet_sll])(handle->userdata, header, packet);
/* return if handle decides we're done */
if (done)
return;
}
/* parse payload */
protocol = ntohs(sll->sll_protocol);
switch (protocol) {
case ETHERTYPE_IP:
dp_parse_ip(handle, header, payload);
break;
case ETHERTYPE_IPV6:
dp_parse_ip6(handle, header, payload);
break;
default:
// TODO: support for other than IPv4 / IPv6
break;
}
}
/* functions to do the monitoring */
void dp_pcap_callback(u_char *u_handle, const struct pcap_pkthdr *header,
const u_char *packet) {
struct dp_handle *handle = (struct dp_handle *)u_handle;
struct dp_header;
/* make a copy of the userdata for every packet */
u_char *userdata_copy = (u_char *)malloc(handle->userdata_size);
memcpy(userdata_copy, handle->userdata, handle->userdata_size);
switch (handle->linktype) {
case (DLT_EN10MB):
dp_parse_ethernet(handle, header, packet);
break;
case (DLT_PPP):
dp_parse_ppp(handle, header, packet);
break;
case (DLT_LINUX_SLL):
dp_parse_linux_cooked(handle, header, packet);
break;
case (DLT_RAW):
case (DLT_NULL):
// hope for the best
dp_parse_ip(handle, header, packet);
break;
default:
fprintf(stdout, "Unknown linktype %d", handle->linktype);
break;
}
free(userdata_copy);
}
int dp_dispatch(struct dp_handle *handle, int count, u_char *user, int size) {
handle->userdata = user;
handle->userdata_size = size;
return pcap_dispatch(handle->pcap_handle, count, dp_pcap_callback,
(u_char *)handle);
}
int dp_setnonblock(struct dp_handle *handle, int i, char *errbuf) {
return pcap_setnonblock(handle->pcap_handle, i, errbuf);
}
char *dp_geterr(struct dp_handle *handle) {
return pcap_geterr(handle->pcap_handle);
}
decpcap.h
#ifndef __DECPCAP_H
#define __DECPCAP_H
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#define DP_ERRBUF_SIZE PCAP_ERRBUF_SIZE
/* definitions */
enum dp_packet_type {
dp_packet_ethernet,
dp_packet_ppp,
dp_packet_sll,
dp_packet_ip,
dp_packet_ip6,
dp_packet_tcp,
dp_packet_udp,
dp_n_packet_types
};
/*enum dp_link_type {
dp_link_ethernet,
dp_link_ppp,
dp_n_link_types
};*/
/*struct dp_header {
* pcap
};*/
typedef struct pcap_pkthdr dp_header;
typedef int (*dp_callback)(u_char *, const dp_header *, const u_char *);
struct dp_handle {
pcap_t *pcap_handle;
dp_callback callback[dp_n_packet_types];
int linktype;
u_char *userdata;
int userdata_size;
};
/* functions to set up a handle (which is basically just a pcap handle) */
struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc,
int to_ms, char *filter, char *errbuf);
struct dp_handle *dp_open_offline(char *fname, char *ebuf);
/* functions to add callbacks */
void dp_addcb(struct dp_handle *handle, enum dp_packet_type type,
dp_callback callback);
/* functions to parse payloads */
void dp_parse(enum dp_packet_type type, void *packet);
/* functions to start monitoring */
int dp_dispatch(struct dp_handle *handler, int count, u_char *user, int size);
/* functions that simply call libpcap */
int dp_datalink(struct dp_handle *handle);
int dp_setnonblock(struct dp_handle *handle, int i, char *errbuf);
char *dp_geterr(struct dp_handle *handle);
#endif
proc_net_info.cpp和proc_net_info.h是自己封装的进程相关的信息
#include "proc_net_info.h"
#include <dirent.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <sys/time.h>
#include <net/if.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
#include "debug.h"
// The interface is up, not a loopback and running?
bool up_running(int ifa_flags) {
return !(ifa_flags & IFF_LOOPBACK) && (ifa_flags & IFF_UP) &&
(ifa_flags & IFF_RUNNING);
}
bool is_number(const char *string)
{
while (*string)
{
if (!isdigit(*string))
return false;
string++;
}
return true;
}
unsigned long str2ulong(const char *ptr)
{
unsigned long retval = 0;
while ((*ptr >= '0') && (*ptr <= '9'))
{
retval *= 10;
retval += *ptr - '0';
ptr++;
}
return retval;
}
CProcNetInfo::CProcNetInfo(pid_t pid, const char *dev_name)
: pid_(pid),
devname_(dev_name),
localAddr_(getLocalAddr())
{
inodes_ = getSocketInodes();
getTCPInfo();
getUDPInfo();
}
std::vector<unsigned long> CProcNetInfo::getSocketInodes()
{
char dirname[64];
snprintf(dirname, sizeof(dirname), "/proc/%d/fd", pid_);
DIR *dir = opendir(dirname);
if (dir == NULL)
{
ERR_EXIT("proc %d does not exist!\n", pid_);
}
//查找套接字描述符
dirent *entry;
std::vector<unsigned long> socket_inodes;
while ((entry = readdir(dir)))
{
if (entry->d_type != DT_LNK)
continue;
char fromname[512];
snprintf(fromname, sizeof(fromname), "%s/%s", dirname, entry->d_name);
int linklen = 64;
char linkname[linklen];
int usedlen = readlink(fromname, linkname, linklen - 1);
assert(usedlen < linklen);
linkname[usedlen] = '\0';
if (strncmp(linkname, "socket:[", 8) == 0)
{
socket_inodes.push_back(str2ulong(linkname + 8));
}
}
closedir(dir);
return socket_inodes;
}
void CProcNetInfo::getTCPInfo()
{
FILE *tcp_info = fopen("/proc/net/tcp", "r");
if (tcp_info == NULL)
{
ERR_EXIT("fopen /proc/net/tcp fail");
}
char buffer[8192];
fgets(buffer, sizeof(buffer), tcp_info);
while (fgets(buffer, sizeof(buffer), tcp_info) != NULL)
{
char rem_addr[128], local_addr[128];
int local_port, rem_port;
unsigned long inode;
int state;
int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X "
"%*X:%*X %*X:%*X %*X %*d %*d %lu %*512s\n",
local_addr, &local_port, rem_addr, &rem_port, &state, &inode);
if (matches != 6) {
ERR_EXIT("Unexpected buffer: '%s'\n", buffer);
}
if (find(inodes_.begin(), inodes_.end(), inode) != inodes_.end())
{
// TRACE("inode[%lu] state[%d] %s:%d--%s:%d", inode, state, local_addr, local_port, rem_addr, rem_port);
tcpPort_.insert(local_port);
}
}
fclose(tcp_info);
}
void CProcNetInfo::getUDPInfo()
{
FILE *tcp_info = fopen("/proc/net/udp", "r");
if (tcp_info == NULL)
{
ERR_EXIT("fopen /proc/net/udp fail");
}
char buffer[8192];
fgets(buffer, sizeof(buffer), tcp_info);
while (fgets(buffer, sizeof(buffer), tcp_info) != NULL)
{
char rem_addr[128], local_addr[128];
int local_port, rem_port;
unsigned long inode;
int state;
int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X "
"%*X:%*X %*X:%*X %*X %*d %*d %lu %*512s\n",
local_addr, &local_port, rem_addr, &rem_port, &state, &inode);
if (matches != 6) {
ERR_EXIT("Unexpected buffer: '%s'\n", buffer);
}
if (find(inodes_.begin(), inodes_.end(), inode) != inodes_.end())
{
// TRACE("inode[%lu] state[%d] %s:%d--%s:%d", inode, state, local_addr, local_port, rem_addr, rem_port);
udpPort_.insert(local_port);
}
}
fclose(tcp_info);
}
LocalAddr CProcNetInfo::getLocalAddr()
{
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs(&ifaddr) == -1)
{
ERR_EXIT("getifaddrs fail");
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
if (devname_ == ifa->ifa_name)
{
if (!up_running(ifa->ifa_flags))
{
ERR_EXIT("%s is not running\n", ifa->ifa_name);
}
int family = ifa->ifa_addr->sa_family;
if (family == AF_INET)
{
struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
struct sockaddr_in *broad_addr = (struct sockaddr_in *)ifa->ifa_broadaddr;
// TRACE("Adding local address: %s", inet_ntoa(addr->sin_addr));
return LocalAddr(addr->sin_addr.s_addr, broad_addr->sin_addr.s_addr);
}
else
{
//不处理IPv6
}
}
}
freeifaddrs(ifaddr);
ERR_EXIT("dev %s not found\n", devname_.c_str());
}
void CProcNetInfo::show()
{
printf("pid[%d] devname[%s] ip[%s] addr[0x%X] ",
pid_, devname_.c_str(), localAddr_.ip_str.c_str(), localAddr_.addr);
printf("tcp port:[");
for (std::set<int>::const_iterator citer = tcpPort_.begin();
citer != tcpPort_.end(); ++citer)
{
printf("%d ", *citer);
}
printf( "] udp port[");
for (std::set<int>::const_iterator citer = udpPort_.begin();
citer != udpPort_.end(); ++citer)
{
printf("%d ", *citer);
}
printf("]\n");
}
bool CProcNetInfo::findTCPPort(int port)
{
if (tcpPort_.find(port) != tcpPort_.end())
return true;
return false;
}
bool CProcNetInfo::findUDPPort(int port)
{
if (udpPort_.find(port) != udpPort_.end())
return true;
return false;
}
void CProcNetInfo::refreshPort()
{
std::vector<unsigned long> inodes = getSocketInodes();
if (inodes == inodes_)
return;
else
inodes_ = inodes;
getTCPInfo();
getUDPInfo();
}
#ifndef __PROC_NET_INFO__
#define __PROC_NET_INFO__
#include <arpa/inet.h>
#include <string>
#include <vector>
#include <set>
#include <map>
struct LocalAddr
{
public:
/* ipv4 constructor takes an in_addr_t */
LocalAddr(in_addr_t m_addr, in_addr_t m_broadcast_addr)
: addr(m_addr), broadcast_addr(m_broadcast_addr), sa_family(AF_INET)
{
char buf[64];
inet_ntop(AF_INET, &m_addr, buf, sizeof(buf));
ip_str = buf;
inet_ntop(AF_INET, &broadcast_addr, buf, sizeof(buf));
broadcast_ip_str = buf;
}
std::string ip_str;
std::string broadcast_ip_str;
in_addr_t addr;
in_addr_t broadcast_addr;
short int sa_family;
};
class CProcNetInfo
{
public:
CProcNetInfo(pid_t pid, const char *dev_name);
public:
void show();
std::string getIPStr() { return localAddr_.ip_str; }
in_addr_t getIPAddr() { return localAddr_.addr; }
std::string getBroadcastIPStr() { return localAddr_.broadcast_ip_str; }
in_addr_t getBroadcastIPAddr() { return localAddr_.broadcast_addr; }
std::string getDevname() { return devname_; }
bool findTCPPort(int port);
bool findUDPPort(int port);
void refreshPort();
private:
std::vector<unsigned long> getSocketInodes();
void getTCPInfo();
void getUDPInfo();
LocalAddr getLocalAddr();
private:
// std::string cmdline_;
pid_t pid_;
const std::string devname_;
const LocalAddr localAddr_;
std::vector<unsigned long> inodes_;
std::set<int> tcpPort_; //需要监视的tcp端口号
std::set<int> udpPort_; //需要监视的udp端口号
};
#endif
main.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <assert.h>
#include <ifaddrs.h>
#include <sys/time.h>
#include <net/if.h>
#include <sys/socket.h>
#include <linux/capability.h>
#include <linux/limits.h>
#include <sys/xattr.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <algorithm>
#include "decpcap.h"
#include "proc_net_info.h"
#include "debug.h"
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
bool g_debug_flag = false;
int64_t getMicroSecondsSinceEpoch()
{
struct timeval tv;
gettimeofday(&tv, NULL);
int64_t seconds = tv.tv_sec;
return seconds * 1000000 + tv.tv_usec;
}
double timeDifference(int64_t t1, int64_t t2)
{
int64_t diff = t1 - t2;
return (double)diff / 1000000;
}
std::string toFormattedString(int64_t t1, bool showMicroseconds)
{
char buf[64] = {0};
time_t seconds = static_cast<time_t>(t1 / 1000000);
struct tm tm_time;
localtime_r(&seconds, &tm_time);
if (showMicroseconds)
{
int microseconds = static_cast<int>(t1 % 1000000);
snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
microseconds);
}
else
{
snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
}
return buf;
}
std::string format(uint64_t s)
{
double n = static_cast<double>(s);
char buf[64];
const double Ki = 1024.0;
const double Mi = Ki * 1024.0;
const double Gi = Mi * 1024.0;
const double Ti = Gi * 1024.0;
const double Pi = Ti * 1024.0;
const double Ei = Pi * 1024.0;
if (n < Ki)
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)s);
else if (n < Ki*9.995)
snprintf(buf, sizeof buf, "%.2fKB", n / Ki);
else if (n < Ki*99.95)
snprintf(buf, sizeof buf, "%.1fKB", n / Ki);
else if (n < Ki*1023.5)
snprintf(buf, sizeof buf, "%.0fKB", n / Ki);
else if (n < Mi*9.995)
snprintf(buf, sizeof buf, "%.2fMB", n / Mi);
else if (n < Mi*99.95)
snprintf(buf, sizeof buf, "%.1fMB", n / Mi);
else if (n < Mi*1023.5)
snprintf(buf, sizeof buf, "%.0fMB", n / Mi);
else if (n < Gi*9.995)
snprintf(buf, sizeof buf, "%.2fGB", n / Gi);
else if (n < Gi*99.95)
snprintf(buf, sizeof buf, "%.1fGB", n / Gi);
else if (n < Gi*1023.5)
snprintf(buf, sizeof buf, "%.0fGB", n / Gi);
else if (n < Ti*9.995)
snprintf(buf, sizeof buf, "%.2fTB", n / Ti);
else if (n < Ti*99.95)
snprintf(buf, sizeof buf, "%.1fTB", n / Ti);
else if (n < Ti*1023.5)
snprintf(buf, sizeof buf, "%.0fTB", n / Ti);
else if (n < Pi*9.995)
snprintf(buf, sizeof buf, "%.2fPB", n / Pi);
else if (n < Pi*99.95)
snprintf(buf, sizeof buf, "%.1fPB", n / Pi);
else if (n < Pi*1023.5)
snprintf(buf, sizeof buf, "%.0fPB", n / Pi);
else if (n < Ei*9.995)
snprintf(buf, sizeof buf, "%.2fEB", n / Ei );
else
snprintf(buf, sizeof buf, "%.1fEB", n / Ei );
return buf;
}
struct DataSum
{
std::map<int64_t, uint64_t> preSumsSent; //统计的各个时间段的发字节数,只记录60s
std::map<int64_t, uint64_t> preSumsRecv; //统计的各个时间段的收字节数,只记录60s
uint64_t sumSent; //本次统计的发字节
uint64_t sumRecv; //本次统计的收字节
uint64_t b1sSent; //1s平均发字节数
uint64_t b1sRecv; //1s平均收字节数
uint64_t b10sSent; //10s发总字节数
uint64_t b10sRecv; //10s收总字节数
uint64_t b10sPerSent; //10s发的平均每秒字节数
uint64_t b10sPerRecv; //10s收的平均每秒字节数
uint64_t b60sSent; //60s发总字节数
uint64_t b60sRecv; //60s收总字节数
uint64_t b60sPerSent; //60s发的平均每秒字节数
uint64_t b60sPerRecv; //60s收的平均每秒字节数
uint64_t allSent; //从开始的发总字节数
uint64_t allRecv; //从开始的收总字节数
DataSum()
: sumSent(0), sumRecv(0), b1sSent(0), b1sRecv(0), b10sSent(0), b10sRecv(0),
b10sPerSent(0), b10sPerRecv(0), b60sSent(0), b60sRecv(0), b60sPerSent(0),
b60sPerRecv(0), allSent(0), allRecv(0) { }
//start_time为本次记录开始的时间
void do_sum(int64_t start_time)
{
//总的收发
allSent += sumSent;
allRecv += sumRecv;
preSumsSent[start_time] = sumSent;
preSumsRecv[start_time] = sumRecv;
int64_t now = getMicroSecondsSinceEpoch();
//1s收发
b1sSent = sumSent / timeDifference(now, start_time);
b1sRecv = sumRecv / timeDifference(now, start_time);
sumSent = 0;
sumRecv = 0;
//10s发
std::map<int64_t, uint64_t>::iterator end;
end = preSumsSent.upper_bound(start_time - 10 * 1000000);
b10sSent = 0;
for (std::map<int64_t, uint64_t>::const_iterator citer = end;
citer != preSumsSent.end(); ++citer)
{
b10sSent += citer->second;
}
b10sPerSent = b10sSent / timeDifference(now, end->first);
//10s收
end = preSumsRecv.upper_bound(start_time - 10 * 1000000);
b10sRecv = 0;
for (std::map<int64_t, uint64_t>::const_iterator citer = end;
citer != preSumsRecv.end(); ++citer)
{
b10sRecv += citer->second;
}
b10sPerRecv = b10sRecv / timeDifference(now, end->first);
//60s发
end = preSumsSent.upper_bound(start_time - 60 * 1000000);
b60sSent = 0;
for (std::map<int64_t, uint64_t>::const_iterator citer = end;
citer != preSumsSent.end(); ++citer)
{
b60sSent += citer->second;
}
b60sPerSent = b60sSent / timeDifference(now, end->first);
//只记录60s
preSumsSent.erase(preSumsSent.begin(), end);
//60s收
end = preSumsRecv.upper_bound(start_time - 60 * 1000000);
b60sRecv = 0;
for (std::map<int64_t, uint64_t>::const_iterator citer = end;
citer != preSumsRecv.end(); ++citer)
{
b60sRecv += citer->second;
}
b60sPerRecv = b60sRecv / timeDifference(now, end->first);
//只记录60s
preSumsRecv.erase(preSumsRecv.begin(), end);
}
};
DataSum g_tcpSum;
DataSum g_udpSum;
CProcNetInfo *g_proc_net_info;
bool is_root()
{
if (geteuid() != 0) {
char exe_path[PATH_MAX];
ssize_t len;
unsigned int caps[5] = {0, 0, 0, 0, 0};
if ((len = readlink("/proc/self/exe", exe_path, PATH_MAX)) == -1)
ERR_EXIT("Failed to locate nethogs binary.");
exe_path[len] = '\0';
getxattr(exe_path, "security.capability", (char *)caps, sizeof(caps));
if ((((caps[1] >> CAP_NET_ADMIN) & 1) != 1) ||
(((caps[1] >> CAP_NET_RAW) & 1) != 1))
return false;
}
return true;
}
bool is_multicast_addr(uint32_t addr)
{
return addr > 0xE00000FF && addr <= 0xEFFFFFFF;
}
struct dpargs
{
const char *device;
int sa_family;
in_addr ip_src;
in_addr ip_dst;
in6_addr ip6_src;
in6_addr ip6_dst;
};
int process_ip(u_char *userdata, const dp_header * /* header */,
const u_char *m_packet) {
struct dpargs *args = (struct dpargs *)userdata;
struct ip *ip = (struct ip *)m_packet;
args->sa_family = AF_INET;
args->ip_src = ip->ip_src;
args->ip_dst = ip->ip_dst;
/* we're not done yet - also parse tcp :) */
return false;
}
int process_tcp(u_char *userdata, const dp_header *header,
const u_char *m_packet) {
struct dpargs *args = (struct dpargs *)userdata;
struct tcphdr *tcp = (struct tcphdr *)m_packet;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
/* get info from userdata, then call getPacket */
switch (args->sa_family) {
case AF_INET:
#if defined(__APPLE__) || defined(__FreeBSD__)
if (g_proc_net_info->findTCPPort(ntohs(tcp->th_sport)) &&
g_proc_net_info->getIPAddr() == args->ip_src.s_addr)
{
g_tcpSum.sumSent += header->len;
if (g_debug_flag)
{
TRACE("__FreeBSD__ src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(tcp->th_sport),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(tcp->th_dport), header->len);
}
}
else if (g_proc_net_info->findTCPPort(ntohs(tcp->th_dport)) &&
g_proc_net_info->getIPAddr() == args->ip_dst.s_addr)
{
g_tcpSum.sumRecv += header->len;
if (g_debug_flag)
{
TRACE("__FreeBSD__ src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(tcp->th_sport),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(tcp->th_dport), header->len);
}
}
#else
if (g_proc_net_info->findTCPPort(ntohs(tcp->source)) &&
g_proc_net_info->getIPAddr() == args->ip_src.s_addr)
{
g_tcpSum.sumSent += header->len;
if (g_debug_flag)
{
TRACE("ELSE src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(tcp->source),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(tcp->dest), header->len);
}
}
else if (g_proc_net_info->findTCPPort(ntohs(tcp->dest)) &&
g_proc_net_info->getIPAddr() == args->ip_dst.s_addr)
{
g_tcpSum.sumRecv += header->len;
if (g_debug_flag)
{
TRACE("ELSE src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(tcp->source),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(tcp->dest), header->len);
}
}
#endif
break;
case AF_INET6:
break;
default:
printf("Invalid address family for TCP packet: %d\n", args->sa_family);
break;
}
/* we're done now. */
return true;
}
int process_udp(u_char *userdata, const dp_header *header,
const u_char *m_packet) {
struct dpargs *args = (struct dpargs *)userdata;
struct udphdr *udp = (struct udphdr *)m_packet;
(void)header;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
switch (args->sa_family) {
case AF_INET:
#if defined(__APPLE__) || defined(__FreeBSD__)
if (g_proc_net_info->findUDPPort(ntohs(udp->uh_sport)) &&
(g_proc_net_info->getIPAddr() == args->ip_src.s_addr ||
g_proc_net_info->getBroadcastIPAddr() == args->ip_src.s_addr ||
is_multicast_addr(args->ip_src.s_addr)))
{
uint16_t len = htons(udp->len) - sizeof(struct udphdr);
g_udpSum.sumSent += len;
if (g_debug_flag)
{
TRACE("__FreeBSD__ src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(udp->uh_sport),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(udp->uh_dport), len);
}
}
if (g_proc_net_info->findUDPPort(ntohs(udp->uh_dport)) &&
(g_proc_net_info->getIPAddr() == args->ip_dst.s_addr ||
g_proc_net_info->getBroadcastIPAddr() == args->ip_dst.s_addr ||
is_multicast_addr(args->ip_dst.s_addr)))
{
uint16_t len = htons(udp->len) - sizeof(struct udphdr);
g_udpSum.sumRecv += len;
if (g_debug_flag)
{
TRACE("__FreeBSD__ src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(udp->uh_sport),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(udp->uh_dport), len);
}
}
#else
if (g_proc_net_info->findUDPPort(ntohs(udp->source)) &&
(g_proc_net_info->getIPAddr() == args->ip_src.s_addr ||
g_proc_net_info->getBroadcastIPAddr() == args->ip_src.s_addr ||
is_multicast_addr(args->ip_src.s_addr)))
{
uint16_t len = htons(udp->len) - sizeof(struct udphdr);
g_udpSum.sumSent += len;
if (g_debug_flag)
{
TRACE("ELSE src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(udp->source),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(udp->dest), len);
}
}
if (g_proc_net_info->findUDPPort(ntohs(udp->dest)) &&
(g_proc_net_info->getIPAddr() == args->ip_dst.s_addr ||
g_proc_net_info->getBroadcastIPAddr() == args->ip_dst.s_addr ||
is_multicast_addr(args->ip_dst.s_addr)))
{
uint16_t len = htons(udp->len) - sizeof(struct udphdr);
g_udpSum.sumRecv += len;
if (g_debug_flag)
{
TRACE("ELSE src[%s:%d] --> dst[%s:%d] len[%u]",
inet_ntop(AF_INET, &args->ip_src, buf1, sizeof(buf1)), ntohs(udp->source),
inet_ntop(AF_INET, &args->ip_dst, buf2, sizeof(buf2)), ntohs(udp->dest), len);
}
}
#endif
break;
case AF_INET6:
//不处理ipv6
break;
default:
printf("Invalid address family for UDP packet: %d\n", args->sa_family);
break;
}
return true;
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("%s pid device [--debug]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc > 3 && strcmp(argv[3], "--debug") == 0)
g_debug_flag = true;
int pid = atoi(argv[1]);
char *dev_name = argv[2];
g_proc_net_info = new CProcNetInfo(pid, dev_name);
g_proc_net_info->show();
if (!is_root())
{
ERR_EXIT("Root is required to run the program.");
}
int promisc = 0;
char *filter = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
dp_handle *newhandle =
dp_open_live(argv[2], BUFSIZ, promisc, 1000, filter, errbuf);
if (newhandle == NULL)
{
printf("dp_open_live fail\n");
exit(EXIT_FAILURE);
}
dp_addcb(newhandle, dp_packet_ip, process_ip);
// dp_addcb(newhandle, dp_packet_ip6, process_ip6);
dp_addcb(newhandle, dp_packet_tcp, process_tcp);
dp_addcb(newhandle, dp_packet_udp, process_udp);
if (dp_setnonblock(newhandle, 1, errbuf) == -1)
{
printf("dp_setnonblock fail\n");
}
// int const fd = pcap_get_selectable_fd(newhandle->pcap_handle);
// if (fd < 0)
// {
// DEBUG("pcap_get_selectable_fd fail\n");
// }
struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs));
int64_t pre_time = getMicroSecondsSinceEpoch();
while (1)
{
userdata->device = dev_name;
userdata->sa_family = AF_UNSPEC;
int retval = dp_dispatch(newhandle, -1, (u_char *)userdata,
sizeof(struct dpargs));
if (retval == -1)
DEBUG("dp_dispatch fail for device[%s] [%s]\n", dev_name, dp_geterr(newhandle));
else if (retval < 0)
DEBUG("dp_dispatch fail for device[%s] [%d]\n", dev_name, retval);
int64_t now_time = getMicroSecondsSinceEpoch();
if (timeDifference(now_time, pre_time) > 1.0)
{
printf("\ntime: %s\n", toFormattedString(pre_time, false).c_str());
g_tcpSum.do_sum(pre_time);
printf(" %-15s %-15s %-15s %-15s\n", "1s", "10s", "60s", "total");
printf(" TCP TX : %-15s %-15s %-15s %-15s\n",
format(g_tcpSum.b1sSent).c_str(), format(g_tcpSum.b10sPerSent).c_str(),
format(g_tcpSum.b60sPerSent).c_str(), format(g_tcpSum.allSent).c_str());
printf(" TCP RX : %-15s %-15s %-15s %-15s\n",
format(g_tcpSum.b1sRecv).c_str(), format(g_tcpSum.b10sPerRecv).c_str(),
format(g_tcpSum.b60sPerRecv).c_str(), format(g_tcpSum.allRecv).c_str());
g_udpSum.do_sum(pre_time);
printf(" UDP TX : %-15s %-15s %-15s %-15s\n",
format(g_udpSum.b1sSent).c_str(), format(g_udpSum.b10sPerSent).c_str(),
format(g_udpSum.b60sPerSent).c_str(), format(g_udpSum.allSent).c_str());
printf(" UDP RX : %-15s %-15s %-15s %-15s\n",
format(g_udpSum.b1sRecv).c_str(), format(g_udpSum.b10sPerRecv).c_str(),
format(g_udpSum.b60sPerRecv).c_str(), format(g_udpSum.allRecv).c_str());
// printf("tcp:sent map size=%zu recv map size=%zu\n", g_tcpSum.preSumsSent.size(), g_tcpSum.preSumsRecv.size());
// printf("udp:sent map size=%zu recv map size=%zu\n", g_udpSum.preSumsSent.size(), g_udpSum.preSumsRecv.size());
pre_time = now_time;
g_proc_net_info->refreshPort();
}
}
}
debug.h
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <stdio.h>
#include <stdlib.h>
#define ERR_EXIT(format, ...) \
do { \
fprintf(stderr, "[" __FILE__ "][%s:%d] " format "\n", \
__FUNCTION__ , __LINE__, ##__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
#define ERROR(format, ...) \
do { \
fprintf(stderr, "[" __FILE__ "][%s:%d] " format "\n", \
__FUNCTION__ , __LINE__, ##__VA_ARGS__); \
} while (0)
#define DEBUG(format, ...) \
do { \
fprintf(stdout, "[" __FILE__ "][%s:%d] " format "\n", \
__FUNCTION__ , __LINE__, ##__VA_ARGS__); \
} while (0)
#define __TRACE__ 1
#if __TRACE__
#define TRACE(format, ...) \
do { \
fprintf(stdout, "[" __FILE__ "][%s:%d] " format "\n", \
__FUNCTION__ , __LINE__, ##__VA_ARGS__); \
} while (0)
#else
#define TRACE(format, ...)
#endif
#endif
Makfile
CC = g++
CCFLAG = -g -Wall -Wextra -Werror
.PHONY : all
all:
$(CC) $(CCFLAG) main.cpp decpcap.c proc_net_info.cpp -o nettool -lpcap
使用方法和打印信息如下:
linux0@ubuntu:~/test/nettool$ sudo ./nettool 5544 ens33
pid[5544] devname[ens33] ip[192.168.232.130] addr[0x82E8A8C0] tcp port:[9876 ] udp port[]
Ethernet link detected
time: 20201205 15:04:42
1s 10s 60s total
TCP TX : 0 0 0 0
TCP RX : 0 0 0 0
UDP TX : 0 0 0 0
UDP RX : 0 0 0 0
time: 20201205 15:04:43
1s 10s 60s total
TCP TX : 0 0 0 0
TCP RX : 0 0 0 0
UDP TX : 0 0 0 0
UDP RX : 0 0 0 0
命令:nettool + 进程ID + 网卡名
输出:1s、10s、60s表示的都是平均每秒的平均流量,total表示从监控开始的总的流量。