File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / net / route_msg.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, 10 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 <arpa/inet.h>

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

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

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

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

struct route_msg {
	int		type;		/* message type */
	int		index;		/* index for associated ifp */
	int		flags;		/* flags, incl. kern & msg, e.g. DONE */
	pid_t		pid;		/* for sender to identify action */
	int		seq;		/* for sender to identify action */
	int		error;		/* why failed */
	struct sockaddr	*dest;		/* destination sockaddr */
	struct sockaddr	*gateway;	/* gateway sockaddr */
	struct sockaddr	*netmask;	/* netmask sockaddr */
	struct sockaddr	*genmask;	/* genmask sockaddr */
	struct sockaddr	*ifp;		/* interface name sockaddr */
	struct sockaddr	*ifa;		/* interface addr sockaddr */
	struct sockaddr	*author;	/* author of redirect sockaddr */
	struct sockaddr	*brd;		/* for NEWADDR, broadcast or p2p dest */
};

/*
 * Internal variables
 */

/* List of sockaddr's in a route message from the kernel */
struct msg_sock {
	const char	*name;
	u_int		offset;
};

static const	struct msg_sock msg_socks[] = {
	{ "dest",	offsetof(struct route_msg, dest)	},
	{ "gateway",	offsetof(struct route_msg, gateway)	},
	{ "netmask",	offsetof(struct route_msg, netmask)	},
	{ "genmask",	offsetof(struct route_msg, genmask)	},
	{ "ifp",	offsetof(struct route_msg, ifp)		},
	{ "ifa",	offsetof(struct route_msg, ifa)		},
	{ "author",	offsetof(struct route_msg, author)	},
	{ "brd",	offsetof(struct route_msg, brd)		},
	{ NULL, 0 }
};

static int	prev_seq;		/* last used sequence number */

/*
 * Internal functions
 */
static int	route_msg_set_sa(struct route_msg *msg,
			const char *name, const struct sockaddr *sa);

/*
 * Create a new route message.
 */
struct route_msg *
route_msg_create(void)
{
	struct route_msg *msg;

	if ((msg = MALLOC("route_msg", sizeof(*msg))) == NULL)
		return (NULL);
	memset(msg, 0, sizeof(*msg));
	msg->seq = ++prev_seq;				/* XXX not atomic */
	msg->pid = getpid();
	return (msg);
}

/*
 * Destroy a routing message.
 */
void
route_msg_destroy(struct route_msg **msgp)
{
	struct route_msg *const msg = *msgp;

	if (msg == NULL)
		return;
	FREE("route_msg.dest", msg->dest);
	FREE("route_msg.gateway", msg->gateway);
	FREE("route_msg.netmask", msg->netmask);
	FREE("route_msg.genmask", msg->genmask);
	FREE("route_msg.ifp", msg->ifp);
	FREE("route_msg.ifa", msg->ifa);
	FREE("route_msg.author", msg->author);
	FREE("route_msg.brd", msg->brd);
	FREE("route_msg", msg);
	*msgp = NULL;
}

/*
 * Get message type.
 */
int
route_msg_get_type(struct route_msg *msg)
{
	return (msg->type);
}

/*
 * Set message type.
 */
void
route_msg_set_type(struct route_msg *msg, int type)
{
	msg->type = type;
}

/*
 * Get message index.
 */
int
route_msg_get_index(struct route_msg *msg)
{
	return (msg->index);
}

/*
 * Set message index.
 */
void
route_msg_set_index(struct route_msg *msg, int index)
{
	msg->index = index;
}

/*
 * Get message flags.
 */
int
route_msg_get_flags(struct route_msg *msg)
{
	return (msg->flags);
}

/*
 * Set message flags.
 */
void
route_msg_set_flags(struct route_msg *msg, int flags)
{
	msg->flags = flags;
}

/*
 * Get message error code.
 */
int
route_msg_get_error(struct route_msg *msg)
{
	return (msg->error);
}

