1e1e1452dSArchie Cobbs 2e1e1452dSArchie Cobbs /* 3e1e1452dSArchie Cobbs * ng_ether.c 4e1e1452dSArchie Cobbs * 5e1e1452dSArchie Cobbs * Copyright (c) 1996-2000 Whistle Communications, Inc. 6e1e1452dSArchie Cobbs * All rights reserved. 7e1e1452dSArchie Cobbs * 8e1e1452dSArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 9e1e1452dSArchie Cobbs * redistribution of this software, in source or object code forms, with or 10e1e1452dSArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 11e1e1452dSArchie Cobbs * provided, however, that: 12e1e1452dSArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 13e1e1452dSArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 14e1e1452dSArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 15e1e1452dSArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 16e1e1452dSArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17e1e1452dSArchie Cobbs * such appears in the above copyright notice or in the software. 18e1e1452dSArchie Cobbs * 19e1e1452dSArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20e1e1452dSArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21e1e1452dSArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22e1e1452dSArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23e1e1452dSArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24e1e1452dSArchie Cobbs * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25e1e1452dSArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26e1e1452dSArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27e1e1452dSArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28e1e1452dSArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29e1e1452dSArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30e1e1452dSArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31e1e1452dSArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32e1e1452dSArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33e1e1452dSArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34e1e1452dSArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35e1e1452dSArchie Cobbs * OF SUCH DAMAGE. 36e1e1452dSArchie Cobbs * 37e1e1452dSArchie Cobbs * Authors: Archie Cobbs <archie@freebsd.org> 38e1e1452dSArchie Cobbs * Julian Elischer <julian@freebsd.org> 39e1e1452dSArchie Cobbs * 40e1e1452dSArchie Cobbs * $FreeBSD$ 41e1e1452dSArchie Cobbs */ 42e1e1452dSArchie Cobbs 43e1e1452dSArchie Cobbs /* 44e1e1452dSArchie Cobbs * ng_ether(4) netgraph node type 45e1e1452dSArchie Cobbs */ 46e1e1452dSArchie Cobbs 47e1e1452dSArchie Cobbs #include <sys/param.h> 48e1e1452dSArchie Cobbs #include <sys/systm.h> 49e1e1452dSArchie Cobbs #include <sys/kernel.h> 50e1e1452dSArchie Cobbs #include <sys/malloc.h> 51e1e1452dSArchie Cobbs #include <sys/mbuf.h> 52e1e1452dSArchie Cobbs #include <sys/errno.h> 53e1e1452dSArchie Cobbs #include <sys/syslog.h> 54e1e1452dSArchie Cobbs #include <sys/socket.h> 55e1e1452dSArchie Cobbs 56e1e1452dSArchie Cobbs #include <net/if.h> 57e1e1452dSArchie Cobbs #include <net/if_types.h> 58e1e1452dSArchie Cobbs #include <net/if_arp.h> 59e1e1452dSArchie Cobbs #include <net/if_var.h> 60e1e1452dSArchie Cobbs #include <net/ethernet.h> 61e1e1452dSArchie Cobbs 62e1e1452dSArchie Cobbs #include <netgraph/ng_message.h> 63e1e1452dSArchie Cobbs #include <netgraph/netgraph.h> 64e1e1452dSArchie Cobbs #include <netgraph/ng_parse.h> 65e1e1452dSArchie Cobbs #include <netgraph/ng_ether.h> 66e1e1452dSArchie Cobbs 67561e4fb9SJulian Elischer #define IFP2AC(IFP) ((struct arpcom *)IFP) 68e1e1452dSArchie Cobbs #define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) 69e1e1452dSArchie Cobbs 703c976c3fSPawel Jakub Dawidek /* Per-node private data */ 713c976c3fSPawel Jakub Dawidek struct private { 723c976c3fSPawel Jakub Dawidek struct ifnet *ifp; /* associated interface */ 733c976c3fSPawel Jakub Dawidek hook_p upper; /* upper hook connection */ 743c976c3fSPawel Jakub Dawidek hook_p lower; /* lower OR orphan hook connection */ 753c976c3fSPawel Jakub Dawidek u_char lowerOrphan; /* whether lower is lower or orphan */ 763c976c3fSPawel Jakub Dawidek u_char autoSrcAddr; /* always overwrite source address */ 773c976c3fSPawel Jakub Dawidek u_char promisc; /* promiscuous mode enabled */ 783c976c3fSPawel Jakub Dawidek u_long hwassist; /* hardware checksum capabilities */ 793c976c3fSPawel Jakub Dawidek u_int flags; /* flags e.g. really die */ 803c976c3fSPawel Jakub Dawidek }; 813c976c3fSPawel Jakub Dawidek typedef struct private *priv_p; 82e1e1452dSArchie Cobbs 83edbb5246SSam Leffler /* Hook pointers used by if_ethersubr.c to callback to netgraph */ 84edbb5246SSam Leffler extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 85edbb5246SSam Leffler extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 86edbb5246SSam Leffler extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 87edbb5246SSam Leffler extern void (*ng_ether_attach_p)(struct ifnet *ifp); 88edbb5246SSam Leffler extern void (*ng_ether_detach_p)(struct ifnet *ifp); 89edbb5246SSam Leffler 90e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */ 91edbb5246SSam Leffler static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 92edbb5246SSam Leffler static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 93e1e1452dSArchie Cobbs static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 94e1e1452dSArchie Cobbs static void ng_ether_attach(struct ifnet *ifp); 95e1e1452dSArchie Cobbs static void ng_ether_detach(struct ifnet *ifp); 96e1e1452dSArchie Cobbs 97e1e1452dSArchie Cobbs /* Other functions */ 98edbb5246SSam Leffler static void ng_ether_input2(node_p node, struct mbuf **mp); 99e1e1452dSArchie Cobbs static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 100e1e1452dSArchie Cobbs static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 101e1e1452dSArchie Cobbs 102e1e1452dSArchie Cobbs /* Netgraph node methods */ 103e1e1452dSArchie Cobbs static ng_constructor_t ng_ether_constructor; 104e1e1452dSArchie Cobbs static ng_rcvmsg_t ng_ether_rcvmsg; 105069154d5SJulian Elischer static ng_shutdown_t ng_ether_shutdown; 106e1e1452dSArchie Cobbs static ng_newhook_t ng_ether_newhook; 107859a4d16SJulian Elischer static ng_connect_t ng_ether_connect; 108e1e1452dSArchie Cobbs static ng_rcvdata_t ng_ether_rcvdata; 109e1e1452dSArchie Cobbs static ng_disconnect_t ng_ether_disconnect; 110e1e1452dSArchie Cobbs static int ng_ether_mod_event(module_t mod, int event, void *data); 111e1e1452dSArchie Cobbs 112e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 113e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = { 114e1e1452dSArchie Cobbs { 115e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 116e1e1452dSArchie Cobbs NGM_ETHER_GET_IFNAME, 117e1e1452dSArchie Cobbs "getifname", 118e1e1452dSArchie Cobbs NULL, 119e1e1452dSArchie Cobbs &ng_parse_string_type 120e1e1452dSArchie Cobbs }, 121e1e1452dSArchie Cobbs { 122e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 123e1e1452dSArchie Cobbs NGM_ETHER_GET_IFINDEX, 124e1e1452dSArchie Cobbs "getifindex", 125e1e1452dSArchie Cobbs NULL, 126e1e1452dSArchie Cobbs &ng_parse_int32_type 127e1e1452dSArchie Cobbs }, 1284b39c3c7SArchie Cobbs { 1294b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 1304b39c3c7SArchie Cobbs NGM_ETHER_GET_ENADDR, 1314b39c3c7SArchie Cobbs "getenaddr", 1324b39c3c7SArchie Cobbs NULL, 1338c7e4101SRuslan Ermilov &ng_parse_enaddr_type 1344b39c3c7SArchie Cobbs }, 1354b39c3c7SArchie Cobbs { 1364b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 13756045c66SArchie Cobbs NGM_ETHER_SET_ENADDR, 13856045c66SArchie Cobbs "setenaddr", 1398c7e4101SRuslan Ermilov &ng_parse_enaddr_type, 14056045c66SArchie Cobbs NULL 14156045c66SArchie Cobbs }, 14256045c66SArchie Cobbs { 14356045c66SArchie Cobbs NGM_ETHER_COOKIE, 14456045c66SArchie Cobbs NGM_ETHER_GET_PROMISC, 14556045c66SArchie Cobbs "getpromisc", 14656045c66SArchie Cobbs NULL, 14756045c66SArchie Cobbs &ng_parse_int32_type 14856045c66SArchie Cobbs }, 14956045c66SArchie Cobbs { 15056045c66SArchie Cobbs NGM_ETHER_COOKIE, 1514b39c3c7SArchie Cobbs NGM_ETHER_SET_PROMISC, 1524b39c3c7SArchie Cobbs "setpromisc", 1534b39c3c7SArchie Cobbs &ng_parse_int32_type, 1544b39c3c7SArchie Cobbs NULL 1554b39c3c7SArchie Cobbs }, 1564b39c3c7SArchie Cobbs { 1574b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 15856045c66SArchie Cobbs NGM_ETHER_GET_AUTOSRC, 15956045c66SArchie Cobbs "getautosrc", 16056045c66SArchie Cobbs NULL, 16156045c66SArchie Cobbs &ng_parse_int32_type 16256045c66SArchie Cobbs }, 16356045c66SArchie Cobbs { 16456045c66SArchie Cobbs NGM_ETHER_COOKIE, 1654b39c3c7SArchie Cobbs NGM_ETHER_SET_AUTOSRC, 1664b39c3c7SArchie Cobbs "setautosrc", 1674b39c3c7SArchie Cobbs &ng_parse_int32_type, 1684b39c3c7SArchie Cobbs NULL 1694b39c3c7SArchie Cobbs }, 170e1e1452dSArchie Cobbs { 0 } 171e1e1452dSArchie Cobbs }; 172e1e1452dSArchie Cobbs 173e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = { 174589f6ed8SJulian Elischer NG_ABI_VERSION, 175e1e1452dSArchie Cobbs NG_ETHER_NODE_TYPE, 176e1e1452dSArchie Cobbs ng_ether_mod_event, 177e1e1452dSArchie Cobbs ng_ether_constructor, 178e1e1452dSArchie Cobbs ng_ether_rcvmsg, 179069154d5SJulian Elischer ng_ether_shutdown, 180e1e1452dSArchie Cobbs ng_ether_newhook, 181e1e1452dSArchie Cobbs NULL, 182859a4d16SJulian Elischer ng_ether_connect, 183e1e1452dSArchie Cobbs ng_ether_rcvdata, 184e1e1452dSArchie Cobbs ng_ether_disconnect, 185e1e1452dSArchie Cobbs ng_ether_cmdlist, 186e1e1452dSArchie Cobbs }; 1876b795970SJulian Elischer MODULE_VERSION(ng_ether, 1); 188e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct); 189e1e1452dSArchie Cobbs 190e1e1452dSArchie Cobbs /****************************************************************** 191e1e1452dSArchie Cobbs ETHERNET FUNCTION HOOKS 192e1e1452dSArchie Cobbs ******************************************************************/ 193e1e1452dSArchie Cobbs 194e1e1452dSArchie Cobbs /* 195e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface. We get to 196e1e1452dSArchie Cobbs * look at it here before any upper layer protocols do. 197e1e1452dSArchie Cobbs * 198e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 199e1e1452dSArchie Cobbs */ 200e1e1452dSArchie Cobbs static void 201edbb5246SSam Leffler ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 202e1e1452dSArchie Cobbs { 203e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 20430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 205e1e1452dSArchie Cobbs 206e1e1452dSArchie Cobbs /* If "lower" hook not connected, let packet continue */ 207e1e1452dSArchie Cobbs if (priv->lower == NULL || priv->lowerOrphan) 208e1e1452dSArchie Cobbs return; 209edbb5246SSam Leffler ng_ether_input2(node, mp); 210e1e1452dSArchie Cobbs } 211e1e1452dSArchie Cobbs 212e1e1452dSArchie Cobbs /* 213e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface, and which 214e1e1452dSArchie Cobbs * does not match any of our known protocols (an ``orphan''). 215e1e1452dSArchie Cobbs * 216e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 217e1e1452dSArchie Cobbs */ 218e1e1452dSArchie Cobbs static void 219edbb5246SSam Leffler ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 220e1e1452dSArchie Cobbs { 221e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 22230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 223e1e1452dSArchie Cobbs 224e1e1452dSArchie Cobbs /* If "orphan" hook not connected, let packet continue */ 225e1e1452dSArchie Cobbs if (priv->lower == NULL || !priv->lowerOrphan) { 226e1e1452dSArchie Cobbs m_freem(m); 227e1e1452dSArchie Cobbs return; 228e1e1452dSArchie Cobbs } 229edbb5246SSam Leffler ng_ether_input2(node, &m); 230e1e1452dSArchie Cobbs if (m != NULL) 231e1e1452dSArchie Cobbs m_freem(m); 232e1e1452dSArchie Cobbs } 233e1e1452dSArchie Cobbs 234e1e1452dSArchie Cobbs /* 235859a4d16SJulian Elischer * Handle a packet that has come in on an ethernet interface. 236e1e1452dSArchie Cobbs * The Ethernet header has already been detached from the mbuf, 237e1e1452dSArchie Cobbs * so we have to put it back. 238e1e1452dSArchie Cobbs * 239e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 240e1e1452dSArchie Cobbs */ 241e1e1452dSArchie Cobbs static void 242edbb5246SSam Leffler ng_ether_input2(node_p node, struct mbuf **mp) 243e1e1452dSArchie Cobbs { 24430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 245e1e1452dSArchie Cobbs int error; 246e1e1452dSArchie Cobbs 247e1e1452dSArchie Cobbs /* Send out lower/orphan hook */ 248859a4d16SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, *mp); 249e1e1452dSArchie Cobbs *mp = NULL; 250e1e1452dSArchie Cobbs } 251e1e1452dSArchie Cobbs 252e1e1452dSArchie Cobbs /* 253e1e1452dSArchie Cobbs * Handle a packet that is going out on an interface. 254e1e1452dSArchie Cobbs * The Ethernet header is already attached to the mbuf. 255e1e1452dSArchie Cobbs */ 256e1e1452dSArchie Cobbs static int 257e1e1452dSArchie Cobbs ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 258e1e1452dSArchie Cobbs { 259e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 26030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 261e1e1452dSArchie Cobbs int error = 0; 262e1e1452dSArchie Cobbs 263e1e1452dSArchie Cobbs /* If "upper" hook not connected, let packet continue */ 264e1e1452dSArchie Cobbs if (priv->upper == NULL) 265e1e1452dSArchie Cobbs return (0); 266e1e1452dSArchie Cobbs 267e1e1452dSArchie Cobbs /* Send it out "upper" hook */ 268069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->upper, *mp); 269e1e1452dSArchie Cobbs return (error); 270e1e1452dSArchie Cobbs } 271e1e1452dSArchie Cobbs 272e1e1452dSArchie Cobbs /* 273e1e1452dSArchie Cobbs * A new Ethernet interface has been attached. 274e1e1452dSArchie Cobbs * Create a new node for it, etc. 275e1e1452dSArchie Cobbs */ 276e1e1452dSArchie Cobbs static void 277e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp) 278e1e1452dSArchie Cobbs { 279e1e1452dSArchie Cobbs priv_p priv; 280e1e1452dSArchie Cobbs node_p node; 281e1e1452dSArchie Cobbs 282e1e1452dSArchie Cobbs /* Create node */ 2836e551fb6SDavid E. O'Brien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 284e1e1452dSArchie Cobbs if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 285e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 2869bf40edeSBrooks Davis __func__, "create node", ifp->if_xname); 287e1e1452dSArchie Cobbs return; 288e1e1452dSArchie Cobbs } 289e1e1452dSArchie Cobbs 290e1e1452dSArchie Cobbs /* Allocate private data */ 29199cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 292e1e1452dSArchie Cobbs if (priv == NULL) { 293e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 2949bf40edeSBrooks Davis __func__, "allocate memory", ifp->if_xname); 29530400f03SJulian Elischer NG_NODE_UNREF(node); 296e1e1452dSArchie Cobbs return; 297e1e1452dSArchie Cobbs } 29830400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 299e1e1452dSArchie Cobbs priv->ifp = ifp; 300e1e1452dSArchie Cobbs IFP2NG(ifp) = node; 3014b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; 302a3e232d6SArchie Cobbs priv->hwassist = ifp->if_hwassist; 303e1e1452dSArchie Cobbs 304e1e1452dSArchie Cobbs /* Try to give the node the same name as the interface */ 3059bf40edeSBrooks Davis if (ng_name_node(node, ifp->if_xname) != 0) { 306e1e1452dSArchie Cobbs log(LOG_WARNING, "%s: can't name node %s\n", 3079bf40edeSBrooks Davis __func__, ifp->if_xname); 308e1e1452dSArchie Cobbs } 309e1e1452dSArchie Cobbs } 310e1e1452dSArchie Cobbs 311e1e1452dSArchie Cobbs /* 312e1e1452dSArchie Cobbs * An Ethernet interface is being detached. 3131acb27c6SJulian Elischer * REALLY Destroy its node. 314e1e1452dSArchie Cobbs */ 315e1e1452dSArchie Cobbs static void 316e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp) 317e1e1452dSArchie Cobbs { 318e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 3191acb27c6SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 320e1e1452dSArchie Cobbs 321e1e1452dSArchie Cobbs if (node == NULL) /* no node (why not?), ignore */ 322e1e1452dSArchie Cobbs return; 3231acb27c6SJulian Elischer NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 3241acb27c6SJulian Elischer /* 3251acb27c6SJulian Elischer * We can't assume the ifnet is still around when we run shutdown 3261acb27c6SJulian Elischer * So zap it now. XXX We HOPE that anything running at this time 3271acb27c6SJulian Elischer * handles it (as it should in the non netgraph case). 3281acb27c6SJulian Elischer */ 3291acb27c6SJulian Elischer IFP2NG(ifp) = NULL; 3301acb27c6SJulian Elischer priv->ifp = NULL; /* XXX race if interrupted an output packet */ 3311acb27c6SJulian Elischer ng_rmnode_self(node); /* remove all netgraph parts */ 332e1e1452dSArchie Cobbs } 333e1e1452dSArchie Cobbs 334e1e1452dSArchie Cobbs /****************************************************************** 335e1e1452dSArchie Cobbs NETGRAPH NODE METHODS 336e1e1452dSArchie Cobbs ******************************************************************/ 337e1e1452dSArchie Cobbs 338e1e1452dSArchie Cobbs /* 339e1e1452dSArchie Cobbs * It is not possible or allowable to create a node of this type. 340e1e1452dSArchie Cobbs * Nodes get created when the interface is attached (or, when 341e1e1452dSArchie Cobbs * this node type's KLD is loaded). 342e1e1452dSArchie Cobbs */ 343e1e1452dSArchie Cobbs static int 344069154d5SJulian Elischer ng_ether_constructor(node_p node) 345e1e1452dSArchie Cobbs { 346e1e1452dSArchie Cobbs return (EINVAL); 347e1e1452dSArchie Cobbs } 348e1e1452dSArchie Cobbs 349e1e1452dSArchie Cobbs /* 350e1e1452dSArchie Cobbs * Check for attaching a new hook. 351e1e1452dSArchie Cobbs */ 352e1e1452dSArchie Cobbs static int 353e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name) 354e1e1452dSArchie Cobbs { 35530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 356e1e1452dSArchie Cobbs u_char orphan = priv->lowerOrphan; 357e1e1452dSArchie Cobbs hook_p *hookptr; 358e1e1452dSArchie Cobbs 359e1e1452dSArchie Cobbs /* Divert hook is an alias for lower */ 360e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 361e1e1452dSArchie Cobbs name = NG_ETHER_HOOK_LOWER; 362e1e1452dSArchie Cobbs 363e1e1452dSArchie Cobbs /* Which hook? */ 364e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 365e1e1452dSArchie Cobbs hookptr = &priv->upper; 366e1e1452dSArchie Cobbs else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 367e1e1452dSArchie Cobbs hookptr = &priv->lower; 368e1e1452dSArchie Cobbs orphan = 0; 369e1e1452dSArchie Cobbs } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 370e1e1452dSArchie Cobbs hookptr = &priv->lower; 371e1e1452dSArchie Cobbs orphan = 1; 372e1e1452dSArchie Cobbs } else 373e1e1452dSArchie Cobbs return (EINVAL); 374e1e1452dSArchie Cobbs 375e1e1452dSArchie Cobbs /* Check if already connected (shouldn't be, but doesn't hurt) */ 376e1e1452dSArchie Cobbs if (*hookptr != NULL) 377e1e1452dSArchie Cobbs return (EISCONN); 378e1e1452dSArchie Cobbs 379a3e232d6SArchie Cobbs /* Disable hardware checksums while 'upper' hook is connected */ 380a3e232d6SArchie Cobbs if (hookptr == &priv->upper) 381a3e232d6SArchie Cobbs priv->ifp->if_hwassist = 0; 382a3e232d6SArchie Cobbs 383e1e1452dSArchie Cobbs /* OK */ 384e1e1452dSArchie Cobbs *hookptr = hook; 385e1e1452dSArchie Cobbs priv->lowerOrphan = orphan; 386e1e1452dSArchie Cobbs return (0); 387e1e1452dSArchie Cobbs } 388e1e1452dSArchie Cobbs 389e1e1452dSArchie Cobbs /* 390859a4d16SJulian Elischer * Hooks are attached, adjust to force queueing. 391859a4d16SJulian Elischer * We don't really care which hook it is. 392859a4d16SJulian Elischer * they should all be queuing for outgoing data. 393859a4d16SJulian Elischer */ 394859a4d16SJulian Elischer static int 395859a4d16SJulian Elischer ng_ether_connect(hook_p hook) 396859a4d16SJulian Elischer { 39730400f03SJulian Elischer NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 398859a4d16SJulian Elischer return (0); 399859a4d16SJulian Elischer } 400859a4d16SJulian Elischer 401859a4d16SJulian Elischer /* 402e1e1452dSArchie Cobbs * Receive an incoming control message. 403e1e1452dSArchie Cobbs */ 404e1e1452dSArchie Cobbs static int 405069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 406e1e1452dSArchie Cobbs { 40730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 408e1e1452dSArchie Cobbs struct ng_mesg *resp = NULL; 409e1e1452dSArchie Cobbs int error = 0; 410069154d5SJulian Elischer struct ng_mesg *msg; 411e1e1452dSArchie Cobbs 412069154d5SJulian Elischer NGI_GET_MSG(item, msg); 413e1e1452dSArchie Cobbs switch (msg->header.typecookie) { 414e1e1452dSArchie Cobbs case NGM_ETHER_COOKIE: 415e1e1452dSArchie Cobbs switch (msg->header.cmd) { 416e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFNAME: 417e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 418e1e1452dSArchie Cobbs if (resp == NULL) { 419e1e1452dSArchie Cobbs error = ENOMEM; 420e1e1452dSArchie Cobbs break; 421e1e1452dSArchie Cobbs } 4229bf40edeSBrooks Davis strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ + 1); 423e1e1452dSArchie Cobbs break; 424e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFINDEX: 425e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 426e1e1452dSArchie Cobbs if (resp == NULL) { 427e1e1452dSArchie Cobbs error = ENOMEM; 428e1e1452dSArchie Cobbs break; 429e1e1452dSArchie Cobbs } 430e1e1452dSArchie Cobbs *((u_int32_t *)resp->data) = priv->ifp->if_index; 431e1e1452dSArchie Cobbs break; 4324b39c3c7SArchie Cobbs case NGM_ETHER_GET_ENADDR: 4334b39c3c7SArchie Cobbs NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 4344b39c3c7SArchie Cobbs if (resp == NULL) { 4354b39c3c7SArchie Cobbs error = ENOMEM; 4364b39c3c7SArchie Cobbs break; 4374b39c3c7SArchie Cobbs } 4384b39c3c7SArchie Cobbs bcopy((IFP2AC(priv->ifp))->ac_enaddr, 4394b39c3c7SArchie Cobbs resp->data, ETHER_ADDR_LEN); 4404b39c3c7SArchie Cobbs break; 44156045c66SArchie Cobbs case NGM_ETHER_SET_ENADDR: 44256045c66SArchie Cobbs { 44356045c66SArchie Cobbs if (msg->header.arglen != ETHER_ADDR_LEN) { 44456045c66SArchie Cobbs error = EINVAL; 44556045c66SArchie Cobbs break; 44656045c66SArchie Cobbs } 44756045c66SArchie Cobbs error = if_setlladdr(priv->ifp, 44856045c66SArchie Cobbs (u_char *)msg->data, ETHER_ADDR_LEN); 44956045c66SArchie Cobbs break; 45056045c66SArchie Cobbs } 45156045c66SArchie Cobbs case NGM_ETHER_GET_PROMISC: 45256045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 45356045c66SArchie Cobbs if (resp == NULL) { 45456045c66SArchie Cobbs error = ENOMEM; 45556045c66SArchie Cobbs break; 45656045c66SArchie Cobbs } 45756045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->promisc; 45856045c66SArchie Cobbs break; 4594b39c3c7SArchie Cobbs case NGM_ETHER_SET_PROMISC: 4604b39c3c7SArchie Cobbs { 4614b39c3c7SArchie Cobbs u_char want; 4624b39c3c7SArchie Cobbs 4634b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 4644b39c3c7SArchie Cobbs error = EINVAL; 4654b39c3c7SArchie Cobbs break; 4664b39c3c7SArchie Cobbs } 4674b39c3c7SArchie Cobbs want = !!*((u_int32_t *)msg->data); 4684b39c3c7SArchie Cobbs if (want ^ priv->promisc) { 4694b39c3c7SArchie Cobbs if ((error = ifpromisc(priv->ifp, want)) != 0) 4704b39c3c7SArchie Cobbs break; 4714b39c3c7SArchie Cobbs priv->promisc = want; 4724b39c3c7SArchie Cobbs } 4734b39c3c7SArchie Cobbs break; 4744b39c3c7SArchie Cobbs } 47556045c66SArchie Cobbs case NGM_ETHER_GET_AUTOSRC: 47656045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 47756045c66SArchie Cobbs if (resp == NULL) { 47856045c66SArchie Cobbs error = ENOMEM; 47956045c66SArchie Cobbs break; 48056045c66SArchie Cobbs } 48156045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->autoSrcAddr; 48256045c66SArchie Cobbs break; 4834b39c3c7SArchie Cobbs case NGM_ETHER_SET_AUTOSRC: 4844b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 4854b39c3c7SArchie Cobbs error = EINVAL; 4864b39c3c7SArchie Cobbs break; 4874b39c3c7SArchie Cobbs } 4884b39c3c7SArchie Cobbs priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 4894b39c3c7SArchie Cobbs break; 490e1e1452dSArchie Cobbs default: 491e1e1452dSArchie Cobbs error = EINVAL; 492e1e1452dSArchie Cobbs break; 493e1e1452dSArchie Cobbs } 494e1e1452dSArchie Cobbs break; 495e1e1452dSArchie Cobbs default: 496e1e1452dSArchie Cobbs error = EINVAL; 497e1e1452dSArchie Cobbs break; 498e1e1452dSArchie Cobbs } 499069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 500069154d5SJulian Elischer NG_FREE_MSG(msg); 501e1e1452dSArchie Cobbs return (error); 502e1e1452dSArchie Cobbs } 503e1e1452dSArchie Cobbs 504e1e1452dSArchie Cobbs /* 505e1e1452dSArchie Cobbs * Receive data on a hook. 506e1e1452dSArchie Cobbs */ 507e1e1452dSArchie Cobbs static int 508069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item) 509e1e1452dSArchie Cobbs { 51030400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 51130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 512069154d5SJulian Elischer struct mbuf *m; 513069154d5SJulian Elischer meta_p meta; 514e1e1452dSArchie Cobbs 515069154d5SJulian Elischer NGI_GET_M(item, m); 516069154d5SJulian Elischer NGI_GET_META(item, meta); 517069154d5SJulian Elischer NG_FREE_ITEM(item); 518e1e1452dSArchie Cobbs if (hook == priv->lower) 519e1e1452dSArchie Cobbs return ng_ether_rcv_lower(node, m, meta); 520e1e1452dSArchie Cobbs if (hook == priv->upper) 521e1e1452dSArchie Cobbs return ng_ether_rcv_upper(node, m, meta); 5226e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 523b40ce416SJulian Elischer #ifdef RESTARTABLE_PANICS /* so we don;t get an error msg in LINT */ 524b40ce416SJulian Elischer return NULL; 525b40ce416SJulian Elischer #endif 526e1e1452dSArchie Cobbs } 527e1e1452dSArchie Cobbs 528e1e1452dSArchie Cobbs /* 529e1e1452dSArchie Cobbs * Handle an mbuf received on the "lower" hook. 530e1e1452dSArchie Cobbs */ 531e1e1452dSArchie Cobbs static int 532e1e1452dSArchie Cobbs ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 533e1e1452dSArchie Cobbs { 53430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 535a1479aa2SArchie Cobbs struct ifnet *const ifp = priv->ifp; 536a1479aa2SArchie Cobbs 5377b9f235fSArchie Cobbs /* Discard meta info */ 5387b9f235fSArchie Cobbs NG_FREE_META(meta); 5397b9f235fSArchie Cobbs 540a1479aa2SArchie Cobbs /* Check whether interface is ready for packets */ 541a1479aa2SArchie Cobbs if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 542a1479aa2SArchie Cobbs NG_FREE_M(m); 543a1479aa2SArchie Cobbs return (ENETDOWN); 544a1479aa2SArchie Cobbs } 545e1e1452dSArchie Cobbs 546e1e1452dSArchie Cobbs /* Make sure header is fully pulled up */ 547e1e1452dSArchie Cobbs if (m->m_pkthdr.len < sizeof(struct ether_header)) { 548069154d5SJulian Elischer NG_FREE_M(m); 549e1e1452dSArchie Cobbs return (EINVAL); 550e1e1452dSArchie Cobbs } 551e1e1452dSArchie Cobbs if (m->m_len < sizeof(struct ether_header) 5527b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 553e1e1452dSArchie Cobbs return (ENOBUFS); 554e1e1452dSArchie Cobbs 5554b39c3c7SArchie Cobbs /* Drop in the MAC address if desired */ 5564b39c3c7SArchie Cobbs if (priv->autoSrcAddr) { 5577b9f235fSArchie Cobbs 5587b9f235fSArchie Cobbs /* Make the mbuf writable if it's not already */ 5597b9f235fSArchie Cobbs if (!M_WRITABLE(m) 5607b9f235fSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 5617b9f235fSArchie Cobbs return (ENOBUFS); 5627b9f235fSArchie Cobbs 5637b9f235fSArchie Cobbs /* Overwrite source MAC address */ 564a1479aa2SArchie Cobbs bcopy((IFP2AC(ifp))->ac_enaddr, 5654b39c3c7SArchie Cobbs mtod(m, struct ether_header *)->ether_shost, 5664b39c3c7SArchie Cobbs ETHER_ADDR_LEN); 5674b39c3c7SArchie Cobbs } 568561e4fb9SJulian Elischer 569e1e1452dSArchie Cobbs /* Send it on its way */ 570a1479aa2SArchie Cobbs return ether_output_frame(ifp, m); 571e1e1452dSArchie Cobbs } 572e1e1452dSArchie Cobbs 573e1e1452dSArchie Cobbs /* 574e1e1452dSArchie Cobbs * Handle an mbuf received on the "upper" hook. 575e1e1452dSArchie Cobbs */ 576e1e1452dSArchie Cobbs static int 577e1e1452dSArchie Cobbs ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 578e1e1452dSArchie Cobbs { 57930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 580e1e1452dSArchie Cobbs 5817b9f235fSArchie Cobbs /* Discard meta info */ 5827b9f235fSArchie Cobbs NG_FREE_META(meta); 5837b9f235fSArchie Cobbs 584a62b20c4SJulian Elischer m->m_pkthdr.rcvif = priv->ifp; 585e1e1452dSArchie Cobbs 586e1e1452dSArchie Cobbs /* Route packet back in */ 587edbb5246SSam Leffler ether_demux(priv->ifp, m); 588e1e1452dSArchie Cobbs return (0); 589e1e1452dSArchie Cobbs } 590e1e1452dSArchie Cobbs 591e1e1452dSArchie Cobbs /* 5921acb27c6SJulian Elischer * Shutdown node. This resets the node but does not remove it 5931acb27c6SJulian Elischer * unless the REALLY_DIE flag is set. 594e1e1452dSArchie Cobbs */ 595e1e1452dSArchie Cobbs static int 596069154d5SJulian Elischer ng_ether_shutdown(node_p node) 597e1e1452dSArchie Cobbs { 59830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 5994b39c3c7SArchie Cobbs 6001acb27c6SJulian Elischer if (node->nd_flags & NG_REALLY_DIE) { 6011acb27c6SJulian Elischer /* 6021acb27c6SJulian Elischer * WE came here because the ethernet card is being unloaded, 6031acb27c6SJulian Elischer * so stop being persistant. 6041acb27c6SJulian Elischer * Actually undo all the things we did on creation. 6051acb27c6SJulian Elischer * Assume the ifp has already been freed. 6061acb27c6SJulian Elischer */ 6071acb27c6SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 6081acb27c6SJulian Elischer FREE(priv, M_NETGRAPH); 6091acb27c6SJulian Elischer NG_NODE_UNREF(node); /* free node itself */ 6101acb27c6SJulian Elischer return (0); 611069154d5SJulian Elischer } 612018df1c3SBrian Feldman if (priv->promisc) { /* disable promiscuous mode */ 613018df1c3SBrian Feldman (void)ifpromisc(priv->ifp, 0); 614018df1c3SBrian Feldman priv->promisc = 0; 615018df1c3SBrian Feldman } 6164b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 6171acb27c6SJulian Elischer node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 618e1e1452dSArchie Cobbs return (0); 619e1e1452dSArchie Cobbs } 620e1e1452dSArchie Cobbs 621e1e1452dSArchie Cobbs /* 622e1e1452dSArchie Cobbs * Hook disconnection. 623e1e1452dSArchie Cobbs */ 624e1e1452dSArchie Cobbs static int 625e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook) 626e1e1452dSArchie Cobbs { 62730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 628e1e1452dSArchie Cobbs 629a3e232d6SArchie Cobbs if (hook == priv->upper) { 630e1e1452dSArchie Cobbs priv->upper = NULL; 631b712e9ecSBrian Feldman if (priv->ifp != NULL) /* restore h/w csum */ 632b712e9ecSBrian Feldman priv->ifp->if_hwassist = priv->hwassist; 633a3e232d6SArchie Cobbs } else if (hook == priv->lower) { 634e1e1452dSArchie Cobbs priv->lower = NULL; 635e1e1452dSArchie Cobbs priv->lowerOrphan = 0; 636e1e1452dSArchie Cobbs } else 6376e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 63830400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 63930400f03SJulian Elischer && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 64030400f03SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 641e1e1452dSArchie Cobbs return (0); 642e1e1452dSArchie Cobbs } 643e1e1452dSArchie Cobbs 644e1e1452dSArchie Cobbs /****************************************************************** 645e1e1452dSArchie Cobbs INITIALIZATION 646e1e1452dSArchie Cobbs ******************************************************************/ 647e1e1452dSArchie Cobbs 648e1e1452dSArchie Cobbs /* 649e1e1452dSArchie Cobbs * Handle loading and unloading for this node type. 650e1e1452dSArchie Cobbs */ 651e1e1452dSArchie Cobbs static int 652e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data) 653e1e1452dSArchie Cobbs { 654e1e1452dSArchie Cobbs struct ifnet *ifp; 655e1e1452dSArchie Cobbs int error = 0; 656e1e1452dSArchie Cobbs int s; 657e1e1452dSArchie Cobbs 658e1e1452dSArchie Cobbs s = splnet(); 659e1e1452dSArchie Cobbs switch (event) { 660e1e1452dSArchie Cobbs case MOD_LOAD: 661e1e1452dSArchie Cobbs 662e1e1452dSArchie Cobbs /* Register function hooks */ 663e1e1452dSArchie Cobbs if (ng_ether_attach_p != NULL) { 664e1e1452dSArchie Cobbs error = EEXIST; 665e1e1452dSArchie Cobbs break; 666e1e1452dSArchie Cobbs } 667e1e1452dSArchie Cobbs ng_ether_attach_p = ng_ether_attach; 668e1e1452dSArchie Cobbs ng_ether_detach_p = ng_ether_detach; 669e1e1452dSArchie Cobbs ng_ether_output_p = ng_ether_output; 670e1e1452dSArchie Cobbs ng_ether_input_p = ng_ether_input; 671e1e1452dSArchie Cobbs ng_ether_input_orphan_p = ng_ether_input_orphan; 672e1e1452dSArchie Cobbs 673e1e1452dSArchie Cobbs /* Create nodes for any already-existing Ethernet interfaces */ 674b30a244cSJeffrey Hsu IFNET_RLOCK(); 675e1e1452dSArchie Cobbs TAILQ_FOREACH(ifp, &ifnet, if_link) { 676cf2010b8SArchie Cobbs if (ifp->if_type == IFT_ETHER 677cf2010b8SArchie Cobbs || ifp->if_type == IFT_L2VLAN) 678e1e1452dSArchie Cobbs ng_ether_attach(ifp); 679e1e1452dSArchie Cobbs } 680b30a244cSJeffrey Hsu IFNET_RUNLOCK(); 681e1e1452dSArchie Cobbs break; 682e1e1452dSArchie Cobbs 683e1e1452dSArchie Cobbs case MOD_UNLOAD: 684e1e1452dSArchie Cobbs 685e1e1452dSArchie Cobbs /* 686e1e1452dSArchie Cobbs * Note that the base code won't try to unload us until 687e1e1452dSArchie Cobbs * all nodes have been removed, and that can't happen 688e1e1452dSArchie Cobbs * until all Ethernet interfaces are removed. In any 689e1e1452dSArchie Cobbs * case, we know there are no nodes left if the action 690e1e1452dSArchie Cobbs * is MOD_UNLOAD, so there's no need to detach any nodes. 691e1e1452dSArchie Cobbs */ 692e1e1452dSArchie Cobbs 693e1e1452dSArchie Cobbs /* Unregister function hooks */ 694e1e1452dSArchie Cobbs ng_ether_attach_p = NULL; 695e1e1452dSArchie Cobbs ng_ether_detach_p = NULL; 696e1e1452dSArchie Cobbs ng_ether_output_p = NULL; 697e1e1452dSArchie Cobbs ng_ether_input_p = NULL; 698e1e1452dSArchie Cobbs ng_ether_input_orphan_p = NULL; 699e1e1452dSArchie Cobbs break; 700e1e1452dSArchie Cobbs 701e1e1452dSArchie Cobbs default: 702e1e1452dSArchie Cobbs error = EOPNOTSUPP; 703e1e1452dSArchie Cobbs break; 704e1e1452dSArchie Cobbs } 705e1e1452dSArchie Cobbs splx(s); 706e1e1452dSArchie Cobbs return (error); 707e1e1452dSArchie Cobbs } 708e1e1452dSArchie Cobbs 709