aboutsummaryrefslogtreecommitdiffstats
path: root/gg_sniff/pcap.c
blob: b13a4866d4ce142d3434ca014cff36017f9b84e4 (plain) (blame)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#define PCAP_INTERFACE "lo"
#define PCAP_SNAPLEN 100
#define PCAP_FILTER "not port 4430 and not port 53"
#define PCAP_COUNT 20
#define PCAP_TO 300

static pcap_t *
ggsniff_pcap_init(void)
{
	pcap = my_pcap_open_live(PCAP_INTERFACE, PCAP_SNAPLEN, 1, PCAP_TO, errbuf, -1, 0);
	if (pcap == NULL)
		fatal("capture: pcap_open_live failed on interface %s\n"
			"with snaplen %d : %s",
			PCAP_INTERFACE, PCAP_SNAPLEN, errbuf);
	if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0)
		fatal("capture: pcap_compile failed with filter %s : %s",
			PCAP_FILTER, pcap_geterr(pcap));
	if (pcap_setfilter(pcap, &bprog) < 0)
		fatal("capture: pcap_setfilter failed : %s",
			pcap_geterr(pcap));
}

/*
 * reimplement pcap_open_live with more restrictions on the bpf fd :
 * - open device read only
 * - lock the fd
 * based on OpenBSD tcpdump, privsep_pcap.c v1.16
 */

static pcap_t *
my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms,
	char *ebuf, u_int dlt, u_int dirfilt)
{
#if defined(__OpenBSD__)
	struct bpf_version bv;
	u_int v;
	pcap_t *p;
	char		bpf[sizeof "/dev/bpf0000000000"];
	int		fd, n = 0;
	struct ifreq	ifr;

	p = xmalloc(sizeof(*p));
	bzero(p, sizeof(*p));

	/* priv part */

	do {
		snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++);
		fd = open(bpf, O_RDONLY);
	} while (fd < 0 && errno == EBUSY);
	if (fd < 0)
		return NULL;

	v = 32768;	/* XXX this should be a user-accessible hook */
	ioctl(fd, BIOCSBLEN, &v);

	strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
	if (ioctl(fd, BIOCSETIF, &ifr) < 0)
		return NULL;

	if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
		return NULL;

	if (promisc)
		/* this is allowed to fail */
		ioctl(fd, BIOCPROMISC, NULL);
	if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
		return NULL;

	/* lock the descriptor */
	if (ioctl(fd, BIOCLOCK, NULL) < 0)
		return NULL;

	/* end of priv part */

	/* fd is locked, can only use 'safe' ioctls */
	if (ioctl(fd, BIOCVERSION, &bv) < 0) {
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
		    pcap_strerror(errno));
		return NULL;
	}

	if (bv.bv_major != BPF_MAJOR_VERSION ||
	    bv.bv_minor < BPF_MINOR_VERSION) {
		snprintf(ebuf, PCAP_ERRBUF_SIZE,
		    "kernel bpf filter out of date");
		return NULL;
	}

	p->fd = fd;
	p->snapshot = slen;

	/* Get the data link layer type. */
	if (ioctl(fd, BIOCGDLT, &v) < 0) {
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
		    pcap_strerror(errno));
		return NULL;
	}
#if _BSDI_VERSION - 0 >= 199510
	/* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
	switch (v) {

	case DLT_SLIP:
		v = DLT_SLIP_BSDOS;
		break;

	case DLT_PPP:
		v = DLT_PPP_BSDOS;
		break;
	}
#endif
	p->linktype = v;

	/* XXX hack from tcpdump */
	if (p->linktype == DLT_PFLOG && p->snapshot < 160)
		p->snapshot = 160;

	/* set timeout */
	if (to_ms != 0) {
		struct timeval to;
		to.tv_sec = to_ms / 1000;
		to.tv_usec = (to_ms * 1000) % 1000000;
		if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
			snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
			    pcap_strerror(errno));
			return NULL;
		}
	}

	if (ioctl(fd, BIOCGBLEN, &v) < 0) {
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
		    pcap_strerror(errno));
		return NULL;
	}
	p->bufsize = v;
	p->buffer = (u_char *)malloc(p->bufsize);
	if (p->buffer == NULL) {
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
		    pcap_strerror(errno));
		return NULL;
	}
	return p;
#else /* defined(__OpenBSD__) */
  return pcap_open_live(dev, slen, promisc, to_ms, ebuf);
#endif
}