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> 57603724d3SBjoern A. Zeeb #include <sys/vimage.h> 58e1e1452dSArchie Cobbs 59e1e1452dSArchie Cobbs #include <net/if.h> 60810d5e89SGleb Smirnoff #include <net/if_dl.h> 61e1e1452dSArchie Cobbs #include <net/if_types.h> 62e1e1452dSArchie Cobbs #include <net/if_arp.h> 63e1e1452dSArchie Cobbs #include <net/if_var.h> 64e1e1452dSArchie Cobbs #include <net/ethernet.h> 65fd6238a6SAndrew Thompson #include <net/if_bridgevar.h> 6633553d6eSBjoern A. Zeeb #include <net/route.h> 674b79449eSBjoern A. Zeeb #include <net/vnet.h> 68e1e1452dSArchie Cobbs 69e1e1452dSArchie Cobbs #include <netgraph/ng_message.h> 70e1e1452dSArchie Cobbs #include <netgraph/netgraph.h> 71e1e1452dSArchie Cobbs #include <netgraph/ng_parse.h> 72e1e1452dSArchie Cobbs #include <netgraph/ng_ether.h> 73e1e1452dSArchie Cobbs 745240dcdbSRuslan Ermilov #define IFP2NG(ifp) (IFP2AC((ifp))->ac_netgraph) 75e1e1452dSArchie Cobbs 76aef8f344SMarko Zec static vnet_attach_fn ng_ether_iattach; 77aef8f344SMarko Zec 78aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS 79aef8f344SMarko Zec static vnet_modinfo_t vnet_ng_ether_modinfo = { 80aef8f344SMarko Zec .vmi_id = VNET_MOD_NG_ETHER, 81aef8f344SMarko Zec .vmi_name = "ng_ether", 82aef8f344SMarko Zec .vmi_dependson = VNET_MOD_NETGRAPH, 83aef8f344SMarko Zec .vmi_iattach = ng_ether_iattach, 84aef8f344SMarko Zec }; 85aef8f344SMarko Zec #endif 86aef8f344SMarko Zec 873c976c3fSPawel Jakub Dawidek /* Per-node private data */ 883c976c3fSPawel Jakub Dawidek struct private { 893c976c3fSPawel Jakub Dawidek struct ifnet *ifp; /* associated interface */ 903c976c3fSPawel Jakub Dawidek hook_p upper; /* upper hook connection */ 911a292b80SArchie Cobbs hook_p lower; /* lower hook connection */ 921a292b80SArchie Cobbs hook_p orphan; /* orphan hook connection */ 933c976c3fSPawel Jakub Dawidek u_char autoSrcAddr; /* always overwrite source address */ 943c976c3fSPawel Jakub Dawidek u_char promisc; /* promiscuous mode enabled */ 953c976c3fSPawel Jakub Dawidek u_long hwassist; /* hardware checksum capabilities */ 963c976c3fSPawel Jakub Dawidek u_int flags; /* flags e.g. really die */ 973c976c3fSPawel Jakub Dawidek }; 983c976c3fSPawel Jakub Dawidek typedef struct private *priv_p; 99e1e1452dSArchie Cobbs 100edbb5246SSam Leffler /* Hook pointers used by if_ethersubr.c to callback to netgraph */ 101edbb5246SSam Leffler extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 102edbb5246SSam Leffler extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 103edbb5246SSam Leffler extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 104edbb5246SSam Leffler extern void (*ng_ether_attach_p)(struct ifnet *ifp); 105edbb5246SSam Leffler extern void (*ng_ether_detach_p)(struct ifnet *ifp); 1061c7899c7SGleb Smirnoff extern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); 107edbb5246SSam Leffler 108e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */ 109edbb5246SSam Leffler static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 110edbb5246SSam Leffler static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 111e1e1452dSArchie Cobbs static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 112e1e1452dSArchie Cobbs static void ng_ether_attach(struct ifnet *ifp); 113e1e1452dSArchie Cobbs static void ng_ether_detach(struct ifnet *ifp); 1141c7899c7SGleb Smirnoff static void ng_ether_link_state(struct ifnet *ifp, int state); 115e1e1452dSArchie Cobbs 116e1e1452dSArchie Cobbs /* Other functions */ 117f664dcdeSJulian Elischer static int ng_ether_rcv_lower(hook_p node, item_p item); 118f664dcdeSJulian Elischer static int ng_ether_rcv_upper(hook_p node, item_p item); 119e1e1452dSArchie Cobbs 120e1e1452dSArchie Cobbs /* Netgraph node methods */ 121e1e1452dSArchie Cobbs static ng_constructor_t ng_ether_constructor; 122e1e1452dSArchie Cobbs static ng_rcvmsg_t ng_ether_rcvmsg; 123069154d5SJulian Elischer static ng_shutdown_t ng_ether_shutdown; 124e1e1452dSArchie Cobbs static ng_newhook_t ng_ether_newhook; 125e1e1452dSArchie Cobbs static ng_rcvdata_t ng_ether_rcvdata; 126e1e1452dSArchie Cobbs static ng_disconnect_t ng_ether_disconnect; 127e1e1452dSArchie Cobbs static int ng_ether_mod_event(module_t mod, int event, void *data); 128e1e1452dSArchie Cobbs 129e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 130e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = { 131e1e1452dSArchie Cobbs { 132e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 133e1e1452dSArchie Cobbs NGM_ETHER_GET_IFNAME, 134e1e1452dSArchie Cobbs "getifname", 135e1e1452dSArchie Cobbs NULL, 136e1e1452dSArchie Cobbs &ng_parse_string_type 137e1e1452dSArchie Cobbs }, 138e1e1452dSArchie Cobbs { 139e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 140e1e1452dSArchie Cobbs NGM_ETHER_GET_IFINDEX, 141e1e1452dSArchie Cobbs "getifindex", 142e1e1452dSArchie Cobbs NULL, 143e1e1452dSArchie Cobbs &ng_parse_int32_type 144e1e1452dSArchie Cobbs }, 1454b39c3c7SArchie Cobbs { 1464b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 1474b39c3c7SArchie Cobbs NGM_ETHER_GET_ENADDR, 1484b39c3c7SArchie Cobbs "getenaddr", 1494b39c3c7SArchie Cobbs NULL, 1508c7e4101SRuslan Ermilov &ng_parse_enaddr_type 1514b39c3c7SArchie Cobbs }, 1524b39c3c7SArchie Cobbs { 1534b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 15456045c66SArchie Cobbs NGM_ETHER_SET_ENADDR, 15556045c66SArchie Cobbs "setenaddr", 1568c7e4101SRuslan Ermilov &ng_parse_enaddr_type, 15756045c66SArchie Cobbs NULL 15856045c66SArchie Cobbs }, 15956045c66SArchie Cobbs { 16056045c66SArchie Cobbs NGM_ETHER_COOKIE, 16156045c66SArchie Cobbs NGM_ETHER_GET_PROMISC, 16256045c66SArchie Cobbs "getpromisc", 16356045c66SArchie Cobbs NULL, 16456045c66SArchie Cobbs &ng_parse_int32_type 16556045c66SArchie Cobbs }, 16656045c66SArchie Cobbs { 16756045c66SArchie Cobbs NGM_ETHER_COOKIE, 1684b39c3c7SArchie Cobbs NGM_ETHER_SET_PROMISC, 1694b39c3c7SArchie Cobbs "setpromisc", 1704b39c3c7SArchie Cobbs &ng_parse_int32_type, 1714b39c3c7SArchie Cobbs NULL 1724b39c3c7SArchie Cobbs }, 1734b39c3c7SArchie Cobbs { 1744b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 17556045c66SArchie Cobbs NGM_ETHER_GET_AUTOSRC, 17656045c66SArchie Cobbs "getautosrc", 17756045c66SArchie Cobbs NULL, 17856045c66SArchie Cobbs &ng_parse_int32_type 17956045c66SArchie Cobbs }, 18056045c66SArchie Cobbs { 18156045c66SArchie Cobbs NGM_ETHER_COOKIE, 1824b39c3c7SArchie Cobbs NGM_ETHER_SET_AUTOSRC, 1834b39c3c7SArchie Cobbs "setautosrc", 1844b39c3c7SArchie Cobbs &ng_parse_int32_type, 1854b39c3c7SArchie Cobbs NULL 1864b39c3c7SArchie Cobbs }, 187810d5e89SGleb Smirnoff { 188810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 189810d5e89SGleb Smirnoff NGM_ETHER_ADD_MULTI, 190810d5e89SGleb Smirnoff "addmulti", 191810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 192810d5e89SGleb Smirnoff NULL 193810d5e89SGleb Smirnoff }, 194810d5e89SGleb Smirnoff { 195810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 196810d5e89SGleb Smirnoff NGM_ETHER_DEL_MULTI, 197810d5e89SGleb Smirnoff "delmulti", 198810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 199810d5e89SGleb Smirnoff NULL 200810d5e89SGleb Smirnoff }, 201cefddd66SGleb Smirnoff { 202cefddd66SGleb Smirnoff NGM_ETHER_COOKIE, 203cefddd66SGleb Smirnoff NGM_ETHER_DETACH, 204cefddd66SGleb Smirnoff "detach", 205cefddd66SGleb Smirnoff NULL, 206cefddd66SGleb Smirnoff NULL 207cefddd66SGleb Smirnoff }, 208e1e1452dSArchie Cobbs { 0 } 209e1e1452dSArchie Cobbs }; 210e1e1452dSArchie Cobbs 211e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = { 212f8aae777SJulian Elischer .version = NG_ABI_VERSION, 213f8aae777SJulian Elischer .name = NG_ETHER_NODE_TYPE, 214f8aae777SJulian Elischer .mod_event = ng_ether_mod_event, 215f8aae777SJulian Elischer .constructor = ng_ether_constructor, 216f8aae777SJulian Elischer .rcvmsg = ng_ether_rcvmsg, 217f8aae777SJulian Elischer .shutdown = ng_ether_shutdown, 218f8aae777SJulian Elischer .newhook = ng_ether_newhook, 219f8aae777SJulian Elischer .rcvdata = ng_ether_rcvdata, 220f8aae777SJulian Elischer .disconnect = ng_ether_disconnect, 221f8aae777SJulian Elischer .cmdlist = ng_ether_cmdlist, 222e1e1452dSArchie Cobbs }; 223e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct); 224e1e1452dSArchie Cobbs 225e1e1452dSArchie Cobbs /****************************************************************** 226e1e1452dSArchie Cobbs ETHERNET FUNCTION HOOKS 227e1e1452dSArchie Cobbs ******************************************************************/ 228e1e1452dSArchie Cobbs 229e1e1452dSArchie Cobbs /* 230e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface. We get to 231e1e1452dSArchie Cobbs * look at it here before any upper layer protocols do. 232e1e1452dSArchie Cobbs * 233e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 234e1e1452dSArchie Cobbs */ 235e1e1452dSArchie Cobbs static void 236edbb5246SSam Leffler ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 237e1e1452dSArchie Cobbs { 238e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 23930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2401a292b80SArchie Cobbs int error; 241e1e1452dSArchie Cobbs 242e1e1452dSArchie Cobbs /* If "lower" hook not connected, let packet continue */ 2431a292b80SArchie Cobbs if (priv->lower == NULL) 244e1e1452dSArchie Cobbs return; 2451a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 246e1e1452dSArchie Cobbs } 247e1e1452dSArchie Cobbs 248e1e1452dSArchie Cobbs /* 249e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface, and which 250e1e1452dSArchie Cobbs * does not match any of our known protocols (an ``orphan''). 251e1e1452dSArchie Cobbs * 252e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 253e1e1452dSArchie Cobbs */ 254e1e1452dSArchie Cobbs static void 255edbb5246SSam Leffler ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 256e1e1452dSArchie Cobbs { 257e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 25830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2591a292b80SArchie Cobbs int error; 260e1e1452dSArchie Cobbs 2611a292b80SArchie Cobbs /* If "orphan" hook not connected, discard packet */ 2621a292b80SArchie Cobbs if (priv->orphan == NULL) { 263e1e1452dSArchie Cobbs m_freem(m); 264e1e1452dSArchie Cobbs return; 265e1e1452dSArchie Cobbs } 2661a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->orphan, m); 267e1e1452dSArchie Cobbs } 268e1e1452dSArchie Cobbs 269e1e1452dSArchie Cobbs /* 270e1e1452dSArchie Cobbs * Handle a packet that is going out on an interface. 271e1e1452dSArchie Cobbs * The Ethernet header is already attached to the mbuf. 272e1e1452dSArchie Cobbs */ 273e1e1452dSArchie Cobbs static int 274e1e1452dSArchie Cobbs ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 275e1e1452dSArchie Cobbs { 276e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 27730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 278e1e1452dSArchie Cobbs int error = 0; 279e1e1452dSArchie Cobbs 280e1e1452dSArchie Cobbs /* If "upper" hook not connected, let packet continue */ 281e1e1452dSArchie Cobbs if (priv->upper == NULL) 282e1e1452dSArchie Cobbs return (0); 283e1e1452dSArchie Cobbs 284e1e1452dSArchie Cobbs /* Send it out "upper" hook */ 285f089869fSMarko Zec NG_OUTBOUND_THREAD_REF(); 286069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->upper, *mp); 287f089869fSMarko Zec NG_OUTBOUND_THREAD_UNREF(); 288e1e1452dSArchie Cobbs return (error); 289e1e1452dSArchie Cobbs } 290e1e1452dSArchie Cobbs 291e1e1452dSArchie Cobbs /* 292e1e1452dSArchie Cobbs * A new Ethernet interface has been attached. 293e1e1452dSArchie Cobbs * Create a new node for it, etc. 294e1e1452dSArchie Cobbs */ 295e1e1452dSArchie Cobbs static void 296e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp) 297e1e1452dSArchie Cobbs { 298e1e1452dSArchie Cobbs priv_p priv; 299e1e1452dSArchie Cobbs node_p node; 300e1e1452dSArchie Cobbs 301aef8f344SMarko Zec /* 302aef8f344SMarko Zec * Do not create / attach an ether node to this ifnet if 303aef8f344SMarko Zec * a netgraph node with the same name already exists. 304aef8f344SMarko Zec * This should prevent ether nodes to become attached to 305aef8f344SMarko Zec * eiface nodes, which may be problematic due to naming 306aef8f344SMarko Zec * clashes. 307aef8f344SMarko Zec */ 308aef8f344SMarko Zec if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) { 309aef8f344SMarko Zec NG_NODE_UNREF(node); 310aef8f344SMarko Zec return; 311aef8f344SMarko Zec } 312aef8f344SMarko Zec 313e1e1452dSArchie Cobbs /* Create node */ 3146e551fb6SDavid E. O'Brien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 315e1e1452dSArchie Cobbs if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 316e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3179bf40edeSBrooks Davis __func__, "create node", ifp->if_xname); 318e1e1452dSArchie Cobbs return; 319e1e1452dSArchie Cobbs } 320e1e1452dSArchie Cobbs 321e1e1452dSArchie Cobbs /* Allocate private data */ 3221ede983cSDag-Erling Smørgrav priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 323e1e1452dSArchie Cobbs if (priv == NULL) { 324e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3259bf40edeSBrooks Davis __func__, "allocate memory", ifp->if_xname); 32630400f03SJulian Elischer NG_NODE_UNREF(node); 327e1e1452dSArchie Cobbs return; 328e1e1452dSArchie Cobbs } 32930400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 330e1e1452dSArchie Cobbs priv->ifp = ifp; 3315240dcdbSRuslan Ermilov IFP2NG(ifp) = node; 332a3e232d6SArchie Cobbs priv->hwassist = ifp->if_hwassist; 333e1e1452dSArchie Cobbs 334e1e1452dSArchie Cobbs /* Try to give the node the same name as the interface */ 3359bf40edeSBrooks Davis if (ng_name_node(node, ifp->if_xname) != 0) { 336e1e1452dSArchie Cobbs log(LOG_WARNING, "%s: can't name node %s\n", 3379bf40edeSBrooks Davis __func__, ifp->if_xname); 338e1e1452dSArchie Cobbs } 339e1e1452dSArchie Cobbs } 340e1e1452dSArchie Cobbs 341e1e1452dSArchie Cobbs /* 342e1e1452dSArchie Cobbs * An Ethernet interface is being detached. 3431acb27c6SJulian Elischer * REALLY Destroy its node. 344e1e1452dSArchie Cobbs */ 345e1e1452dSArchie Cobbs static void 346e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp) 347e1e1452dSArchie Cobbs { 348e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 3491acb27c6SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 350e1e1452dSArchie Cobbs 3511acb27c6SJulian Elischer NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 3521acb27c6SJulian Elischer /* 3531acb27c6SJulian Elischer * We can't assume the ifnet is still around when we run shutdown 3541acb27c6SJulian Elischer * So zap it now. XXX We HOPE that anything running at this time 3551acb27c6SJulian Elischer * handles it (as it should in the non netgraph case). 3561acb27c6SJulian Elischer */ 3575240dcdbSRuslan Ermilov IFP2NG(ifp) = NULL; 3581acb27c6SJulian Elischer priv->ifp = NULL; /* XXX race if interrupted an output packet */ 3591acb27c6SJulian Elischer ng_rmnode_self(node); /* remove all netgraph parts */ 360e1e1452dSArchie Cobbs } 361e1e1452dSArchie Cobbs 3621c7899c7SGleb Smirnoff /* 3631c7899c7SGleb Smirnoff * Notify graph about link event. 3641c7899c7SGleb Smirnoff * if_link_state_change() has already checked that the state has changed. 3651c7899c7SGleb Smirnoff */ 3661c7899c7SGleb Smirnoff static void 3671c7899c7SGleb Smirnoff ng_ether_link_state(struct ifnet *ifp, int state) 3681c7899c7SGleb Smirnoff { 3691c7899c7SGleb Smirnoff const node_p node = IFP2NG(ifp); 3701c7899c7SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 3711c7899c7SGleb Smirnoff struct ng_mesg *msg; 3721c7899c7SGleb Smirnoff int cmd, dummy_error = 0; 3731c7899c7SGleb Smirnoff 3741c7899c7SGleb Smirnoff if (priv->lower == NULL) 3751c7899c7SGleb Smirnoff return; 3761c7899c7SGleb Smirnoff 3771c7899c7SGleb Smirnoff if (state == LINK_STATE_UP) 3781c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_UP; 3791c7899c7SGleb Smirnoff else if (state == LINK_STATE_DOWN) 3801c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_DOWN; 3811c7899c7SGleb Smirnoff else 3821c7899c7SGleb Smirnoff return; 3831c7899c7SGleb Smirnoff 3841c7899c7SGleb Smirnoff NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 3851c7899c7SGleb Smirnoff if (msg != NULL) 3861c7899c7SGleb Smirnoff NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 3871c7899c7SGleb Smirnoff } 3881c7899c7SGleb Smirnoff 389e1e1452dSArchie Cobbs /****************************************************************** 390e1e1452dSArchie Cobbs NETGRAPH NODE METHODS 391e1e1452dSArchie Cobbs ******************************************************************/ 392e1e1452dSArchie Cobbs 393e1e1452dSArchie Cobbs /* 394e1e1452dSArchie Cobbs * It is not possible or allowable to create a node of this type. 395e1e1452dSArchie Cobbs * Nodes get created when the interface is attached (or, when 396e1e1452dSArchie Cobbs * this node type's KLD is loaded). 397e1e1452dSArchie Cobbs */ 398e1e1452dSArchie Cobbs static int 399069154d5SJulian Elischer ng_ether_constructor(node_p node) 400e1e1452dSArchie Cobbs { 401e1e1452dSArchie Cobbs return (EINVAL); 402e1e1452dSArchie Cobbs } 403e1e1452dSArchie Cobbs 404e1e1452dSArchie Cobbs /* 405e1e1452dSArchie Cobbs * Check for attaching a new hook. 406e1e1452dSArchie Cobbs */ 407e1e1452dSArchie Cobbs static int 408e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name) 409e1e1452dSArchie Cobbs { 41030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 411e1e1452dSArchie Cobbs hook_p *hookptr; 412e1e1452dSArchie Cobbs 413e1e1452dSArchie Cobbs /* Divert hook is an alias for lower */ 414e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 415e1e1452dSArchie Cobbs name = NG_ETHER_HOOK_LOWER; 416e1e1452dSArchie Cobbs 417e1e1452dSArchie Cobbs /* Which hook? */ 418f664dcdeSJulian Elischer if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) { 419e1e1452dSArchie Cobbs hookptr = &priv->upper; 420f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper); 421f089869fSMarko Zec NG_HOOK_SET_TO_INBOUND(hook); 422f664dcdeSJulian Elischer } else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 423e1e1452dSArchie Cobbs hookptr = &priv->lower; 424f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 425f664dcdeSJulian Elischer } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 4261a292b80SArchie Cobbs hookptr = &priv->orphan; 427f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 428f664dcdeSJulian Elischer } else 429e1e1452dSArchie Cobbs return (EINVAL); 430e1e1452dSArchie Cobbs 431e1e1452dSArchie Cobbs /* Check if already connected (shouldn't be, but doesn't hurt) */ 432e1e1452dSArchie Cobbs if (*hookptr != NULL) 433e1e1452dSArchie Cobbs return (EISCONN); 434e1e1452dSArchie Cobbs 435a3e232d6SArchie Cobbs /* Disable hardware checksums while 'upper' hook is connected */ 436a3e232d6SArchie Cobbs if (hookptr == &priv->upper) 437a3e232d6SArchie Cobbs priv->ifp->if_hwassist = 0; 438c1b8a9edSAlexander Motin NG_HOOK_HI_STACK(hook); 439e1e1452dSArchie Cobbs /* OK */ 440e1e1452dSArchie Cobbs *hookptr = hook; 441e1e1452dSArchie Cobbs return (0); 442e1e1452dSArchie Cobbs } 443e1e1452dSArchie Cobbs 444e1e1452dSArchie Cobbs /* 445e1e1452dSArchie Cobbs * Receive an incoming control message. 446e1e1452dSArchie Cobbs */ 447e1e1452dSArchie Cobbs static int 448069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 449e1e1452dSArchie Cobbs { 45030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 451e1e1452dSArchie Cobbs struct ng_mesg *resp = NULL; 452e1e1452dSArchie Cobbs int error = 0; 453069154d5SJulian Elischer struct ng_mesg *msg; 454e1e1452dSArchie Cobbs 455069154d5SJulian Elischer NGI_GET_MSG(item, msg); 456e1e1452dSArchie Cobbs switch (msg->header.typecookie) { 457e1e1452dSArchie Cobbs case NGM_ETHER_COOKIE: 458e1e1452dSArchie Cobbs switch (msg->header.cmd) { 459e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFNAME: 460bbb75d78SRuslan Ermilov NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 461e1e1452dSArchie Cobbs if (resp == NULL) { 462e1e1452dSArchie Cobbs error = ENOMEM; 463e1e1452dSArchie Cobbs break; 464e1e1452dSArchie Cobbs } 465bbb75d78SRuslan Ermilov strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 466e1e1452dSArchie Cobbs break; 467e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFINDEX: 468e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 469e1e1452dSArchie Cobbs if (resp == NULL) { 470e1e1452dSArchie Cobbs error = ENOMEM; 471e1e1452dSArchie Cobbs break; 472e1e1452dSArchie Cobbs } 473e1e1452dSArchie Cobbs *((u_int32_t *)resp->data) = priv->ifp->if_index; 474e1e1452dSArchie Cobbs break; 4754b39c3c7SArchie Cobbs case NGM_ETHER_GET_ENADDR: 4764b39c3c7SArchie Cobbs NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 4774b39c3c7SArchie Cobbs if (resp == NULL) { 4784b39c3c7SArchie Cobbs error = ENOMEM; 4794b39c3c7SArchie Cobbs break; 4804b39c3c7SArchie Cobbs } 4814a0d6638SRuslan Ermilov bcopy(IF_LLADDR(priv->ifp), 4824b39c3c7SArchie Cobbs resp->data, ETHER_ADDR_LEN); 4834b39c3c7SArchie Cobbs break; 48456045c66SArchie Cobbs case NGM_ETHER_SET_ENADDR: 48556045c66SArchie Cobbs { 48656045c66SArchie Cobbs if (msg->header.arglen != ETHER_ADDR_LEN) { 48756045c66SArchie Cobbs error = EINVAL; 48856045c66SArchie Cobbs break; 48956045c66SArchie Cobbs } 49056045c66SArchie Cobbs error = if_setlladdr(priv->ifp, 49156045c66SArchie Cobbs (u_char *)msg->data, ETHER_ADDR_LEN); 49256045c66SArchie Cobbs break; 49356045c66SArchie Cobbs } 49456045c66SArchie Cobbs case NGM_ETHER_GET_PROMISC: 49556045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 49656045c66SArchie Cobbs if (resp == NULL) { 49756045c66SArchie Cobbs error = ENOMEM; 49856045c66SArchie Cobbs break; 49956045c66SArchie Cobbs } 50056045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->promisc; 50156045c66SArchie Cobbs break; 5024b39c3c7SArchie Cobbs case NGM_ETHER_SET_PROMISC: 5034b39c3c7SArchie Cobbs { 5044b39c3c7SArchie Cobbs u_char want; 5054b39c3c7SArchie Cobbs 5064b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5074b39c3c7SArchie Cobbs error = EINVAL; 5084b39c3c7SArchie Cobbs break; 5094b39c3c7SArchie Cobbs } 5104b39c3c7SArchie Cobbs want = !!*((u_int32_t *)msg->data); 5114b39c3c7SArchie Cobbs if (want ^ priv->promisc) { 5124b39c3c7SArchie Cobbs if ((error = ifpromisc(priv->ifp, want)) != 0) 5134b39c3c7SArchie Cobbs break; 5144b39c3c7SArchie Cobbs priv->promisc = want; 5154b39c3c7SArchie Cobbs } 5164b39c3c7SArchie Cobbs break; 5174b39c3c7SArchie Cobbs } 51856045c66SArchie Cobbs case NGM_ETHER_GET_AUTOSRC: 51956045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 52056045c66SArchie Cobbs if (resp == NULL) { 52156045c66SArchie Cobbs error = ENOMEM; 52256045c66SArchie Cobbs break; 52356045c66SArchie Cobbs } 52456045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->autoSrcAddr; 52556045c66SArchie Cobbs break; 5264b39c3c7SArchie Cobbs case NGM_ETHER_SET_AUTOSRC: 5274b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5284b39c3c7SArchie Cobbs error = EINVAL; 5294b39c3c7SArchie Cobbs break; 5304b39c3c7SArchie Cobbs } 5314b39c3c7SArchie Cobbs priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 5324b39c3c7SArchie Cobbs break; 533810d5e89SGleb Smirnoff case NGM_ETHER_ADD_MULTI: 534810d5e89SGleb Smirnoff { 535810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 536ec002feeSBruce M Simpson struct ifmultiaddr *ifma; 537810d5e89SGleb Smirnoff 538810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 539810d5e89SGleb Smirnoff error = EINVAL; 540810d5e89SGleb Smirnoff break; 541810d5e89SGleb Smirnoff } 542ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 543810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 544810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 545ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 546810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 547810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 548ec002feeSBruce M Simpson /* 549ec002feeSBruce M Simpson * Netgraph is only permitted to join groups once 550ec002feeSBruce M Simpson * via the if_addmulti() KPI, because it cannot hold 551ec002feeSBruce M Simpson * struct ifmultiaddr * between calls. It may also 552ec002feeSBruce M Simpson * lose a race while we check if the membership 553ec002feeSBruce M Simpson * already exists. 554ec002feeSBruce M Simpson */ 555ec002feeSBruce M Simpson IF_ADDR_LOCK(priv->ifp); 556ec002feeSBruce M Simpson ifma = if_findmulti(priv->ifp, 557ec002feeSBruce M Simpson (struct sockaddr *)&sa_dl); 558ec002feeSBruce M Simpson IF_ADDR_UNLOCK(priv->ifp); 559ec002feeSBruce M Simpson if (ifma != NULL) { 560ec002feeSBruce M Simpson error = EADDRINUSE; 561ec002feeSBruce M Simpson } else { 562810d5e89SGleb Smirnoff error = if_addmulti(priv->ifp, 563ec002feeSBruce M Simpson (struct sockaddr *)&sa_dl, &ifma); 564ec002feeSBruce M Simpson } 565810d5e89SGleb Smirnoff break; 566810d5e89SGleb Smirnoff } 567810d5e89SGleb Smirnoff case NGM_ETHER_DEL_MULTI: 568810d5e89SGleb Smirnoff { 569810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 570810d5e89SGleb Smirnoff 571810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 572810d5e89SGleb Smirnoff error = EINVAL; 573810d5e89SGleb Smirnoff break; 574810d5e89SGleb Smirnoff } 575ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 576810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 577810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 578ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 579810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 580810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 581810d5e89SGleb Smirnoff error = if_delmulti(priv->ifp, 582810d5e89SGleb Smirnoff (struct sockaddr *)&sa_dl); 583810d5e89SGleb Smirnoff break; 584810d5e89SGleb Smirnoff } 585cefddd66SGleb Smirnoff case NGM_ETHER_DETACH: 586cefddd66SGleb Smirnoff ng_ether_detach(priv->ifp); 587cefddd66SGleb Smirnoff break; 588e1e1452dSArchie Cobbs default: 589e1e1452dSArchie Cobbs error = EINVAL; 590e1e1452dSArchie Cobbs break; 591e1e1452dSArchie Cobbs } 592e1e1452dSArchie Cobbs break; 593e1e1452dSArchie Cobbs default: 594e1e1452dSArchie Cobbs error = EINVAL; 595e1e1452dSArchie Cobbs break; 596e1e1452dSArchie Cobbs } 597069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 598069154d5SJulian Elischer NG_FREE_MSG(msg); 599e1e1452dSArchie Cobbs return (error); 600e1e1452dSArchie Cobbs } 601e1e1452dSArchie Cobbs 602e1e1452dSArchie Cobbs /* 603e1e1452dSArchie Cobbs * Receive data on a hook. 604f664dcdeSJulian Elischer * Since we use per-hook recveive methods this should never be called. 605e1e1452dSArchie Cobbs */ 606e1e1452dSArchie Cobbs static int 607069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item) 608e1e1452dSArchie Cobbs { 609069154d5SJulian Elischer NG_FREE_ITEM(item); 6103ca24c28SJulian Elischer 6116e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 6121a292b80SArchie Cobbs #ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ 6137ea5573cSDag-Erling Smørgrav return (0); 614b40ce416SJulian Elischer #endif 615e1e1452dSArchie Cobbs } 616e1e1452dSArchie Cobbs 617e1e1452dSArchie Cobbs /* 6181a292b80SArchie Cobbs * Handle an mbuf received on the "lower" or "orphan" hook. 619e1e1452dSArchie Cobbs */ 620e1e1452dSArchie Cobbs static int 621f664dcdeSJulian Elischer ng_ether_rcv_lower(hook_p hook, item_p item) 622e1e1452dSArchie Cobbs { 623f664dcdeSJulian Elischer struct mbuf *m; 624f664dcdeSJulian Elischer const node_p node = NG_HOOK_NODE(hook); 62530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 626a1479aa2SArchie Cobbs struct ifnet *const ifp = priv->ifp; 627a1479aa2SArchie Cobbs 628f664dcdeSJulian Elischer NGI_GET_M(item, m); 629f664dcdeSJulian Elischer NG_FREE_ITEM(item); 630f664dcdeSJulian Elischer 631a1479aa2SArchie Cobbs /* Check whether interface is ready for packets */ 632f664dcdeSJulian Elischer 63313f4c340SRobert Watson if (!((ifp->if_flags & IFF_UP) && 63413f4c340SRobert Watson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 635a1479aa2SArchie Cobbs NG_FREE_M(m); 636a1479aa2SArchie Cobbs return (ENETDOWN); 637a1479aa2SArchie Cobbs } 638e1e1452dSArchie Cobbs 639e1e1452dSArchie Cobbs /* Make sure header is fully pulled up */ 640e1e1452dSArchie Cobbs if (m->m_pkthdr.len < sizeof(struct ether_header)) { 641069154d5SJulian Elischer NG_FREE_M(m); 642e1e1452dSArchie Cobbs return (EINVAL); 643e1e1452dSArchie Cobbs } 644e1e1452dSArchie Cobbs if (m->m_len < sizeof(struct ether_header) 6457b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 646e1e1452dSArchie Cobbs return (ENOBUFS); 647e1e1452dSArchie Cobbs 6484b39c3c7SArchie Cobbs /* Drop in the MAC address if desired */ 6494b39c3c7SArchie Cobbs if (priv->autoSrcAddr) { 6507b9f235fSArchie Cobbs 6517b9f235fSArchie Cobbs /* Make the mbuf writable if it's not already */ 6527b9f235fSArchie Cobbs if (!M_WRITABLE(m) 6537b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 6547b9f235fSArchie Cobbs return (ENOBUFS); 6557b9f235fSArchie Cobbs 6567b9f235fSArchie Cobbs /* Overwrite source MAC address */ 6574a0d6638SRuslan Ermilov bcopy(IF_LLADDR(ifp), 6584b39c3c7SArchie Cobbs mtod(m, struct ether_header *)->ether_shost, 6594b39c3c7SArchie Cobbs ETHER_ADDR_LEN); 6604b39c3c7SArchie Cobbs } 661561e4fb9SJulian Elischer 662e1e1452dSArchie Cobbs /* Send it on its way */ 663a1479aa2SArchie Cobbs return ether_output_frame(ifp, m); 664e1e1452dSArchie Cobbs } 665e1e1452dSArchie Cobbs 666e1e1452dSArchie Cobbs /* 667e1e1452dSArchie Cobbs * Handle an mbuf received on the "upper" hook. 668e1e1452dSArchie Cobbs */ 669e1e1452dSArchie Cobbs static int 670f664dcdeSJulian Elischer ng_ether_rcv_upper(hook_p hook, item_p item) 671e1e1452dSArchie Cobbs { 672f664dcdeSJulian Elischer struct mbuf *m; 673f664dcdeSJulian Elischer const node_p node = NG_HOOK_NODE(hook); 67430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 6756512768bSGleb Smirnoff struct ifnet *ifp = priv->ifp; 676e1e1452dSArchie Cobbs 677f664dcdeSJulian Elischer NGI_GET_M(item, m); 678f664dcdeSJulian Elischer NG_FREE_ITEM(item); 679f664dcdeSJulian Elischer 680c60c00bcSRuslan Ermilov /* Check length and pull off header */ 681c60c00bcSRuslan Ermilov if (m->m_pkthdr.len < sizeof(struct ether_header)) { 682c60c00bcSRuslan Ermilov NG_FREE_M(m); 683c60c00bcSRuslan Ermilov return (EINVAL); 684c60c00bcSRuslan Ermilov } 685c60c00bcSRuslan Ermilov if (m->m_len < sizeof(struct ether_header) && 686c60c00bcSRuslan Ermilov (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 687c60c00bcSRuslan Ermilov return (ENOBUFS); 688c60c00bcSRuslan Ermilov 6896512768bSGleb Smirnoff m->m_pkthdr.rcvif = ifp; 690e1e1452dSArchie Cobbs 691fd6238a6SAndrew Thompson /* Pass the packet to the bridge, it may come back to us */ 6926512768bSGleb Smirnoff if (ifp->if_bridge) { 693fd6238a6SAndrew Thompson BRIDGE_INPUT(ifp, m); 6946512768bSGleb Smirnoff if (m == NULL) 6956512768bSGleb Smirnoff return (0); 6966512768bSGleb Smirnoff } 697a176c2aeSGleb Smirnoff 698e1e1452dSArchie Cobbs /* Route packet back in */ 699fd6238a6SAndrew Thompson ether_demux(ifp, m); 700e1e1452dSArchie Cobbs return (0); 701e1e1452dSArchie Cobbs } 702e1e1452dSArchie Cobbs 703e1e1452dSArchie Cobbs /* 7041acb27c6SJulian Elischer * Shutdown node. This resets the node but does not remove it 7051acb27c6SJulian Elischer * unless the REALLY_DIE flag is set. 706e1e1452dSArchie Cobbs */ 707e1e1452dSArchie Cobbs static int 708069154d5SJulian Elischer ng_ether_shutdown(node_p node) 709e1e1452dSArchie Cobbs { 71030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 7114b39c3c7SArchie Cobbs 712be4252b3SJulian Elischer if (node->nd_flags & NGF_REALLY_DIE) { 7131acb27c6SJulian Elischer /* 7141acb27c6SJulian Elischer * WE came here because the ethernet card is being unloaded, 7151acb27c6SJulian Elischer * so stop being persistant. 7161acb27c6SJulian Elischer * Actually undo all the things we did on creation. 7171acb27c6SJulian Elischer * Assume the ifp has already been freed. 7181acb27c6SJulian Elischer */ 7191acb27c6SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 7201ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 7211acb27c6SJulian Elischer NG_NODE_UNREF(node); /* free node itself */ 7221acb27c6SJulian Elischer return (0); 723069154d5SJulian Elischer } 724018df1c3SBrian Feldman if (priv->promisc) { /* disable promiscuous mode */ 725018df1c3SBrian Feldman (void)ifpromisc(priv->ifp, 0); 726018df1c3SBrian Feldman priv->promisc = 0; 727018df1c3SBrian Feldman } 7284b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 729be4252b3SJulian Elischer NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 730be4252b3SJulian Elischer 731e1e1452dSArchie Cobbs return (0); 732e1e1452dSArchie Cobbs } 733e1e1452dSArchie Cobbs 734e1e1452dSArchie Cobbs /* 735e1e1452dSArchie Cobbs * Hook disconnection. 736e1e1452dSArchie Cobbs */ 737e1e1452dSArchie Cobbs static int 738e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook) 739e1e1452dSArchie Cobbs { 74030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 741e1e1452dSArchie Cobbs 742a3e232d6SArchie Cobbs if (hook == priv->upper) { 743e1e1452dSArchie Cobbs priv->upper = NULL; 744b712e9ecSBrian Feldman if (priv->ifp != NULL) /* restore h/w csum */ 745b712e9ecSBrian Feldman priv->ifp->if_hwassist = priv->hwassist; 7461a292b80SArchie Cobbs } else if (hook == priv->lower) 747e1e1452dSArchie Cobbs priv->lower = NULL; 7481a292b80SArchie Cobbs else if (hook == priv->orphan) 7491a292b80SArchie Cobbs priv->orphan = NULL; 7501a292b80SArchie Cobbs else 7516e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 75230400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 75330400f03SJulian Elischer && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 75430400f03SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 755e1e1452dSArchie Cobbs return (0); 756e1e1452dSArchie Cobbs } 757e1e1452dSArchie Cobbs 758e1e1452dSArchie Cobbs /****************************************************************** 759e1e1452dSArchie Cobbs INITIALIZATION 760e1e1452dSArchie Cobbs ******************************************************************/ 761e1e1452dSArchie Cobbs 762e1e1452dSArchie Cobbs /* 763e1e1452dSArchie Cobbs * Handle loading and unloading for this node type. 764e1e1452dSArchie Cobbs */ 765e1e1452dSArchie Cobbs static int 766e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data) 767e1e1452dSArchie Cobbs { 768e1e1452dSArchie Cobbs int error = 0; 769e1e1452dSArchie Cobbs int s; 770e1e1452dSArchie Cobbs 771e1e1452dSArchie Cobbs s = splnet(); 772e1e1452dSArchie Cobbs switch (event) { 773e1e1452dSArchie Cobbs case MOD_LOAD: 774e1e1452dSArchie Cobbs 775e1e1452dSArchie Cobbs /* Register function hooks */ 776e1e1452dSArchie Cobbs if (ng_ether_attach_p != NULL) { 777e1e1452dSArchie Cobbs error = EEXIST; 778e1e1452dSArchie Cobbs break; 779e1e1452dSArchie Cobbs } 780e1e1452dSArchie Cobbs ng_ether_attach_p = ng_ether_attach; 781e1e1452dSArchie Cobbs ng_ether_detach_p = ng_ether_detach; 782e1e1452dSArchie Cobbs ng_ether_output_p = ng_ether_output; 783e1e1452dSArchie Cobbs ng_ether_input_p = ng_ether_input; 784e1e1452dSArchie Cobbs ng_ether_input_orphan_p = ng_ether_input_orphan; 7851c7899c7SGleb Smirnoff ng_ether_link_state_p = ng_ether_link_state; 786e1e1452dSArchie Cobbs 787aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS 788aef8f344SMarko Zec vnet_mod_register(&vnet_ng_ether_modinfo); 789aef8f344SMarko Zec #else 790aef8f344SMarko Zec error = ng_ether_iattach(NULL); 791aef8f344SMarko Zec #endif 792e1e1452dSArchie Cobbs break; 793e1e1452dSArchie Cobbs 794e1e1452dSArchie Cobbs case MOD_UNLOAD: 795e1e1452dSArchie Cobbs 796e1e1452dSArchie Cobbs /* 797e1e1452dSArchie Cobbs * Note that the base code won't try to unload us until 798e1e1452dSArchie Cobbs * all nodes have been removed, and that can't happen 799e1e1452dSArchie Cobbs * until all Ethernet interfaces are removed. In any 800e1e1452dSArchie Cobbs * case, we know there are no nodes left if the action 801e1e1452dSArchie Cobbs * is MOD_UNLOAD, so there's no need to detach any nodes. 802e1e1452dSArchie Cobbs */ 803e1e1452dSArchie Cobbs 804aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS 805aef8f344SMarko Zec vnet_mod_deregister(&vnet_ng_ether_modinfo); 806aef8f344SMarko Zec #endif 807aef8f344SMarko Zec 808e1e1452dSArchie Cobbs /* Unregister function hooks */ 809e1e1452dSArchie Cobbs ng_ether_attach_p = NULL; 810e1e1452dSArchie Cobbs ng_ether_detach_p = NULL; 811e1e1452dSArchie Cobbs ng_ether_output_p = NULL; 812e1e1452dSArchie Cobbs ng_ether_input_p = NULL; 813e1e1452dSArchie Cobbs ng_ether_input_orphan_p = NULL; 8141c7899c7SGleb Smirnoff ng_ether_link_state_p = NULL; 815e1e1452dSArchie Cobbs break; 816e1e1452dSArchie Cobbs 817e1e1452dSArchie Cobbs default: 818e1e1452dSArchie Cobbs error = EOPNOTSUPP; 819e1e1452dSArchie Cobbs break; 820e1e1452dSArchie Cobbs } 821e1e1452dSArchie Cobbs splx(s); 822e1e1452dSArchie Cobbs return (error); 823e1e1452dSArchie Cobbs } 824e1e1452dSArchie Cobbs 825aef8f344SMarko Zec static int ng_ether_iattach(const void *unused) 826aef8f344SMarko Zec { 827aef8f344SMarko Zec INIT_VNET_NET(curvnet); 828aef8f344SMarko Zec struct ifnet *ifp; 829aef8f344SMarko Zec 830aef8f344SMarko Zec /* Create nodes for any already-existing Ethernet interfaces. */ 831aef8f344SMarko Zec IFNET_RLOCK(); 832aef8f344SMarko Zec TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 833aef8f344SMarko Zec if (ifp->if_type == IFT_ETHER 834aef8f344SMarko Zec || ifp->if_type == IFT_L2VLAN) 835aef8f344SMarko Zec ng_ether_attach(ifp); 836aef8f344SMarko Zec } 837aef8f344SMarko Zec IFNET_RUNLOCK(); 838aef8f344SMarko Zec 839aef8f344SMarko Zec return (0); 840aef8f344SMarko Zec } 841