ng_ether.c revision 82586
162143Sarchie 262143Sarchie/* 362143Sarchie * ng_ether.c 462143Sarchie * 562143Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc. 662143Sarchie * All rights reserved. 762143Sarchie * 862143Sarchie * Subject to the following obligations and disclaimer of warranty, use and 962143Sarchie * redistribution of this software, in source or object code forms, with or 1062143Sarchie * without modifications are expressly permitted by Whistle Communications; 1162143Sarchie * provided, however, that: 1262143Sarchie * 1. Any and all reproductions of the source or object code must include the 1362143Sarchie * copyright notice above and the following disclaimer of warranties; and 1462143Sarchie * 2. No rights are granted, in any manner or form, to use Whistle 1562143Sarchie * Communications, Inc. trademarks, including the mark "WHISTLE 1662143Sarchie * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1762143Sarchie * such appears in the above copyright notice or in the software. 1862143Sarchie * 1962143Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2062143Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2162143Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2262143Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2362143Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2462143Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2562143Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2662143Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2762143Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2862143Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2962143Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3062143Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3162143Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3262143Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3362143Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3462143Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3562143Sarchie * OF SUCH DAMAGE. 3662143Sarchie * 3762143Sarchie * Authors: Archie Cobbs <archie@freebsd.org> 3862143Sarchie * Julian Elischer <julian@freebsd.org> 3962143Sarchie * 4062143Sarchie * $FreeBSD: head/sys/netgraph/ng_ether.c 82586 2001-08-30 19:09:10Z archie $ 4162143Sarchie */ 4262143Sarchie 4362143Sarchie/* 4462143Sarchie * ng_ether(4) netgraph node type 4562143Sarchie */ 4662143Sarchie 4762143Sarchie#include <sys/param.h> 4862143Sarchie#include <sys/systm.h> 4962143Sarchie#include <sys/kernel.h> 5062143Sarchie#include <sys/malloc.h> 5162143Sarchie#include <sys/mbuf.h> 5262143Sarchie#include <sys/errno.h> 5362143Sarchie#include <sys/syslog.h> 5462143Sarchie#include <sys/socket.h> 5562143Sarchie 5662143Sarchie#include <net/if.h> 5762143Sarchie#include <net/if_types.h> 5862143Sarchie#include <net/if_arp.h> 5962143Sarchie#include <net/if_var.h> 6062143Sarchie#include <net/ethernet.h> 6162143Sarchie 6262143Sarchie#include <netgraph/ng_message.h> 6362143Sarchie#include <netgraph/netgraph.h> 6462143Sarchie#include <netgraph/ng_parse.h> 6562143Sarchie#include <netgraph/ng_ether.h> 6662143Sarchie 6762678Sjulian#define IFP2AC(IFP) ((struct arpcom *)IFP) 6862143Sarchie#define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) 6962143Sarchie 7062143Sarchie/* Per-node private data */ 7162143Sarchiestruct private { 7262143Sarchie struct ifnet *ifp; /* associated interface */ 7362143Sarchie hook_p upper; /* upper hook connection */ 7462143Sarchie hook_p lower; /* lower OR orphan hook connection */ 7562143Sarchie u_char lowerOrphan; /* whether lower is lower or orphan */ 7664358Sarchie u_char autoSrcAddr; /* always overwrite source address */ 7764358Sarchie u_char promisc; /* promiscuous mode enabled */ 7871849Sjulian u_int flags; /* flags e.g. really die */ 7962143Sarchie}; 8062143Sarchietypedef struct private *priv_p; 8162143Sarchie 8262143Sarchie/* Functional hooks called from if_ethersubr.c */ 8362143Sarchiestatic void ng_ether_input(struct ifnet *ifp, 8462143Sarchie struct mbuf **mp, struct ether_header *eh); 8562143Sarchiestatic void ng_ether_input_orphan(struct ifnet *ifp, 8662143Sarchie struct mbuf *m, struct ether_header *eh); 8762143Sarchiestatic int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 8862143Sarchiestatic void ng_ether_attach(struct ifnet *ifp); 8962143Sarchiestatic void ng_ether_detach(struct ifnet *ifp); 9062143Sarchie 9162143Sarchie/* Other functions */ 9262143Sarchiestatic void ng_ether_input2(node_p node, 9362143Sarchie struct mbuf **mp, struct ether_header *eh); 9462143Sarchiestatic int ng_ether_glueback_header(struct mbuf **mp, 9562143Sarchie struct ether_header *eh); 9662143Sarchiestatic int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 9762143Sarchiestatic int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 9862143Sarchie 9962143Sarchie/* Netgraph node methods */ 10062143Sarchiestatic ng_constructor_t ng_ether_constructor; 10162143Sarchiestatic ng_rcvmsg_t ng_ether_rcvmsg; 10270700Sjulianstatic ng_shutdown_t ng_ether_shutdown; 10362143Sarchiestatic ng_newhook_t ng_ether_newhook; 10469922Sjulianstatic ng_connect_t ng_ether_connect; 10562143Sarchiestatic ng_rcvdata_t ng_ether_rcvdata; 10662143Sarchiestatic ng_disconnect_t ng_ether_disconnect; 10762143Sarchiestatic int ng_ether_mod_event(module_t mod, int event, void *data); 10862143Sarchie 10964653Sarchie/* Parse type for an Ethernet address */ 11064653Sarchiestatic ng_parse_t ng_enaddr_parse; 11164653Sarchiestatic ng_unparse_t ng_enaddr_unparse; 11264653Sarchieconst struct ng_parse_type ng_ether_enaddr_type = { 11364653Sarchie NULL, 11464653Sarchie NULL, 11564653Sarchie NULL, 11664653Sarchie ng_enaddr_parse, 11764653Sarchie ng_enaddr_unparse, 11864653Sarchie NULL, /* no such thing as a "default" EN address */ 11964653Sarchie 0 12064358Sarchie}; 12164358Sarchie 12262143Sarchie/* List of commands and how to convert arguments to/from ASCII */ 12362143Sarchiestatic const struct ng_cmdlist ng_ether_cmdlist[] = { 12462143Sarchie { 12562143Sarchie NGM_ETHER_COOKIE, 12662143Sarchie NGM_ETHER_GET_IFNAME, 12762143Sarchie "getifname", 12862143Sarchie NULL, 12962143Sarchie &ng_parse_string_type 13062143Sarchie }, 13162143Sarchie { 13262143Sarchie NGM_ETHER_COOKIE, 13362143Sarchie NGM_ETHER_GET_IFINDEX, 13462143Sarchie "getifindex", 13562143Sarchie NULL, 13662143Sarchie &ng_parse_int32_type 13762143Sarchie }, 13864358Sarchie { 13964358Sarchie NGM_ETHER_COOKIE, 14064358Sarchie NGM_ETHER_GET_ENADDR, 14164358Sarchie "getenaddr", 14264358Sarchie NULL, 14364358Sarchie &ng_ether_enaddr_type 14464358Sarchie }, 14564358Sarchie { 14664358Sarchie NGM_ETHER_COOKIE, 14764653Sarchie NGM_ETHER_SET_ENADDR, 14864653Sarchie "setenaddr", 14964653Sarchie &ng_ether_enaddr_type, 15064653Sarchie NULL 15164653Sarchie }, 15264653Sarchie { 15364653Sarchie NGM_ETHER_COOKIE, 15464653Sarchie NGM_ETHER_GET_PROMISC, 15564653Sarchie "getpromisc", 15664653Sarchie NULL, 15764653Sarchie &ng_parse_int32_type 15864653Sarchie }, 15964653Sarchie { 16064653Sarchie NGM_ETHER_COOKIE, 16164358Sarchie NGM_ETHER_SET_PROMISC, 16264358Sarchie "setpromisc", 16364358Sarchie &ng_parse_int32_type, 16464358Sarchie NULL 16564358Sarchie }, 16664358Sarchie { 16764358Sarchie NGM_ETHER_COOKIE, 16864653Sarchie NGM_ETHER_GET_AUTOSRC, 16964653Sarchie "getautosrc", 17064653Sarchie NULL, 17164653Sarchie &ng_parse_int32_type 17264653Sarchie }, 17364653Sarchie { 17464653Sarchie NGM_ETHER_COOKIE, 17564358Sarchie NGM_ETHER_SET_AUTOSRC, 17664358Sarchie "setautosrc", 17764358Sarchie &ng_parse_int32_type, 17864358Sarchie NULL 17964358Sarchie }, 18062143Sarchie { 0 } 18162143Sarchie}; 18262143Sarchie 18362143Sarchiestatic struct ng_type ng_ether_typestruct = { 18470159Sjulian NG_ABI_VERSION, 18562143Sarchie NG_ETHER_NODE_TYPE, 18662143Sarchie ng_ether_mod_event, 18762143Sarchie ng_ether_constructor, 18862143Sarchie ng_ether_rcvmsg, 18970700Sjulian ng_ether_shutdown, 19062143Sarchie ng_ether_newhook, 19162143Sarchie NULL, 19269922Sjulian ng_ether_connect, 19362143Sarchie ng_ether_rcvdata, 19462143Sarchie ng_ether_disconnect, 19562143Sarchie ng_ether_cmdlist, 19662143Sarchie}; 19771047SjulianMODULE_VERSION(ng_ether, 1); 19862143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct); 19962143Sarchie 20062143Sarchie/****************************************************************** 20162143Sarchie ETHERNET FUNCTION HOOKS 20262143Sarchie******************************************************************/ 20362143Sarchie 20462143Sarchie/* 20562143Sarchie * Handle a packet that has come in on an interface. We get to 20662143Sarchie * look at it here before any upper layer protocols do. 20762143Sarchie * 20862143Sarchie * NOTE: this function will get called at splimp() 20962143Sarchie */ 21062143Sarchiestatic void 21162143Sarchieng_ether_input(struct ifnet *ifp, 21262143Sarchie struct mbuf **mp, struct ether_header *eh) 21362143Sarchie{ 21462143Sarchie const node_p node = IFP2NG(ifp); 21570784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 21662143Sarchie 21762143Sarchie /* If "lower" hook not connected, let packet continue */ 21862143Sarchie if (priv->lower == NULL || priv->lowerOrphan) 21962143Sarchie return; 22062143Sarchie ng_ether_input2(node, mp, eh); 22162143Sarchie} 22262143Sarchie 22362143Sarchie/* 22462143Sarchie * Handle a packet that has come in on an interface, and which 22562143Sarchie * does not match any of our known protocols (an ``orphan''). 22662143Sarchie * 22762143Sarchie * NOTE: this function will get called at splimp() 22862143Sarchie */ 22962143Sarchiestatic void 23062143Sarchieng_ether_input_orphan(struct ifnet *ifp, 23162143Sarchie struct mbuf *m, struct ether_header *eh) 23262143Sarchie{ 23362143Sarchie const node_p node = IFP2NG(ifp); 23470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 23562143Sarchie 23662143Sarchie /* If "orphan" hook not connected, let packet continue */ 23762143Sarchie if (priv->lower == NULL || !priv->lowerOrphan) { 23862143Sarchie m_freem(m); 23962143Sarchie return; 24062143Sarchie } 24162143Sarchie ng_ether_input2(node, &m, eh); 24262143Sarchie if (m != NULL) 24362143Sarchie m_freem(m); 24462143Sarchie} 24562143Sarchie 24662143Sarchie/* 24769922Sjulian * Handle a packet that has come in on an ethernet interface. 24862143Sarchie * The Ethernet header has already been detached from the mbuf, 24962143Sarchie * so we have to put it back. 25062143Sarchie * 25162143Sarchie * NOTE: this function will get called at splimp() 25262143Sarchie */ 25362143Sarchiestatic void 25462143Sarchieng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh) 25562143Sarchie{ 25670784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 25762143Sarchie int error; 25862143Sarchie 25962143Sarchie /* Glue Ethernet header back on */ 26062143Sarchie if ((error = ng_ether_glueback_header(mp, eh)) != 0) 26162143Sarchie return; 26262143Sarchie 26362143Sarchie /* Send out lower/orphan hook */ 26469922Sjulian NG_SEND_DATA_ONLY(error, priv->lower, *mp); 26562143Sarchie *mp = NULL; 26662143Sarchie} 26762143Sarchie 26862143Sarchie/* 26962143Sarchie * Handle a packet that is going out on an interface. 27062143Sarchie * The Ethernet header is already attached to the mbuf. 27162143Sarchie */ 27262143Sarchiestatic int 27362143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp) 27462143Sarchie{ 27562143Sarchie const node_p node = IFP2NG(ifp); 27670784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 27762143Sarchie int error = 0; 27862143Sarchie 27962143Sarchie /* If "upper" hook not connected, let packet continue */ 28062143Sarchie if (priv->upper == NULL) 28162143Sarchie return (0); 28262143Sarchie 28362143Sarchie /* Send it out "upper" hook */ 28470700Sjulian NG_SEND_DATA_ONLY(error, priv->upper, *mp); 28562143Sarchie return (error); 28662143Sarchie} 28762143Sarchie 28862143Sarchie/* 28962143Sarchie * A new Ethernet interface has been attached. 29062143Sarchie * Create a new node for it, etc. 29162143Sarchie */ 29262143Sarchiestatic void 29362143Sarchieng_ether_attach(struct ifnet *ifp) 29462143Sarchie{ 29562143Sarchie char name[IFNAMSIZ + 1]; 29662143Sarchie priv_p priv; 29762143Sarchie node_p node; 29862143Sarchie 29962143Sarchie /* Create node */ 30062143Sarchie KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __FUNCTION__)); 30162143Sarchie snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); 30262143Sarchie if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 30362143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 30462143Sarchie __FUNCTION__, "create node", name); 30562143Sarchie return; 30662143Sarchie } 30762143Sarchie 30862143Sarchie /* Allocate private data */ 30968876Sdwmalone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 31062143Sarchie if (priv == NULL) { 31162143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 31262143Sarchie __FUNCTION__, "allocate memory", name); 31370784Sjulian NG_NODE_UNREF(node); 31462143Sarchie return; 31562143Sarchie } 31670784Sjulian NG_NODE_SET_PRIVATE(node, priv); 31762143Sarchie priv->ifp = ifp; 31862143Sarchie IFP2NG(ifp) = node; 31964358Sarchie priv->autoSrcAddr = 1; 32062143Sarchie 32162143Sarchie /* Try to give the node the same name as the interface */ 32262143Sarchie if (ng_name_node(node, name) != 0) { 32362143Sarchie log(LOG_WARNING, "%s: can't name node %s\n", 32462143Sarchie __FUNCTION__, name); 32562143Sarchie } 32662143Sarchie} 32762143Sarchie 32862143Sarchie/* 32962143Sarchie * An Ethernet interface is being detached. 33071849Sjulian * REALLY Destroy its node. 33162143Sarchie */ 33262143Sarchiestatic void 33362143Sarchieng_ether_detach(struct ifnet *ifp) 33462143Sarchie{ 33562143Sarchie const node_p node = IFP2NG(ifp); 33671849Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 33762143Sarchie 33862143Sarchie if (node == NULL) /* no node (why not?), ignore */ 33962143Sarchie return; 34071849Sjulian NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 34171849Sjulian /* 34271849Sjulian * We can't assume the ifnet is still around when we run shutdown 34371849Sjulian * So zap it now. XXX We HOPE that anything running at this time 34471849Sjulian * handles it (as it should in the non netgraph case). 34571849Sjulian */ 34671849Sjulian IFP2NG(ifp) = NULL; 34771849Sjulian priv->ifp = NULL; /* XXX race if interrupted an output packet */ 34871849Sjulian ng_rmnode_self(node); /* remove all netgraph parts */ 34962143Sarchie} 35062143Sarchie 35162143Sarchie/* 35262143Sarchie * Optimization for gluing the Ethernet header back onto 35362143Sarchie * the front of an incoming packet. 35462143Sarchie */ 35562143Sarchiestatic int 35662143Sarchieng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh) 35762143Sarchie{ 35862143Sarchie struct mbuf *m = *mp; 35962143Sarchie uintfptr_t room; 36062143Sarchie int error = 0; 36162143Sarchie 36262143Sarchie /* 36362143Sarchie * Possibly the header is already on the front. 36462143Sarchie * If this is the case so just move the markers back 36562143Sarchie * to re-include it. We lucked out. 36662143Sarchie * This allows us to avoid a yucky m_pullup 36762143Sarchie * in later nodes if it works. 36862143Sarchie */ 36962143Sarchie if (eh == mtod(m, struct ether_header *) - 1) { 37062143Sarchie m->m_len += sizeof(*eh); 37162143Sarchie m->m_data -= sizeof(*eh); 37262143Sarchie m->m_pkthdr.len += sizeof(*eh); 37362143Sarchie goto done; 37462143Sarchie } 37562143Sarchie 37662143Sarchie /* 37762143Sarchie * Alternatively there may be room even though 37862143Sarchie * it is stored somewhere else. If so, copy it in. 37962143Sarchie * This only safe because we KNOW that this packet has 38062143Sarchie * just been generated by an ethernet card, so there are 38162143Sarchie * no aliases to the buffer (not so for outgoing packets). 38262143Sarchie * Nearly all ethernet cards will end up producing mbufs 38362143Sarchie * that fall into these cases. So we are not optimizing 38462143Sarchie * contorted cases. 38562143Sarchie */ 38662143Sarchie if ((m->m_flags & M_EXT) != 0) { 38762143Sarchie room = mtod(m, caddr_t) - m->m_ext.ext_buf; 38862143Sarchie if (room > m->m_ext.ext_size) /* garbage, fail immediately */ 38962143Sarchie room = 0; 39062143Sarchie } else 39162143Sarchie room = mtod(m, caddr_t) - m->m_pktdat; 39262143Sarchie 39362143Sarchie /* 39462143Sarchie * If we have room, just copy it and adjust 39562143Sarchie */ 39662143Sarchie if (room >= sizeof(*eh)) { 39762143Sarchie m->m_len += sizeof(*eh); 39862143Sarchie m->m_data -= sizeof(*eh); 39962143Sarchie m->m_pkthdr.len += sizeof(*eh); 40062143Sarchie goto copy; 40162143Sarchie } 40262143Sarchie 40362143Sarchie /* 40462143Sarchie * Doing anything more is likely to get more 40562143Sarchie * expensive than it's worth.. 40662143Sarchie * it's probable that everything else is in one 40762143Sarchie * big lump. The next node will do an m_pullup() 40862143Sarchie * for exactly the amount of data it needs and 40962143Sarchie * hopefully everything after that will not 41062143Sarchie * need one. So let's just use M_PREPEND. 41162143Sarchie */ 41262143Sarchie M_PREPEND(m, sizeof (*eh), M_DONTWAIT); 41362143Sarchie if (m == NULL) { 41462143Sarchie error = ENOBUFS; 41562143Sarchie goto done; 41662143Sarchie } 41762143Sarchie 41862143Sarchiecopy: 41962143Sarchie /* Copy header and return (possibly new) mbuf */ 42062143Sarchie bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh)); 42162143Sarchiedone: 42262143Sarchie *mp = m; 42362143Sarchie return error; 42462143Sarchie} 42562143Sarchie 42662143Sarchie/****************************************************************** 42762143Sarchie NETGRAPH NODE METHODS 42862143Sarchie******************************************************************/ 42962143Sarchie 43062143Sarchie/* 43162143Sarchie * It is not possible or allowable to create a node of this type. 43262143Sarchie * Nodes get created when the interface is attached (or, when 43362143Sarchie * this node type's KLD is loaded). 43462143Sarchie */ 43562143Sarchiestatic int 43670700Sjulianng_ether_constructor(node_p node) 43762143Sarchie{ 43862143Sarchie return (EINVAL); 43962143Sarchie} 44062143Sarchie 44162143Sarchie/* 44262143Sarchie * Check for attaching a new hook. 44362143Sarchie */ 44462143Sarchiestatic int 44562143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name) 44662143Sarchie{ 44770784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 44862143Sarchie u_char orphan = priv->lowerOrphan; 44962143Sarchie hook_p *hookptr; 45062143Sarchie 45162143Sarchie /* Divert hook is an alias for lower */ 45262143Sarchie if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 45362143Sarchie name = NG_ETHER_HOOK_LOWER; 45462143Sarchie 45562143Sarchie /* Which hook? */ 45662143Sarchie if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 45762143Sarchie hookptr = &priv->upper; 45862143Sarchie else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 45962143Sarchie hookptr = &priv->lower; 46062143Sarchie orphan = 0; 46162143Sarchie } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 46262143Sarchie hookptr = &priv->lower; 46362143Sarchie orphan = 1; 46462143Sarchie } else 46562143Sarchie return (EINVAL); 46662143Sarchie 46762143Sarchie /* Check if already connected (shouldn't be, but doesn't hurt) */ 46862143Sarchie if (*hookptr != NULL) 46962143Sarchie return (EISCONN); 47062143Sarchie 47162143Sarchie /* OK */ 47262143Sarchie *hookptr = hook; 47362143Sarchie priv->lowerOrphan = orphan; 47462143Sarchie return (0); 47562143Sarchie} 47662143Sarchie 47762143Sarchie/* 47869922Sjulian * Hooks are attached, adjust to force queueing. 47969922Sjulian * We don't really care which hook it is. 48069922Sjulian * they should all be queuing for outgoing data. 48169922Sjulian */ 48269922Sjulianstatic int 48369922Sjulianng_ether_connect(hook_p hook) 48469922Sjulian{ 48570784Sjulian NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 48669922Sjulian return (0); 48769922Sjulian} 48869922Sjulian 48969922Sjulian/* 49062143Sarchie * Receive an incoming control message. 49162143Sarchie */ 49262143Sarchiestatic int 49370700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 49462143Sarchie{ 49570784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 49662143Sarchie struct ng_mesg *resp = NULL; 49762143Sarchie int error = 0; 49870700Sjulian struct ng_mesg *msg; 49962143Sarchie 50070700Sjulian NGI_GET_MSG(item, msg); 50162143Sarchie switch (msg->header.typecookie) { 50262143Sarchie case NGM_ETHER_COOKIE: 50362143Sarchie switch (msg->header.cmd) { 50462143Sarchie case NGM_ETHER_GET_IFNAME: 50562143Sarchie NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 50662143Sarchie if (resp == NULL) { 50762143Sarchie error = ENOMEM; 50862143Sarchie break; 50962143Sarchie } 51062143Sarchie snprintf(resp->data, IFNAMSIZ + 1, 51162143Sarchie "%s%d", priv->ifp->if_name, priv->ifp->if_unit); 51262143Sarchie break; 51362143Sarchie case NGM_ETHER_GET_IFINDEX: 51462143Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 51562143Sarchie if (resp == NULL) { 51662143Sarchie error = ENOMEM; 51762143Sarchie break; 51862143Sarchie } 51962143Sarchie *((u_int32_t *)resp->data) = priv->ifp->if_index; 52062143Sarchie break; 52164358Sarchie case NGM_ETHER_GET_ENADDR: 52264358Sarchie NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 52364358Sarchie if (resp == NULL) { 52464358Sarchie error = ENOMEM; 52564358Sarchie break; 52664358Sarchie } 52764358Sarchie bcopy((IFP2AC(priv->ifp))->ac_enaddr, 52864358Sarchie resp->data, ETHER_ADDR_LEN); 52964358Sarchie break; 53064653Sarchie case NGM_ETHER_SET_ENADDR: 53164653Sarchie { 53264653Sarchie if (msg->header.arglen != ETHER_ADDR_LEN) { 53364653Sarchie error = EINVAL; 53464653Sarchie break; 53564653Sarchie } 53664653Sarchie error = if_setlladdr(priv->ifp, 53764653Sarchie (u_char *)msg->data, ETHER_ADDR_LEN); 53864653Sarchie break; 53964653Sarchie } 54064653Sarchie case NGM_ETHER_GET_PROMISC: 54164653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 54264653Sarchie if (resp == NULL) { 54364653Sarchie error = ENOMEM; 54464653Sarchie break; 54564653Sarchie } 54664653Sarchie *((u_int32_t *)resp->data) = priv->promisc; 54764653Sarchie break; 54864358Sarchie case NGM_ETHER_SET_PROMISC: 54964358Sarchie { 55064358Sarchie u_char want; 55164358Sarchie 55264358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 55364358Sarchie error = EINVAL; 55464358Sarchie break; 55564358Sarchie } 55664358Sarchie want = !!*((u_int32_t *)msg->data); 55764358Sarchie if (want ^ priv->promisc) { 55864358Sarchie if ((error = ifpromisc(priv->ifp, want)) != 0) 55964358Sarchie break; 56064358Sarchie priv->promisc = want; 56164358Sarchie } 56264358Sarchie break; 56364358Sarchie } 56464653Sarchie case NGM_ETHER_GET_AUTOSRC: 56564653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 56664653Sarchie if (resp == NULL) { 56764653Sarchie error = ENOMEM; 56864653Sarchie break; 56964653Sarchie } 57064653Sarchie *((u_int32_t *)resp->data) = priv->autoSrcAddr; 57164653Sarchie break; 57264358Sarchie case NGM_ETHER_SET_AUTOSRC: 57364358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 57464358Sarchie error = EINVAL; 57564358Sarchie break; 57664358Sarchie } 57764358Sarchie priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 57864358Sarchie break; 57962143Sarchie default: 58062143Sarchie error = EINVAL; 58162143Sarchie break; 58262143Sarchie } 58362143Sarchie break; 58462143Sarchie default: 58562143Sarchie error = EINVAL; 58662143Sarchie break; 58762143Sarchie } 58870700Sjulian NG_RESPOND_MSG(error, node, item, resp); 58970700Sjulian NG_FREE_MSG(msg); 59062143Sarchie return (error); 59162143Sarchie} 59262143Sarchie 59362143Sarchie/* 59462143Sarchie * Receive data on a hook. 59562143Sarchie */ 59662143Sarchiestatic int 59770700Sjulianng_ether_rcvdata(hook_p hook, item_p item) 59862143Sarchie{ 59970784Sjulian const node_p node = NG_HOOK_NODE(hook); 60070784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 60170700Sjulian struct mbuf *m; 60270700Sjulian meta_p meta; 60362143Sarchie 60470700Sjulian NGI_GET_M(item, m); 60570700Sjulian NGI_GET_META(item, meta); 60670700Sjulian NG_FREE_ITEM(item); 60762143Sarchie if (hook == priv->lower) 60862143Sarchie return ng_ether_rcv_lower(node, m, meta); 60962143Sarchie if (hook == priv->upper) 61062143Sarchie return ng_ether_rcv_upper(node, m, meta); 61162143Sarchie panic("%s: weird hook", __FUNCTION__); 61262143Sarchie} 61362143Sarchie 61462143Sarchie/* 61562143Sarchie * Handle an mbuf received on the "lower" hook. 61662143Sarchie */ 61762143Sarchiestatic int 61862143Sarchieng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 61962143Sarchie{ 62070784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 62162143Sarchie 62262143Sarchie /* Make sure header is fully pulled up */ 62362143Sarchie if (m->m_pkthdr.len < sizeof(struct ether_header)) { 62470700Sjulian NG_FREE_M(m); 62570700Sjulian NG_FREE_META(meta); 62662143Sarchie return (EINVAL); 62762143Sarchie } 62862143Sarchie if (m->m_len < sizeof(struct ether_header) 62962143Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { 63062143Sarchie NG_FREE_META(meta); 63162143Sarchie return (ENOBUFS); 63262143Sarchie } 63362143Sarchie 63464358Sarchie /* Drop in the MAC address if desired */ 63564358Sarchie if (priv->autoSrcAddr) { 63664358Sarchie bcopy((IFP2AC(priv->ifp))->ac_enaddr, 63764358Sarchie mtod(m, struct ether_header *)->ether_shost, 63864358Sarchie ETHER_ADDR_LEN); 63964358Sarchie } 64062678Sjulian 64162143Sarchie /* Send it on its way */ 64262143Sarchie NG_FREE_META(meta); 64362143Sarchie return ether_output_frame(priv->ifp, m); 64462143Sarchie} 64562143Sarchie 64662143Sarchie/* 64762143Sarchie * Handle an mbuf received on the "upper" hook. 64862143Sarchie */ 64962143Sarchiestatic int 65062143Sarchieng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 65162143Sarchie{ 65270784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 65362143Sarchie struct ether_header *eh; 65462143Sarchie 65562143Sarchie /* Check length and pull off header */ 65662143Sarchie if (m->m_pkthdr.len < sizeof(*eh)) { 65770700Sjulian NG_FREE_M(m); 65870700Sjulian NG_FREE_META(meta); 65962143Sarchie return (EINVAL); 66062143Sarchie } 66162143Sarchie if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) { 66262143Sarchie NG_FREE_META(meta); 66362143Sarchie return (ENOBUFS); 66462143Sarchie } 66562143Sarchie eh = mtod(m, struct ether_header *); 66662143Sarchie m->m_data += sizeof(*eh); 66762143Sarchie m->m_len -= sizeof(*eh); 66862143Sarchie m->m_pkthdr.len -= sizeof(*eh); 66966061Sjulian m->m_pkthdr.rcvif = priv->ifp; 67062143Sarchie 67162143Sarchie /* Route packet back in */ 67262143Sarchie NG_FREE_META(meta); 67362143Sarchie ether_demux(priv->ifp, eh, m); 67462143Sarchie return (0); 67562143Sarchie} 67662143Sarchie 67762143Sarchie/* 67871849Sjulian * Shutdown node. This resets the node but does not remove it 67971849Sjulian * unless the REALLY_DIE flag is set. 68062143Sarchie */ 68162143Sarchiestatic int 68270700Sjulianng_ether_shutdown(node_p node) 68362143Sarchie{ 68470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 68564358Sarchie 68664358Sarchie if (priv->promisc) { /* disable promiscuous mode */ 68764358Sarchie (void)ifpromisc(priv->ifp, 0); 68864358Sarchie priv->promisc = 0; 68964358Sarchie } 69071849Sjulian if (node->nd_flags & NG_REALLY_DIE) { 69171849Sjulian /* 69271849Sjulian * WE came here because the ethernet card is being unloaded, 69371849Sjulian * so stop being persistant. 69471849Sjulian * Actually undo all the things we did on creation. 69571849Sjulian * Assume the ifp has already been freed. 69671849Sjulian */ 69771849Sjulian NG_NODE_SET_PRIVATE(node, NULL); 69871849Sjulian FREE(priv, M_NETGRAPH); 69971849Sjulian NG_NODE_UNREF(node); /* free node itself */ 70071849Sjulian return (0); 70170700Sjulian } 70264358Sarchie priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 70371849Sjulian node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 70462143Sarchie return (0); 70562143Sarchie} 70662143Sarchie 70762143Sarchie/* 70862143Sarchie * Hook disconnection. 70962143Sarchie */ 71062143Sarchiestatic int 71162143Sarchieng_ether_disconnect(hook_p hook) 71262143Sarchie{ 71370784Sjulian const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 71462143Sarchie 71562143Sarchie if (hook == priv->upper) 71662143Sarchie priv->upper = NULL; 71762143Sarchie else if (hook == priv->lower) { 71862143Sarchie priv->lower = NULL; 71962143Sarchie priv->lowerOrphan = 0; 72062143Sarchie } else 72162143Sarchie panic("%s: weird hook", __FUNCTION__); 72270784Sjulian if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 72370784Sjulian && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 72470784Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 72562143Sarchie return (0); 72662143Sarchie} 72762143Sarchie 72864653Sarchiestatic int 72964653Sarchieng_enaddr_parse(const struct ng_parse_type *type, 73064653Sarchie const char *s, int *const off, const u_char *const start, 73164653Sarchie u_char *const buf, int *const buflen) 73264653Sarchie{ 73364653Sarchie char *eptr; 73464653Sarchie u_long val; 73564653Sarchie int i; 73664653Sarchie 73764653Sarchie if (*buflen < ETHER_ADDR_LEN) 73864653Sarchie return (ERANGE); 73964653Sarchie for (i = 0; i < ETHER_ADDR_LEN; i++) { 74064653Sarchie val = strtoul(s + *off, &eptr, 16); 74164653Sarchie if (val > 0xff || eptr == s + *off) 74264653Sarchie return (EINVAL); 74364653Sarchie buf[i] = (u_char)val; 74464653Sarchie *off = (eptr - s); 74564653Sarchie if (i < ETHER_ADDR_LEN - 1) { 74664653Sarchie if (*eptr != ':') 74764653Sarchie return (EINVAL); 74864653Sarchie (*off)++; 74964653Sarchie } 75064653Sarchie } 75164653Sarchie *buflen = ETHER_ADDR_LEN; 75264653Sarchie return (0); 75364653Sarchie} 75464653Sarchie 75564653Sarchiestatic int 75664653Sarchieng_enaddr_unparse(const struct ng_parse_type *type, 75764653Sarchie const u_char *data, int *off, char *cbuf, int cbuflen) 75864653Sarchie{ 75964653Sarchie int len; 76064653Sarchie 76164653Sarchie len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", 76265305Sarchie data[*off], data[*off + 1], data[*off + 2], 76365305Sarchie data[*off + 3], data[*off + 4], data[*off + 5]); 76464653Sarchie if (len >= cbuflen) 76564653Sarchie return (ERANGE); 76664653Sarchie *off += ETHER_ADDR_LEN; 76764653Sarchie return (0); 76864653Sarchie} 76964653Sarchie 77062143Sarchie/****************************************************************** 77162143Sarchie INITIALIZATION 77262143Sarchie******************************************************************/ 77362143Sarchie 77462143Sarchie/* 77562143Sarchie * Handle loading and unloading for this node type. 77662143Sarchie */ 77762143Sarchiestatic int 77862143Sarchieng_ether_mod_event(module_t mod, int event, void *data) 77962143Sarchie{ 78062143Sarchie struct ifnet *ifp; 78162143Sarchie int error = 0; 78262143Sarchie int s; 78362143Sarchie 78462143Sarchie s = splnet(); 78562143Sarchie switch (event) { 78662143Sarchie case MOD_LOAD: 78762143Sarchie 78862143Sarchie /* Register function hooks */ 78962143Sarchie if (ng_ether_attach_p != NULL) { 79062143Sarchie error = EEXIST; 79162143Sarchie break; 79262143Sarchie } 79362143Sarchie ng_ether_attach_p = ng_ether_attach; 79462143Sarchie ng_ether_detach_p = ng_ether_detach; 79562143Sarchie ng_ether_output_p = ng_ether_output; 79662143Sarchie ng_ether_input_p = ng_ether_input; 79762143Sarchie ng_ether_input_orphan_p = ng_ether_input_orphan; 79862143Sarchie 79962143Sarchie /* Create nodes for any already-existing Ethernet interfaces */ 80062143Sarchie TAILQ_FOREACH(ifp, &ifnet, if_link) { 80182586Sarchie if (ifp->if_type == IFT_ETHER 80282586Sarchie || ifp->if_type == IFT_L2VLAN) 80362143Sarchie ng_ether_attach(ifp); 80462143Sarchie } 80562143Sarchie break; 80662143Sarchie 80762143Sarchie case MOD_UNLOAD: 80862143Sarchie 80962143Sarchie /* 81062143Sarchie * Note that the base code won't try to unload us until 81162143Sarchie * all nodes have been removed, and that can't happen 81262143Sarchie * until all Ethernet interfaces are removed. In any 81362143Sarchie * case, we know there are no nodes left if the action 81462143Sarchie * is MOD_UNLOAD, so there's no need to detach any nodes. 81562143Sarchie */ 81662143Sarchie 81762143Sarchie /* Unregister function hooks */ 81862143Sarchie ng_ether_attach_p = NULL; 81962143Sarchie ng_ether_detach_p = NULL; 82062143Sarchie ng_ether_output_p = NULL; 82162143Sarchie ng_ether_input_p = NULL; 82262143Sarchie ng_ether_input_orphan_p = NULL; 82362143Sarchie break; 82462143Sarchie 82562143Sarchie default: 82662143Sarchie error = EOPNOTSUPP; 82762143Sarchie break; 82862143Sarchie } 82962143Sarchie splx(s); 83062143Sarchie return (error); 83162143Sarchie} 83262143Sarchie 833