Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Oct 2004 18:29:42 +0400
From:      Gleb Smirnoff <glebius@freebsd.org>
To:        net@freebsd.org
Subject:   [REVIEW/TEST] netgraph node to wrap interface
Message-ID:  <20041010142942.GA13032@cell.sick.ru>

next in thread | raw e-mail | index | archive | help

--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=koi8-r
Content-Disposition: inline

  This node is just a proof of concept. At this moment a small
number of interfaces is supported. Supported interfaces are those,
who have if_input method defined (all have if_output method defined,
AFAIK).

Hook semantics are very similar to ng_ether. You have "upper" and
"lower" hooks. In most setups mbufs coming from upper should
later be sent on lower, and vice versa. However, you can modify
them or just read in a netgraph chain.

Sample usage is:

/usr/sbin/ngctl -f- <<-SEQ
        mkpeer ifwrap qq upper
        name .:qq wrap_fxp0
        disconnect .:qq
        msg wrap_fxp0: attach "fxp0"
SEQ
# race?
sleep 1
/usr/sbin/ngctl -f- <<-SEQ
        mkpeer wrap_fxp0: tee upper right
        connect wrap_fxp0: wrap_fxp0:upper lower left
SEQ

-- 
Totus tuus, Glebius.
GLEBIUS-RIPN GLEB-RIPE

--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=koi8-r
Content-Disposition: attachment; filename="ng_ifwrap.c"

/*-
 * Copyright (c) 2004 Gleb Smirnoff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON 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 ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#if 1
#define DFUNC(msg) printf("ifwrap: %s: %s\n", __func__, msg);
#define DLINE(msg) printf("ifwrap: -%d-: %s", __LINE__, msg );
#else
#define DFUNC(msg)
#define DLINE(msg)
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <sys/socket.h>

#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>

#include <netgraph/ng_message.h>
#include <netgraph/ng_parse.h>
#include <netgraph/netgraph.h>

#include <netgraph/ng_ifwrap.h>

#define ERROUT(x)	do { error = (x); goto done; } while (0)

/* Netgraph methods */
static ng_constructor_t	ng_ifwrap_constructor;
static ng_rcvmsg_t	ng_ifwrap_rcvmsg;
static ng_shutdown_t	ng_ifwrap_shutdown;
static ng_newhook_t	ng_ifwrap_newhook;
static ng_rcvdata_t	ng_ifwrap_rcvdata;
static ng_disconnect_t	ng_ifwrap_disconnect;

/* New routines for interface */
static int	ng_ifwrap_output(struct ifnet *, struct mbuf *,
			struct sockaddr *, struct rtentry *);
static void	ng_ifwrap_input(struct ifnet *, struct mbuf *);

/*
 * Our internal tag to store next hop and rtentry. It is declared
 * here, since noone except of this node should take care of it.
 */
MALLOC_DEFINE(M_IFWRAP_TAGS, "ng_ifwrap tags", "packet-attached information");
struct ifwrap_tag {
	struct m_tag    mt;
	struct rtentry	*rt;
	struct sockaddr sa;
};

/* Methods for our tags */
static struct ifwrap_tag * ifwrap_tag_alloc(void);
static void ifwrap_tag_free(struct m_tag *);

/* List of commands and how to convert arguments to/from ASCII */
static const struct ng_cmdlist ng_ifwrap_cmdlist[] = {
	{
	  NGM_IFWRAP_COOKIE,
	  NGM_IFWRAP_ATTACH,
	  "attach",
	  &ng_parse_string_type,
	  NULL
	},
	{ 0 }
};

/* Netgraph node type descriptor */
static struct ng_type ng_ifwrap_typestruct = {
	.version =	NG_ABI_VERSION,
	.name =		NG_IFWRAP_NODE_TYPE,
	.constructor =	ng_ifwrap_constructor,
	.rcvmsg =	ng_ifwrap_rcvmsg,
	.shutdown =	ng_ifwrap_shutdown,
	.newhook =	ng_ifwrap_newhook,
	.rcvdata =	ng_ifwrap_rcvdata,
	.disconnect =	ng_ifwrap_disconnect,
	.cmdlist =	ng_ifwrap_cmdlist,
};
NETGRAPH_INIT(ifwrap, &ng_ifwrap_typestruct);

/* Information we store for each node */
struct ng_ifwrap_priv {
	struct ifnet	*ifp;		/* pointer to our ifnet */
	node_p		node;		/* back pointer to node */
	hook_p		upper;		/* hook for input */
	hook_p		lower;		/* hook for output */