/*
 * Get message process ID.
 */
pid_t
route_msg_get_pid(struct route_msg *msg)
{
	return (msg->pid);
}

/*
 * Get message sequence number.
 */
int
route_msg_get_seq(struct route_msg *msg)
{
	return (msg->seq);
}

/*
 * Get message destination.
 */
const struct sockaddr *
route_msg_get_dest(struct route_msg *msg)
{
	return (msg->dest);
}

/*
 * Set message destination.
 */
int
route_msg_set_dest(struct route_msg *msg, const struct sockaddr *dest)
{
	return (route_msg_set_sa(msg, "dest", dest));
}

/*
 * Get message gateway.
 */
const struct sockaddr *
route_msg_get_gateway(struct route_msg *msg)
{
	return (msg->gateway);
}

/*
 * Set message gateway.
 */
int
route_msg_set_gateway(struct route_msg *msg, const struct sockaddr *gateway)
{
	return (route_msg_set_sa(msg, "gateway", gateway));
}

/*
 * Get message netmask.
 */
const struct sockaddr *
route_msg_get_netmask(struct route_msg *msg)
{
	return (msg->netmask);
}

/*
 * Set message netmask.
 */
int
route_msg_set_netmask(struct route_msg *msg, const struct sockaddr *netmask)
{
	return (route_msg_set_sa(msg, "netmask", netmask));
}

/*
 * Get message cloning netmask.
 */
const struct sockaddr *
route_msg_get_genmask(struct route_msg *msg)
{
	return (msg->genmask);
}

/*
 * Set message cloning netmask.
 */
int
route_msg_set_genmask(struct route_msg *msg, const struct sockaddr *genmask)
{
	return (route_msg_set_sa(msg, "genmask", genmask));
}

/*
 * Get message interface name.
 */
const struct sockaddr *
route_msg_get_ifp(struct route_msg *msg)
{
	return (msg->ifp);
}

/*
 * Set message interface name.
 */
int
route_msg_set_ifp(struct route_msg *msg, const struct sockaddr *ifp)
{
	return (route_msg_set_sa(msg, "ifp", ifp));
}

/*
 * Get message interface address.
 */
const struct sockaddr *
route_msg_get_ifa(struct route_msg *msg)
{
	return (msg->ifa);
}

/*
 * Set message interface address.
 */
int
route_msg_set_ifa(struct route_msg *msg, const struct sockaddr *ifa)
{
	return (route_msg_set_sa(msg, "ifa", ifa));
}

/*
 * Get message author of the redirect message.
 */
const struct sockaddr *
route_msg_get_author(struct route_msg *msg)
{
	return (msg->author);
}

/*
 * Set message author of the redirect message.
 */
int
route_msg_set_author(struct route_msg *msg, const struct sockaddr *author)
{
	return (route_msg_set_sa(msg, "author", author));
}

/*
 * Get message broadcast or point-to-point destination address.
 */
const struct sockaddr *
route_msg_get_brd(struct route_msg *msg)
{
	return (msg->brd);
}

/*
 * Set message broadcast or point-to-point destination address.
 */
int
route_msg_set_brd(struct route_msg *msg, const struct sockaddr *brd)
{
	return (route_msg_set_sa(msg, "brd", brd));
}

/*
 * Set one of the struct sockaddr fields.
 */
static int
route_msg_set_sa(struct route_msg *msg,
	const char *name, const struct sockaddr *sa)
{
	struct sockaddr *copy;
	struct sockaddr **sp;
	char buf[32];
	int i;

	for (i = 0; msg_socks[i].name != NULL
	    && strcmp(name, msg_socks[i].name) != 0; i++);
	assert(msg_socks[i].name != NULL);
	sp = (struct sockaddr **)(void *)((u_char *)msg + msg_socks[i].offset);
	snprintf(buf, sizeof(buf), "route_msg.%s", name);
	if (sa != NULL) {
		if ((copy = MALLOC(buf, sa->sa_len)) == NULL)
			return (-1);
		memcpy(copy, sa, sa->sa_len);
	} else
		copy = NULL;
	FREE(buf, *sp);
	*sp = copy;
	return (0);
}

