File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / net / uroute.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.h>
#include <sys/ioctl.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <err.h>

#include "structs/structs.h"
#include "structs/type/array.h"

#include "net/route_msg.h"
#include "net/uroute.h"
#include "util/typed_mem.h"

#define ROUNDUP(a) \
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n)	((x) += ROUNDUP((n)->sa_len))

#ifdef RTF_CLONING
#define WRITABLE_FLAGS	(RTF_STATIC | RTF_LLINFO | RTF_REJECT | RTF_BLACKHOLE \
			    | RTF_PROTO1 | RTF_PROTO2 | RTF_CLONING \
			    | RTF_XRESOLVE | RTF_UP | RTF_GATEWAY)
#else
#define WRITABLE_FLAGS	(RTF_STATIC | RTF_REJECT | RTF_BLACKHOLE \
			    | RTF_PROTO1 | RTF_PROTO2 \
			    | RTF_XRESOLVE | RTF_UP | RTF_GATEWAY)
#endif

struct route_flag {
	const char	*name;
	int		bit;
};

static const	struct route_flag route_flags[] = {
#define FLAG(x)	{ #x, RTF_ ## x }
	FLAG(UP),
	FLAG(GATEWAY),
	FLAG(HOST),
	FLAG(REJECT),
	FLAG(DYNAMIC),
	FLAG(MODIFIED),
	FLAG(DONE),
#ifdef RTF_CLONING
	FLAG(CLONING),
#endif
	FLAG(XRESOLVE),
#ifdef RTF_LLINFO
	FLAG(LLINFO),
#endif
	FLAG(STATIC),
	FLAG(BLACKHOLE),
	FLAG(PROTO2),
	FLAG(PROTO1),
	FLAG(PRCLONING),
#ifdef RTF_WASCLONED
	FLAG(WASCLONED),
#endif
	FLAG(PROTO3),
	FLAG(PINNED),
	FLAG(LOCAL),
	FLAG(BROADCAST),
	FLAG(MULTICAST),
#undef FLAG
	{ NULL, 0 }
};

/* Route structure */
struct uroute {
	int flags;
	struct sockaddr	*dest;
	struct sockaddr	*gateway;
	struct sockaddr	*netmask;
};

/*
 * Internal functions
 */
static int	uroute_do_route(struct uroute *route, int sock, int type);
static const	char *uroute_sockaddr_string(const struct sockaddr *sa);

/*
 * Create a new route.
 */
struct uroute *
uroute_create(const struct sockaddr *dest, const struct sockaddr *gateway,
	const struct sockaddr *netmask)
{
	struct uroute *route;

	if (dest == NULL || gateway == NULL) {
		errno = EINVAL;
		return (NULL);
	}
	if ((route = MALLOC("uroute", sizeof(*route))) == NULL)
		return (NULL);
	memset(route, 0, sizeof(*route));
	if ((route->dest = MALLOC("uroute.dest", dest->sa_len)) == NULL)
		goto fail;
	memcpy(route->dest, dest, dest->sa_len);
	if ((route->gateway = MALLOC("uroute.gateway",
	    gateway->sa_len)) == NULL)
		goto fail;
	memcpy(route->gateway, gateway, gateway->sa_len);
	if (netmask != NULL) {
		if ((route->netmask = MALLOC("uroute.netmask",
		    netmask->sa_len)) == NULL)
			goto fail;
		memcpy(route->netmask, netmask, netmask->sa_len);
	}
	route->flags = RTF_STATIC;
	return (route);

fail:
	/* Clean up */
	uroute_destroy(&route);
	return (NULL);
}

/*
 * Free a route.
 */
void
uroute_destroy(struct uroute **routep)
{
	struct uroute *const route = *routep;

	if (route == NULL)
		return;
	FREE("uroute.dest", route->dest);
	FREE("uroute.gateway", route->gateway);
	FREE("uroute.netmask", route->netmask);
	FREE("uroute", route);
	*routep = NULL;
}

/*
 * Get the destination address.
 */
