From owner-freebsd-net@FreeBSD.ORG Mon Oct 18 11:56:33 2004 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id C1DED16A4CF for ; Mon, 18 Oct 2004 11:56:33 +0000 (GMT) Received: from cell.sick.ru (cell.sick.ru [217.72.144.68]) by mx1.FreeBSD.org (Postfix) with ESMTP id D740243D31 for ; Mon, 18 Oct 2004 11:56:32 +0000 (GMT) (envelope-from glebius@freebsd.org) Received: from cell.sick.ru (glebius@localhost [127.0.0.1]) by cell.sick.ru (8.12.11/8.12.8) with ESMTP id i9IBuVRJ072407 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Mon, 18 Oct 2004 15:56:31 +0400 (MSD) (envelope-from glebius@freebsd.org) Received: (from glebius@localhost) by cell.sick.ru (8.12.11/8.12.11/Submit) id i9IBuUgq072406 for net@freebsd.org; Mon, 18 Oct 2004 15:56:30 +0400 (MSD) (envelope-from glebius@freebsd.org) X-Authentication-Warning: cell.sick.ru: glebius set sender to glebius@freebsd.org using -f Date: Mon, 18 Oct 2004 15:56:30 +0400 From: Gleb Smirnoff To: net@freebsd.org Message-ID: <20041018115630.GA72342@cell.sick.ru> References: <20041010142942.GA13032@cell.sick.ru> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="zhXaljGHf11kAtnf" Content-Disposition: inline In-Reply-To: <20041010142942.GA13032@cell.sick.ru> User-Agent: Mutt/1.5.6i Subject: Re: [REVIEW/TEST] netgraph node to wrap interface X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 18 Oct 2004 11:56:34 -0000 --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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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--