UniK olsrd custom packet implementation HOWTO
Version 0.1
for olsrd 0.4.0
by Andreas Tønnesen
AS OF 0.4.1 THIS HOWTO WILL BE OBSOLETE DUE TO THE NEW PLUG-IN
SYSTEM.
I WILL WRITE A NEW HOWTO WHEN 0.4.1 IS RELEASED.
Abstract
The Optimized LinkState Routing
protocol(RFC3626) is a table-driven pro-active routing protocol
for mobile multi-hop ad-hoc networks(MANETs). OLSR uses a optimalization
called Multipoint Relaying to flood packets throughout the network.
All control traffic in OLSR is transmitted by broadcast(or multicast)
on port 698 using the User Datagram Protocol(UDP).
OLSR provides a default forwarding algorithm that allows
for forwarding of OLSR messages of unknown types. A user may
want to use the optimized flooding technique in OLSR to flood
certain information, routing related or not, to all nodes
that knows how to handle this message.
This document does not try to explain the MPR functioning of the OLSR
protocol. If you are not familiar with the MPR scheme you should read
up on it!
This document gives hints on how to implement custom packet types to
the UniK OLSR implementation.
The information in this document is only valid for UniK
OLSR daemon version 0.4.0
Introduction
The UniK OLSR daemon
The UniK OLSR daemon is a implementation of the OLSR protocol for
GNU/Linux systems by Andreas Tønnesen. The implementation
was based on a OLSR draft 3 compliant implementation by INRIA however
most of the INRIA code has either been replaced or heavily modified.
UniK olsrd is in some ascpects, implemented with adding of custom packet-types
in mind - giving a developer some flexibility.
UniK OLSRd has a homepage at www.olsr.org.
Form this page the latest source code can be downloaded.
Throughout this HOWTO it is assumed that the reader has access to
the source-code and is familiar with the C programming language.
Cross-referenced and commented code created with doxygen
is available at the UniK olsrd website.
Comments
This document describes how to extend the olsrd sourcecode. I am planning
to implement support in the daemon for module-loding at runtime. This
way extentions can be written as modules(dynamically linked libraries)
which provides a more modular and clean design.
The OLSR deamon structure
OLSR is a table driven routing protocol - so everything is
basically about maintaining tables describing the current state
of the network.
The implementation is made up of a variety of information repositories
that can be queried for various information. The basic layout of
olsrd can be depicted as:
+--------+ +--------------+ +-----------+
| PACKET | | INFORMATION | | EVENT |
INPUT -> | PARSER | -> | REPOSITORIES | <-> | SCHEDULER | -> OUTPUT
| | | | | |
+--------+ +--------------+ +-----------+
| |
----------------------
|
V
ROUTE UPDATES
Lets take a closer look at the components:
- Packet parser - The packet parser receives all incoming
OLSR traffic. Even tough the parser has several responsibilities
it basically has three options when it comes to treating a message.
- Discard the packet. This is done if the packet is found to
be invalid.
- Forward the packet according to the default forwarding algorithm.
This is done if the packet is valid but the parser has no knowledge
of this message-type.
- Process the packet according to given instructions. To do this
the parser has to be capable of treating this message-type.
The entity to which a received message is sent is responsible for
updating the information repositories needed if the information
is considered fresh enough and has not already been processed.
- Information repositories - This are the "databases" in which
OLSR keeps the information needed to at minimum describe the current
state of the network.
The various packet parsing functions both updates these tables and
relies on information in these tables to be able to process the messages.
All these tables are regularly "timed out". That means that entries no
longer considered valid are removed.
Whenever these databases are updated in a way that changes the understanding
of the network topology the routes are recalculated.
- Event scheduler - The event scheduler runs different events at
different intervals. When one wishes to transmit a message at a given
interval one must register a packet generation entity with the scheduler.
Timing out of tables is also triggered by the scheduler. If one wishes to
maintain a information repository that should be timed out on a regular
basis then one must register a timeout entity with the scheduler.
The scheduler runs in a thread of its own and share memory is protected
using a mutex so that the packet creation entity does not have to
consider syncronisation and memory-protection.
So when adding a custom packet type and possibly a custom information repository
to olsrd one must roughly go trough the following steps:
- Define the packet structure and see that it is known to all
entities that will have to access objects of this type.
- Create a information repository base if needed.
- Create a function that parses your message type and
register it with the parser.
- Create a function that builds a message of the desired type
and transmits it. Then register this function with the scheduler.
- Creates a function that times out a possible information repository and
register it with the scheduler
Some of these steps might be omitted depending on whether or not you need
to keep a information repository withing the daemon.
The Power status case
We'll consider a simple example case throughout the rest of this HOWTO.
We are interested in a solution where the powerstatus of nodes in the
MANET can be registered. This solution should not affect node that has
not enabled this functioning. So we'll assume having access to a MANET
where a subset of nodes are power-status enabled.
A node is to periodically flood the network with a custom packet
containing the following information:
- Whether or not the node is battery powered
- Estimated usage time left on the battery if using a battery
- percentage of power left on the battery, if batterypowered
So what we need to implement is:
- A message format for the powerinfo.
- A information repository in which we store the info.
- Periodic generation of this message based on our own powerstatus.
- Parsing of this message and updating of the repository
- Timeout of the repository
Issues like a way of polling powerinfo about the local node
will not be covered in this HOWTO.(If you are interested in
how to do this - check out the proc file-system).
We decide that this message is to be transmitted every
10 seconds and that the information should be considered valid
for 30 seconds.
This example-case is in no way intended to be a real-life solution!
We'll consider it as a example for
the sake of the example! NO IPv6 considerations will be
taken either!
UniK olsrd functions and datatypes
We'll start out with a look at the various olsrd specific functions
and datatypes you should use in your code.
olsrd IP addresses
To be able to handle both 32-bit IPv4 addresses and 128-bit IPv6 addresses olsrd
uses a union defined in olsr_protocol.h as:
union olsr_ip_addr
{
olsr_u32_t v4;
struct in6_addr v6;
};
This datatype should be used whenever dealing with IP addresses in olsrd.
The variable ipsize(defined in defs.h and set in main.c) contains
the size of the addresses used. So whenever copying or comparing IP addresses
in olsrd you should do:
/* compare */
memcmp(first_addr, second_addr, ipsize);
/* copy */
memcpy(first_addr, second_addr, ipsize);
Where first_addr and second_addr are pointers to a olsr_ip_addr struct.
olsrd hashing
Olsrd provides some hashing functions that will create a 4 bit hash
(represented as a 32 bit datatype) from a union olsr_ip_address.
The function is defined in hashing.h as:
olsr_u32_t
olsr_hashing(union olsr_ip_addr *);
It returns a 32 bit value but only the lower 4 bits are actually used. The
maximal hash-value is defined in the symbol HASHSIZE. It is
currently 16.
olsr_ip_to_string
This function is defined in net.h:
char *
olsr_ip_to_string(union olsr_ip_addr *);
This function converts a IP address of type union olsr_ip_addr to a
human readable string. Very useful for debugging output.
OBS: it returns a pointer to a statically allocated buffer - and is not
reentrant! If you are to print two ip addresses you MUST do it in
two seperate instructions(eg. not in the same printf(3) call)!
olsrd_printf
Defined in olsr.h
int
olsr_printf(int, char *, ...);
The olsr printfwrapper must be used for all output to stdout from olsrd.
This typically means that you should use olsr_printf whenever you would
have used the printf(3) function from libc.
olsr_printf is similar to printf(3) except from the fact that it takes an integer
as the first argument. This integer defines the debuglevel of the message. Olsrd
uses 10 debuglevels where 0 means no debug(olsrd runs in the background), 1
means minimal debugoutput and the debugoutput increases with the debuglevel.
So of you are to output essential information to stdout that you consider important
you might use debug level 1:
olsr_printf(1, "An error occured %d %s\n", index, olsr_ip_to_string(&address));
This message will be sent to stdout when running olsrd with debug level 1 or
above.
Syslogging
Olsrd runs with a opened syslog entry that you should use for critical output.
The output sent to the syslog will end up in a system log dependant on your
system. You should use LOG_ERR to log error messages.
See "man syslog"
olsr_malloc
This is a wrapper for the malloc(3) syscall. It is defined in olsr.h:
void *
olsr_malloc(size_t, const char *);
You should always use this function when allocating memory in the cases where one
would normally do malloc(3)!
It works like malloc(3) except that is does error checing and possibly ouputs
information to both sdtout(debuglevel 1) and the syslog(LOG_ERR).
olsr_malloc takes a string representing the caller as its second argument. This
strng will be included in error output.
Mantissa exponent functions
The vtime and htime fields of a OLSR message is to be calculated using
a exponent/mantissa scheme described in the RFC. These calculations can
be done using two functions defined in mantissa.h:
olsr_u8_t
double_to_me(double);
double
me_to_double(olsr_u8_t);
The first one converts a double to a mantissa/exponent value(8 bits). The second
one does the opposite.
olsrd timers
Most entries in all tables in olsrd keep a value representing
for how long this entry is to be considered valid. The datatype
used is struct timeval(defined in sys/time.h) and olsrd offers some
functions for working on timvals.
First of all the scheduler maintains a timeval variable that
always contains the "current time". You can access this variable
when you need a current timestamp.
For working on timevals olsr.h defines the two functions:
int
olsr_timed_out(struct timeval *);
void
olsr_init_timer(olsr_u32_t, struct timeval *);
void
olsr_get_timestamp(olsr_u32_t, struct timeval *);
olsr_timed_out checks if the time-data contained in a struct timeval is
in the future. It returns positive if the timeval you passed is not in the
future(that means it is "timed out").
olsr_init_timer - fills a provided structure of type timeval with data
representing the amount of milliseconds provided in the first argument. OBS
note that this function does NOT create a timestamp for the future.
olsr_get_timestamp - fills a provided timeval structure with a timstamp
of current time + the number of milliseconds provided by the first argument.
Defining a message
OLSR message types
OLSR uses a 8 bit integer to identify packet types which gives 256
possible different packet types totally. Types 0-127 are reserved
by OLSR while 128-255 are available for custom packet types. OLSR uses
a default forwarding algorithm on unknown packet-types(and
possibly on known packet-types as well), which makes nodes transmitting
custom packets not breaking compabillity - and nodes not configured
to process these packets forward them according to the MPR scheme.
Default forwarding of unknown messagetypes using MPR flooding makes
the design of custom messages containing various information
to be diffused into the network rather easy.
Designing a custom message
OLSR packages and messages
All OLSR traffic is transmitted in OLSR packets. They all use the same
generic OLSR header. A OLSR packet can contain several OLSR messages.
The basic for of the OLSR packet is:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet Length | Packet Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type | Vtime | Message Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Originator Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time To Live | Hop Count | Message Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
: MESSAGE :
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type | Vtime | Message Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Originator Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time To Live | Hop Count | Message Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
: MESSAGE :
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: :
(etc.)
OLSR defines 4 types of packages as of yet:
- TYPE 1 - HELLO A message used for neighbor and link detection,
MPR calculation and other things. This message is NOT flooded.
- TYPE 2 - TC The Topology Control is flooded throughout
the network. In the Tc message a node announces a subset of its linkset.
- TYPE 3 - MID The Multiple Interface Declaration message
is used to announce that a node runs OLSR on multiple interfaces.
This message is flooded.
- TYPE 4 - HNA The Host and Network Association message
is used to announce connectivity to external networks. This message
is flooded.
A OLSR message has the following form
HEADER+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type | Vtime | Message Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Originator Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time To Live | Hop Count | Message Sequence Number |
BODY +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
: MESSAGE :
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Header fields:
-
Message Type - Type of the message. For custom messages
the space 127-225 can be used.
-
Vtime - The time the information in this message should
be considered valid. This value is computed using a exponent/mantissa
function as described later.
-
Message Size - Size of this message, including message header,
in bytes.
-
Originator Address - IP address of the originator
of this message.
-
Time To Live - A value describing how many hop this message should
be forwarded. This field is decremented by 1 every time the message is
forwarded.
-
Hop Count - Incremented by 1 every time this message is forwarded.
-
Message Sequence Number - The sequence-number of this message. Should
be incremented by 1 every time a node generates a packet of this type
in a pr. interface basis.
The body part is defined by the designer.
In UniK olsrd this datastructure(the OLSR message) for IPv4 is
defined in the headerfile olsr_protocol.h as:
struct olsrmsg
{
olsr_u8_t olsr_msgtype;
olsr_u8_t olsr_vtime;
olsr_u16_t olsr_msgsize;
olsr_u32_t originator;
olsr_u8_t ttl;
olsr_u8_t hopcnt;
olsr_u16_t seqno;
union
{
struct hellomsg hello;
struct tcmsg tc;
struct hnamsg hna;
struct midmsg mid;
} message;
};
The various datatypes used here are also defined in olsr_protocol.h.
The olsr_protocol.h headerfile should be studied as it contains all
OLSR standard packet definitions
Looking at the olsrmsg struct we recognize the fields from the
packetformat we looked at earlier. The actual message body is contained in
the message union. If you are not familiar with the union keyword
you should consult a C programming manual.
The powerinfo message
What we need to do first is to design the format of the
message body of out powerstatus message. We go for the following:
BODY +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Powersource | percentage | Remaining Usage Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The fields are:
- Powersource - set to 1 if the originatornode is running
on battery power and 0 if it's running on AC power.
- Percentage - A integer in the 1-100 space if Powersource == 1,
set to 0 if powersource == 0.
- Remaining Usage Time - the calculated time left of operation
on this node based on the powersource status in minutes. Set to 0 if
Powersource == 0.
To make this as easy as possible we'll just add our datatypes to the
olsr_protocol.h header file. In real-life you should possibly
create you own headerfile as your custom messages are not part of the
OLSR protocol which the olsr_protocol headerfile is to define.
First we add our messagetype to the headerfile. We decide that the powerstatus
message is of type 128. So we update the following section from the olsr_protocol
headerfile:
/*
*Message Types
*/
#define HELLO_MESSAGE 1
#define TC_MESSAGE 2
#define MID_MESSAGE 3
#define HNA_MESSAGE 4
#define MAX_MESSAGE 4
to:
/*
*Message Types
*/
#define HELLO_MESSAGE 1
#define TC_MESSAGE 2
#define MID_MESSAGE 3
#define HNA_MESSAGE 4
#define POWERSTATUS_MESSAGE 128
#define MAX_MESSAGE 128
We also add some constants:
#define POWER_AC 0
#define POWER_BATTERY 1
Then we add the struct describing the message body of the powerstatus message.
We add the following to the "OLSR packet definitions" section in the
headerfile:
/*
* Powerstatus message
*/
struct powermsg
{
olsr_u8_t source_type;
olsr_u8_t percentage;
olsr_u16_t time_left;
};
Then we need to add the message in the body of the olsrmessage definition:
struct olsrmsg
{
olsr_u8_t olsr_msgtype;
olsr_u8_t olsr_vtime;
olsr_u16_t olsr_msgsize;
olsr_u32_t originator;
olsr_u8_t ttl;
olsr_u8_t hopcnt;
olsr_u16_t seqno;
union
{
struct hellomsg hello;
struct tcmsg tc;
struct hnamsg hna;
struct midmsg mid;
struct powermsg pms; /* Our new power message */
} message;
};
And that's all there is to it! We have now added the definition of our custom
message type. And since all files accessing the olsrmsg datatype already
must include the olsr_protocol headerfile - we do not need to add any
#includes anywhere in the source code files.
Creating a information repository
olsrd database structures
We are interested in being able to store the received information about
other nodes powerstatus in a database. This database should only contain
entries that are considered valid according to the vtime field of
the received messages.
Throughout most of olsrd hashed, double-linked ring-lists with static root
entries are used. Lets have a look at what that means:
- The lists are double-linked in the sense that each entry contains
a pointer to the previous and next entry in line.
- The lists are ring-shaped in the sense that the last element points to
the first as its next-in-line.
- The lists are static-rooted in the sense that every list is initialized
as one statically allocated list entry. This entry will never be used for
data storage but rather as a indicator of the "root" of the list.
This might seem strange - but this makes inserting and removing
entries from these lists very easy as you never have to check if the root
element is NULL.
- The lists are said to be hashed due to the fact that a number
of lists are created at initialization. HASHSIZE is the constant describing
how many lists should be created if the hashing functioning provided
by olsrd is to be used.
Hashing reduces search-time of the database
A typical olsr database structure can be schetced as:
+----------------------------------------+
| |
V V
list[0](root) <-> element1 <-> element2 <-> element3
|
| +----------------------------------------+
| | |
| V V
list[1](root) <-> element1 <-> element2 <-> element3
|
| +----------------------------------------+
| | |
| V V
list[2](root) <-> element1 <-> element2 <-> element3
|
.
.
.
| +----------------------------------------+
| | |
| V V
list[HASHSIZE](root) <-> element1 <-> element2 <-> element3
We can see that to find a entry we must first find the hash to be
able to access the correct list-array element. Than we need to
traverse that list.
Ok - first of all we'll create two files called powerstatus_set.c
and powerstatus_set.h. In the headerfile we define the list
entries needed and in the c file we implement the functions needed.
Lets start with the headerfile:
#include "olsr_hashing.h" /* for HASHSIZE */
/* The list entry datatype */
struct powerstatus_entry
{
/* Data */
union olsr_ip_address main_address;
olsr_u8_t source_type;
olsr_u8_t percentage;
olsr_u16_t time_left;
struct timeval valid_time;
/* List pointers */
struct powerstatus_entry *prev;
struct powerstatus_entry *next;
};
/* The repository */
struct powerstatus_entry power_set[HASHSIZE];
/* Functions */
/*
* These are only the functions used in this example
* you need more functionality to actually implement this
*/
/* Initialization function */
void
olsr_init_powerstatus_set();
/* Timeout function */
void
olsr_time_out_powerstatus_set();
Before starting to use the hashed list one MUST initialize the pointers.
In powerstatus_set.c add:
void
olsr_init_powerstatus_set()
{
int index;
for(index=0;index<HASHSIZE;index++)
{
power_set[index].next = &power_set[index];
power_set[index].prev = &power_set[index];
}
What is done here is setting all the root entries to point to them selves - both ways.
Thus the lists only contain the root element.
THIS INITIALIZATION FUNCTION SHOULD BE CALLED FROM THE main FUNCTION IN main.c
Working on the lists
To insert a entry into a list you can do:
/* You have some pointer to a struct powerset_entry
called new_entry */
int hash;
/* Get the hash */
hash = olsr_hashing(&new_entry->main_address);
/* Insert the entry into the correct hashlist */
power_set[hash].next->prev = new_entry;
new_entry->next = power_set[hash].next;
power_set[hash].next = new_entry;
new_entry->prev = &power_set[hash];
OBS!! Notice that the root element(power_set[hash]) is NOT a pointer
so this has to be dereferenced!
To remove an entry you can do:
/* You have some pointer to a struct powerset_entry
called del_entry */
/* Dequeue */
del_entry->next->prev = del_entry->prev;
del_entry->prev->next = del_entry->next;
/* Delete */
free(del_entry);
Registering a timeout function with the scheduler
You should create a function that traverses you lists and removed timed out
entries. To be registered with the scheduler this function must be of type
void taking no parameters.
When registering a timeout function with the scheduler the function is executed
every sched_pollrate seconds. By default this is set to be every 0.1 seconds.
If you need a more precise value you can set it in the olsrd configfile
(/etc/olsrd.conf) or simply hard-code it into olsrd.
To register a function:
void
olsr_time_out_powerstatus_set();
with the scheduler, you must do:
#include "scheduler.h"
....
olsr_register_timeout_function(&olsr_time_out_powerstatus_set);
This should preferably be done in the init function of your
data repository:
void
olsr_init_powerstatus_set()
{
int index;
olsr_register_timeout_function(&olsr_time_out_powerstatus_set);
for(index=0;index<HASHSIZE;index++)
{
power_set[index].next = &power_set[index];
power_set[index].prev = &power_set[index];
}
Notes
At this stage it is assumed that you have created proper functions for
initializing, adding, deleting, searching and timing out your
information repository.
Now we are ready to move on!
Parsing messages
Overwiev
We now need to create functionality to parse a received powerstatus
message. We need to:
- Register the parsing function and message type with the parser
- Update the powerstatus repository based on the information in
the message
- If nessecary forward the message
Implementing a parser function
In the file process_package.c all package parsing functions
can be found. You can create your own function here.
For the OLSR messages in uolsrd this functioning consists of
two functions pr. messagetype one that converts the bytearray containing
the data into a packet object of the desired type (see the *_chgstruct functions)
and one that does all nessecary work based on the converted packet object.
You stand free to adopt this approach but the function that receives the
data from the parser must be of type void and take a parameter of type
union olsr_message *. This parameter is a pointer to the received data.
Since our packetformat is very simple we do not implement a chgstruct function
but rather do all processing in one function:
void
olsr_process_received_power(union olsr_message *m)
{
/* Update the power_set based on this info here */
}
Checking for duplicates
The first thing to do when parsing a message will in most cases be to
check if we have treated this message before. OLSR uses a duplicate table
to be able to do this.
To check the duplicate table(and update it) the function
olsr_check_dup_table_proc can be used. This function returns
negative if this message has been processed before:
void
olsr_process_received_power(union olsr_message *m)
{
/* If this message has been processed before - we should still
* check if it should be forwarded
*/
if(!olsr_check_dup_table_proc(originator, seqno, msgtype)
goto forward;
/* Update the power_set based on this info here */
forward:
/* Forward here */
}
You need to extract these values(originator, seqno, msgtype) from the message
and pass them to the duplicate check function.
Forwarding
The function parsing the message is also responsible for forwarding it
as not all parse functions may choose to use the default forwarding algorithm.
But since we have no special needs we will utilize the default forwarding. This
is available to us trough the function olsr_forward_message:
void
olsr_process_received_power(union olsr_message *m)
{
/* If this message has been processed before - we should still
* check if it should be forwarded
*/
if(!olsr_check_dup_table_proc(originator, seqno, msgtype)
goto forward;
/* Update the power_set based on this info here */
forward:
olsr_forward_message(m, originator, seqno, msgtype);
return;
}
The forwarding function will do all nessecary checks for duplicates and register
this message to avoid processing it again.
Registering the function with the parser
To register out parse function with the parse multiplexer we can use the
function olsr_parser_add_function. All parse functions are
registered in the olsr_init_package_process function
in the process_package.c file. We must add out own function here
including function type:
void
olsr_init_package_process()
{
olsr_parser_add_function(&olsr_process_received_hello, HELLO_MESSAGE);
olsr_parser_add_function(&olsr_process_received_tc, TC_MESSAGE);
olsr_parser_add_function(&olsr_process_received_mid, MID_MESSAGE);
olsr_parser_add_function(&olsr_process_received_hna, HNA_MESSAGE);
olsr_parser_add_function(&olsr_process_received_paa, PAA_FORWARD_MESSAGE);
/* our new function */
olsr_parser_add_function(&olsr_process_received_power, POWER_MESSAGE);
}
Now our parsing function will be passed all received messages of type POWER_MESSAGE!
And that's all there is to it :-)
Generating messages
Now we need to create functionality to generate powerstatus messages
on a regular basis.
Register a sequence number
Sequence numbers are maintained on a pr interface basis in olsrd. To add
a sequence number for our powerstatus messaging we update the file interface.h:
/*
* sequence numbers for interface
*/
struct olsr_seqnums
{
short mid_seqnum;
short hello_seqnum;
short tc_seqnum;
short hna_seqnum;
short paa_seqnum;
short olsr_seqnum;
/* Our new sequence number */
short power_seqnum;
};
We also update the initialization of the sequence-numbers in main.c:
/*
*Initialize sequence-numbers
*for each interface
*/
ifp->seqnums.mid_seqnum = 1;
ifp->seqnums.hello_seqnum = 1;
ifp->seqnums.tc_seqnum = 1;
ifp->seqnums.hna_seqnum = 1;
ifp->seqnums.paa_seqnum = 1;
ifp->seqnums.olsr_seqnum = 1;
/* our new entry */
ifp->seqnums.power_sequnm = 1;
This ensures that all powerstatus sequnms are set to 1(for every interface) at
startup.
The outputbuffer and outputsize
UniK olsrd uses a global buffer for data that is to be sent. This buffer can
be accessed trough the union olsr_packet *msg pointer declared in main.h.
The size of the data in this buffer must be set in the global variable outputsize.
The size set here(in bytes) is the amount of bytes that will be transmitted from
this node!
Creating a message build function
The file build_msg.c contains all current packet building functions. Note that
some messagetypes uses other functions combined with these to generate more complex
packages.
Packets will essentially be different when using different IP address versions, so
we need to create one function for version 4 and one for version 6. But to
keep the transparency we create one general function that based on the IPversion used
calls the correct function.
So - first we add:
int
power_build(struct interface *ifn)
{
switch(ipversion)
{
case(AF_INET):
return power_build4(ifn);
break;
case(AF_INET6):
return power_build6(ifn);
break;
default:
return -1;
}
return -1;
}
Now we need to create the IPversion dependent functions. We add the following to
build_msg.c:
int
power_build4(struct interface *ifn)
{
int i;
union olsr_message *m;
struct powermsg *pmsg;
/* Get a pointer to the message header */
m = msg->v4.olsr_msg
/* Get a pointer to the body */
pmsg = m->message.pms;
/* Set initial hopcount and time to live */
m->v4.hopcnt = 0;
m->v4.ttl = MAX_TTL;
/* Seqno */
m->v4.seqno = htons(ifn->seqnums.power_seqnum);
/* Set main address */
memcpy(&m->v4.originator, &main_addr, ipsize);
/* Set message type */
m->v4.olsr_msgtype = POWER_MESSAGE;
/* Set size - fixed in this example - 16 bytes */
m->v4.olsr_msgsize = htons(16);
/*
* we set the vtalidity time to 30
* This value must be in mantissa/exponent format
* as explained in the RFC
*/
m->v4.olsr_vtime = double_to_me(30);
/* Total size including OLSR header! */
outputsize = 20;
msg->v4.olsr_packlen = htons(20);
/* FILL MESSAGE WITH POWERINFO */
/* These functions are assumed to be implemented */
pmsg->source_type = htons(get_powersource());
pmsg->percentage = htons(get_power_percentage());
pmsg->time_left = htons(get_power_ttl());
return 0;
}
You should make sure you understand what is going on here!
Basically what happens is that pointers to the message header and
body are acquired from the outputbuffer. Then the packet-header, message-header
and message-body is filled with info.
The actual sending is NOT done here!
Creating a generation function
The file generate_msg.c contains all current packet generation functions.
These functions does not actually build the messages - that is described in the
previous section.
A function that is to register itself as a packet generator with the scheduler
must be of the type void and take no parameters. In generate_msg.c
we add:
void
generate_power()
{
struct interface *ifn;
/* looping trough interfaces */
for (ifn = ifnet; ifn ; ifn = ifn->int_next)
{
power_build(ifn);
net_output(ifn);
/* increment the seqno for this interface */
ifn->seqnums.power_seqnum++;
}
return;
}
This function loops trough all interfaces that OLSR is running on.
For every interface it calls the power_build function that fills
the outputbuffer with a powerstatus message and sets the outputsize.
Then the function net_output is invoked.
The net_output function transmits the message in the outputbuffer
on the interface specified. The reason this is done on a pr interface
basis it that some messages might need to send different data on different
interfaces.
Registering the generation function with the scheduler
Now all we need to do is to register our generation function with
the scheduler. This is done using the olsr_register_scheduler_event
function.
This function takes three parameters (void (*event_function)(), float interval, float initial, olsr_u8_t *trigger):
- event_function - the function to be called
- interval - the interval in seconds between calls to the above function
- trigger - a pointer to a trigger that if set to 1 will trig the call of the
registered function immediately. The scheduler will reset it to 0.
The registration should be added to the init_msg_generator in
generate_msg.c:
void
init_msg_generator()
{
olsr_register_scheduler_event(&generate_hello, hello_int, 0, NULL);
olsr_register_scheduler_event(&generate_hello_nw, hello_int_nw, 0, NULL);
olsr_register_scheduler_event(&generate_tc, tc_int, tc_int, &changes);
if(nbinterf > 1)
olsr_register_scheduler_event(&generate_mid, mid_int, mid_int/2, NULL);
if(send_hna)
olsr_register_scheduler_event(&generate_hna, hna_int, hna_int/2, NULL);
/* Our new event */
olsr_register_scheduler_event(&generate_power, power_int, 0, NULL);
}
Thats it!!!
Notes
Summary
(C)Andreas Tønnesen 2004