libpcap on MacOS

The pcap library is one of the most famous and used network packet sniffing tool around the world. libpcap is the underlying library running under the famous tcpdump program which is a powerful network diagnosis tool.

What is pcap

pcap stands for: Packet Capture. The library which contains the pcap module, is called libpcap and Linux-based distros ship with it by default. If you’re on a Mac (like me), installing this library is easy-peasy thanks to the amazing (and almighty) Homebrew.

This library is the powerful back end for the powerful tool tcpdump so you can really get an idea of how powerful it is. The library is designed to work on C and C++, and there are ports for Java, Python and other languages.

How to install pcap

To install pcap, we need to have Homebrew installed in our system, you can check if you have Homebrew by running:

$ brew --version

Which should show you something like:

1
2
3
Homebrew 2.0.0
Homebrew/homebrew-core (git revision a968a; last commit 2019-02-05)
Homebrew/homebrew-cask (git revision f3d7c; last commit 2019-02-05)

In case you do not have installed Homebrew yet, go to Homebrew’s homepage and follow the instructions.

If you got brew, then the next step is simple as in:

$ brew install libpcap

After that, you should be able to use pcap in your C/C++ programs. Hooray!

Linker and Includes

In order to get the library into your code, you need to specify the object files and the directories from which the compiler will grab the headers. Usually, this means knowing the exact location of your library and probably check out for version upgrades and changes.

If you followed my previous instructions, pcap will come with a special utility in which the library itself takes care of the paths and object files used in the compilation process.

This utility is as simple as specifying the -lpcap parameter with your compiler options, easy and simple. When you specify -lpcap your compiler will use your local $PATH to get the library and definitions paths and will setup them accordingly. Now, let’s write a sample program to check everything is working ok.

Sample program in C

This is a sample program in C which does 2 basic things:

  1. Displays a list of all the NICs in your computer and allows you to select one to perform the packet capturing.
  2. Captures 15 network packets and displays them with the Source MAC, Destiny MAC and the Packet Type.

Source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# ifdef _MSC_VER
/*
 * we do not want the warnings about the old deprecated and unsecure CRT functions
 * since these examples can be compiled under *nix as well
 */
# define _CRT_SECURE_NO_WARNINGS
# endif

# include <pcap.h>

/*prototype of the packet handler*/
void packet_handler(u_char *param, const struct pcap_pkthdr*header, const u_char *pkt_data);

int main()
{
  pcap_if_t *alldevs;
  pcap_if_t*d;
  int inum;
  int i=0;
  pcap_t *adhandle;
  char errbuf[PCAP_ERRBUF_SIZE];

  /*Retrieve the device list*/
  if(pcap_findalldevs(&alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }

  /*Print the list*/
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
      printf(" (%s)\n", d->description);
    else
      printf(" (No description available)\n");
  }

  if(i==0)
  {
    printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
    return -1;
  }

  printf("Enter the interface number (1-%d):",i);
  scanf("%d", &inum);

  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /*Free the device list*/
    pcap_freealldevs(alldevs);
    return -1;
  }

  /*Jump to the selected adapter*/
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

  /*Open the device*/
  /*Open the adapter*/
  if ((adhandle= pcap_open_live(
    d->name,  // name of the device
    65536,    // portion of the packet to capture.
              // 65536 grants that the whole packet will be captured on all the MACs.
    1,        // promiscuous mode (nonzero means promiscuous)
    1000,     // read timeout
    errbuf    // error buffer
    )) == NULL)
  {
    fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
    /*Free the device list*/
    pcap_freealldevs(alldevs);
    return -1;
  }

  printf("\nlistening on %s...\n", d->description);

  /*At this point, we don't need any more the device list. Free it*/
  pcap_freealldevs(alldevs);

  /*start the capture*/
  pcap_loop(adhandle, 15, packet_handler, NULL);

  pcap_close(adhandle);
  return 0;
}

/*Callback function invoked by libpcap for every incoming packet*/
void packet_handler(u_char *param, const struct pcap_pkthdr*header, const u_char *pkt_data)
{
  struct tm *ltime;
  char timestr[16];
  time_t local_tv_sec;

  /*
   * unused parameters
   */
  (void)(param);
  (void)(pkt_data);

  /*convert the timestamp to readable format*/
  local_tv_sec = header->ts.tv_sec;
  ltime=localtime(&local_tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

  printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
  int j=0,k=0;
  printf("Destiny MAC:\n");
  for(j=0;j<6;j++){
     printf("%02X:",pkt_data[j]);
  }
  printf("\n Source MAC:\n");
  for(k=6;k<12;k++){
     printf("%02X: ",pkt_data[k]);
  }

  printf(" \n\n");
    unsigned short tipo = (pkt_data[12]*256)+pkt_data[13];
    printf("Type: %d   %02X %02X \n",tipo,pkt_data[12],pkt_data[13]);
}

Save your program as AwesomePacketSniffer.c (or any other kinky name), and you should be good to go.

To compile the program you’ll need to run:

$ gcc AwesomePacketSniffer.c -lpcap -o AwesomePacketSniffer.out

Finally, run the program with:

$ ./AwesomePacketSniffer.out

In case the program isn’t able to detect any network interfaces, try running it with sudo privileges. Note that this is not the intended way to run this kind of programs so you should check your libpcap permissions and configuration.

The output

The expected output should be something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
1. en0 (No description available)
2. p2p0 (No description available)
3. awdl0 (No description available)
4. bridge0 (No description available)
5. utun0 (No description available)
6. en1 (No description available)
7. lo0 (No description available)
8. gif0 (No description available)
9. stf0 (No description available)
10. XHC20 (No description available)

Enter the interface number (1-10): 1

listening on (en0)...

09:57:38,578314 len:140
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,578397 len:662
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,611104 len:66
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00
09:57:38,611414 len:66
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00
09:57:38,632868 len:147
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00
09:57:38,632872 len:830
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00
09:57:38,632971 len:66
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,632972 len:66
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,633381 len:112
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00
09:57:38,633423 len:66
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,635294 len:112
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,645214 len:347
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,645274 len:112
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,645284 len:709
Destiny MAC:
E0:55:3D:62:77:A0:
Source MAC:
D4: 61: 9D: 2D: E1: 58:

Type: 2048   08 00
09:57:38,662611 len:129
Destiny MAC:
D4:61:9D:2D:E1:58:
Source MAC:
E0: 55: 3D: 62: 77: A0:

Type: 2048   08 00

Conclusion

This library allows us to access network packets and the information we can get from them goes beyond this simple tutorial. If you feel like something’s missing, you’d like to discuss this article or got any comments and/or feedback, you can find me on Twitter as @humbertowoody.