const struct sockaddr *
uroute_get_dest(struct uroute *route)
{
	return (route->dest);
}

/*
 * Get the gateway address.
 */
const struct sockaddr *
uroute_get_gateway(struct uroute *route)
{
	return (route->gateway);
}

/*
 * Get the netmask.
 *
 * Returns NULL for a host route.
 */
const struct sockaddr *
uroute_get_netmask(struct uroute *route)
{
	return (route->netmask);
}

/*
 * Get the flags.
 */
int
uroute_get_flags(struct uroute *route)
{
	return (route->flags);
}

/*
 * Set the flags.
 */
void
uroute_set_flags(struct uroute *route, int flags)
{
	route->flags = flags;
}

/*
 * Add route to the kernel routing table.
 */
int
uroute_add(struct uroute *route)
{
	return (uroute_do_route(route, -1, RTM_ADD));
}

/*
 * Delete a route.
 */
int
uroute_delete(struct uroute *route)
{
	return (uroute_do_route(route, -1, RTM_DELETE));
}

/*
 * Get the installed kernel route that matches the destination "dest".
 */
struct uroute *
uroute_get(const struct sockaddr *dest)
{
	struct uroute *route = NULL;
	struct route_msg *msg = NULL;
	struct route_msg **list = NULL;
	struct sockaddr_dl sdl;
	int errno_save;
	int sock = -1;
	int num = 0;
	int seq;
	int i;

	/* Build new route message */
	if ((msg = route_msg_create()) == NULL)
		goto fail;
	seq = route_msg_get_seq(msg);
	route_msg_set_type(msg, RTM_GET);
	if (route_msg_set_dest(msg, dest) == -1)
		goto fail;
	memset(&sdl, 0, sizeof(sdl));
	sdl.sdl_type = IFT_OTHER;
	sdl.sdl_len = 8;
	sdl.sdl_family = AF_LINK;
	if (route_msg_set_ifp(msg, (struct sockaddr *)&sdl) == -1)
		goto fail;
	route_msg_set_flags(msg, RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_STATIC);

	/* Send request */
	if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
		goto fail;
	if (route_msg_send(msg, sock) == -1)
		goto fail;
	route_msg_destroy(&msg);
	msg = NULL;

	/* Get response */
	while (1) {

		/* Decode it */
		if ((num = route_msg_recv(&list, sock, TYPED_MEM_TEMP)) == -1)
			goto fail;

		/* Check returned messages for a match */
		for (i = 0; i < num; i++) {
			struct route_msg *const msg = list[i];

			if (route_msg_get_pid(msg) == getpid()
			    && route_msg_get_seq(msg) == seq) {
				if ((route = uroute_create(route_msg_get_dest(
				    msg), route_msg_get_gateway(msg),
				    route_msg_get_netmask(msg))) == NULL)
					goto fail;
				break;
			}
		}
		if (route != NULL)
			break;

		/* Free list and try again */
		for (i = 0; i < num; i++)
			route_msg_destroy(&list[i]);
		FREE(TYPED_MEM_TEMP, list);
		list = NULL;
	}

fail:
	/* Clean up and exit */
	errno_save = errno;
	if (sock != -1)
		(void)close(sock);
	if (msg != NULL)
		route_msg_destroy(&msg);
	if (list != NULL) {
		for (i = 0; i < num; i++)
			route_msg_destroy(&list[i]);
		FREE(TYPED_MEM_TEMP, list);
	}
	errno = errno_save;
	return (route);
}

/*
 * Build and send a route message by type.
 */
