简单的单进程的网络流量监控程序

前一篇博客说了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表示从监控开始的总的流量。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值