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$ 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> 55196019Srwatson#include <sys/proc.h> 5662143Sarchie#include <sys/syslog.h> 5762143Sarchie#include <sys/socket.h> 58224107Szec#include <sys/taskqueue.h> 5962143Sarchie 6062143Sarchie#include <net/if.h> 61141721Sglebius#include <net/if_dl.h> 6262143Sarchie#include <net/if_types.h> 6362143Sarchie#include <net/if_arp.h> 6462143Sarchie#include <net/if_var.h> 6562143Sarchie#include <net/ethernet.h> 66151305Sthompsa#include <net/if_bridgevar.h> 67185571Sbz#include <net/vnet.h> 6862143Sarchie 6962143Sarchie#include <netgraph/ng_message.h> 7062143Sarchie#include <netgraph/netgraph.h> 7162143Sarchie#include <netgraph/ng_parse.h> 7262143Sarchie#include <netgraph/ng_ether.h> 7362143Sarchie 74238844SemasteMODULE_VERSION(ng_ether, 1); 75238844Semaste 76152243Sru#define IFP2NG(ifp) (IFP2AC((ifp))->ac_netgraph) 7762143Sarchie 78126035Spjd/* Per-node private data */ 79126035Spjdstruct private { 80126035Spjd struct ifnet *ifp; /* associated interface */ 81126035Spjd hook_p upper; /* upper hook connection */ 82129281Sarchie hook_p lower; /* lower hook connection */ 83129281Sarchie hook_p orphan; /* orphan hook connection */ 84126035Spjd u_char autoSrcAddr; /* always overwrite source address */ 85126035Spjd u_char promisc; /* promiscuous mode enabled */ 86126035Spjd u_long hwassist; /* hardware checksum capabilities */ 87126035Spjd u_int flags; /* flags e.g. really die */ 88126035Spjd}; 89126035Spjdtypedef struct private *priv_p; 9062143Sarchie 91106933Ssam/* Hook pointers used by if_ethersubr.c to callback to netgraph */ 92106933Ssamextern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 93106933Ssamextern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 94106933Ssamextern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 95106933Ssamextern void (*ng_ether_attach_p)(struct ifnet *ifp); 96106933Ssamextern void (*ng_ether_detach_p)(struct ifnet *ifp); 97139903Sglebiusextern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); 98106933Ssam 9962143Sarchie/* Functional hooks called from if_ethersubr.c */ 100106933Ssamstatic void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 101106933Ssamstatic void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 10262143Sarchiestatic int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 10362143Sarchiestatic void ng_ether_attach(struct ifnet *ifp); 10462143Sarchiestatic void ng_ether_detach(struct ifnet *ifp); 105139903Sglebiusstatic void ng_ether_link_state(struct ifnet *ifp, int state); 10662143Sarchie 10762143Sarchie/* Other functions */ 108186488Sjulianstatic int ng_ether_rcv_lower(hook_p node, item_p item); 109186488Sjulianstatic int ng_ether_rcv_upper(hook_p node, item_p item); 11062143Sarchie 11162143Sarchie/* Netgraph node methods */ 11262143Sarchiestatic ng_constructor_t ng_ether_constructor; 11362143Sarchiestatic ng_rcvmsg_t ng_ether_rcvmsg; 11470700Sjulianstatic ng_shutdown_t ng_ether_shutdown; 11562143Sarchiestatic ng_newhook_t ng_ether_newhook; 11662143Sarchiestatic ng_rcvdata_t ng_ether_rcvdata; 11762143Sarchiestatic ng_disconnect_t ng_ether_disconnect; 11862143Sarchiestatic int ng_ether_mod_event(module_t mod, int event, void *data); 11962143Sarchie 120246245Savgstatic eventhandler_tag ng_ether_ifnet_arrival_cookie; 121246245Savg 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, 143123600Sru &ng_parse_enaddr_type 14464358Sarchie }, 14564358Sarchie { 14664358Sarchie NGM_ETHER_COOKIE, 14764653Sarchie NGM_ETHER_SET_ENADDR, 14864653Sarchie "setenaddr", 149123600Sru &ng_parse_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 }, 180141721Sglebius { 181141721Sglebius NGM_ETHER_COOKIE, 182141721Sglebius NGM_ETHER_ADD_MULTI, 183141721Sglebius "addmulti", 184141721Sglebius &ng_parse_enaddr_type, 185141721Sglebius NULL 186141721Sglebius }, 187141721Sglebius { 188141721Sglebius NGM_ETHER_COOKIE, 189141721Sglebius NGM_ETHER_DEL_MULTI, 190141721Sglebius "delmulti", 191141721Sglebius &ng_parse_enaddr_type, 192141721Sglebius NULL 193141721Sglebius }, 194141910Sglebius { 195141910Sglebius NGM_ETHER_COOKIE, 196141910Sglebius NGM_ETHER_DETACH, 197141910Sglebius "detach", 198141910Sglebius NULL, 199141910Sglebius NULL 200141910Sglebius }, 20162143Sarchie { 0 } 20262143Sarchie}; 20362143Sarchie 20462143Sarchiestatic struct ng_type ng_ether_typestruct = { 205129823Sjulian .version = NG_ABI_VERSION, 206129823Sjulian .name = NG_ETHER_NODE_TYPE, 207129823Sjulian .mod_event = ng_ether_mod_event, 208129823Sjulian .constructor = ng_ether_constructor, 209129823Sjulian .rcvmsg = ng_ether_rcvmsg, 210129823Sjulian .shutdown = ng_ether_shutdown, 211129823Sjulian .newhook = ng_ether_newhook, 212129823Sjulian .rcvdata = ng_ether_rcvdata, 213129823Sjulian .disconnect = ng_ether_disconnect, 214129823Sjulian .cmdlist = ng_ether_cmdlist, 21562143Sarchie}; 21662143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct); 21762143Sarchie 21862143Sarchie/****************************************************************** 219246245Savg UTILITY FUNCTIONS 220246245Savg******************************************************************/ 221246245Savgstatic void 222246245Savgng_ether_sanitize_ifname(const char *ifname, char *name) 223246245Savg{ 224246245Savg int i; 225246245Savg 226246245Savg for (i = 0; i < IFNAMSIZ; i++) { 227246245Savg if (ifname[i] == '.' || ifname[i] == ':') 228246245Savg name[i] = '_'; 229246245Savg else 230246245Savg name[i] = ifname[i]; 231246245Savg if (name[i] == '\0') 232246245Savg break; 233246245Savg } 234246245Savg} 235246245Savg 236246245Savg/****************************************************************** 23762143Sarchie ETHERNET FUNCTION HOOKS 23862143Sarchie******************************************************************/ 23962143Sarchie 24062143Sarchie/* 24162143Sarchie * Handle a packet that has come in on an interface. We get to 24262143Sarchie * look at it here before any upper layer protocols do. 24362143Sarchie */ 24462143Sarchiestatic void 245106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp) 24662143Sarchie{ 24762143Sarchie const node_p node = IFP2NG(ifp); 24870784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 249129281Sarchie int error; 25062143Sarchie 25162143Sarchie /* If "lower" hook not connected, let packet continue */ 252129281Sarchie if (priv->lower == NULL) 25362143Sarchie return; 254129281Sarchie NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 25562143Sarchie} 25662143Sarchie 25762143Sarchie/* 25862143Sarchie * Handle a packet that has come in on an interface, and which 25962143Sarchie * does not match any of our known protocols (an ``orphan''). 26062143Sarchie */ 26162143Sarchiestatic void 262106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 26362143Sarchie{ 26462143Sarchie const node_p node = IFP2NG(ifp); 26570784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 266129281Sarchie int error; 26762143Sarchie 268129281Sarchie /* If "orphan" hook not connected, discard packet */ 269129281Sarchie if (priv->orphan == NULL) { 27062143Sarchie m_freem(m); 27162143Sarchie return; 27262143Sarchie } 273129281Sarchie NG_SEND_DATA_ONLY(error, priv->orphan, m); 27462143Sarchie} 27562143Sarchie 27662143Sarchie/* 27762143Sarchie * Handle a packet that is going out on an interface. 27862143Sarchie * The Ethernet header is already attached to the mbuf. 27962143Sarchie */ 28062143Sarchiestatic int 28162143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp) 28262143Sarchie{ 28362143Sarchie const node_p node = IFP2NG(ifp); 28470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 28562143Sarchie int error = 0; 28662143Sarchie 28762143Sarchie /* If "upper" hook not connected, let packet continue */ 28862143Sarchie if (priv->upper == NULL) 28962143Sarchie return (0); 29062143Sarchie 29162143Sarchie /* Send it out "upper" hook */ 292194012Szec NG_OUTBOUND_THREAD_REF(); 29370700Sjulian NG_SEND_DATA_ONLY(error, priv->upper, *mp); 294194012Szec NG_OUTBOUND_THREAD_UNREF(); 29562143Sarchie return (error); 29662143Sarchie} 29762143Sarchie 29862143Sarchie/* 29962143Sarchie * A new Ethernet interface has been attached. 30062143Sarchie * Create a new node for it, etc. 30162143Sarchie */ 30262143Sarchiestatic void 30362143Sarchieng_ether_attach(struct ifnet *ifp) 30462143Sarchie{ 305246245Savg char name[IFNAMSIZ]; 30662143Sarchie priv_p priv; 30762143Sarchie node_p node; 30862143Sarchie 309191510Szec /* 310191510Szec * Do not create / attach an ether node to this ifnet if 311191510Szec * a netgraph node with the same name already exists. 312191510Szec * This should prevent ether nodes to become attached to 313191510Szec * eiface nodes, which may be problematic due to naming 314191510Szec * clashes. 315191510Szec */ 316191510Szec if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) { 317191510Szec NG_NODE_UNREF(node); 318191510Szec return; 319191510Szec } 320191510Szec 32162143Sarchie /* Create node */ 32287599Sobrien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 32362143Sarchie if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 32462143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 325121816Sbrooks __func__, "create node", ifp->if_xname); 32662143Sarchie return; 32762143Sarchie } 32862143Sarchie 32962143Sarchie /* Allocate private data */ 330184205Sdes priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 33162143Sarchie if (priv == NULL) { 33262143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 333121816Sbrooks __func__, "allocate memory", ifp->if_xname); 33470784Sjulian NG_NODE_UNREF(node); 33562143Sarchie return; 33662143Sarchie } 33770784Sjulian NG_NODE_SET_PRIVATE(node, priv); 33862143Sarchie priv->ifp = ifp; 339152243Sru IFP2NG(ifp) = node; 34090249Sarchie priv->hwassist = ifp->if_hwassist; 34162143Sarchie 34262143Sarchie /* Try to give the node the same name as the interface */ 343246245Savg ng_ether_sanitize_ifname(ifp->if_xname, name); 344246245Savg if (ng_name_node(node, name) != 0) 345246245Savg log(LOG_WARNING, "%s: can't name node %s\n", __func__, name); 34662143Sarchie} 34762143Sarchie 34862143Sarchie/* 34962143Sarchie * An Ethernet interface is being detached. 35071849Sjulian * REALLY Destroy its node. 35162143Sarchie */ 35262143Sarchiestatic void 35362143Sarchieng_ether_detach(struct ifnet *ifp) 35462143Sarchie{ 35562143Sarchie const node_p node = IFP2NG(ifp); 35671849Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 35762143Sarchie 358224107Szec taskqueue_drain(taskqueue_swi, &ifp->if_linktask); 35971849Sjulian NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 36071849Sjulian /* 36171849Sjulian * We can't assume the ifnet is still around when we run shutdown 36271849Sjulian * So zap it now. XXX We HOPE that anything running at this time 36371849Sjulian * handles it (as it should in the non netgraph case). 36471849Sjulian */ 365152243Sru IFP2NG(ifp) = NULL; 36671849Sjulian priv->ifp = NULL; /* XXX race if interrupted an output packet */ 36771849Sjulian ng_rmnode_self(node); /* remove all netgraph parts */ 36862143Sarchie} 36962143Sarchie 370139903Sglebius/* 371139903Sglebius * Notify graph about link event. 372139903Sglebius * if_link_state_change() has already checked that the state has changed. 373139903Sglebius */ 374139903Sglebiusstatic void 375139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state) 376139903Sglebius{ 377139903Sglebius const node_p node = IFP2NG(ifp); 378139903Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 379139903Sglebius struct ng_mesg *msg; 380139903Sglebius int cmd, dummy_error = 0; 381139903Sglebius 382139903Sglebius if (state == LINK_STATE_UP) 383139903Sglebius cmd = NGM_LINK_IS_UP; 384139903Sglebius else if (state == LINK_STATE_DOWN) 385139903Sglebius cmd = NGM_LINK_IS_DOWN; 386139903Sglebius else 387139903Sglebius return; 388139903Sglebius 389201924Sfjoe if (priv->lower != NULL) { 390201924Sfjoe NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 391201924Sfjoe if (msg != NULL) 392201924Sfjoe NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 393201924Sfjoe } 394201924Sfjoe if (priv->orphan != NULL) { 395201924Sfjoe NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 396201924Sfjoe if (msg != NULL) 397201924Sfjoe NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0); 398201924Sfjoe } 399139903Sglebius} 400139903Sglebius 401246245Savg/* 402246245Savg * Interface arrival notification handler. 403246245Savg * The notification is produced in two cases: 404246245Savg * o a new interface arrives 405246245Savg * o an existing interface got renamed 406246245Savg * Currently the first case is handled by ng_ether_attach via special 407246245Savg * hook ng_ether_attach_p. 408246245Savg */ 409246245Savgstatic void 410246245Savgng_ether_ifnet_arrival_event(void *arg __unused, struct ifnet *ifp) 411246245Savg{ 412246245Savg char name[IFNAMSIZ]; 413246324Savg node_p node; 414246245Savg 415246324Savg /* Only ethernet interfaces are of interest. */ 416246324Savg if (ifp->if_type != IFT_ETHER 417246324Savg && ifp->if_type != IFT_L2VLAN) 418246324Savg return; 419246324Savg 420246245Savg /* 421246245Savg * Just return if it's a new interface without an ng_ether companion. 422246245Savg */ 423246324Savg node = IFP2NG(ifp); 424246245Savg if (node == NULL) 425246245Savg return; 426246245Savg 427246245Savg /* Try to give the node the same name as the new interface name */ 428246245Savg ng_ether_sanitize_ifname(ifp->if_xname, name); 429246245Savg if (ng_name_node(node, name) != 0) 430246245Savg log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name); 431246245Savg} 432246245Savg 43362143Sarchie/****************************************************************** 43462143Sarchie NETGRAPH NODE METHODS 43562143Sarchie******************************************************************/ 43662143Sarchie 43762143Sarchie/* 43862143Sarchie * It is not possible or allowable to create a node of this type. 43962143Sarchie * Nodes get created when the interface is attached (or, when 44062143Sarchie * this node type's KLD is loaded). 44162143Sarchie */ 44262143Sarchiestatic int 44370700Sjulianng_ether_constructor(node_p node) 44462143Sarchie{ 44562143Sarchie return (EINVAL); 44662143Sarchie} 44762143Sarchie 44862143Sarchie/* 44962143Sarchie * Check for attaching a new hook. 45062143Sarchie */ 45162143Sarchiestatic int 45262143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name) 45362143Sarchie{ 45470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 45562143Sarchie hook_p *hookptr; 45662143Sarchie 45762143Sarchie /* Divert hook is an alias for lower */ 45862143Sarchie if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 45962143Sarchie name = NG_ETHER_HOOK_LOWER; 46062143Sarchie 46162143Sarchie /* Which hook? */ 462186488Sjulian if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) { 46362143Sarchie hookptr = &priv->upper; 464186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper); 465194012Szec NG_HOOK_SET_TO_INBOUND(hook); 466186488Sjulian } else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 46762143Sarchie hookptr = &priv->lower; 468186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 469186488Sjulian } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 470129281Sarchie hookptr = &priv->orphan; 471186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 472186488Sjulian } else 47362143Sarchie return (EINVAL); 47462143Sarchie 47562143Sarchie /* Check if already connected (shouldn't be, but doesn't hurt) */ 47662143Sarchie if (*hookptr != NULL) 47762143Sarchie return (EISCONN); 47862143Sarchie 47990249Sarchie /* Disable hardware checksums while 'upper' hook is connected */ 48090249Sarchie if (hookptr == &priv->upper) 48190249Sarchie priv->ifp->if_hwassist = 0; 482194699Smav NG_HOOK_HI_STACK(hook); 48362143Sarchie /* OK */ 48462143Sarchie *hookptr = hook; 48562143Sarchie return (0); 48662143Sarchie} 48762143Sarchie 48862143Sarchie/* 48962143Sarchie * Receive an incoming control message. 49062143Sarchie */ 49162143Sarchiestatic int 49270700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 49362143Sarchie{ 49470784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 49562143Sarchie struct ng_mesg *resp = NULL; 49662143Sarchie int error = 0; 49770700Sjulian struct ng_mesg *msg; 49862143Sarchie 49970700Sjulian NGI_GET_MSG(item, msg); 50062143Sarchie switch (msg->header.typecookie) { 50162143Sarchie case NGM_ETHER_COOKIE: 50262143Sarchie switch (msg->header.cmd) { 50362143Sarchie case NGM_ETHER_GET_IFNAME: 504141195Sru NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 50562143Sarchie if (resp == NULL) { 50662143Sarchie error = ENOMEM; 50762143Sarchie break; 50862143Sarchie } 509141195Sru strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 51062143Sarchie break; 51162143Sarchie case NGM_ETHER_GET_IFINDEX: 51262143Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 51362143Sarchie if (resp == NULL) { 51462143Sarchie error = ENOMEM; 51562143Sarchie break; 51662143Sarchie } 51762143Sarchie *((u_int32_t *)resp->data) = priv->ifp->if_index; 51862143Sarchie break; 51964358Sarchie case NGM_ETHER_GET_ENADDR: 52064358Sarchie NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 52164358Sarchie if (resp == NULL) { 52264358Sarchie error = ENOMEM; 52364358Sarchie break; 52464358Sarchie } 525152315Sru bcopy(IF_LLADDR(priv->ifp), 52664358Sarchie resp->data, ETHER_ADDR_LEN); 52764358Sarchie break; 52864653Sarchie case NGM_ETHER_SET_ENADDR: 52964653Sarchie { 53064653Sarchie if (msg->header.arglen != ETHER_ADDR_LEN) { 53164653Sarchie error = EINVAL; 53264653Sarchie break; 53364653Sarchie } 53464653Sarchie error = if_setlladdr(priv->ifp, 53564653Sarchie (u_char *)msg->data, ETHER_ADDR_LEN); 536202588Sthompsa EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp); 53764653Sarchie break; 53864653Sarchie } 53964653Sarchie case NGM_ETHER_GET_PROMISC: 54064653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 54164653Sarchie if (resp == NULL) { 54264653Sarchie error = ENOMEM; 54364653Sarchie break; 54464653Sarchie } 54564653Sarchie *((u_int32_t *)resp->data) = priv->promisc; 54664653Sarchie break; 54764358Sarchie case NGM_ETHER_SET_PROMISC: 54864358Sarchie { 54964358Sarchie u_char want; 55064358Sarchie 55164358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 55264358Sarchie error = EINVAL; 55364358Sarchie break; 55464358Sarchie } 55564358Sarchie want = !!*((u_int32_t *)msg->data); 55664358Sarchie if (want ^ priv->promisc) { 55764358Sarchie if ((error = ifpromisc(priv->ifp, want)) != 0) 55864358Sarchie break; 55964358Sarchie priv->promisc = want; 56064358Sarchie } 56164358Sarchie break; 56264358Sarchie } 56364653Sarchie case NGM_ETHER_GET_AUTOSRC: 56464653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 56564653Sarchie if (resp == NULL) { 56664653Sarchie error = ENOMEM; 56764653Sarchie break; 56864653Sarchie } 56964653Sarchie *((u_int32_t *)resp->data) = priv->autoSrcAddr; 57064653Sarchie break; 57164358Sarchie case NGM_ETHER_SET_AUTOSRC: 57264358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 57364358Sarchie error = EINVAL; 57464358Sarchie break; 57564358Sarchie } 57664358Sarchie priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 57764358Sarchie break; 578141721Sglebius case NGM_ETHER_ADD_MULTI: 579141721Sglebius { 580141721Sglebius struct sockaddr_dl sa_dl; 581167729Sbms struct ifmultiaddr *ifma; 582141721Sglebius 583141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 584141721Sglebius error = EINVAL; 585141721Sglebius break; 586141721Sglebius } 587141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 588141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 589141721Sglebius sa_dl.sdl_family = AF_LINK; 590141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 591141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 592141721Sglebius ETHER_ADDR_LEN); 593167729Sbms /* 594167729Sbms * Netgraph is only permitted to join groups once 595167729Sbms * via the if_addmulti() KPI, because it cannot hold 596167729Sbms * struct ifmultiaddr * between calls. It may also 597167729Sbms * lose a race while we check if the membership 598167729Sbms * already exists. 599167729Sbms */ 600195049Srwatson if_maddr_rlock(priv->ifp); 601167729Sbms ifma = if_findmulti(priv->ifp, 602167729Sbms (struct sockaddr *)&sa_dl); 603195049Srwatson if_maddr_runlock(priv->ifp); 604167729Sbms if (ifma != NULL) { 605167729Sbms error = EADDRINUSE; 606167729Sbms } else { 607167729Sbms error = if_addmulti(priv->ifp, 608167729Sbms (struct sockaddr *)&sa_dl, &ifma); 609167729Sbms } 610141721Sglebius break; 611141721Sglebius } 612141721Sglebius case NGM_ETHER_DEL_MULTI: 613141721Sglebius { 614141721Sglebius struct sockaddr_dl sa_dl; 615141721Sglebius 616141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 617141721Sglebius error = EINVAL; 618141721Sglebius break; 619141721Sglebius } 620141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 621141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 622141721Sglebius sa_dl.sdl_family = AF_LINK; 623141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 624141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 625141721Sglebius ETHER_ADDR_LEN); 626141721Sglebius error = if_delmulti(priv->ifp, 627141721Sglebius (struct sockaddr *)&sa_dl); 628141721Sglebius break; 629141721Sglebius } 630141910Sglebius case NGM_ETHER_DETACH: 631141910Sglebius ng_ether_detach(priv->ifp); 632141910Sglebius break; 63362143Sarchie default: 63462143Sarchie error = EINVAL; 63562143Sarchie break; 63662143Sarchie } 63762143Sarchie break; 63862143Sarchie default: 63962143Sarchie error = EINVAL; 64062143Sarchie break; 64162143Sarchie } 64270700Sjulian NG_RESPOND_MSG(error, node, item, resp); 64370700Sjulian NG_FREE_MSG(msg); 64462143Sarchie return (error); 64562143Sarchie} 64662143Sarchie 64762143Sarchie/* 64862143Sarchie * Receive data on a hook. 649186488Sjulian * Since we use per-hook recveive methods this should never be called. 65062143Sarchie */ 65162143Sarchiestatic int 65270700Sjulianng_ether_rcvdata(hook_p hook, item_p item) 65362143Sarchie{ 65470700Sjulian NG_FREE_ITEM(item); 655131155Sjulian 65687599Sobrien panic("%s: weird hook", __func__); 65762143Sarchie} 65862143Sarchie 65962143Sarchie/* 660129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook. 66162143Sarchie */ 66262143Sarchiestatic int 663186488Sjulianng_ether_rcv_lower(hook_p hook, item_p item) 66462143Sarchie{ 665186488Sjulian struct mbuf *m; 666186488Sjulian const node_p node = NG_HOOK_NODE(hook); 66770784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 66896265Sarchie struct ifnet *const ifp = priv->ifp; 66962143Sarchie 670186488Sjulian NGI_GET_M(item, m); 671186488Sjulian NG_FREE_ITEM(item); 672186488Sjulian 67396265Sarchie /* Check whether interface is ready for packets */ 674186488Sjulian 675148887Srwatson if (!((ifp->if_flags & IFF_UP) && 676148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 67796265Sarchie NG_FREE_M(m); 67896265Sarchie return (ENETDOWN); 67996265Sarchie } 68096265Sarchie 68162143Sarchie /* Make sure header is fully pulled up */ 68262143Sarchie if (m->m_pkthdr.len < sizeof(struct ether_header)) { 68370700Sjulian NG_FREE_M(m); 68462143Sarchie return (EINVAL); 68562143Sarchie } 68662143Sarchie if (m->m_len < sizeof(struct ether_header) 68797896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 68862143Sarchie return (ENOBUFS); 68962143Sarchie 69064358Sarchie /* Drop in the MAC address if desired */ 69164358Sarchie if (priv->autoSrcAddr) { 69297896Sarchie 69397896Sarchie /* Make the mbuf writable if it's not already */ 69497896Sarchie if (!M_WRITABLE(m) 69597896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 69697896Sarchie return (ENOBUFS); 69797896Sarchie 69897896Sarchie /* Overwrite source MAC address */ 699152315Sru bcopy(IF_LLADDR(ifp), 70064358Sarchie mtod(m, struct ether_header *)->ether_shost, 70164358Sarchie ETHER_ADDR_LEN); 70264358Sarchie } 70362678Sjulian 70462143Sarchie /* Send it on its way */ 70596265Sarchie return ether_output_frame(ifp, m); 70662143Sarchie} 70762143Sarchie 70862143Sarchie/* 70962143Sarchie * Handle an mbuf received on the "upper" hook. 71062143Sarchie */ 71162143Sarchiestatic int 712186488Sjulianng_ether_rcv_upper(hook_p hook, item_p item) 71362143Sarchie{ 714186488Sjulian struct mbuf *m; 715186488Sjulian const node_p node = NG_HOOK_NODE(hook); 71670784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 717151063Sglebius struct ifnet *ifp = priv->ifp; 71862143Sarchie 719186488Sjulian NGI_GET_M(item, m); 720186488Sjulian NG_FREE_ITEM(item); 721186488Sjulian 722152001Sru /* Check length and pull off header */ 723152001Sru if (m->m_pkthdr.len < sizeof(struct ether_header)) { 724152001Sru NG_FREE_M(m); 725152001Sru return (EINVAL); 726152001Sru } 727152001Sru if (m->m_len < sizeof(struct ether_header) && 728152001Sru (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 729152001Sru return (ENOBUFS); 730152001Sru 731151063Sglebius m->m_pkthdr.rcvif = ifp; 73262143Sarchie 733151305Sthompsa /* Pass the packet to the bridge, it may come back to us */ 734151063Sglebius if (ifp->if_bridge) { 735151305Sthompsa BRIDGE_INPUT(ifp, m); 736151063Sglebius if (m == NULL) 737151063Sglebius return (0); 738151063Sglebius } 739151063Sglebius 74062143Sarchie /* Route packet back in */ 741151305Sthompsa ether_demux(ifp, m); 74262143Sarchie return (0); 74362143Sarchie} 74462143Sarchie 74562143Sarchie/* 74671849Sjulian * Shutdown node. This resets the node but does not remove it 74771849Sjulian * unless the REALLY_DIE flag is set. 74862143Sarchie */ 74962143Sarchiestatic int 75070700Sjulianng_ether_shutdown(node_p node) 75162143Sarchie{ 75270784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 75364358Sarchie 754132464Sjulian if (node->nd_flags & NGF_REALLY_DIE) { 75571849Sjulian /* 75671849Sjulian * WE came here because the ethernet card is being unloaded, 75771849Sjulian * so stop being persistant. 75871849Sjulian * Actually undo all the things we did on creation. 75971849Sjulian * Assume the ifp has already been freed. 76071849Sjulian */ 76171849Sjulian NG_NODE_SET_PRIVATE(node, NULL); 762184205Sdes free(priv, M_NETGRAPH); 76371849Sjulian NG_NODE_UNREF(node); /* free node itself */ 76471849Sjulian return (0); 76570700Sjulian } 766124269Sgreen if (priv->promisc) { /* disable promiscuous mode */ 767124269Sgreen (void)ifpromisc(priv->ifp, 0); 768124269Sgreen priv->promisc = 0; 769124269Sgreen } 770132464Sjulian NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 771132464Sjulian 77262143Sarchie return (0); 77362143Sarchie} 77462143Sarchie 77562143Sarchie/* 77662143Sarchie * Hook disconnection. 77762143Sarchie */ 77862143Sarchiestatic int 77962143Sarchieng_ether_disconnect(hook_p hook) 78062143Sarchie{ 78170784Sjulian const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 78262143Sarchie 78390249Sarchie if (hook == priv->upper) { 78462143Sarchie priv->upper = NULL; 785124270Sgreen if (priv->ifp != NULL) /* restore h/w csum */ 786124270Sgreen priv->ifp->if_hwassist = priv->hwassist; 787129281Sarchie } else if (hook == priv->lower) 78862143Sarchie priv->lower = NULL; 789129281Sarchie else if (hook == priv->orphan) 790129281Sarchie priv->orphan = NULL; 791129281Sarchie else 79287599Sobrien panic("%s: weird hook", __func__); 79370784Sjulian if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 79470784Sjulian && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 79570784Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 79662143Sarchie return (0); 79762143Sarchie} 79862143Sarchie 79962143Sarchie/****************************************************************** 80062143Sarchie INITIALIZATION 80162143Sarchie******************************************************************/ 80262143Sarchie 80362143Sarchie/* 80462143Sarchie * Handle loading and unloading for this node type. 80562143Sarchie */ 80662143Sarchiestatic int 80762143Sarchieng_ether_mod_event(module_t mod, int event, void *data) 80862143Sarchie{ 80962143Sarchie int error = 0; 81062143Sarchie 81162143Sarchie switch (event) { 81262143Sarchie case MOD_LOAD: 81362143Sarchie 81462143Sarchie /* Register function hooks */ 81562143Sarchie if (ng_ether_attach_p != NULL) { 81662143Sarchie error = EEXIST; 81762143Sarchie break; 81862143Sarchie } 81962143Sarchie ng_ether_attach_p = ng_ether_attach; 82062143Sarchie ng_ether_detach_p = ng_ether_detach; 82162143Sarchie ng_ether_output_p = ng_ether_output; 82262143Sarchie ng_ether_input_p = ng_ether_input; 82362143Sarchie ng_ether_input_orphan_p = ng_ether_input_orphan; 824139903Sglebius ng_ether_link_state_p = ng_ether_link_state; 82562143Sarchie 826246245Savg ng_ether_ifnet_arrival_cookie = 827246245Savg EVENTHANDLER_REGISTER(ifnet_arrival_event, 828246245Savg ng_ether_ifnet_arrival_event, NULL, EVENTHANDLER_PRI_ANY); 82962143Sarchie break; 83062143Sarchie 83162143Sarchie case MOD_UNLOAD: 83262143Sarchie 83362143Sarchie /* 83462143Sarchie * Note that the base code won't try to unload us until 83562143Sarchie * all nodes have been removed, and that can't happen 83662143Sarchie * until all Ethernet interfaces are removed. In any 83762143Sarchie * case, we know there are no nodes left if the action 83862143Sarchie * is MOD_UNLOAD, so there's no need to detach any nodes. 83962143Sarchie */ 84062143Sarchie 841246245Savg EVENTHANDLER_DEREGISTER(ifnet_arrival_event, 842246245Savg ng_ether_ifnet_arrival_cookie); 843246245Savg 84462143Sarchie /* Unregister function hooks */ 84562143Sarchie ng_ether_attach_p = NULL; 84662143Sarchie ng_ether_detach_p = NULL; 84762143Sarchie ng_ether_output_p = NULL; 84862143Sarchie ng_ether_input_p = NULL; 84962143Sarchie ng_ether_input_orphan_p = NULL; 850139903Sglebius ng_ether_link_state_p = NULL; 85162143Sarchie break; 85262143Sarchie 85362143Sarchie default: 85462143Sarchie error = EOPNOTSUPP; 85562143Sarchie break; 85662143Sarchie } 85762143Sarchie return (error); 85862143Sarchie} 85962143Sarchie 860195837Srwatsonstatic void 861195837Srwatsonvnet_ng_ether_init(const void *unused) 862191510Szec{ 863191510Szec struct ifnet *ifp; 864191510Szec 865195837Srwatson /* If module load was rejected, don't attach to vnets. */ 866195837Srwatson if (ng_ether_attach_p != ng_ether_attach) 867195837Srwatson return; 868195837Srwatson 869191510Szec /* Create nodes for any already-existing Ethernet interfaces. */ 870191510Szec IFNET_RLOCK(); 871191510Szec TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 872191510Szec if (ifp->if_type == IFT_ETHER 873191510Szec || ifp->if_type == IFT_L2VLAN) 874191510Szec ng_ether_attach(ifp); 875191510Szec } 876191510Szec IFNET_RUNLOCK(); 877191510Szec} 878195837SrwatsonVNET_SYSINIT(vnet_ng_ether_init, SI_SUB_PSEUDO, SI_ORDER_ANY, 879195837Srwatson vnet_ng_ether_init, NULL); 880