Show ip_connector.c syntax highlighted
/* ex: set tabstop=2 expandtab shiftwidth=2 softtabstop=2: */
/*
*
* Copyright (c) 2003 The Regents of the University of California. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the University 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 REGENTS 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 REGENTS 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.
*
*/
char ip_connector_c_id[] = "$Id: ip_connector.c,v 1.10 2005/10/22 00:28:59 karenyc Exp $";
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h> // for offsetof
#include "libmisc/misc.h"
#include "link/link.h"
#include "libdev/packet_dev.h"
#include "emrun/emrun.h"
#include <fcntl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/if_tun.h>
#include <libdev/glib_dev.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define CORRECT_NET 0x00010000 //0.1.0.0
#define BOGUS_NET 0x00020000 //0.2.0.0
#define CHECK_MASK 0x00FF0000 //0.255.0.0
#define BOGUS_OCTET 2 // the bogus net as an octet (i.e, 2)
#define BOGUS_OCTET_OFFSET 1 // offset when IP is a char array
#define HWADDR_SIZE 6 // ethernet address size, in bytes
#define IPADDR_SIZE 4 // ip address size, in bytes
#define PKT_ERR -1
#define PKT_DONT_FWD 0
#define PKT_FWD_TO_TAP 1
#define PKT_FWD_TO_RAW 2
#define PKT_FWD_TO_TAP_AND_RAW 3
#define ARP_NORMAL_IFACE 1
#define ARP_BOGUS_IFACE 2
#define ARP_NOT_MINE 0
// first and second octets are pre-defined, the rest are created from the
// interface ids
#define IP_FIRST_OCTET 10
#define IP_SECOND_OCTET 1
#define ARP_PKTLEN (sizeof(struct ether_header) + sizeof(struct arphdr) + sizeof(eth_arp_t))
// copied off of if_arp.h (it's ifdefed out over there)
typedef struct _eth_arp {
uint8_t ar_sha[HWADDR_SIZE];
uint8_t ar_sip[IPADDR_SIZE];
uint8_t ar_tha[HWADDR_SIZE];
uint8_t ar_tip[IPADDR_SIZE];
} eth_arp_t;
typedef struct ipconn_state {
/* state needed for the "lower" (mote) side of the interface */
char *uses_name;
char *provides_name;
/* state needed for the link-interface side */
if_id_t link_if_id;
lu_context_t *link_ref;
/* other state */
uint16_t link_mtu;
buf_t *sendbuf;
buf_t *recvbuf;
g_event_t *tap_fd_cb_ref;
g_event_handler_cb_t tap_fd_cb;
int tap_fd; // the tun/tap fd
struct ifreq *ifr;
char real_hwaddr[HWADDR_SIZE];
char noarp_hwaddr[HWADDR_SIZE];
char ifaddr[IPADDR_SIZE];
char ifaddr_bogus[IPADDR_SIZE];
char ifaddr_dotted[20];
int bogus_on;
int arp_on;
int raw_sd;
char *pkt_buf;
int pkt_len;
} ipconn_state_t;
int open_tap(ipconn_state_t *ipc);
int close_tap(ipconn_state_t *ipc);
int notify_rw(void *data, int fd, int fusd_condition, g_event_t *event);
int open_link(ipconn_state_t *ipc);
int link_pkt_rcvd(lu_context_t *link, link_pkt_t *link_pkt, int datalen);
int link_pkt_writable(lu_context_t *link);
int send_eth_pkt_over_link_dev(ipconn_state_t *ipc,
char *pktbuf, uint16_t len);
int send_arp_reply(ipconn_state_t *ipc, struct ether_header *r_ethhdr,
int bogus);
int create_raw_socket(ipconn_state_t *ipc);
int create_packet_socket();
int handle_arp_packet(ipconn_state_t *ipc, struct ether_header *ethhdr,
char *data, int datalen);
int handle_ip_packet(ipconn_state_t *ipc, struct ether_header *ethhdr,
char *data, int datalen);
struct ether_header *generate_arp_reply(ipconn_state_t *ipc,
struct ether_header *request_ethhdr, int bogus);
int fwd_data_to_tap(ipconn_state_t *ipc, char *data, int length);
char *create_bogus_ip_from_real_ip(char *ip);
char *get_hwaddr_from_ip(char *ip);
int fake_arp(ipconn_state_t *ipc, struct ether_header *ethhdr);
int check_ip(ipconn_state_t *ipc, eth_arp_t *etharp);
int check_hwaddr_of_eth_header(ipconn_state_t *ipc,
struct ether_header *ethhdr);
int check_eth_bcast_addr(char *dhost);
/*****************************************************************/
/* Callback when we are asked (by emrun) to shut down */
static void ipconn_shutdown(void *data)
{
ipconn_state_t *ipc = (ipconn_state_t *)data;
close_tap(ipc);
elog(LOG_DEBUG(0), "shutting down IP connector");
exit(0);
}
void usage(void)
{
fprintf(stderr,
"ipconn release: %s\n"
"\n"
"usage: ipconn [--uses <link_name>] [--provides <ethernet interface name>]"
" --bogus --arp\n"
"\n"
" -b: turns on 'bogus' mode. In this mode, interfaces have a \n"
" normal IP address as well as a 'bogus' one. The bogus\n"
" address is 10.2.x.x while the normal address is 10.1.x.x\n"
" Interfaces use the bogus address to fool the kernel into\n"
" thinking that packets are arriving from some other machine\n"
" NOTE: when running in simulation mode, bogus mode is ON\n"
" -a: turns on ARP mode. In this mode, ARP packets are sent\n"
" and received normally through the link interfaces\n."
" If ARP mode is OFF, arp packets are faked and do not\n"
" get sent over the link\n. Default is OFF\n"
"\n"
,CVSTAG
);
exit(1);
}
int main(int argc, char *argv[])
{
ipconn_state_t *ipc = NULL;
int help=0;
buf_t *if_name;
emrun_opts_t emrun_opts = {
shutdown: ipconn_shutdown,
data: ipc,
};
ipc = malloc(sizeof(ipconn_state_t));
memset(ipc, 0, sizeof(ipconn_state_t));
emrun_opts.data = ipc;
/** generic init **/
misc_init(&argc, argv, CVSTAG);
// the default name is motenic+node_id
// this enables ipconn to run in sim_mode
if_name = buf_new();
bufprintf(if_name, "motenic%d", my_node_id);
/********** Parse command-line arguments ************/
ipc->uses_name = link_parse_uses(&argc, argv, "mote0");
ipc->provides_name = link_parse_provides(&argc, argv, if_name->buf);
ipc->bogus_on = misc_parse_out_switch(&argc, argv, "bogus", 'b');
ipc->arp_on = misc_parse_out_switch(&argc, argv, "arp", 'a');
help = misc_parse_out_switch(&argc, argv, "help", 'h');
if (help==1) {
usage();
}
if (ipc->arp_on) {
elog(LOG_DEBUG(0), "ARP mode ON");
}
if (ipc->bogus_on) {
elog(LOG_DEBUG(0), "Bogus mode ON");
}
// if we are in sim mode, bogus should be always on
if (in_sim) {
ipc->bogus_on = 1;
}
emrun_init(&emrun_opts);
// first open the underlying link dev
open_link(ipc);
// now open the tap
open_tap(ipc);
// create the raw socket
ipc->raw_sd = create_raw_socket(ipc);
//ipc->raw_sd = create_packet_socket();
// start the beer flow!
g_main();
elog(LOG_CRIT, "event loop exited unexpectedly!");
return 1;
}
int open_tap(ipconn_state_t *ipc)
{
struct ifreq ifr = {};
long owner = geteuid();
buf_t *buf;
int n=0;
int skfd=-1;
buf = buf_new();
if (ipc==NULL) {
elog(LOG_ERR, "NULL ipc pointer");
exit(1);
}
// modprobe tun
system("/sbin/modprobe tun");
if ((ipc->tap_fd = open("/dev/net/tun", O_RDWR)) < 0) {
elog(LOG_ERR, "Can't open /dev/net/tun: %m");
exit(1);
}
// set the interface request flags
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
// set the name
strncpy(ifr.ifr_name, ipc->provides_name, sizeof(ifr.ifr_name) - 1);
// set the interface---ioctl
if (ioctl(ipc->tap_fd, TUNSETIFF, (void *)&ifr) < 0) {
elog(LOG_ERR, "ioctl(TUNSETIFF) error: %m");
if (errno==EBUSY) {
// hmm, device exists and is busy...probably stale
// remove it and try again
elog(LOG_ERR, "Got EBUSY, closing and reopening");
close_tap(ipc);
if (ioctl(ipc->tap_fd, TUNSETIFF, (void *)&ifr) < 0) {
elog(LOG_ERR, "ioctl(TUNSETIFF) error: %m");
elog(LOG_ERR, "Reopen failed, giving up");
exit(1);
}
} else {
exit(1);
}
}
// set persistent mode---ioctl
if (ioctl(ipc->tap_fd, TUNSETPERSIST, 1) < 0) {
elog(LOG_ERR, "ioctl(TUNSETPERSIST) error: %m");
exit(1);
}
// set ownership---ioctl
if (ioctl(ipc->tap_fd, TUNSETOWNER, owner) < 0) {
elog(LOG_ERR, "ioctl(TUNSETOWNER) error: %m");
exit(1);
}
if (ioctl(ipc->tap_fd, TUNSETNOCSUM, 1) < 0) {
elog(LOG_ERR, "ioctl(TUNSETNOCSUM) error: %m");
exit(1);
}
//fcntl(ipc->tap_fd, F_SETFL, O_NONBLOCK | O_ASYNC);
//open a basic socket
if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
elog(LOG_ERR, "socket() failed: %m");
exit(1);
}
if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) {
elog(LOG_ERR, "ioctl failed: %m");
exit(1);
} else {
char *hwaddr = ifr.ifr_hwaddr.sa_data;
elog(LOG_DEBUG(0), "Interface's HW address is %2.2x:%2.2x:%2.2x:%2.2x"
":%2.2x:%2.2x.",
(uint8_t)hwaddr[0], (uint8_t)hwaddr[1], (uint8_t)hwaddr[2],
(uint8_t)hwaddr[3], (uint8_t)hwaddr[4], (uint8_t)hwaddr[5]);
memcpy(ipc->real_hwaddr, hwaddr, HWADDR_SIZE);
}
// Register the FD with glib
ipc->tap_fd_cb = notify_rw;
if (g_event_add(ipc->tap_fd, FUSD_NOTIFY_INPUT /*| FUSD_NOTIFY_OUTPUT*/,
ipc->tap_fd_cb, ipc, NULL, &ipc->tap_fd_cb_ref) < 0) {
elog(LOG_ERR, "Cannot add event: %m");
exit(1);
}
// system is LAME but I was unsuccessful in doing it the 'right' way i.e.
// by copying ifconfig functionality, so...
bufprintf(buf, "/sbin/ifconfig %s arp %d.%d.%d.%d netmask 255.0.0.0 "
"broadcast 10.255.255.255 mtu %d up\n",
ifr.ifr_name,
IP_FIRST_OCTET,
IP_SECOND_OCTET,
(uint8_t)(((ipc->link_if_id >> 8) & 0xFF)),
(uint8_t)(((ipc->link_if_id) & 0xFF)),
// subtract 14, the ethernet header size, from the mtu since
// what we link to is an 'ethernet' device
(uint16_t)(((ipc->link_mtu-14) & 0xFFFF)));
//(uint8_t)(((ipc->link_if_id >> 8) & 0xFF)));
n=system(buf->buf);
if (n==0) {
elog(LOG_DEBUG(0), "Successufully configured TAP interface '%s' "
"with IP addr %d.%d.%d.%d and MTU %d",
ifr.ifr_name,
IP_FIRST_OCTET,
IP_SECOND_OCTET,
(uint8_t)(((ipc->link_if_id >> 8) & 0xFF)),
(uint8_t)(((ipc->link_if_id) & 0xFF)),
(uint16_t)(((ipc->link_mtu-14) & 0xFFFF)));
} else {
elog(LOG_ERR, "Couldn't create interface; "
"make sure you're running as root");
exit(1);
}
if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) {
elog(LOG_ERR, "ioctl failed: %m");
exit(1);
} else {
memcpy(ipc->ifaddr, &ifr.ifr_addr.sa_data[2], IPADDR_SIZE);
elog(LOG_NOTICE, "Interface normal IP address is %d.%d.%d.%d",
ipc->ifaddr[0], ipc->ifaddr[1], ipc->ifaddr[2], ipc->ifaddr[3]);
// POLICY NOTE: The bogus address is the ip address that we have with the
// only difference that we add '1' to the second octet
// so if an interface has 10.1.0.1 as its normal address, its bogus
// address will be 10.2.0.1
memcpy(ipc->ifaddr_bogus, ipc->ifaddr, IPADDR_SIZE);
if (ipc->bogus_on) {
ipc->ifaddr_bogus[1]=BOGUS_OCTET;
elog(LOG_NOTICE, "Interface bogus IP address is %d.%d.%d.%d",
ipc->ifaddr_bogus[0], ipc->ifaddr_bogus[1],
ipc->ifaddr_bogus[2], ipc->ifaddr_bogus[3]);
sprintf(ipc->ifaddr_dotted, "%d.%d.%d.%d",
ipc->ifaddr[0], ipc->ifaddr[1], ipc->ifaddr[2], ipc->ifaddr[3]);
}
}
// copy the ifr struct to the state struct
ipc->ifr = malloc(sizeof(struct ifreq));
memcpy(ipc->ifr, &ifr, sizeof(struct ifreq));
// if ARP mode is OFF, set our noarp_hwaddr based on our IP
if (ipc->arp_on==0) {
// set the HWADDR
ipc->noarp_hwaddr[0] = 0x00;
ipc->noarp_hwaddr[1] = 0x01;
ipc->noarp_hwaddr[2] = 0x02;
ipc->noarp_hwaddr[3] = 0x03;
ipc->noarp_hwaddr[4] = ipc->ifaddr[2];
ipc->noarp_hwaddr[5] = ipc->ifaddr[3];
elog(LOG_NOTICE, "Interface's NOARP HW address is %2.2x:%2.2x:%2.2x:%2.2x"
":%2.2x:%2.2x.",
(uint8_t)ipc->noarp_hwaddr[0], (uint8_t)ipc->noarp_hwaddr[1],
(uint8_t)ipc->noarp_hwaddr[2], (uint8_t)ipc->noarp_hwaddr[3],
(uint8_t)ipc->noarp_hwaddr[4], (uint8_t)ipc->noarp_hwaddr[5]);
}
buf_free(buf);
return 0;
}
int close_tap(ipconn_state_t *ipc)
{
if (ipc==NULL) {
elog(LOG_ERR, "NULL ipc pointer");
exit(1);
}
if (ioctl(ipc->tap_fd, TUNSETPERSIST, 0) < 0) {
elog(LOG_ERR, "ioctl(TUNSETPERSIST) error: %m");
exit(1);
}
return 0;
}
int fwd_data_to_tap(ipconn_state_t *ipc, char *data, int length)
{
int n=0;
if (data==NULL) {
elog(LOG_ERR, "NULL data pointer");
exit(1);
}
n=write(ipc->tap_fd, data, length);
if (n<0) {
elog(LOG_ERR, "Can't send packet to TAP interface: %m");
} else {
elog(LOG_DEBUG(0), "Sent %d bytes to TAP interface", n);
elog_raw(LOG_DEBUG(2), data, length);
}
return n;
}
int fake_arp(ipconn_state_t *ipc, struct ether_header *ethhdr)
{
struct ether_header *fake_ehdr=NULL;
int bogus=0;
if (ipc==NULL || ethhdr == NULL) {
elog(LOG_ERR, "NULL pointer(s)");
exit(1);
}
fake_ehdr = generate_arp_reply(ipc, ethhdr, bogus);
// forward to tap. This is an ARP REPLY pkt so we know its length
fwd_data_to_tap(ipc, (char *)fake_ehdr, ARP_PKTLEN);
// free the generated ethernet frame
free(fake_ehdr);
return 1;
}
int notify_rw(void *data, int fd, int fusd_condition, g_event_t *event)
{
ipconn_state_t *ipc = (ipconn_state_t *)data;
char readbuf[4096]={0}; // ideally this should be the MTU of the
// interface
struct ether_header *ethhdr;
int n=0;
switch (fusd_condition) {
case FUSD_NOTIFY_INPUT:
n = read(fd, readbuf, sizeof(readbuf));
elog(LOG_DEBUG(0), "Read %d bytes from TAP fd %d", n, fd);
elog_raw(LOG_DEBUG(2), readbuf, n);
ethhdr = (struct ether_header *)readbuf;
if (ntohs(ethhdr->ether_type)==ETHERTYPE_ARP) {
elog(LOG_DEBUG(0), "GOT ARP, read size is %d", n);
// DONT send the ARP if arps are faked
if (ipc->arp_on==0) {
elog(LOG_DEBUG(0), "ARPs are faked, packet not sent");
// create the arp reply
fake_arp(ipc, ethhdr);
return EVENT_RENEW;
}
}
if (ipc->pkt_buf==NULL) {
int m=0;
// pkt buf is empty, malloc n bytes and copy the packet in it
ipc->pkt_buf = malloc(n);
ipc->pkt_len = n;
memcpy(ipc->pkt_buf, readbuf, n);
// now try to send
m=send_eth_pkt_over_link_dev(ipc, ipc->pkt_buf, ipc->pkt_len);
if (m<0) {
elog(LOG_ERR, "Can't send packet over link dev: %m");
}
} else {
elog(LOG_ERR, "IPC pkt buffer NOT NULL!");
}
break;
default:
elog(LOG_ERR, "Got unhandled FUSD CONDITION %d", fusd_condition);
return EVENT_DONE;
break;
}
return EVENT_RENEW;
}
char *create_bogus_ip_from_real_ip(char *ip)
{
char *bogus = malloc(IPADDR_SIZE);
memcpy(bogus, ip, IPADDR_SIZE);
bogus[BOGUS_OCTET_OFFSET] = BOGUS_OCTET;
return bogus;
}
char *get_hwaddr_from_ip(char *ip)
{
char *hwaddr = malloc(HWADDR_SIZE);
// array offsets, yea i know it's nasty...
hwaddr[0] = 0x00;
hwaddr[1] = 0x01;
hwaddr[2] = 0x02;
hwaddr[3] = 0x03;
hwaddr[4] = ip[2];
hwaddr[5] = ip[3];
return hwaddr;
}
char *create_ip_from_hwaddr(char *hwaddr)
{
char *ip = malloc(IPADDR_SIZE);
ip[0] = IP_FIRST_OCTET;
ip[1] = IP_SECOND_OCTET;
ip[2] = hwaddr[4];
ip[3] = hwaddr[5];
return ip;
}
struct ether_header *generate_arp_reply(ipconn_state_t *ipc,
struct ether_header *request_ethhdr, int bogus)
{
char *sendbuf=NULL;
struct ether_header *reply_ethhdr=NULL;
struct arphdr *request_ahdr=NULL;
struct arphdr *reply_ahdr=NULL;
eth_arp_t *request_etharp=NULL;
eth_arp_t *reply_etharp=NULL;
char *hwaddr=NULL;
char *ip=NULL;
char *bogus_ip=NULL;
/*
int pktlen = sizeof(struct ether_header) + sizeof(struct arphdr)
+ sizeof(eth_arp_t);
*/
int pktlen = ARP_PKTLEN;
// allocate mem for our sendbuf
sendbuf = malloc(pktlen);
memset(sendbuf, 0, pktlen);
// cast the sending headers
request_ahdr = (struct arphdr *)((char *)request_ethhdr +
sizeof(struct ether_header));
request_etharp= (eth_arp_t *)((char *)request_ethhdr +
sizeof(struct ether_header) + sizeof(struct arphdr));
// cast the reply headers
reply_ethhdr = (struct ether_header *)(sendbuf);
reply_ahdr = (struct arphdr *)(sendbuf + sizeof(struct ether_header));
reply_etharp = (eth_arp_t *)(sendbuf + sizeof(struct ether_header)
+ sizeof(struct arphdr));
// assign the hwaddr pointer to the right address, depending on
// whether we use real arp or not
if (ipc->arp_on==1) {
hwaddr = ipc->real_hwaddr;
ip = ipc->ifaddr;
bogus_ip = ipc->ifaddr_bogus;
} else {
ip = (char *)request_etharp->ar_tip;
bogus_ip = create_bogus_ip_from_real_ip(ip);
hwaddr = get_hwaddr_from_ip(ip);
}
// construct the ethernet header
// the dhost of the reply header is our hw_addr (as created by arp)
memcpy(reply_ethhdr->ether_dhost, request_ethhdr->ether_shost, HWADDR_SIZE);
// the shost of the reply header is either our real hwaddr OR
// the noarp_hwaddr of the node that has the requested ip address
memcpy(reply_ethhdr->ether_shost, hwaddr, HWADDR_SIZE);
reply_ethhdr->ether_type = request_ethhdr->ether_type;
// construct the arp header
memcpy(reply_ahdr, request_ahdr, sizeof(struct arphdr));
reply_ahdr->ar_op = htons(ARPOP_REPLY);
//construct the arp payload--eth_arp_t
// Sender MAC address is either our hwaddr or the 'mapped' one
memcpy(reply_etharp->ar_sha, hwaddr, HWADDR_SIZE);
// Sender IP address -> our ip addr
if (bogus) {
memcpy(reply_etharp->ar_sip, bogus_ip, IPADDR_SIZE);
} else {
memcpy(reply_etharp->ar_sip, ip, IPADDR_SIZE);
}
// Target MAC address -> the mac address of the requestor
memcpy(reply_etharp->ar_tha, request_etharp->ar_sha, HWADDR_SIZE);
// Target IP address -> the IP address of the requestor
memcpy(reply_etharp->ar_tip, request_etharp->ar_sip, IPADDR_SIZE);
// free hwaddr when arps are off since get_hwaddr_from_ip mallocs
if (ipc->arp_on==0) {
free(hwaddr);
free(bogus_ip);
}
return (struct ether_header *)sendbuf;
}
int open_link(ipconn_state_t *ipc)
{
lu_opts_t link_opts = {
opts: {
name: ipc->uses_name,
q_opts: {
outq_len: 10,
},
data: (void *)ipc,
},
receive: link_pkt_rcvd,
writable: link_pkt_writable,
//status_notify: // will add this when I understand what it
// does:)
};
if (ipc->uses_name == NULL) {
elog(LOG_ERR, "NULL link name");
exit(1);
}
if (lu_open(&link_opts, &(ipc->link_ref)) < 0) {
elog(LOG_CRIT, "Can't open %s: %m", link_name(&link_opts.opts, NULL));
exit(1);
} else {
elog(LOG_DEBUG(0), "Using link device %s", lu_name(ipc->link_ref, NULL));
}
if (lu_get_if_id(ipc->link_ref, &ipc->link_if_id) < 0) {
elog(LOG_ERR, "Unable to get interface id for link %s",
lu_name(ipc->link_ref, NULL));
exit(1);
}
if (lu_get_mtu(ipc->link_ref, &ipc->link_mtu) < 0) {
elog(LOG_ERR, "Unable to get mtu for link %s",
lu_name(ipc->link_ref, NULL));
exit(1);
}
return 0;
}
unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* * * Our algorithm is simple, using a 32 bit accumulator (sum), we add
* * * sequential 16 bit words to it, and at the end, fold back all the
* * * carry bits from the top 16 bits into the lower 16 bits.
* * */
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
int check_eth_bcast_addr(char *dhost)
{
int i=0;
if (dhost==NULL) {
elog(LOG_ERR, "NULL dhost pointer!");
exit(1);
}
for (i=0; i<HWADDR_SIZE; i++) {
if ((uint8_t)dhost[i] != 0xFF) {
return 0;
}
}
// if we come here, the check has succeeded
return 1;
}
int check_hwaddr_of_eth_header(ipconn_state_t *ipc,
struct ether_header *ethhdr)
{
int mismatch=0;
int i=0;
if (ipc==NULL || ethhdr==NULL) {
elog(LOG_ERR, "NULL pointer(s)");
exit(1);
}
if (ipc->arp_on==1) {
elog(LOG_ERR, "ARPs are on, ignoring eth header");
return 1;
}
// check ether_dhost with our noarp_hwaddr address
for (i=0; i<HWADDR_SIZE; i++) {
if (ethhdr->ether_dhost[i] != ipc->noarp_hwaddr[i]) {
// found mismatch, set flag and break
mismatch=-1;
break;
}
}
if (mismatch==0) {
// addresses match! we now need to copy our REAL hwaddr
// to ether_dhost
memcpy(ethhdr->ether_dhost, ipc->real_hwaddr, HWADDR_SIZE);
}
return mismatch;
}
int link_pkt_rcvd(lu_context_t *link, link_pkt_t *link_pkt, int datalen)
{
ipconn_state_t *ipc = (ipconn_state_t *)lu_data(link);
struct ether_header *ethhdr;
struct iphdr *iphdr;
int n=0;
int process_result=-1;
struct sockaddr_in sin; // for the raw socket
if (link_pkt == NULL) {
elog(LOG_ERR, "NULL link_pkt");
exit(1);
}
if ((link_pkt->type != PKT_TYPE_ETH)
&& (link_pkt->type != PKT_TYPE_IP)) {
elog(LOG_WARNING, "Got a non-IP/ETH type packet!");
goto done;
}
ethhdr = (struct ether_header *)(link_pkt->data);
elog(LOG_DEBUG(0), "Ethernet header type is %hx",
ntohs(ethhdr->ether_type));
// if arps are off, check the hw address, extract the ip,
// make sure it matches our own and then process the packet
// if it doesn, we drop it
// or we can still let it go up but the kernel will throw it away
if (ipc->arp_on==0) {
if ((link_pkt->dst.id != LINK_BROADCAST) &&
(check_hwaddr_of_eth_header(ipc, ethhdr) < 0)) {
// mismatch. drop the packet
goto done;
}
}
// dispatch to different handlers
switch (ntohs(ethhdr->ether_type)) {
case ETHERTYPE_ARP:
process_result = handle_arp_packet(ipc, ethhdr, (char *)link_pkt->data,
datalen);
break;
case ETHERTYPE_IP:
process_result = handle_ip_packet(ipc, ethhdr, (char *)link_pkt->data,
datalen);
break;
default:
elog(LOG_DEBUG(0), "Got unhandled ETH type %d",
ntohs(ethhdr->ether_type));
// send it to the tap interface, maybe the stack will know what
// to do with it
process_result = PKT_FWD_TO_TAP;
break;
}
// decide what to do with the packet based on the return codes of the
// handlers
switch (process_result) {
case PKT_FWD_TO_TAP:
n=fwd_data_to_tap(ipc, (char *)link_pkt->data, datalen);
break;
case PKT_FWD_TO_RAW:
iphdr = (struct iphdr *)(link_pkt->data
+ sizeof(struct ether_header));
n = sendto(ipc->raw_sd, iphdr, datalen - sizeof(struct ether_header),
0x0, (struct sockaddr *)&sin, sizeof(sin));
if (n<0) {
elog(LOG_ERR, "Can't send to raw socket: %m");
} else {
elog(LOG_DEBUG(0), "Sent %d bytes to raw socket", n);
//goto done;
}
break;
case PKT_FWD_TO_TAP_AND_RAW:
iphdr = (struct iphdr *)(link_pkt->data
+ sizeof(struct ether_header));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = iphdr->daddr; // the dest. addr has already been
// changed at this point
n = sendto(ipc->raw_sd, iphdr, datalen - sizeof(struct ether_header),
0x0, (struct sockaddr *)&sin, sizeof(sin));
if (n<0) {
elog(LOG_ERR, "Can't send to raw socket: %m");
} else {
elog(LOG_DEBUG(0), "Sent %d bytes to raw socket", n);
//goto done;
}
n=fwd_data_to_tap(ipc, (char *)link_pkt->data, datalen);
break;
case PKT_DONT_FWD:
default:
elog(LOG_DEBUG(0), "Dropping ethernet packet");
break;
}
done:
free(link_pkt);
return EVENT_RENEW;
}
int send_eth_pkt_over_link_dev(ipconn_state_t *ipc,
char *pktbuf, uint16_t len)
{
link_pkt_t *pkt = NULL;
char *buf = NULL;
struct ether_header *ethhdr=NULL;
int lpkt_len=sizeof(link_pkt_t) + len;
if (pktbuf==NULL || ipc==NULL) {
elog(LOG_ERR, "NULL pointer(s)");
exit(1);
}
buf = malloc(lpkt_len);
memset(buf, 0, lpkt_len);
pkt = (link_pkt_t *)buf;
// pkt type is ethernet
pkt->type = PKT_TYPE_ETH;
// cast pktbuf to an ethhdr to get the destination addr
ethhdr = (struct ether_header *)pktbuf;
// check to see if this is an ethernet broadcast
// do some munging to get the right link id
if (check_eth_bcast_addr((char *)ethhdr->ether_dhost)==1) {
// this is an ethernet broadcast pkt, set dst_id to LINK_BCAST
pkt->dst.id = LINK_BROADCAST;
} else {
if (ipc->arp_on==0) {
// for now, we can only use unicasts when arps are off
// otherwise, we have no idea where to send the packet...
// we sort of need a resolution between emstar link ids
// and hw addresses
pkt->dst.id = ((ethhdr->ether_dhost[4] << 8) +
ethhdr->ether_dhost[5]) & 0xFFFF;
} else {
pkt->dst.id = LINK_BROADCAST;
}
}
// copy the payload
memcpy(pkt->data, pktbuf, len);
// go
if (lu_send(ipc->link_ref, pkt, len) < 0) {
// see if we have an eagain; if so, we enable the writable callback,
// disable the TAP readable callback and try again
if (errno==EAGAIN) {
// set writable on link
lu_writable_cb_set_enable(ipc->link_ref, 1);
// unset readable on tap
g_event_set_enable(ipc->tap_fd_cb_ref, 0);
return 0;
}
elog(LOG_ERR, "Can't send on %s: %m", lu_name(ipc->link_ref, NULL));
} else {
elog(LOG_DEBUG(0), "Sending ethernet frame over link %s, dst=%s",
lu_name(ipc->link_ref, NULL), print_if_id(pkt->dst.id));
}
// free the ip connector's buffered packet
if (ipc->pkt_buf != NULL) {
free(ipc->pkt_buf);
ipc->pkt_buf = NULL;
}
// unset writable on link
lu_writable_cb_set_enable(ipc->link_ref, 0);
// set readable on tap
g_event_set_enable(ipc->tap_fd_cb_ref, 1);
free(pkt);
return 0;
}
int link_pkt_writable(lu_context_t *link)
{
ipconn_state_t *ipc = (ipconn_state_t *)lu_data(link);
int n=0;
if(ipc->pkt_buf==NULL || ipc->pkt_len==0) {
elog(LOG_ERR, "NULL pkt buf or 0-length pkt");
exit(1);
}
n=send_eth_pkt_over_link_dev(ipc, ipc->pkt_buf, ipc->pkt_len);
if (n<0) {
elog(LOG_ERR, "Can't send packet over link dev: %m");
}
return EVENT_RENEW;
}
int send_arp_reply(ipconn_state_t *ipc, struct ether_header *r_ethhdr,
int bogus)
{
int n=0;
struct ether_header *reply_ehdr=NULL;
reply_ehdr = generate_arp_reply(ipc, r_ethhdr, bogus);
if (reply_ehdr == NULL) {
elog(LOG_ERR, "NULL ARP reply header!");
exit(1);
}
if (ipc->pkt_buf == NULL) {
ipc->pkt_len = ARP_PKTLEN;
ipc->pkt_buf = malloc(ipc->pkt_len);
memcpy(ipc->pkt_buf, (char *)reply_ehdr, ARP_PKTLEN);
n=send_eth_pkt_over_link_dev(ipc, (char *)reply_ehdr, ARP_PKTLEN);
} else {
// pkt buf wasn't null...
elog(LOG_ERR, "IPC pkt buffer NOT NULL (arp pkt)");
}
// free the generated arp reply pkt!
free(reply_ehdr);
return n;
}
int create_raw_socket(ipconn_state_t *ipc)
{
int sd;
int on=1;
sd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
if (sd<0) {
elog(LOG_ERR, "Can't create raw socket: %m");
exit(1);
}
// we are including the IP header
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0) {
elog(LOG_ERR, "setsockopt(HDRINCL) failed: %m");
exit(1);
}
if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, ipc->provides_name,
strlen(ipc->provides_name)+1) < 0) {
// if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth1\0",
// strlen("eth0\0")) < 0) {
elog(LOG_ERR, "setsockopt(BIND) failed: %m");
exit(1);
} else {
elog(LOG_DEBUG(0), "Raw socket bound to device %s", ipc->provides_name);
}
return sd;
}
int create_packet_socket()
{
int sd;
sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
if (sd<0) {
elog(LOG_ERR, "Can't create packet socket: %m");
exit(1);
}
return sd;
}
int check_ip(ipconn_state_t *ipc, eth_arp_t *etharp)
{
int i=0;
int is_normal=0;
int is_bogus=0;
// this is probably too complicated and far from optimal, there must
// be a better way to do it...
for (i=0;i<IPADDR_SIZE;i++) {
if (etharp->ar_tip[i] != ipc->ifaddr[i]) {
is_normal = 0;
break;
} else {
is_normal = 1;
}
}
for (i=0;i<IPADDR_SIZE;i++) {
if (etharp->ar_tip[i] != ipc->ifaddr_bogus[i]) {
is_bogus=0;
break;
} else {
is_bogus=1;
}
}
if (is_normal) {
return ARP_NORMAL_IFACE;
}
if (is_bogus) {
return ARP_BOGUS_IFACE;
}
return ARP_NOT_MINE;
}
int handle_arp_packet(ipconn_state_t *ipc, struct ether_header *ethhdr,
char *data, int datalen)
{
struct arphdr *ahdr;
int n=0;
if (ipc==NULL || ethhdr == NULL || data == NULL) {
elog(LOG_ERR, "NULL pointer(s)");
exit(1);
}
if (ipc->arp_on==0) {
// hmm, arps are off but the handler was invoked!
// This is not good(tm)
elog(LOG_ERR, "ARPs are off but ARP packet handler was invoked!");
// don't send the packet to the kernel
return PKT_DONT_FWD;
}
// cast the packet into an arp header
ahdr = (struct arphdr *)(data + sizeof(struct ether_header));
if (htons(ahdr->ar_op) == ARPOP_REQUEST) {
eth_arp_t *etharp = (eth_arp_t *)(data
+ sizeof(struct ether_header)
+ sizeof(struct arphdr));
// we have an arp request
// We need to check if we own this IP
n = check_ip(ipc, etharp);
switch (n) {
case ARP_NORMAL_IFACE:
send_arp_reply(ipc, ethhdr, 0);
break;
case ARP_BOGUS_IFACE:
send_arp_reply(ipc, ethhdr, 1);
break;
case ARP_NOT_MINE:
default:
break;
}
// don't forward in either case
return PKT_DONT_FWD;
} else if (htons(ahdr->ar_op) == ARPOP_REPLY) {
elog(LOG_DEBUG(0), "Got ARPPOP_REPLY!");
// send this to the tap iface
return PKT_FWD_TO_TAP;
}
return PKT_DONT_FWD;
}
int handle_ip_packet(ipconn_state_t *ipc, struct ether_header *ethhdr,
char *data, int datalen)
{
struct iphdr *iphdr;
uint32_t check_octet;
uint32_t tmpaddr;
int send_to_raw=0;
if (ipc==NULL || ethhdr == NULL || data == NULL) {
elog(LOG_ERR, "NULL pointer(s)");
exit(1);
}
iphdr = (struct iphdr *)(data + sizeof(struct ether_header));
check_octet = (ntohl(iphdr->daddr) & CHECK_MASK);
switch (check_octet) {
case BOGUS_NET:
// destination address of the packet is bogus
// we need to turn it back to a normal address
if (ipc->bogus_on) {
// first we clear the network octet
tmpaddr = ntohl(iphdr->daddr) & (~CHECK_MASK);
// now we or the correct net octet
tmpaddr = tmpaddr | CORRECT_NET;
iphdr->daddr = htonl(tmpaddr);
// if this is a tcp packet, fix its checksum--it should be off by 1 bit
if (iphdr->protocol == IPPROTO_TCP) {
// ip header length is in WORDS which are 32-bit in size
uint8_t iphdr_len = iphdr->ihl * sizeof(uint32_t);
struct tcphdr *th;
th = (struct tcphdr *)(data + sizeof(struct ether_header) +
iphdr_len);
elog(LOG_DEBUG(0), "TCP packet detected, recomputing TCP chksum."
" Old = %hx, New = %hx", th->check, th->check+htons(1));
// this is an ugly hack but it's much much faster than recomputing
// the tcp checksum every time, especially on 1.5k packets
//
//
// NOTE: on this side we ADD htons(1) from the check because
// the source address is htons(1) less than it should be
//
th->check = th->check + htons(1);
}
// we need to send to the raw socket--raise the flag
send_to_raw=1;
}
break;
case CORRECT_NET:
// Destination address of the packet is correct
// if the packet is destined to us, we need to change its SOURCE
// address to be bogus
if (ipc->bogus_on) {
tmpaddr = ntohl(iphdr->saddr) & (~CHECK_MASK);
tmpaddr = tmpaddr | BOGUS_NET;
iphdr->saddr = htonl(tmpaddr);
}
// if this is a tcp packet, fix its checksum--it should be off by 1 bit
if (iphdr->protocol == IPPROTO_TCP) {
// ip header length is in WORDS which are 32-bit in size
uint8_t iphdr_len = iphdr->ihl * sizeof(uint32_t);
struct tcphdr *th;
th = (struct tcphdr *)(data + sizeof(struct ether_header) +
iphdr_len);
elog(LOG_DEBUG(0), "TCP packet detected, recomputing TCP chksum."
" Old = %hx, New = %hx", th->check, th->check-htons(1));
// this is an ugly hack but it's much much faster than recomputing
// the tcp checksum every time, especially on 1.5k packets
//
//
// NOTE: on this side we SUBTRACT htons(1) from the check because
// the source address is htons(1) greater than it should be
//
th->check = th->check - htons(1);
}
break;
default:
break;
}
// fix the checksum
elog(LOG_DEBUG(0), "Original checksum = 0x%hx", htons(iphdr->check));
iphdr->check = (0x0);
iphdr->check = in_cksum((uint16_t *)iphdr, sizeof(struct iphdr));
elog(LOG_DEBUG(0), "New checksum = 0x%hx", htons(iphdr->check));
if (send_to_raw) {
return PKT_FWD_TO_TAP_AND_RAW;
} else {
return PKT_FWD_TO_TAP;
}
}
See more files for this project here