A hack for tunneling IP packets from a pcap device to SLIP under MS Windows. Essentially a combination of the wpcap driver and the tunslip tool.

This commit is contained in:
adamdunkels 2008-02-07 09:39:35 +00:00
parent 2187212703
commit f38255b80f
5 changed files with 1572 additions and 0 deletions

8
tools/wpcapslip/Makefile Normal file
View file

@ -0,0 +1,8 @@
CFLAGS=-Wall -Werror -I../../core -I.
all: wpcapslip
wpcapslip: wpcapslip.o wpcap.o
%: %.o
$(CC) $(LDFLAGS) $^ /lib/w32api/libws2_32.a /lib/w32api/libiphlpapi.a -o $@

View file

@ -0,0 +1,16 @@
#ifndef __CONTIKI_CONF_H__
#define __CONTIKI_CONF_H__
#include <stdint.h>
#define CCIF
#define CLIF
typedef uint8_t u8_t;
typedef uint16_t u16_t;
typedef uint32_t u32_t;
typedef int32_t s32_t;
typedef unsigned short uip_stats_t;
typedef unsigned long clock_time_t;
#define CLOCK_CONF_SECOND 1000
#endif /* __CONTIKI_CONF_H__ */

View file

651
tools/wpcapslip/wpcap.c Normal file
View file

