/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
* includes code by Bruno Randolf
* 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: olsrd_dot_draw.c,v 1.27 2007/09/13 15:31:58 bernd67 Exp $
*/
/*
* Dynamic linked library for the olsr.org olsr daemon
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "olsr.h"
#include "olsr_types.h"
#include "neighbor_table.h"
#include "two_hop_neighbor_table.h"
#include "tc_set.h"
#include "hna_set.h"
#include "mid_set.h"
#include "link_set.h"
#include "socket_parser.h"
#include "olsrd_dot_draw.h"
#include "olsrd_plugin.h"
#ifdef WIN32
#define close(x) closesocket(x)
#endif
static int ipc_socket;
static int ipc_open;
static int ipc_connection;
static int ipc_socket_up;
/* IPC initialization function */
static int
plugin_ipc_init(void);
static char*
olsr_netmask_to_string(union hna_netmask *mask);
/* Event function to register with the sceduler */
static int
pcf_event(int, int, int);
static void
ipc_action(int);
static void
ipc_print_neigh_link(struct neighbor_entry *neighbor);
static void
ipc_print_tc_link(struct tc_entry *entry, struct tc_edge_entry *dst_entry);
static void
ipc_print_net(union olsr_ip_addr *, union olsr_ip_addr *, union hna_netmask *);
static int
ipc_send(const char *, int);
static int
ipc_send_str(const char *);
static double
calc_etx(double, double);
/**
*Do initialization here
*
*This function is called by the my_init
*function in uolsrd_plugin.c
*/
int
olsrd_plugin_init(void)
{
/* Initial IPC value */
ipc_open = 0;
ipc_socket_up = 0;
ipc_socket = -1;
ipc_connection = -1;
/* Register the "ProcessChanges" function */
register_pcf(&pcf_event);
plugin_ipc_init();
return 1;
}
/**
* destructor - called at unload
*/
void
olsr_plugin_exit(void)
{
if(ipc_open)
close(ipc_socket);
}
static void
ipc_print_neigh_link(struct neighbor_entry *neighbor)
{
char buf[256];
const char* adr;
double etx = 0.0;
char* style = "solid";
struct link_entry* link;
adr = olsr_ip_to_string(&olsr_cnf->main_addr);
sprintf( buf, "\"%s\" -> ", adr );
ipc_send_str(buf);
adr = olsr_ip_to_string(&neighbor->neighbor_main_addr);
if (neighbor->status == 0) { // non SYM
style = "dashed";
}
else {
link = get_best_link_to_neighbor(&neighbor->neighbor_main_addr);
if (link) {
etx = calc_etx( link->loss_link_quality, link->neigh_link_quality);
}
}
sprintf( buf, "\"%s\"[label=\"%.2f\", style=%s];\n", adr, etx, style );
ipc_send_str(buf);
if (neighbor->is_mpr) {
sprintf( buf, "\"%s\"[shape=box];\n", adr );
ipc_send_str(buf);
}
}
static int
plugin_ipc_init(void)
{
struct sockaddr_in sin;
olsr_u32_t yes = 1;
/* Init ipc socket */
if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
olsr_printf(1, "(DOT DRAW)IPC socket %s\n", strerror(errno));
return 0;
}
else
{
if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
{
perror("SO_REUSEADDR failed");
return 0;
}
#if defined __FreeBSD__ && defined SO_NOSIGPIPE
if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0)
{
perror("SO_REUSEADDR failed");
return 0;
}
#endif
/* Bind the socket */
/* complete the socket structure */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(ipc_port);
/* bind the socket to the port number */
if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1)
{
olsr_printf(1, "(DOT DRAW)IPC bind %s\n", strerror(errno));
return 0;
}
/* show that we are willing to listen */
if (listen(ipc_socket, 1) == -1)
{
olsr_printf(1, "(DOT DRAW)IPC listen %s\n", strerror(errno));
return 0;
}
/* Register with olsrd */
//printf("Adding socket with olsrd\n");
add_olsr_socket(ipc_socket, &ipc_action);
ipc_socket_up = 1;
}
return 1;
}
static void
ipc_action(int fd __attribute__((unused)))
{
struct sockaddr_in pin;
socklen_t addrlen = sizeof(struct sockaddr_in);
if (ipc_open)
{
int rc;
do {
rc = close(ipc_connection);
} while (rc == -1 && (errno == EINTR || errno == EAGAIN));
if (rc == -1) {
olsr_printf(1, "(DOT DRAW) Error on closing previously active TCP connection on fd %d: %s\n", ipc_connection, strerror(errno));
}
ipc_open = 0;
}
if ((ipc_connection = accept(ipc_socket, (struct sockaddr *) &pin, &addrlen)) == -1)
{
olsr_printf(1, "(DOT DRAW)IPC accept: %s\n", strerror(errno));
}
else
{
if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.v4))
{
olsr_printf(0, "Front end-connection from foreign host (%s) not allowed!\n", inet_ntoa(pin.sin_addr));
close(ipc_connection);
ipc_connection = -1;
}
else
{
ipc_open = 1;
olsr_printf(1, "(DOT DRAW)IPC: Connection from %s\n",inet_ntoa(pin.sin_addr));
pcf_event(1, 1, 1);
}
}
}
/**
*Scheduled event
*/
static int
pcf_event(int changes_neighborhood,
int changes_topology,
int changes_hna)
{
int res;
olsr_u8_t index;
struct neighbor_entry *neighbor_table_tmp;
struct tc_entry *tc;
struct tc_edge_entry *tc_edge;
struct hna_entry *tmp_hna;
struct hna_net *tmp_net;
res = 0;
if(changes_neighborhood || changes_topology || changes_hna)
{
/* Print tables to IPC socket */
ipc_send_str("digraph topology\n{\n");
/* Neighbors */
for(index=0;index<HASHSIZE;index++)
{
for(neighbor_table_tmp = neighbortable[index].next;
neighbor_table_tmp != &neighbortable[index];
neighbor_table_tmp = neighbor_table_tmp->next)
{
ipc_print_neigh_link( neighbor_table_tmp );
}
}
/* Topology */
OLSR_FOR_ALL_TC_ENTRIES(tc) {
OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
ipc_print_tc_link(tc, tc_edge);
} OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
} OLSR_FOR_ALL_TC_ENTRIES_END(tc);
/* HNA entries */
for(index=0;index<HASHSIZE;index++)
{
tmp_hna = hna_set[index].next;
/* Check all entrys */
while(tmp_hna != &hna_set[index])
{
/* Check all networks */
tmp_net = tmp_hna->networks.next;
while(tmp_net != &tmp_hna->networks)
{
ipc_print_net(&tmp_hna->A_gateway_addr,
&tmp_net->A_network_addr,
&tmp_net->A_netmask);
tmp_net = tmp_net->next;
}
tmp_hna = tmp_hna->next;
}
}
ipc_send_str("}\n\n");
res = 1;
}
if(!ipc_socket_up)
plugin_ipc_init();
return res;
}
#define MIN_LINK_QUALITY 0.01
static double
calc_etx(double loss, double neigh_loss)
{
if (loss < MIN_LINK_QUALITY || neigh_loss < MIN_LINK_QUALITY)
return 0.0;
else
return 1.0 / (loss * neigh_loss);
}
static void
ipc_print_tc_link(struct tc_entry *entry, struct tc_edge_entry *dst_entry)
{
char buf[256];
const char* adr;
double etx = calc_etx( dst_entry->link_quality, dst_entry->inverse_link_quality );
adr = olsr_ip_to_string(&entry->addr);
sprintf( buf, "\"%s\" -> ", adr );
ipc_send_str(buf);
adr = olsr_ip_to_string(&dst_entry->T_dest_addr);
sprintf( buf, "\"%s\"[label=\"%.2f\"];\n", adr, etx );
ipc_send_str(buf);
}
static void
ipc_print_net(union olsr_ip_addr *gw, union olsr_ip_addr *net, union hna_netmask *mask)
{
const char *adr;
adr = olsr_ip_to_string(gw);
ipc_send_str("\"");
ipc_send_str(adr);
ipc_send_str("\" -> \"");
adr = olsr_ip_to_string(net);
ipc_send_str(adr);
ipc_send_str("/");
adr = olsr_netmask_to_string(mask);
ipc_send_str(adr);
ipc_send_str("\"[label=\"HNA\"];\n");
ipc_send_str("\"");
adr = olsr_ip_to_string(net);
ipc_send_str(adr);
ipc_send_str("/");
adr = olsr_netmask_to_string(mask);
ipc_send_str(adr);
ipc_send_str("\"");
ipc_send_str("[shape=diamond];\n");
}
static int
ipc_send_str(const char *data)
{
if(!ipc_open)
return 0;
return ipc_send(data, strlen(data));
}
static int
ipc_send(const char *data, int size)
{
if(!ipc_open)
return 0;
#if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
#define FLAGS 0
#else
#define FLAGS MSG_NOSIGNAL
#endif
if (send(ipc_connection, data, size, FLAGS) == -1)
{
olsr_printf(1, "(DOT DRAW)IPC connection lost!\n");
close(ipc_connection);
ipc_open = 0;
return -1;
}
return 1;
}
static char*
olsr_netmask_to_string(union hna_netmask *mask)
{
char *ret;
struct in_addr in;
if(olsr_cnf->ip_version == AF_INET)
{
in.s_addr = mask->v4;
ret = inet_ntoa(in);
}
else
{
/* IPv6 */
static char netmask[5];
sprintf(netmask, "%d", mask->v6);
ret = netmask;
}
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1