#include <sys/param.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <sys/sysctl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "inetroute.h"
#include "util.h"
#define INDEX_NONE -1
inetroute_list_t *
inetroute_list_init()
{
char * buf = NULL;
char * lim;
inetroute_list_t * list_p = NULL;
int list_size = 2;
int mib[6];
size_t needed;
char * next;
struct rt_msghdr * rtm;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_DUMP;
mib[5] = 0;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
perror("route-sysctl-estimate");
goto err;
}
if ((buf = malloc(needed)) == 0) {
goto err;
}
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
perror("sysctl of routing table");
goto err;
}
list_p = (inetroute_list_t *)malloc(sizeof(*list_p));
list_p->def_index = INDEX_NONE;
if (list_p == NULL)
goto err;
list_p->count = 0;
list_p->list = (inetroute_t *)malloc(sizeof(*(list_p->list)) * list_size);
if (list_p->list == NULL)
goto err;
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
void * addrs;
struct sockaddr * dst = NULL;
struct sockaddr * gateway = NULL;
struct sockaddr * mask = NULL;
rtm = (struct rt_msghdr *)next;
addrs = (void *)(&rtm[1]);
if (rtm->rtm_addrs & RTA_DST) {
dst = (struct sockaddr *)addrs;
addrs += MAX(sizeof(*dst), dst->sa_len);
}
if (rtm->rtm_addrs & RTA_GATEWAY) {
gateway = (struct sockaddr *)addrs;
addrs += MAX(sizeof(*dst), gateway->sa_len);
}
if (rtm->rtm_addrs & RTA_NETMASK) {
mask = (struct sockaddr *)addrs;
}
if (dst && dst->sa_family == AF_INET
&& gateway
&& mask
&& !(rtm->rtm_flags & RTF_HOST)) {
struct sockaddr_in * dst_p = (struct sockaddr_in *)dst;
struct sockaddr_in * mask_p = (struct sockaddr_in *)mask;
inetroute_t * entry;
if (list_p->count == list_size) {
list_size *= 2;
list_p->list = (inetroute_t *)
realloc(list_p->list, sizeof(*(list_p->list)) * list_size);
if (list_p->list == NULL)
goto err;
}
entry = list_p->list + list_p->count;
bzero(entry, sizeof(*entry));
entry->dest = dst_p->sin_addr;
if (mask_p->sin_len != 0) {
entry->mask = mask_p->sin_addr;
}
if ((rtm->rtm_flags & RTF_IFSCOPE) == 0
&& dst_p->sin_addr.s_addr == INADDR_ANY) {
list_p->def_index = list_p->count;
}
if (gateway->sa_family == AF_LINK) {
struct sockaddr_dl * sdl = (struct sockaddr_dl *)gateway;
entry->gateway.link = *sdl;
}
else {
struct sockaddr_in * in_p = (struct sockaddr_in *)gateway;
entry->gateway.inet = *in_p;
}
list_p->count++;
}
}
free(buf);
return (list_p);
err:
if (buf)
free(buf);
inetroute_list_free(&list_p);
return (NULL);
}
void
inetroute_list_free(inetroute_list_t * * list)
{
if (list != NULL && *list != NULL) {
if ((*list)->list)
free((*list)->list);
(*list)->list = NULL;
free(*list);
*list = NULL;
}
}
struct in_addr *
inetroute_default(inetroute_list_t * list_p)
{
inetroute_t * entry;
if (list_p->def_index == INDEX_NONE) {
return (NULL);
}
entry = list_p->list + list_p->def_index;
if (entry->gateway.inet.sin_family == AF_INET) {
return (&(entry->gateway.inet.sin_addr));
}
return (NULL);
}
void
inetroute_list_print(inetroute_list_t * list_p)
{
int i;
for (i = 0; i < list_p->count; i++) {
inetroute_t * entry = list_p->list + i;
if (entry->gateway.link.sdl_family == AF_LINK) {
printf("%s ==> link %d\n",
inet_nettoa(entry->dest, entry->mask),
entry->gateway.link.sdl_index);
}
else {
printf("%s ==> %s\n",
inet_nettoa(entry->dest, entry->mask),
inet_ntoa(entry->gateway.inet.sin_addr));
}
}
}
#ifdef TEST_INETROUTE
int
main()
{
inetroute_list_t * list_p;
struct in_addr * def;
list_p = inetroute_list_init();
if (list_p == NULL)
exit(0);
inetroute_list_print(list_p);
def = inetroute_default(list_p);
if (def) {
printf("default route: %s\n", inet_ntoa(*def));
}
inetroute_list_free(&list_p);
exit(0);
}
#endif