@ -0,0 +1,651 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Oliver Schmidt <ol.sc@web.de>
*
* $Id: wpcap.c,v 1.1 2008/02/07 09:39:35 adamdunkels Exp $
*/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __CYGWIN__
#include <alloca.h>
#else /* __CYGWIN__ */
#include <malloc.h>
#endif /* __CYGWIN__ */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <err.h>
/* Avoid 'conflicting types' errors. */
#define htonl
#define htons
#define PROGRESS(x)
static void raw_send(void *buf, int len);
struct pcap;
struct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr {
struct pcap_addr *next;
struct sockaddr *addr;
struct sockaddr *netmask;
struct sockaddr *broadaddr;
struct sockaddr *dstaddr;
} *addresses;
DWORD flags;
};
struct pcap_pkthdr {
struct timeval ts;
DWORD caplen;
DWORD len;
};
void wpcap_send(void *buf, int len);
HMODULE wpcap;
static struct pcap *pcap;
static int (* pcap_findalldevs)(struct pcap_if **, char *);
static struct pcap *(* pcap_open_live)(char *, int, int, int, char *);
static int (* pcap_next_ex)(struct pcap *, struct pcap_pkthdr **, unsigned char **);
static int (* pcap_sendpacket)(struct pcap *, unsigned char *, int);
#define BUFSIZE 1514
#define ARP_REQUEST 1
#define ARP_REPLY 2
#define ARP_HWTYPE_ETH 1
#include "net/uip.h"
#include "net/uip_arp.h"
struct ethip_hdr {
struct uip_eth_hdr ethhdr;
/* IP header. */
u8_t vhl,
tos,
len[2],
ipid[2],
ipoffset[2],
ttl,
proto;
u16_t ipchksum;
uip_ipaddr_t srcipaddr, destipaddr;
};
struct arp_hdr {
struct uip_eth_hdr ethhdr;
uint16_t hwtype;
uint16_t protocol;
uint8_t hwlen;
uint8_t protolen;
uint16_t opcode;
struct uip_eth_addr shwaddr;
uip_ipaddr_t sipaddr;
struct uip_eth_addr dhwaddr;
uip_ipaddr_t dipaddr;
} __attribute__ ((packed));
struct arp_entry {
uip_ipaddr_t ipaddr;
struct uip_eth_addr ethaddr;
u8_t time;
};
static struct uip_eth_addr uip_ethaddr = {{0,0,0,0,0,0}};
static const uip_ipaddr_t all_zeroes_addr = { { 0x0, /* rest is 0 */ } };
static const struct uip_eth_addr broadcast_ethaddr =
{{0xff,0xff,0xff,0xff,0xff,0xff}};
static const uip_ipaddr_t broadcast_ipaddr = {{255,255,255,255}};
static struct arp_entry arp_table[UIP_ARPTAB_SIZE];
static uip_ipaddr_t ifaddr, netaddr, netmask;
static int arptime;
static void
log_message(char *msg1, char *msg2)
{
/* printf("Log: %s %s\n", msg1, msg2);*/
}
/*---------------------------------------------------------------------------*/
static void
error_exit(char *msg1)
{
printf("error_exit: %s", msg1);
exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
static void
init_pcap(struct in_addr addr)
{
struct pcap_if *interfaces;
char error[256];
if(pcap_findalldevs(&interfaces, error) == -1) {
error_exit(error);
}
while(interfaces != NULL) {
log_message("init_pcap: found interface: ", interfaces->description);
if(interfaces->addresses != NULL &&
interfaces->addresses->addr != NULL &&
interfaces->addresses->addr->sa_family == AF_INET) {
struct in_addr interface_addr;
interface_addr = ((struct sockaddr_in *)interfaces->addresses->addr)->sin_addr;
log_message("init_pcap: with address: ", inet_ntoa(interface_addr));
if(interface_addr.s_addr == addr.s_addr) {
break;
}
}
interfaces = interfaces->next;
}
if(interfaces == NULL) {
error_exit("no interface found with ip addr specified on cmdline\n");
}
pcap = pcap_open_live(interfaces->name, BUFSIZE, 0, -1, error);
if(pcap == NULL) {
error_exit(error);
}
}
/*---------------------------------------------------------------------------*/
static void
setethaddr(struct uip_eth_addr *a)
{
memcpy(&uip_ethaddr, a, sizeof(struct uip_eth_addr));
}
/*---------------------------------------------------------------------------*/
static void
set_ethaddr(struct in_addr addr)
{
PIP_ADAPTER_ADDRESSES adapters;
ULONG size = 0;
if(GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER,
NULL, NULL, &size) != ERROR_BUFFER_OVERFLOW) {
error_exit("error on access to adapter list size\n");
}
adapters = alloca(size);
if(GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER,
NULL, adapters, &size) != ERROR_SUCCESS) {
error_exit("error on access to adapter list\n");
}
while(adapters != NULL) {
char buffer[256];
WideCharToMultiByte(CP_ACP, 0, adapters->Description, -1,
buffer, sizeof(buffer), NULL, NULL);
log_message("set_ethaddr: found adapter: ", buffer);
if(adapters->FirstUnicastAddress != NULL &&
adapters->FirstUnicastAddress->Address.lpSockaddr != NULL &&
adapters->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET) {
struct in_addr adapter_addr;
adapter_addr = ((struct sockaddr_in *)adapters->FirstUnicastAddress->Address.lpSockaddr)->sin_addr;
log_message("set_ethaddr: with address: ", inet_ntoa(adapter_addr));
if(adapter_addr.s_addr == addr.s_addr) {
printf("Using local network interface with address %s\n",
inet_ntoa(adapter_addr));
if(adapters->PhysicalAddressLength != 6) {
error_exit("ip addr specified on cmdline does not belong to an ethernet card\n");
}
wsprintf(buffer, "%02X-%02X-%02X-%02X-%02X-%02X",
adapters->PhysicalAddress[0], adapters->PhysicalAddress[1],
adapters->PhysicalAddress[2], adapters->PhysicalAddress[3],
adapters->PhysicalAddress[4], adapters->PhysicalAddress[5]);
log_message("set_ethaddr: ethernetaddr: ", buffer);
setethaddr((struct uip_eth_addr *)adapters->PhysicalAddress);
break;
}
}
adapters = adapters->Next;
}
if(adapters == NULL) {
error_exit("no adapter found with ip addr specified on cmdline\n");
}
}
/*---------------------------------------------------------------------------*/
static void
print_packet(unsigned char *buf, int len)
{
int i;
for(i = 0; i < len; ++i) {
printf("0x%02x, ", buf[i]);
if(i % 8 == 7) {
printf("\n");
}
}
printf("\n\n");
}
/*---------------------------------------------------------------------------*/
static void
uip_arp_update(uip_ipaddr_t *ipaddr, struct uip_eth_addr *ethaddr)
{
struct arp_entry *tabptr;
int i, tmpage, c;
/* Walk through the ARP mapping table and try to find an entry to
update. If none is found, the IP -> MAC address mapping is
inserted in the ARP table. */
for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
/* Only check those entries that are actually in use. */
if(!uip_ipaddr_cmp(&tabptr->ipaddr, &all_zeroes_addr)) {
/* Check if the source IP address of the incoming packet matches
the IP address in this ARP table entry. */
if(uip_ipaddr_cmp(ipaddr, &tabptr->ipaddr)) {
/* An old entry found, update this and return. */
memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);
tabptr->time = arptime;
return;
}
}
}
/* If we get here, no existing ARP table entry was found, so we
create one. */
/* First, we try to find an unused entry in the ARP table. */
for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
if(uip_ipaddr_cmp(&tabptr->ipaddr, &all_zeroes_addr)) {
break;
}
}
/* If no unused entry is found, we try to find the oldest entry and
throw it away. */
if(i == UIP_ARPTAB_SIZE) {
tmpage = 0;
c = 0;
for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
if(arptime - tabptr->time > tmpage) {
tmpage = arptime - tabptr->time;
c = i;
}
}
i = c;
tabptr = &arp_table[i];
}
/* Now, i is the ARP table entry which we will fill with the new
information. */
uip_ipaddr_copy(&tabptr->ipaddr, ipaddr);
memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);
tabptr->time = arptime;
}
/*---------------------------------------------------------------------------*/
/**
* Prepend Ethernet header to an outbound IP packet and see if we need
* to send out an ARP request.
*
* This function should be called before sending out an IP packet. The
* function checks the destination IP address of the IP packet to see
* what Ethernet MAC address that should be used as a destination MAC
* address on the Ethernet.
*
* If the destination IP address is in the local network (determined
* by logical ANDing of netmask and our IP address), the function
* checks the ARP cache to see if an entry for the destination IP
* address is found. If so, an Ethernet header is prepended and the
* function returns. If no ARP cache entry is found for the
* destination IP address, the packet in the uip_buf[] is replaced by
* an ARP request packet for the IP address. The IP packet is dropped
* and it is assumed that they higher level protocols (e.g., TCP)
* eventually will retransmit the dropped packet.
*
* If the destination IP address is not on the local network, the IP
* address of the default router is used instead.
*
* When the function returns, a packet is present in the uip_buf[]
* buffer, and the length of the packet is in the global variable
* uip_len.
*/
/*---------------------------------------------------------------------------*/
static int
arp_out(struct ethip_hdr *iphdr, int len)
{
#if 1
struct arp_entry *tabptr;
struct arp_hdr *arphdr = (struct arp_hdr *)iphdr;
uip_ipaddr_t ipaddr;
int i;
#endif
#if 1
/* Find the destination IP address in the ARP table and construct
the Ethernet header. If the destination IP addres isn't on the
local network, we use the default router's IP address instead.
If not ARP table entry is found, we overwrite the original IP
packet with an ARP request for the IP address. */
/* First check if destination is a local broadcast. */
if(uip_ipaddr_cmp(&iphdr->destipaddr, &broadcast_ipaddr)) {
memcpy(iphdr->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
} else {
/* Check if the destination address is on the local network. */
#if 1
if(!uip_ipaddr_maskcmp(&iphdr->destipaddr, &netaddr, &netmask)) {
/* Destination address was not on the local network, so we need to
use the default router's IP address instead of the destination
address when determining the MAC address. */
uip_ipaddr_copy(&ipaddr, &ifaddr);
} else {
#else
{
#endif
/* Else, we use the destination IP address. */
uip_ipaddr_copy(&ipaddr, &iphdr->destipaddr);
}
for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
if(uip_ipaddr_cmp(&ipaddr, &tabptr->ipaddr)) {
break;
}
}
if(i == UIP_ARPTAB_SIZE) {
/* The destination address was not in our ARP table, so we
overwrite the IP packet with an ARP request. */
memset(arphdr->ethhdr.dest.addr, 0xff, 6);
memset(arphdr->dhwaddr.addr, 0x00, 6);
memcpy(arphdr->ethhdr.src.addr, uip_ethaddr.addr, 6);
memcpy(arphdr->shwaddr.addr, uip_ethaddr.addr, 6);
uip_ipaddr_copy(&arphdr->dipaddr, &ipaddr);
uip_ipaddr_copy(&arphdr->sipaddr, &netaddr);
arphdr->opcode = HTONS(ARP_REQUEST); /* ARP request. */
arphdr->hwtype = HTONS(ARP_HWTYPE_ETH);
arphdr->protocol = HTONS(UIP_ETHTYPE_IP);
arphdr->hwlen = 6;
arphdr->protolen = 4;
arphdr->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
/* uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];*/
return sizeof(struct arp_hdr);
}
/* Build an ethernet header. */
memcpy(iphdr->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
}
#endif /* 0 */
memcpy(iphdr->ethhdr.src.addr, uip_ethaddr.addr, 6);
iphdr->ethhdr.type = HTONS(UIP_ETHTYPE_IP);
return len + sizeof(struct uip_eth_hdr);
}
/*---------------------------------------------------------------------------*/
void *
do_arp(void *buf, int len)
{
struct arp_hdr *hdr;
hdr = (struct arp_hdr *)buf;
if(hdr->ethhdr.type == HTONS(UIP_ETHTYPE_ARP)) {
if(hdr->opcode == HTONS(ARP_REQUEST)) {
/* Check if the ARP is for our network */
printf("ARP for %d.%d.%d.%d we are %d.%d.%d.%d/%d.%d.%d.%d\n",
uip_ipaddr_to_quad(&hdr->dipaddr),
uip_ipaddr_to_quad(&netaddr),
uip_ipaddr_to_quad(&netmask));
if(uip_ipaddr_maskcmp(&hdr->dipaddr, &netaddr, &netmask)) {
uip_ipaddr_t tmpaddr;
printf("ARP for us.\n");
uip_arp_update(&hdr->sipaddr, &hdr->shwaddr);
hdr->opcode = HTONS(ARP_REPLY);
memcpy(&hdr->dhwaddr.addr, &hdr->shwaddr.addr, 6);
memcpy(&hdr->shwaddr.addr, &uip_ethaddr.addr, 6);
memcpy(&hdr->ethhdr.src.addr, &uip_ethaddr.addr, 6);
memcpy(&hdr->ethhdr.dest.addr, &hdr->dhwaddr.addr, 6);
uip_ipaddr_copy(&tmpaddr, &hdr->dipaddr);
uip_ipaddr_copy(&hdr->dipaddr, &hdr->sipaddr);
uip_ipaddr_copy(&hdr->sipaddr, &tmpaddr);
hdr->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
raw_send(hdr, sizeof(struct arp_hdr));
return NULL;
}
} else if(hdr->opcode == HTONS(ARP_REPLY)) {
/* ARP reply. We insert or update the ARP table if it was meant
for us. */
if(uip_ipaddr_maskcmp(&hdr->dipaddr, &netaddr, &netmask)) {
uip_arp_update(&hdr->sipaddr, &hdr->shwaddr);
}
}
}
if(hdr->ethhdr.type == HTONS(UIP_ETHTYPE_IP)) {
return &((struct ethip_hdr *)hdr)->vhl;
}
return NULL;
}
/*---------------------------------------------------------------------------*/
void
cleanup(void)
{
char buf[1024];
snprintf(buf, sizeof(buf), "route delete %d.%d.%d.%d",
uip_ipaddr_to_quad(&ifaddr));
printf("%s\n", buf);
system(buf);
}
/*---------------------------------------------------------------------------*/
static void
remove_route(int s)
{
cleanup();
_exit(0);
}
/*---------------------------------------------------------------------------*/
void
wpcap_start(char *ethcardaddr, char *slipnetaddr, char *slipnetmask)
{
struct in_addr addr;
char buf[4000];
addr.s_addr = inet_addr(ethcardaddr);
ifaddr.u32[0] = inet_addr(ethcardaddr);
netaddr.u32[0] = inet_addr(slipnetaddr);
netmask.u32[0] = inet_addr(slipnetmask);
printf("Network address %d.%d.%d.%d/%d.%d.%d.%d\n",
uip_ipaddr_to_quad(&netaddr),
uip_ipaddr_to_quad(&netmask));
snprintf(buf, sizeof(buf), "route add %d.%d.%d.%d mask %d.%d.%d.%d %d.%d.%d.%d",
uip_ipaddr_to_quad(&netaddr),
uip_ipaddr_to_quad(&netmask),
uip_ipaddr_to_quad(&ifaddr));
printf("%s\n", buf);
system(buf);
signal(SIGTERM, remove_route);
log_message("wpcap_init: cmdline address: ", inet_ntoa(addr));
wpcap = LoadLibrary("wpcap.dll");
pcap_findalldevs = (int (*)(struct pcap_if **, char *))
GetProcAddress(wpcap, "pcap_findalldevs");
pcap_open_live = (struct pcap *(*)(char *, int, int, int, char *))
GetProcAddress(wpcap, "pcap_open_live");
pcap_next_ex = (int (*)(struct pcap *, struct pcap_pkthdr **, unsigned char **))
GetProcAddress(wpcap, "pcap_next_ex");
pcap_sendpacket = (int (*)(struct pcap *, unsigned char *, int))
GetProcAddress(wpcap, "pcap_sendpacket");
if(pcap_findalldevs == NULL || pcap_open_live == NULL ||
pcap_next_ex == NULL || pcap_sendpacket == NULL) {
error_exit("error on access to winpcap library\n");
}
init_pcap(addr);
set_ethaddr(addr);
return;
#if 0
while(1) {
int ret;
ret = wpcap_poll(buf);
if(ret > 0) {
/* print_packet(buf, ret);*/
if(do_arp(buf, ret)) {
printf("IP packet\n");
}
}
sleep(1);
}
#endif
}
/*---------------------------------------------------------------------------*/
uint16_t
wpcap_poll(char **buf)
{
struct pcap_pkthdr *packet_header;
unsigned char *packet;
int len;
char *buf2;
switch(pcap_next_ex(pcap, &packet_header, &packet)) {
case -1:
error_exit("error on poll\n");
case 0:
return 0;
}
if(packet_header->caplen > BUFSIZE) {
return 0;
}
CopyMemory(*buf, packet, packet_header->caplen);
len = packet_header->caplen;
/* printf("len %d\n", len);*/
buf2 = do_arp(*buf, len);
if(buf2 == NULL) {
return 0;
} else {
len = len - (buf2 - *buf);
*buf = buf2;
return len;
}
}
/*---------------------------------------------------------------------------*/
void
wpcap_send(void *buf, int len)
{
char buf2[4000];
memcpy(&buf2[sizeof(struct uip_eth_hdr)], buf, len);
len = arp_out((struct ethip_hdr *)buf2, len);
raw_send(buf2, len);
}
/*---------------------------------------------------------------------------*/
static void
raw_send(void *buf, int len)
{
/* printf("sending len %d\n", len);*/
if(pcap_sendpacket(pcap, buf, len) == -1) {
print_packet(buf, len);
error_exit("error on send\n");
}
}
/*---------------------------------------------------------------------------*/
void
wpcap_exit(void)
{
FreeLibrary(wpcap);
}
/*---------------------------------------------------------------------------*/

897
tools/wpcapslip/wpcapslip.c Normal file
View file

@ -0,0 +1,897 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Oliver Schmidt <ol.sc@web.de>
*
* $Id: wpcapslip.c,v 1.1 2008/02/07 09:39:35 adamdunkels Exp $
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __CYGWIN__
#include <alloca.h>
#else /* __CYGWIN__ */
#include <malloc.h>
#endif /* __CYGWIN__ */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <err.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PROGRESS(x)
void wpcap_start(char *ethifaddr, char *netaddr, char *netmask);
void wpcap_send(void *buf, int len);
uint16_t wpcap_poll(char **buf);
/*---------------------------------------------------------------------------*/
void cleanup(void);
/*---------------------------------------------------------------------------*/
void
sigcleanup(int signo)
{
fprintf(stderr, "signal %d\n", signo);
exit(0); /* exit(0) will call cleanup() */
}
/*---------------------------------------------------------------------------*/
#define SLIP_END 0300
#define SLIP_ESC 0333
#define SLIP_ESC_END 0334
#define SLIP_ESC_ESC 0335
struct ip {
u_int8_t ip_vhl; /* version and header length */
#define IP_V4 0x40
#define IP_V 0xf0
#define IP_HL 0x0f
u_int8_t ip_tos; /* type of service */
u_int16_t ip_len; /* total length */
u_int16_t ip_id; /* identification */
u_int16_t ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_int8_t ip_ttl; /* time to live */
u_int8_t ip_p; /* protocol */
u_int16_t ip_sum; /* checksum */
u_int32_t ip_src, ip_dst; /* source and dest address */
u_int16_t uh_sport; /* source port */
u_int16_t uh_dport; /* destination port */
u_int16_t uh_ulen; /* udp length */
u_int16_t uh_sum; /* udp checksum */
};
static int ip_id;
int
ssystem(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2)));
int
ssystem(const char *fmt, ...)
{
char cmd[128];
va_list ap;
va_start(ap, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, ap);
va_end(ap);
printf("%s\n", cmd);
fflush(stdout);
return system(cmd);
}
int
is_sensible_string(const unsigned char *s, int len)
{
int i;
for(i = 1; i < len; i++) {
if(s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] == '\t') {
continue;
} else if(s[i] < ' ' || '~' < s[i]) {
return 0;
}
}
return 1;
}
u_int16_t
ip4sum(u_int16_t sum, const void *_p, u_int16_t len)
{
u_int16_t t;
const u_int8_t *p = _p;
const u_int8_t *end = p + len;
while(p < (end-1)) {
t = (p[0] << 8) + p[1];
sum += t;
if (sum < t) sum++;
p += 2;
}
if(p < end) {
t = (p[0] << 8) + 0;
sum += t;
if (sum < t) sum++;
}
return sum;
}
int
check_ip(const struct ip *ip, unsigned ip_len)
{
u_int16_t sum, ip_hl;
/* Check IP version and length. */
if((ip->ip_vhl & IP_V) != IP_V4)
return -1;
if(ntohs(ip->ip_len) > ip_len)
return -2;
if(ntohs(ip->ip_len) < ip_len)
return -3;
/* Check IP header. */
ip_hl = 4*(ip->ip_vhl & IP_HL);
sum = ip4sum(0, ip, ip_hl);
if(sum != 0xffff && sum != 0x0)
return -4;
if(ip->ip_p == 6 || ip->ip_p == 17) { /* Check TCP or UDP header. */
u_int16_t tcp_len = ip_len - ip_hl;
/* Sum pseudoheader. */
sum = ip->ip_p + tcp_len; /* proto and len, no carry */
sum = ip4sum(sum, &ip->ip_src, 8); /* src and dst */
/* Sum TCP/UDP header and data. */
sum = ip4sum(sum, (u_int8_t*)ip + ip_hl, tcp_len);
/* Failed checksum test? */
if (sum != 0xffff && sum != 0x0) {
if (ip->ip_p == 6) { /* TCP == 6 */
return -5;
} else { /* UDP */
/* Deal with disabled UDP checksums. */
if (ip->uh_sum != 0)
return -6;
}
}
} else if (ip->ip_p == 1) { /* ICMP */
u_int16_t icmp_len = ip_len - ip_hl;
sum = ip4sum(0, (u_int8_t*)ip + ip_hl, icmp_len);
if(sum != 0xffff && sum != 0x0)
return -7;
}
return 0;
}
/*
* Read from serial, when we have a packet write it to tun. No output
* buffering, input buffered by stdio.
*/
void
serial_to_wpcap(FILE *inslip)
{
static union {
unsigned char inbuf[2000];
struct ip iphdr;
} uip;
static int inbufptr = 0;
int ret;
unsigned char c;
#ifdef linux
ret = fread(&c, 1, 1, inslip);
if(ret == -1 || ret == 0) err(1, "serial_to_tun: read");
goto after_fread;
#endif
read_more:
if(inbufptr >= sizeof(uip.inbuf)) {
inbufptr = 0;
}
ret = fread(&c, 1, 1, inslip);
#ifdef linux
after_fread:
#endif
if(ret == -1) {
err(1, "serial_to_tun: read");
}
if(ret == 0) {
clearerr(inslip);
return;
fprintf(stderr, "serial_to_tun: EOF\n");
exit(1);
}
/* fprintf(stderr, ".");*/
switch(c) {
case SLIP_END:
if(inbufptr > 0) {
/*
* Sanity checks.
*/
#define DEBUG_LINE_MARKER '\r'
int ecode;
ecode = check_ip(&uip.iphdr, inbufptr);
if(ecode < 0 && inbufptr == 8 && strncmp(uip.inbuf, "=IPA", 4) == 0) {
static struct in_addr ipa;
inbufptr = 0;
if(memcmp(&ipa, &uip.inbuf[4], sizeof(ipa)) == 0) {
break;
}
/* New address. */
if(ipa.s_addr != 0) {
#if 0
#ifdef linux
ssystem("route delete -net %s netmask %s dev %s",
inet_ntoa(ipa), "255.255.255.255", tundev);
#else
ssystem("route delete -net %s -netmask %s -interface %s",
inet_ntoa(ipa), "255.255.255.255", tundev);
#endif
#endif /* 0 */
}
memcpy(&ipa, &uip.inbuf[4], sizeof(ipa));
if(ipa.s_addr != 0) {
#if 0
#ifdef linux
ssystem("route add -net %s netmask %s dev %s",
inet_ntoa(ipa), "255.255.255.255", tundev);
#else
ssystem("route add -net %s -netmask %s -interface %s",
inet_ntoa(ipa), "255.255.255.255", tundev);
#endif
#endif /* 0 */
}
break;
} else if(ecode < 0) {
/*
* If sensible ASCII string, print it as debug info!
*/
/* printf("----------------------------------\n");*/
if(uip.inbuf[0] == DEBUG_LINE_MARKER) {
fwrite(uip.inbuf + 1, inbufptr - 1, 1, stderr);
} else if(is_sensible_string(uip.inbuf, inbufptr)) {
fwrite(uip.inbuf, inbufptr, 1, stderr);
} else {
fprintf(stderr,
"serial_to_tun: drop packet len=%d ecode=%d\n",
inbufptr, ecode);
}
inbufptr = 0;
break;
}
PROGRESS("s");
#if 0
if(dhsock != -1) {
struct ip *ip = (void *)uip.inbuf;
if(ip->ip_p == 17 && ip->ip_dst == 0xffffffff /* UDP and broadcast */
&& ip->uh_sport == ntohs(BOOTPC) && ip->uh_dport == ntohs(BOOTPS)) {
relay_dhcp_to_server(ip, inbufptr);
inbufptr = 0;
}
}
#endif /* 0 */
/* if(write(outfd, uip.inbuf, inbufptr) != inbufptr) {
err(1, "serial_to_tun: write");
}*/
/* printf("Sending to wpcap\n");*/
wpcap_send(uip.inbuf, inbufptr);
/* printf("After sending to wpcap\n");*/
inbufptr = 0;
}
break;
case SLIP_ESC:
if(fread(&c, 1, 1, inslip) != 1) {
clearerr(inslip);
/* Put ESC back and give up! */
ungetc(SLIP_ESC, inslip);
return;
}
switch(c) {
case SLIP_ESC_END:
c = SLIP_END;
break;
case SLIP_ESC_ESC:
c = SLIP_ESC;
break;
}
/* FALLTHROUGH */
default:
uip.inbuf[inbufptr++] = c;
break;
}
goto read_more;
}
/*---------------------------------------------------------------------------*/
unsigned char slip_buf[2000];
int slip_end, slip_begin;
/*---------------------------------------------------------------------------*/
void
slip_send(int fd, unsigned char c)
{
if(slip_end >= sizeof(slip_buf)) {
err(1, "slip_send overflow");
}
slip_buf[slip_end] = c;
slip_end++;
}
/*---------------------------------------------------------------------------*/
int
slip_empty()
{
return slip_end == 0;
}
/*---------------------------------------------------------------------------*/
void
slip_flushbuf(int fd)
{
int n;
if (slip_empty())
return;
n = write(fd, slip_buf + slip_begin, (slip_end - slip_begin));
if(n == -1 && errno != EAGAIN) {
err(1, "slip_flushbuf write failed");
} else if(n == -1) {
PROGRESS("Q"); /* Outqueueis full! */
} else {
slip_begin += n;
if(slip_begin == slip_end) {
slip_begin = slip_end = 0;
}
}
}
/*---------------------------------------------------------------------------*/
void
write_to_serial(int outfd, void *inbuf, int len)
{
u_int8_t *p = inbuf;
int i, ecode;
struct ip *iphdr = inbuf;
/*
* Sanity checks.
*/
ecode = check_ip(inbuf, len);
if(ecode < 0) {
fprintf(stderr, "write_to_serial: drop packet %d\n", ecode);
return;
}
if(iphdr->ip_id == 0 && iphdr->ip_off & IP_DF) {
uint16_t nid = htons(ip_id++);
iphdr->ip_id = nid;
nid = ~nid; /* negate */
iphdr->ip_sum += nid; /* add */
if(iphdr->ip_sum < nid) { /* 1-complement overflow? */
iphdr->ip_sum++;
}
ecode = check_ip(inbuf, len);
if(ecode < 0) {
fprintf(stderr, "tun_to_serial: drop packet %d\n", ecode);
return;
}
}
/* It would be ``nice'' to send a SLIP_END here but it's not
* really necessary.
*/
/* slip_send(outfd, SLIP_END); */
for(i = 0; i < len; i++) {
switch(p[i]) {
case SLIP_END:
slip_send(outfd, SLIP_ESC);
slip_send(outfd, SLIP_ESC_END);
break;
case SLIP_ESC:
slip_send(outfd, SLIP_ESC);
slip_send(outfd, SLIP_ESC_ESC);
break;
default:
slip_send(outfd, p[i]);
break;
}
}
slip_send(outfd, SLIP_END);
PROGRESS("t");
}
/*---------------------------------------------------------------------------*/
/*
* Read from tun, write to slip.
*/
#if 0
void
tun_to_serial(int infd, int outfd)
{
static union {
unsigned char inbuf[2000];
struct ip iphdr;
} uip;
int size;
if((size = read(infd, uip.inbuf, 2000)) == -1) {
err(1, "tun_to_serial: read");
}
write_to_serial(outfd, uip.inbuf, size);
}
#endif /* 0 */
/*---------------------------------------------------------------------------*/
#ifndef BAUDRATE
#define BAUDRATE B115200
#endif
speed_t b_rate = BAUDRATE;
void
cfmakeraw(t)
struct termios *t;
{
t->c_iflag &= ~(IMAXBEL|IXOFF|INPCK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IGNPAR);
t->c_iflag |= IGNBRK;
t->c_oflag &= ~OPOST;
t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|NOFLSH|TOSTOP);
t->c_cflag &= ~(CSIZE|PARENB);
t->c_cflag |= CS8|CREAD;
t->c_cc[VMIN] = 1;
t->c_cc[VTIME] = 0;
}
void
stty_telos(int fd)
{
struct termios options;
speed_t speed = b_rate;
/* if(fcntl(fd, F_SETFL, 0) < 0) {
perror("could not set fcntl");
exit(-1);
}*/
if(tcgetattr(fd, &options) < 0) {
perror("could not get options");
exit(-1);
}
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);
/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);
/* Mask the character size bits and turn off (odd) parity */
options.c_cflag &= ~(CSIZE|PARENB|PARODD);
/* Select 8 data bits */
options.c_cflag |= CS8;
/* Raw input */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* Raw output */
options.c_oflag &= ~OPOST;
if (tcsetattr(fd, TCSANOW, &options) < 0) {
perror("could not set options");
exit(-1);
}
#if 0
struct termios tty;
speed_t speed = b_rate;
int i;
if(tcflush(fd, TCIOFLUSH) == -1) err(1, "tcflush");
if(tcgetattr(fd, &tty) == -1) err(1, "tcgetattr");
cfmakeraw(&tty);
/* Nonblocking read. */
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 0;
tty.c_cflag &= ~CRTSCTS;
tty.c_cflag &= ~HUPCL;
tty.c_cflag &= ~CLOCAL;
cfsetispeed(&tty, speed);
cfsetospeed(&tty, speed);
if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) err(1, "tcsetattr");
#if 1
/* Nonblocking read and write. */
/* if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); */
tty.c_cflag |= CLOCAL;
if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) err(1, "tcsetattr");
i = TIOCM_DTR;
if(ioctl(fd, TIOCMBIS, &i) == -1) err(1, "ioctl");
#endif
usleep(10*1000); /* Wait for hardware 10ms. */
/* Flush input and output buffers. */
if(tcflush(fd, TCIOFLUSH) == -1) err(1, "tcflush");
#endif /* 0 */
}
/*---------------------------------------------------------------------------*/
int
devopen(const char *dev, int flags)
{
char t[32];
strcpy(t, "/dev/");
strcat(t, dev);
return open(t, flags);
}
/*---------------------------------------------------------------------------*/
/*const char *ipaddr;*/
/*const char *netmask;*/
static int got_sigalarm;
void
sigalarm(int signo)
{
got_sigalarm = 1;
return;
}
/*---------------------------------------------------------------------------*/
void
sigalarm_reset()
{
#ifdef linux
#define TIMEOUT (997*1000)
#else
#define TIMEOUT (2451*1000)
#endif
ualarm(TIMEOUT, TIMEOUT);
got_sigalarm = 0;
}
/*---------------------------------------------------------------------------*/
int
main(int argc, char **argv)
{
int c;
int slipfd, maxfd;
int ret;
fd_set rset, wset;
FILE *inslip;
const char *siodev = NULL;
const char *dhcp_server = NULL;
/* u_int16_t myport = BOOTPS, dhport = BOOTPS;*/
int baudrate = -2;
char buf[4000];
ip_id = getpid() * time(NULL);
setvbuf(stdout, NULL, _IOLBF, 0); /* Line buffered output. */
while((c = getopt(argc, argv, "B:D:hs:t:")) != -1) {
switch (c) {
case 'B':
baudrate = atoi(optarg);
break;
case 'D':
dhcp_server = optarg;
break;
case 's':
if(strncmp("/dev/", optarg, 5) == 0) {
siodev = optarg + 5;
} else {
siodev = optarg;
}
break;
#if 0
case 't':
if(strncmp("/dev/", optarg, 5) == 0) {
strcpy(tundev, optarg + 5);
} else {
strcpy(tundev, optarg);
}
break;
#endif /* 0 */
case '?':
case 'h':
default:
err(1, "usage: wpcapslip [-B baudrate] [-s siodev] [-D dhcp-server] ipaddress netmask [dhcp-server]");
break;
}
}
argc -= (optind - 1);
argv += (optind - 1);
if(argc != 4) {
err(1, "usage: wpcapslip [-s siodev] [-D dhcp-server] <IP address of local Ethernet card> <IP address of SLIP network> <netmask of SLIP network>");
}
/* ipaddr = argv[1];
netmask = argv[2];*/
wpcap_start(argv[1], argv[2], argv[3]);
/* netaddr = inet_addr(ipaddr) & inet_addr(netmask);*/
switch(baudrate) {
case -2:
break; /* Use default. */
case 9600:
b_rate = B9600;
break;
case 19200:
b_rate = B19200;
break;
case 38400:
b_rate = B38400;
break;
case 57600:
b_rate = B57600;
break;
case 115200:
b_rate = B115200;
break;
default:
err(1, "unknown baudrate %d", baudrate);
break;
}
/*
* Set up DHCP relay agent socket and find the address of this relay
* agent.
*/
#if 0
if(argc == 4) {
dhcp_server = argv[3];
}
if(dhcp_server != NULL) {
struct sockaddr_in myaddr;
socklen_t len;
in_addr_t a;
if(strchr(dhcp_server, ':') != NULL) {
dhport = atoi(strchr(dhcp_server, ':') + 1);
myport = dhport + 1;
*strchr(dhcp_server, ':') = '\0';
}
a = inet_addr(dhcp_server);
if(a == -1) {
err(1, "illegal dhcp-server address");
}
#ifndef linux
dhaddr.sin_len = sizeof(dhaddr);
#endif
dhaddr.sin_family = AF_INET;
dhaddr.sin_port = htons(dhport);
dhaddr.sin_addr.s_addr = a;
dhsock = socket(AF_INET, SOCK_DGRAM, 0);
if(dhsock < 0) {
err (1, "socket");
}
memset(&myaddr, 0x0, sizeof(myaddr));
#ifndef linux
myaddr.sin_len = sizeof(myaddr);
#endif
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons(myport);
if(bind(dhsock, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
err(1, "bind dhcp-relay");
}
if(connect(dhsock, (struct sockaddr *)&dhaddr, sizeof(dhaddr)) < 0) {
err(1, "connect to dhcp-server");
}
len = sizeof(myaddr);
if(getsockname(dhsock, (struct sockaddr *)&myaddr, &len) < 0) {
err(1, "getsockname dhsock");
}
giaddr = myaddr.sin_addr.s_addr;
/*
* Don't want connected socket.
*/
close(dhsock);
dhsock = socket(AF_INET, SOCK_DGRAM, 0);
if(dhsock < 0) {
err (1, "socket");
}
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons(myport);
if(bind(dhsock, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
err(1, "bind dhcp-relay");
}
fprintf(stderr, "DHCP server at %s:%d\n", dhcp_server, dhport);
}
#endif /* 0 */
if(siodev != NULL) {
slipfd = devopen(siodev, O_RDWR | O_NONBLOCK | O_NOCTTY | O_NDELAY | O_DIRECT | O_SYNC );
if(slipfd == -1) {
err(1, "can't open siodev ``/dev/%s''", siodev);
}
} else {
static const char *siodevs[] = {
"ttyUSB0", "cuaU0", "ucom0" /* linux, fbsd6, fbsd5 */
};
int i;
for(i = 0; i < 3; i++) {
siodev = siodevs[i];
slipfd = devopen(siodev, O_RDWR | O_NONBLOCK);
if(slipfd != -1) {
break;
}
}
if(slipfd == -1) {
err(1, "can't open siodev");
}
}
fprintf(stderr, "slip started on ``/dev/%s''\n", siodev);
stty_telos(slipfd);
slip_send(slipfd, SLIP_END);
inslip = fdopen(slipfd, "r");
if(inslip == NULL) {
err(1, "main: fdopen");
}
/* tunfd = tun_alloc(tundev);
if(tunfd == -1) err(1, "main: open");
fprintf(stderr, "opened device ``/dev/%s''\n", tundev);*/
atexit(cleanup);
signal(SIGHUP, sigcleanup);
signal(SIGTERM, sigcleanup);
signal(SIGINT, sigcleanup);
signal(SIGALRM, sigalarm);
/* ifconf(tundev, ipaddr, netmask);*/
while(1) {
maxfd = 0;
FD_ZERO(&rset);
FD_ZERO(&wset);
if(got_sigalarm) {
/* Send "?IPA". */
slip_send(slipfd, '?');
slip_send(slipfd, 'I');
slip_send(slipfd, 'P');
slip_send(slipfd, 'A');
slip_send(slipfd, SLIP_END);
got_sigalarm = 0;
}
if(!slip_empty()) { /* Anything to flush? */
FD_SET(slipfd, &wset);
}
FD_SET(slipfd, &rset); /* Read from slip ASAP! */
if(slipfd > maxfd) {
maxfd = slipfd;
}
/* We only have one packet at a time queued for slip output. */
if(slip_empty()) {
/* FD_SET(tunfd, &rset);
if(tunfd > maxfd) maxfd = tunfd;
if(dhsock != -1) {
FD_SET(dhsock, &rset);
if(dhsock > maxfd) maxfd = dhsock;
}*/
}
if(slip_empty()) {
char *pbuf = buf;
ret = wpcap_poll(&pbuf);
if(ret > 0) {
/* printf("wpcap_poll ret %d", ret);
printf("IP packet\n");*/
write_to_serial(slipfd, pbuf, ret);
slip_flushbuf(slipfd);
sigalarm_reset();
}
/* } else {
printf("!slip_empty\n");*/
}
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10;
ret = select(maxfd + 1, &rset, &wset, NULL, &tv);
}
if(ret == -1 && errno != EINTR) {
err(1, "select");
} else if(ret > 0) {
if(FD_ISSET(slipfd, &rset)) {
/* printf("serial_to_wpcap\n");*/
/*serial_to_tun(inslip, tunfd);*/
serial_to_wpcap(inslip);
/* printf("End of serial_to_wpcap\n");*/
}
if(FD_ISSET(slipfd, &wset)) {
/* printf("slip_flushbuf\n");*/
slip_flushbuf(slipfd);
sigalarm_reset();
}
/* if(slip_empty() && FD_ISSET(tunfd, &rset)) {
tun_to_serial(tunfd, slipfd);
slip_flushbuf(slipfd);
sigalarm_reset();
}*/
#if 0
if(dhsock != -1 && slip_empty() && FD_ISSET(dhsock, &rset)) {
relay_dhcp_to_client(slipfd);
slip_flushbuf(slipfd);
}
#endif /* 0 */
}
}
}
/*---------------------------------------------------------------------------*/