/*
 * Decode one or more routing messages from raw socket data.
 *
 * Returns the number of decoded messages, or -1 for error.
 * The array of struct route_msg pointers is returned in *listp,
 * in an array allocated with memory type 'mtype'.
 */
int
route_msg_decode(const u_char *data, size_t dlen,
	struct route_msg ***listp, const char *mtype)
{
	struct route_msg **list;
	struct rt_msghdr rtm;
	int posn;
	int num;
	int i;

	/* Count number of messages */
	for (num = posn = 0; posn < dlen; num++, posn += rtm.rtm_msglen) {
		if (dlen - posn < sizeof(rtm)) {
			errno = EINVAL;
			return (-1);
		}
		memcpy(&rtm, data + posn, sizeof(rtm.rtm_msglen));
		if (rtm.rtm_msglen < sizeof(rtm)) {
			errno = EINVAL;
			return (-1);
		}
	}

	/* Allocate list */
	if ((list = MALLOC(mtype, num * sizeof(*list))) == NULL)
		return (-1);
	memset(list, 0, num * sizeof(*list));

	/* Decode each message */
	for (i = posn = 0; i < num; i++, posn += rtm.rtm_msglen) {
		int posn2;
		int j;

		/* Create new route_msg object */
		if ((list[i] = route_msg_create()) == NULL)
			goto fail;

		/* Copy fields from route message header */
		memcpy(&rtm, data + posn, sizeof(rtm));
		list[i]->type = rtm.rtm_type;
		list[i]->index = rtm.rtm_index;
		list[i]->flags = rtm.rtm_flags;
		list[i]->pid = rtm.rtm_pid;
		list[i]->seq = rtm.rtm_seq;
		list[i]->error = rtm.rtm_errno;

		/* Create struct sockaddr's from each appended sockaddr */
		for (posn2 = posn + sizeof(rtm), j = 0;
		    msg_socks[j].name != NULL; j++) {
			const struct sockaddr *const sa
			    = (struct sockaddr *)(data + posn2);

			if ((rtm.rtm_addrs & (1 << j)) == 0)
				continue;
			if (sa->sa_len != 0) {
				if (route_msg_set_sa(list[i],
				    msg_socks[j].name, sa) == -1)
					goto fail;
			}
			ADVANCE(posn2, sa);
		}
	}

	/* Done */
	*listp = list;
	return (num);

fail:
	/* Cleanup after failure */
	for (i = 0; i < num; i++)
		route_msg_destroy(&list[i]);
	FREE(mtype, list);
	return (-1);
}

/*
 * Encode a route message into raw binary form.
 *
 * Returns the total encoded length, or -1 for error.
 */
int
route_msg_encode(const struct route_msg *msg, u_char *data, size_t dlen)
{
	struct rt_msghdr rtm;
	int totalLen;
	int i;

	/* Prepare header */
	if ((totalLen = sizeof(rtm)) > dlen) {
		errno = EMSGSIZE;
		return (-1);
	}
	memset(&rtm, 0, sizeof(rtm));
	rtm.rtm_version = RTM_VERSION;
	rtm.rtm_type = msg->type;
	rtm.rtm_index = msg->index;
	rtm.rtm_flags = msg->flags;
	rtm.rtm_pid = msg->pid;
	rtm.rtm_seq = msg->seq;
	rtm.rtm_errno = msg->error;

	/* Append struct sockaddr's */
	for (i = 0; msg_socks[i].name != NULL; i++) {
		struct sockaddr *const sa = *((struct sockaddr **)(void *)
		    ((u_char *)msg + msg_socks[i].offset));
		int len;

		if (sa == NULL)
			continue;
		rtm.rtm_addrs |= (1 << i);
		len = ROUNDUP(sa->sa_len);
		if (totalLen + len > dlen) {
			errno = EMSGSIZE;
			return (-1);
		}
		memcpy(data + totalLen, sa, sa->sa_len);
		memset(data + totalLen + sa->sa_len, 0, len - sa->sa_len);
		totalLen += len;
	}

	/* Copy in completed header */
	rtm.rtm_msglen = totalLen;
	memcpy(data, &rtm, sizeof(rtm));

	/* Done */
	return (totalLen);
}