static int
uroute_do_route(struct uroute *route, int sock, int type)
{
	struct route_msg *msg;
	int close_sock = 0;
	int r = -1;
	int flags;

	/* Create new route message */
	if ((msg = route_msg_create()) == NULL)
		return (-1);

	/* Open socket if requested */
	if (sock == -1) {
		if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
			goto fail;
		close_sock = 1;
	}

	/* Build message */
	route_msg_set_type(msg, type);
	flags = route->flags & WRITABLE_FLAGS;
	flags |= RTF_UP;
	if (route->gateway->sa_family != AF_LINK)
		flags |= RTF_GATEWAY;
	if (route->netmask == NULL)
		flags |= RTF_HOST;
	else
		flags &= ~RTF_HOST;
	route_msg_set_flags(msg, flags);
	if (route_msg_set_dest(msg, route->dest) == -1)
		goto fail;
	if (route_msg_set_gateway(msg, route->gateway) == -1)
		goto fail;
	if (route_msg_set_netmask(msg, route->netmask) == -1)
		goto fail;

	/* Send messsage */
	r = route_msg_send(msg, sock);

fail:
	/* Clean up */
	route_msg_destroy(&msg);
	if (close_sock)
		(void)close(sock);

	/* Done */
	return (r);
}

/*
 * Get the entire routing table.
 */
int
uroute_get_all(struct uroute ***listp, const char *mtype)
{
	static int oid[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_DUMP, 0 };
	static const int noid = sizeof(oid) / sizeof(*oid);
	struct route_msg **list = NULL;
	struct uroute **routes = NULL;
	u_char *buf = NULL;
	size_t actual;
	size_t needed;
	int num = 0;
	int i;

	/* Get size estimate */
	if (sysctl(oid, noid, NULL, &needed, NULL, 0) < 0)
		goto fail;

	/* Allocate buffer for returned value */
	if ((buf = MALLOC(TYPED_MEM_TEMP, needed)) == NULL)
		goto fail;

	/* Get actual data */
	actual = needed;
	if (sysctl(oid, noid, buf, &actual, NULL, 0) < 0)
		goto fail;

	/* Decode route messages */
	if ((num = route_msg_decode(buf, actual, &list, TYPED_MEM_TEMP)) == -1)
		goto fail;

	/* Create routes array from route message array */
	if ((routes = MALLOC(mtype, num * sizeof(*routes))) == NULL)
		goto fail;
	memset(routes, 0, num * sizeof(*routes));
	for (i = 0; i < num; i++) {
		struct route_msg *const msg = list[i];

		if ((routes[i] = uroute_create(route_msg_get_dest(msg),
		    route_msg_get_gateway(msg),
		    route_msg_get_netmask(msg))) == NULL) {
			while (i > 0)
				uroute_destroy(&routes[--i]);
			FREE(mtype, routes);
			routes = NULL;
			goto fail;
		}
		routes[i]->flags = route_msg_get_flags(msg);
	}

fail:
	/* Clean up and exit */
	if (buf != NULL)
		FREE(TYPED_MEM_TEMP, buf);
	if (list != NULL) {
		for (i = 0; i < num; i++)
			route_msg_destroy(&list[i]);
		FREE(TYPED_MEM_TEMP, list);
	}
	if (routes != NULL) {
		*listp = routes;
		return (num);
	}
	return (-1);
}

/*
 * Print a route.
 */
void
uroute_print(struct uroute *route, FILE *fp)
{
	int didflag;
	int i;

	fprintf(fp, "dest %s gateway %s",
	    uroute_sockaddr_string(route->dest),
	    uroute_sockaddr_string(route->gateway));
	if (route->netmask != NULL) {
		fprintf(fp, " netmask %s",
		    uroute_sockaddr_string(route->netmask));
	}
	fprintf(fp, " flags=<");
	for (i = didflag = 0; route_flags[i].name != NULL; i++) {
		if ((route->flags & route_flags[i].bit) != 0) {
			if (didflag)
				fprintf(fp, ",");
			didflag = 1;
			fprintf(fp, "%s", route_flags[i].name);
		}
	}
	fprintf(fp, ">");
}

