/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
* 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.
* * 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.
* * Neither the name of olsr.org, olsrd 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 COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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.
*
* Visit http://www.olsr.org for more information.
*
* If you find this software useful feel free to make a donation
* to the project. For more information see the website or contact
* the copyright holders.
*
* $Id: parser.c,v 1.35 2007/05/08 23:34:52 bernd67 Exp $
*/
#include "parser.h"
#include "defs.h"
#include "process_package.h"
#include "mantissa.h"
#include "hysteresis.h"
#include "duplicate_set.h"
#include "mid_set.h"
#include "olsr.h"
#include "rebuild_packet.h"
#include "net_os.h"
#include "log.h"
#include "print_packet.h"
#ifdef WIN32
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#undef errno
#define errno WSAGetLastError()
#undef strerror
#define strerror(x) StrError(x)
#endif
/* Sven-Ola: On very slow devices used in huge networks
* the amount of lq_tc messages is so high, that the
* recv() loop never ends. This is a small hack to end
* the loop in this cases
*/
unsigned int cpu_overload_exit = 0;
struct parse_function_entry *parse_functions;
static char inbuf[MAXMESSAGESIZE+1];
static olsr_bool disp_pack_in = OLSR_FALSE;
void
parser_set_disp_pack_in(olsr_bool val)
{
disp_pack_in = val;
}
/**
*Initialize the parser.
*
*@return nada
*/
void
olsr_init_parser(void)
{
OLSR_PRINTF(3, "Initializing parser...\n");
/* Initialize the packet functions */
olsr_init_package_process();
}
void
olsr_parser_add_function(void (*function)(union olsr_message *, struct interface *, union olsr_ip_addr *), olsr_u32_t type, int forwarding)
{
struct parse_function_entry *new_entry;
OLSR_PRINTF(3, "Parser: registering event for type %d\n", type);
new_entry = olsr_malloc(sizeof(struct parse_function_entry), "Register parse function");
new_entry->function = function;
new_entry->type = type;
new_entry->caller_forwarding = forwarding;
/* Queue */
new_entry->next = parse_functions;
parse_functions = new_entry;
OLSR_PRINTF(3, "Register parse function: Added function for type %d\n", type);
}
int
olsr_parser_remove_function(void (*function)(union olsr_message *, struct interface *, union olsr_ip_addr *), olsr_u32_t type, int forwarding)
{
struct parse_function_entry *entry, *prev;
entry = parse_functions;
prev = NULL;
while(entry)
{
if((entry->function == function) &&
(entry->type == type) &&
(entry->caller_forwarding == forwarding))
{
if(entry == parse_functions)
{
parse_functions = entry->next;
}
else
{
prev->next = entry->next;
}
free(entry);
return 1;
}
prev = entry;
entry = entry->next;
}
return 0;
}
/**
*Process a newly received OLSR packet. Checks the type
*and to the neccessary convertions and call the
*corresponding functions to handle the information.
*@param from the sockaddr struct describing the sender
*@param olsr the olsr struct containing the message
*@param size the size of the message
*@return nada
*/
void
parse_packet(struct olsr *olsr, int size, struct interface *in_if, union olsr_ip_addr *from_addr)
{
union olsr_message *m = (union olsr_message *)olsr->olsr_msg;
struct unknown_message unkpacket;
int count;
int msgsize;
int processed;
struct parse_function_entry *entry;
count = size - ((char *)m - (char *)olsr);
if (count < MIN_PACKET_SIZE(olsr_cnf->ip_version))
return;
if (ntohs(olsr->olsr_packlen) != size)
{
OLSR_PRINTF(1, "Size error detected in received packet.\nRecieved %d, in packet %d\n", size, ntohs(olsr->olsr_packlen));
olsr_syslog(OLSR_LOG_ERR, " packet length error in packet received from %s!",
olsr_ip_to_string(from_addr));
return;
}
//printf("Message from %s\n\n", olsr_ip_to_string(from_addr));
/* Display packet */
if(disp_pack_in)
print_olsr_serialized_packet(stdout, (union olsr_packet *)olsr, size, from_addr);
if(olsr_cnf->ip_version == AF_INET)
msgsize = ntohs(m->v4.olsr_msgsize);
else
msgsize = ntohs(m->v6.olsr_msgsize);
/*
* Hysteresis update - for every OLSR package
*/
if(olsr_cnf->use_hysteresis)
{
if(olsr_cnf->ip_version == AF_INET)
{
/* IPv4 */
update_hysteresis_incoming(from_addr,
in_if,
ntohs(olsr->olsr_seqno));
}
else
{
/* IPv6 */
update_hysteresis_incoming(from_addr,
in_if,
ntohs(olsr->olsr_seqno));
}
}
if (olsr_cnf->lq_level > 0)
{
olsr_update_packet_loss(from_addr, in_if,
ntohs(olsr->olsr_seqno));
}
for ( ; count > 0; m = (union olsr_message *)((char *)m + (msgsize)))
{
processed = 0;
if (count < MIN_PACKET_SIZE(olsr_cnf->ip_version))
break;
if(olsr_cnf->ip_version == AF_INET)
msgsize = ntohs(m->v4.olsr_msgsize);
else
msgsize = ntohs(m->v6.olsr_msgsize);
count -= msgsize;
/* Check size of message */
if(count < 0)
{
OLSR_PRINTF(1, "packet length error in packet received from %s!",
olsr_ip_to_string(from_addr));
olsr_syslog(OLSR_LOG_ERR, " packet length error in packet received from %s!",
olsr_ip_to_string(from_addr));
break;
}
/* Treat TTL hopcnt */
if(olsr_cnf->ip_version == AF_INET)
{
/* IPv4 */
if (m->v4.ttl <= 0 && olsr_cnf->lq_fish == 0)
{
OLSR_PRINTF(2, "Dropping packet type %d from neigh %s with TTL 0\n",
m->v4.olsr_msgtype,
olsr_ip_to_string(from_addr));
continue;
}
}
else
{
/* IPv6 */
if (m->v6.ttl <= 0 && olsr_cnf->lq_fish == 0)
{
OLSR_PRINTF(2, "Dropping packet type %d from %s with TTL 0\n",
m->v4.olsr_msgtype,
olsr_ip_to_string(from_addr));
continue;
}
}
/*RFC 3626 section 3.4:
* 2 If the time to live of the message is less than or equal to
* '0' (zero), or if the message was sent by the receiving node
* (i.e., the Originator Address of the message is the main
* address of the receiving node): the message MUST silently be
* dropped.
*/
/* Should be the same for IPv4 and IPv6 */
if(COMP_IP(&m->v4.originator, &olsr_cnf->main_addr))
{
#ifdef DEBUG
OLSR_PRINTF(3, "Not processing message originating from us!\n");
#endif
continue;
}
//printf("MESSAGETYPE: %d\n", m->v4.olsr_msgtype);
entry = parse_functions;
while(entry)
{
/* Should be the same for IPv4 and IPv6 */
/* Promiscuous or exact match */
if((entry->type == PROMISCUOUS) ||
(entry->type == m->v4.olsr_msgtype))
{
entry->function(m, in_if, from_addr);
if(entry->caller_forwarding)
processed = 1;
}
entry = entry->next;
}
/* UNKNOWN PACKETTYPE */
if(processed == 0)
{
unk_chgestruct(&unkpacket, m);
OLSR_PRINTF(3, "Unknown type: %d, size %d, from %s\n",
m->v4.olsr_msgtype,
size,
olsr_ip_to_string(&unkpacket.originator));
/* Forward message */
if(!COMP_IP(&unkpacket.originator, &olsr_cnf->main_addr))
{
/* Forward */
olsr_forward_message(m,
&unkpacket.originator,
unkpacket.seqno,
in_if,
from_addr);
}
}
} /* for olsr_msg */
}
/**
*Processing OLSR data from socket. Reading data, setting
*wich interface recieved the message, Sends IPC(if used)
*and passes the packet on to parse_packet().
*
*@param fd the filedescriptor that data should be read from.
*@return nada
*/
void
olsr_input(int fd)
{
/* sockaddr_in6 is bigger than sockaddr !!!! */
struct sockaddr_storage from;
socklen_t fromlen;
int cc;
struct interface *olsr_in_if;
union olsr_ip_addr from_addr;
cpu_overload_exit = 0;
for (;;)
{
if (32 < ++cpu_overload_exit)
{
OLSR_PRINTF(1, "CPU overload detected, ending olsr_input() loop\n");
break;
}
fromlen = sizeof(struct sockaddr_storage);
cc = olsr_recvfrom(fd,
inbuf,
sizeof (inbuf),
0,
(struct sockaddr *)&from,
&fromlen);
if (cc <= 0)
{
if (cc < 0 && errno != EWOULDBLOCK)
{
OLSR_PRINTF(1, "error recvfrom: %s", strerror(errno));
olsr_syslog(OLSR_LOG_ERR, "error recvfrom: %m");
}
break;
}
if(olsr_cnf->ip_version == AF_INET)
{
/* IPv4 sender address */
COPY_IP(&from_addr, &((struct sockaddr_in *)&from)->sin_addr.s_addr);
}
else
{
/* IPv6 sender address */
COPY_IP(&from_addr, &((struct sockaddr_in6 *)&from)->sin6_addr);
}
#ifdef DEBUG
OLSR_PRINTF(5, "Recieved a packet from %s\n", olsr_ip_to_string((union olsr_ip_addr *)&((struct sockaddr_in *)&from)->sin_addr.s_addr));
#endif
if ((olsr_cnf->ip_version == AF_INET) && (fromlen != sizeof (struct sockaddr_in)))
break;
else if ((olsr_cnf->ip_version == AF_INET6) && (fromlen != sizeof (struct sockaddr_in6)))
break;
/* are we talking to ourselves? */
if(if_ifwithaddr(&from_addr) != NULL)
return;
if((olsr_in_if = if_ifwithsock(fd)) == NULL)
{
OLSR_PRINTF(1, "Could not find input interface for message from %s size %d\n",
olsr_ip_to_string(&from_addr),
cc);
olsr_syslog(OLSR_LOG_ERR, "Could not find input interface for message from %s size %d\n",
olsr_ip_to_string(&from_addr),
cc);
return ;
}
/*
* &from - sender
* &inbuf.olsr
* cc - bytes read
*/
parse_packet((struct olsr *)inbuf, cc, olsr_in_if, &from_addr);
}
}
/**
*Processing OLSR data from socket. Reading data, setting
*wich interface recieved the message, Sends IPC(if used)
*and passes the packet on to parse_packet().
*
*@param fd the filedescriptor that data should be read from.
*@return nada
*/
void
olsr_input_hostemu(int fd)
{
/* sockaddr_in6 is bigger than sockaddr !!!! */
struct sockaddr_storage from;
socklen_t fromlen;
int cc;
struct interface *olsr_in_if;
union olsr_ip_addr from_addr;
olsr_u16_t pcklen;
/* Host emulator receives IP address first to emulate
direct link */
if((cc = recv(fd, from_addr.v6.s6_addr, olsr_cnf->ipsize, 0)) != (int)olsr_cnf->ipsize)
{
fprintf(stderr, "Error receiving host-client IP hook(%d) %s!\n", cc, strerror(errno));
COPY_IP(&from_addr, &((struct olsr *)inbuf)->olsr_msg->originator);
}
/* are we talking to ourselves? */
if(if_ifwithaddr(&from_addr) != NULL)
return;
/* Extract size */
if((cc = recv(fd, (void *)&pcklen, 2, MSG_PEEK)) != 2) /* Win needs a cast */
{
if(cc <= 0)
{
fprintf(stderr, "Lost olsr_switch connection - exit!\n");
olsr_exit(__func__, EXIT_FAILURE);
}
fprintf(stderr, "[hust-emu] error extracting size(%d) %s!\n", cc, strerror(errno));
return;
}
else
{
pcklen = ntohs(pcklen);
}
fromlen = sizeof(struct sockaddr_storage);
cc = olsr_recvfrom(fd,
inbuf,
pcklen,
0,
(struct sockaddr *)&from,
&fromlen);
if (cc <= 0)
{
if (cc < 0 && errno != EWOULDBLOCK)
{
const char * const err_msg = strerror(errno);
OLSR_PRINTF(1, "error recvfrom: %s", err_msg);
olsr_syslog(OLSR_LOG_ERR, "error recvfrom: %s", err_msg);
}
return;
}
if(cc != pcklen)
{
printf("Could not read whole packet(size %d, read %d)\n", pcklen, cc);
return;
}
if((olsr_in_if = if_ifwithsock(fd)) == NULL)
{
OLSR_PRINTF(1, "Could not find input interface for message from %s size %d\n",
olsr_ip_to_string(&from_addr),
cc);
olsr_syslog(OLSR_LOG_ERR, "Could not find input interface for message from %s size %d\n",
olsr_ip_to_string(&from_addr),
cc);
return;
}
/*
* &from - sender
* &inbuf.olsr
* cc - bytes read
*/
parse_packet((struct olsr *)inbuf, cc, olsr_in_if, &from_addr);
}
syntax highlighted by Code2HTML, v. 0.9.1