1e1e1452dSArchie Cobbs 2e1e1452dSArchie Cobbs /* 3e1e1452dSArchie Cobbs * ng_ether.c 4c398230bSWarner Losh */ 5c398230bSWarner Losh 6c398230bSWarner Losh /*- 7e1e1452dSArchie Cobbs * Copyright (c) 1996-2000 Whistle Communications, Inc. 8e1e1452dSArchie Cobbs * All rights reserved. 9e1e1452dSArchie Cobbs * 10e1e1452dSArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 11e1e1452dSArchie Cobbs * redistribution of this software, in source or object code forms, with or 12e1e1452dSArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 13e1e1452dSArchie Cobbs * provided, however, that: 14e1e1452dSArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 15e1e1452dSArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 16e1e1452dSArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 17e1e1452dSArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 18e1e1452dSArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 19e1e1452dSArchie Cobbs * such appears in the above copyright notice or in the software. 20e1e1452dSArchie Cobbs * 21e1e1452dSArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 22e1e1452dSArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 23e1e1452dSArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 24e1e1452dSArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 25e1e1452dSArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 26e1e1452dSArchie Cobbs * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 27e1e1452dSArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 28e1e1452dSArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 29e1e1452dSArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 30e1e1452dSArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 31e1e1452dSArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 32e1e1452dSArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 33e1e1452dSArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 34e1e1452dSArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35e1e1452dSArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36e1e1452dSArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 37e1e1452dSArchie Cobbs * OF SUCH DAMAGE. 38e1e1452dSArchie Cobbs * 39e1e1452dSArchie Cobbs * Authors: Archie Cobbs <archie@freebsd.org> 40e1e1452dSArchie Cobbs * Julian Elischer <julian@freebsd.org> 41e1e1452dSArchie Cobbs * 42e1e1452dSArchie Cobbs * $FreeBSD$ 43e1e1452dSArchie Cobbs */ 44e1e1452dSArchie Cobbs 45e1e1452dSArchie Cobbs /* 46e1e1452dSArchie Cobbs * ng_ether(4) netgraph node type 47e1e1452dSArchie Cobbs */ 48e1e1452dSArchie Cobbs 49e1e1452dSArchie Cobbs #include <sys/param.h> 50e1e1452dSArchie Cobbs #include <sys/systm.h> 51e1e1452dSArchie Cobbs #include <sys/kernel.h> 52e1e1452dSArchie Cobbs #include <sys/malloc.h> 53e1e1452dSArchie Cobbs #include <sys/mbuf.h> 54e1e1452dSArchie Cobbs #include <sys/errno.h> 55e1e1452dSArchie Cobbs #include <sys/syslog.h> 56e1e1452dSArchie Cobbs #include <sys/socket.h> 57e1e1452dSArchie Cobbs 58e1e1452dSArchie Cobbs #include <net/if.h> 59810d5e89SGleb Smirnoff #include <net/if_dl.h> 60e1e1452dSArchie Cobbs #include <net/if_types.h> 61e1e1452dSArchie Cobbs #include <net/if_arp.h> 62e1e1452dSArchie Cobbs #include <net/if_var.h> 63e1e1452dSArchie Cobbs #include <net/ethernet.h> 64e1e1452dSArchie Cobbs 65e1e1452dSArchie Cobbs #include <netgraph/ng_message.h> 66e1e1452dSArchie Cobbs #include <netgraph/netgraph.h> 67e1e1452dSArchie Cobbs #include <netgraph/ng_parse.h> 68e1e1452dSArchie Cobbs #include <netgraph/ng_ether.h> 69e1e1452dSArchie Cobbs 70fc74a9f9SBrooks Davis #define IFP2NG(ifp) ((struct ng_node *)IFP2AC((ifp))->ac_netgraph) 71fc74a9f9SBrooks Davis #define IFP2NG_SET(ifp, val) (IFP2AC((ifp))->ac_netgraph = (val)) 72e1e1452dSArchie Cobbs 733c976c3fSPawel Jakub Dawidek /* Per-node private data */ 743c976c3fSPawel Jakub Dawidek struct private { 753c976c3fSPawel Jakub Dawidek struct ifnet *ifp; /* associated interface */ 763c976c3fSPawel Jakub Dawidek hook_p upper; /* upper hook connection */ 771a292b80SArchie Cobbs hook_p lower; /* lower hook connection */ 781a292b80SArchie Cobbs hook_p orphan; /* orphan hook connection */ 793c976c3fSPawel Jakub Dawidek u_char autoSrcAddr; /* always overwrite source address */ 803c976c3fSPawel Jakub Dawidek u_char promisc; /* promiscuous mode enabled */ 813c976c3fSPawel Jakub Dawidek u_long hwassist; /* hardware checksum capabilities */ 823c976c3fSPawel Jakub Dawidek u_int flags; /* flags e.g. really die */ 833c976c3fSPawel Jakub Dawidek }; 843c976c3fSPawel Jakub Dawidek typedef struct private *priv_p; 85e1e1452dSArchie Cobbs 86edbb5246SSam Leffler /* Hook pointers used by if_ethersubr.c to callback to netgraph */ 87edbb5246SSam Leffler extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 88edbb5246SSam Leffler extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 89edbb5246SSam Leffler extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 90edbb5246SSam Leffler extern void (*ng_ether_attach_p)(struct ifnet *ifp); 91edbb5246SSam Leffler extern void (*ng_ether_detach_p)(struct ifnet *ifp); 921c7899c7SGleb Smirnoff extern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); 93edbb5246SSam Leffler 94e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */ 95edbb5246SSam Leffler static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 96edbb5246SSam Leffler static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 97e1e1452dSArchie Cobbs static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 98e1e1452dSArchie Cobbs static void ng_ether_attach(struct ifnet *ifp); 99e1e1452dSArchie Cobbs static void ng_ether_detach(struct ifnet *ifp); 1001c7899c7SGleb Smirnoff static void ng_ether_link_state(struct ifnet *ifp, int state); 101e1e1452dSArchie Cobbs 102e1e1452dSArchie Cobbs /* Other functions */ 1033ca24c28SJulian Elischer static int ng_ether_rcv_lower(node_p node, struct mbuf *m); 1043ca24c28SJulian Elischer static int ng_ether_rcv_upper(node_p node, struct mbuf *m); 105e1e1452dSArchie Cobbs 1066512768bSGleb Smirnoff /* if_bridge(4) support. XXX: should go into some include. */ 1076512768bSGleb Smirnoff extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); 1086512768bSGleb Smirnoff static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = 1096512768bSGleb Smirnoff { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1106512768bSGleb Smirnoff 111e1e1452dSArchie Cobbs /* Netgraph node methods */ 112e1e1452dSArchie Cobbs static ng_constructor_t ng_ether_constructor; 113e1e1452dSArchie Cobbs static ng_rcvmsg_t ng_ether_rcvmsg; 114069154d5SJulian Elischer static ng_shutdown_t ng_ether_shutdown; 115e1e1452dSArchie Cobbs static ng_newhook_t ng_ether_newhook; 116859a4d16SJulian Elischer static ng_connect_t ng_ether_connect; 117e1e1452dSArchie Cobbs static ng_rcvdata_t ng_ether_rcvdata; 118e1e1452dSArchie Cobbs static ng_disconnect_t ng_ether_disconnect; 119e1e1452dSArchie Cobbs static int ng_ether_mod_event(module_t mod, int event, void *data); 120e1e1452dSArchie Cobbs 121e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 122e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = { 123e1e1452dSArchie Cobbs { 124e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 125e1e1452dSArchie Cobbs NGM_ETHER_GET_IFNAME, 126e1e1452dSArchie Cobbs "getifname", 127e1e1452dSArchie Cobbs NULL, 128e1e1452dSArchie Cobbs &ng_parse_string_type 129e1e1452dSArchie Cobbs }, 130e1e1452dSArchie Cobbs { 131e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 132e1e1452dSArchie Cobbs NGM_ETHER_GET_IFINDEX, 133e1e1452dSArchie Cobbs "getifindex", 134e1e1452dSArchie Cobbs NULL, 135e1e1452dSArchie Cobbs &ng_parse_int32_type 136e1e1452dSArchie Cobbs }, 1374b39c3c7SArchie Cobbs { 1384b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 1394b39c3c7SArchie Cobbs NGM_ETHER_GET_ENADDR, 1404b39c3c7SArchie Cobbs "getenaddr", 1414b39c3c7SArchie Cobbs NULL, 1428c7e4101SRuslan Ermilov &ng_parse_enaddr_type 1434b39c3c7SArchie Cobbs }, 1444b39c3c7SArchie Cobbs { 1454b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 14656045c66SArchie Cobbs NGM_ETHER_SET_ENADDR, 14756045c66SArchie Cobbs "setenaddr", 1488c7e4101SRuslan Ermilov &ng_parse_enaddr_type, 14956045c66SArchie Cobbs NULL 15056045c66SArchie Cobbs }, 15156045c66SArchie Cobbs { 15256045c66SArchie Cobbs NGM_ETHER_COOKIE, 15356045c66SArchie Cobbs NGM_ETHER_GET_PROMISC, 15456045c66SArchie Cobbs "getpromisc", 15556045c66SArchie Cobbs NULL, 15656045c66SArchie Cobbs &ng_parse_int32_type 15756045c66SArchie Cobbs }, 15856045c66SArchie Cobbs { 15956045c66SArchie Cobbs NGM_ETHER_COOKIE, 1604b39c3c7SArchie Cobbs NGM_ETHER_SET_PROMISC, 1614b39c3c7SArchie Cobbs "setpromisc", 1624b39c3c7SArchie Cobbs &ng_parse_int32_type, 1634b39c3c7SArchie Cobbs NULL 1644b39c3c7SArchie Cobbs }, 1654b39c3c7SArchie Cobbs { 1664b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 16756045c66SArchie Cobbs NGM_ETHER_GET_AUTOSRC, 16856045c66SArchie Cobbs "getautosrc", 16956045c66SArchie Cobbs NULL, 17056045c66SArchie Cobbs &ng_parse_int32_type 17156045c66SArchie Cobbs }, 17256045c66SArchie Cobbs { 17356045c66SArchie Cobbs NGM_ETHER_COOKIE, 1744b39c3c7SArchie Cobbs NGM_ETHER_SET_AUTOSRC, 1754b39c3c7SArchie Cobbs "setautosrc", 1764b39c3c7SArchie Cobbs &ng_parse_int32_type, 1774b39c3c7SArchie Cobbs NULL 1784b39c3c7SArchie Cobbs }, 179810d5e89SGleb Smirnoff { 180810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 181810d5e89SGleb Smirnoff NGM_ETHER_ADD_MULTI, 182810d5e89SGleb Smirnoff "addmulti", 183810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 184810d5e89SGleb Smirnoff NULL 185810d5e89SGleb Smirnoff }, 186810d5e89SGleb Smirnoff { 187810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 188810d5e89SGleb Smirnoff NGM_ETHER_DEL_MULTI, 189810d5e89SGleb Smirnoff "delmulti", 190810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 191810d5e89SGleb Smirnoff NULL 192810d5e89SGleb Smirnoff }, 193cefddd66SGleb Smirnoff { 194cefddd66SGleb Smirnoff NGM_ETHER_COOKIE, 195cefddd66SGleb Smirnoff NGM_ETHER_DETACH, 196cefddd66SGleb Smirnoff "detach", 197cefddd66SGleb Smirnoff NULL, 198cefddd66SGleb Smirnoff NULL 199cefddd66SGleb Smirnoff }, 200e1e1452dSArchie Cobbs { 0 } 201e1e1452dSArchie Cobbs }; 202e1e1452dSArchie Cobbs 203e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = { 204f8aae777SJulian Elischer .version = NG_ABI_VERSION, 205f8aae777SJulian Elischer .name = NG_ETHER_NODE_TYPE, 206f8aae777SJulian Elischer .mod_event = ng_ether_mod_event, 207f8aae777SJulian Elischer .constructor = ng_ether_constructor, 208f8aae777SJulian Elischer .rcvmsg = ng_ether_rcvmsg, 209f8aae777SJulian Elischer .shutdown = ng_ether_shutdown, 210f8aae777SJulian Elischer .newhook = ng_ether_newhook, 211f8aae777SJulian Elischer .connect = ng_ether_connect, 212f8aae777SJulian Elischer .rcvdata = ng_ether_rcvdata, 213f8aae777SJulian Elischer .disconnect = ng_ether_disconnect, 214f8aae777SJulian Elischer .cmdlist = ng_ether_cmdlist, 215e1e1452dSArchie Cobbs }; 216e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct); 217e1e1452dSArchie Cobbs 218e1e1452dSArchie Cobbs /****************************************************************** 219e1e1452dSArchie Cobbs ETHERNET FUNCTION HOOKS 220e1e1452dSArchie Cobbs ******************************************************************/ 221e1e1452dSArchie Cobbs 222e1e1452dSArchie Cobbs /* 223e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface. We get to 224e1e1452dSArchie Cobbs * look at it here before any upper layer protocols do. 225e1e1452dSArchie Cobbs * 226e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 227e1e1452dSArchie Cobbs */ 228e1e1452dSArchie Cobbs static void 229edbb5246SSam Leffler ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 230e1e1452dSArchie Cobbs { 231e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 23230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2331a292b80SArchie Cobbs int error; 234e1e1452dSArchie Cobbs 235e1e1452dSArchie Cobbs /* If "lower" hook not connected, let packet continue */ 2361a292b80SArchie Cobbs if (priv->lower == NULL) 237e1e1452dSArchie Cobbs return; 2381a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 239e1e1452dSArchie Cobbs } 240e1e1452dSArchie Cobbs 241e1e1452dSArchie Cobbs /* 242e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface, and which 243e1e1452dSArchie Cobbs * does not match any of our known protocols (an ``orphan''). 244e1e1452dSArchie Cobbs * 245e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 246e1e1452dSArchie Cobbs */ 247e1e1452dSArchie Cobbs static void 248edbb5246SSam Leffler ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 249e1e1452dSArchie Cobbs { 250e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 25130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2521a292b80SArchie Cobbs int error; 253e1e1452dSArchie Cobbs 2541a292b80SArchie Cobbs /* If "orphan" hook not connected, discard packet */ 2551a292b80SArchie Cobbs if (priv->orphan == NULL) { 256e1e1452dSArchie Cobbs m_freem(m); 257e1e1452dSArchie Cobbs return; 258e1e1452dSArchie Cobbs } 2591a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->orphan, m); 260e1e1452dSArchie Cobbs } 261e1e1452dSArchie Cobbs 262e1e1452dSArchie Cobbs /* 263e1e1452dSArchie Cobbs * Handle a packet that is going out on an interface. 264e1e1452dSArchie Cobbs * The Ethernet header is already attached to the mbuf. 265e1e1452dSArchie Cobbs */ 266e1e1452dSArchie Cobbs static int 267e1e1452dSArchie Cobbs ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 268e1e1452dSArchie Cobbs { 269e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 27030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 271e1e1452dSArchie Cobbs int error = 0; 272e1e1452dSArchie Cobbs 273e1e1452dSArchie Cobbs /* If "upper" hook not connected, let packet continue */ 274e1e1452dSArchie Cobbs if (priv->upper == NULL) 275e1e1452dSArchie Cobbs return (0); 276e1e1452dSArchie Cobbs 277e1e1452dSArchie Cobbs /* Send it out "upper" hook */ 278069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->upper, *mp); 279e1e1452dSArchie Cobbs return (error); 280e1e1452dSArchie Cobbs } 281e1e1452dSArchie Cobbs 282e1e1452dSArchie Cobbs /* 283e1e1452dSArchie Cobbs * A new Ethernet interface has been attached. 284e1e1452dSArchie Cobbs * Create a new node for it, etc. 285e1e1452dSArchie Cobbs */ 286e1e1452dSArchie Cobbs static void 287e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp) 288e1e1452dSArchie Cobbs { 289e1e1452dSArchie Cobbs priv_p priv; 290e1e1452dSArchie Cobbs node_p node; 291e1e1452dSArchie Cobbs 292e1e1452dSArchie Cobbs /* Create node */ 2936e551fb6SDavid E. O'Brien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 294e1e1452dSArchie Cobbs if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 295e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 2969bf40edeSBrooks Davis __func__, "create node", ifp->if_xname); 297e1e1452dSArchie Cobbs return; 298e1e1452dSArchie Cobbs } 299e1e1452dSArchie Cobbs 300e1e1452dSArchie Cobbs /* Allocate private data */ 30199cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 302e1e1452dSArchie Cobbs if (priv == NULL) { 303e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3049bf40edeSBrooks Davis __func__, "allocate memory", ifp->if_xname); 30530400f03SJulian Elischer NG_NODE_UNREF(node); 306e1e1452dSArchie Cobbs return; 307e1e1452dSArchie Cobbs } 30830400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 309e1e1452dSArchie Cobbs priv->ifp = ifp; 310445e045bSAlexander Kabaev IFP2NG_SET(ifp, node); 3114b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; 312a3e232d6SArchie Cobbs priv->hwassist = ifp->if_hwassist; 313e1e1452dSArchie Cobbs 314e1e1452dSArchie Cobbs /* Try to give the node the same name as the interface */ 3159bf40edeSBrooks Davis if (ng_name_node(node, ifp->if_xname) != 0) { 316e1e1452dSArchie Cobbs log(LOG_WARNING, "%s: can't name node %s\n", 3179bf40edeSBrooks Davis __func__, ifp->if_xname); 318e1e1452dSArchie Cobbs } 319e1e1452dSArchie Cobbs } 320e1e1452dSArchie Cobbs 321e1e1452dSArchie Cobbs /* 322e1e1452dSArchie Cobbs * An Ethernet interface is being detached. 3231acb27c6SJulian Elischer * REALLY Destroy its node. 324e1e1452dSArchie Cobbs */ 325e1e1452dSArchie Cobbs static void 326e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp) 327e1e1452dSArchie Cobbs { 328e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 3291acb27c6SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 330e1e1452dSArchie Cobbs 3311acb27c6SJulian Elischer NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 3321acb27c6SJulian Elischer /* 3331acb27c6SJulian Elischer * We can't assume the ifnet is still around when we run shutdown 3341acb27c6SJulian Elischer * So zap it now. XXX We HOPE that anything running at this time 3351acb27c6SJulian Elischer * handles it (as it should in the non netgraph case). 3361acb27c6SJulian Elischer */ 337445e045bSAlexander Kabaev IFP2NG_SET(ifp, NULL); 3381acb27c6SJulian Elischer priv->ifp = NULL; /* XXX race if interrupted an output packet */ 3391acb27c6SJulian Elischer ng_rmnode_self(node); /* remove all netgraph parts */ 340e1e1452dSArchie Cobbs } 341e1e1452dSArchie Cobbs 3421c7899c7SGleb Smirnoff /* 3431c7899c7SGleb Smirnoff * Notify graph about link event. 3441c7899c7SGleb Smirnoff * if_link_state_change() has already checked that the state has changed. 3451c7899c7SGleb Smirnoff */ 3461c7899c7SGleb Smirnoff static void 3471c7899c7SGleb Smirnoff ng_ether_link_state(struct ifnet *ifp, int state) 3481c7899c7SGleb Smirnoff { 3491c7899c7SGleb Smirnoff const node_p node = IFP2NG(ifp); 3501c7899c7SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 3511c7899c7SGleb Smirnoff struct ng_mesg *msg; 3521c7899c7SGleb Smirnoff int cmd, dummy_error = 0; 3531c7899c7SGleb Smirnoff 3541c7899c7SGleb Smirnoff if (priv->lower == NULL) 3551c7899c7SGleb Smirnoff return; 3561c7899c7SGleb Smirnoff 3571c7899c7SGleb Smirnoff if (state == LINK_STATE_UP) 3581c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_UP; 3591c7899c7SGleb Smirnoff else if (state == LINK_STATE_DOWN) 3601c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_DOWN; 3611c7899c7SGleb Smirnoff else 3621c7899c7SGleb Smirnoff return; 3631c7899c7SGleb Smirnoff 3641c7899c7SGleb Smirnoff NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 3651c7899c7SGleb Smirnoff if (msg != NULL) 3661c7899c7SGleb Smirnoff NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 3671c7899c7SGleb Smirnoff } 3681c7899c7SGleb Smirnoff 369e1e1452dSArchie Cobbs /****************************************************************** 370e1e1452dSArchie Cobbs NETGRAPH NODE METHODS 371e1e1452dSArchie Cobbs ******************************************************************/ 372e1e1452dSArchie Cobbs 373e1e1452dSArchie Cobbs /* 374e1e1452dSArchie Cobbs * It is not possible or allowable to create a node of this type. 375e1e1452dSArchie Cobbs * Nodes get created when the interface is attached (or, when 376e1e1452dSArchie Cobbs * this node type's KLD is loaded). 377e1e1452dSArchie Cobbs */ 378e1e1452dSArchie Cobbs static int 379069154d5SJulian Elischer ng_ether_constructor(node_p node) 380e1e1452dSArchie Cobbs { 381e1e1452dSArchie Cobbs return (EINVAL); 382e1e1452dSArchie Cobbs } 383e1e1452dSArchie Cobbs 384e1e1452dSArchie Cobbs /* 385e1e1452dSArchie Cobbs * Check for attaching a new hook. 386e1e1452dSArchie Cobbs */ 387e1e1452dSArchie Cobbs static int 388e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name) 389e1e1452dSArchie Cobbs { 39030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 391e1e1452dSArchie Cobbs hook_p *hookptr; 392e1e1452dSArchie Cobbs 393e1e1452dSArchie Cobbs /* Divert hook is an alias for lower */ 394e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 395e1e1452dSArchie Cobbs name = NG_ETHER_HOOK_LOWER; 396e1e1452dSArchie Cobbs 397e1e1452dSArchie Cobbs /* Which hook? */ 398e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 399e1e1452dSArchie Cobbs hookptr = &priv->upper; 4001a292b80SArchie Cobbs else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) 401e1e1452dSArchie Cobbs hookptr = &priv->lower; 4021a292b80SArchie Cobbs else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) 4031a292b80SArchie Cobbs hookptr = &priv->orphan; 4041a292b80SArchie Cobbs else 405e1e1452dSArchie Cobbs return (EINVAL); 406e1e1452dSArchie Cobbs 407e1e1452dSArchie Cobbs /* Check if already connected (shouldn't be, but doesn't hurt) */ 408e1e1452dSArchie Cobbs if (*hookptr != NULL) 409e1e1452dSArchie Cobbs return (EISCONN); 410e1e1452dSArchie Cobbs 411a3e232d6SArchie Cobbs /* Disable hardware checksums while 'upper' hook is connected */ 412a3e232d6SArchie Cobbs if (hookptr == &priv->upper) 413a3e232d6SArchie Cobbs priv->ifp->if_hwassist = 0; 414a3e232d6SArchie Cobbs 415e1e1452dSArchie Cobbs /* OK */ 416e1e1452dSArchie Cobbs *hookptr = hook; 417e1e1452dSArchie Cobbs return (0); 418e1e1452dSArchie Cobbs } 419e1e1452dSArchie Cobbs 420e1e1452dSArchie Cobbs /* 421859a4d16SJulian Elischer * Hooks are attached, adjust to force queueing. 422859a4d16SJulian Elischer * We don't really care which hook it is. 423859a4d16SJulian Elischer * they should all be queuing for outgoing data. 424859a4d16SJulian Elischer */ 425859a4d16SJulian Elischer static int 426859a4d16SJulian Elischer ng_ether_connect(hook_p hook) 427859a4d16SJulian Elischer { 42830400f03SJulian Elischer NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 429859a4d16SJulian Elischer return (0); 430859a4d16SJulian Elischer } 431859a4d16SJulian Elischer 432859a4d16SJulian Elischer /* 433e1e1452dSArchie Cobbs * Receive an incoming control message. 434e1e1452dSArchie Cobbs */ 435e1e1452dSArchie Cobbs static int 436069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 437e1e1452dSArchie Cobbs { 43830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 439e1e1452dSArchie Cobbs struct ng_mesg *resp = NULL; 440e1e1452dSArchie Cobbs int error = 0; 441069154d5SJulian Elischer struct ng_mesg *msg; 442e1e1452dSArchie Cobbs 443069154d5SJulian Elischer NGI_GET_MSG(item, msg); 444e1e1452dSArchie Cobbs switch (msg->header.typecookie) { 445e1e1452dSArchie Cobbs case NGM_ETHER_COOKIE: 446e1e1452dSArchie Cobbs switch (msg->header.cmd) { 447e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFNAME: 448bbb75d78SRuslan Ermilov NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 449e1e1452dSArchie Cobbs if (resp == NULL) { 450e1e1452dSArchie Cobbs error = ENOMEM; 451e1e1452dSArchie Cobbs break; 452e1e1452dSArchie Cobbs } 453bbb75d78SRuslan Ermilov strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 454e1e1452dSArchie Cobbs break; 455e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFINDEX: 456e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 457e1e1452dSArchie Cobbs if (resp == NULL) { 458e1e1452dSArchie Cobbs error = ENOMEM; 459e1e1452dSArchie Cobbs break; 460e1e1452dSArchie Cobbs } 461e1e1452dSArchie Cobbs *((u_int32_t *)resp->data) = priv->ifp->if_index; 462e1e1452dSArchie Cobbs break; 4634b39c3c7SArchie Cobbs case NGM_ETHER_GET_ENADDR: 4644b39c3c7SArchie Cobbs NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 4654b39c3c7SArchie Cobbs if (resp == NULL) { 4664b39c3c7SArchie Cobbs error = ENOMEM; 4674b39c3c7SArchie Cobbs break; 4684b39c3c7SArchie Cobbs } 469fc74a9f9SBrooks Davis bcopy(IFP2ENADDR(priv->ifp), 4704b39c3c7SArchie Cobbs resp->data, ETHER_ADDR_LEN); 4714b39c3c7SArchie Cobbs break; 47256045c66SArchie Cobbs case NGM_ETHER_SET_ENADDR: 47356045c66SArchie Cobbs { 47456045c66SArchie Cobbs if (msg->header.arglen != ETHER_ADDR_LEN) { 47556045c66SArchie Cobbs error = EINVAL; 47656045c66SArchie Cobbs break; 47756045c66SArchie Cobbs } 47856045c66SArchie Cobbs error = if_setlladdr(priv->ifp, 47956045c66SArchie Cobbs (u_char *)msg->data, ETHER_ADDR_LEN); 48056045c66SArchie Cobbs break; 48156045c66SArchie Cobbs } 48256045c66SArchie Cobbs case NGM_ETHER_GET_PROMISC: 48356045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 48456045c66SArchie Cobbs if (resp == NULL) { 48556045c66SArchie Cobbs error = ENOMEM; 48656045c66SArchie Cobbs break; 48756045c66SArchie Cobbs } 48856045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->promisc; 48956045c66SArchie Cobbs break; 4904b39c3c7SArchie Cobbs case NGM_ETHER_SET_PROMISC: 4914b39c3c7SArchie Cobbs { 4924b39c3c7SArchie Cobbs u_char want; 4934b39c3c7SArchie Cobbs 4944b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 4954b39c3c7SArchie Cobbs error = EINVAL; 4964b39c3c7SArchie Cobbs break; 4974b39c3c7SArchie Cobbs } 4984b39c3c7SArchie Cobbs want = !!*((u_int32_t *)msg->data); 4994b39c3c7SArchie Cobbs if (want ^ priv->promisc) { 5004b39c3c7SArchie Cobbs if ((error = ifpromisc(priv->ifp, want)) != 0) 5014b39c3c7SArchie Cobbs break; 5024b39c3c7SArchie Cobbs priv->promisc = want; 5034b39c3c7SArchie Cobbs } 5044b39c3c7SArchie Cobbs break; 5054b39c3c7SArchie Cobbs } 50656045c66SArchie Cobbs case NGM_ETHER_GET_AUTOSRC: 50756045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 50856045c66SArchie Cobbs if (resp == NULL) { 50956045c66SArchie Cobbs error = ENOMEM; 51056045c66SArchie Cobbs break; 51156045c66SArchie Cobbs } 51256045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->autoSrcAddr; 51356045c66SArchie Cobbs break; 5144b39c3c7SArchie Cobbs case NGM_ETHER_SET_AUTOSRC: 5154b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5164b39c3c7SArchie Cobbs error = EINVAL; 5174b39c3c7SArchie Cobbs break; 5184b39c3c7SArchie Cobbs } 5194b39c3c7SArchie Cobbs priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 5204b39c3c7SArchie Cobbs break; 521810d5e89SGleb Smirnoff case NGM_ETHER_ADD_MULTI: 522810d5e89SGleb Smirnoff { 523810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 524810d5e89SGleb Smirnoff struct ifmultiaddr *ifm; 525810d5e89SGleb Smirnoff 526810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 527810d5e89SGleb Smirnoff error = EINVAL; 528810d5e89SGleb Smirnoff break; 529810d5e89SGleb Smirnoff } 530ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 531810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 532810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 533ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 534810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 535810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 536810d5e89SGleb Smirnoff error = if_addmulti(priv->ifp, 537810d5e89SGleb Smirnoff (struct sockaddr *)&sa_dl, &ifm); 538810d5e89SGleb Smirnoff break; 539810d5e89SGleb Smirnoff } 540810d5e89SGleb Smirnoff case NGM_ETHER_DEL_MULTI: 541810d5e89SGleb Smirnoff { 542810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 543810d5e89SGleb Smirnoff 544810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 545810d5e89SGleb Smirnoff error = EINVAL; 546810d5e89SGleb Smirnoff break; 547810d5e89SGleb Smirnoff } 548ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 549810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 550810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 551ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 552810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 553810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 554810d5e89SGleb Smirnoff error = if_delmulti(priv->ifp, 555810d5e89SGleb Smirnoff (struct sockaddr *)&sa_dl); 556810d5e89SGleb Smirnoff break; 557810d5e89SGleb Smirnoff } 558cefddd66SGleb Smirnoff case NGM_ETHER_DETACH: 559cefddd66SGleb Smirnoff ng_ether_detach(priv->ifp); 560cefddd66SGleb Smirnoff break; 561e1e1452dSArchie Cobbs default: 562e1e1452dSArchie Cobbs error = EINVAL; 563e1e1452dSArchie Cobbs break; 564e1e1452dSArchie Cobbs } 565e1e1452dSArchie Cobbs break; 566e1e1452dSArchie Cobbs default: 567e1e1452dSArchie Cobbs error = EINVAL; 568e1e1452dSArchie Cobbs break; 569e1e1452dSArchie Cobbs } 570069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 571069154d5SJulian Elischer NG_FREE_MSG(msg); 572e1e1452dSArchie Cobbs return (error); 573e1e1452dSArchie Cobbs } 574e1e1452dSArchie Cobbs 575e1e1452dSArchie Cobbs /* 576e1e1452dSArchie Cobbs * Receive data on a hook. 577e1e1452dSArchie Cobbs */ 578e1e1452dSArchie Cobbs static int 579069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item) 580e1e1452dSArchie Cobbs { 58130400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 58230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 583069154d5SJulian Elischer struct mbuf *m; 584e1e1452dSArchie Cobbs 585069154d5SJulian Elischer NGI_GET_M(item, m); 586069154d5SJulian Elischer NG_FREE_ITEM(item); 5873ca24c28SJulian Elischer 5881a292b80SArchie Cobbs if (hook == priv->lower || hook == priv->orphan) 5893ca24c28SJulian Elischer return ng_ether_rcv_lower(node, m); 590e1e1452dSArchie Cobbs if (hook == priv->upper) 5913ca24c28SJulian Elischer return ng_ether_rcv_upper(node, m); 5926e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 5931a292b80SArchie Cobbs #ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ 5947ea5573cSDag-Erling Smørgrav return (0); 595b40ce416SJulian Elischer #endif 596e1e1452dSArchie Cobbs } 597e1e1452dSArchie Cobbs 598e1e1452dSArchie Cobbs /* 5991a292b80SArchie Cobbs * Handle an mbuf received on the "lower" or "orphan" hook. 600e1e1452dSArchie Cobbs */ 601e1e1452dSArchie Cobbs static int 6023ca24c28SJulian Elischer ng_ether_rcv_lower(node_p node, struct mbuf *m) 603e1e1452dSArchie Cobbs { 60430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 605a1479aa2SArchie Cobbs struct ifnet *const ifp = priv->ifp; 606a1479aa2SArchie Cobbs 607a1479aa2SArchie Cobbs /* Check whether interface is ready for packets */ 60813f4c340SRobert Watson if (!((ifp->if_flags & IFF_UP) && 60913f4c340SRobert Watson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 610a1479aa2SArchie Cobbs NG_FREE_M(m); 611a1479aa2SArchie Cobbs return (ENETDOWN); 612a1479aa2SArchie Cobbs } 613e1e1452dSArchie Cobbs 614e1e1452dSArchie Cobbs /* Make sure header is fully pulled up */ 615e1e1452dSArchie Cobbs if (m->m_pkthdr.len < sizeof(struct ether_header)) { 616069154d5SJulian Elischer NG_FREE_M(m); 617e1e1452dSArchie Cobbs return (EINVAL); 618e1e1452dSArchie Cobbs } 619e1e1452dSArchie Cobbs if (m->m_len < sizeof(struct ether_header) 6207b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 621e1e1452dSArchie Cobbs return (ENOBUFS); 622e1e1452dSArchie Cobbs 6234b39c3c7SArchie Cobbs /* Drop in the MAC address if desired */ 6244b39c3c7SArchie Cobbs if (priv->autoSrcAddr) { 6257b9f235fSArchie Cobbs 6267b9f235fSArchie Cobbs /* Make the mbuf writable if it's not already */ 6277b9f235fSArchie Cobbs if (!M_WRITABLE(m) 6287b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 6297b9f235fSArchie Cobbs return (ENOBUFS); 6307b9f235fSArchie Cobbs 6317b9f235fSArchie Cobbs /* Overwrite source MAC address */ 632fc74a9f9SBrooks Davis bcopy(IFP2ENADDR(ifp), 6334b39c3c7SArchie Cobbs mtod(m, struct ether_header *)->ether_shost, 6344b39c3c7SArchie Cobbs ETHER_ADDR_LEN); 6354b39c3c7SArchie Cobbs } 636561e4fb9SJulian Elischer 637e1e1452dSArchie Cobbs /* Send it on its way */ 638a1479aa2SArchie Cobbs return ether_output_frame(ifp, m); 639e1e1452dSArchie Cobbs } 640e1e1452dSArchie Cobbs 641e1e1452dSArchie Cobbs /* 642e1e1452dSArchie Cobbs * Handle an mbuf received on the "upper" hook. 643e1e1452dSArchie Cobbs */ 644e1e1452dSArchie Cobbs static int 6453ca24c28SJulian Elischer ng_ether_rcv_upper(node_p node, struct mbuf *m) 646e1e1452dSArchie Cobbs { 64730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 6486512768bSGleb Smirnoff struct ifnet *ifp = priv->ifp; 649e1e1452dSArchie Cobbs 6506512768bSGleb Smirnoff m->m_pkthdr.rcvif = ifp; 651e1e1452dSArchie Cobbs 6526512768bSGleb Smirnoff /* 6536512768bSGleb Smirnoff * XXX: This is a copy'and'paste from if_ethersubr.c:ether_input() 6546512768bSGleb Smirnoff */ 6556512768bSGleb Smirnoff if (ifp->if_bridge) { 6566512768bSGleb Smirnoff struct ether_header *eh; 6576512768bSGleb Smirnoff 6586512768bSGleb Smirnoff KASSERT(bridge_input_p != NULL, 6596512768bSGleb Smirnoff ("%s: if_bridge not loaded!", __func__)); 6606512768bSGleb Smirnoff 6616512768bSGleb Smirnoff eh = mtod(m, struct ether_header *); 6626512768bSGleb Smirnoff 6636512768bSGleb Smirnoff /* Mark the packet as broadcast or multicast. This is also set 6646512768bSGleb Smirnoff * further down the code in ether_demux() but since the bridge 6656512768bSGleb Smirnoff * input routine rarely returns a mbuf for further processing, 6666512768bSGleb Smirnoff * it is an acceptable duplication. 6676512768bSGleb Smirnoff */ 6686512768bSGleb Smirnoff if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 6696512768bSGleb Smirnoff if (bcmp(etherbroadcastaddr, eh->ether_dhost, 6706512768bSGleb Smirnoff sizeof(etherbroadcastaddr)) == 0) 6716512768bSGleb Smirnoff m->m_flags |= M_BCAST; 6726512768bSGleb Smirnoff else 6736512768bSGleb Smirnoff m->m_flags |= M_MCAST; 6746512768bSGleb Smirnoff } 6756512768bSGleb Smirnoff 6766512768bSGleb Smirnoff m = (*bridge_input_p)(ifp, m); 6776512768bSGleb Smirnoff if (m == NULL) 6786512768bSGleb Smirnoff return (0); 6796512768bSGleb Smirnoff /* 6806512768bSGleb Smirnoff * Bridge has determined that the packet is for us. 6816512768bSGleb Smirnoff * Update our interface pointer -- we may have had 6826512768bSGleb Smirnoff * to "bridge" the packet locally. 6836512768bSGleb Smirnoff */ 6846512768bSGleb Smirnoff ifp = m->m_pkthdr.rcvif; 6856512768bSGleb Smirnoff } 686a176c2aeSGleb Smirnoff 687e1e1452dSArchie Cobbs /* Route packet back in */ 688edbb5246SSam Leffler ether_demux(priv->ifp, m); 689e1e1452dSArchie Cobbs return (0); 690e1e1452dSArchie Cobbs } 691e1e1452dSArchie Cobbs 692e1e1452dSArchie Cobbs /* 6931acb27c6SJulian Elischer * Shutdown node. This resets the node but does not remove it 6941acb27c6SJulian Elischer * unless the REALLY_DIE flag is set. 695e1e1452dSArchie Cobbs */ 696e1e1452dSArchie Cobbs static int 697069154d5SJulian Elischer ng_ether_shutdown(node_p node) 698e1e1452dSArchie Cobbs { 69930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 7004b39c3c7SArchie Cobbs 701be4252b3SJulian Elischer if (node->nd_flags & NGF_REALLY_DIE) { 7021acb27c6SJulian Elischer /* 7031acb27c6SJulian Elischer * WE came here because the ethernet card is being unloaded, 7041acb27c6SJulian Elischer * so stop being persistant. 7051acb27c6SJulian Elischer * Actually undo all the things we did on creation. 7061acb27c6SJulian Elischer * Assume the ifp has already been freed. 7071acb27c6SJulian Elischer */ 7081acb27c6SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 7091acb27c6SJulian Elischer FREE(priv, M_NETGRAPH); 7101acb27c6SJulian Elischer NG_NODE_UNREF(node); /* free node itself */ 7111acb27c6SJulian Elischer return (0); 712069154d5SJulian Elischer } 713018df1c3SBrian Feldman if (priv->promisc) { /* disable promiscuous mode */ 714018df1c3SBrian Feldman (void)ifpromisc(priv->ifp, 0); 715018df1c3SBrian Feldman priv->promisc = 0; 716018df1c3SBrian Feldman } 7174b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 718be4252b3SJulian Elischer NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 719be4252b3SJulian Elischer 720e1e1452dSArchie Cobbs return (0); 721e1e1452dSArchie Cobbs } 722e1e1452dSArchie Cobbs 723e1e1452dSArchie Cobbs /* 724e1e1452dSArchie Cobbs * Hook disconnection. 725e1e1452dSArchie Cobbs */ 726e1e1452dSArchie Cobbs static int 727e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook) 728e1e1452dSArchie Cobbs { 72930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 730e1e1452dSArchie Cobbs 731a3e232d6SArchie Cobbs if (hook == priv->upper) { 732e1e1452dSArchie Cobbs priv->upper = NULL; 733b712e9ecSBrian Feldman if (priv->ifp != NULL) /* restore h/w csum */ 734b712e9ecSBrian Feldman priv->ifp->if_hwassist = priv->hwassist; 7351a292b80SArchie Cobbs } else if (hook == priv->lower) 736e1e1452dSArchie Cobbs priv->lower = NULL; 7371a292b80SArchie Cobbs else if (hook == priv->orphan) 7381a292b80SArchie Cobbs priv->orphan = NULL; 7391a292b80SArchie Cobbs else 7406e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 74130400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 74230400f03SJulian Elischer && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 74330400f03SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 744e1e1452dSArchie Cobbs return (0); 745e1e1452dSArchie Cobbs } 746e1e1452dSArchie Cobbs 747e1e1452dSArchie Cobbs /****************************************************************** 748e1e1452dSArchie Cobbs INITIALIZATION 749e1e1452dSArchie Cobbs ******************************************************************/ 750e1e1452dSArchie Cobbs 751e1e1452dSArchie Cobbs /* 752e1e1452dSArchie Cobbs * Handle loading and unloading for this node type. 753e1e1452dSArchie Cobbs */ 754e1e1452dSArchie Cobbs static int 755e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data) 756e1e1452dSArchie Cobbs { 757e1e1452dSArchie Cobbs struct ifnet *ifp; 758e1e1452dSArchie Cobbs int error = 0; 759e1e1452dSArchie Cobbs int s; 760e1e1452dSArchie Cobbs 761e1e1452dSArchie Cobbs s = splnet(); 762e1e1452dSArchie Cobbs switch (event) { 763e1e1452dSArchie Cobbs case MOD_LOAD: 764e1e1452dSArchie Cobbs 765e1e1452dSArchie Cobbs /* Register function hooks */ 766e1e1452dSArchie Cobbs if (ng_ether_attach_p != NULL) { 767e1e1452dSArchie Cobbs error = EEXIST; 768e1e1452dSArchie Cobbs break; 769e1e1452dSArchie Cobbs } 770e1e1452dSArchie Cobbs ng_ether_attach_p = ng_ether_attach; 771e1e1452dSArchie Cobbs ng_ether_detach_p = ng_ether_detach; 772e1e1452dSArchie Cobbs ng_ether_output_p = ng_ether_output; 773e1e1452dSArchie Cobbs ng_ether_input_p = ng_ether_input; 774e1e1452dSArchie Cobbs ng_ether_input_orphan_p = ng_ether_input_orphan; 7751c7899c7SGleb Smirnoff ng_ether_link_state_p = ng_ether_link_state; 776e1e1452dSArchie Cobbs 777e1e1452dSArchie Cobbs /* Create nodes for any already-existing Ethernet interfaces */ 778b30a244cSJeffrey Hsu IFNET_RLOCK(); 779e1e1452dSArchie Cobbs TAILQ_FOREACH(ifp, &ifnet, if_link) { 780cf2010b8SArchie Cobbs if (ifp->if_type == IFT_ETHER 781cf2010b8SArchie Cobbs || ifp->if_type == IFT_L2VLAN) 782e1e1452dSArchie Cobbs ng_ether_attach(ifp); 783e1e1452dSArchie Cobbs } 784b30a244cSJeffrey Hsu IFNET_RUNLOCK(); 785e1e1452dSArchie Cobbs break; 786e1e1452dSArchie Cobbs 787e1e1452dSArchie Cobbs case MOD_UNLOAD: 788e1e1452dSArchie Cobbs 789e1e1452dSArchie Cobbs /* 790e1e1452dSArchie Cobbs * Note that the base code won't try to unload us until 791e1e1452dSArchie Cobbs * all nodes have been removed, and that can't happen 792e1e1452dSArchie Cobbs * until all Ethernet interfaces are removed. In any 793e1e1452dSArchie Cobbs * case, we know there are no nodes left if the action 794e1e1452dSArchie Cobbs * is MOD_UNLOAD, so there's no need to detach any nodes. 795e1e1452dSArchie Cobbs */ 796e1e1452dSArchie Cobbs 797e1e1452dSArchie Cobbs /* Unregister function hooks */ 798e1e1452dSArchie Cobbs ng_ether_attach_p = NULL; 799e1e1452dSArchie Cobbs ng_ether_detach_p = NULL; 800e1e1452dSArchie Cobbs ng_ether_output_p = NULL; 801e1e1452dSArchie Cobbs ng_ether_input_p = NULL; 802e1e1452dSArchie Cobbs ng_ether_input_orphan_p = NULL; 8031c7899c7SGleb Smirnoff ng_ether_link_state_p = NULL; 804e1e1452dSArchie Cobbs break; 805e1e1452dSArchie Cobbs 806e1e1452dSArchie Cobbs default: 807e1e1452dSArchie Cobbs error = EOPNOTSUPP; 808e1e1452dSArchie Cobbs break; 809e1e1452dSArchie Cobbs } 810e1e1452dSArchie Cobbs splx(s); 811e1e1452dSArchie Cobbs return (error); 812e1e1452dSArchie Cobbs } 813e1e1452dSArchie Cobbs 814