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 70e1e1452dSArchie Cobbs /* Per-node private data */ 71e1e1452dSArchie Cobbs struct private { 72e1e1452dSArchie Cobbs struct ifnet *ifp; /* associated interface */ 73e1e1452dSArchie Cobbs hook_p upper; /* upper hook connection */ 74e1e1452dSArchie Cobbs hook_p lower; /* lower OR orphan hook connection */ 75e1e1452dSArchie Cobbs u_char lowerOrphan; /* whether lower is lower or orphan */ 764b39c3c7SArchie Cobbs u_char autoSrcAddr; /* always overwrite source address */ 774b39c3c7SArchie Cobbs u_char promisc; /* promiscuous mode enabled */ 78a3e232d6SArchie Cobbs u_long hwassist; /* hardware checksum capabilities */ 791acb27c6SJulian Elischer u_int flags; /* flags e.g. really die */ 80e1e1452dSArchie Cobbs }; 81e1e1452dSArchie Cobbs typedef struct private *priv_p; 82e1e1452dSArchie Cobbs 83e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */ 84e1e1452dSArchie Cobbs static void ng_ether_input(struct ifnet *ifp, 85e1e1452dSArchie Cobbs struct mbuf **mp, struct ether_header *eh); 86e1e1452dSArchie Cobbs static void ng_ether_input_orphan(struct ifnet *ifp, 87e1e1452dSArchie Cobbs struct mbuf *m, struct ether_header *eh); 88e1e1452dSArchie Cobbs static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 89e1e1452dSArchie Cobbs static void ng_ether_attach(struct ifnet *ifp); 90e1e1452dSArchie Cobbs static void ng_ether_detach(struct ifnet *ifp); 91e1e1452dSArchie Cobbs 92e1e1452dSArchie Cobbs /* Other functions */ 93e1e1452dSArchie Cobbs static void ng_ether_input2(node_p node, 94e1e1452dSArchie Cobbs struct mbuf **mp, struct ether_header *eh); 95e1e1452dSArchie Cobbs static int ng_ether_glueback_header(struct mbuf **mp, 96e1e1452dSArchie Cobbs struct ether_header *eh); 97e1e1452dSArchie Cobbs static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 98e1e1452dSArchie Cobbs static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 99e1e1452dSArchie Cobbs 100e1e1452dSArchie Cobbs /* Netgraph node methods */ 101e1e1452dSArchie Cobbs static ng_constructor_t ng_ether_constructor; 102e1e1452dSArchie Cobbs static ng_rcvmsg_t ng_ether_rcvmsg; 103069154d5SJulian Elischer static ng_shutdown_t ng_ether_shutdown; 104e1e1452dSArchie Cobbs static ng_newhook_t ng_ether_newhook; 105859a4d16SJulian Elischer static ng_connect_t ng_ether_connect; 106e1e1452dSArchie Cobbs static ng_rcvdata_t ng_ether_rcvdata; 107e1e1452dSArchie Cobbs static ng_disconnect_t ng_ether_disconnect; 108e1e1452dSArchie Cobbs static int ng_ether_mod_event(module_t mod, int event, void *data); 109e1e1452dSArchie Cobbs 11056045c66SArchie Cobbs /* Parse type for an Ethernet address */ 11156045c66SArchie Cobbs static ng_parse_t ng_enaddr_parse; 11256045c66SArchie Cobbs static ng_unparse_t ng_enaddr_unparse; 11356045c66SArchie Cobbs const struct ng_parse_type ng_ether_enaddr_type = { 11456045c66SArchie Cobbs NULL, 11556045c66SArchie Cobbs NULL, 11656045c66SArchie Cobbs NULL, 11756045c66SArchie Cobbs ng_enaddr_parse, 11856045c66SArchie Cobbs ng_enaddr_unparse, 11956045c66SArchie Cobbs NULL, /* no such thing as a "default" EN address */ 12056045c66SArchie Cobbs 0 1214b39c3c7SArchie Cobbs }; 1224b39c3c7SArchie Cobbs 123e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 124e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = { 125e1e1452dSArchie Cobbs { 126e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 127e1e1452dSArchie Cobbs NGM_ETHER_GET_IFNAME, 128e1e1452dSArchie Cobbs "getifname", 129e1e1452dSArchie Cobbs NULL, 130e1e1452dSArchie Cobbs &ng_parse_string_type 131e1e1452dSArchie Cobbs }, 132e1e1452dSArchie Cobbs { 133e1e1452dSArchie Cobbs NGM_ETHER_COOKIE, 134e1e1452dSArchie Cobbs NGM_ETHER_GET_IFINDEX, 135e1e1452dSArchie Cobbs "getifindex", 136e1e1452dSArchie Cobbs NULL, 137e1e1452dSArchie Cobbs &ng_parse_int32_type 138e1e1452dSArchie Cobbs }, 1394b39c3c7SArchie Cobbs { 1404b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 1414b39c3c7SArchie Cobbs NGM_ETHER_GET_ENADDR, 1424b39c3c7SArchie Cobbs "getenaddr", 1434b39c3c7SArchie Cobbs NULL, 1444b39c3c7SArchie Cobbs &ng_ether_enaddr_type 1454b39c3c7SArchie Cobbs }, 1464b39c3c7SArchie Cobbs { 1474b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 14856045c66SArchie Cobbs NGM_ETHER_SET_ENADDR, 14956045c66SArchie Cobbs "setenaddr", 15056045c66SArchie Cobbs &ng_ether_enaddr_type, 15156045c66SArchie Cobbs NULL 15256045c66SArchie Cobbs }, 15356045c66SArchie Cobbs { 15456045c66SArchie Cobbs NGM_ETHER_COOKIE, 15556045c66SArchie Cobbs NGM_ETHER_GET_PROMISC, 15656045c66SArchie Cobbs "getpromisc", 15756045c66SArchie Cobbs NULL, 15856045c66SArchie Cobbs &ng_parse_int32_type 15956045c66SArchie Cobbs }, 16056045c66SArchie Cobbs { 16156045c66SArchie Cobbs NGM_ETHER_COOKIE, 1624b39c3c7SArchie Cobbs NGM_ETHER_SET_PROMISC, 1634b39c3c7SArchie Cobbs "setpromisc", 1644b39c3c7SArchie Cobbs &ng_parse_int32_type, 1654b39c3c7SArchie Cobbs NULL 1664b39c3c7SArchie Cobbs }, 1674b39c3c7SArchie Cobbs { 1684b39c3c7SArchie Cobbs NGM_ETHER_COOKIE, 16956045c66SArchie Cobbs NGM_ETHER_GET_AUTOSRC, 17056045c66SArchie Cobbs "getautosrc", 17156045c66SArchie Cobbs NULL, 17256045c66SArchie Cobbs &ng_parse_int32_type 17356045c66SArchie Cobbs }, 17456045c66SArchie Cobbs { 17556045c66SArchie Cobbs NGM_ETHER_COOKIE, 1764b39c3c7SArchie Cobbs NGM_ETHER_SET_AUTOSRC, 1774b39c3c7SArchie Cobbs "setautosrc", 1784b39c3c7SArchie Cobbs &ng_parse_int32_type, 1794b39c3c7SArchie Cobbs NULL 1804b39c3c7SArchie Cobbs }, 181e1e1452dSArchie Cobbs { 0 } 182e1e1452dSArchie Cobbs }; 183e1e1452dSArchie Cobbs 184e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = { 185589f6ed8SJulian Elischer NG_ABI_VERSION, 186e1e1452dSArchie Cobbs NG_ETHER_NODE_TYPE, 187e1e1452dSArchie Cobbs ng_ether_mod_event, 188e1e1452dSArchie Cobbs ng_ether_constructor, 189e1e1452dSArchie Cobbs ng_ether_rcvmsg, 190069154d5SJulian Elischer ng_ether_shutdown, 191e1e1452dSArchie Cobbs ng_ether_newhook, 192e1e1452dSArchie Cobbs NULL, 193859a4d16SJulian Elischer ng_ether_connect, 194e1e1452dSArchie Cobbs ng_ether_rcvdata, 195e1e1452dSArchie Cobbs ng_ether_disconnect, 196e1e1452dSArchie Cobbs ng_ether_cmdlist, 197e1e1452dSArchie Cobbs }; 1986b795970SJulian Elischer MODULE_VERSION(ng_ether, 1); 199e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct); 200e1e1452dSArchie Cobbs 201e1e1452dSArchie Cobbs /****************************************************************** 202e1e1452dSArchie Cobbs ETHERNET FUNCTION HOOKS 203e1e1452dSArchie Cobbs ******************************************************************/ 204e1e1452dSArchie Cobbs 205e1e1452dSArchie Cobbs /* 206e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface. We get to 207e1e1452dSArchie Cobbs * look at it here before any upper layer protocols do. 208e1e1452dSArchie Cobbs * 209e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 210e1e1452dSArchie Cobbs */ 211e1e1452dSArchie Cobbs static void 212e1e1452dSArchie Cobbs ng_ether_input(struct ifnet *ifp, 213e1e1452dSArchie Cobbs struct mbuf **mp, struct ether_header *eh) 214e1e1452dSArchie Cobbs { 215e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 21630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 217e1e1452dSArchie Cobbs 218e1e1452dSArchie Cobbs /* If "lower" hook not connected, let packet continue */ 219e1e1452dSArchie Cobbs if (priv->lower == NULL || priv->lowerOrphan) 220e1e1452dSArchie Cobbs return; 221e1e1452dSArchie Cobbs ng_ether_input2(node, mp, eh); 222e1e1452dSArchie Cobbs } 223e1e1452dSArchie Cobbs 224e1e1452dSArchie Cobbs /* 225e1e1452dSArchie Cobbs * Handle a packet that has come in on an interface, and which 226e1e1452dSArchie Cobbs * does not match any of our known protocols (an ``orphan''). 227e1e1452dSArchie Cobbs * 228e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 229e1e1452dSArchie Cobbs */ 230e1e1452dSArchie Cobbs static void 231e1e1452dSArchie Cobbs ng_ether_input_orphan(struct ifnet *ifp, 232e1e1452dSArchie Cobbs struct mbuf *m, struct ether_header *eh) 233e1e1452dSArchie Cobbs { 234e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 23530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 236e1e1452dSArchie Cobbs 237e1e1452dSArchie Cobbs /* If "orphan" hook not connected, let packet continue */ 238e1e1452dSArchie Cobbs if (priv->lower == NULL || !priv->lowerOrphan) { 239e1e1452dSArchie Cobbs m_freem(m); 240e1e1452dSArchie Cobbs return; 241e1e1452dSArchie Cobbs } 242e1e1452dSArchie Cobbs ng_ether_input2(node, &m, eh); 243e1e1452dSArchie Cobbs if (m != NULL) 244e1e1452dSArchie Cobbs m_freem(m); 245e1e1452dSArchie Cobbs } 246e1e1452dSArchie Cobbs 247e1e1452dSArchie Cobbs /* 248859a4d16SJulian Elischer * Handle a packet that has come in on an ethernet interface. 249e1e1452dSArchie Cobbs * The Ethernet header has already been detached from the mbuf, 250e1e1452dSArchie Cobbs * so we have to put it back. 251e1e1452dSArchie Cobbs * 252e1e1452dSArchie Cobbs * NOTE: this function will get called at splimp() 253e1e1452dSArchie Cobbs */ 254e1e1452dSArchie Cobbs static void 255e1e1452dSArchie Cobbs ng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh) 256e1e1452dSArchie Cobbs { 25730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 258e1e1452dSArchie Cobbs int error; 259e1e1452dSArchie Cobbs 260e1e1452dSArchie Cobbs /* Glue Ethernet header back on */ 261e1e1452dSArchie Cobbs if ((error = ng_ether_glueback_header(mp, eh)) != 0) 262e1e1452dSArchie Cobbs return; 263e1e1452dSArchie Cobbs 264e1e1452dSArchie Cobbs /* Send out lower/orphan hook */ 265859a4d16SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, *mp); 266e1e1452dSArchie Cobbs *mp = NULL; 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 */ 285069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->upper, *mp); 286e1e1452dSArchie Cobbs return (error); 287e1e1452dSArchie Cobbs } 288e1e1452dSArchie Cobbs 289e1e1452dSArchie Cobbs /* 290e1e1452dSArchie Cobbs * A new Ethernet interface has been attached. 291e1e1452dSArchie Cobbs * Create a new node for it, etc. 292e1e1452dSArchie Cobbs */ 293e1e1452dSArchie Cobbs static void 294e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp) 295e1e1452dSArchie Cobbs { 296e1e1452dSArchie Cobbs char name[IFNAMSIZ + 1]; 297e1e1452dSArchie Cobbs priv_p priv; 298e1e1452dSArchie Cobbs node_p node; 299e1e1452dSArchie Cobbs 300e1e1452dSArchie Cobbs /* Create node */ 3016e551fb6SDavid E. O'Brien KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 302e1e1452dSArchie Cobbs snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); 303e1e1452dSArchie Cobbs if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 304e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3056e551fb6SDavid E. O'Brien __func__, "create node", name); 306e1e1452dSArchie Cobbs return; 307e1e1452dSArchie Cobbs } 308e1e1452dSArchie Cobbs 309e1e1452dSArchie Cobbs /* Allocate private data */ 31099cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 311e1e1452dSArchie Cobbs if (priv == NULL) { 312e1e1452dSArchie Cobbs log(LOG_ERR, "%s: can't %s for %s\n", 3136e551fb6SDavid E. O'Brien __func__, "allocate memory", name); 31430400f03SJulian Elischer NG_NODE_UNREF(node); 315e1e1452dSArchie Cobbs return; 316e1e1452dSArchie Cobbs } 31730400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 318e1e1452dSArchie Cobbs priv->ifp = ifp; 319e1e1452dSArchie Cobbs IFP2NG(ifp) = node; 3204b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; 321a3e232d6SArchie Cobbs priv->hwassist = ifp->if_hwassist; 322e1e1452dSArchie Cobbs 323e1e1452dSArchie Cobbs /* Try to give the node the same name as the interface */ 324e1e1452dSArchie Cobbs if (ng_name_node(node, name) != 0) { 325e1e1452dSArchie Cobbs log(LOG_WARNING, "%s: can't name node %s\n", 3266e551fb6SDavid E. O'Brien __func__, name); 327e1e1452dSArchie Cobbs } 328e1e1452dSArchie Cobbs } 329e1e1452dSArchie Cobbs 330e1e1452dSArchie Cobbs /* 331e1e1452dSArchie Cobbs * An Ethernet interface is being detached. 3321acb27c6SJulian Elischer * REALLY Destroy its node. 333e1e1452dSArchie Cobbs */ 334e1e1452dSArchie Cobbs static void 335e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp) 336e1e1452dSArchie Cobbs { 337e1e1452dSArchie Cobbs const node_p node = IFP2NG(ifp); 3381acb27c6SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 339e1e1452dSArchie Cobbs 340e1e1452dSArchie Cobbs if (node == NULL) /* no node (why not?), ignore */ 341e1e1452dSArchie Cobbs return; 3421acb27c6SJulian Elischer NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 3431acb27c6SJulian Elischer /* 3441acb27c6SJulian Elischer * We can't assume the ifnet is still around when we run shutdown 3451acb27c6SJulian Elischer * So zap it now. XXX We HOPE that anything running at this time 3461acb27c6SJulian Elischer * handles it (as it should in the non netgraph case). 3471acb27c6SJulian Elischer */ 3481acb27c6SJulian Elischer IFP2NG(ifp) = NULL; 3491acb27c6SJulian Elischer priv->ifp = NULL; /* XXX race if interrupted an output packet */ 3501acb27c6SJulian Elischer ng_rmnode_self(node); /* remove all netgraph parts */ 351e1e1452dSArchie Cobbs } 352e1e1452dSArchie Cobbs 353e1e1452dSArchie Cobbs /* 354e1e1452dSArchie Cobbs * Optimization for gluing the Ethernet header back onto 355e1e1452dSArchie Cobbs * the front of an incoming packet. 356e1e1452dSArchie Cobbs */ 357e1e1452dSArchie Cobbs static int 358e1e1452dSArchie Cobbs ng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh) 359e1e1452dSArchie Cobbs { 360e1e1452dSArchie Cobbs struct mbuf *m = *mp; 361e1e1452dSArchie Cobbs uintfptr_t room; 362e1e1452dSArchie Cobbs int error = 0; 363e1e1452dSArchie Cobbs 364e1e1452dSArchie Cobbs /* 365e1e1452dSArchie Cobbs * Possibly the header is already on the front. 366e1e1452dSArchie Cobbs * If this is the case so just move the markers back 367e1e1452dSArchie Cobbs * to re-include it. We lucked out. 368e1e1452dSArchie Cobbs * This allows us to avoid a yucky m_pullup 369e1e1452dSArchie Cobbs * in later nodes if it works. 370e1e1452dSArchie Cobbs */ 371e1e1452dSArchie Cobbs if (eh == mtod(m, struct ether_header *) - 1) { 372e1e1452dSArchie Cobbs m->m_len += sizeof(*eh); 373e1e1452dSArchie Cobbs m->m_data -= sizeof(*eh); 374e1e1452dSArchie Cobbs m->m_pkthdr.len += sizeof(*eh); 375e1e1452dSArchie Cobbs goto done; 376e1e1452dSArchie Cobbs } 377e1e1452dSArchie Cobbs 378e1e1452dSArchie Cobbs /* 379e1e1452dSArchie Cobbs * Alternatively there may be room even though 380e1e1452dSArchie Cobbs * it is stored somewhere else. If so, copy it in. 381e1e1452dSArchie Cobbs * This only safe because we KNOW that this packet has 382e1e1452dSArchie Cobbs * just been generated by an ethernet card, so there are 383e1e1452dSArchie Cobbs * no aliases to the buffer (not so for outgoing packets). 384e1e1452dSArchie Cobbs * Nearly all ethernet cards will end up producing mbufs 385e1e1452dSArchie Cobbs * that fall into these cases. So we are not optimizing 386e1e1452dSArchie Cobbs * contorted cases. 387e1e1452dSArchie Cobbs */ 388e1e1452dSArchie Cobbs if ((m->m_flags & M_EXT) != 0) { 389e1e1452dSArchie Cobbs room = mtod(m, caddr_t) - m->m_ext.ext_buf; 390e1e1452dSArchie Cobbs if (room > m->m_ext.ext_size) /* garbage, fail immediately */ 391e1e1452dSArchie Cobbs room = 0; 392e1e1452dSArchie Cobbs } else 393e1e1452dSArchie Cobbs room = mtod(m, caddr_t) - m->m_pktdat; 394e1e1452dSArchie Cobbs 395e1e1452dSArchie Cobbs /* 396e1e1452dSArchie Cobbs * If we have room, just copy it and adjust 397e1e1452dSArchie Cobbs */ 398e1e1452dSArchie Cobbs if (room >= sizeof(*eh)) { 399e1e1452dSArchie Cobbs m->m_len += sizeof(*eh); 400e1e1452dSArchie Cobbs m->m_data -= sizeof(*eh); 401e1e1452dSArchie Cobbs m->m_pkthdr.len += sizeof(*eh); 402e1e1452dSArchie Cobbs goto copy; 403e1e1452dSArchie Cobbs } 404e1e1452dSArchie Cobbs 405e1e1452dSArchie Cobbs /* 406e1e1452dSArchie Cobbs * Doing anything more is likely to get more 407e1e1452dSArchie Cobbs * expensive than it's worth.. 408e1e1452dSArchie Cobbs * it's probable that everything else is in one 409e1e1452dSArchie Cobbs * big lump. The next node will do an m_pullup() 410e1e1452dSArchie Cobbs * for exactly the amount of data it needs and 411e1e1452dSArchie Cobbs * hopefully everything after that will not 412e1e1452dSArchie Cobbs * need one. So let's just use M_PREPEND. 413e1e1452dSArchie Cobbs */ 414e1e1452dSArchie Cobbs M_PREPEND(m, sizeof (*eh), M_DONTWAIT); 415e1e1452dSArchie Cobbs if (m == NULL) { 416e1e1452dSArchie Cobbs error = ENOBUFS; 417e1e1452dSArchie Cobbs goto done; 418e1e1452dSArchie Cobbs } 419e1e1452dSArchie Cobbs 420e1e1452dSArchie Cobbs copy: 421e1e1452dSArchie Cobbs /* Copy header and return (possibly new) mbuf */ 422e1e1452dSArchie Cobbs bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh)); 423e1e1452dSArchie Cobbs done: 424e1e1452dSArchie Cobbs *mp = m; 425e1e1452dSArchie Cobbs return error; 426e1e1452dSArchie Cobbs } 427e1e1452dSArchie Cobbs 428e1e1452dSArchie Cobbs /****************************************************************** 429e1e1452dSArchie Cobbs NETGRAPH NODE METHODS 430e1e1452dSArchie Cobbs ******************************************************************/ 431e1e1452dSArchie Cobbs 432e1e1452dSArchie Cobbs /* 433e1e1452dSArchie Cobbs * It is not possible or allowable to create a node of this type. 434e1e1452dSArchie Cobbs * Nodes get created when the interface is attached (or, when 435e1e1452dSArchie Cobbs * this node type's KLD is loaded). 436e1e1452dSArchie Cobbs */ 437e1e1452dSArchie Cobbs static int 438069154d5SJulian Elischer ng_ether_constructor(node_p node) 439e1e1452dSArchie Cobbs { 440e1e1452dSArchie Cobbs return (EINVAL); 441e1e1452dSArchie Cobbs } 442e1e1452dSArchie Cobbs 443e1e1452dSArchie Cobbs /* 444e1e1452dSArchie Cobbs * Check for attaching a new hook. 445e1e1452dSArchie Cobbs */ 446e1e1452dSArchie Cobbs static int 447e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name) 448e1e1452dSArchie Cobbs { 44930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 450e1e1452dSArchie Cobbs u_char orphan = priv->lowerOrphan; 451e1e1452dSArchie Cobbs hook_p *hookptr; 452e1e1452dSArchie Cobbs 453e1e1452dSArchie Cobbs /* Divert hook is an alias for lower */ 454e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 455e1e1452dSArchie Cobbs name = NG_ETHER_HOOK_LOWER; 456e1e1452dSArchie Cobbs 457e1e1452dSArchie Cobbs /* Which hook? */ 458e1e1452dSArchie Cobbs if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 459e1e1452dSArchie Cobbs hookptr = &priv->upper; 460e1e1452dSArchie Cobbs else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 461e1e1452dSArchie Cobbs hookptr = &priv->lower; 462e1e1452dSArchie Cobbs orphan = 0; 463e1e1452dSArchie Cobbs } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 464e1e1452dSArchie Cobbs hookptr = &priv->lower; 465e1e1452dSArchie Cobbs orphan = 1; 466e1e1452dSArchie Cobbs } else 467e1e1452dSArchie Cobbs return (EINVAL); 468e1e1452dSArchie Cobbs 469e1e1452dSArchie Cobbs /* Check if already connected (shouldn't be, but doesn't hurt) */ 470e1e1452dSArchie Cobbs if (*hookptr != NULL) 471e1e1452dSArchie Cobbs return (EISCONN); 472e1e1452dSArchie Cobbs 473a3e232d6SArchie Cobbs /* Disable hardware checksums while 'upper' hook is connected */ 474a3e232d6SArchie Cobbs if (hookptr == &priv->upper) 475a3e232d6SArchie Cobbs priv->ifp->if_hwassist = 0; 476a3e232d6SArchie Cobbs 477e1e1452dSArchie Cobbs /* OK */ 478e1e1452dSArchie Cobbs *hookptr = hook; 479e1e1452dSArchie Cobbs priv->lowerOrphan = orphan; 480e1e1452dSArchie Cobbs return (0); 481e1e1452dSArchie Cobbs } 482e1e1452dSArchie Cobbs 483e1e1452dSArchie Cobbs /* 484859a4d16SJulian Elischer * Hooks are attached, adjust to force queueing. 485859a4d16SJulian Elischer * We don't really care which hook it is. 486859a4d16SJulian Elischer * they should all be queuing for outgoing data. 487859a4d16SJulian Elischer */ 488859a4d16SJulian Elischer static int 489859a4d16SJulian Elischer ng_ether_connect(hook_p hook) 490859a4d16SJulian Elischer { 49130400f03SJulian Elischer NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 492859a4d16SJulian Elischer return (0); 493859a4d16SJulian Elischer } 494859a4d16SJulian Elischer 495859a4d16SJulian Elischer /* 496e1e1452dSArchie Cobbs * Receive an incoming control message. 497e1e1452dSArchie Cobbs */ 498e1e1452dSArchie Cobbs static int 499069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 500e1e1452dSArchie Cobbs { 50130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 502e1e1452dSArchie Cobbs struct ng_mesg *resp = NULL; 503e1e1452dSArchie Cobbs int error = 0; 504069154d5SJulian Elischer struct ng_mesg *msg; 505e1e1452dSArchie Cobbs 506069154d5SJulian Elischer NGI_GET_MSG(item, msg); 507e1e1452dSArchie Cobbs switch (msg->header.typecookie) { 508e1e1452dSArchie Cobbs case NGM_ETHER_COOKIE: 509e1e1452dSArchie Cobbs switch (msg->header.cmd) { 510e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFNAME: 511e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 512e1e1452dSArchie Cobbs if (resp == NULL) { 513e1e1452dSArchie Cobbs error = ENOMEM; 514e1e1452dSArchie Cobbs break; 515e1e1452dSArchie Cobbs } 516e1e1452dSArchie Cobbs snprintf(resp->data, IFNAMSIZ + 1, 517e1e1452dSArchie Cobbs "%s%d", priv->ifp->if_name, priv->ifp->if_unit); 518e1e1452dSArchie Cobbs break; 519e1e1452dSArchie Cobbs case NGM_ETHER_GET_IFINDEX: 520e1e1452dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 521e1e1452dSArchie Cobbs if (resp == NULL) { 522e1e1452dSArchie Cobbs error = ENOMEM; 523e1e1452dSArchie Cobbs break; 524e1e1452dSArchie Cobbs } 525e1e1452dSArchie Cobbs *((u_int32_t *)resp->data) = priv->ifp->if_index; 526e1e1452dSArchie Cobbs break; 5274b39c3c7SArchie Cobbs case NGM_ETHER_GET_ENADDR: 5284b39c3c7SArchie Cobbs NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 5294b39c3c7SArchie Cobbs if (resp == NULL) { 5304b39c3c7SArchie Cobbs error = ENOMEM; 5314b39c3c7SArchie Cobbs break; 5324b39c3c7SArchie Cobbs } 5334b39c3c7SArchie Cobbs bcopy((IFP2AC(priv->ifp))->ac_enaddr, 5344b39c3c7SArchie Cobbs resp->data, ETHER_ADDR_LEN); 5354b39c3c7SArchie Cobbs break; 53656045c66SArchie Cobbs case NGM_ETHER_SET_ENADDR: 53756045c66SArchie Cobbs { 53856045c66SArchie Cobbs if (msg->header.arglen != ETHER_ADDR_LEN) { 53956045c66SArchie Cobbs error = EINVAL; 54056045c66SArchie Cobbs break; 54156045c66SArchie Cobbs } 54256045c66SArchie Cobbs error = if_setlladdr(priv->ifp, 54356045c66SArchie Cobbs (u_char *)msg->data, ETHER_ADDR_LEN); 54456045c66SArchie Cobbs break; 54556045c66SArchie Cobbs } 54656045c66SArchie Cobbs case NGM_ETHER_GET_PROMISC: 54756045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 54856045c66SArchie Cobbs if (resp == NULL) { 54956045c66SArchie Cobbs error = ENOMEM; 55056045c66SArchie Cobbs break; 55156045c66SArchie Cobbs } 55256045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->promisc; 55356045c66SArchie Cobbs break; 5544b39c3c7SArchie Cobbs case NGM_ETHER_SET_PROMISC: 5554b39c3c7SArchie Cobbs { 5564b39c3c7SArchie Cobbs u_char want; 5574b39c3c7SArchie Cobbs 5584b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5594b39c3c7SArchie Cobbs error = EINVAL; 5604b39c3c7SArchie Cobbs break; 5614b39c3c7SArchie Cobbs } 5624b39c3c7SArchie Cobbs want = !!*((u_int32_t *)msg->data); 5634b39c3c7SArchie Cobbs if (want ^ priv->promisc) { 5644b39c3c7SArchie Cobbs if ((error = ifpromisc(priv->ifp, want)) != 0) 5654b39c3c7SArchie Cobbs break; 5664b39c3c7SArchie Cobbs priv->promisc = want; 5674b39c3c7SArchie Cobbs } 5684b39c3c7SArchie Cobbs break; 5694b39c3c7SArchie Cobbs } 57056045c66SArchie Cobbs case NGM_ETHER_GET_AUTOSRC: 57156045c66SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 57256045c66SArchie Cobbs if (resp == NULL) { 57356045c66SArchie Cobbs error = ENOMEM; 57456045c66SArchie Cobbs break; 57556045c66SArchie Cobbs } 57656045c66SArchie Cobbs *((u_int32_t *)resp->data) = priv->autoSrcAddr; 57756045c66SArchie Cobbs break; 5784b39c3c7SArchie Cobbs case NGM_ETHER_SET_AUTOSRC: 5794b39c3c7SArchie Cobbs if (msg->header.arglen != sizeof(u_int32_t)) { 5804b39c3c7SArchie Cobbs error = EINVAL; 5814b39c3c7SArchie Cobbs break; 5824b39c3c7SArchie Cobbs } 5834b39c3c7SArchie Cobbs priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 5844b39c3c7SArchie Cobbs break; 585e1e1452dSArchie Cobbs default: 586e1e1452dSArchie Cobbs error = EINVAL; 587e1e1452dSArchie Cobbs break; 588e1e1452dSArchie Cobbs } 589e1e1452dSArchie Cobbs break; 590e1e1452dSArchie Cobbs default: 591e1e1452dSArchie Cobbs error = EINVAL; 592e1e1452dSArchie Cobbs break; 593e1e1452dSArchie Cobbs } 594069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 595069154d5SJulian Elischer NG_FREE_MSG(msg); 596e1e1452dSArchie Cobbs return (error); 597e1e1452dSArchie Cobbs } 598e1e1452dSArchie Cobbs 599e1e1452dSArchie Cobbs /* 600e1e1452dSArchie Cobbs * Receive data on a hook. 601e1e1452dSArchie Cobbs */ 602e1e1452dSArchie Cobbs static int 603069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item) 604e1e1452dSArchie Cobbs { 60530400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 60630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 607069154d5SJulian Elischer struct mbuf *m; 608069154d5SJulian Elischer meta_p meta; 609e1e1452dSArchie Cobbs 610069154d5SJulian Elischer NGI_GET_M(item, m); 611069154d5SJulian Elischer NGI_GET_META(item, meta); 612069154d5SJulian Elischer NG_FREE_ITEM(item); 613e1e1452dSArchie Cobbs if (hook == priv->lower) 614e1e1452dSArchie Cobbs return ng_ether_rcv_lower(node, m, meta); 615e1e1452dSArchie Cobbs if (hook == priv->upper) 616e1e1452dSArchie Cobbs return ng_ether_rcv_upper(node, m, meta); 6176e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 618b40ce416SJulian Elischer #ifdef RESTARTABLE_PANICS /* so we don;t get an error msg in LINT */ 619b40ce416SJulian Elischer return NULL; 620b40ce416SJulian Elischer #endif 621e1e1452dSArchie Cobbs } 622e1e1452dSArchie Cobbs 623e1e1452dSArchie Cobbs /* 624e1e1452dSArchie Cobbs * Handle an mbuf received on the "lower" hook. 625e1e1452dSArchie Cobbs */ 626e1e1452dSArchie Cobbs static int 627e1e1452dSArchie Cobbs ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 628e1e1452dSArchie Cobbs { 62930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 630a1479aa2SArchie Cobbs struct ifnet *const ifp = priv->ifp; 631a1479aa2SArchie Cobbs 632a1479aa2SArchie Cobbs /* Check whether interface is ready for packets */ 633a1479aa2SArchie Cobbs if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 634a1479aa2SArchie Cobbs NG_FREE_M(m); 635a1479aa2SArchie Cobbs NG_FREE_META(meta); 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); 642069154d5SJulian Elischer NG_FREE_META(meta); 643e1e1452dSArchie Cobbs return (EINVAL); 644e1e1452dSArchie Cobbs } 645e1e1452dSArchie Cobbs if (m->m_len < sizeof(struct ether_header) 646e1e1452dSArchie Cobbs && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { 647e1e1452dSArchie Cobbs NG_FREE_META(meta); 648e1e1452dSArchie Cobbs return (ENOBUFS); 649e1e1452dSArchie Cobbs } 650e1e1452dSArchie Cobbs 6514b39c3c7SArchie Cobbs /* Drop in the MAC address if desired */ 6524b39c3c7SArchie Cobbs if (priv->autoSrcAddr) { 653a1479aa2SArchie Cobbs bcopy((IFP2AC(ifp))->ac_enaddr, 6544b39c3c7SArchie Cobbs mtod(m, struct ether_header *)->ether_shost, 6554b39c3c7SArchie Cobbs ETHER_ADDR_LEN); 6564b39c3c7SArchie Cobbs } 657561e4fb9SJulian Elischer 658e1e1452dSArchie Cobbs /* Send it on its way */ 659e1e1452dSArchie Cobbs NG_FREE_META(meta); 660a1479aa2SArchie Cobbs return ether_output_frame(ifp, m); 661e1e1452dSArchie Cobbs } 662e1e1452dSArchie Cobbs 663e1e1452dSArchie Cobbs /* 664e1e1452dSArchie Cobbs * Handle an mbuf received on the "upper" hook. 665e1e1452dSArchie Cobbs */ 666e1e1452dSArchie Cobbs static int 667e1e1452dSArchie Cobbs ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 668e1e1452dSArchie Cobbs { 66930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 670e1e1452dSArchie Cobbs struct ether_header *eh; 671e1e1452dSArchie Cobbs 672e1e1452dSArchie Cobbs /* Check length and pull off header */ 673e1e1452dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*eh)) { 674069154d5SJulian Elischer NG_FREE_M(m); 675069154d5SJulian Elischer NG_FREE_META(meta); 676e1e1452dSArchie Cobbs return (EINVAL); 677e1e1452dSArchie Cobbs } 678e1e1452dSArchie Cobbs if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) { 679e1e1452dSArchie Cobbs NG_FREE_META(meta); 680e1e1452dSArchie Cobbs return (ENOBUFS); 681e1e1452dSArchie Cobbs } 682e1e1452dSArchie Cobbs eh = mtod(m, struct ether_header *); 683e1e1452dSArchie Cobbs m->m_data += sizeof(*eh); 684e1e1452dSArchie Cobbs m->m_len -= sizeof(*eh); 685e1e1452dSArchie Cobbs m->m_pkthdr.len -= sizeof(*eh); 686a62b20c4SJulian Elischer m->m_pkthdr.rcvif = priv->ifp; 687e1e1452dSArchie Cobbs 688e1e1452dSArchie Cobbs /* Route packet back in */ 689e1e1452dSArchie Cobbs NG_FREE_META(meta); 690e1e1452dSArchie Cobbs ether_demux(priv->ifp, eh, m); 691e1e1452dSArchie Cobbs return (0); 692e1e1452dSArchie Cobbs } 693e1e1452dSArchie Cobbs 694e1e1452dSArchie Cobbs /* 6951acb27c6SJulian Elischer * Shutdown node. This resets the node but does not remove it 6961acb27c6SJulian Elischer * unless the REALLY_DIE flag is set. 697e1e1452dSArchie Cobbs */ 698e1e1452dSArchie Cobbs static int 699069154d5SJulian Elischer ng_ether_shutdown(node_p node) 700e1e1452dSArchie Cobbs { 70130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 7024b39c3c7SArchie Cobbs 7034b39c3c7SArchie Cobbs if (priv->promisc) { /* disable promiscuous mode */ 7044b39c3c7SArchie Cobbs (void)ifpromisc(priv->ifp, 0); 7054b39c3c7SArchie Cobbs priv->promisc = 0; 7064b39c3c7SArchie Cobbs } 7071acb27c6SJulian Elischer if (node->nd_flags & NG_REALLY_DIE) { 7081acb27c6SJulian Elischer /* 7091acb27c6SJulian Elischer * WE came here because the ethernet card is being unloaded, 7101acb27c6SJulian Elischer * so stop being persistant. 7111acb27c6SJulian Elischer * Actually undo all the things we did on creation. 7121acb27c6SJulian Elischer * Assume the ifp has already been freed. 7131acb27c6SJulian Elischer */ 7141acb27c6SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 7151acb27c6SJulian Elischer FREE(priv, M_NETGRAPH); 7161acb27c6SJulian Elischer NG_NODE_UNREF(node); /* free node itself */ 7171acb27c6SJulian Elischer return (0); 718069154d5SJulian Elischer } 7194b39c3c7SArchie Cobbs priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 7201acb27c6SJulian Elischer node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 721e1e1452dSArchie Cobbs return (0); 722e1e1452dSArchie Cobbs } 723e1e1452dSArchie Cobbs 724e1e1452dSArchie Cobbs /* 725e1e1452dSArchie Cobbs * Hook disconnection. 726e1e1452dSArchie Cobbs */ 727e1e1452dSArchie Cobbs static int 728e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook) 729e1e1452dSArchie Cobbs { 73030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 731e1e1452dSArchie Cobbs 732a3e232d6SArchie Cobbs if (hook == priv->upper) { 733e1e1452dSArchie Cobbs priv->upper = NULL; 734a3e232d6SArchie Cobbs priv->ifp->if_hwassist = priv->hwassist; /* restore h/w csum */ 735a3e232d6SArchie Cobbs } else if (hook == priv->lower) { 736e1e1452dSArchie Cobbs priv->lower = NULL; 737e1e1452dSArchie Cobbs priv->lowerOrphan = 0; 738e1e1452dSArchie Cobbs } else 7396e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 74030400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 74130400f03SJulian Elischer && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 74230400f03SJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 743e1e1452dSArchie Cobbs return (0); 744e1e1452dSArchie Cobbs } 745e1e1452dSArchie Cobbs 74656045c66SArchie Cobbs static int 74756045c66SArchie Cobbs ng_enaddr_parse(const struct ng_parse_type *type, 74856045c66SArchie Cobbs const char *s, int *const off, const u_char *const start, 74956045c66SArchie Cobbs u_char *const buf, int *const buflen) 75056045c66SArchie Cobbs { 75156045c66SArchie Cobbs char *eptr; 75256045c66SArchie Cobbs u_long val; 75356045c66SArchie Cobbs int i; 75456045c66SArchie Cobbs 75556045c66SArchie Cobbs if (*buflen < ETHER_ADDR_LEN) 75656045c66SArchie Cobbs return (ERANGE); 75756045c66SArchie Cobbs for (i = 0; i < ETHER_ADDR_LEN; i++) { 75856045c66SArchie Cobbs val = strtoul(s + *off, &eptr, 16); 75956045c66SArchie Cobbs if (val > 0xff || eptr == s + *off) 76056045c66SArchie Cobbs return (EINVAL); 76156045c66SArchie Cobbs buf[i] = (u_char)val; 76256045c66SArchie Cobbs *off = (eptr - s); 76356045c66SArchie Cobbs if (i < ETHER_ADDR_LEN - 1) { 76456045c66SArchie Cobbs if (*eptr != ':') 76556045c66SArchie Cobbs return (EINVAL); 76656045c66SArchie Cobbs (*off)++; 76756045c66SArchie Cobbs } 76856045c66SArchie Cobbs } 76956045c66SArchie Cobbs *buflen = ETHER_ADDR_LEN; 77056045c66SArchie Cobbs return (0); 77156045c66SArchie Cobbs } 77256045c66SArchie Cobbs 77356045c66SArchie Cobbs static int 77456045c66SArchie Cobbs ng_enaddr_unparse(const struct ng_parse_type *type, 77556045c66SArchie Cobbs const u_char *data, int *off, char *cbuf, int cbuflen) 77656045c66SArchie Cobbs { 77756045c66SArchie Cobbs int len; 77856045c66SArchie Cobbs 77956045c66SArchie Cobbs len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", 7807f98dc04SArchie Cobbs data[*off], data[*off + 1], data[*off + 2], 7817f98dc04SArchie Cobbs data[*off + 3], data[*off + 4], data[*off + 5]); 78256045c66SArchie Cobbs if (len >= cbuflen) 78356045c66SArchie Cobbs return (ERANGE); 78456045c66SArchie Cobbs *off += ETHER_ADDR_LEN; 78556045c66SArchie Cobbs return (0); 78656045c66SArchie Cobbs } 78756045c66SArchie Cobbs 788e1e1452dSArchie Cobbs /****************************************************************** 789e1e1452dSArchie Cobbs INITIALIZATION 790e1e1452dSArchie Cobbs ******************************************************************/ 791e1e1452dSArchie Cobbs 792e1e1452dSArchie Cobbs /* 793e1e1452dSArchie Cobbs * Handle loading and unloading for this node type. 794e1e1452dSArchie Cobbs */ 795e1e1452dSArchie Cobbs static int 796e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data) 797e1e1452dSArchie Cobbs { 798e1e1452dSArchie Cobbs struct ifnet *ifp; 799e1e1452dSArchie Cobbs int error = 0; 800e1e1452dSArchie Cobbs int s; 801e1e1452dSArchie Cobbs 802e1e1452dSArchie Cobbs s = splnet(); 803e1e1452dSArchie Cobbs switch (event) { 804e1e1452dSArchie Cobbs case MOD_LOAD: 805e1e1452dSArchie Cobbs 806e1e1452dSArchie Cobbs /* Register function hooks */ 807e1e1452dSArchie Cobbs if (ng_ether_attach_p != NULL) { 808e1e1452dSArchie Cobbs error = EEXIST; 809e1e1452dSArchie Cobbs break; 810e1e1452dSArchie Cobbs } 811e1e1452dSArchie Cobbs ng_ether_attach_p = ng_ether_attach; 812e1e1452dSArchie Cobbs ng_ether_detach_p = ng_ether_detach; 813e1e1452dSArchie Cobbs ng_ether_output_p = ng_ether_output; 814e1e1452dSArchie Cobbs ng_ether_input_p = ng_ether_input; 815e1e1452dSArchie Cobbs ng_ether_input_orphan_p = ng_ether_input_orphan; 816e1e1452dSArchie Cobbs 817e1e1452dSArchie Cobbs /* Create nodes for any already-existing Ethernet interfaces */ 818e1e1452dSArchie Cobbs TAILQ_FOREACH(ifp, &ifnet, if_link) { 819cf2010b8SArchie Cobbs if (ifp->if_type == IFT_ETHER 820cf2010b8SArchie Cobbs || ifp->if_type == IFT_L2VLAN) 821e1e1452dSArchie Cobbs ng_ether_attach(ifp); 822e1e1452dSArchie Cobbs } 823e1e1452dSArchie Cobbs break; 824e1e1452dSArchie Cobbs 825e1e1452dSArchie Cobbs case MOD_UNLOAD: 826e1e1452dSArchie Cobbs 827e1e1452dSArchie Cobbs /* 828e1e1452dSArchie Cobbs * Note that the base code won't try to unload us until 829e1e1452dSArchie Cobbs * all nodes have been removed, and that can't happen 830e1e1452dSArchie Cobbs * until all Ethernet interfaces are removed. In any 831e1e1452dSArchie Cobbs * case, we know there are no nodes left if the action 832e1e1452dSArchie Cobbs * is MOD_UNLOAD, so there's no need to detach any nodes. 833e1e1452dSArchie Cobbs */ 834e1e1452dSArchie Cobbs 835e1e1452dSArchie Cobbs /* Unregister function hooks */ 836e1e1452dSArchie Cobbs ng_ether_attach_p = NULL; 837e1e1452dSArchie Cobbs ng_ether_detach_p = NULL; 838e1e1452dSArchie Cobbs ng_ether_output_p = NULL; 839e1e1452dSArchie Cobbs ng_ether_input_p = NULL; 840e1e1452dSArchie Cobbs ng_ether_input_orphan_p = NULL; 841e1e1452dSArchie Cobbs break; 842e1e1452dSArchie Cobbs 843e1e1452dSArchie Cobbs default: 844e1e1452dSArchie Cobbs error = EOPNOTSUPP; 845e1e1452dSArchie Cobbs break; 846e1e1452dSArchie Cobbs } 847e1e1452dSArchie Cobbs splx(s); 848e1e1452dSArchie Cobbs return (error); 849e1e1452dSArchie Cobbs } 850e1e1452dSArchie Cobbs 851