ng_ether.c revision 185571
162143Sarchie 262143Sarchie/* 362143Sarchie * ng_ether.c 4139823Simp */ 5139823Simp 6139823Simp/*- 762143Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc. 862143Sarchie * All rights reserved. 962143Sarchie * 1062143Sarchie * Subject to the following obligations and disclaimer of warranty, use and 1162143Sarchie * redistribution of this software, in source or object code forms, with or 1262143Sarchie * without modifications are expressly permitted by Whistle Communications; 1362143Sarchie * provided, however, that: 1462143Sarchie * 1. Any and all reproductions of the source or object code must include the 1562143Sarchie * copyright notice above and the following disclaimer of warranties; and 1662143Sarchie * 2. No rights are granted, in any manner or form, to use Whistle 1762143Sarchie * Communications, Inc. trademarks, including the mark "WHISTLE 1862143Sarchie * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1962143Sarchie * such appears in the above copyright notice or in the software. 2062143Sarchie * 2162143Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2262143Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2362143Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2462143Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2562143Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2662143Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2762143Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2862143Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2962143Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 3062143Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 3162143Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3262143Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3362143Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3462143Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3562143Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3662143Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3762143Sarchie * OF SUCH DAMAGE. 3862143Sarchie * 3962143Sarchie * Authors: Archie Cobbs <archie@freebsd.org> 4062143Sarchie * Julian Elischer <julian@freebsd.org> 4162143Sarchie * 4262143Sarchie * $FreeBSD: head/sys/netgraph/ng_ether.c 185571 2008-12-02 21:37:28Z bz $ 4362143Sarchie */ 4462143Sarchie 4562143Sarchie/* 4662143Sarchie * ng_ether(4) netgraph node type 4762143Sarchie */ 4862143Sarchie 4962143Sarchie#include <sys/param.h> 5062143Sarchie#include <sys/systm.h> 5162143Sarchie#include <sys/kernel.h> 5262143Sarchie#include <sys/malloc.h> 5362143Sarchie#include <sys/mbuf.h> 5462143Sarchie#include <sys/errno.h> 5562143Sarchie#include <sys/syslog.h> 5662143Sarchie#include <sys/socket.h> 57181803Sbz#include <sys/vimage.h> 5862143Sarchie 5962143Sarchie#include <net/if.h> 60141721Sglebius#include <net/if_dl.h> 6162143Sarchie#include <net/if_types.h> 6262143Sarchie#include <net/if_arp.h> 6362143Sarchie#include <net/if_var.h> 6462143Sarchie#include <net/ethernet.h> 65151305Sthompsa#include <net/if_bridgevar.h> 66185571Sbz#include <net/vnet.h> 6762143Sarchie 6862143Sarchie#include <netgraph/ng_message.h> 6962143Sarchie#include <netgraph/netgraph.h> 7062143Sarchie#include <netgraph/ng_parse.h> 7162143Sarchie#include <netgraph/ng_ether.h> 7262143Sarchie 73152243Sru#define IFP2NG(ifp) (IFP2AC((ifp))->ac_netgraph) 7462143Sarchie 75126035Spjd/* Per-node private data */ 76126035Spjdstruct private { 77126035Spjd struct ifnet *ifp; /* associated interface */ 78126035Spjd hook_p upper; /* upper hook connection */ 79129281Sarchie hook_p lower; /* lower hook connection */ 80129281Sarchie hook_p orphan; /* orphan hook connection */ 81126035Spjd u_char autoSrcAddr; /* always overwrite source address */ 82126035Spjd u_char promisc; /* promiscuous mode enabled */ 83126035Spjd u_long hwassist; /* hardware checksum capabilities */ 84126035Spjd u_int flags; /* flags e.g. really die */ 85126035Spjd}; 86126035Spjdtypedef struct private *priv_p; 8762143Sarchie 88106933Ssam/* Hook pointers used by if_ethersubr.c to callback to netgraph */ 89106933Ssamextern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 90106933Ssamextern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 91106933Ssamextern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 92106933Ssamextern void (*ng_ether_attach_p)(struct ifnet *ifp); 93106933Ssamextern void (*ng_ether_detach_p)(struct ifnet *ifp); 94139903Sglebiusextern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); 95106933Ssam 9662143Sarchie/* Functional hooks called from if_ethersubr.c */ 97106933Ssamstatic void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 98106933Ssamstatic void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 9962143Sarchiestatic int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 10062143Sarchiestatic void ng_ether_attach(struct ifnet *ifp); 10162143Sarchiestatic void ng_ether_detach(struct ifnet *ifp); 102139903Sglebiusstatic void ng_ether_link_state(struct ifnet *ifp, int state); 10362143Sarchie 10462143Sarchie/* Other functions */ 105131155Sjulianstatic int ng_ether_rcv_lower(node_p node, struct mbuf *m); 106131155Sjulianstatic int ng_ether_rcv_upper(node_p node, struct mbuf *m); 10762143Sarchie 10862143Sarchie/* Netgraph node methods */ 10962143Sarchiestatic ng_constructor_t ng_ether_constructor; 11062143Sarchiestatic ng_rcvmsg_t ng_ether_rcvmsg; 11170700Sjulianstatic ng_shutdown_t ng_ether_shutdown; 11262143Sarchiestatic ng_newhook_t ng_ether_newhook; 11362143Sarchiestatic ng_rcvdata_t ng_ether_rcvdata; 11462143Sarchiestatic ng_disconnect_t ng_ether_disconnect; 11562143Sarchiestatic int ng_ether_mod_event(module_t mod, int event, void *data); 11662143Sarchie 11762143Sarchie/* List of commands and how to convert arguments to/from ASCII */ 11862143Sarchiestatic const struct ng_cmdlist ng_ether_cmdlist[] = { 11962143Sarchie { 12062143Sarchie NGM_ETHER_COOKIE, 12162143Sarchie NGM_ETHER_GET_IFNAME, 12262143Sarchie "getifname", 12362143Sarchie NULL, 12462143Sarchie &ng_parse_string_type 12562143Sarchie }, 12662143Sarchie { 12762143Sarchie NGM_ETHER_COOKIE, 12862143Sarchie NGM_ETHER_GET_IFINDEX, 12962143Sarchie "getifindex", 13062143Sarchie NULL, 13162143Sarchie &ng_parse_int32_type 13262143Sarchie }, 13364358Sarchie { 13464358Sarchie NGM_ETHER_COOKIE, 13564358Sarchie NGM_ETHER_GET_ENADDR, 13664358Sarchie "getenaddr", 13764358Sarchie NULL, 138123600Sru &ng_parse_enaddr_type 13964358Sarchie }, 14064358Sarchie { 14164358Sarchie NGM_ETHER_COOKIE, 14264653Sarchie NGM_ETHER_SET_ENADDR, 14364653Sarchie "setenaddr", 144123600Sru &ng_parse_enaddr_type, 14564653Sarchie NULL 14664653Sarchie }, 14764653Sarchie { 14864653Sarchie NGM_ETHER_COOKIE, 14964653Sarchie NGM_ETHER_GET_PROMISC, 15064653Sarchie "getpromisc", 15164653Sarchie NULL, 15264653Sarchie &ng_parse_int32_type 15364653Sarchie }, 15464653Sarchie { 15564653Sarchie NGM_ETHER_COOKIE, 15664358Sarchie NGM_ETHER_SET_PROMISC, 15764358Sarchie "setpromisc", 15864358Sarchie &ng_parse_int32_type, 15964358Sarchie NULL 16064358Sarchie }, 16164358Sarchie { 16264358Sarchie NGM_ETHER_COOKIE, 16364653Sarchie NGM_ETHER_GET_AUTOSRC, 16464653Sarchie "getautosrc", 16564653Sarchie NULL, 16664653Sarchie &ng_parse_int32_type 16764653Sarchie }, 16864653Sarchie { 16964653Sarchie NGM_ETHER_COOKIE, 17064358Sarchie NGM_ETHER_SET_AUTOSRC, 17164358Sarchie "setautosrc", 17264358Sarchie &ng_parse_int32_type, 17364358Sarchie NULL 17464358Sarchie }, 175141721Sglebius { 176141721Sglebius NGM_ETHER_COOKIE, 177141721Sglebius NGM_ETHER_ADD_MULTI, 178141721Sglebius "addmulti", 179141721Sglebius &ng_parse_enaddr_type, 180141721Sglebius NULL 181141721Sglebius }, 182141721Sglebius { 183141721Sglebius NGM_ETHER_COOKIE, 184141721Sglebius NGM_ETHER_DEL_MULTI, 185141721Sglebius "delmulti", 186141721Sglebius &ng_parse_enaddr_type, 187141721Sglebius NULL 188141721Sglebius }, 189141910Sglebius { 190141910Sglebius NGM_ETHER_COOKIE, 191141910Sglebius NGM_ETHER_DETACH, 192141910Sglebius "detach", 193141910Sglebius NULL, 194141910Sglebius NULL 195141910Sglebius }, 19662143Sarchie { 0 } 19762143Sarchie}; 19862143Sarchie 19962143Sarchiestatic struct ng_type ng_ether_typestruct = { 200129823Sjulian .version = NG_ABI_VERSION, 201129823Sjulian .name = NG_ETHER_NODE_TYPE, 202129823Sjulian .mod_event = ng_ether_mod_event, 203129823Sjulian .constructor = ng_ether_constructor, 204129823Sjulian .rcvmsg = ng_ether_rcvmsg, 205129823Sjulian .shutdown = ng_ether_shutdown, 206129823Sjulian .newhook = ng_ether_newhook, 207129823Sjulian .rcvdata = ng_ether_rcvdata, 208129823Sjulian .disconnect = ng_ether_disconnect, 209129823Sjulian .cmdlist = ng_ether_cmdlist, 21062143Sarchie}; 21162143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct); 21262143Sarchie 21362143Sarchie/****************************************************************** 21462143Sarchie ETHERNET FUNCTION HOOKS 21562143Sarchie******************************************************************/ 21662143Sarchie 21762143Sarchie/* 21862143Sarchie * Handle a packet that has come in on an interface. We get to 21962143Sarchie * look at it here before any upper layer protocols do. 22062143Sarchie * 22162143Sarchie * NOTE: this function will get called at splimp() 22262143Sarchie */ 22362143Sarchiestatic void 224106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp) 22562143Sarchie{ 22662143Sarchie const node_p node = IFP2NG(ifp); 22770784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 228129281Sarchie int error; 22962143Sarchie 23062143Sarchie /* If "lower" hook not connected, let packet continue */ 231129281Sarchie if (priv->lower == NULL) 23262143Sarchie return; 233129281Sarchie NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 23462143Sarchie} 23562143Sarchie 23662143Sarchie/* 23762143Sarchie * Handle a packet that has come in on an interface, and which 23862143Sarchie * does not match any of our known protocols (an ``orphan''). 23962143Sarchie * 24062143Sarchie * NOTE: this function will get called at splimp() 24162143Sarchie */ 24262143Sarchiestatic void 243106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 24462143Sarchie{ 24562143Sarchie const node_p node = IFP2NG(ifp); 24670784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 247129281Sarchie int error; 24862143Sarchie 249129281Sarchie /* If "orphan" hook not connected, discard packet */ 250129281Sarchie if (priv->orphan == NULL) { 25162143Sarchie m_freem(m); 25262143Sarchie return; 25362143Sarchie } 254129281Sarchie NG_SEND_DATA_ONLY(error, priv->orphan, m); 25562143Sarchie} 25662143Sarchie 25762143Sarchie/* 25862143Sarchie * Handle a packet that is going out on an interface. 25962143Sarchie * The Ethernet header is already attached to the mbuf. 26062143Sarchie */ 26162143Sarchiestatic int 26262143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp) 26362143Sarchie{ 26462143Sarchie const node_p node = IFP2NG(ifp); 26570784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 26662143Sarchie int error = 0; 26762143Sarchie 26862143Sarchie /* If "upper" hook not connected, let packet continue */ 26962143Sarchie if (priv->upper == NULL) 27062143Sarchie return (0); 27162143Sarchie 27262143Sarchie /* Send it out "upper" hook */ 27370700Sjulian NG_SEND_DATA_ONLY(error, priv->upper, *mp); 27462143Sarchie return (error); 27562143Sarchie} 27662143Sarchie 27762143Sarchie/* 27862143Sarchie * A new Ethernet interface has been attached. 27962143Sarchie * Create a new node for it, etc. 28062143Sarchie */ 28162143Sarchiestatic void 28262143Sarchieng_ether_attach(struct ifnet *ifp) 28362143Sarchie{ 28462143Sarchie priv_p priv; 28562143Sarchie node_p node; 28662143Sarchie 28762143Sarchie /* Create node */ 28887599Sobrien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 28962143Sarchie if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 29062143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 291121816Sbrooks __func__, "create node", ifp->if_xname); 29262143Sarchie return; 29362143Sarchie } 29462143Sarchie 29562143Sarchie /* Allocate private data */ 296184205Sdes priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 29762143Sarchie if (priv == NULL) { 29862143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 299121816Sbrooks __func__, "allocate memory", ifp->if_xname); 30070784Sjulian NG_NODE_UNREF(node); 30162143Sarchie return; 30262143Sarchie } 30370784Sjulian NG_NODE_SET_PRIVATE(node, priv); 30462143Sarchie priv->ifp = ifp; 305152243Sru IFP2NG(ifp) = node; 30690249Sarchie priv->hwassist = ifp->if_hwassist; 30762143Sarchie 30862143Sarchie /* Try to give the node the same name as the interface */ 309121816Sbrooks if (ng_name_node(node, ifp->if_xname) != 0) { 31062143Sarchie log(LOG_WARNING, "%s: can't name node %s\n", 311121816Sbrooks __func__, ifp->if_xname); 31262143Sarchie } 31362143Sarchie} 31462143Sarchie 31562143Sarchie/* 31662143Sarchie * An Ethernet interface is being detached. 31771849Sjulian * REALLY Destroy its node. 31862143Sarchie */ 31962143Sarchiestatic void 32062143Sarchieng_ether_detach(struct ifnet *ifp) 32162143Sarchie{ 32262143Sarchie const node_p node = IFP2NG(ifp); 32371849Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 32462143Sarchie 32571849Sjulian NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 32671849Sjulian /* 32771849Sjulian * We can't assume the ifnet is still around when we run shutdown 32871849Sjulian * So zap it now. XXX We HOPE that anything running at this time 32971849Sjulian * handles it (as it should in the non netgraph case). 33071849Sjulian */ 331152243Sru IFP2NG(ifp) = NULL; 33271849Sjulian priv->ifp = NULL; /* XXX race if interrupted an output packet */ 33371849Sjulian ng_rmnode_self(node); /* remove all netgraph parts */ 33462143Sarchie} 33562143Sarchie 336139903Sglebius/* 337139903Sglebius * Notify graph about link event. 338139903Sglebius * if_link_state_change() has already checked that the state has changed. 339139903Sglebius */ 340139903Sglebiusstatic void 341139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state) 342139903Sglebius{ 343139903Sglebius const node_p node = IFP2NG(ifp); 344139903Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 345139903Sglebius struct ng_mesg *msg; 346139903Sglebius int cmd, dummy_error = 0; 347139903Sglebius 348139903Sglebius if (priv->lower == NULL) 349139903Sglebius return; 350139903Sglebius 351139903Sglebius if (state == LINK_STATE_UP) 352139903Sglebius cmd = NGM_LINK_IS_UP; 353139903Sglebius else if (state == LINK_STATE_DOWN) 354139903Sglebius cmd = NGM_LINK_IS_DOWN; 355139903Sglebius else 356139903Sglebius return; 357139903Sglebius 358139903Sglebius NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 359139903Sglebius if (msg != NULL) 360139903Sglebius NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 361139903Sglebius} 362139903Sglebius 36362143Sarchie/****************************************************************** 36462143Sarchie NETGRAPH NODE METHODS 36562143Sarchie******************************************************************/ 36662143Sarchie 36762143Sarchie/* 36862143Sarchie * It is not possible or allowable to create a node of this type. 36962143Sarchie * Nodes get created when the interface is attached (or, when 37062143Sarchie * this node type's KLD is loaded). 37162143Sarchie */ 37262143Sarchiestatic int 37370700Sjulianng_ether_constructor(node_p node) 37462143Sarchie{ 37562143Sarchie return (EINVAL); 37662143Sarchie} 37762143Sarchie 37862143Sarchie/* 37962143Sarchie * Check for attaching a new hook. 38062143Sarchie */ 38162143Sarchiestatic int 38262143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name) 38362143Sarchie{ 38470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 38562143Sarchie hook_p *hookptr; 38662143Sarchie 38762143Sarchie /* Divert hook is an alias for lower */ 38862143Sarchie if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 38962143Sarchie name = NG_ETHER_HOOK_LOWER; 39062143Sarchie 39162143Sarchie /* Which hook? */ 39262143Sarchie if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 39362143Sarchie hookptr = &priv->upper; 394129281Sarchie else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) 39562143Sarchie hookptr = &priv->lower; 396129281Sarchie else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) 397129281Sarchie hookptr = &priv->orphan; 398129281Sarchie else 39962143Sarchie return (EINVAL); 40062143Sarchie 40162143Sarchie /* Check if already connected (shouldn't be, but doesn't hurt) */ 40262143Sarchie if (*hookptr != NULL) 40362143Sarchie return (EISCONN); 40462143Sarchie 40590249Sarchie /* Disable hardware checksums while 'upper' hook is connected */ 40690249Sarchie if (hookptr == &priv->upper) 40790249Sarchie priv->ifp->if_hwassist = 0; 40890249Sarchie 40962143Sarchie /* OK */ 41062143Sarchie *hookptr = hook; 41162143Sarchie return (0); 41262143Sarchie} 41362143Sarchie 41462143Sarchie/* 41562143Sarchie * Receive an incoming control message. 41662143Sarchie */ 41762143Sarchiestatic int 41870700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 41962143Sarchie{ 42070784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 42162143Sarchie struct ng_mesg *resp = NULL; 42262143Sarchie int error = 0; 42370700Sjulian struct ng_mesg *msg; 42462143Sarchie 42570700Sjulian NGI_GET_MSG(item, msg); 42662143Sarchie switch (msg->header.typecookie) { 42762143Sarchie case NGM_ETHER_COOKIE: 42862143Sarchie switch (msg->header.cmd) { 42962143Sarchie case NGM_ETHER_GET_IFNAME: 430141195Sru NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 43162143Sarchie if (resp == NULL) { 43262143Sarchie error = ENOMEM; 43362143Sarchie break; 43462143Sarchie } 435141195Sru strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 43662143Sarchie break; 43762143Sarchie case NGM_ETHER_GET_IFINDEX: 43862143Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 43962143Sarchie if (resp == NULL) { 44062143Sarchie error = ENOMEM; 44162143Sarchie break; 44262143Sarchie } 44362143Sarchie *((u_int32_t *)resp->data) = priv->ifp->if_index; 44462143Sarchie break; 44564358Sarchie case NGM_ETHER_GET_ENADDR: 44664358Sarchie NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 44764358Sarchie if (resp == NULL) { 44864358Sarchie error = ENOMEM; 44964358Sarchie break; 45064358Sarchie } 451152315Sru bcopy(IF_LLADDR(priv->ifp), 45264358Sarchie resp->data, ETHER_ADDR_LEN); 45364358Sarchie break; 45464653Sarchie case NGM_ETHER_SET_ENADDR: 45564653Sarchie { 45664653Sarchie if (msg->header.arglen != ETHER_ADDR_LEN) { 45764653Sarchie error = EINVAL; 45864653Sarchie break; 45964653Sarchie } 46064653Sarchie error = if_setlladdr(priv->ifp, 46164653Sarchie (u_char *)msg->data, ETHER_ADDR_LEN); 46264653Sarchie break; 46364653Sarchie } 46464653Sarchie case NGM_ETHER_GET_PROMISC: 46564653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 46664653Sarchie if (resp == NULL) { 46764653Sarchie error = ENOMEM; 46864653Sarchie break; 46964653Sarchie } 47064653Sarchie *((u_int32_t *)resp->data) = priv->promisc; 47164653Sarchie break; 47264358Sarchie case NGM_ETHER_SET_PROMISC: 47364358Sarchie { 47464358Sarchie u_char want; 47564358Sarchie 47664358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 47764358Sarchie error = EINVAL; 47864358Sarchie break; 47964358Sarchie } 48064358Sarchie want = !!*((u_int32_t *)msg->data); 48164358Sarchie if (want ^ priv->promisc) { 48264358Sarchie if ((error = ifpromisc(priv->ifp, want)) != 0) 48364358Sarchie break; 48464358Sarchie priv->promisc = want; 48564358Sarchie } 48664358Sarchie break; 48764358Sarchie } 48864653Sarchie case NGM_ETHER_GET_AUTOSRC: 48964653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 49064653Sarchie if (resp == NULL) { 49164653Sarchie error = ENOMEM; 49264653Sarchie break; 49364653Sarchie } 49464653Sarchie *((u_int32_t *)resp->data) = priv->autoSrcAddr; 49564653Sarchie break; 49664358Sarchie case NGM_ETHER_SET_AUTOSRC: 49764358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 49864358Sarchie error = EINVAL; 49964358Sarchie break; 50064358Sarchie } 50164358Sarchie priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 50264358Sarchie break; 503141721Sglebius case NGM_ETHER_ADD_MULTI: 504141721Sglebius { 505141721Sglebius struct sockaddr_dl sa_dl; 506167729Sbms struct ifmultiaddr *ifma; 507141721Sglebius 508141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 509141721Sglebius error = EINVAL; 510141721Sglebius break; 511141721Sglebius } 512141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 513141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 514141721Sglebius sa_dl.sdl_family = AF_LINK; 515141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 516141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 517141721Sglebius ETHER_ADDR_LEN); 518167729Sbms /* 519167729Sbms * Netgraph is only permitted to join groups once 520167729Sbms * via the if_addmulti() KPI, because it cannot hold 521167729Sbms * struct ifmultiaddr * between calls. It may also 522167729Sbms * lose a race while we check if the membership 523167729Sbms * already exists. 524167729Sbms */ 525167729Sbms IF_ADDR_LOCK(priv->ifp); 526167729Sbms ifma = if_findmulti(priv->ifp, 527167729Sbms (struct sockaddr *)&sa_dl); 528167729Sbms IF_ADDR_UNLOCK(priv->ifp); 529167729Sbms if (ifma != NULL) { 530167729Sbms error = EADDRINUSE; 531167729Sbms } else { 532167729Sbms error = if_addmulti(priv->ifp, 533167729Sbms (struct sockaddr *)&sa_dl, &ifma); 534167729Sbms } 535141721Sglebius break; 536141721Sglebius } 537141721Sglebius case NGM_ETHER_DEL_MULTI: 538141721Sglebius { 539141721Sglebius struct sockaddr_dl sa_dl; 540141721Sglebius 541141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 542141721Sglebius error = EINVAL; 543141721Sglebius break; 544141721Sglebius } 545141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 546141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 547141721Sglebius sa_dl.sdl_family = AF_LINK; 548141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 549141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 550141721Sglebius ETHER_ADDR_LEN); 551141721Sglebius error = if_delmulti(priv->ifp, 552141721Sglebius (struct sockaddr *)&sa_dl); 553141721Sglebius break; 554141721Sglebius } 555141910Sglebius case NGM_ETHER_DETACH: 556141910Sglebius ng_ether_detach(priv->ifp); 557141910Sglebius break; 55862143Sarchie default: 55962143Sarchie error = EINVAL; 56062143Sarchie break; 56162143Sarchie } 56262143Sarchie break; 56362143Sarchie default: 56462143Sarchie error = EINVAL; 56562143Sarchie break; 56662143Sarchie } 56770700Sjulian NG_RESPOND_MSG(error, node, item, resp); 56870700Sjulian NG_FREE_MSG(msg); 56962143Sarchie return (error); 57062143Sarchie} 57162143Sarchie 57262143Sarchie/* 57362143Sarchie * Receive data on a hook. 57462143Sarchie */ 57562143Sarchiestatic int 57670700Sjulianng_ether_rcvdata(hook_p hook, item_p item) 57762143Sarchie{ 57870784Sjulian const node_p node = NG_HOOK_NODE(hook); 57970784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 58070700Sjulian struct mbuf *m; 58162143Sarchie 58270700Sjulian NGI_GET_M(item, m); 58370700Sjulian NG_FREE_ITEM(item); 584131155Sjulian 585129281Sarchie if (hook == priv->lower || hook == priv->orphan) 586131155Sjulian return ng_ether_rcv_lower(node, m); 58762143Sarchie if (hook == priv->upper) 588131155Sjulian return ng_ether_rcv_upper(node, m); 58987599Sobrien panic("%s: weird hook", __func__); 590129281Sarchie#ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ 591136312Sdes return (0); 59283366Sjulian#endif 59362143Sarchie} 59462143Sarchie 59562143Sarchie/* 596129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook. 59762143Sarchie */ 59862143Sarchiestatic int 599131155Sjulianng_ether_rcv_lower(node_p node, struct mbuf *m) 60062143Sarchie{ 60170784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 60296265Sarchie struct ifnet *const ifp = priv->ifp; 60362143Sarchie 60496265Sarchie /* Check whether interface is ready for packets */ 605148887Srwatson if (!((ifp->if_flags & IFF_UP) && 606148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 60796265Sarchie NG_FREE_M(m); 60896265Sarchie return (ENETDOWN); 60996265Sarchie } 61096265Sarchie 61162143Sarchie /* Make sure header is fully pulled up */ 61262143Sarchie if (m->m_pkthdr.len < sizeof(struct ether_header)) { 61370700Sjulian NG_FREE_M(m); 61462143Sarchie return (EINVAL); 61562143Sarchie } 61662143Sarchie if (m->m_len < sizeof(struct ether_header) 61797896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 61862143Sarchie return (ENOBUFS); 61962143Sarchie 62064358Sarchie /* Drop in the MAC address if desired */ 62164358Sarchie if (priv->autoSrcAddr) { 62297896Sarchie 62397896Sarchie /* Make the mbuf writable if it's not already */ 62497896Sarchie if (!M_WRITABLE(m) 62597896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 62697896Sarchie return (ENOBUFS); 62797896Sarchie 62897896Sarchie /* Overwrite source MAC address */ 629152315Sru bcopy(IF_LLADDR(ifp), 63064358Sarchie mtod(m, struct ether_header *)->ether_shost, 63164358Sarchie ETHER_ADDR_LEN); 63264358Sarchie } 63362678Sjulian 63462143Sarchie /* Send it on its way */ 63596265Sarchie return ether_output_frame(ifp, m); 63662143Sarchie} 63762143Sarchie 63862143Sarchie/* 63962143Sarchie * Handle an mbuf received on the "upper" hook. 64062143Sarchie */ 64162143Sarchiestatic int 642131155Sjulianng_ether_rcv_upper(node_p node, struct mbuf *m) 64362143Sarchie{ 64470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 645151063Sglebius struct ifnet *ifp = priv->ifp; 64662143Sarchie 647152001Sru /* Check length and pull off header */ 648152001Sru if (m->m_pkthdr.len < sizeof(struct ether_header)) { 649152001Sru NG_FREE_M(m); 650152001Sru return (EINVAL); 651152001Sru } 652152001Sru if (m->m_len < sizeof(struct ether_header) && 653152001Sru (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 654152001Sru return (ENOBUFS); 655152001Sru 656151063Sglebius m->m_pkthdr.rcvif = ifp; 65762143Sarchie 658151305Sthompsa /* Pass the packet to the bridge, it may come back to us */ 659151063Sglebius if (ifp->if_bridge) { 660151305Sthompsa BRIDGE_INPUT(ifp, m); 661151063Sglebius if (m == NULL) 662151063Sglebius return (0); 663151063Sglebius } 664151063Sglebius 66562143Sarchie /* Route packet back in */ 666151305Sthompsa ether_demux(ifp, m); 66762143Sarchie return (0); 66862143Sarchie} 66962143Sarchie 67062143Sarchie/* 67171849Sjulian * Shutdown node. This resets the node but does not remove it 67271849Sjulian * unless the REALLY_DIE flag is set. 67362143Sarchie */ 67462143Sarchiestatic int 67570700Sjulianng_ether_shutdown(node_p node) 67662143Sarchie{ 67770784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 67864358Sarchie 679132464Sjulian if (node->nd_flags & NGF_REALLY_DIE) { 68071849Sjulian /* 68171849Sjulian * WE came here because the ethernet card is being unloaded, 68271849Sjulian * so stop being persistant. 68371849Sjulian * Actually undo all the things we did on creation. 68471849Sjulian * Assume the ifp has already been freed. 68571849Sjulian */ 68671849Sjulian NG_NODE_SET_PRIVATE(node, NULL); 687184205Sdes free(priv, M_NETGRAPH); 68871849Sjulian NG_NODE_UNREF(node); /* free node itself */ 68971849Sjulian return (0); 69070700Sjulian } 691124269Sgreen if (priv->promisc) { /* disable promiscuous mode */ 692124269Sgreen (void)ifpromisc(priv->ifp, 0); 693124269Sgreen priv->promisc = 0; 694124269Sgreen } 69564358Sarchie priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 696132464Sjulian NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 697132464Sjulian 69862143Sarchie return (0); 69962143Sarchie} 70062143Sarchie 70162143Sarchie/* 70262143Sarchie * Hook disconnection. 70362143Sarchie */ 70462143Sarchiestatic int 70562143Sarchieng_ether_disconnect(hook_p hook) 70662143Sarchie{ 70770784Sjulian const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 70862143Sarchie 70990249Sarchie if (hook == priv->upper) { 71062143Sarchie priv->upper = NULL; 711124270Sgreen if (priv->ifp != NULL) /* restore h/w csum */ 712124270Sgreen priv->ifp->if_hwassist = priv->hwassist; 713129281Sarchie } else if (hook == priv->lower) 71462143Sarchie priv->lower = NULL; 715129281Sarchie else if (hook == priv->orphan) 716129281Sarchie priv->orphan = NULL; 717129281Sarchie else 71887599Sobrien panic("%s: weird hook", __func__); 71970784Sjulian if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 72070784Sjulian && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 72170784Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 72262143Sarchie return (0); 72362143Sarchie} 72462143Sarchie 72562143Sarchie/****************************************************************** 72662143Sarchie INITIALIZATION 72762143Sarchie******************************************************************/ 72862143Sarchie 72962143Sarchie/* 73062143Sarchie * Handle loading and unloading for this node type. 73162143Sarchie */ 73262143Sarchiestatic int 73362143Sarchieng_ether_mod_event(module_t mod, int event, void *data) 73462143Sarchie{ 73562143Sarchie struct ifnet *ifp; 73662143Sarchie int error = 0; 73762143Sarchie int s; 73862143Sarchie 73962143Sarchie s = splnet(); 74062143Sarchie switch (event) { 74162143Sarchie case MOD_LOAD: 74262143Sarchie 74362143Sarchie /* Register function hooks */ 74462143Sarchie if (ng_ether_attach_p != NULL) { 74562143Sarchie error = EEXIST; 74662143Sarchie break; 74762143Sarchie } 74862143Sarchie ng_ether_attach_p = ng_ether_attach; 74962143Sarchie ng_ether_detach_p = ng_ether_detach; 75062143Sarchie ng_ether_output_p = ng_ether_output; 75162143Sarchie ng_ether_input_p = ng_ether_input; 75262143Sarchie ng_ether_input_orphan_p = ng_ether_input_orphan; 753139903Sglebius ng_ether_link_state_p = ng_ether_link_state; 75462143Sarchie 75562143Sarchie /* Create nodes for any already-existing Ethernet interfaces */ 756108172Shsu IFNET_RLOCK(); 757181803Sbz TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 75882586Sarchie if (ifp->if_type == IFT_ETHER 75982586Sarchie || ifp->if_type == IFT_L2VLAN) 76062143Sarchie ng_ether_attach(ifp); 76162143Sarchie } 762108172Shsu IFNET_RUNLOCK(); 76362143Sarchie break; 76462143Sarchie 76562143Sarchie case MOD_UNLOAD: 76662143Sarchie 76762143Sarchie /* 76862143Sarchie * Note that the base code won't try to unload us until 76962143Sarchie * all nodes have been removed, and that can't happen 77062143Sarchie * until all Ethernet interfaces are removed. In any 77162143Sarchie * case, we know there are no nodes left if the action 77262143Sarchie * is MOD_UNLOAD, so there's no need to detach any nodes. 77362143Sarchie */ 77462143Sarchie 77562143Sarchie /* Unregister function hooks */ 77662143Sarchie ng_ether_attach_p = NULL; 77762143Sarchie ng_ether_detach_p = NULL; 77862143Sarchie ng_ether_output_p = NULL; 77962143Sarchie ng_ether_input_p = NULL; 78062143Sarchie ng_ether_input_orphan_p = NULL; 781139903Sglebius ng_ether_link_state_p = NULL; 78262143Sarchie break; 78362143Sarchie 78462143Sarchie default: 78562143Sarchie error = EOPNOTSUPP; 78662143Sarchie break; 78762143Sarchie } 78862143Sarchie splx(s); 78962143Sarchie return (error); 79062143Sarchie} 79162143Sarchie 792