diff options
Diffstat (limited to 'arch/um/drivers/pcap_user.c')
-rw-r--r-- | arch/um/drivers/pcap_user.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c new file mode 100644 index 000000000..c07b9c752 --- /dev/null +++ b/arch/um/drivers/pcap_user.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL. + */ + +#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, +}; |