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> 50c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 51e1e1452dSArchie Cobbs #include <sys/systm.h> 52e1e1452dSArchie Cobbs #include <sys/kernel.h> 53e1e1452dSArchie Cobbs #include <sys/malloc.h> 54e1e1452dSArchie Cobbs #include <sys/mbuf.h> 55e1e1452dSArchie Cobbs #include <sys/errno.h> 56530c0060SRobert Watson #include <sys/proc.h> 57e1e1452dSArchie Cobbs #include <sys/syslog.h> 58e1e1452dSArchie Cobbs #include <sys/socket.h> 592cdf8c49SMarko Zec #include <sys/taskqueue.h> 60e1e1452dSArchie Cobbs 61e1e1452dSArchie Cobbs #include <net/if.h> 62810d5e89SGleb Smirnoff #include <net/if_dl.h> 63e1e1452dSArchie Cobbs #include <net/if_types.h> 64e1e1452dSArchie Cobbs #include <net/if_arp.h> 65e1e1452dSArchie Cobbs #include <net/if_var.h> 66*3d0d5b21SJustin Hibbits #include <net/if_private.h> 67e1e1452dSArchie Cobbs #include <net/ethernet.h> 68fd6238a6SAndrew Thompson #include <net/if_bridgevar.h> 694b79449eSBjoern A. Zeeb #include <net/vnet.h> 70e1e1452dSArchie Cobbs 71e1e1452dSArchie Cobbs #include <netgraph/ng_message.h> 72e1e1452dSArchie Cobbs #include <netgraph/netgraph.h> 73e1e1452dSArchie Cobbs #include <netgraph/ng_parse.h> 74e1e1452dSArchie Cobbs #include <netgraph/ng_ether.h> 75e1e1452dSArchie Cobbs 7639b553ceSEd Maste MODULE_VERSION(ng_ether, 1); 7739b553ceSEd Maste 78833e8dc5SGleb Smirnoff #define IFP2NG(ifp) ((ifp)->if_l2com) 79e1e1452dSArchie Cobbs 803c976c3fSPawel Jakub Dawidek /* Per-node private data */ 813c976c3fSPawel Jakub Dawidek struct private { 823c976c3fSPawel Jakub Dawidek struct ifnet *ifp; /* associated interface */ 833c976c3fSPawel Jakub Dawidek hook_p upper; /* upper hook connection */ 841a292b80SArchie Cobbs hook_p lower; /* lower hook connection */ 851a292b80SArchie Cobbs hook_p orphan; /* orphan hook connection */ 863c976c3fSPawel Jakub Dawidek u_char autoSrcAddr; /* always overwrite source address */ 873c976c3fSPawel Jakub Dawidek u_char promisc; /* promiscuous mode enabled */ 883c976c3fSPawel Jakub Dawidek u_long hwassist; /* hardware checksum capabilities */ 893c976c3fSPawel Jakub Dawidek u_int flags; /* flags e.g. really die */ 903c976c3fSPawel Jakub Dawidek }; 913c976c3fSPawel Jakub Dawidek typedef struct private *priv_p; 92e1e1452dSArchie Cobbs 93edbb5246SSam Leffler /* Hook pointers used by if_ethersubr.c to callback to netgraph */ 94edbb5246SSam Leffler extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 95edbb5246SSam Leffler extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 96edbb5246SSam Leffler extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 97edbb5246SSam Leffler extern void (*ng_ether_attach_p)(struct ifnet *ifp); 98edbb5246SSam Leffler extern void (*ng_ether_detach_p)(struct ifnet *ifp); 991c7899c7SGleb Smirnoff extern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); 100edbb5246SSam Leffler 101e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */ 102edbb5246SSam Leffler static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 103edbb5246SSam Leffler static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 104e1e1452dSArchie Cobbs static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 105e1e1452dSArchie Cobbs static void ng_ether_attach(struct ifnet *ifp); 106e1e1452dSArchie Cobbs static void ng_ether_detach(struct ifnet *ifp); 1071c7899c7SGleb Smirnoff static void ng_ether_link_state(struct ifnet *ifp, int state); 108e1e1452dSArchie Cobbs 109e1e1452dSArchie Cobbs /* Other functions */ 110f664dcdeSJulian Elischer static int ng_ether_rcv_lower(hook_p node, item_p item); 111f664dcdeSJulian Elischer static int ng_ether_rcv_upper(hook_p node, item_p item); 112e1e1452dSArchie Cobbs 113e1e1452dSArchie Cobbs /* Netgraph node methods */ 114e1e1452dSArchie Cobbs static ng_constructor_t ng_ether_constructor; 115e1e1452dSArchie Cobbs static ng_rcvmsg_t ng_ether_rcvmsg; 116069154d5SJulian Elischer static ng_shutdown_t ng_ether_shutdown; 117e1e1452dSArchie Cobbs static ng_newhook_t ng_ether_newhook; 118e1e1452dSArchie Cobbs static ng_rcvdata_t ng_ether_rcvdata; 119e1e1452dSArchie Cobbs static ng_disconnect_t ng_ether_disconnect; 120e1e1452dSArchie Cobbs static int ng_ether_mod_event(module_t mod, int event, void *data); 121e1e1452dSArchie Cobbs 122499f60b1SAndriy Gapon static eventhandler_tag ng_ether_ifnet_arrival_cookie; 123499f60b1SAndriy Gapon 124e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 125e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = { 126e1e1452dSArchie Cobbs { 127e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 128e1e1452dSArchie Cobbs NGM_ETHER_GET_IFNAME, 129e1e1452dSArchie Cobbs "getifname", 130e1e1452dSArchie Cobbs NULL, 131e1e1452dSArchie Cobbs &ng_parse_string_type 132e1e1452dSArchie Cobbs }, 133e1e1452dSArchie Cobbs { 134e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 135e1e1452dSArchie Cobbs NGM_ETHER_GET_IFINDEX, 136e1e1452dSArchie Cobbs "getifindex", 137e1e1452dSArchie Cobbs NULL, 138e1e1452dSArchie Cobbs &ng_parse_int32_type 139e1e1452dSArchie Cobbs }, 1404b39c3c7SArchie Cobbs { 1414b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 1424b39c3c7SArchie Cobbs NGM_ETHER_GET_ENADDR, 1434b39c3c7SArchie Cobbs "getenaddr", 1444b39c3c7SArchie Cobbs NULL, 1458c7e4101SRuslan Ermilov &ng_parse_enaddr_type 1464b39c3c7SArchie Cobbs }, 1474b39c3c7SArchie Cobbs { 1484b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 14956045c66SArchie Cobbs NGM_ETHER_SET_ENADDR, 15056045c66SArchie Cobbs "setenaddr", 1518c7e4101SRuslan Ermilov &ng_parse_enaddr_type, 15256045c66SArchie Cobbs NULL 15356045c66SArchie Cobbs }, 15456045c66SArchie Cobbs { 15556045c66SArchie Cobbs NGM_ETHER_COOKIE, 15656045c66SArchie Cobbs NGM_ETHER_GET_PROMISC, 15756045c66SArchie Cobbs "getpromisc", 15856045c66SArchie Cobbs NULL, 15956045c66SArchie Cobbs &ng_parse_int32_type 16056045c66SArchie Cobbs }, 16156045c66SArchie Cobbs { 16256045c66SArchie Cobbs NGM_ETHER_COOKIE, 1634b39c3c7SArchie Cobbs NGM_ETHER_SET_PROMISC, 1644b39c3c7SArchie Cobbs "setpromisc", 1654b39c3c7SArchie Cobbs &ng_parse_int32_type, 1664b39c3c7SArchie Cobbs NULL 1674b39c3c7SArchie Cobbs }, 1684b39c3c7SArchie Cobbs { 1694b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 17056045c66SArchie Cobbs NGM_ETHER_GET_AUTOSRC, 17156045c66SArchie Cobbs "getautosrc", 17256045c66SArchie Cobbs NULL, 17356045c66SArchie Cobbs &ng_parse_int32_type 17456045c66SArchie Cobbs }, 17556045c66SArchie Cobbs { 17656045c66SArchie Cobbs NGM_ETHER_COOKIE, 1774b39c3c7SArchie Cobbs NGM_ETHER_SET_AUTOSRC, 1784b39c3c7SArchie Cobbs "setautosrc", 1794b39c3c7SArchie Cobbs &ng_parse_int32_type, 1804b39c3c7SArchie Cobbs NULL 1814b39c3c7SArchie Cobbs }, 182810d5e89SGleb Smirnoff { 183810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 184810d5e89SGleb Smirnoff NGM_ETHER_ADD_MULTI, 185810d5e89SGleb Smirnoff "addmulti", 186810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 187810d5e89SGleb Smirnoff NULL 188810d5e89SGleb Smirnoff }, 189810d5e89SGleb Smirnoff { 190810d5e89SGleb Smirnoff NGM_ETHER_COOKIE, 191810d5e89SGleb Smirnoff NGM_ETHER_DEL_MULTI, 192810d5e89SGleb Smirnoff "delmulti", 193810d5e89SGleb Smirnoff &ng_parse_enaddr_type, 194810d5e89SGleb Smirnoff NULL 195810d5e89SGleb Smirnoff }, 196cefddd66SGleb Smirnoff { 197cefddd66SGleb Smirnoff NGM_ETHER_COOKIE, 198cefddd66SGleb Smirnoff NGM_ETHER_DETACH, 199cefddd66SGleb Smirnoff "detach", 200cefddd66SGleb Smirnoff NULL, 201cefddd66SGleb Smirnoff NULL 202cefddd66SGleb Smirnoff }, 203e1e1452dSArchie Cobbs { 0 } 204e1e1452dSArchie Cobbs }; 205e1e1452dSArchie Cobbs 206e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = { 207f8aae777SJulian Elischer .version = NG_ABI_VERSION, 208f8aae777SJulian Elischer .name = NG_ETHER_NODE_TYPE, 209f8aae777SJulian Elischer .mod_event = ng_ether_mod_event, 210f8aae777SJulian Elischer .constructor = ng_ether_constructor, 211f8aae777SJulian Elischer .rcvmsg = ng_ether_rcvmsg, 212f8aae777SJulian Elischer .shutdown = ng_ether_shutdown, 213f8aae777SJulian Elischer .newhook = ng_ether_newhook, 214f8aae777SJulian Elischer .rcvdata = ng_ether_rcvdata, 215f8aae777SJulian Elischer .disconnect = ng_ether_disconnect, 216f8aae777SJulian Elischer .cmdlist = ng_ether_cmdlist, 217e1e1452dSArchie Cobbs }; 218e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct); 219e1e1452dSArchie Cobbs 220e1e1452dSArchie Cobbs /****************************************************************** 221499f60b1SAndriy Gapon UTILITY FUNCTIONS 222499f60b1SAndriy Gapon ******************************************************************/ 223499f60b1SAndriy Gapon static void 224499f60b1SAndriy Gapon ng_ether_sanitize_ifname(const char *ifname, char *name) 225499f60b1SAndriy Gapon { 226499f60b1SAndriy Gapon int i; 227499f60b1SAndriy Gapon 228499f60b1SAndriy Gapon for (i = 0; i < IFNAMSIZ; i++) { 229499f60b1SAndriy Gapon if (ifname[i] == '.' || ifname[i] == ':') 230499f60b1SAndriy Gapon name[i] = '_'; 231499f60b1SAndriy Gapon else 232499f60b1SAndriy Gapon name[i] = ifname[i]; 233499f60b1SAndriy Gapon if (name[i] == '\0') 234499f60b1SAndriy Gapon break; 235499f60b1SAndriy Gapon } 236499f60b1SAndriy Gapon } 237499f60b1SAndriy Gapon 238499f60b1SAndriy Gapon /****************************************************************** 239e1e1452dSArchie Cobbs ETHERNET FUNCTION HOOKS 240e1e1452dSArchie Cobbs ******************************************************************/ 241e1e1452dSArchie Cobbs 242e1e1452dSArchie Cobbs /* 243e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface. We get to 244e1e1452dSArchie Cobbs * look at it here before any upper layer protocols do. 245e1e1452dSArchie Cobbs */ 246e1e1452dSArchie Cobbs static void 247edbb5246SSam Leffler ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 248e1e1452dSArchie Cobbs { 249e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 25030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2511a292b80SArchie Cobbs int error; 252e1e1452dSArchie Cobbs 253e1e1452dSArchie Cobbs /* If "lower" hook not connected, let packet continue */ 2541a292b80SArchie Cobbs if (priv->lower == NULL) 255e1e1452dSArchie Cobbs return; 2561a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 257e1e1452dSArchie Cobbs } 258e1e1452dSArchie Cobbs 259e1e1452dSArchie Cobbs /* 260e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface, and which 261e1e1452dSArchie Cobbs * does not match any of our known protocols (an ``orphan''). 262e1e1452dSArchie Cobbs */ 263e1e1452dSArchie Cobbs static void 264edbb5246SSam Leffler ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 265e1e1452dSArchie Cobbs { 266e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 26730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 2681a292b80SArchie Cobbs int error; 269e1e1452dSArchie Cobbs 2701a292b80SArchie Cobbs /* If "orphan" hook not connected, discard packet */ 2711a292b80SArchie Cobbs if (priv->orphan == NULL) { 272e1e1452dSArchie Cobbs m_freem(m); 273e1e1452dSArchie Cobbs return; 274e1e1452dSArchie Cobbs } 2751a292b80SArchie Cobbs NG_SEND_DATA_ONLY(error, priv->orphan, m); 276e1e1452dSArchie Cobbs } 277e1e1452dSArchie Cobbs 278e1e1452dSArchie Cobbs /* 279e1e1452dSArchie Cobbs * Handle a packet that is going out on an interface. 280e1e1452dSArchie Cobbs * The Ethernet header is already attached to the mbuf. 281e1e1452dSArchie Cobbs */ 282e1e1452dSArchie Cobbs static int 283e1e1452dSArchie Cobbs ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 284e1e1452dSArchie Cobbs { 285e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 28630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 287e1e1452dSArchie Cobbs int error = 0; 288e1e1452dSArchie Cobbs 289e1e1452dSArchie Cobbs /* If "upper" hook not connected, let packet continue */ 290e1e1452dSArchie Cobbs if (priv->upper == NULL) 291e1e1452dSArchie Cobbs return (0); 292e1e1452dSArchie Cobbs 293e1e1452dSArchie Cobbs /* Send it out "upper" hook */ 294f089869fSMarko Zec NG_OUTBOUND_THREAD_REF(); 295069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->upper, *mp); 296f089869fSMarko Zec NG_OUTBOUND_THREAD_UNREF(); 297e1e1452dSArchie Cobbs return (error); 298e1e1452dSArchie Cobbs } 299e1e1452dSArchie Cobbs 300e1e1452dSArchie Cobbs /* 301e1e1452dSArchie Cobbs * A new Ethernet interface has been attached. 302e1e1452dSArchie Cobbs * Create a new node for it, etc. 303e1e1452dSArchie Cobbs */ 304e1e1452dSArchie Cobbs static void 305e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp) 306e1e1452dSArchie Cobbs { 307499f60b1SAndriy Gapon char name[IFNAMSIZ]; 308e1e1452dSArchie Cobbs priv_p priv; 309e1e1452dSArchie Cobbs node_p node; 310e1e1452dSArchie Cobbs 311aef8f344SMarko Zec /* 312aef8f344SMarko Zec * Do not create / attach an ether node to this ifnet if 313aef8f344SMarko Zec * a netgraph node with the same name already exists. 314aef8f344SMarko Zec * This should prevent ether nodes to become attached to 315aef8f344SMarko Zec * eiface nodes, which may be problematic due to naming 316aef8f344SMarko Zec * clashes. 317aef8f344SMarko Zec */ 31802fd7b50SLuiz Otavio O Souza ng_ether_sanitize_ifname(ifp->if_xname, name); 31902fd7b50SLuiz Otavio O Souza if ((node = ng_name2noderef(NULL, name)) != NULL) { 320aef8f344SMarko Zec NG_NODE_UNREF(node); 321aef8f344SMarko Zec return; 322aef8f344SMarko Zec } 323aef8f344SMarko Zec 324e1e1452dSArchie Cobbs /* Create node */ 3256e551fb6SDavid E. O'Brien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 326e1e1452dSArchie Cobbs if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 327e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3289bf40edeSBrooks Davis __func__, "create node", ifp->if_xname); 329e1e1452dSArchie Cobbs return; 330e1e1452dSArchie Cobbs } 331e1e1452dSArchie Cobbs 332e1e1452dSArchie Cobbs /* Allocate private data */ 3331ede983cSDag-Erling Smørgrav priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 334e1e1452dSArchie Cobbs if (priv == NULL) { 335e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3369bf40edeSBrooks Davis __func__, "allocate memory", ifp->if_xname); 33730400f03SJulian Elischer NG_NODE_UNREF(node); 338e1e1452dSArchie Cobbs return; 339e1e1452dSArchie Cobbs } 34030400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 341e1e1452dSArchie Cobbs priv->ifp = ifp; 3425240dcdbSRuslan Ermilov IFP2NG(ifp) = node; 343a3e232d6SArchie Cobbs priv->hwassist = ifp->if_hwassist; 344e1e1452dSArchie Cobbs 345e1e1452dSArchie Cobbs /* Try to give the node the same name as the interface */ 346499f60b1SAndriy Gapon if (ng_name_node(node, name) != 0) 347499f60b1SAndriy Gapon log(LOG_WARNING, "%s: can't name node %s\n", __func__, name); 348e1e1452dSArchie Cobbs } 349e1e1452dSArchie Cobbs 350e1e1452dSArchie Cobbs /* 351e1e1452dSArchie Cobbs * An Ethernet interface is being detached. 3521acb27c6SJulian Elischer * REALLY Destroy its node. 353e1e1452dSArchie Cobbs */ 354e1e1452dSArchie Cobbs static void 355e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp) 356e1e1452dSArchie Cobbs { 357e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 3581acb27c6SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 359e1e1452dSArchie Cobbs 3602cdf8c49SMarko Zec taskqueue_drain(taskqueue_swi, &ifp->if_linktask); 3611acb27c6SJulian Elischer NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 3621acb27c6SJulian Elischer /* 3631acb27c6SJulian Elischer * We can't assume the ifnet is still around when we run shutdown 3641acb27c6SJulian Elischer * So zap it now. XXX We HOPE that anything running at this time 3651acb27c6SJulian Elischer * handles it (as it should in the non netgraph case). 3661acb27c6SJulian Elischer */ 3675240dcdbSRuslan Ermilov IFP2NG(ifp) = NULL; 3681acb27c6SJulian Elischer priv->ifp = NULL; /* XXX race if interrupted an output packet */ 3691acb27c6SJulian Elischer ng_rmnode_self(node); /* remove all netgraph parts */ 370e1e1452dSArchie Cobbs } 371e1e1452dSArchie Cobbs 3721c7899c7SGleb Smirnoff /* 3731c7899c7SGleb Smirnoff * Notify graph about link event. 3741c7899c7SGleb Smirnoff * if_link_state_change() has already checked that the state has changed. 3751c7899c7SGleb Smirnoff */ 3761c7899c7SGleb Smirnoff static void 3771c7899c7SGleb Smirnoff ng_ether_link_state(struct ifnet *ifp, int state) 3781c7899c7SGleb Smirnoff { 3791c7899c7SGleb Smirnoff const node_p node = IFP2NG(ifp); 3801c7899c7SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 3811c7899c7SGleb Smirnoff struct ng_mesg *msg; 3821c7899c7SGleb Smirnoff int cmd, dummy_error = 0; 3831c7899c7SGleb Smirnoff 3841c7899c7SGleb Smirnoff if (state == LINK_STATE_UP) 3851c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_UP; 3861c7899c7SGleb Smirnoff else if (state == LINK_STATE_DOWN) 3871c7899c7SGleb Smirnoff cmd = NGM_LINK_IS_DOWN; 3881c7899c7SGleb Smirnoff else 3891c7899c7SGleb Smirnoff return; 3901c7899c7SGleb Smirnoff 391bb027e06SMax Khon if (priv->lower != NULL) { 3921c7899c7SGleb Smirnoff NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 3931c7899c7SGleb Smirnoff if (msg != NULL) 3941c7899c7SGleb Smirnoff NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); 3951c7899c7SGleb Smirnoff } 396bb027e06SMax Khon if (priv->orphan != NULL) { 397bb027e06SMax Khon NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); 398bb027e06SMax Khon if (msg != NULL) 399bb027e06SMax Khon NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0); 400bb027e06SMax Khon } 401bb027e06SMax Khon } 4021c7899c7SGleb Smirnoff 403499f60b1SAndriy Gapon /* 404499f60b1SAndriy Gapon * Interface arrival notification handler. 405499f60b1SAndriy Gapon * The notification is produced in two cases: 406499f60b1SAndriy Gapon * o a new interface arrives 407499f60b1SAndriy Gapon * o an existing interface got renamed 408499f60b1SAndriy Gapon * Currently the first case is handled by ng_ether_attach via special 409499f60b1SAndriy Gapon * hook ng_ether_attach_p. 410499f60b1SAndriy Gapon */ 411499f60b1SAndriy Gapon static void 412499f60b1SAndriy Gapon ng_ether_ifnet_arrival_event(void *arg __unused, struct ifnet *ifp) 413499f60b1SAndriy Gapon { 414499f60b1SAndriy Gapon char name[IFNAMSIZ]; 41530bc1032SAndriy Gapon node_p node; 41630bc1032SAndriy Gapon 41730bc1032SAndriy Gapon /* Only ethernet interfaces are of interest. */ 418d653b188SYoshihiro Takahashi if (ifp->if_type != IFT_ETHER && 419d653b188SYoshihiro Takahashi ifp->if_type != IFT_L2VLAN && 420d653b188SYoshihiro Takahashi ifp->if_type != IFT_BRIDGE) 42130bc1032SAndriy Gapon return; 422499f60b1SAndriy Gapon 423499f60b1SAndriy Gapon /* 424499f60b1SAndriy Gapon * Just return if it's a new interface without an ng_ether companion. 425499f60b1SAndriy Gapon */ 42630bc1032SAndriy Gapon node = IFP2NG(ifp); 427499f60b1SAndriy Gapon if (node == NULL) 428499f60b1SAndriy Gapon return; 429499f60b1SAndriy Gapon 430499f60b1SAndriy Gapon /* Try to give the node the same name as the new interface name */ 431499f60b1SAndriy Gapon ng_ether_sanitize_ifname(ifp->if_xname, name); 432499f60b1SAndriy Gapon if (ng_name_node(node, name) != 0) 433499f60b1SAndriy Gapon log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name); 434499f60b1SAndriy Gapon } 435499f60b1SAndriy Gapon 436e1e1452dSArchie Cobbs /****************************************************************** 437e1e1452dSArchie Cobbs NETGRAPH NODE METHODS 438e1e1452dSArchie Cobbs ******************************************************************/ 439e1e1452dSArchie Cobbs 440e1e1452dSArchie Cobbs /* 441e1e1452dSArchie Cobbs * It is not possible or allowable to create a node of this type. 442e1e1452dSArchie Cobbs * Nodes get created when the interface is attached (or, when 443e1e1452dSArchie Cobbs * this node type's KLD is loaded). 444e1e1452dSArchie Cobbs */ 445e1e1452dSArchie Cobbs static int 446069154d5SJulian Elischer ng_ether_constructor(node_p node) 447e1e1452dSArchie Cobbs { 448e1e1452dSArchie Cobbs return (EINVAL); 449e1e1452dSArchie Cobbs } 450e1e1452dSArchie Cobbs 451e1e1452dSArchie Cobbs /* 452e1e1452dSArchie Cobbs * Check for attaching a new hook. 453e1e1452dSArchie Cobbs */ 454e1e1452dSArchie Cobbs static int 455e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name) 456e1e1452dSArchie Cobbs { 45730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 458e1e1452dSArchie Cobbs hook_p *hookptr; 459e1e1452dSArchie Cobbs 460e1e1452dSArchie Cobbs /* Divert hook is an alias for lower */ 461e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 462e1e1452dSArchie Cobbs name = NG_ETHER_HOOK_LOWER; 463e1e1452dSArchie Cobbs 464e1e1452dSArchie Cobbs /* Which hook? */ 465f664dcdeSJulian Elischer if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) { 466e1e1452dSArchie Cobbs hookptr = &priv->upper; 467f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper); 468f089869fSMarko Zec NG_HOOK_SET_TO_INBOUND(hook); 469f664dcdeSJulian Elischer } else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 470e1e1452dSArchie Cobbs hookptr = &priv->lower; 471f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 472f664dcdeSJulian Elischer } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 4731a292b80SArchie Cobbs hookptr = &priv->orphan; 474f664dcdeSJulian Elischer NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower); 475f664dcdeSJulian Elischer } else 476e1e1452dSArchie Cobbs return (EINVAL); 477e1e1452dSArchie Cobbs 478e1e1452dSArchie Cobbs /* Check if already connected (shouldn't be, but doesn't hurt) */ 479e1e1452dSArchie Cobbs if (*hookptr != NULL) 480e1e1452dSArchie Cobbs return (EISCONN); 481e1e1452dSArchie Cobbs 482a3e232d6SArchie Cobbs /* Disable hardware checksums while 'upper' hook is connected */ 483a3e232d6SArchie Cobbs if (hookptr == &priv->upper) 484a3e232d6SArchie Cobbs priv->ifp->if_hwassist = 0; 485c1b8a9edSAlexander Motin NG_HOOK_HI_STACK(hook); 486e1e1452dSArchie Cobbs /* OK */ 487e1e1452dSArchie Cobbs *hookptr = hook; 488e1e1452dSArchie Cobbs return (0); 489e1e1452dSArchie Cobbs } 490e1e1452dSArchie Cobbs 491e1e1452dSArchie Cobbs /* 492e1e1452dSArchie Cobbs * Receive an incoming control message. 493e1e1452dSArchie Cobbs */ 494e1e1452dSArchie Cobbs static int 495069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 496e1e1452dSArchie Cobbs { 49730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 498e1e1452dSArchie Cobbs struct ng_mesg *resp = NULL; 499e1e1452dSArchie Cobbs int error = 0; 500069154d5SJulian Elischer struct ng_mesg *msg; 501e1e1452dSArchie Cobbs 502069154d5SJulian Elischer NGI_GET_MSG(item, msg); 503e1e1452dSArchie Cobbs switch (msg->header.typecookie) { 504e1e1452dSArchie Cobbs case NGM_ETHER_COOKIE: 505e1e1452dSArchie Cobbs switch (msg->header.cmd) { 506e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFNAME: 507bbb75d78SRuslan Ermilov NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 508e1e1452dSArchie Cobbs if (resp == NULL) { 509e1e1452dSArchie Cobbs error = ENOMEM; 510e1e1452dSArchie Cobbs break; 511e1e1452dSArchie Cobbs } 512bbb75d78SRuslan Ermilov strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 513e1e1452dSArchie Cobbs break; 514e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFINDEX: 515e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 516e1e1452dSArchie Cobbs if (resp == NULL) { 517e1e1452dSArchie Cobbs error = ENOMEM; 518e1e1452dSArchie Cobbs break; 519e1e1452dSArchie Cobbs } 520e1e1452dSArchie Cobbs *((u_int32_t *)resp->data) = priv->ifp->if_index; 521e1e1452dSArchie Cobbs break; 5224b39c3c7SArchie Cobbs case NGM_ETHER_GET_ENADDR: 5234b39c3c7SArchie Cobbs NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 5244b39c3c7SArchie Cobbs if (resp == NULL) { 5254b39c3c7SArchie Cobbs error = ENOMEM; 5264b39c3c7SArchie Cobbs break; 5274b39c3c7SArchie Cobbs } 5284a0d6638SRuslan Ermilov bcopy(IF_LLADDR(priv->ifp), 5294b39c3c7SArchie Cobbs resp->data, ETHER_ADDR_LEN); 5304b39c3c7SArchie Cobbs break; 53156045c66SArchie Cobbs case NGM_ETHER_SET_ENADDR: 53256045c66SArchie Cobbs { 53356045c66SArchie Cobbs if (msg->header.arglen != ETHER_ADDR_LEN) { 53456045c66SArchie Cobbs error = EINVAL; 53556045c66SArchie Cobbs break; 53656045c66SArchie Cobbs } 53756045c66SArchie Cobbs error = if_setlladdr(priv->ifp, 53856045c66SArchie Cobbs (u_char *)msg->data, ETHER_ADDR_LEN); 53956045c66SArchie Cobbs break; 54056045c66SArchie Cobbs } 54156045c66SArchie Cobbs case NGM_ETHER_GET_PROMISC: 54256045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 54356045c66SArchie Cobbs if (resp == NULL) { 54456045c66SArchie Cobbs error = ENOMEM; 54556045c66SArchie Cobbs break; 54656045c66SArchie Cobbs } 54756045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->promisc; 54856045c66SArchie Cobbs break; 5494b39c3c7SArchie Cobbs case NGM_ETHER_SET_PROMISC: 5504b39c3c7SArchie Cobbs { 5514b39c3c7SArchie Cobbs u_char want; 5524b39c3c7SArchie Cobbs 5534b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5544b39c3c7SArchie Cobbs error = EINVAL; 5554b39c3c7SArchie Cobbs break; 5564b39c3c7SArchie Cobbs } 5574b39c3c7SArchie Cobbs want = !!*((u_int32_t *)msg->data); 5584b39c3c7SArchie Cobbs if (want ^ priv->promisc) { 5594b39c3c7SArchie Cobbs if ((error = ifpromisc(priv->ifp, want)) != 0) 5604b39c3c7SArchie Cobbs break; 5614b39c3c7SArchie Cobbs priv->promisc = want; 5624b39c3c7SArchie Cobbs } 5634b39c3c7SArchie Cobbs break; 5644b39c3c7SArchie Cobbs } 56556045c66SArchie Cobbs case NGM_ETHER_GET_AUTOSRC: 56656045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 56756045c66SArchie Cobbs if (resp == NULL) { 56856045c66SArchie Cobbs error = ENOMEM; 56956045c66SArchie Cobbs break; 57056045c66SArchie Cobbs } 57156045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->autoSrcAddr; 57256045c66SArchie Cobbs break; 5734b39c3c7SArchie Cobbs case NGM_ETHER_SET_AUTOSRC: 5744b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5754b39c3c7SArchie Cobbs error = EINVAL; 5764b39c3c7SArchie Cobbs break; 5774b39c3c7SArchie Cobbs } 5784b39c3c7SArchie Cobbs priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 5794b39c3c7SArchie Cobbs break; 580810d5e89SGleb Smirnoff case NGM_ETHER_ADD_MULTI: 581810d5e89SGleb Smirnoff { 582810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 58357985d11SGleb Smirnoff struct epoch_tracker et; 584ec002feeSBruce M Simpson struct ifmultiaddr *ifma; 585810d5e89SGleb Smirnoff 586810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 587810d5e89SGleb Smirnoff error = EINVAL; 588810d5e89SGleb Smirnoff break; 589810d5e89SGleb Smirnoff } 590ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 591810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 592810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 593ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 594810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 595810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 596ec002feeSBruce M Simpson /* 597ec002feeSBruce M Simpson * Netgraph is only permitted to join groups once 598ec002feeSBruce M Simpson * via the if_addmulti() KPI, because it cannot hold 599ec002feeSBruce M Simpson * struct ifmultiaddr * between calls. It may also 600ec002feeSBruce M Simpson * lose a race while we check if the membership 601ec002feeSBruce M Simpson * already exists. 602ec002feeSBruce M Simpson */ 60357985d11SGleb Smirnoff NET_EPOCH_ENTER(et); 604ec002feeSBruce M Simpson ifma = if_findmulti(priv->ifp, 605ec002feeSBruce M Simpson (struct sockaddr *)&sa_dl); 60657985d11SGleb Smirnoff NET_EPOCH_EXIT(et); 607ec002feeSBruce M Simpson if (ifma != NULL) { 608ec002feeSBruce M Simpson error = EADDRINUSE; 609ec002feeSBruce M Simpson } else { 610810d5e89SGleb Smirnoff error = if_addmulti(priv->ifp, 611ec002feeSBruce M Simpson (struct sockaddr *)&sa_dl, &ifma); 612ec002feeSBruce M Simpson } 613810d5e89SGleb Smirnoff break; 614810d5e89SGleb Smirnoff } 615810d5e89SGleb Smirnoff case NGM_ETHER_DEL_MULTI: 616810d5e89SGleb Smirnoff { 617810d5e89SGleb Smirnoff struct sockaddr_dl sa_dl; 618810d5e89SGleb Smirnoff 619810d5e89SGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) { 620810d5e89SGleb Smirnoff error = EINVAL; 621810d5e89SGleb Smirnoff break; 622810d5e89SGleb Smirnoff } 623ba20540eSGleb Smirnoff bzero(&sa_dl, sizeof(struct sockaddr_dl)); 624810d5e89SGleb Smirnoff sa_dl.sdl_len = sizeof(struct sockaddr_dl); 625810d5e89SGleb Smirnoff sa_dl.sdl_family = AF_LINK; 626ba20540eSGleb Smirnoff sa_dl.sdl_alen = ETHER_ADDR_LEN; 627810d5e89SGleb Smirnoff bcopy((void *)msg->data, LLADDR(&sa_dl), 628810d5e89SGleb Smirnoff ETHER_ADDR_LEN); 629810d5e89SGleb Smirnoff error = if_delmulti(priv->ifp, 630810d5e89SGleb Smirnoff (struct sockaddr *)&sa_dl); 631810d5e89SGleb Smirnoff break; 632810d5e89SGleb Smirnoff } 633cefddd66SGleb Smirnoff case NGM_ETHER_DETACH: 634cefddd66SGleb Smirnoff ng_ether_detach(priv->ifp); 635cefddd66SGleb Smirnoff break; 636e1e1452dSArchie Cobbs default: 637e1e1452dSArchie Cobbs error = EINVAL; 638e1e1452dSArchie Cobbs break; 639e1e1452dSArchie Cobbs } 640e1e1452dSArchie Cobbs break; 641e1e1452dSArchie Cobbs default: 642e1e1452dSArchie Cobbs error = EINVAL; 643e1e1452dSArchie Cobbs break; 644e1e1452dSArchie Cobbs } 645069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 646069154d5SJulian Elischer NG_FREE_MSG(msg); 647e1e1452dSArchie Cobbs return (error); 648e1e1452dSArchie Cobbs } 649e1e1452dSArchie Cobbs 650e1e1452dSArchie Cobbs /* 651e1e1452dSArchie Cobbs * Receive data on a hook. 652f664dcdeSJulian Elischer * Since we use per-hook recveive methods this should never be called. 653e1e1452dSArchie Cobbs */ 654e1e1452dSArchie Cobbs static int 655069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item) 656e1e1452dSArchie Cobbs { 657069154d5SJulian Elischer NG_FREE_ITEM(item); 6583ca24c28SJulian Elischer 6596e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 660e1e1452dSArchie Cobbs } 661e1e1452dSArchie Cobbs 662e1e1452dSArchie Cobbs /* 6631a292b80SArchie Cobbs * Handle an mbuf received on the "lower" or "orphan" hook. 664e1e1452dSArchie Cobbs */ 665e1e1452dSArchie Cobbs static int 666f664dcdeSJulian Elischer ng_ether_rcv_lower(hook_p hook, item_p item) 667e1e1452dSArchie Cobbs { 668f664dcdeSJulian Elischer struct mbuf *m; 669f664dcdeSJulian Elischer const node_p node = NG_HOOK_NODE(hook); 67030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 671a1479aa2SArchie Cobbs struct ifnet *const ifp = priv->ifp; 672a1479aa2SArchie Cobbs 673f664dcdeSJulian Elischer NGI_GET_M(item, m); 674f664dcdeSJulian Elischer NG_FREE_ITEM(item); 675f664dcdeSJulian Elischer 676a1479aa2SArchie Cobbs /* Check whether interface is ready for packets */ 677f664dcdeSJulian Elischer 67813f4c340SRobert Watson if (!((ifp->if_flags & IFF_UP) && 67913f4c340SRobert Watson (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 680a1479aa2SArchie Cobbs NG_FREE_M(m); 681a1479aa2SArchie Cobbs return (ENETDOWN); 682a1479aa2SArchie Cobbs } 683e1e1452dSArchie Cobbs 684e1e1452dSArchie Cobbs /* Make sure header is fully pulled up */ 685e1e1452dSArchie Cobbs if (m->m_pkthdr.len < sizeof(struct ether_header)) { 686069154d5SJulian Elischer NG_FREE_M(m); 687e1e1452dSArchie Cobbs return (EINVAL); 688e1e1452dSArchie Cobbs } 689e1e1452dSArchie Cobbs if (m->m_len < sizeof(struct ether_header) 6907b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 691e1e1452dSArchie Cobbs return (ENOBUFS); 692e1e1452dSArchie Cobbs 6934b39c3c7SArchie Cobbs /* Drop in the MAC address if desired */ 6944b39c3c7SArchie Cobbs if (priv->autoSrcAddr) { 6957b9f235fSArchie Cobbs /* Make the mbuf writable if it's not already */ 6967b9f235fSArchie Cobbs if (!M_WRITABLE(m) 6977b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 6987b9f235fSArchie Cobbs return (ENOBUFS); 6997b9f235fSArchie Cobbs 7007b9f235fSArchie Cobbs /* Overwrite source MAC address */ 7014a0d6638SRuslan Ermilov bcopy(IF_LLADDR(ifp), 7024b39c3c7SArchie Cobbs mtod(m, struct ether_header *)->ether_shost, 7034b39c3c7SArchie Cobbs ETHER_ADDR_LEN); 7044b39c3c7SArchie Cobbs } 705561e4fb9SJulian Elischer 706e1e1452dSArchie Cobbs /* Send it on its way */ 707a1479aa2SArchie Cobbs return ether_output_frame(ifp, m); 708e1e1452dSArchie Cobbs } 709e1e1452dSArchie Cobbs 710e1e1452dSArchie Cobbs /* 711e1e1452dSArchie Cobbs * Handle an mbuf received on the "upper" hook. 712e1e1452dSArchie Cobbs */ 713e1e1452dSArchie Cobbs static int 714f664dcdeSJulian Elischer ng_ether_rcv_upper(hook_p hook, item_p item) 715e1e1452dSArchie Cobbs { 716f664dcdeSJulian Elischer struct mbuf *m; 717f664dcdeSJulian Elischer const node_p node = NG_HOOK_NODE(hook); 71830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 7196512768bSGleb Smirnoff struct ifnet *ifp = priv->ifp; 720e1e1452dSArchie Cobbs 721f664dcdeSJulian Elischer NGI_GET_M(item, m); 722f664dcdeSJulian Elischer NG_FREE_ITEM(item); 723f664dcdeSJulian Elischer 724c60c00bcSRuslan Ermilov /* Check length and pull off header */ 725c60c00bcSRuslan Ermilov if (m->m_pkthdr.len < sizeof(struct ether_header)) { 726c60c00bcSRuslan Ermilov NG_FREE_M(m); 727c60c00bcSRuslan Ermilov return (EINVAL); 728c60c00bcSRuslan Ermilov } 729c60c00bcSRuslan Ermilov if (m->m_len < sizeof(struct ether_header) && 730c60c00bcSRuslan Ermilov (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 731c60c00bcSRuslan Ermilov return (ENOBUFS); 732c60c00bcSRuslan Ermilov 7336512768bSGleb Smirnoff m->m_pkthdr.rcvif = ifp; 734e1e1452dSArchie Cobbs 735fd6238a6SAndrew Thompson /* Pass the packet to the bridge, it may come back to us */ 7366512768bSGleb Smirnoff if (ifp->if_bridge) { 737fd6238a6SAndrew Thompson BRIDGE_INPUT(ifp, m); 7386512768bSGleb Smirnoff if (m == NULL) 7396512768bSGleb Smirnoff return (0); 7406512768bSGleb Smirnoff } 741a176c2aeSGleb Smirnoff 742e1e1452dSArchie Cobbs /* Route packet back in */ 743fd6238a6SAndrew Thompson ether_demux(ifp, m); 744e1e1452dSArchie Cobbs return (0); 745e1e1452dSArchie Cobbs } 746e1e1452dSArchie Cobbs 747e1e1452dSArchie Cobbs /* 7481acb27c6SJulian Elischer * Shutdown node. This resets the node but does not remove it 7491acb27c6SJulian Elischer * unless the REALLY_DIE flag is set. 750e1e1452dSArchie Cobbs */ 751e1e1452dSArchie Cobbs static int 752069154d5SJulian Elischer ng_ether_shutdown(node_p node) 753e1e1452dSArchie Cobbs { 75430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 7554b39c3c7SArchie Cobbs 756be4252b3SJulian Elischer if (node->nd_flags & NGF_REALLY_DIE) { 7571acb27c6SJulian Elischer /* 758cd698c51SMark Johnston * The ifnet is going away, perhaps because the driver was 759cd698c51SMark Johnston * unloaded or its vnet is being torn down. 7601acb27c6SJulian Elischer */ 7611acb27c6SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 762cd698c51SMark Johnston if (priv->ifp != NULL) 763cd698c51SMark Johnston IFP2NG(priv->ifp) = NULL; 7641ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 7651acb27c6SJulian Elischer NG_NODE_UNREF(node); /* free node itself */ 7661acb27c6SJulian Elischer return (0); 767069154d5SJulian Elischer } 768018df1c3SBrian Feldman if (priv->promisc) { /* disable promiscuous mode */ 769018df1c3SBrian Feldman (void)ifpromisc(priv->ifp, 0); 770018df1c3SBrian Feldman priv->promisc = 0; 771018df1c3SBrian Feldman } 772be4252b3SJulian Elischer NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ 773be4252b3SJulian Elischer 774e1e1452dSArchie Cobbs return (0); 775e1e1452dSArchie Cobbs } 776e1e1452dSArchie Cobbs 777e1e1452dSArchie Cobbs /* 778e1e1452dSArchie Cobbs * Hook disconnection. 779e1e1452dSArchie Cobbs */ 780e1e1452dSArchie Cobbs static int 781e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook) 782e1e1452dSArchie Cobbs { 78330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 784e1e1452dSArchie Cobbs 785a3e232d6SArchie Cobbs if (hook == priv->upper) { 786e1e1452dSArchie Cobbs priv->upper = NULL; 787b712e9ecSBrian Feldman if (priv->ifp != NULL) /* restore h/w csum */ 788b712e9ecSBrian Feldman priv->ifp->if_hwassist = priv->hwassist; 7891a292b80SArchie Cobbs } else if (hook == priv->lower) 790e1e1452dSArchie Cobbs priv->lower = NULL; 7911a292b80SArchie Cobbs else if (hook == priv->orphan) 7921a292b80SArchie Cobbs priv->orphan = NULL; 7931a292b80SArchie Cobbs else 7946e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 79530400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 79630400f03SJulian Elischer && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 79730400f03SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 798e1e1452dSArchie Cobbs return (0); 799e1e1452dSArchie Cobbs } 800e1e1452dSArchie Cobbs 801e1e1452dSArchie Cobbs /****************************************************************** 802e1e1452dSArchie Cobbs INITIALIZATION 803e1e1452dSArchie Cobbs ******************************************************************/ 804e1e1452dSArchie Cobbs 805e1e1452dSArchie Cobbs /* 806e1e1452dSArchie Cobbs * Handle loading and unloading for this node type. 807e1e1452dSArchie Cobbs */ 808e1e1452dSArchie Cobbs static int 809e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data) 810e1e1452dSArchie Cobbs { 811e1e1452dSArchie Cobbs int error = 0; 812e1e1452dSArchie Cobbs 813e1e1452dSArchie Cobbs switch (event) { 814e1e1452dSArchie Cobbs case MOD_LOAD: 815e1e1452dSArchie Cobbs 816e1e1452dSArchie Cobbs /* Register function hooks */ 817e1e1452dSArchie Cobbs if (ng_ether_attach_p != NULL) { 818e1e1452dSArchie Cobbs error = EEXIST; 819e1e1452dSArchie Cobbs break; 820e1e1452dSArchie Cobbs } 821e1e1452dSArchie Cobbs ng_ether_attach_p = ng_ether_attach; 822e1e1452dSArchie Cobbs ng_ether_detach_p = ng_ether_detach; 823e1e1452dSArchie Cobbs ng_ether_output_p = ng_ether_output; 824e1e1452dSArchie Cobbs ng_ether_input_p = ng_ether_input; 825e1e1452dSArchie Cobbs ng_ether_input_orphan_p = ng_ether_input_orphan; 8261c7899c7SGleb Smirnoff ng_ether_link_state_p = ng_ether_link_state; 827e1e1452dSArchie Cobbs 828499f60b1SAndriy Gapon ng_ether_ifnet_arrival_cookie = 829499f60b1SAndriy Gapon EVENTHANDLER_REGISTER(ifnet_arrival_event, 830499f60b1SAndriy Gapon ng_ether_ifnet_arrival_event, NULL, EVENTHANDLER_PRI_ANY); 831e1e1452dSArchie Cobbs break; 832e1e1452dSArchie Cobbs 833e1e1452dSArchie Cobbs case MOD_UNLOAD: 834e1e1452dSArchie Cobbs 835e1e1452dSArchie Cobbs /* 836e1e1452dSArchie Cobbs * Note that the base code won't try to unload us until 837e1e1452dSArchie Cobbs * all nodes have been removed, and that can't happen 838e1e1452dSArchie Cobbs * until all Ethernet interfaces are removed. In any 839e1e1452dSArchie Cobbs * case, we know there are no nodes left if the action 840e1e1452dSArchie Cobbs * is MOD_UNLOAD, so there's no need to detach any nodes. 841e1e1452dSArchie Cobbs */ 842e1e1452dSArchie Cobbs 843499f60b1SAndriy Gapon EVENTHANDLER_DEREGISTER(ifnet_arrival_event, 844499f60b1SAndriy Gapon ng_ether_ifnet_arrival_cookie); 845499f60b1SAndriy Gapon 846e1e1452dSArchie Cobbs /* Unregister function hooks */ 847e1e1452dSArchie Cobbs ng_ether_attach_p = NULL; 848e1e1452dSArchie Cobbs ng_ether_detach_p = NULL; 849e1e1452dSArchie Cobbs ng_ether_output_p = NULL; 850e1e1452dSArchie Cobbs ng_ether_input_p = NULL; 851e1e1452dSArchie Cobbs ng_ether_input_orphan_p = NULL; 8521c7899c7SGleb Smirnoff ng_ether_link_state_p = NULL; 853e1e1452dSArchie Cobbs break; 854e1e1452dSArchie Cobbs 855e1e1452dSArchie Cobbs default: 856e1e1452dSArchie Cobbs error = EOPNOTSUPP; 857e1e1452dSArchie Cobbs break; 858e1e1452dSArchie Cobbs } 859e1e1452dSArchie Cobbs return (error); 860e1e1452dSArchie Cobbs } 861e1e1452dSArchie Cobbs 862d0728d71SRobert Watson static void 863d0728d71SRobert Watson vnet_ng_ether_init(const void *unused) 864aef8f344SMarko Zec { 865aef8f344SMarko Zec struct ifnet *ifp; 866aef8f344SMarko Zec 867d0728d71SRobert Watson /* If module load was rejected, don't attach to vnets. */ 868d0728d71SRobert Watson if (ng_ether_attach_p != ng_ether_attach) 869d0728d71SRobert Watson return; 870d0728d71SRobert Watson 871aef8f344SMarko Zec /* Create nodes for any already-existing Ethernet interfaces. */ 872aef8f344SMarko Zec IFNET_RLOCK(); 8734560b78dSJohn Baldwin CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { 874d653b188SYoshihiro Takahashi if (ifp->if_type == IFT_ETHER || 875d653b188SYoshihiro Takahashi ifp->if_type == IFT_L2VLAN || 876d653b188SYoshihiro Takahashi ifp->if_type == IFT_BRIDGE) 877aef8f344SMarko Zec ng_ether_attach(ifp); 878aef8f344SMarko Zec } 879aef8f344SMarko Zec IFNET_RUNLOCK(); 880aef8f344SMarko Zec } 881d0728d71SRobert Watson VNET_SYSINIT(vnet_ng_ether_init, SI_SUB_PSEUDO, SI_ORDER_ANY, 882d0728d71SRobert Watson vnet_ng_ether_init, NULL); 883