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>