static const char *
uroute_sockaddr_string(const struct sockaddr *sa)
{
	static char buf[2][256];
	static int n;
	int i;

	n ^= 1;
	switch (sa->sa_family) {
	case AF_INET:
		strlcpy(buf[n], inet_ntoa(((struct sockaddr_in *)(void *)
		    sa)->sin_addr), sizeof(buf[n]));
		break;
	case AF_LINK:
		/* XXX implement me */
	default:
		snprintf(buf[n], sizeof(buf[n]), "{ len=%d af=%d data=[",
		    sa->sa_len, sa->sa_family);
		for (i = 2; i < sa->sa_len; i++) {
			snprintf(buf[n] + strlen(buf[n]),
			    sizeof(buf[n]) - strlen(buf[n]),
			    " %02X", ((u_char *)sa)[i]);
		}
		strlcat(buf[n], " ] }", sizeof(buf[n]));
	}
	return (buf[n]);
}


#ifdef UROUTE_TEST

int
main(int ac, char **av)
{
	struct sockaddr_in dest;
	struct sockaddr_in gateway;
	struct sockaddr_in netmask;
	struct uroute *route = NULL;
	int do_netmask = 0;
	int fail = 0;
	int cmd = 0;

	memset(&dest, 0, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_len = sizeof(dest);
	memset(&gateway, 0, sizeof(gateway));
	gateway.sin_family = AF_INET;
	gateway.sin_len = sizeof(gateway);
	memset(&netmask, 0, sizeof(netmask));
	netmask.sin_family = AF_INET;
	netmask.sin_len = sizeof(netmask);

	av++;
	ac--;
	switch (ac) {
	case 4:
		if (inet_aton(av[3], &netmask.sin_addr))
			;
		else {
			u_long haddr;

			if (sscanf(av[3], "%lu", &haddr) != 1)
				err(1, "invalid netmask \"%s\"", av[3]);
			netmask.sin_addr.s_addr = htonl(haddr);
		}
		do_netmask = 1;
		// fall through
	case 3:
		if (!inet_aton(av[2], &gateway.sin_addr))
			err(1, "invalid IP address \"%s\"", av[2]);
		if (!strcmp(av[0], "add"))
			cmd = 1;
		else if (!strcmp(av[0], "delete"))
			cmd = 2;
		else {
			fail = 1;
			break;
		}
		if (!inet_aton(av[1], &dest.sin_addr))
			err(1, "invalid IP address \"%s\"", av[1]);
		if ((route = uroute_create((struct sockaddr *)&dest,
		    (struct sockaddr *)&gateway,
		    do_netmask ? (struct sockaddr *)&netmask : NULL)) == NULL)
			err(1, "uroute_create");
		break;
	case 2:
		if (strcmp(av[0], "get") != 0) {
			fail = 1;
			break;
		}
		if (!inet_aton(av[1], &dest.sin_addr))
			err(1, "invalid IP address \"%s\"", av[1]);
		break;
	case 1:
		if (strcmp(av[0], "list") != 0) {
			fail = 1;
			break;
		}
		// fall through
	case 0:
		cmd = 3;
		break;
	default:
		fail = 1;
		break;
	}
	if (fail) {
		fprintf(stderr, "Usage: uroute <add | delete | get | list>"
		    " [dest [ gateway [netmask] ]]\n");
		exit(1);
	}

	switch (cmd) {
	case 0:				// get
		if ((route = uroute_get((struct sockaddr *)&dest)) == NULL)
			err(1, "uroute_get");
		uroute_print(route, stdout);
		printf("\n");
		uroute_destroy(&route);
		break;
	case 1:				// add
		if (uroute_add(route) == -1)
			err(1, "uroute_add");
		uroute_destroy(&route);
		break;
	case 2:				// delete
		if (uroute_delete(route) == -1)
			err(1, "uroute_delete");
		uroute_destroy(&route);
		break;
	case 3:				// list
	    {
		struct uroute **list;
		int num;
		int i;

		if ((num = uroute_get_all(&list, "main")) == -1)
			err(1, "uroute_get_all");
		for (i = 0; i < num; i++) {
			uroute_print(list[i], stdout);
			printf("\n");
		}
		for (i = 0; i < num; i++)
			uroute_destroy(&list[i]);
		FREE("main", list);
		break;
	    }
	default:
		assert(0);
	}

	/* Done */
	typed_mem_dump(stdout);
	return (0);
}
#endif


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>