/*
 * Send a route message to a socket.
 */
int
route_msg_send(struct route_msg *msg, int sock)
{
	u_char buf[512];
	int count;
	int elen;
	int r;

	if ((elen = route_msg_encode(msg, buf, sizeof(buf))) == -1)
		return (-1);
	for (count = 0; count < 32; count++) {
		if ((r = send(sock, buf, elen, 0)) >= 0)
			return (0);
		switch (errno) {
		case ENETUNREACH:
		case ESRCH:
			break;
		default:
			return (-1);
		}
	}
	return (-1);
}

/*
 * Receive route messages from a socket.
 */
int
route_msg_recv(struct route_msg ***listp, int sock, const char *mtype)
{
	u_char buf[512];
	int r;

	/* Read next raw message array */
	if ((r = recv(sock, buf, sizeof(buf), 0)) < 0)
		return (-1);

	/* Decode it */
	return (route_msg_decode(buf, r, listp, mtype));
}

/*
 * Print route message as a string.
 */
void
route_msg_print(struct route_msg *msg, FILE *fp)
{
	const char *typestr = NULL;

	fprintf(fp, "type=");
	switch (msg->type) {
	case RTM_ADD:		typestr = "ADD"; break;
	case RTM_DELETE:	typestr = "DELETE"; break;
	case RTM_CHANGE:	typestr = "CHANGE"; break;
	case RTM_GET:		typestr = "GET"; break;
	case RTM_LOSING:	typestr = "LOSING"; break;
	case RTM_REDIRECT:	typestr = "REDIRECT"; break;
	case RTM_MISS:		typestr = "MISS"; break;
	case RTM_LOCK:		typestr = "LOCK"; break;
	case RTM_OLDADD:	typestr = "OLDADD"; break;
	case RTM_OLDDEL:	typestr = "OLDDEL"; break;
	case RTM_RESOLVE:	typestr = "RESOLVE"; break;
	case RTM_NEWADDR:	typestr = "NEWADDR"; break;
	case RTM_DELADDR:	typestr = "DELADDR"; break;
	case RTM_IFINFO:	typestr = "IFINFO"; break;
	case RTM_NEWMADDR:	typestr = "NEWMADDR"; break;
	case RTM_DELMADDR:	typestr = "DELMADDR"; break;
	default:
		fprintf(fp, "%d", msg->type);
		break;
	}
	if (typestr != NULL)
		fprintf(fp, "%s", typestr);
	fprintf(fp, " index=%d flags=0x%x pid=%lu seq=%d errno=%d",
	    msg->index, msg->flags, (u_long)msg->pid, msg->seq, msg->error);
	if (msg->dest != NULL)
		fprintf(fp, " dest=%p", msg->dest);
	if (msg->gateway != NULL)
		fprintf(fp, " gateway=%p", msg->gateway);
	if (msg->netmask != NULL)
		fprintf(fp, " netmask=%p", msg->netmask);
	if (msg->genmask != NULL)
		fprintf(fp, " genmask=%p", msg->genmask);
	if (msg->ifp != NULL)
		fprintf(fp, " ifp=%p", msg->ifp);
	if (msg->ifa != NULL)
		fprintf(fp, " ifa=%p", msg->ifa);
	if (msg->author != NULL)
		fprintf(fp, " author=%p", msg->author);
	if (msg->brd != NULL)
		fprintf(fp, " brd=%p", msg->brd);
	fflush(fp);
}


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