	/* Pointers to original routines */
        int	(*if_output) (struct ifnet *, struct mbuf *, struct sockaddr *,
                     struct rtentry *);
        void 	(*if_input)  (struct ifnet *, struct mbuf *);
};
typedef struct ng_ifwrap_priv *priv_p;

/* This is where we store pointer from iface to node private date. This
 * makes us incompatible with ng_fec(4).
 */
#define IFP2NG(ifp)	(priv_p )(ifp->if_afdata[AF_NETGRAPH])
#define IFP2NG_SET(ifp, val)	ifp->if_afdata[AF_NETGRAPH] = (val);

/******************************************************************************
 *  Netgraph methods
 ******************************************************************************/

static int
ng_ifwrap_constructor(node_p node)
{
	priv_p priv;

	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
	if (priv == NULL)
		return (ENOMEM);

	NG_NODE_SET_PRIVATE(node, priv);
	priv->node = node;

	return (0);
}

/*
 * Hooks are almost the same as ng_ether's, and so is this callback.
 */
static int
ng_ifwrap_newhook(node_p node, hook_p hook, const char *name)
{
	const priv_p priv = NG_NODE_PRIVATE(node);
	hook_p *hookptr;

	if (strcmp(name, NG_IFWRAP_HOOK_UPPER) == 0)
		hookptr = &priv->upper;
	else if (strcmp(name, NG_IFWRAP_HOOK_LOWER) == 0)
		hookptr = &priv->lower;
	else
		return (EINVAL);

	/* Check if already connected */
        if (*hookptr != NULL)
		return (EISCONN);

	*hookptr = hook;

	return (0);
}

static int
ng_ifwrap_rcvmsg(node_p node, item_p item, hook_p lasthook)
{
	const priv_p priv = NG_NODE_PRIVATE(node);
	struct ng_mesg *msg, *resp = NULL;
	int error = 0;

	NGI_GET_MSG(item, msg);

	switch (msg->header.typecookie) {
	case NGM_IFWRAP_COOKIE: 
		switch (msg->header.cmd) {
		case NGM_IFWRAP_ATTACH:

			/* Check if we are already initialized */
			if (priv->ifp != NULL)
				ERROUT(EISCONN);

			if (msg->header.arglen == 0)
				ERROUT(EINVAL);

			if ((priv->ifp = ifunit((char *)msg->data)) == NULL)
				ERROUT(ENOENT);

			/*
			 * Not all interfaces have both input and output
			 * method. Those are not supported.
			 */
			if (priv->ifp->if_input == NULL ||
			    priv->ifp->if_output == NULL)
				ERROUT(ENOTSUP);

			/*
			 * XXX: There is no mutex to lock struct ifnet yet,
			 * so we will hold afdata_mtx for the whole surgery
			 * procedure. This will not stop races, since other
			 * struct-ifnet-surgeons does not do it same way.
			 */
			IF_AFDATA_LOCK(priv->ifp);

			/* Check if someone already have grabbed AF_NETGRAPH */
			if(IFP2NG(priv->ifp) != NULL) {
				IF_AFDATA_UNLOCK(priv->ifp);
				priv->ifp = NULL;
				ERROUT(EISCONN);
			}

			IFP2NG_SET(priv->ifp, priv);
			priv->if_input = priv->ifp->if_input;
			priv->ifp->if_input = ng_ifwrap_input;
			priv->if_output = priv->ifp->if_output;
			priv->ifp->if_output = ng_ifwrap_output;

			IF_AFDATA_UNLOCK(priv->ifp);

			break;
		default:
			error = EINVAL;
			break;
		}
		break;
	default:
		error = EINVAL;
		break;
	}

done:
	NG_RESPOND_MSG(error, node, item, resp);
	NG_FREE_MSG(msg);

	return(error);
}

static int
ng_ifwrap_rcvdata(hook_p hook, item_p item )
{
	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	struct mbuf *m;
	int error = 0;

	NGI_GET_M(item, m);
	NG_FREE_ITEM(item);

	/* Check if we have attached interface */
	if (priv->ifp == NULL) {
		NG_FREE_M(m);
		return (ENOTCONN);
	}

	if (hook == priv->upper) {
		(priv->if_input)(priv->ifp, m);
		return (0);
	} else if (hook == priv->lower) {
		struct ifwrap_tag *tag;
		struct sockaddr	*dst;

		tag = (struct ifwrap_tag *)m_tag_locate(m, NGM_IFWRAP_COOKIE,
		    NG_IFWRAP_TAG_OUTPUT, NULL);
		if (tag == NULL) {
			DFUNC("no tag in input packet");
			NG_FREE_M(m);
			return (EDESTADDRREQ);
		}

		dst = &tag->sa;
		
		error =  (priv->if_output)(priv->ifp, m, dst, tag->rt);

		return (error);
	} else
		panic("ng_ifwrap: unknown hook");

	/* not reach */
	return (0);
}

