ng_ether.c revision 70700
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 70700 2001-01-06 00:46:47Z julian $ 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 */ 7862143Sarchie}; 7962143Sarchietypedef struct private *priv_p; 8062143Sarchie 8162143Sarchie/* Functional hooks called from if_ethersubr.c */ 8262143Sarchiestatic void ng_ether_input(struct ifnet *ifp, 8362143Sarchie struct mbuf **mp, struct ether_header *eh); 8462143Sarchiestatic void ng_ether_input_orphan(struct ifnet *ifp, 8562143Sarchie struct mbuf *m, struct ether_header *eh); 8662143Sarchiestatic int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 8762143Sarchiestatic void ng_ether_attach(struct ifnet *ifp); 8862143Sarchiestatic void ng_ether_detach(struct ifnet *ifp); 8962143Sarchie 9062143Sarchie/* Other functions */ 9162143Sarchiestatic void ng_ether_input2(node_p node, 9262143Sarchie struct mbuf **mp, struct ether_header *eh); 9362143Sarchiestatic int ng_ether_glueback_header(struct mbuf **mp, 9462143Sarchie struct ether_header *eh); 9562143Sarchiestatic int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 9662143Sarchiestatic int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 9762143Sarchie 9862143Sarchie/* Netgraph node methods */ 9962143Sarchiestatic ng_constructor_t ng_ether_constructor; 10062143Sarchiestatic ng_rcvmsg_t ng_ether_rcvmsg; 10170700Sjulianstatic ng_shutdown_t ng_ether_shutdown; 10262143Sarchiestatic ng_newhook_t ng_ether_newhook; 10369922Sjulianstatic ng_connect_t ng_ether_connect; 10462143Sarchiestatic ng_rcvdata_t ng_ether_rcvdata; 10562143Sarchiestatic ng_disconnect_t ng_ether_disconnect; 10662143Sarchiestatic int ng_ether_mod_event(module_t mod, int event, void *data); 10762143Sarchie 10864653Sarchie/* Parse type for an Ethernet address */ 10964653Sarchiestatic ng_parse_t ng_enaddr_parse; 11064653Sarchiestatic ng_unparse_t ng_enaddr_unparse; 11164653Sarchieconst struct ng_parse_type ng_ether_enaddr_type = { 11264653Sarchie NULL, 11364653Sarchie NULL, 11464653Sarchie NULL, 11564653Sarchie ng_enaddr_parse, 11664653Sarchie ng_enaddr_unparse, 11764653Sarchie NULL, /* no such thing as a "default" EN address */ 11864653Sarchie 0 11964358Sarchie}; 12064358Sarchie 12162143Sarchie/* List of commands and how to convert arguments to/from ASCII */ 12262143Sarchiestatic const struct ng_cmdlist ng_ether_cmdlist[] = { 12362143Sarchie { 12462143Sarchie NGM_ETHER_COOKIE, 12562143Sarchie NGM_ETHER_GET_IFNAME, 12662143Sarchie "getifname", 12762143Sarchie NULL, 12862143Sarchie &ng_parse_string_type 12962143Sarchie }, 13062143Sarchie { 13162143Sarchie NGM_ETHER_COOKIE, 13262143Sarchie NGM_ETHER_GET_IFINDEX, 13362143Sarchie "getifindex", 13462143Sarchie NULL, 13562143Sarchie &ng_parse_int32_type 13662143Sarchie }, 13764358Sarchie { 13864358Sarchie NGM_ETHER_COOKIE, 13964358Sarchie NGM_ETHER_GET_ENADDR, 14064358Sarchie "getenaddr", 14164358Sarchie NULL, 14264358Sarchie &ng_ether_enaddr_type 14364358Sarchie }, 14464358Sarchie { 14564358Sarchie NGM_ETHER_COOKIE, 14664653Sarchie NGM_ETHER_SET_ENADDR, 14764653Sarchie "setenaddr", 14864653Sarchie &ng_ether_enaddr_type, 14964653Sarchie NULL 15064653Sarchie }, 15164653Sarchie { 15264653Sarchie NGM_ETHER_COOKIE, 15364653Sarchie NGM_ETHER_GET_PROMISC, 15464653Sarchie "getpromisc", 15564653Sarchie NULL, 15664653Sarchie &ng_parse_int32_type 15764653Sarchie }, 15864653Sarchie { 15964653Sarchie NGM_ETHER_COOKIE, 16064358Sarchie NGM_ETHER_SET_PROMISC, 16164358Sarchie "setpromisc", 16264358Sarchie &ng_parse_int32_type, 16364358Sarchie NULL 16464358Sarchie }, 16564358Sarchie { 16664358Sarchie NGM_ETHER_COOKIE, 16764653Sarchie NGM_ETHER_GET_AUTOSRC, 16864653Sarchie "getautosrc", 16964653Sarchie NULL, 17064653Sarchie &ng_parse_int32_type 17164653Sarchie }, 17264653Sarchie { 17364653Sarchie NGM_ETHER_COOKIE, 17464358Sarchie NGM_ETHER_SET_AUTOSRC, 17564358Sarchie "setautosrc", 17664358Sarchie &ng_parse_int32_type, 17764358Sarchie NULL 17864358Sarchie }, 17962143Sarchie { 0 } 18062143Sarchie}; 18162143Sarchie 18262143Sarchiestatic struct ng_type ng_ether_typestruct = { 18370159Sjulian NG_ABI_VERSION, 18462143Sarchie NG_ETHER_NODE_TYPE, 18562143Sarchie ng_ether_mod_event, 18662143Sarchie ng_ether_constructor, 18762143Sarchie ng_ether_rcvmsg, 18870700Sjulian ng_ether_shutdown, 18962143Sarchie ng_ether_newhook, 19062143Sarchie NULL, 19169922Sjulian ng_ether_connect, 19262143Sarchie ng_ether_rcvdata, 19362143Sarchie ng_ether_disconnect, 19462143Sarchie ng_ether_cmdlist, 19562143Sarchie}; 19662143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct); 19762143Sarchie 19862143Sarchie/****************************************************************** 19962143Sarchie ETHERNET FUNCTION HOOKS 20062143Sarchie******************************************************************/ 20162143Sarchie 20262143Sarchie/* 20362143Sarchie * Handle a packet that has come in on an interface. We get to 20462143Sarchie * look at it here before any upper layer protocols do. 20562143Sarchie * 20662143Sarchie * NOTE: this function will get called at splimp() 20762143Sarchie */ 20862143Sarchiestatic void 20962143Sarchieng_ether_input(struct ifnet *ifp, 21062143Sarchie struct mbuf **mp, struct ether_header *eh) 21162143Sarchie{ 21262143Sarchie const node_p node = IFP2NG(ifp); 21362143Sarchie const priv_p priv = node->private; 21462143Sarchie 21562143Sarchie /* If "lower" hook not connected, let packet continue */ 21662143Sarchie if (priv->lower == NULL || priv->lowerOrphan) 21762143Sarchie return; 21862143Sarchie ng_ether_input2(node, mp, eh); 21962143Sarchie} 22062143Sarchie 22162143Sarchie/* 22262143Sarchie * Handle a packet that has come in on an interface, and which 22362143Sarchie * does not match any of our known protocols (an ``orphan''). 22462143Sarchie * 22562143Sarchie * NOTE: this function will get called at splimp() 22662143Sarchie */ 22762143Sarchiestatic void 22862143Sarchieng_ether_input_orphan(struct ifnet *ifp, 22962143Sarchie struct mbuf *m, struct ether_header *eh) 23062143Sarchie{ 23162143Sarchie const node_p node = IFP2NG(ifp); 23262143Sarchie const priv_p priv = node->private; 23362143Sarchie 23462143Sarchie /* If "orphan" hook not connected, let packet continue */ 23562143Sarchie if (priv->lower == NULL || !priv->lowerOrphan) { 23662143Sarchie m_freem(m); 23762143Sarchie return; 23862143Sarchie } 23962143Sarchie ng_ether_input2(node, &m, eh); 24062143Sarchie if (m != NULL) 24162143Sarchie m_freem(m); 24262143Sarchie} 24362143Sarchie 24462143Sarchie/* 24569922Sjulian * Handle a packet that has come in on an ethernet interface. 24662143Sarchie * The Ethernet header has already been detached from the mbuf, 24762143Sarchie * so we have to put it back. 24862143Sarchie * 24962143Sarchie * NOTE: this function will get called at splimp() 25062143Sarchie */ 25162143Sarchiestatic void 25262143Sarchieng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh) 25362143Sarchie{ 25462143Sarchie const priv_p priv = node->private; 25562143Sarchie int error; 25662143Sarchie 25762143Sarchie /* Glue Ethernet header back on */ 25862143Sarchie if ((error = ng_ether_glueback_header(mp, eh)) != 0) 25962143Sarchie return; 26062143Sarchie 26162143Sarchie /* Send out lower/orphan hook */ 26269922Sjulian NG_SEND_DATA_ONLY(error, priv->lower, *mp); 26362143Sarchie *mp = NULL; 26462143Sarchie} 26562143Sarchie 26662143Sarchie/* 26762143Sarchie * Handle a packet that is going out on an interface. 26862143Sarchie * The Ethernet header is already attached to the mbuf. 26962143Sarchie */ 27062143Sarchiestatic int 27162143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp) 27262143Sarchie{ 27362143Sarchie const node_p node = IFP2NG(ifp); 27462143Sarchie const priv_p priv = node->private; 27562143Sarchie int error = 0; 27662143Sarchie 27762143Sarchie /* If "upper" hook not connected, let packet continue */ 27862143Sarchie if (priv->upper == NULL) 27962143Sarchie return (0); 28062143Sarchie 28162143Sarchie /* Send it out "upper" hook */ 28270700Sjulian NG_SEND_DATA_ONLY(error, priv->upper, *mp); 28362143Sarchie return (error); 28462143Sarchie} 28562143Sarchie 28662143Sarchie/* 28762143Sarchie * A new Ethernet interface has been attached. 28862143Sarchie * Create a new node for it, etc. 28962143Sarchie */ 29062143Sarchiestatic void 29162143Sarchieng_ether_attach(struct ifnet *ifp) 29262143Sarchie{ 29362143Sarchie char name[IFNAMSIZ + 1]; 29462143Sarchie priv_p priv; 29562143Sarchie node_p node; 29662143Sarchie 29762143Sarchie /* Create node */ 29862143Sarchie KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __FUNCTION__)); 29962143Sarchie snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); 30062143Sarchie if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 30162143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 30262143Sarchie __FUNCTION__, "create node", name); 30362143Sarchie return; 30462143Sarchie } 30562143Sarchie 30662143Sarchie /* Allocate private data */ 30768876Sdwmalone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 30862143Sarchie if (priv == NULL) { 30962143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 31062143Sarchie __FUNCTION__, "allocate memory", name); 31162143Sarchie ng_unref(node); 31262143Sarchie return; 31362143Sarchie } 31462143Sarchie node->private = priv; 31562143Sarchie priv->ifp = ifp; 31662143Sarchie IFP2NG(ifp) = node; 31764358Sarchie priv->autoSrcAddr = 1; 31862143Sarchie 31962143Sarchie /* Try to give the node the same name as the interface */ 32062143Sarchie if (ng_name_node(node, name) != 0) { 32162143Sarchie log(LOG_WARNING, "%s: can't name node %s\n", 32262143Sarchie __FUNCTION__, name); 32362143Sarchie } 32462143Sarchie} 32562143Sarchie 32662143Sarchie/* 32762143Sarchie * An Ethernet interface is being detached. 32862143Sarchie * Destroy its node. 32962143Sarchie */ 33062143Sarchiestatic void 33162143Sarchieng_ether_detach(struct ifnet *ifp) 33262143Sarchie{ 33362143Sarchie const node_p node = IFP2NG(ifp); 33462143Sarchie priv_p priv; 33562143Sarchie 33662143Sarchie if (node == NULL) /* no node (why not?), ignore */ 33762143Sarchie return; 33870700Sjulian ng_rmnode_self(node); /* break all links to other nodes */ 33963195Sarchie node->flags |= NG_INVALID; 34062143Sarchie IFP2NG(ifp) = NULL; /* detach node from interface */ 34162143Sarchie priv = node->private; /* free node private info */ 34262143Sarchie bzero(priv, sizeof(*priv)); 34362143Sarchie FREE(priv, M_NETGRAPH); 34462143Sarchie node->private = NULL; 34562143Sarchie ng_unref(node); /* free node itself */ 34662143Sarchie} 34762143Sarchie 34862143Sarchie/* 34962143Sarchie * Optimization for gluing the Ethernet header back onto 35062143Sarchie * the front of an incoming packet. 35162143Sarchie */ 35262143Sarchiestatic int 35362143Sarchieng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh) 35462143Sarchie{ 35562143Sarchie struct mbuf *m = *mp; 35662143Sarchie uintfptr_t room; 35762143Sarchie int error = 0; 35862143Sarchie 35962143Sarchie /* 36062143Sarchie * Possibly the header is already on the front. 36162143Sarchie * If this is the case so just move the markers back 36262143Sarchie * to re-include it. We lucked out. 36362143Sarchie * This allows us to avoid a yucky m_pullup 36462143Sarchie * in later nodes if it works. 36562143Sarchie */ 36662143Sarchie if (eh == mtod(m, struct ether_header *) - 1) { 36762143Sarchie m->m_len += sizeof(*eh); 36862143Sarchie m->m_data -= sizeof(*eh); 36962143Sarchie m->m_pkthdr.len += sizeof(*eh); 37062143Sarchie goto done; 37162143Sarchie } 37262143Sarchie 37362143Sarchie /* 37462143Sarchie * Alternatively there may be room even though 37562143Sarchie * it is stored somewhere else. If so, copy it in. 37662143Sarchie * This only safe because we KNOW that this packet has 37762143Sarchie * just been generated by an ethernet card, so there are 37862143Sarchie * no aliases to the buffer (not so for outgoing packets). 37962143Sarchie * Nearly all ethernet cards will end up producing mbufs 38062143Sarchie * that fall into these cases. So we are not optimizing 38162143Sarchie * contorted cases. 38262143Sarchie */ 38362143Sarchie if ((m->m_flags & M_EXT) != 0) { 38462143Sarchie room = mtod(m, caddr_t) - m->m_ext.ext_buf; 38562143Sarchie if (room > m->m_ext.ext_size) /* garbage, fail immediately */ 38662143Sarchie room = 0; 38762143Sarchie } else 38862143Sarchie room = mtod(m, caddr_t) - m->m_pktdat; 38962143Sarchie 39062143Sarchie /* 39162143Sarchie * If we have room, just copy it and adjust 39262143Sarchie */ 39362143Sarchie if (room >= sizeof(*eh)) { 39462143Sarchie m->m_len += sizeof(*eh); 39562143Sarchie m->m_data -= sizeof(*eh); 39662143Sarchie m->m_pkthdr.len += sizeof(*eh); 39762143Sarchie goto copy; 39862143Sarchie } 39962143Sarchie 40062143Sarchie /* 40162143Sarchie * Doing anything more is likely to get more 40262143Sarchie * expensive than it's worth.. 40362143Sarchie * it's probable that everything else is in one 40462143Sarchie * big lump. The next node will do an m_pullup() 40562143Sarchie * for exactly the amount of data it needs and 40662143Sarchie * hopefully everything after that will not 40762143Sarchie * need one. So let's just use M_PREPEND. 40862143Sarchie */ 40962143Sarchie M_PREPEND(m, sizeof (*eh), M_DONTWAIT); 41062143Sarchie if (m == NULL) { 41162143Sarchie error = ENOBUFS; 41262143Sarchie goto done; 41362143Sarchie } 41462143Sarchie 41562143Sarchiecopy: 41662143Sarchie /* Copy header and return (possibly new) mbuf */ 41762143Sarchie bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh)); 41862143Sarchiedone: 41962143Sarchie *mp = m; 42062143Sarchie return error; 42162143Sarchie} 42262143Sarchie 42362143Sarchie/****************************************************************** 42462143Sarchie NETGRAPH NODE METHODS 42562143Sarchie******************************************************************/ 42662143Sarchie 42762143Sarchie/* 42862143Sarchie * It is not possible or allowable to create a node of this type. 42962143Sarchie * Nodes get created when the interface is attached (or, when 43062143Sarchie * this node type's KLD is loaded). 43162143Sarchie */ 43262143Sarchiestatic int 43370700Sjulianng_ether_constructor(node_p node) 43462143Sarchie{ 43562143Sarchie return (EINVAL); 43662143Sarchie} 43762143Sarchie 43862143Sarchie/* 43962143Sarchie * Check for attaching a new hook. 44062143Sarchie */ 44162143Sarchiestatic int 44262143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name) 44362143Sarchie{ 44462143Sarchie const priv_p priv = node->private; 44562143Sarchie u_char orphan = priv->lowerOrphan; 44662143Sarchie hook_p *hookptr; 44762143Sarchie 44862143Sarchie /* Divert hook is an alias for lower */ 44962143Sarchie if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 45062143Sarchie name = NG_ETHER_HOOK_LOWER; 45162143Sarchie 45262143Sarchie /* Which hook? */ 45362143Sarchie if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 45462143Sarchie hookptr = &priv->upper; 45562143Sarchie else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 45662143Sarchie hookptr = &priv->lower; 45762143Sarchie orphan = 0; 45862143Sarchie } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 45962143Sarchie hookptr = &priv->lower; 46062143Sarchie orphan = 1; 46162143Sarchie } else 46262143Sarchie return (EINVAL); 46362143Sarchie 46462143Sarchie /* Check if already connected (shouldn't be, but doesn't hurt) */ 46562143Sarchie if (*hookptr != NULL) 46662143Sarchie return (EISCONN); 46762143Sarchie 46862143Sarchie /* OK */ 46962143Sarchie *hookptr = hook; 47062143Sarchie priv->lowerOrphan = orphan; 47162143Sarchie return (0); 47262143Sarchie} 47362143Sarchie 47462143Sarchie/* 47569922Sjulian * Hooks are attached, adjust to force queueing. 47669922Sjulian * We don't really care which hook it is. 47769922Sjulian * they should all be queuing for outgoing data. 47869922Sjulian */ 47969922Sjulianstatic int 48069922Sjulianng_ether_connect(hook_p hook) 48169922Sjulian{ 48269922Sjulian hook->peer->flags |= HK_QUEUE; 48369922Sjulian return (0); 48469922Sjulian} 48569922Sjulian 48669922Sjulian/* 48762143Sarchie * Receive an incoming control message. 48862143Sarchie */ 48962143Sarchiestatic int 49070700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 49162143Sarchie{ 49262143Sarchie const priv_p priv = node->private; 49362143Sarchie struct ng_mesg *resp = NULL; 49462143Sarchie int error = 0; 49570700Sjulian struct ng_mesg *msg; 49662143Sarchie 49770700Sjulian NGI_GET_MSG(item, msg); 49862143Sarchie switch (msg->header.typecookie) { 49962143Sarchie case NGM_ETHER_COOKIE: 50062143Sarchie switch (msg->header.cmd) { 50162143Sarchie case NGM_ETHER_GET_IFNAME: 50262143Sarchie NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 50362143Sarchie if (resp == NULL) { 50462143Sarchie error = ENOMEM; 50562143Sarchie break; 50662143Sarchie } 50762143Sarchie snprintf(resp->data, IFNAMSIZ + 1, 50862143Sarchie "%s%d", priv->ifp->if_name, priv->ifp->if_unit); 50962143Sarchie break; 51062143Sarchie case NGM_ETHER_GET_IFINDEX: 51162143Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 51262143Sarchie if (resp == NULL) { 51362143Sarchie error = ENOMEM; 51462143Sarchie break; 51562143Sarchie } 51662143Sarchie *((u_int32_t *)resp->data) = priv->ifp->if_index; 51762143Sarchie break; 51864358Sarchie case NGM_ETHER_GET_ENADDR: 51964358Sarchie NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 52064358Sarchie if (resp == NULL) { 52164358Sarchie error = ENOMEM; 52264358Sarchie break; 52364358Sarchie } 52464358Sarchie bcopy((IFP2AC(priv->ifp))->ac_enaddr, 52564358Sarchie resp->data, ETHER_ADDR_LEN); 52664358Sarchie break; 52764653Sarchie case NGM_ETHER_SET_ENADDR: 52864653Sarchie { 52964653Sarchie if (msg->header.arglen != ETHER_ADDR_LEN) { 53064653Sarchie error = EINVAL; 53164653Sarchie break; 53264653Sarchie } 53364653Sarchie error = if_setlladdr(priv->ifp, 53464653Sarchie (u_char *)msg->data, ETHER_ADDR_LEN); 53564653Sarchie break; 53664653Sarchie } 53764653Sarchie case NGM_ETHER_GET_PROMISC: 53864653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 53964653Sarchie if (resp == NULL) { 54064653Sarchie error = ENOMEM; 54164653Sarchie break; 54264653Sarchie } 54364653Sarchie *((u_int32_t *)resp->data) = priv->promisc; 54464653Sarchie break; 54564358Sarchie case NGM_ETHER_SET_PROMISC: 54664358Sarchie { 54764358Sarchie u_char want; 54864358Sarchie 54964358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 55064358Sarchie error = EINVAL; 55164358Sarchie break; 55264358Sarchie } 55364358Sarchie want = !!*((u_int32_t *)msg->data); 55464358Sarchie if (want ^ priv->promisc) { 55564358Sarchie if ((error = ifpromisc(priv->ifp, want)) != 0) 55664358Sarchie break; 55764358Sarchie priv->promisc = want; 55864358Sarchie } 55964358Sarchie break; 56064358Sarchie } 56164653Sarchie case NGM_ETHER_GET_AUTOSRC: 56264653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 56364653Sarchie if (resp == NULL) { 56464653Sarchie error = ENOMEM; 56564653Sarchie break; 56664653Sarchie } 56764653Sarchie *((u_int32_t *)resp->data) = priv->autoSrcAddr; 56864653Sarchie break; 56964358Sarchie case NGM_ETHER_SET_AUTOSRC: 57064358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 57164358Sarchie error = EINVAL; 57264358Sarchie break; 57364358Sarchie } 57464358Sarchie priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 57564358Sarchie break; 57662143Sarchie default: 57762143Sarchie error = EINVAL; 57862143Sarchie break; 57962143Sarchie } 58062143Sarchie break; 58162143Sarchie default: 58262143Sarchie error = EINVAL; 58362143Sarchie break; 58462143Sarchie } 58570700Sjulian NG_RESPOND_MSG(error, node, item, resp); 58670700Sjulian NG_FREE_MSG(msg); 58762143Sarchie return (error); 58862143Sarchie} 58962143Sarchie 59062143Sarchie/* 59162143Sarchie * Receive data on a hook. 59262143Sarchie */ 59362143Sarchiestatic int 59470700Sjulianng_ether_rcvdata(hook_p hook, item_p item) 59562143Sarchie{ 59662143Sarchie const node_p node = hook->node; 59762143Sarchie const priv_p priv = node->private; 59870700Sjulian struct mbuf *m; 59970700Sjulian meta_p meta; 60062143Sarchie 60170700Sjulian NGI_GET_M(item, m); 60270700Sjulian NGI_GET_META(item, meta); 60370700Sjulian NG_FREE_ITEM(item); 60462143Sarchie if (hook == priv->lower) 60562143Sarchie return ng_ether_rcv_lower(node, m, meta); 60662143Sarchie if (hook == priv->upper) 60762143Sarchie return ng_ether_rcv_upper(node, m, meta); 60862143Sarchie panic("%s: weird hook", __FUNCTION__); 60962143Sarchie} 61062143Sarchie 61162143Sarchie/* 61262143Sarchie * Handle an mbuf received on the "lower" hook. 61362143Sarchie */ 61462143Sarchiestatic int 61562143Sarchieng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 61662143Sarchie{ 61762143Sarchie const priv_p priv = node->private; 61862143Sarchie 61962143Sarchie /* Make sure header is fully pulled up */ 62062143Sarchie if (m->m_pkthdr.len < sizeof(struct ether_header)) { 62170700Sjulian NG_FREE_M(m); 62270700Sjulian NG_FREE_META(meta); 62362143Sarchie return (EINVAL); 62462143Sarchie } 62562143Sarchie if (m->m_len < sizeof(struct ether_header) 62662143Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { 62762143Sarchie NG_FREE_META(meta); 62862143Sarchie return (ENOBUFS); 62962143Sarchie } 63062143Sarchie 63164358Sarchie /* Drop in the MAC address if desired */ 63264358Sarchie if (priv->autoSrcAddr) { 63364358Sarchie bcopy((IFP2AC(priv->ifp))->ac_enaddr, 63464358Sarchie mtod(m, struct ether_header *)->ether_shost, 63564358Sarchie ETHER_ADDR_LEN); 63664358Sarchie } 63762678Sjulian 63862143Sarchie /* Send it on its way */ 63962143Sarchie NG_FREE_META(meta); 64062143Sarchie return ether_output_frame(priv->ifp, m); 64162143Sarchie} 64262143Sarchie 64362143Sarchie/* 64462143Sarchie * Handle an mbuf received on the "upper" hook. 64562143Sarchie */ 64662143Sarchiestatic int 64762143Sarchieng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 64862143Sarchie{ 64962143Sarchie const priv_p priv = node->private; 65062143Sarchie struct ether_header *eh; 65162143Sarchie 65262143Sarchie /* Check length and pull off header */ 65362143Sarchie if (m->m_pkthdr.len < sizeof(*eh)) { 65470700Sjulian NG_FREE_M(m); 65570700Sjulian NG_FREE_META(meta); 65662143Sarchie return (EINVAL); 65762143Sarchie } 65862143Sarchie if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) { 65962143Sarchie NG_FREE_META(meta); 66062143Sarchie return (ENOBUFS); 66162143Sarchie } 66262143Sarchie eh = mtod(m, struct ether_header *); 66362143Sarchie m->m_data += sizeof(*eh); 66462143Sarchie m->m_len -= sizeof(*eh); 66562143Sarchie m->m_pkthdr.len -= sizeof(*eh); 66666061Sjulian m->m_pkthdr.rcvif = priv->ifp; 66762143Sarchie 66862143Sarchie /* Route packet back in */ 66962143Sarchie NG_FREE_META(meta); 67062143Sarchie ether_demux(priv->ifp, eh, m); 67162143Sarchie return (0); 67262143Sarchie} 67362143Sarchie 67462143Sarchie/* 67562143Sarchie * Shutdown node. This resets the node but does not remove it. 67670700Sjulian * Actually it produces a new node. XXX The problem is what to do when 67770700Sjulian * the node really DOES need to go away, 67870700Sjulian * or if our re-make of the node fails. 67962143Sarchie */ 68062143Sarchiestatic int 68170700Sjulianng_ether_shutdown(node_p node) 68262143Sarchie{ 68370700Sjulian char name[IFNAMSIZ + 1]; 68464358Sarchie const priv_p priv = node->private; 68564358Sarchie 68664358Sarchie if (priv->promisc) { /* disable promiscuous mode */ 68764358Sarchie (void)ifpromisc(priv->ifp, 0); 68864358Sarchie priv->promisc = 0; 68964358Sarchie } 69070700Sjulian ng_unref(node); 69170700Sjulian snprintf(name, sizeof(name), "%s%d", priv->ifp->if_name, priv->ifp->if_unit); 69270700Sjulian if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 69370700Sjulian log(LOG_ERR, "%s: can't %s for %s\n", 69470700Sjulian __FUNCTION__, "create node", name); 69570700Sjulian return (ENOMEM); 69670700Sjulian } 69770700Sjulian 69870700Sjulian /* Allocate private data */ 69970700Sjulian node->private = priv; 70070700Sjulian IFP2NG(priv->ifp) = node; 70164358Sarchie priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 70270700Sjulian 70370700Sjulian /* Try to give the node the same name as the interface */ 70470700Sjulian if (ng_name_node(node, name) != 0) { 70570700Sjulian log(LOG_WARNING, "%s: can't name node %s\n", 70670700Sjulian __FUNCTION__, name); 70770700Sjulian } 70862143Sarchie return (0); 70962143Sarchie} 71062143Sarchie 71162143Sarchie/* 71262143Sarchie * Hook disconnection. 71362143Sarchie */ 71462143Sarchiestatic int 71562143Sarchieng_ether_disconnect(hook_p hook) 71662143Sarchie{ 71762143Sarchie const priv_p priv = hook->node->private; 71862143Sarchie 71962143Sarchie if (hook == priv->upper) 72062143Sarchie priv->upper = NULL; 72162143Sarchie else if (hook == priv->lower) { 72262143Sarchie priv->lower = NULL; 72362143Sarchie priv->lowerOrphan = 0; 72462143Sarchie } else 72562143Sarchie panic("%s: weird hook", __FUNCTION__); 72670700Sjulian if ((hook->node->numhooks == 0) 72770700Sjulian && ((hook->node->flags & NG_INVALID) == 0)) 72870700Sjulian ng_rmnode_self(hook->node); /* reset node */ 72962143Sarchie return (0); 73062143Sarchie} 73162143Sarchie 73264653Sarchiestatic int 73364653Sarchieng_enaddr_parse(const struct ng_parse_type *type, 73464653Sarchie const char *s, int *const off, const u_char *const start, 73564653Sarchie u_char *const buf, int *const buflen) 73664653Sarchie{ 73764653Sarchie char *eptr; 73864653Sarchie u_long val; 73964653Sarchie int i; 74064653Sarchie 74164653Sarchie if (*buflen < ETHER_ADDR_LEN) 74264653Sarchie return (ERANGE); 74364653Sarchie for (i = 0; i < ETHER_ADDR_LEN; i++) { 74464653Sarchie val = strtoul(s + *off, &eptr, 16); 74564653Sarchie if (val > 0xff || eptr == s + *off) 74664653Sarchie return (EINVAL); 74764653Sarchie buf[i] = (u_char)val; 74864653Sarchie *off = (eptr - s); 74964653Sarchie if (i < ETHER_ADDR_LEN - 1) { 75064653Sarchie if (*eptr != ':') 75164653Sarchie return (EINVAL); 75264653Sarchie (*off)++; 75364653Sarchie } 75464653Sarchie } 75564653Sarchie *buflen = ETHER_ADDR_LEN; 75664653Sarchie return (0); 75764653Sarchie} 75864653Sarchie 75964653Sarchiestatic int 76064653Sarchieng_enaddr_unparse(const struct ng_parse_type *type, 76164653Sarchie const u_char *data, int *off, char *cbuf, int cbuflen) 76264653Sarchie{ 76364653Sarchie int len; 76464653Sarchie 76564653Sarchie len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", 76665305Sarchie data[*off], data[*off + 1], data[*off + 2], 76765305Sarchie data[*off + 3], data[*off + 4], data[*off + 5]); 76864653Sarchie if (len >= cbuflen) 76964653Sarchie return (ERANGE); 77064653Sarchie *off += ETHER_ADDR_LEN; 77164653Sarchie return (0); 77264653Sarchie} 77364653Sarchie 77462143Sarchie/****************************************************************** 77562143Sarchie INITIALIZATION 77662143Sarchie******************************************************************/ 77762143Sarchie 77862143Sarchie/* 77962143Sarchie * Handle loading and unloading for this node type. 78062143Sarchie */ 78162143Sarchiestatic int 78262143Sarchieng_ether_mod_event(module_t mod, int event, void *data) 78362143Sarchie{ 78462143Sarchie struct ifnet *ifp; 78562143Sarchie int error = 0; 78662143Sarchie int s; 78762143Sarchie 78862143Sarchie s = splnet(); 78962143Sarchie switch (event) { 79062143Sarchie case MOD_LOAD: 79162143Sarchie 79262143Sarchie /* Register function hooks */ 79362143Sarchie if (ng_ether_attach_p != NULL) { 79462143Sarchie error = EEXIST; 79562143Sarchie break; 79662143Sarchie } 79762143Sarchie ng_ether_attach_p = ng_ether_attach; 79862143Sarchie ng_ether_detach_p = ng_ether_detach; 79962143Sarchie ng_ether_output_p = ng_ether_output; 80062143Sarchie ng_ether_input_p = ng_ether_input; 80162143Sarchie ng_ether_input_orphan_p = ng_ether_input_orphan; 80262143Sarchie 80362143Sarchie /* Create nodes for any already-existing Ethernet interfaces */ 80462143Sarchie TAILQ_FOREACH(ifp, &ifnet, if_link) { 80562143Sarchie if (ifp->if_type == IFT_ETHER) 80662143Sarchie ng_ether_attach(ifp); 80762143Sarchie } 80862143Sarchie break; 80962143Sarchie 81062143Sarchie case MOD_UNLOAD: 81162143Sarchie 81262143Sarchie /* 81362143Sarchie * Note that the base code won't try to unload us until 81462143Sarchie * all nodes have been removed, and that can't happen 81562143Sarchie * until all Ethernet interfaces are removed. In any 81662143Sarchie * case, we know there are no nodes left if the action 81762143Sarchie * is MOD_UNLOAD, so there's no need to detach any nodes. 81862143Sarchie */ 81962143Sarchie 82062143Sarchie /* Unregister function hooks */ 82162143Sarchie ng_ether_attach_p = NULL; 82262143Sarchie ng_ether_detach_p = NULL; 82362143Sarchie ng_ether_output_p = NULL; 82462143Sarchie ng_ether_input_p = NULL; 82562143Sarchie ng_ether_input_orphan_p = NULL; 82662143Sarchie break; 82762143Sarchie 82862143Sarchie default: 82962143Sarchie error = EOPNOTSUPP; 83062143Sarchie break; 83162143Sarchie } 83262143Sarchie splx(s); 83362143Sarchie return (error); 83462143Sarchie} 83562143Sarchie 836