summaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/pcap_user.c
blob: bbd20638788aff787964f56ac6862e771a80a79a (plain)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 */

#include <errno.h>
#include <pcap.h>
#include <string.h>
#include <asm/types.h>
#include <net_user.h>
#include "pcap_user.h"
#include <um_malloc.h>

#define PCAP_FD(p) (*(int *)(p))

static int pcap_user_init(void *data, void *dev)
{
	struct pcap_data *pri = data;
	pcap_t *p;
	char errors[PCAP_ERRBUF_SIZE];

	p = pcap_open_live(pri->host_if, ETH_MAX_PACKET + ETH_HEADER_OTHER,
			   pri->promisc, 0, errors);
	if (p == NULL) {
		printk(UM_KERN_ERR "pcap_user_init : pcap_open_live failed - "
		       "'%s'\n", errors);
		return -EINVAL;
	}

	pri->dev = dev;
	pri->pcap = p;
	return 0;
}

static int pcap_open(void *data)
{
	struct pcap_data *pri = data;
	__u32 netmask;
	int err;

	if (pri->pcap == NULL)
		return -ENODEV;

	if (pri->filter != NULL) {
		err = dev_netmask(pri->dev, &netmask);
		if (err < 0) {
			printk(UM_KERN_ERR "pcap_open : dev_netmask failed\n");
			return -EIO;
		}

		pri->compiled = uml_kmalloc(sizeof(struct bpf_program),
					UM_GFP_KERNEL);
		if (pri->compiled == NULL) {
			printk(UM_KERN_ERR "pcap_open : kmalloc failed\n");
			return -ENOMEM;
		}

		err = pcap_compile(pri->pcap,
				   (struct bpf_program *) pri->compiled,
				   pri->filter, pri->optimize, netmask);
		if (err < 0) {
			printk(UM_KERN_ERR "pcap_open : pcap_compile failed - "
			       "'%s'\n", pcap_geterr(pri->pcap));
			goto out;
		}

		err = pcap_setfilter(pri->pcap, pri->compiled);
		if (err < 0) {
			printk(UM_KERN_ERR "pcap_open : pcap_setfilter "
			       "failed - '%s'\n", pcap_geterr(pri->pcap));
			goto out;
		}
	}

	return PCAP_FD(pri->pcap);

 out:
	kfree(pri->compiled);
	return -EIO;
}

static void pcap_remove(void *data)
{
	struct pcap_data *pri = data;

	if (pri->compiled != NULL)
		pcap_freecode(pri->compiled);

	if (pri->pcap != NULL)
		pcap_close(pri->pcap);
}

struct pcap_handler_data {
	char *buffer;
	int len;
};

static void handler(u_char *data, const struct pcap_pkthdr *header,
		    const u_char *packet)
{
	int len;

	struct pcap_handler_data *hdata = (struct pcap_handler_data *) data;

	len = hdata->len < header->caplen ? hdata->len : header->caplen;
	memcpy(hdata->buffer, packet, len);
	hdata->len = len;
}

int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri)
{
	struct pcap_handler_data hdata = ((struct pcap_handler_data)
		                          { .buffer  	= buffer,
					    .len 	= len });
	int n;

	n = pcap_dispatch(pri->pcap, 1, handler, (u_char *) &hdata);
	if (n < 0) {
		printk(UM_KERN_ERR "pcap_dispatch failed - %s\n",
		       pcap_geterr(pri->pcap));
		return -EIO;
	}
	else if (n == 0)
		return 0;
	return hdata.len;
}

const struct net_user_info pcap_user_info = {
	.init		= pcap_user_init,
	.open		= pcap_open,
	.close	 	= NULL,
	.remove	 	= pcap_remove,
	.add_address	= NULL,
	.delete_address = NULL,
	.mtu		= ETH_MAX_PACKET,
	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
};