static int
ng_ifwrap_shutdown(node_p node)
{
	const priv_p priv = NG_NODE_PRIVATE(node);

	if (priv->ifp != NULL) {
		IF_AFDATA_LOCK(priv->ifp);

		IFP2NG_SET(priv->ifp, NULL);

		/* Restore old methods */
		priv->ifp->if_input = priv->if_input;
		priv->ifp->if_output = priv->if_output;

		IF_AFDATA_UNLOCK(priv->ifp);
	}

	NG_NODE_UNREF(node);
	FREE(priv, M_NETGRAPH);

	return (0);
}

static int
ng_ifwrap_disconnect(hook_p hook)
{
	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));

	if (hook == priv->lower)
		priv->lower = NULL;

	if (hook == priv->upper)
		priv->upper = NULL;

	return (0);
}

static int
ng_ifwrap_output(struct ifnet *ifp, struct mbuf *m,
	struct sockaddr *dst, struct rtentry *rt)
{
	const priv_p priv = IFP2NG(ifp);
	struct ifwrap_tag *tag;
	int error = 0;

	DFUNC("in");
	if (priv->upper == NULL)
		return (priv->if_output)(ifp, m, dst, rt);

	/* Save rt and dst */
	if ((tag = ifwrap_tag_alloc()) == NULL) {
		m_freem(m);
		return (ENOMEM);
	}
	bcopy(dst, &tag->sa, dst->sa_len);

	/* do not allow ip_output() to free our rt */
	if (rt != NULL) {
		RT_LOCK(rt);
		RT_ADDREF(rt);
		RT_UNLOCK(rt);

		tag->rt = rt;
	} else
		tag->rt = NULL;

	m_tag_prepend(m, &tag->mt);

	NG_SEND_DATA_ONLY(error, priv->upper, m);

	return (error);
}

static void
ng_ifwrap_input(struct ifnet *ifp, struct mbuf *m)
{
	const priv_p priv = IFP2NG(ifp);
	int error;

	DFUNC("in");
	if (priv->lower == NULL)
		return (priv->if_input)(ifp, m);

	NG_SEND_DATA_ONLY(error, priv->lower, m);

	return;
}

/******************************************************************************
 *  Helper functions
 ******************************************************************************/

static struct ifwrap_tag *
ifwrap_tag_alloc()
{
	struct ifwrap_tag *tag;

	DFUNC("in");

	/* XXX: cut'n'paste from uipc_mbuf2.c:m_tag_alloc() */
	MBUF_CHECKSLEEP(M_NOWAIT);
	MALLOC(tag, struct ifwrap_tag *, sizeof(struct ifwrap_tag), M_IFWRAP_TAGS, M_NOWAIT);
	if (tag == NULL)
		return (NULL);
	m_tag_setup((struct m_tag *)tag, NGM_IFWRAP_COOKIE, NG_IFWRAP_TAG_OUTPUT,
	    (sizeof(struct ifwrap_tag) - sizeof(struct m_tag)));
	tag->mt.m_tag_free = ifwrap_tag_free;

	return (tag);
}

static void
ifwrap_tag_free(struct m_tag *mt)
{
	struct ifwrap_tag *tag = (struct ifwrap_tag *)mt;

	DFUNC("in");
	if (tag->rt != NULL)
		RTFREE(tag->rt);

	free(tag, M_IFWRAP_TAGS);
}

--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=koi8-r
Content-Disposition: attachment; filename="ng_ifwrap.h"

/*-
 * Copyright (c) 2004 Gleb Smirnoff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHIFWRAP IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#ifndef _NETGRAPH_NG_IFWRAP_H_
#define _NETGRAPH_NG_IFWRAP_H_

#define NG_IFWRAP_NODE_TYPE	"ifwrap"
#define NGM_IFWRAP_COOKIE	1094849975

/* Hook names, just like in ng_ether */
#define NG_IFWRAP_HOOK_LOWER     "lower"	/* -> input */
#define NG_IFWRAP_HOOK_UPPER     "upper"	/* -> output */

/* Tags */
enum {
	NG_IFWRAP_TAG_OUTPUT,	/* stores parameters of if_output() */
};

/* Netgraph commands */
enum {
	NGM_IFWRAP_ATTACH,	/* attach to interface */
};

#endif /* _NETGRAPH_NG_IFWRAP_H_ */

--YZ5djTAD1cGYuMQK--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20041010142942.GA13032>