Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 18 Oct 2004 15:56:30 +0400
From:      Gleb Smirnoff <glebius@freebsd.org>
To:        net@freebsd.org
Subject:   Re: [REVIEW/TEST] netgraph node to wrap interface
Message-ID:  <20041018115630.GA72342@cell.sick.ru>
In-Reply-To: <20041010142942.GA13032@cell.sick.ru>
References:  <20041010142942.GA13032@cell.sick.ru>

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

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

A more simple version for review, which can be built on latest CURRENT,
where m_tag_free_default() function is defined in sys/mbuf.h.

On Sun, Oct 10, 2004 at 06:29:42PM +0400, Gleb Smirnoff wrote:
T>   This node is just a proof of concept. At this moment a small
T> number of interfaces is supported. Supported interfaces are those,
T> who have if_input method defined (all have if_output method defined,
T> AFAIK).
T> 
T> Hook semantics are very similar to ng_ether. You have "upper" and
T> "lower" hooks. In most setups mbufs coming from upper should
T> later be sent on lower, and vice versa. However, you can modify
T> them or just read in a netgraph chain.
T> 
T> Sample usage is:
T> 
T> /usr/sbin/ngctl -f- <<-SEQ
T>         mkpeer ifwrap qq upper
T>         name .:qq wrap_fxp0
T>         disconnect .:qq
T>         msg wrap_fxp0: attach "fxp0"
T> SEQ
T> # race?
T> sleep 1
T> /usr/sbin/ngctl -f- <<-SEQ
T>         mkpeer wrap_fxp0: tee upper right
T>         connect wrap_fxp0: wrap_fxp0:upper lower left
T> SEQ

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

--zhXaljGHf11kAtnf
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 0
#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.
 */
struct ifwrap_tag {
	struct m_tag    mt;
	struct rtentry	*rt;
	struct sockaddr sa;
};
#define	TAGSIZ	(sizeof(struct ifwrap_tag) - sizeof(struct m_tag))

/* Free method for our tags */
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 in mbuf tag */
	if ((tag = (struct ifwrap_tag *)m_tag_alloc(NGM_IFWRAP_COOKIE,
	    NG_IFWRAP_TAG_OUTPUT, TAGSIZ, M_DONTWAIT)) == NULL) {
		m_freem(m);
		return (ENOMEM);
	}
	tag->mt.m_tag_free = &ifwrap_tag_free;
	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 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);

	m_tag_free_default(mt);
}

--zhXaljGHf11kAtnf
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_ */

--zhXaljGHf11kAtnf--



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