summaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/pcap_user.c
blob: 11921a7baa7b14066d2c2f7d7f653063535e516e (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
138
139
140
141
142
143
144
/*
 * Copyright (C) 2002 Jeff Dike <jdike@karaya.com>
 * Licensed under the GPL.
 */

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

#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER)

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

static void 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, MAX_PACKET, pri->promisc, 0, errors);
	if(p == NULL){
		printk("pcap_user_init : pcap_open_live failed - '%s'\n", 
		       errors);
		return;
	}

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

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("pcap_open : dev_netmask failed\n");
			return(-EIO);
		}

		pri->compiled = um_kmalloc(sizeof(struct bpf_program));
		if(pri->compiled == NULL){
			printk("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("pcap_open : pcap_compile failed - '%s'\n", 
			       pcap_geterr(pri->pcap));
			return(-EIO);
		}

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

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

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

	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("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,
	.set_mtu	= NULL,
	.add_address	= NULL,
	.delete_address = NULL,
	.max_packet	= MAX_PACKET - ETH_HEADER_OTHER
};

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */