Commits

ripencc committed 136e2f1

cleanup docs a bit

  • Participants
  • Parent commits 227395f

Comments (0)

Files changed (5)

 libbgpdump.so: libbgpdump.a
 	$(COMPILE) $(LDFLAGS) -o libbgpdump.so $(LIB_O) $(SYS_LIBS)
 
-testbgpdump: test.c libbgpdump.a
-	$(COMPILE) -o testbgpdump test.c libbgpdump.a $(SYS_LIBS)
+example: example.c libbgpdump.a
+	$(COMPILE) -o testbgpdump example.c libbgpdump.a $(SYS_LIBS)
 
 bgpdump: bgpdump.c libbgpdump.a
 	$(COMPILE) -o bgpdump bgpdump.c libbgpdump.a $(SYS_LIBS)
 
-test-clean:
+check-clean:
 	rm -f test_out/*.bgp.gz
 
-test: test-clean bgpdump
+check: check-clean bgpdump
 	./test.sh
 
-clean: test-clean
+clean: check-clean
 	rm -f libbgpdump.so libbgpdump.a testbgpdump bgpdump $(LIB_O)
 
 distclean: clean
+
+==== INTRODUCTION ====
+
 This is libbgpdump - a C library designed to help with analyzing dump
 files produced by Zebra/Quagga or MRT.
 
+New versions are available for download at:
+http://www.ris.ripe.net/source/bgpdump/
 
+The <ris-users@ripe.net> mailing list is for discussion of RIS data and
+related tools, weekly reports and occasional public announcements.
+
+Please send bug reports and patches to <ris@ripe.net>.
 
 === SUPPORTED INPUT FORMAT ===
 
 	- BGP state changes
 
 
-
 === COMPILING ===
 
-- ./configure [--disable-ipv6] ; make
-- Note that IPv6 is enabled by default, and if the library has IPv6
-  support your application must be able to handle or ignore the resulting
-  IPv6 data!
+- ./configure ; make
 - libbgpdump should compile on Linux, FreeBSD, Solaris, and Cygwin
 
-
-
 === HOW TO USE THE LIBRARY YOURSELF ===
 
 - include the file bgpdump_lib.h
 - call bgpdump_open_dump() to open desired Zebra/MRT dump/update file
-- call bgpdump_read_next() to read an entry
-- analyze the entry
-- call bgdump_free_mem() to free up memory allocated by bgpdump_read_next()
-- repeat last 3 steps until EOF of input
+- repeat the next 3 steps until EOF
+	- call bgpdump_read_next() to read an entry
+	- analyze the entry
+	- call bgdump_free_mem() to free up memory allocated by bgpdump_read_next()
 - call bgdump_close_dump() to close the dump file
 
 Please see bgdump_formats.h for a description of data structures used.
 
-An example is included - test.c - it lists all the information gathered
+An example is included - example.c - it lists all the information gathered
 from a dump/update file given as a command line parameter.
 
-
-
 === NOTES ON TABLE_DUMP_V2 ===
 
 You might note that there are some strange loops in the code handling
   entry->subtype
 for BGPDUMP_SUBTYPE_TABLE_DUMP_V2_RIB_IPV4_UNICAST etc.
 
+=== NOTES ON IPv6 SUPPORT ===
 
+libbgpdump v1.3 and above support IPv6. Both address types are 
+usually stored the BGPDUMP_IP_ADDRESS data structure.
 
-=== BUGS ===
+IPv6 announcements and withdrawals aren't stored directly
+in the update data structures like their IPv4
+counterparts, they are stored in a separate struct
+mp_info. Two convenient macros, MP_IPV6_ANNOUNCE() and
+MP_IPV6_WITHDRAW(), are provided to get them out.
 
-If you find a bug, please report it to ris@ripe.net.
+So if for IPv4 prefixes you did something like:
+
+    for (i = 0; i < entry->body.zebra_message.announce_count; i++) {
+	do_something_with_prefix(entry->body.zebra_message.announce[i]);
+    }
+
+for IPv6 prefixes you would do this:
+
+    struct mp_nlri *v6_announce;
+    if(entry->attr->mp_info &&
+       (v6_announce = MP_IPV6_ANNOUNCE(entry->attr->mp_info)) != NULL) {
+	for (i = 0; i < v6_announce->prefix_count; i++) {
+	    do_something_with_prefix(v6_announce->nlri[i]);
+	}
+    }
+
+for withdrawals, do the same using MP_IPV6_WITHDRAW().

File README.ipv6

-IPv6 support in libbgpdump
---------------------------
-
-libbgpdump supports IPv6 in v1.3 and above. IPv6 should
-be supported for all record types that were supported by
-previous versions, including RIB dumps and BGP updates.
-
-IPv6 support is detected by ./configure. If desired, it
-can be turned off at compile time by specifying the
---disable-ipv6 option to ./configure.
-
-Note that the introduction of IPv6 support required a
-change in data structures: everything that used to be a
-sin_addr, including for example IP addresses and prefixes,
-is now a BGPDUMP_IP_ADDRESS:
-
-    typedef union union_BGPDUMP_IP_ADDRESS {
-	struct in_addr    v4_addr;
-	struct in6_addr    v6_addr;
-    } BGPDUMP_IP_ADDRESS;
-
-So whatever code referred to IPv4 addresses must be
-modified by adding .v4_addr to the variable in question.
-For example,
-
-    printf("%s\n", inet_ntoa(prefix.address));
-
-becomes
-
-    printf("%s\n", inet_ntoa(prefix.address.v4_addr));
-
-
-IPv6 announcements and withdrawals
-----------------------------------
-
-IPv6 announcements and withdrawals aren't stored directly
-in the update data structures like their IPv4
-counterparts, they are stored in a separate struct
-mp_info. Two convenient macros, MP_IPV6_ANNOUNCE() and
-MP_IPV6_WITHDRAW(), are provided to get them out.
-
-So if for IPv4 prefixes you did something like:
-
-    for (i = 0; i < entry->body.zebra_message.announce_count; i++) {
-	do_something_with_prefix(entry->body.zebra_message.announce[i]);
-    }
-
-for IPv6 prefixes you would do this:
-
-    struct mp_nlri *v6_announce;
-    if(entry->attr->mp_info &&
-       (v6_announce = MP_IPV6_ANNOUNCE(entry->attr->mp_info)) != NULL) {
-	for (i = 0; i < v6_announce->prefix_count; i++) {
-	    do_something_with_prefix(v6_announce->nlri[i]);
-	}
-    }
-
-for withdrawals, do the same using MP_IPV6_WITHDRAW().
-For more examples, look at test.c.
+/*
+ Copyright (c) 2007 - 2010 RIPE NCC - All Rights Reserved
+ 
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted, provided
+ that the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation, and that the name of the author not be used in advertising or
+ publicity pertaining to distribution of the software without specific,
+ written prior permission.
+ 
+ THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
+ AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+ DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ 
+Parts of this code have been engineered after analiyzing GNU Zebra's
+source code and therefore might contain declarations/code from GNU
+Zebra, Copyright (C) 1999 Kunihiro Ishiguro. Zebra is a free routing
+software, distributed under the GNU General Public License. A copy of
+this license is included with libbgpdump.
+
+Author: Dan Ardelean (dan@ripe.net)
+*/
+
+#include "bgpdump_lib.h"
+#include <time.h>
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+    void process(BGPDUMP_ENTRY *entry);
+    void show_attr(attributes_t *attr);
+    void show_prefixes(int count,struct prefix *prefix);
+#ifdef BGPDUMP_HAVE_IPV6
+    void show_v6_prefixes(int count, struct prefix *prefix);
+#endif
+
+int main(int argc, char **argv) {  
+    BGPDUMP *my_dump;
+    BGPDUMP_ENTRY *my_entry=NULL;
+
+    if(argc>1) {
+	my_dump=bgpdump_open_dump(argv[1]);
+    } else {
+	my_dump=bgpdump_open_dump("dumps/updates.20020701.0032");
+    }
+
+    if(my_dump==NULL) {
+	printf("Error opening dump file ...\n");
+	exit(1);
+    }
+
+    do {
+//fprintf(stdout, "Offset: %d\n", gztell(my_dump->f));
+	my_entry=bgpdump_read_next(my_dump);
+	if(my_entry!=NULL) {
+	    process(my_entry);
+	    bgpdump_free_mem(my_entry);
+	}
+    } while(my_dump->eof==0);
+
+    bgpdump_close_dump(my_dump);
+//fprintf(stderr, "%s: OK=%d, BAD=%d (%f%% OK)\n", my_dump->filename, my_dump->parsed_ok, my_dump->parsed - my_dump->parsed_ok, (float) my_dump->parsed_ok / my_dump->parsed * 100);
+    
+ return 0;
+}
+
+char *bgp_state_name[] = {
+    "Unknown",
+    "IDLE",
+    "CONNECT",
+    "ACTIVE",
+    "OPEN_SENT",
+    "OPEN_CONFIRM",
+    "ESTABLISHED",
+    NULL
+};
+
+char *bgp_message_types[] = {
+    "Unknown",
+    "Open",
+    "Update/Withdraw",
+    "Notification",
+    "Keepalive"
+};
+
+char *notify_codes[] = {
+    "Unknown",
+    "Message Header Error",
+    "OPEN Message Error",
+    "UPDATE Message Error",
+    "Hold Timer Expired",
+    "Finite State Machine Error",
+    "Cease"
+};
+
+char *notify_subcodes[][12] = {
+    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+    /* Message Header Error */
+    {
+	"None",
+ 	"Connection Not Synchronized",
+	"Bad Message Length",
+	"Bad Message Type",
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
+    },
+    /* OPEN Message Error */
+    {
+	"None",
+	"Unsupported Version Number",
+	"Bad Peer AS",
+	"Bad BGP Identifier",
+	"Unsupported Optional Parameter",
+	"Authentication Failure",
+	"Unacceptable Hold Time",
+	NULL, NULL, NULL, NULL, NULL
+    },
+    /* UPDATE Message Error */
+    {
+	"None",
+	"Malformed Attribute List",
+	"Unrecognized Well-known Attribute",
+	"Missing Well-known Attribute",
+	"Attribute Flags Error",
+	"Attribute Length Error",
+	"Invalid ORIGIN Attribute",
+	"AS Routing Loop",
+	"Invalid NEXT_HOP Attribute",
+	"Optional Attribute Error",
+	"Invalid Network Field",
+	"Malformed AS_PATH"
+    },
+    /* Hold Timer Expired */
+    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+    /* Finite State Machine Error */
+    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+    /* Cease */
+    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+};
+
+void process(BGPDUMP_ENTRY *entry) {
+    char prefix[BGPDUMP_ADDRSTRLEN], peer_ip[BGPDUMP_ADDRSTRLEN];
+    char source_ip[BGPDUMP_ADDRSTRLEN], destination_ip[BGPDUMP_ADDRSTRLEN];
+    struct mp_nlri *mp_announce, *mp_withdraw;
+    int i, code, subcode;
+	BGPDUMP_TABLE_DUMP_V2_PREFIX *e;
+
+if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_KEEPALIVE) return;
+if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_OPEN) return;
+if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_NOTIFY) return;
+if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_STATE_CHANGE && entry->length == 8) return;
+
+    printf("TIME            : %s",asctime(gmtime(&entry->time)));
+    printf("LENGTH          : %u\n", entry->length);
+    switch(entry->type) {
+	case BGPDUMP_TYPE_MRTD_TABLE_DUMP:
+	    if(entry->subtype == AFI_IP) {
+		strcpy(prefix, inet_ntoa(entry->body.mrtd_table_dump.prefix.v4_addr));
+		strcpy(peer_ip, inet_ntoa(entry->body.mrtd_table_dump.peer_ip.v4_addr));
+#ifdef BGPDUMP_HAVE_IPV6
+	    } else if(entry->subtype == AFI_IP6) {
+		inet_ntop(AF_INET6, &entry->body.mrtd_table_dump.prefix.v6_addr, prefix,
+			  sizeof(prefix));
+		inet_ntop(AF_INET6, &entry->body.mrtd_table_dump.peer_ip.v6_addr, peer_ip,
+			  sizeof(peer_ip));
+#endif
+	    } else {
+		*prefix = '\0';
+		*peer_ip = '\0';
+	    }
+	    printf("TYPE            : BGP Table Dump Entry\n");
+	    printf("    VIEW        : %d\n",entry->body.mrtd_table_dump.view);
+	    printf("    SEQUENCE    : %d\n",entry->body.mrtd_table_dump.sequence);
+	    printf("    PREFIX      : %s/%d\n",prefix,entry->body.mrtd_table_dump.mask);
+	    printf("    STATUS      : %d\n",entry->body.mrtd_table_dump.status);
+	    printf("    UPTIME      : %s",asctime(gmtime(&entry->body.mrtd_table_dump.uptime)));
+	    printf("    PEER IP     : %s\n",peer_ip);
+	    printf("    PEER AS     : %u\n",entry->body.mrtd_table_dump.peer_as);
+    	show_attr(entry->attr);
+	    break;
+
+	case BGPDUMP_TYPE_TABLE_DUMP_V2:
+
+		e = &entry->body.mrtd_table_dump_v2_prefix;
+
+	    if(e->afi == AFI_IP) {
+			strcpy(prefix, inet_ntoa(e->prefix.v4_addr));
+#ifdef BGPDUMP_HAVE_IPV6
+	    } else if(e->afi == AFI_IP6) {
+			inet_ntop(AF_INET6, &e->prefix.v6_addr, prefix, INET6_ADDRSTRLEN);
+#endif
+	    } else {
+			printf("Error: BGP table dump version 2 entry with unknown subtype\n");
+			break;
+	    }
+
+		for(i = 0; i < e->entry_count; i++){
+			if(i){
+    			printf("\nTIME            : %s",asctime(gmtime(&entry->time)));
+    			printf("LENGTH          : %u\n", entry->length);
+			}
+
+
+    		printf("TYPE            : BGP Table Dump version 2 Entry\n");
+    		printf("    SEQUENCE    : %d\n",e->seq);
+    		printf("    PREFIX      : %s/%d\n",prefix,e->prefix_length);
+
+			if(e->entries[i].peer->afi == AFI_IP){
+				inet_ntop(AF_INET, &e->entries[i].peer->peer_ip, peer_ip, INET6_ADDRSTRLEN);
+#ifdef BGPDUMP_HAVE_IPV6
+			} else if (e->entries[i].peer->afi == AFI_IP6){
+				inet_ntop(AF_INET6, &e->entries[i].peer->peer_ip, peer_ip, INET6_ADDRSTRLEN);
+#endif
+			} else {
+				sprintf(peer_ip, "N/A, unsupported AF");
+			}
+    		printf("    PEER IP     : %s\n",peer_ip);
+    		printf("    PEER AS     : %u\n",e->entries[i].peer->peer_as);
+
+   			show_attr(e->entries[i].attr);
+		}
+
+	    break;
+
+	case BGPDUMP_TYPE_ZEBRA_BGP:
+	    printf("TYPE            : Zebra BGP \n");
+		if(entry->body.zebra_message.address_family == AFI_IP) {
+		    strcpy(source_ip, inet_ntoa(entry->body.zebra_message.source_ip.v4_addr));
+		    strcpy(destination_ip, inet_ntoa(entry->body.zebra_message.destination_ip.v4_addr));
+#ifdef BGPDUMP_HAVE_IPV6
+		} else if(entry->body.zebra_message.address_family == AFI_IP6) {
+		    inet_ntop(AF_INET6, &entry->body.zebra_message.source_ip.v6_addr, source_ip,
+			      sizeof(source_ip));
+		    inet_ntop(AF_INET6, &entry->body.zebra_message.destination_ip.v6_addr, destination_ip,
+			      sizeof(destination_ip));
+#endif
+		} else {
+		    *source_ip = '\0';
+		    *destination_ip = '\0';
+		}
+	    switch(entry->subtype) {
+		case BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE:
+		case BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE_AS4:
+		    printf("SUBTYPE         : Zebra BGP Message");
+		    if(entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE_AS4) {
+		      printf(" (32-bit ASN)\n");
+		    } else {
+		      printf("\n");
+		    }
+		    printf("    SOURCE_AS   : %u\n",entry->body.zebra_message.source_as);
+		    printf("    DEST_AS     : %u\n",entry->body.zebra_message.destination_as);
+		    printf("    INTERFACE   : %d\n",entry->body.zebra_message.interface_index);
+		    printf("    SOURCE_IP   : %s\n",source_ip);
+		    printf("    DEST_IP     : %s\n",destination_ip);
+
+		    if(entry->body.zebra_message.type > sizeof(bgp_message_types) / sizeof(bgp_message_types[0]))
+			printf("MESSAGE TYPE    : Unknown\n");
+		    else
+			printf("MESSAGE TYPE    : %s\n", bgp_message_types[entry->body.zebra_message.type]);
+
+		    switch(entry->body.zebra_message.type) {
+			case BGP_MSG_UPDATE:
+			    printf("WITHDRAW        :\n");
+			    show_prefixes(entry->body.zebra_message.withdraw_count,entry->body.zebra_message.withdraw);
+#ifdef BGPDUMP_HAVE_IPV6
+			    if(entry->attr->mp_info &&
+			       (mp_withdraw = MP_IPV6_WITHDRAW(entry->attr->mp_info)) != NULL) {
+				show_v6_prefixes(mp_withdraw->prefix_count, mp_withdraw->nlri);
+			    }
+#endif
+			    printf("ANNOUNCE        :\n");
+			    show_prefixes(entry->body.zebra_message.announce_count,entry->body.zebra_message.announce);
+#ifdef BGPDUMP_HAVE_IPV6
+			    if(entry->attr->mp_info &&
+			       (mp_announce = MP_IPV6_ANNOUNCE(entry->attr->mp_info)) != NULL) {
+				show_v6_prefixes(mp_announce->prefix_count, mp_announce->nlri);
+			    }
+#endif
+			    break;
+			case BGP_MSG_KEEPALIVE:
+			    /* Nothing to do */
+			    break;
+			case BGP_MSG_OPEN:
+			    printf("    VERSION     : %d\n",entry->body.zebra_message.version);
+			    printf("    MY_ASN      : %u\n",entry->body.zebra_message.my_as);
+			    printf("    HOLD_TIME   : %d\n",entry->body.zebra_message.hold_time);
+			    printf("    ROUTER_ID   : %s\n",inet_ntoa(entry->body.zebra_message.bgp_id));
+			    printf("    OPTION_LEN  : %d\n",entry->body.zebra_message.opt_len);
+			    printf("    OPTION_DATA :");
+			    for(i = 0; i < entry->body.zebra_message.opt_len; i++) {
+				printf(" %02x", entry->body.zebra_message.opt_data[i]);
+			    }
+			    printf("\n");
+			    break;
+			case BGP_MSG_NOTIFY:
+			    code = entry->body.zebra_message.error_code;
+			    subcode = entry->body.zebra_message.sub_error_code;
+
+			    printf("    CODE        : %d", code);
+			    if(code >= sizeof(notify_codes) / sizeof(notify_codes[0]))
+				printf(" (Unknown)\n");
+			    else
+				printf(" (%s)\n", notify_codes[code]);
+
+			    printf("    SUBCODE     : %d", subcode);
+			    if(code >= sizeof(notify_codes) / sizeof(notify_codes[0]) ||
+			       subcode >= sizeof(notify_subcodes[0]) / sizeof(notify_subcodes[0][0]) ||
+			       notify_subcodes[code][subcode] == NULL)
+				printf(" (Unknown)\n");
+			    else
+				printf(" (%s)\n", notify_subcodes[code][subcode]);
+
+			    printf("    DATA        :");
+			    for(i = 0; i < entry->body.zebra_message.notify_len; i++) {
+				printf(" %02x", entry->body.zebra_message.notify_data[i]);
+			    }
+			    printf("\n");
+			    break;
+			default:
+			    break;
+		    }
+		    break;
+
+		case BGPDUMP_SUBTYPE_ZEBRA_BGP_STATE_CHANGE:
+		    printf("SUBTYPE         : Zebra BGP State Change\n");
+		    printf("    SOURCE_AS   : %u\n",entry->body.zebra_state_change.source_as);
+		    printf("    DEST_AS     : %u\n",entry->body.zebra_state_change.destination_as);
+		    printf("    INTERFACE   : %d\n",entry->body.zebra_state_change.interface_index);
+		    printf("    SOURCE_IP   : %s\n",source_ip);
+		    printf("    DEST_IP     : %s\n",destination_ip);
+		    printf("    OLD_STATE   : %s\n",bgp_state_name[entry->body.zebra_state_change.old_state]);
+		    printf("    NEW_STATE   : %s\n",bgp_state_name[entry->body.zebra_state_change.new_state]);
+    		show_attr(entry->attr);
+		    break;
+
+		default:
+		    printf("SUBTYPE         : Unknown %d\n", entry->subtype);
+	    }
+    	show_attr(entry->attr);
+	    break;
+	default:
+	    printf("TYPE            : Unknown %d\n", entry->type);
+    	show_attr(entry->attr);
+	    
+    }
+    printf("\n");
+}
+
+void show_attr(attributes_t *attr) {
+    int have_nexthop = 0;
+    printf("ATTRIBUTES      :\n");
+    
+    if(attr != NULL) {
+	    printf("   ATTR_LEN     : %d\n",attr->len);
+
+	    if( (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGIN) ) !=0 )		printf("   ORIGIN       : %d\n",attr->origin);
+	    else printf("   ORIGIN       : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AS_PATH) ) !=0)		printf("   ASPATH       : %s\n",attr->aspath->str);
+	    else printf("   ASPATH       : N/A\n");
+
+	    printf("   NEXT_HOP     : ");
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP) ) !=0) {
+		have_nexthop = 1;
+		printf("%s", inet_ntoa(attr->nexthop));
+	    }
+
+#ifdef BGPDUMP_HAVE_IPV6
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) &&
+	         MP_IPV6_ANNOUNCE(attr->mp_info) != NULL) {
+		char addr[INET6_ADDRSTRLEN];
+		struct mp_nlri *mp_nlri = MP_IPV6_ANNOUNCE(attr->mp_info);
+		u_int8_t len = mp_nlri->nexthop_len;
+
+		if(have_nexthop)
+		    printf(" ");
+
+		have_nexthop = 1;
+		printf("%s", inet_ntop(AF_INET6, &mp_nlri->nexthop, addr, sizeof(addr)));
+		if(len == 32)
+		    printf(" %s", inet_ntop(AF_INET6, &mp_nlri->nexthop_local, addr, sizeof(addr)));
+	    }
+#endif
+
+	    printf(have_nexthop ? "\n" : "N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC) ) !=0)	printf("   MED          : %d\n",attr->med);
+	    else printf("   MED          : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF) ) !=0)		printf("   LOCAL_PREF   : %d\n",attr->local_pref);
+	    else printf("   LOCAL_PREF   : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE) ) !=0)	printf("   ATOMIC_AGREG : Present\n");
+	    else printf("   ATOMIC_AGREG : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR) ) !=0)		printf("   AGGREGATOR   : %s AS%u\n",inet_ntoa(attr->aggregator_addr),attr->aggregator_as);
+	    else printf("   AGGREGATOR   : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES) ) !=0)	printf("   COMMUNITIES  : %s\n",attr->community->str);
+	    else printf("   COMMUNITIES  : N/A\n");
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEW_AS_PATH) ) !=0) {
+		printf("   NEW_ASPATH   : %s\n",attr->new_aspath->str);
+	    	printf("   OLD_ASPATH   : %s\n",attr->old_aspath->str);
+	    }
+
+	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEW_AGGREGATOR) ) !=0)	printf("   NEW_AGGREGTR : %s AS%u\n",inet_ntoa(attr->new_aggregator_addr),attr->new_aggregator_as);
+    }
+}
+
+void show_prefixes(int count,struct prefix *prefix) {
+    int i;
+    for(i=0;i<count;i++)
+	printf("      %s/%d\n",inet_ntoa(prefix[i].address.v4_addr),prefix[i].len);
+}
+
+#ifdef BGPDUMP_HAVE_IPV6
+void show_v6_prefixes(int count, struct prefix *prefix) {
+    int i;
+    char str[INET6_ADDRSTRLEN];
+
+    for(i=0;i<count;i++){
+	inet_ntop(AF_INET6, &prefix[i].address.v6_addr, str, sizeof(str));
+	printf("      %s/%d\n",str, prefix[i].len);
+    }
+}
+#endif

File test.c

-/*
- Copyright (c) 2007 - 2010 RIPE NCC - All Rights Reserved
- 
- Permission to use, copy, modify, and distribute this software and its
- documentation for any purpose and without fee is hereby granted, provided
- that the above copyright notice appear in all copies and that both that
- copyright notice and this permission notice appear in supporting
- documentation, and that the name of the author not be used in advertising or
- publicity pertaining to distribution of the software without specific,
- written prior permission.
- 
- THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
- ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
- AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
- DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- 
-Parts of this code have been engineered after analiyzing GNU Zebra's
-source code and therefore might contain declarations/code from GNU
-Zebra, Copyright (C) 1999 Kunihiro Ishiguro. Zebra is a free routing
-software, distributed under the GNU General Public License. A copy of
-this license is included with libbgpdump.
-
-Author: Dan Ardelean (dan@ripe.net)
-*/
-
-#include "bgpdump_lib.h"
-#include <time.h>
-
-#include <stdlib.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-    void process(BGPDUMP_ENTRY *entry);
-    void show_attr(attributes_t *attr);
-    void show_prefixes(int count,struct prefix *prefix);
-#ifdef BGPDUMP_HAVE_IPV6
-    void show_v6_prefixes(int count, struct prefix *prefix);
-#endif
-
-int main(int argc, char **argv) {  
-    BGPDUMP *my_dump;
-    BGPDUMP_ENTRY *my_entry=NULL;
-
-    if(argc>1) {
-	my_dump=bgpdump_open_dump(argv[1]);
-    } else {
-	my_dump=bgpdump_open_dump("dumps/updates.20020701.0032");
-    }
-
-    if(my_dump==NULL) {
-	printf("Error opening dump file ...\n");
-	exit(1);
-    }
-
-    do {
-//fprintf(stdout, "Offset: %d\n", gztell(my_dump->f));
-	my_entry=bgpdump_read_next(my_dump);
-	if(my_entry!=NULL) {
-	    process(my_entry);
-	    bgpdump_free_mem(my_entry);
-	}
-    } while(my_dump->eof==0);
-
-    bgpdump_close_dump(my_dump);
-//fprintf(stderr, "%s: OK=%d, BAD=%d (%f%% OK)\n", my_dump->filename, my_dump->parsed_ok, my_dump->parsed - my_dump->parsed_ok, (float) my_dump->parsed_ok / my_dump->parsed * 100);
-    
- return 0;
-}
-
-char *bgp_state_name[] = {
-    "Unknown",
-    "IDLE",
-    "CONNECT",
-    "ACTIVE",
-    "OPEN_SENT",
-    "OPEN_CONFIRM",
-    "ESTABLISHED",
-    NULL
-};
-
-char *bgp_message_types[] = {
-    "Unknown",
-    "Open",
-    "Update/Withdraw",
-    "Notification",
-    "Keepalive"
-};
-
-char *notify_codes[] = {
-    "Unknown",
-    "Message Header Error",
-    "OPEN Message Error",
-    "UPDATE Message Error",
-    "Hold Timer Expired",
-    "Finite State Machine Error",
-    "Cease"
-};
-
-char *notify_subcodes[][12] = {
-    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
-    /* Message Header Error */
-    {
-	"None",
- 	"Connection Not Synchronized",
-	"Bad Message Length",
-	"Bad Message Type",
-	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
-    },
-    /* OPEN Message Error */
-    {
-	"None",
-	"Unsupported Version Number",
-	"Bad Peer AS",
-	"Bad BGP Identifier",
-	"Unsupported Optional Parameter",
-	"Authentication Failure",
-	"Unacceptable Hold Time",
-	NULL, NULL, NULL, NULL, NULL
-    },
-    /* UPDATE Message Error */
-    {
-	"None",
-	"Malformed Attribute List",
-	"Unrecognized Well-known Attribute",
-	"Missing Well-known Attribute",
-	"Attribute Flags Error",
-	"Attribute Length Error",
-	"Invalid ORIGIN Attribute",
-	"AS Routing Loop",
-	"Invalid NEXT_HOP Attribute",
-	"Optional Attribute Error",
-	"Invalid Network Field",
-	"Malformed AS_PATH"
-    },
-    /* Hold Timer Expired */
-    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
-    /* Finite State Machine Error */
-    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
-    /* Cease */
-    { "None", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
-
-};
-
-void process(BGPDUMP_ENTRY *entry) {
-    char prefix[BGPDUMP_ADDRSTRLEN], peer_ip[BGPDUMP_ADDRSTRLEN];
-    char source_ip[BGPDUMP_ADDRSTRLEN], destination_ip[BGPDUMP_ADDRSTRLEN];
-    struct mp_nlri *mp_announce, *mp_withdraw;
-    int i, code, subcode;
-	BGPDUMP_TABLE_DUMP_V2_PREFIX *e;
-
-if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_KEEPALIVE) return;
-if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_OPEN) return;
-if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE && entry->body.zebra_message.type == BGP_MSG_NOTIFY) return;
-if(entry->type == BGPDUMP_TYPE_ZEBRA_BGP && entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_STATE_CHANGE && entry->length == 8) return;
-
-    printf("TIME            : %s",asctime(gmtime(&entry->time)));
-    printf("LENGTH          : %u\n", entry->length);
-    switch(entry->type) {
-	case BGPDUMP_TYPE_MRTD_TABLE_DUMP:
-	    if(entry->subtype == AFI_IP) {
-		strcpy(prefix, inet_ntoa(entry->body.mrtd_table_dump.prefix.v4_addr));
-		strcpy(peer_ip, inet_ntoa(entry->body.mrtd_table_dump.peer_ip.v4_addr));
-#ifdef BGPDUMP_HAVE_IPV6
-	    } else if(entry->subtype == AFI_IP6) {
-		inet_ntop(AF_INET6, &entry->body.mrtd_table_dump.prefix.v6_addr, prefix,
-			  sizeof(prefix));
-		inet_ntop(AF_INET6, &entry->body.mrtd_table_dump.peer_ip.v6_addr, peer_ip,
-			  sizeof(peer_ip));
-#endif
-	    } else {
-		*prefix = '\0';
-		*peer_ip = '\0';
-	    }
-	    printf("TYPE            : BGP Table Dump Entry\n");
-	    printf("    VIEW        : %d\n",entry->body.mrtd_table_dump.view);
-	    printf("    SEQUENCE    : %d\n",entry->body.mrtd_table_dump.sequence);
-	    printf("    PREFIX      : %s/%d\n",prefix,entry->body.mrtd_table_dump.mask);
-	    printf("    STATUS      : %d\n",entry->body.mrtd_table_dump.status);
-	    printf("    UPTIME      : %s",asctime(gmtime(&entry->body.mrtd_table_dump.uptime)));
-	    printf("    PEER IP     : %s\n",peer_ip);
-	    printf("    PEER AS     : %u\n",entry->body.mrtd_table_dump.peer_as);
-    	show_attr(entry->attr);
-	    break;
-
-	case BGPDUMP_TYPE_TABLE_DUMP_V2:
-
-		e = &entry->body.mrtd_table_dump_v2_prefix;
-
-	    if(e->afi == AFI_IP) {
-			strcpy(prefix, inet_ntoa(e->prefix.v4_addr));
-#ifdef BGPDUMP_HAVE_IPV6
-	    } else if(e->afi == AFI_IP6) {
-			inet_ntop(AF_INET6, &e->prefix.v6_addr, prefix, INET6_ADDRSTRLEN);
-#endif
-	    } else {
-			printf("Error: BGP table dump version 2 entry with unknown subtype\n");
-			break;
-	    }
-
-		for(i = 0; i < e->entry_count; i++){
-			if(i){
-    			printf("\nTIME            : %s",asctime(gmtime(&entry->time)));
-    			printf("LENGTH          : %u\n", entry->length);
-			}
-
-
-    		printf("TYPE            : BGP Table Dump version 2 Entry\n");
-    		printf("    SEQUENCE    : %d\n",e->seq);
-    		printf("    PREFIX      : %s/%d\n",prefix,e->prefix_length);
-
-			if(e->entries[i].peer->afi == AFI_IP){
-				inet_ntop(AF_INET, &e->entries[i].peer->peer_ip, peer_ip, INET6_ADDRSTRLEN);
-#ifdef BGPDUMP_HAVE_IPV6
-			} else if (e->entries[i].peer->afi == AFI_IP6){
-				inet_ntop(AF_INET6, &e->entries[i].peer->peer_ip, peer_ip, INET6_ADDRSTRLEN);
-#endif
-			} else {
-				sprintf(peer_ip, "N/A, unsupported AF");
-			}
-    		printf("    PEER IP     : %s\n",peer_ip);
-    		printf("    PEER AS     : %u\n",e->entries[i].peer->peer_as);
-
-   			show_attr(e->entries[i].attr);
-		}
-
-	    break;
-
-	case BGPDUMP_TYPE_ZEBRA_BGP:
-	    printf("TYPE            : Zebra BGP \n");
-		if(entry->body.zebra_message.address_family == AFI_IP) {
-		    strcpy(source_ip, inet_ntoa(entry->body.zebra_message.source_ip.v4_addr));
-		    strcpy(destination_ip, inet_ntoa(entry->body.zebra_message.destination_ip.v4_addr));
-#ifdef BGPDUMP_HAVE_IPV6
-		} else if(entry->body.zebra_message.address_family == AFI_IP6) {
-		    inet_ntop(AF_INET6, &entry->body.zebra_message.source_ip.v6_addr, source_ip,
-			      sizeof(source_ip));
-		    inet_ntop(AF_INET6, &entry->body.zebra_message.destination_ip.v6_addr, destination_ip,
-			      sizeof(destination_ip));
-#endif
-		} else {
-		    *source_ip = '\0';
-		    *destination_ip = '\0';
-		}
-	    switch(entry->subtype) {
-		case BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE:
-		case BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE_AS4:
-		    printf("SUBTYPE         : Zebra BGP Message");
-		    if(entry->subtype == BGPDUMP_SUBTYPE_ZEBRA_BGP_MESSAGE_AS4) {
-		      printf(" (32-bit ASN)\n");
-		    } else {
-		      printf("\n");
-		    }
-		    printf("    SOURCE_AS   : %u\n",entry->body.zebra_message.source_as);
-		    printf("    DEST_AS     : %u\n",entry->body.zebra_message.destination_as);
-		    printf("    INTERFACE   : %d\n",entry->body.zebra_message.interface_index);
-		    printf("    SOURCE_IP   : %s\n",source_ip);
-		    printf("    DEST_IP     : %s\n",destination_ip);
-
-		    if(entry->body.zebra_message.type > sizeof(bgp_message_types) / sizeof(bgp_message_types[0]))
-			printf("MESSAGE TYPE    : Unknown\n");
-		    else
-			printf("MESSAGE TYPE    : %s\n", bgp_message_types[entry->body.zebra_message.type]);
-
-		    switch(entry->body.zebra_message.type) {
-			case BGP_MSG_UPDATE:
-			    printf("WITHDRAW        :\n");
-			    show_prefixes(entry->body.zebra_message.withdraw_count,entry->body.zebra_message.withdraw);
-#ifdef BGPDUMP_HAVE_IPV6
-			    if(entry->attr->mp_info &&
-			       (mp_withdraw = MP_IPV6_WITHDRAW(entry->attr->mp_info)) != NULL) {
-				show_v6_prefixes(mp_withdraw->prefix_count, mp_withdraw->nlri);
-			    }
-#endif
-			    printf("ANNOUNCE        :\n");
-			    show_prefixes(entry->body.zebra_message.announce_count,entry->body.zebra_message.announce);
-#ifdef BGPDUMP_HAVE_IPV6
-			    if(entry->attr->mp_info &&
-			       (mp_announce = MP_IPV6_ANNOUNCE(entry->attr->mp_info)) != NULL) {
-				show_v6_prefixes(mp_announce->prefix_count, mp_announce->nlri);
-			    }
-#endif
-			    break;
-			case BGP_MSG_KEEPALIVE:
-			    /* Nothing to do */
-			    break;
-			case BGP_MSG_OPEN:
-			    printf("    VERSION     : %d\n",entry->body.zebra_message.version);
-			    printf("    MY_ASN      : %u\n",entry->body.zebra_message.my_as);
-			    printf("    HOLD_TIME   : %d\n",entry->body.zebra_message.hold_time);
-			    printf("    ROUTER_ID   : %s\n",inet_ntoa(entry->body.zebra_message.bgp_id));
-			    printf("    OPTION_LEN  : %d\n",entry->body.zebra_message.opt_len);
-			    printf("    OPTION_DATA :");
-			    for(i = 0; i < entry->body.zebra_message.opt_len; i++) {
-				printf(" %02x", entry->body.zebra_message.opt_data[i]);
-			    }
-			    printf("\n");
-			    break;
-			case BGP_MSG_NOTIFY:
-			    code = entry->body.zebra_message.error_code;
-			    subcode = entry->body.zebra_message.sub_error_code;
-
-			    printf("    CODE        : %d", code);
-			    if(code >= sizeof(notify_codes) / sizeof(notify_codes[0]))
-				printf(" (Unknown)\n");
-			    else
-				printf(" (%s)\n", notify_codes[code]);
-
-			    printf("    SUBCODE     : %d", subcode);
-			    if(code >= sizeof(notify_codes) / sizeof(notify_codes[0]) ||
-			       subcode >= sizeof(notify_subcodes[0]) / sizeof(notify_subcodes[0][0]) ||
-			       notify_subcodes[code][subcode] == NULL)
-				printf(" (Unknown)\n");
-			    else
-				printf(" (%s)\n", notify_subcodes[code][subcode]);
-
-			    printf("    DATA        :");
-			    for(i = 0; i < entry->body.zebra_message.notify_len; i++) {
-				printf(" %02x", entry->body.zebra_message.notify_data[i]);
-			    }
-			    printf("\n");
-			    break;
-			default:
-			    break;
-		    }
-		    break;
-
-		case BGPDUMP_SUBTYPE_ZEBRA_BGP_STATE_CHANGE:
-		    printf("SUBTYPE         : Zebra BGP State Change\n");
-		    printf("    SOURCE_AS   : %u\n",entry->body.zebra_state_change.source_as);
-		    printf("    DEST_AS     : %u\n",entry->body.zebra_state_change.destination_as);
-		    printf("    INTERFACE   : %d\n",entry->body.zebra_state_change.interface_index);
-		    printf("    SOURCE_IP   : %s\n",source_ip);
-		    printf("    DEST_IP     : %s\n",destination_ip);
-		    printf("    OLD_STATE   : %s\n",bgp_state_name[entry->body.zebra_state_change.old_state]);
-		    printf("    NEW_STATE   : %s\n",bgp_state_name[entry->body.zebra_state_change.new_state]);
-    		show_attr(entry->attr);
-		    break;
-
-		default:
-		    printf("SUBTYPE         : Unknown %d\n", entry->subtype);
-	    }
-    	show_attr(entry->attr);
-	    break;
-	default:
-	    printf("TYPE            : Unknown %d\n", entry->type);
-    	show_attr(entry->attr);
-	    
-    }
-    printf("\n");
-}
-
-void show_attr(attributes_t *attr) {
-    int have_nexthop = 0;
-    printf("ATTRIBUTES      :\n");
-    
-    if(attr != NULL) {
-	    printf("   ATTR_LEN     : %d\n",attr->len);
-
-	    if( (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGIN) ) !=0 )		printf("   ORIGIN       : %d\n",attr->origin);
-	    else printf("   ORIGIN       : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AS_PATH) ) !=0)		printf("   ASPATH       : %s\n",attr->aspath->str);
-	    else printf("   ASPATH       : N/A\n");
-
-	    printf("   NEXT_HOP     : ");
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP) ) !=0) {
-		have_nexthop = 1;
-		printf("%s", inet_ntoa(attr->nexthop));
-	    }
-
-#ifdef BGPDUMP_HAVE_IPV6
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) &&
-	         MP_IPV6_ANNOUNCE(attr->mp_info) != NULL) {
-		char addr[INET6_ADDRSTRLEN];
-		struct mp_nlri *mp_nlri = MP_IPV6_ANNOUNCE(attr->mp_info);
-		u_int8_t len = mp_nlri->nexthop_len;
-
-		if(have_nexthop)
-		    printf(" ");
-
-		have_nexthop = 1;
-		printf("%s", inet_ntop(AF_INET6, &mp_nlri->nexthop, addr, sizeof(addr)));
-		if(len == 32)
-		    printf(" %s", inet_ntop(AF_INET6, &mp_nlri->nexthop_local, addr, sizeof(addr)));
-	    }
-#endif
-
-	    printf(have_nexthop ? "\n" : "N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC) ) !=0)	printf("   MED          : %d\n",attr->med);
-	    else printf("   MED          : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF) ) !=0)		printf("   LOCAL_PREF   : %d\n",attr->local_pref);
-	    else printf("   LOCAL_PREF   : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE) ) !=0)	printf("   ATOMIC_AGREG : Present\n");
-	    else printf("   ATOMIC_AGREG : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR) ) !=0)		printf("   AGGREGATOR   : %s AS%u\n",inet_ntoa(attr->aggregator_addr),attr->aggregator_as);
-	    else printf("   AGGREGATOR   : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES) ) !=0)	printf("   COMMUNITIES  : %s\n",attr->community->str);
-	    else printf("   COMMUNITIES  : N/A\n");
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEW_AS_PATH) ) !=0) {
-		printf("   NEW_ASPATH   : %s\n",attr->new_aspath->str);
-	    	printf("   OLD_ASPATH   : %s\n",attr->old_aspath->str);
-	    }
-
-	    if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEW_AGGREGATOR) ) !=0)	printf("   NEW_AGGREGTR : %s AS%u\n",inet_ntoa(attr->new_aggregator_addr),attr->new_aggregator_as);
-    }
-}
-
-void show_prefixes(int count,struct prefix *prefix) {
-    int i;
-    for(i=0;i<count;i++)
-	printf("      %s/%d\n",inet_ntoa(prefix[i].address.v4_addr),prefix[i].len);
-}
-
-#ifdef BGPDUMP_HAVE_IPV6
-void show_v6_prefixes(int count, struct prefix *prefix) {
-    int i;
-    char str[INET6_ADDRSTRLEN];
-
-    for(i=0;i<count;i++){
-	inet_ntop(AF_INET6, &prefix[i].address.v6_addr, str, sizeof(str));
-	printf("      %s/%d\n",str, prefix[i].len);
-    }
-}
-#endif