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 74239429SemasteMODULE_VERSION(ng_ether, 1); 75239429Semaste 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 120247886Savgstatic eventhandler_tag ng_ether_ifnet_arrival_cookie; 121247886Savg 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/****************************************************************** 219247886Savg UTILITY FUNCTIONS 220247886Savg******************************************************************/ 221247886Savgstatic void 222247886Savgng_ether_sanitize_ifname(const char *ifname, char *name) 223247886Savg{ 224247886Savg int i; 225247886Savg 226247886Savg for (i = 0; i < IFNAMSIZ; i++) { 227247886Savg if (ifname[i] == '.' || ifname[i] == ':') 228247886Savg name[i] = '_'; 229247886Savg else 230247886Savg name[i] = ifname[i]; 231247886Savg if (name[i] == '\0') 232247886Savg break; 233247886Savg } 234247886Savg} 235247886Savg 236247886Savg/****************************************************************** 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 * 24462143Sarchie * NOTE: this function will get called at splimp() 24562143Sarchie */ 24662143Sarchiestatic void 247106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp) 24862143Sarchie{ 24962143Sarchie const node_p node = IFP2NG(ifp); 25070784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 251129281Sarchie int error; 25262143Sarchie 25362143Sarchie /* If "lower" hook not connected, let packet continue */ 254129281Sarchie if (priv->lower == NULL) 25562143Sarchie return; 256129281Sarchie NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 25762143Sarchie} 25862143Sarchie 25962143Sarchie/* 26062143Sarchie * Handle a packet that has come in on an interface, and which 26162143Sarchie * does not match any of our known protocols (an ``orphan''). 26262143Sarchie * 26362143Sarchie * NOTE: this function will get called at splimp() 26462143Sarchie */ 26562143Sarchiestatic void 266106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 26762143Sarchie{ 26862143Sarchie const node_p node = IFP2NG(ifp); 26970784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 270129281Sarchie int error; 27162143Sarchie 272129281Sarchie /* If "orphan" hook not connected, discard packet */ 273129281Sarchie if (priv->orphan == NULL) { 27462143Sarchie m_freem(m); 27562143Sarchie return; 27662143Sarchie } 277129281Sarchie NG_SEND_DATA_ONLY(error, priv->orphan, m); 27862143Sarchie} 27962143Sarchie 28062143Sarchie/* 28162143Sarchie * Handle a packet that is going out on an interface. 28262143Sarchie * The Ethernet header is already attached to the mbuf. 28362143Sarchie */ 28462143Sarchiestatic int 28562143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp) 28662143Sarchie{ 28762143Sarchie const node_p node = IFP2NG(ifp); 28870784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 28962143Sarchie int error = 0; 29062143Sarchie 29162143Sarchie /* If "upper" hook not connected, let packet continue */ 29262143Sarchie if (priv->upper == NULL) 29362143Sarchie return (0); 29462143Sarchie 29562143Sarchie /* Send it out "upper" hook */ 296194012Szec NG_OUTBOUND_THREAD_REF(); 29770700Sjulian NG_SEND_DATA_ONLY(error, priv->upper, *mp); 298194012Szec NG_OUTBOUND_THREAD_UNREF(); 29962143Sarchie return (error); 30062143Sarchie} 30162143Sarchie 30262143Sarchie/* 30362143Sarchie * A new Ethernet interface has been attached. 30462143Sarchie * Create a new node for it, etc. 30562143Sarchie */ 30662143Sarchiestatic void 30762143Sarchieng_ether_attach(struct ifnet *ifp) 30862143Sarchie{ 309247886Savg char name[IFNAMSIZ]; 31062143Sarchie priv_p priv; 31162143Sarchie node_p node; 31262143Sarchie 313191510Szec /* 314191510Szec * Do not create / attach an ether node to this ifnet if 315191510Szec * a netgraph node with the same name already exists. 316191510Szec * This should prevent ether nodes to become attached to 317191510Szec * eiface nodes, which may be problematic due to naming 318191510Szec * clashes. 319191510Szec */ 320191510Szec if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) { 321191510Szec NG_NODE_UNREF(node); 322191510Szec return; 323191510Szec } 324191510Szec 32562143Sarchie /* Create node */ 32687599Sobrien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 32762143Sarchie if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 32862143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 329121816Sbrooks __func__, "create node", ifp->if_xname); 33062143Sarchie return; 33162143Sarchie } 33262143Sarchie 33362143Sarchie /* Allocate private data */ 334184205Sdes priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 33562143Sarchie if (priv == NULL) { 33662143Sarchie log(LOG_ERR, "%s: can't %s for %s\n", 337121816Sbrooks __func__, "allocate memory", ifp->if_xname); 33870784Sjulian NG_NODE_UNREF(node); 33962143Sarchie return; 34062143Sarchie } 34170784Sjulian NG_NODE_SET_PRIVATE(node, priv); 34262143Sarchie priv->ifp = ifp; 343152243Sru IFP2NG(ifp) = node; 34490249Sarchie priv->hwassist = ifp->if_hwassist; 34562143Sarchie 34662143Sarchie /* Try to give the node the same name as the interface */ 347247886Savg ng_ether_sanitize_ifname(ifp->if_xname, name); 348247886Savg if (ng_name_node(node, name) != 0) 349247886Savg log(LOG_WARNING, "%s: can't name node %s\n", __func__, name); 35062143Sarchie} 35162143Sarchie 35262143Sarchie/* 35362143Sarchie * An Ethernet interface is being detached. 35471849Sjulian * REALLY Destroy its node. 35562143Sarchie */ 35662143Sarchiestatic void 35762143Sarchieng_ether_detach(struct ifnet *ifp) 35862143Sarchie{ 35962143Sarchie const node_p node = IFP2NG(ifp); 36071849Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 36162143Sarchie 362224107Szec taskqueue_drain(taskqueue_swi, &ifp->if_linktask); 36371849Sjulian NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 36471849Sjulian /* 36571849Sjulian * We can't assume the ifnet is still around when we run shutdown 36671849Sjulian * So zap it now. XXX We HOPE that anything running at this time 36771849Sjulian * handles it (as it should in the non netgraph case). 36871849Sjulian */ 369152243Sru IFP2NG(ifp) = NULL; 37071849Sjulian priv->ifp = NULL; /* XXX race if interrupted an output packet */ 37171849Sjulian ng_rmnode_self(node); /* remove all netgraph parts */ 37262143Sarchie} 37362143Sarchie 374139903Sglebius/* 375139903Sglebius * Notify graph about link event. 376139903Sglebius * if_link_state_change() has already checked that the state has changed. 377139903Sglebius */ 378139903Sglebiusstatic void 379139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state) 380139903Sglebius{ 381139903Sglebius const node_p node = IFP2NG(ifp); 382139903Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 383139903Sglebius struct ng_mesg *msg; 384139903Sglebius int cmd, dummy_error = 0; 385139903Sglebius 386139903Sglebius if (state == LINK_STATE_UP) 387139903Sglebius cmd = NGM_LINK_IS_UP; 388139903Sglebius else if (state == LINK_STATE_DOWN) 389139903Sglebius cmd = NGM_LINK_IS_DOWN; 390139903Sglebius else 391139903Sglebius return; 392139903Sglebius 393201924Sfjoe if (priv->lower != NULL) { 394201924Sfjoe NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 395201924Sfjoe if (msg != NULL) 396201924Sfjoe NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 397201924Sfjoe } 398201924Sfjoe if (priv->orphan != NULL) { 399201924Sfjoe NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 400201924Sfjoe if (msg != NULL) 401201924Sfjoe NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0); 402201924Sfjoe } 403139903Sglebius} 404139903Sglebius 405247886Savg/* 406247886Savg * Interface arrival notification handler. 407247886Savg * The notification is produced in two cases: 408247886Savg * o a new interface arrives 409247886Savg * o an existing interface got renamed 410247886Savg * Currently the first case is handled by ng_ether_attach via special 411247886Savg * hook ng_ether_attach_p. 412247886Savg */ 413247886Savgstatic void 414247886Savgng_ether_ifnet_arrival_event(void *arg __unused, struct ifnet *ifp) 415247886Savg{ 416247886Savg char name[IFNAMSIZ]; 417247886Savg node_p node; 418247886Savg 419247886Savg /* Only ethernet interfaces are of interest. */ 420247886Savg if (ifp->if_type != IFT_ETHER 421247886Savg && ifp->if_type != IFT_L2VLAN) 422247886Savg return; 423247886Savg 424247886Savg /* 425247886Savg * Just return if it's a new interface without an ng_ether companion. 426247886Savg */ 427247886Savg node = IFP2NG(ifp); 428247886Savg if (node == NULL) 429247886Savg return; 430247886Savg 431247886Savg /* Try to give the node the same name as the new interface name */ 432247886Savg ng_ether_sanitize_ifname(ifp->if_xname, name); 433247886Savg if (ng_name_node(node, name) != 0) 434247886Savg log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name); 435247886Savg} 436247886Savg 43762143Sarchie/****************************************************************** 43862143Sarchie NETGRAPH NODE METHODS 43962143Sarchie******************************************************************/ 44062143Sarchie 44162143Sarchie/* 44262143Sarchie * It is not possible or allowable to create a node of this type. 44362143Sarchie * Nodes get created when the interface is attached (or, when 44462143Sarchie * this node type's KLD is loaded). 44562143Sarchie */ 44662143Sarchiestatic int 44770700Sjulianng_ether_constructor(node_p node) 44862143Sarchie{ 44962143Sarchie return (EINVAL); 45062143Sarchie} 45162143Sarchie 45262143Sarchie/* 45362143Sarchie * Check for attaching a new hook. 45462143Sarchie */ 45562143Sarchiestatic int 45662143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name) 45762143Sarchie{ 45870784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 45962143Sarchie hook_p *hookptr; 46062143Sarchie 46162143Sarchie /* Divert hook is an alias for lower */ 46262143Sarchie if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 46362143Sarchie name = NG_ETHER_HOOK_LOWER; 46462143Sarchie 46562143Sarchie /* Which hook? */ 466186488Sjulian if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) { 46762143Sarchie hookptr = &priv->upper; 468186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper); 469194012Szec NG_HOOK_SET_TO_INBOUND(hook); 470186488Sjulian } else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 47162143Sarchie hookptr = &priv->lower; 472186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 473186488Sjulian } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 474129281Sarchie hookptr = &priv->orphan; 475186488Sjulian NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 476186488Sjulian } else 47762143Sarchie return (EINVAL); 47862143Sarchie 47962143Sarchie /* Check if already connected (shouldn't be, but doesn't hurt) */ 48062143Sarchie if (*hookptr != NULL) 48162143Sarchie return (EISCONN); 48262143Sarchie 48390249Sarchie /* Disable hardware checksums while 'upper' hook is connected */ 48490249Sarchie if (hookptr == &priv->upper) 48590249Sarchie priv->ifp->if_hwassist = 0; 486194699Smav NG_HOOK_HI_STACK(hook); 48762143Sarchie /* OK */ 48862143Sarchie *hookptr = hook; 48962143Sarchie return (0); 49062143Sarchie} 49162143Sarchie 49262143Sarchie/* 49362143Sarchie * Receive an incoming control message. 49462143Sarchie */ 49562143Sarchiestatic int 49670700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 49762143Sarchie{ 49870784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 49962143Sarchie struct ng_mesg *resp = NULL; 50062143Sarchie int error = 0; 50170700Sjulian struct ng_mesg *msg; 50262143Sarchie 50370700Sjulian NGI_GET_MSG(item, msg); 50462143Sarchie switch (msg->header.typecookie) { 50562143Sarchie case NGM_ETHER_COOKIE: 50662143Sarchie switch (msg->header.cmd) { 50762143Sarchie case NGM_ETHER_GET_IFNAME: 508141195Sru NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 50962143Sarchie if (resp == NULL) { 51062143Sarchie error = ENOMEM; 51162143Sarchie break; 51262143Sarchie } 513141195Sru strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 51462143Sarchie break; 51562143Sarchie case NGM_ETHER_GET_IFINDEX: 51662143Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 51762143Sarchie if (resp == NULL) { 51862143Sarchie error = ENOMEM; 51962143Sarchie break; 52062143Sarchie } 52162143Sarchie *((u_int32_t *)resp->data) = priv->ifp->if_index; 52262143Sarchie break; 52364358Sarchie case NGM_ETHER_GET_ENADDR: 52464358Sarchie NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 52564358Sarchie if (resp == NULL) { 52664358Sarchie error = ENOMEM; 52764358Sarchie break; 52864358Sarchie } 529152315Sru bcopy(IF_LLADDR(priv->ifp), 53064358Sarchie resp->data, ETHER_ADDR_LEN); 53164358Sarchie break; 53264653Sarchie case NGM_ETHER_SET_ENADDR: 53364653Sarchie { 53464653Sarchie if (msg->header.arglen != ETHER_ADDR_LEN) { 53564653Sarchie error = EINVAL; 53664653Sarchie break; 53764653Sarchie } 53864653Sarchie error = if_setlladdr(priv->ifp, 53964653Sarchie (u_char *)msg->data, ETHER_ADDR_LEN); 540202588Sthompsa EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp); 54164653Sarchie break; 54264653Sarchie } 54364653Sarchie case NGM_ETHER_GET_PROMISC: 54464653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 54564653Sarchie if (resp == NULL) { 54664653Sarchie error = ENOMEM; 54764653Sarchie break; 54864653Sarchie } 54964653Sarchie *((u_int32_t *)resp->data) = priv->promisc; 55064653Sarchie break; 55164358Sarchie case NGM_ETHER_SET_PROMISC: 55264358Sarchie { 55364358Sarchie u_char want; 55464358Sarchie 55564358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 55664358Sarchie error = EINVAL; 55764358Sarchie break; 55864358Sarchie } 55964358Sarchie want = !!*((u_int32_t *)msg->data); 56064358Sarchie if (want ^ priv->promisc) { 56164358Sarchie if ((error = ifpromisc(priv->ifp, want)) != 0) 56264358Sarchie break; 56364358Sarchie priv->promisc = want; 56464358Sarchie } 56564358Sarchie break; 56664358Sarchie } 56764653Sarchie case NGM_ETHER_GET_AUTOSRC: 56864653Sarchie NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 56964653Sarchie if (resp == NULL) { 57064653Sarchie error = ENOMEM; 57164653Sarchie break; 57264653Sarchie } 57364653Sarchie *((u_int32_t *)resp->data) = priv->autoSrcAddr; 57464653Sarchie break; 57564358Sarchie case NGM_ETHER_SET_AUTOSRC: 57664358Sarchie if (msg->header.arglen != sizeof(u_int32_t)) { 57764358Sarchie error = EINVAL; 57864358Sarchie break; 57964358Sarchie } 58064358Sarchie priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 58164358Sarchie break; 582141721Sglebius case NGM_ETHER_ADD_MULTI: 583141721Sglebius { 584141721Sglebius struct sockaddr_dl sa_dl; 585167729Sbms struct ifmultiaddr *ifma; 586141721Sglebius 587141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 588141721Sglebius error = EINVAL; 589141721Sglebius break; 590141721Sglebius } 591141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 592141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 593141721Sglebius sa_dl.sdl_family = AF_LINK; 594141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 595141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 596141721Sglebius ETHER_ADDR_LEN); 597167729Sbms /* 598167729Sbms * Netgraph is only permitted to join groups once 599167729Sbms * via the if_addmulti() KPI, because it cannot hold 600167729Sbms * struct ifmultiaddr * between calls. It may also 601167729Sbms * lose a race while we check if the membership 602167729Sbms * already exists. 603167729Sbms */ 604195049Srwatson if_maddr_rlock(priv->ifp); 605167729Sbms ifma = if_findmulti(priv->ifp, 606167729Sbms (struct sockaddr *)&sa_dl); 607195049Srwatson if_maddr_runlock(priv->ifp); 608167729Sbms if (ifma != NULL) { 609167729Sbms error = EADDRINUSE; 610167729Sbms } else { 611167729Sbms error = if_addmulti(priv->ifp, 612167729Sbms (struct sockaddr *)&sa_dl, &ifma); 613167729Sbms } 614141721Sglebius break; 615141721Sglebius } 616141721Sglebius case NGM_ETHER_DEL_MULTI: 617141721Sglebius { 618141721Sglebius struct sockaddr_dl sa_dl; 619141721Sglebius 620141721Sglebius if (msg->header.arglen != ETHER_ADDR_LEN) { 621141721Sglebius error = EINVAL; 622141721Sglebius break; 623141721Sglebius } 624141755Sglebius bzero(&sa_dl, sizeof(struct sockaddr_dl)); 625141721Sglebius sa_dl.sdl_len = sizeof(struct sockaddr_dl); 626141721Sglebius sa_dl.sdl_family = AF_LINK; 627141755Sglebius sa_dl.sdl_alen = ETHER_ADDR_LEN; 628141721Sglebius bcopy((void *)msg->data, LLADDR(&sa_dl), 629141721Sglebius ETHER_ADDR_LEN); 630141721Sglebius error = if_delmulti(priv->ifp, 631141721Sglebius (struct sockaddr *)&sa_dl); 632141721Sglebius break; 633141721Sglebius } 634141910Sglebius case NGM_ETHER_DETACH: 635141910Sglebius ng_ether_detach(priv->ifp); 636141910Sglebius break; 63762143Sarchie default: 63862143Sarchie error = EINVAL; 63962143Sarchie break; 64062143Sarchie } 64162143Sarchie break; 64262143Sarchie default: 64362143Sarchie error = EINVAL; 64462143Sarchie break; 64562143Sarchie } 64670700Sjulian NG_RESPOND_MSG(error, node, item, resp); 64770700Sjulian NG_FREE_MSG(msg); 64862143Sarchie return (error); 64962143Sarchie} 65062143Sarchie 65162143Sarchie/* 65262143Sarchie * Receive data on a hook. 653186488Sjulian * Since we use per-hook recveive methods this should never be called. 65462143Sarchie */ 65562143Sarchiestatic int 65670700Sjulianng_ether_rcvdata(hook_p hook, item_p item) 65762143Sarchie{ 65870700Sjulian NG_FREE_ITEM(item); 659131155Sjulian 66087599Sobrien panic("%s: weird hook", __func__); 66162143Sarchie} 66262143Sarchie 66362143Sarchie/* 664129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook. 66562143Sarchie */ 66662143Sarchiestatic int 667186488Sjulianng_ether_rcv_lower(hook_p hook, item_p item) 66862143Sarchie{ 669186488Sjulian struct mbuf *m; 670186488Sjulian const node_p node = NG_HOOK_NODE(hook); 67170784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 67296265Sarchie struct ifnet *const ifp = priv->ifp; 67362143Sarchie 674186488Sjulian NGI_GET_M(item, m); 675186488Sjulian NG_FREE_ITEM(item); 676186488Sjulian 67796265Sarchie /* Check whether interface is ready for packets */ 678186488Sjulian 679148887Srwatson if (!((ifp->if_flags & IFF_UP) && 680148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 68196265Sarchie NG_FREE_M(m); 68296265Sarchie return (ENETDOWN); 68396265Sarchie } 68496265Sarchie 68562143Sarchie /* Make sure header is fully pulled up */ 68662143Sarchie if (m->m_pkthdr.len < sizeof(struct ether_header)) { 68770700Sjulian NG_FREE_M(m); 68862143Sarchie return (EINVAL); 68962143Sarchie } 69062143Sarchie if (m->m_len < sizeof(struct ether_header) 69197896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 69262143Sarchie return (ENOBUFS); 69362143Sarchie 69464358Sarchie /* Drop in the MAC address if desired */ 69564358Sarchie if (priv->autoSrcAddr) { 69697896Sarchie 69797896Sarchie /* Make the mbuf writable if it's not already */ 69897896Sarchie if (!M_WRITABLE(m) 69997896Sarchie && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 70097896Sarchie return (ENOBUFS); 70197896Sarchie 70297896Sarchie /* Overwrite source MAC address */ 703152315Sru bcopy(IF_LLADDR(ifp), 70464358Sarchie mtod(m, struct ether_header *)->ether_shost, 70564358Sarchie ETHER_ADDR_LEN); 70664358Sarchie } 70762678Sjulian 70862143Sarchie /* Send it on its way */ 70996265Sarchie return ether_output_frame(ifp, m); 71062143Sarchie} 71162143Sarchie 71262143Sarchie/* 71362143Sarchie * Handle an mbuf received on the "upper" hook. 71462143Sarchie */ 71562143Sarchiestatic int 716186488Sjulianng_ether_rcv_upper(hook_p hook, item_p item) 71762143Sarchie{ 718186488Sjulian struct mbuf *m; 719186488Sjulian const node_p node = NG_HOOK_NODE(hook); 72070784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 721151063Sglebius struct ifnet *ifp = priv->ifp; 72262143Sarchie 723186488Sjulian NGI_GET_M(item, m); 724186488Sjulian NG_FREE_ITEM(item); 725186488Sjulian 726152001Sru /* Check length and pull off header */ 727152001Sru if (m->m_pkthdr.len < sizeof(struct ether_header)) { 728152001Sru NG_FREE_M(m); 729152001Sru return (EINVAL); 730152001Sru } 731152001Sru if (m->m_len < sizeof(struct ether_header) && 732152001Sru (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 733152001Sru return (ENOBUFS); 734152001Sru 735151063Sglebius m->m_pkthdr.rcvif = ifp; 73662143Sarchie 737151305Sthompsa /* Pass the packet to the bridge, it may come back to us */ 738151063Sglebius if (ifp->if_bridge) { 739151305Sthompsa BRIDGE_INPUT(ifp, m); 740151063Sglebius if (m == NULL) 741151063Sglebius return (0); 742151063Sglebius } 743151063Sglebius 74462143Sarchie /* Route packet back in */ 745151305Sthompsa ether_demux(ifp, m); 74662143Sarchie return (0); 74762143Sarchie} 74862143Sarchie 74962143Sarchie/* 75071849Sjulian * Shutdown node. This resets the node but does not remove it 75171849Sjulian * unless the REALLY_DIE flag is set. 75262143Sarchie */ 75362143Sarchiestatic int 75470700Sjulianng_ether_shutdown(node_p node) 75562143Sarchie{ 75670784Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 75764358Sarchie 758132464Sjulian if (node->nd_flags & NGF_REALLY_DIE) { 75971849Sjulian /* 76071849Sjulian * WE came here because the ethernet card is being unloaded, 76171849Sjulian * so stop being persistant. 76271849Sjulian * Actually undo all the things we did on creation. 76371849Sjulian * Assume the ifp has already been freed. 76471849Sjulian */ 76571849Sjulian NG_NODE_SET_PRIVATE(node, NULL); 766184205Sdes free(priv, M_NETGRAPH); 76771849Sjulian NG_NODE_UNREF(node); /* free node itself */ 76871849Sjulian return (0); 76970700Sjulian } 770124269Sgreen if (priv->promisc) { /* disable promiscuous mode */ 771124269Sgreen (void)ifpromisc(priv->ifp, 0); 772124269Sgreen priv->promisc = 0; 773124269Sgreen } 774132464Sjulian NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 775132464Sjulian 77662143Sarchie return (0); 77762143Sarchie} 77862143Sarchie 77962143Sarchie/* 78062143Sarchie * Hook disconnection. 78162143Sarchie */ 78262143Sarchiestatic int 78362143Sarchieng_ether_disconnect(hook_p hook) 78462143Sarchie{ 78570784Sjulian const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 78662143Sarchie 78790249Sarchie if (hook == priv->upper) { 78862143Sarchie priv->upper = NULL; 789124270Sgreen if (priv->ifp != NULL) /* restore h/w csum */ 790124270Sgreen priv->ifp->if_hwassist = priv->hwassist; 791129281Sarchie } else if (hook == priv->lower) 79262143Sarchie priv->lower = NULL; 793129281Sarchie else if (hook == priv->orphan) 794129281Sarchie priv->orphan = NULL; 795129281Sarchie else 79687599Sobrien panic("%s: weird hook", __func__); 79770784Sjulian if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 79870784Sjulian && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 79970784Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 80062143Sarchie return (0); 80162143Sarchie} 80262143Sarchie 80362143Sarchie/****************************************************************** 80462143Sarchie INITIALIZATION 80562143Sarchie******************************************************************/ 80662143Sarchie 80762143Sarchie/* 80862143Sarchie * Handle loading and unloading for this node type. 80962143Sarchie */ 81062143Sarchiestatic int 81162143Sarchieng_ether_mod_event(module_t mod, int event, void *data) 81262143Sarchie{ 81362143Sarchie int error = 0; 81462143Sarchie int s; 81562143Sarchie 81662143Sarchie s = splnet(); 81762143Sarchie switch (event) { 81862143Sarchie case MOD_LOAD: 81962143Sarchie 82062143Sarchie /* Register function hooks */ 82162143Sarchie if (ng_ether_attach_p != NULL) { 82262143Sarchie error = EEXIST; 82362143Sarchie break; 82462143Sarchie } 82562143Sarchie ng_ether_attach_p = ng_ether_attach; 82662143Sarchie ng_ether_detach_p = ng_ether_detach; 82762143Sarchie ng_ether_output_p = ng_ether_output; 82862143Sarchie ng_ether_input_p = ng_ether_input; 82962143Sarchie ng_ether_input_orphan_p = ng_ether_input_orphan; 830139903Sglebius ng_ether_link_state_p = ng_ether_link_state; 83162143Sarchie 832247886Savg ng_ether_ifnet_arrival_cookie = 833247886Savg EVENTHANDLER_REGISTER(ifnet_arrival_event, 834247886Savg ng_ether_ifnet_arrival_event, NULL, EVENTHANDLER_PRI_ANY); 83562143Sarchie break; 83662143Sarchie 83762143Sarchie case MOD_UNLOAD: 83862143Sarchie 83962143Sarchie /* 84062143Sarchie * Note that the base code won't try to unload us until 84162143Sarchie * all nodes have been removed, and that can't happen 84262143Sarchie * until all Ethernet interfaces are removed. In any 84362143Sarchie * case, we know there are no nodes left if the action 84462143Sarchie * is MOD_UNLOAD, so there's no need to detach any nodes. 84562143Sarchie */ 84662143Sarchie 847247886Savg EVENTHANDLER_DEREGISTER(ifnet_arrival_event, 848247886Savg ng_ether_ifnet_arrival_cookie); 849247886Savg 85062143Sarchie /* Unregister function hooks */ 85162143Sarchie ng_ether_attach_p = NULL; 85262143Sarchie ng_ether_detach_p = NULL; 85362143Sarchie ng_ether_output_p = NULL; 85462143Sarchie ng_ether_input_p = NULL; 85562143Sarchie ng_ether_input_orphan_p = NULL; 856139903Sglebius ng_ether_link_state_p = NULL; 85762143Sarchie break; 85862143Sarchie 85962143Sarchie default: 86062143Sarchie error = EOPNOTSUPP; 86162143Sarchie break; 86262143Sarchie } 86362143Sarchie splx(s); 86462143Sarchie return (error); 86562143Sarchie} 86662143Sarchie 867195837Srwatsonstatic void 868195837Srwatsonvnet_ng_ether_init(const void *unused) 869191510Szec{ 870191510Szec struct ifnet *ifp; 871191510Szec 872195837Srwatson /* If module load was rejected, don't attach to vnets. */ 873195837Srwatson if (ng_ether_attach_p != ng_ether_attach) 874195837Srwatson return; 875195837Srwatson 876191510Szec /* Create nodes for any already-existing Ethernet interfaces. */ 877191510Szec IFNET_RLOCK(); 878191510Szec TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 879191510Szec if (ifp->if_type == IFT_ETHER 880191510Szec || ifp->if_type == IFT_L2VLAN) 881191510Szec ng_ether_attach(ifp); 882191510Szec } 883191510Szec IFNET_RUNLOCK(); 884191510Szec} 885195837SrwatsonVNET_SYSINIT(vnet_ng_ether_init, SI_SUB_PSEUDO, SI_ORDER_ANY, 886195837Srwatson vnet_ng_ether_init, NULL); 887