Title: Zebra Hacking How-To Author: yon uriarte havanna_moon@gmx.net Date: Feb. 2001
ZEBRA FOR DUMMIES
a.k.a. LEARN ZEBRA IN 2 HOURS
a.k.a. Zebra Hacking How-To
DISCLAIMER
The author makes the following assumptions about the reader:
- a certain C know-how
- some motivation
- knows what zebra is and does
- has read at least 2 pages about the general architecture of zebra
- can read a 3 liner unified diff without getting confused
- knows the socket(2) syscall and companions
This is not a 100% documentation. When in doubt, do a less ../lib/*.[ch]
.
This document is based on zebra 0.91
INDEX
OVERVIEW
For the sake of this how-to, let assume you want to write a zebra daemon for a new protocol, ZAP (ZAP's Another Protocol).
You have downloaded a copy of zebra, I'll assume /z contains the unpacked files. There are some subdirectories, like /z/ospfd, containing the different protocol implementations. There is a subdirectory containing the library /z/lib, the main zebra daemon is in /z/zebra.
Quite a good part of your program will be ZAP specific, but some functionality will be needed from the zebra framework. That is, you want to link the libzebra.a library to use certain common functions and you want to connect to a running zebra daemon to have information about the system (interfaces and routes).
The library includes functions and structures for:
- connecting to the zebra daemon (zclient.h)
- vty management (access method (telnet), terminal mangling and cli control)
- command registration
- access lists (commands and functionality) (filter.h)
- prefix lists (commands and functionality) (plist.h)
- route maps
- keychains
- satanism (becoming a daemon)
- logging
- linked lists, vectors and hashes
- memory management, including cli
- cooperative multithreading (using select(2))
- FSF's getopt() and regexs
- MD5
- interface structs and functions, including zebra protocol functions (if.h)
- socket mangling (sockunion.h and sockopt.h)
- ip v4 and v6 route tables
- (if_rmap.h)?
and some internally used functions, like serialization support routines for the zebra protocol and whatnot.
main()
Now, just create a subdirectory, /z/zapd, to store zapd's files. I'll start with main(), as usual, so let's call this file zapd main.c. It is based on ospfd.
You will need a sample zapd.conf, looking more like this:
!----------------------------------------------------------------
!
! ZAPd sample configuratin file
!
! zapd.conf
!
hostname zapd
password zebra
!
!log file zapd.log
!
log stdout
!
!----------------------------------------------------------------
which you will have to put in zebra's config dir, specified with --prefix=DIR when running ./configure to build the library. If you are just experimenting, you might want to define it as $HOME/etc or somesuch and run zebra as non-root, just-in-case. This way, depending on platform, zapd will receive interface and route information but won't be able to change anything.
Now compile it (there is no libzapd.a, yet)
gcc -o zapd -I. -I.. -I../lib zapd_main.c ../lib/libzebra.a libzapd.a
run it
./zapd &
and connect to it:
telnet localhost 26666
Zebra Threads
They are of the easily-and-somewhat-portably-implemented cooperatively-multitasking kind, using select(2).
There are 3 possibilities for a thread to be scheduled:
- timer expiration,
- I/O event (read or write, not both at once)
- as an event (to decouple threads)
The main data structure is struct thread, consider it opaque. The functions for setting up a thread are:
typedef int (*cb)(struct thread* );
/* cb == callback */
struct thread *
thread_add_read (struct thread_master *m,
cb func, void *arg, int fd);
struct thread *
thread_add_write (struct thread_master *m,
cb func, void *arg, int fd);
struct thread *
thread_add_timer (struct thread_master *m,
cb func, void *arg, long timer);
struct thread *
thread_add_event (struct thread_master *m,
cb func, void *arg, int val);
All callback functions can read a single parameter, void* arg,
which is accessed through a simple THREAD_ARG(struct thread*)
macro.
The add_read and add_write functions expect a file descriptor, a read thread example follows:
/*--------------------------*/
extern struct thread_master* master;
extern int my_read_fd;
extern struct zap_fd_data* per_fd_data;
struct thread* tp;
/* This is the handler function */
int thread_read_handler(struct thread* th) {
int which_fd;
struct zap_fd_data* my_fd_data;
u_int8_t c;
which_fd = THREAD_FD(th);
my_fd_data = THREAD_ARG(th);
read(which_fd,&c,1);
/* do something useful with the data */
zap_input(my_arg,which_fd,c);
/* reschedule the read thread */
tp = thread_add_read(master, &thread_read_handler, my_fd_data, which_fd);
return 0;
}
/* Install it for the first time */
tp = thread_add_read(master, &thread_read_handler, per_fd_data, my_read_fd);
/*--------------------------*/
thread_add_timer expects a "seconds" argument as its last parameter instead of a fd and the thread_add_event function allows for an int val parameter, which you can read in the thread handler with the THREAD_VAL macro.
Event threads have priority over timer threads.
All threads are one-time events. If you want some sort of recurring timer or reading/writing more than once you must reschedule your handler inside your handler.
Using "void thread_cancel(struct thread *thread);
" you can remove
any thread from the scheduler's list, and citing "thread.c":
/* Delete all events which has argument value arg. */
void thread_cancel_event (struct thread_master *m, void *arg);
Talking to Zebra
You will want your protocol daemon to communicate with the main zebra daemon. It will tell you about interfaces and routes, allowing you to know which interfaces exist and their configuration and which routes are installed and by whom (ospf,bgp,static,...).
The zclient.h library declares the API, interesting are:
the main structure for talking to the main daemon:
/* Structure for the zebra client. */
struct zclient
{
...
/* Flag of communication to zebra is enabled or not. Default is on.
This flag is disabled by `no router zebra' statement. */
int enable;
...
/* Pointer to the callback functions. */
int (*interface_add) (int, struct zclient *, zebra_size_t);
int (*interface_delete) (int, struct zclient *, zebra_size_t);
int (*interface_up) (int, struct zclient *, zebra_size_t);
int (*interface_down) (int, struct zclient *, zebra_size_t);
int (*interface_address_add) (int, struct zclient *, zebra_size_t);
int (*interface_address_delete) (int, struct zclient *, zebra_size_t);
int (*ipv4_route_add) (int, struct zclient *, zebra_size_t);
int (*ipv4_route_delete) (int, struct zclient *, zebra_size_t);
int (*ipv6_route_add) (int, struct zclient *, zebra_size_t);
int (*ipv6_route_delete) (int, struct zclient *, zebra_size_t);
};
and the many function prototypes, which will be described as they are used.
So, for a starter, let's create a new file, zap zebra.c.
Now, your functions (declared and defined elsewhere) will be called whenever such an event happens. You might want to define some dummy functions, which just print the arguments.
Now you might want to get information about the interfaces on the system, just add the following functions to the start of zap zebra.c and you will get a list of all interfaces active on the machine.
/* Interface addition message from zebra. */
int
zap_interface_add (int command, struct zclient *zclient, zebra_size_t length)
{
struct interface *ifp;
/* now, is your duty to extract the interface information from
* the serialized zebra protocol stream, this was just an event
* indication. This will add the interface to the library's global
* interface list, so you can search for it later on.
*/
ifp = zebra_interface_add_read (zclient->ibuf);
zlog_info ("Zebra: interface add %s index %d flags %d metric %d mtu %d",
ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
return 0;
}
int
zap_interface_delete (int command, struct zclient *zclient,
zebra_size_t length)
{
struct interface *ifp;
/* again, it is your job to pluck the interface information off the stream
*/
ifp = zebra_interface_state_read (zclient->ibuf);
if (ifp == NULL)
return 0;
if (if_is_up (ifp))
zlog_warn ("Zebra: got delete of %s, but interface is still up",
ifp->name);
zlog_info ("Zebra: interface delete %s index %d flags %d metric %d mtu %d",
ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
if_delete(ifp);
return 0;
}
Taking a look at if.h you will see the following functions:
int if_is_up (struct interface *);
int if_is_loopback (struct interface *);
int if_is_broadcast (struct interface *);
int if_is_pointopoint (struct interface *);
int if_is_multicast (struct interface *);
which are just flag testers, as used in the above example. Now for the up and down callbacks:
int
zap_interface_state_up (int command, struct zclient *zclient,
zebra_size_t length)
{
struct interface *ifp;
struct interface if_tmp;
u_int32_t old_cost;
/* searches for the interface by that name (the name specified
* in the stream) in the existing interface list.
*/
ifp = zebra_interface_state_read (zclient->ibuf);
if (ifp == NULL)
return 0;
assert(if_is_up(ifp)); /* maybe untrue FIXME */
zlog_info ("Zebra: Interface[%s] state change to up.", ifp->name);
return 0;
}
int
zap_interface_state_down (int command, struct zclient *zclient,
zebra_size_t length)
{
struct interface *ifp;
ifp = zebra_interface_state_read (zclient->ibuf);
if (ifp == NULL)
return 0;
zlog_info ("Zebra: Interface[%s] state change to down.", ifp->name);
return 0;
}
There are two interface add event triggers:
- a real interface is announced via the zebra protocol
- the user types "interface foo" on the command line
For the second case, the only information about the interface is its name, and for the first one the whole struct interface might be relevant. This structure is also defined in if.h :
/* Interface structure */
struct interface
{
/* Interface name. */
char name[INTERFACE_NAMSIZ + 1];
/* INTERFACE_NAMSIZ is a #define, usually 20 (bytes) */
...
/* Hardware address. */
#ifdef HAVE_SOCKADDR_DL
struct sockaddr_dl sdl;
#else
unsigned short hw_type;
u_char hw_addr[INTERFACE_HWADDR_MAX];
int hw_addr_len;
#endif /* HAVE_SOCKADDR_DL */
/* hw dependant hw address
* INTERFACE_HWADDR_MAX is #defined to 20
*/
...
/* Connected address list. */
list connected;
/* This are the addresses on the interface, with masks */
/* Daemon specific interface data pointer. */
void *info;
/* This is for your convenience */
...};
A struct connected looks like this:
/* Connected address structure. */
struct connected
{
/* Attached interface. */
struct interface *ifp;
/* Flags for configuration. */
u_char conf;
#define ZEBRA_IFC_REAL (1 << 0)
#define ZEBRA_IFC_CONFIGURED (1 << 1)
/* Flags for connected address. */
u_char flags;
#define ZEBRA_IFA_SECONDARY (1 << 0)
/* Address of connected network. */
struct prefix *address;
struct prefix *destination;
...};
thus you have access to the IP addresses of the interface. Now you are ready for the next callbacks:
int
zap_interface_address_add (int command, struct zclient *zclient,
zebra_size_t length)
{
struct connected *c;
struct prefix *p;
/* read the address from the zebra protocol stream */
c = zebra_interface_address_add_read (zclient->ibuf);
if (c == NULL)
return 0;
p = c->address;
if (p->family == AF_INET)
zlog_info (" new connected IPv4 address %s/%d on interface %s",
inet_ntoa (p->u.prefix4), p->prefixlen, c->ifp->name);
else if(p->family == AF_INET6)
zlog_info (" new connected IPv6 address on interface %s", c->ifp->name);
return 0;
}
int
zap_interface_address_delete (int command, struct zclient *zclient,
zebra_size_t length)
{
struct connected *c;
c = zebra_interface_address_delete_read (zclient->ibuf);
if (c == NULL)
return 0;
/* you might want to print something over here */
connected_free (c);
/* what you must do, is free the connected struct, which was implicitely
* created in the zebra_interface_address_delete_read call
*/
return 0;
}
And now, the only callbacks left to fill in, are the route callbacks.
FIXME I am not so sure about this part.
Apparently, there is no function in the library to do the deserialization of the zebra protocol stream, so you have to do it by hand:
void zclient_read_zapi_ipv4( struct zclient* zclient,
struct zapi_ipv4 *zapi, struct prefix_ipv4* p,
unsigned long* ifindex, struct in_addr* nexthop)
{
struct stream *s;
s = zclient->ibuf;
/* read the header */
zapi->type = stream_getc (s);
zapi->flags = stream_getc (s);
zapi->message = stream_getc (s);
/* and the prefix */
memset (p, 0, sizeof (struct prefix_ipv4));
p->family = AF_INET;
p->prefixlen = stream_getc (s);
stream_get (&p->prefix, s, PSIZE (p->prefixlen));
if (CHECK_FLAG (zapi->message, ZAPI_MESSAGE_NEXTHOP))
{
zapi->nexthop_num = stream_getc (s);
nexthop->s_addr = stream_get_ipv4 (s);
}
if (CHECK_FLAG (zapi->message, ZAPI_MESSAGE_IFINDEX))
{
zapi->ifindex_num = stream_getc (s);
*ifindex = stream_getl (s);
}
if (CHECK_FLAG (zapi->message, ZAPI_MESSAGE_DISTANCE))
zapi->distance = stream_getc (s);
if (CHECK_FLAG (zapi->message, ZAPI_MESSAGE_METRIC))
zapi->metric = stream_getl (s);
}
and then just call it from the unified routed add/del manager:
int zap_zebra_route_manage (int command, struct zclient *zclient,
zebra_size_t length) {
struct prefix_ipv4 p;
struct zapi_ipv4 zapi;
unsigned long ifindex;
struct in_addr nexthop;
zclient_read_zapi_ipv4( zclient, &zapi, &p,&ifindex,&nexthop);
if (command == ZEBRA_IPV4_ROUTE_ADD) {
zlog_info (" new IPv4 route %s/%d on interface ifindex %d",
inet_atop (p->u.prefix4), p->prefixlen, ifindex);
} else { /* ZEBRA_IPV4_ROUTE_DELETE */
}
return 0;
}
And to end this chapter, let us take a look at the API for setting and deleting routes:
/* Zebra API message flag. */
#define ZAPI_MESSAGE_NEXTHOP 0x01
#define ZAPI_MESSAGE_IFINDEX 0x02
#define ZAPI_MESSAGE_DISTANCE 0x04
#define ZAPI_MESSAGE_METRIC 0x08
/* Zebra IPv4 route message API. */
struct zapi_ipv4
{
u_char type;
u_char flags;
u_char message;
u_char nexthop_num;
struct in_addr **nexthop;
u_char ifindex_num;
unsigned int *ifindex;
u_char distance;
u_int32_t metric;
};
int
zapi_ipv4_add (struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *);
int
zapi_ipv4_delete (struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *);
Usage example:
struct zapi_ipv4 zr;
zr.type = ZEBRA_ROUTE_ZAP;
zr.flags = 0;
SET_FLAG (zr->message, ZAPI_MESSAGE_NEXTHOP);
SET_FLAG (zr->message, ZAPI_MESSAGE_METRIC);
zr.nexthop_num = 1;
zr.nexthop = &some_nexthop;
zr.metric = 111;
zapi_ipv4_add(&zc, &the_prefix, &zr);
This will set a route of type ZAP to the destination prefix the_prefix with nexthop some_nexthop and metric 111. Interface routes are supported.
FIXME is this right? can a daemon set an interface route without a nexthop? The code suggests only ZAPI_MESSAGE_NEXTHOP is checked and then nexthops and ifindices are sent.
You can combine a number of nexthop/ifindex to install an equal weight load balancing route, if the underlying OS supports it.
Defining Commands
#include "command.h"
You know the cisco interface, which changes the available commands depending on the configuration mode you are. These configuration modes are called nodes in the implementation. The available nodes are defined in the enum node_type, which looks like this:
/* There are some command levels which called from command node. */
enum node_type
{
AUTH_NODE, /* Authentication mode of vty interface. */
VIEW_NODE, /* View node. Default mode of vty interface. */
...
ENABLE_NODE, /* Enable node. */
CONFIG_NODE, /* Config node. Default mode of config file. */
...
INTERFACE_NODE, /* Interface mode node. */
...
RIP_NODE, /* RIP protocol mode node. */
...
ACCESS_NODE, /* Access list node. */
PREFIX_NODE, /* Prefix list node. */
...
VTY_NODE /* Vty node. */
};
As you will want a router-zap node, you will have to extend the enum to include ZAP_NODE. ZAP has some per interface configuration options, but the interface node for the per-interface configuration mode is not installed per default. The following code installs both the interface and the zap node.
#include <stdarg.h>
#include <zebra.h>
#include "command.h"
int zap_interface_config_write (struct vty *vty);
int zap_router_config_write (struct vty *vty);
/* I³ll come to this later
*/
/* zapd's interface node. */
struct cmd_node zap_interface_node =
{
INTERFACE_NODE,
"%s(config-if)# ",
1 /* vtysh ? yes */
};
/* zapd's router node. */
struct cmd_node zap_router_node =
{
ZAP_NODE,
"%s(config-router)# ",
1 /* vtysh ? yes */
};
void zap_cmd_init(void) {
if_init();
/* Install interface nodes. */
install_node (&zap_interface_node, zap_interface_config_write);
install_element (CONFIG_NODE, &interface_cmd); /* from if.h */
install_default (INTERFACE_NODE);
install_element (INTERFACE_NODE, &interface_desc_cmd); /* from if.h */
install_element (INTERFACE_NODE, &no_interface_desc_cmd);
/* Install router nodes. */
install_node (&zap_router_node, zap_router_config_write);
/* add the default commands, like exit(!) */
install_default(ZAP_NODE);
}
This code just made possible to define commands for the router zap configuration mode. There is, of course, no way to reach the node, at the moment. So you will have to define a command to change into ZAP configuration mode.
So, let's start:
extern zap_instance_t zap_global;
DEFUN (router_zap,
router_zap_cmd,
"router zap",
"Enable a routing process\n"
"Enter ZAP configuration mode\n")
{
vty->node = ZAP_NODE;
vty->index = zap_global;
return CMD_SUCCESS;
}
and later on:
...
install_element (CONFIG_NODE, &router_zap_cmd);
...
The DEFUN macro defines a command, first parameter is function name, second parameter is a name for a struct, 3rd is the whole command (some control parameters are allowed), and the fourth parameter is a help string, containing as many \n separated help lines as arguments has the command.
With install_element you register the command at the main configuration node (after "conf XXX").
Suppose you are using a german keyboard, which switches the y and z keys, so you want to add a command "router yap", which does the same as router_zap (calls the same function). Then, just add the following lines.
ALIAS (router_zap,
router_yap_cmd,
"router yap",
"Enable a routing process\n"
"Enter ZAP configuration mode\n")
Just make sure the first parameter is the same as the one in the DEFUN macro and the second isn't. To register this command, we reference the struct again:
...
install_element (CONFIG_NODE, &router_yap_cmd);
...
Now, whenever the user types "router zap" or "router yap", the same function is called. The function, called router_zap in this example, has the signature:
f(struct cmd_element *self, struct vty *vty, int argc, char **argv)
So, whenever your commands are called, you have a struct vty to print information to, a reference to the command struct and the usual argc/argv combo (which, for this example will be 0,GARBAGE).
Getting back to our example, the router_zap function modifies the struct vty, setting the node to ZAP_NODE, so only commands defined in that node are available, and setting the index to point to zap_global.
The index allows you to have a per node private void*
. This could
be useful when expanding router zap to run with multiple instances,
allowing a single lookup of the relevant information on the
"router yap XX" command, so you won't have to find the relevant
data struct on every call to a ZAP_NODE command. A quick
zap_instance_t zi= (zap_instance_t) vty->index;
will be enough (assuming zap_instance_t is some kind of pointer).
A quick look at command.[ch]
suggest a special meaning for
certain formats in the command line in DEFUN. This helps the
parsing routines, so you don't have to start playing with all
those horrible str* functions. A (FIXME partial?) list follows:
"A.B.C.D" IPV4
"A.B.C.D/M" IPV4_PREFIX
"X:X::X:X" IPV6
"X:X::X:X/M" IPV6_PREFIX
Words starting with a capital letter are variables, those are put in argv. For example
DEFUN (...,
"router zap AS",
STR_ROUTER
STR_ZAP
"Autonomous System number for this routing process\n")
will give you the user input in arg[0]
.
For some variables only range of values make sense. This is represented
with the less-than symbol, the above example would now be: "router zap <1-65535>
".
Optional arguments are between square brackets. For ZAP the default AS number is 1, so instead of defining and registering two commands ("router zap" and "router zap AS") you could just define it as
DEFUN(..., "router zap [AS]", ...) {
unsigned long int as = argc == 0 ? 1 : strtoul(argv[0]);
if ( (as == 0) || (as & ! 0xFFFF) ) {
vty_out(vty, "Invalid AS number %d%s", as, VTY_NEWLINE);
return CMD_WARNING;
}
/* switch to ROUTER_ZAP node */
}
FIXME nesting of meta characters: can I define "router zap[<1-65535>]
" ?
For inputs requiring a variable word number you use "." to signal a vararg input. ZAP requires a passphrase for authentication, so you could add a "zap passphrase .PASSPHRASE" command to the inferface node.
FIXME argc == 1 or argc == number of words?
Using Access Lists
#include "filter.h"
You have initialised the subsystem with access_list_init(), so the user has the following commands available:
access-list WORD (deny|permit) (A.B.C.D/M|any)
access-list WORD (deny|permit) A.B.C.D/M (exact-match|)
no access-list WORD (deny|permit) (A.B.C.D/M|any)
no access-list WORD (deny|permit) A.B.C.D/M (exact-match|)
no access-list WORD
access-list WORD remark .LINE
no access-list WORD remark
no access-list WORD remark .LINE
If ipv6 support is configured another set of commands for ipv6 access lists is created.
You have the following functions available (from filter.h):
void access_list_reset (void);
void access_list_add_hook (void (*func)(struct access_list *));
void access_list_delete_hook (void (*func)(struct access_list *));
struct access_list *access_list_lookup (int, char *);
enum filter_type access_list_apply (struct access_list *, void *);
access_list_reset just removes all access-lists.
access_list[add|delete]hook citing filter.c:
"Hook function which is executed when new access_list is added."
"Hook function which is executed when access_list is deleted."
access_list_lookup: first parameter is AF_INET or AF_INET6, second is the name of the list, e.g. "101".
access_list_apply citing: "Apply access list to object (which should be struct prefix *)." enum filter_type is {FILTER_DENY, FILTER_PERMIT, FILTER_DYNAMIC}. non-existant or empty lists return DENY.
Using Prefix Lists
#include "plist.h"
just a quick glance over plist.h shows the following interesting information:
enum prefix_list_type { PREFIX_DENY, PREFIX_PERMIT };
enum prefix_name_type { PREFIX_TYPE_STRING, PREFIX_TYPE_NUMBER};
/* Prototypes. */
void prefix_list_reset (void);
void prefix_list_add_hook (void (*func) (void));
void prefix_list_delete_hook (void (*func) (void));
struct prefix_list *prefix_list_lookup (int family, char *);
enum prefix_list_type prefix_list_apply (struct prefix_list *, void *);
The prototypes look like the access-list API ones, read it there.
The structure prefix has the following layout:
/* IPv4 and IPv6 unified prefix structure. */
struct prefix
{
u_char family;
u_char safi;
u_char prefixlen;
u_char padding;
union
{
u_char prefix;
struct in_addr prefix4;
#ifdef HAVE_IPV6
struct in6_addr prefix6;
#endif /* HAVE_IPV6 */
struct
{
struct in_addr id;
struct in_addr adv_router;
} lp;
u_char val[8];
} u;
};
For access_list_apply and prefix_list_apply, you only need to fill in the prefixlen and u, though it might be useful to fill in the family and safi fields, depending on compilation time configuration.
FIXME I'm not so sure about this last paragraph, are afi and safi needed?
Routing Tables
FIXME This chapter might be absolutely BS
You might want to store the routes you get from zebra on a radix tree, for fast retrieval. The library offers such a service, just include table.h
A little peek at the .h file shows two structures: route_table and route_node.
struct route_table { struct route_node *top; };
struct route_node {
/* Actual prefix of this radix. */
struct prefix p;
/* Tree link. */ /* [deleted] */
/* radix tree, 1 bit per depth, max 32 lookups for IPv4
*/
/* Lock of this radix */
unsigned int lock;
/* Each node of route. */
void *info;
/* info is yours to use */
/* Aggregation. */
void *aggregate;
/* doesn't seem to be used much
/* FIXME What is this for? can it be used by the daemon? */
/* table.c doesn't touch it at all */
/* or any file in /z/zebra/ for that matter. */
};
A mix of usage example and function list follows: zap rib example.c.
Hash Tables
You know the drill.
BUGLET:
- hash_push:hash.c count++ but not --
- hash_clean:hash.c missing count=0
The hash.h suggests it could be possible to have hash with different table sizes. But hash.c, specifically hash_clean, works with a fixed size of HASHTABSIZE.
/* for Hash tables */
#define HASHTABSIZE 2048
typedef struct HashBacket
{
void *data;
struct HashBacket *next;
} HashBacket;
struct Hash
{
/* Hash backet. */
HashBacket **index;
/* Hash size. */
int size;
/* Key make function. */
unsigned int (*hash_key)();
/* in fact, unsigned int (*hash_key)(void* )
*/
/* Data compare function. */
int (*hash_cmp)();
/* in fact, unsigned int (*hash_cmp)(void*, void* )
* returns 1 if equal. (not !0, but == 1)
*/
/* Backet alloc. */
unsigned long count;
};
For each void* p,q with hash_key(p) == hash_key(q), hash_cmp(p,q) != 1 must hold.
/* hash instance ctor & dtor*/
struct Hash *hash_new (int size);
void hash_free (struct Hash *hash);
/* remember to set the 2 functions in the Hash struct, e.g.
unsigned int my_hash_key_gen(zap_some_data* e)
{ return e %HASHTABSIZE; /* simplest hash function */ }
int my_hash_cmp(zap_some_data* p, zap_some_data* q)
{ return p->whatever == q->whatever ? 1 : 0; }
h = hash_new(HASHTABSIZE);
h->hash_key = my_hash_key_gen;
h->hash_cmp = my_hash_cmp;
*/
/* returns the first hash bucket of the hash bucket linked list
* for key int (0 <= key < HASHTABSIZE)
*/
HashBacket *hash_head (struct Hash *, int);
/* usage example:
HashBacket* t; int i;
for( i=0; i < HASHTABSIZE; i++)
for (t=hash_head(my_hash,i); t != NULL, t = t->next)
do_something(t->data);
*/
/* insert the data in the hash */
HashBacket *hash_push (struct Hash *, void *);
/* returns the bucket data portion or NULL
* and removes the hash entry */
void *hash_pull (struct Hash *, void *);
/* returns the bucket data portion or NULL */
void *hash_search (struct Hash *, void *);
/* Deletes all entries from the hash (but not the hash itself)
* calls func on each node's data (if func != NULL)
void hash_clean (struct Hash *hash, void (* func) (void *));
Network Portability and Utility Library
#include sockopt.h
#include sockunion.h
#include network.h
I assume you are already familiar with the usual socket framework. A short description of the available API follows.
First sockopt.h:
I can say it better than the code:
"Set up a multicast socket options for IPv4 This is here so that people only have to do their OS multicast mess in one place rather than all through zebra, ospfd, and ripd"
int
setsockopt_multicast_ipv4(int sock,
int optname,
struct in_addr if_addr,
unsigned int mcast_addr,
unsigned int ifindex);
optname is one of IP_MULTICAST_IF, IP_ADD_MEMBERSHIP or IP_DROP_MEMBERSHIP.
And the IPv6 sockopts, which I won't describe here.
And then sockunion.h, which defines a socket interface, which should be portable.
Starting with 2 simple types:
union sockunion
{
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif /* HAVE_IPV6 */
};
enum connect_result {
connect_error, connect_success, connect_in_progress
};
, some macros, e.g.:
#define sock2ip(X) (((struct sockaddr_in *)(X))->sin_addr.s_addr)
#define sock2ip6(X) (((struct sockaddr_in6 *)(X))->sin6_addr.s6_addr)
#define sockunion_family(X) (X)->sa.sa_family
and many functions:
/* Prototypes. */
/* sockunion is already allocated, parse str into it,
0 if no prob */
int
str2sockunion (char *, union sockunion *);
/* print the sockunion param1 in buffer param2 of size param3 */
const
char *sockunion2str (union sockunion *, char *, size_t);
/* 1,0,-1 for param1 >,==,< param2*/
int
sockunion_cmp (union sockunion *, union sockunion *);
/* Quote: "If same family and same prefix return 1." */
int
sockunion_same (union sockunion *, union sockunion *);
/* returns pointer to newly allocated buffer with the sockunion
printed into in. remember to free the buffer FIXME man strdup */
char *
sockunion_su2str (union sockunion *su);
/* create a sockunion from a str, allocates it.
returns NULL if not possible. */
union sockunion *
sockunion_str2su (char *str);
/* FIXME seems to be a zombie prototype in sockunion.h */
struct in_addr
sockunion_get_in_addr (union sockunion *su);
/* accepts on sock, returns new client in sockunion */
int
sockunion_accept (int sock, union sockunion *);
/* makes stream socket of the family specified in sockunion.
If no family is specified, it is ipv6 unless ipv6 is not
compiled in, in that case, it is ipv4 */
int
sockunion_stream_socket (union sockunion *);
/* bind to addr param4, port param3 (port in host byte order)
returns the socket or <0
if param4 is NULL, use INADDR_ANY (or ipv6 equivalent) ,
param2 must not be NULL and will hold the address the socket was bound
to */
int
sockunion_bind (int sock, union sockunion *, unsigned short, union sockunion *);
/* 0 if no problems */
int sockopt_reuseaddr (int);
int sockopt_reuseport (int);
int sockopt_ttl (int family, int sock, int ttl);
/* returns STREAM socket of sockunion type specified in su,
or error (<0)*/
int
sockunion_socket (union sockunion *su);
const char *
inet_sutop (union sockunion *su, char *str);
/* returns pointer to *static* buffer where su is printed */
char *
sockunion_log (union sockunion *su);
/* fd is socket number, su is the target, port is in network byte order,
last param is ifindex for ipv6 linklocal addresses */
enum connect_result
sockunion_connect (int fd, union sockunion *su, unsigned short port, unsigned int);
/* Quoting:
"After TCP connection is established. Get local address and port."
or NULL */
union sockunion *
sockunion_getsockname (int);
/* Quoting
"After TCP connection is established. Get remote address and port. "
or NULL */
union sockunion *
sockunion_getpeername (int);
/* print addrptr in len length strptr buffer,
return NULL or strptr */
const char *
inet_ntop (int family, const void *addrptr, char *strptr, size_t len);
/* put strptr in addrptr (which is a struct in_addr*),
return 1 if ok */
int
inet_pton (int family, const char *strptr, void *addrptr);
/* put cp in inaddr (network byteorder)
return 1 if ok , 0 if problem */
int
inet_aton (const char *cp, struct in_addr *inaddr);
And last network.h
int readn (int, char *, int);
int writen (int, char *, int);
This is for the known case, where write(2) and read(2) return the number of bytes written/read, which need not be the number of bytes requested, and which doesn't necessarily imply an error.
readn returns 0 if all data was read, otherwise, if an error ocurred, the return value of read(2) is returned (<0). If the connection was closed, the number of bytes sent is returned.
writen returns 0 if all data was sent, the error return value (<0) otherwise.