1 2/* 3 * ng_ether.c 4 * 5 * Copyright (c) 1996-2000 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Authors: Archie Cobbs <archie@freebsd.org> 38 * Julian Elischer <julian@freebsd.org> 39 *
| 1 2/* 3 * ng_ether.c 4 * 5 * Copyright (c) 1996-2000 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Authors: Archie Cobbs <archie@freebsd.org> 38 * Julian Elischer <julian@freebsd.org> 39 *
|
40 * $FreeBSD: head/sys/netgraph/ng_ether.c 129823 2004-05-29 00:51:19Z julian $
| 40 * $FreeBSD: head/sys/netgraph/ng_ether.c 131155 2004-06-26 22:24:16Z julian $
|
41 */ 42 43/* 44 * ng_ether(4) netgraph node type 45 */ 46 47#include <sys/param.h> 48#include <sys/systm.h> 49#include <sys/kernel.h> 50#include <sys/malloc.h> 51#include <sys/mbuf.h> 52#include <sys/errno.h> 53#include <sys/syslog.h> 54#include <sys/socket.h> 55 56#include <net/if.h> 57#include <net/if_types.h> 58#include <net/if_arp.h> 59#include <net/if_var.h> 60#include <net/ethernet.h> 61 62#include <netgraph/ng_message.h> 63#include <netgraph/netgraph.h> 64#include <netgraph/ng_parse.h> 65#include <netgraph/ng_ether.h> 66 67#define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) 68 69/* Per-node private data */ 70struct private { 71 struct ifnet *ifp; /* associated interface */ 72 hook_p upper; /* upper hook connection */ 73 hook_p lower; /* lower hook connection */ 74 hook_p orphan; /* orphan hook connection */ 75 u_char autoSrcAddr; /* always overwrite source address */ 76 u_char promisc; /* promiscuous mode enabled */ 77 u_long hwassist; /* hardware checksum capabilities */ 78 u_int flags; /* flags e.g. really die */ 79}; 80typedef struct private *priv_p; 81 82/* Hook pointers used by if_ethersubr.c to callback to netgraph */ 83extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 84extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 85extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 86extern void (*ng_ether_attach_p)(struct ifnet *ifp); 87extern void (*ng_ether_detach_p)(struct ifnet *ifp); 88 89/* Functional hooks called from if_ethersubr.c */ 90static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 91static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 92static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 93static void ng_ether_attach(struct ifnet *ifp); 94static void ng_ether_detach(struct ifnet *ifp); 95 96/* Other functions */
| 41 */ 42 43/* 44 * ng_ether(4) netgraph node type 45 */ 46 47#include <sys/param.h> 48#include <sys/systm.h> 49#include <sys/kernel.h> 50#include <sys/malloc.h> 51#include <sys/mbuf.h> 52#include <sys/errno.h> 53#include <sys/syslog.h> 54#include <sys/socket.h> 55 56#include <net/if.h> 57#include <net/if_types.h> 58#include <net/if_arp.h> 59#include <net/if_var.h> 60#include <net/ethernet.h> 61 62#include <netgraph/ng_message.h> 63#include <netgraph/netgraph.h> 64#include <netgraph/ng_parse.h> 65#include <netgraph/ng_ether.h> 66 67#define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) 68 69/* Per-node private data */ 70struct private { 71 struct ifnet *ifp; /* associated interface */ 72 hook_p upper; /* upper hook connection */ 73 hook_p lower; /* lower hook connection */ 74 hook_p orphan; /* orphan hook connection */ 75 u_char autoSrcAddr; /* always overwrite source address */ 76 u_char promisc; /* promiscuous mode enabled */ 77 u_long hwassist; /* hardware checksum capabilities */ 78 u_int flags; /* flags e.g. really die */ 79}; 80typedef struct private *priv_p; 81 82/* Hook pointers used by if_ethersubr.c to callback to netgraph */ 83extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 84extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 85extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 86extern void (*ng_ether_attach_p)(struct ifnet *ifp); 87extern void (*ng_ether_detach_p)(struct ifnet *ifp); 88 89/* Functional hooks called from if_ethersubr.c */ 90static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 91static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 92static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 93static void ng_ether_attach(struct ifnet *ifp); 94static void ng_ether_detach(struct ifnet *ifp); 95 96/* Other functions */
|
97static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 98static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta);
| 97static int ng_ether_rcv_lower(node_p node, struct mbuf *m); 98static int ng_ether_rcv_upper(node_p node, struct mbuf *m);
|
99 100/* Netgraph node methods */ 101static ng_constructor_t ng_ether_constructor; 102static ng_rcvmsg_t ng_ether_rcvmsg; 103static ng_shutdown_t ng_ether_shutdown; 104static ng_newhook_t ng_ether_newhook; 105static ng_connect_t ng_ether_connect; 106static ng_rcvdata_t ng_ether_rcvdata; 107static ng_disconnect_t ng_ether_disconnect; 108static int ng_ether_mod_event(module_t mod, int event, void *data); 109 110/* List of commands and how to convert arguments to/from ASCII */ 111static const struct ng_cmdlist ng_ether_cmdlist[] = { 112 { 113 NGM_ETHER_COOKIE, 114 NGM_ETHER_GET_IFNAME, 115 "getifname", 116 NULL, 117 &ng_parse_string_type 118 }, 119 { 120 NGM_ETHER_COOKIE, 121 NGM_ETHER_GET_IFINDEX, 122 "getifindex", 123 NULL, 124 &ng_parse_int32_type 125 }, 126 { 127 NGM_ETHER_COOKIE, 128 NGM_ETHER_GET_ENADDR, 129 "getenaddr", 130 NULL, 131 &ng_parse_enaddr_type 132 }, 133 { 134 NGM_ETHER_COOKIE, 135 NGM_ETHER_SET_ENADDR, 136 "setenaddr", 137 &ng_parse_enaddr_type, 138 NULL 139 }, 140 { 141 NGM_ETHER_COOKIE, 142 NGM_ETHER_GET_PROMISC, 143 "getpromisc", 144 NULL, 145 &ng_parse_int32_type 146 }, 147 { 148 NGM_ETHER_COOKIE, 149 NGM_ETHER_SET_PROMISC, 150 "setpromisc", 151 &ng_parse_int32_type, 152 NULL 153 }, 154 { 155 NGM_ETHER_COOKIE, 156 NGM_ETHER_GET_AUTOSRC, 157 "getautosrc", 158 NULL, 159 &ng_parse_int32_type 160 }, 161 { 162 NGM_ETHER_COOKIE, 163 NGM_ETHER_SET_AUTOSRC, 164 "setautosrc", 165 &ng_parse_int32_type, 166 NULL 167 }, 168 { 0 } 169}; 170 171static struct ng_type ng_ether_typestruct = { 172 .version = NG_ABI_VERSION, 173 .name = NG_ETHER_NODE_TYPE, 174 .mod_event = ng_ether_mod_event, 175 .constructor = ng_ether_constructor, 176 .rcvmsg = ng_ether_rcvmsg, 177 .shutdown = ng_ether_shutdown, 178 .newhook = ng_ether_newhook, 179 .connect = ng_ether_connect, 180 .rcvdata = ng_ether_rcvdata, 181 .disconnect = ng_ether_disconnect, 182 .cmdlist = ng_ether_cmdlist, 183}; 184MODULE_VERSION(ng_ether, 1); 185NETGRAPH_INIT(ether, &ng_ether_typestruct); 186 187/****************************************************************** 188 ETHERNET FUNCTION HOOKS 189******************************************************************/ 190 191/* 192 * Handle a packet that has come in on an interface. We get to 193 * look at it here before any upper layer protocols do. 194 * 195 * NOTE: this function will get called at splimp() 196 */ 197static void 198ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 199{ 200 const node_p node = IFP2NG(ifp); 201 const priv_p priv = NG_NODE_PRIVATE(node); 202 int error; 203 204 /* If "lower" hook not connected, let packet continue */ 205 if (priv->lower == NULL) 206 return; 207 NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 208} 209 210/* 211 * Handle a packet that has come in on an interface, and which 212 * does not match any of our known protocols (an ``orphan''). 213 * 214 * NOTE: this function will get called at splimp() 215 */ 216static void 217ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 218{ 219 const node_p node = IFP2NG(ifp); 220 const priv_p priv = NG_NODE_PRIVATE(node); 221 int error; 222 223 /* If "orphan" hook not connected, discard packet */ 224 if (priv->orphan == NULL) { 225 m_freem(m); 226 return; 227 } 228 NG_SEND_DATA_ONLY(error, priv->orphan, m); 229} 230 231/* 232 * Handle a packet that is going out on an interface. 233 * The Ethernet header is already attached to the mbuf. 234 */ 235static int 236ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 237{ 238 const node_p node = IFP2NG(ifp); 239 const priv_p priv = NG_NODE_PRIVATE(node); 240 int error = 0; 241 242 /* If "upper" hook not connected, let packet continue */ 243 if (priv->upper == NULL) 244 return (0); 245 246 /* Send it out "upper" hook */ 247 NG_SEND_DATA_ONLY(error, priv->upper, *mp); 248 return (error); 249} 250 251/* 252 * A new Ethernet interface has been attached. 253 * Create a new node for it, etc. 254 */ 255static void 256ng_ether_attach(struct ifnet *ifp) 257{ 258 priv_p priv; 259 node_p node; 260 261 /* Create node */ 262 KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 263 if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 264 log(LOG_ERR, "%s: can't %s for %s\n", 265 __func__, "create node", ifp->if_xname); 266 return; 267 } 268 269 /* Allocate private data */ 270 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 271 if (priv == NULL) { 272 log(LOG_ERR, "%s: can't %s for %s\n", 273 __func__, "allocate memory", ifp->if_xname); 274 NG_NODE_UNREF(node); 275 return; 276 } 277 NG_NODE_SET_PRIVATE(node, priv); 278 priv->ifp = ifp; 279 IFP2NG(ifp) = node; 280 priv->autoSrcAddr = 1; 281 priv->hwassist = ifp->if_hwassist; 282 283 /* Try to give the node the same name as the interface */ 284 if (ng_name_node(node, ifp->if_xname) != 0) { 285 log(LOG_WARNING, "%s: can't name node %s\n", 286 __func__, ifp->if_xname); 287 } 288} 289 290/* 291 * An Ethernet interface is being detached. 292 * REALLY Destroy its node. 293 */ 294static void 295ng_ether_detach(struct ifnet *ifp) 296{ 297 const node_p node = IFP2NG(ifp); 298 const priv_p priv = NG_NODE_PRIVATE(node); 299 300 if (node == NULL) /* no node (why not?), ignore */ 301 return; 302 NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 303 /* 304 * We can't assume the ifnet is still around when we run shutdown 305 * So zap it now. XXX We HOPE that anything running at this time 306 * handles it (as it should in the non netgraph case). 307 */ 308 IFP2NG(ifp) = NULL; 309 priv->ifp = NULL; /* XXX race if interrupted an output packet */ 310 ng_rmnode_self(node); /* remove all netgraph parts */ 311} 312 313/****************************************************************** 314 NETGRAPH NODE METHODS 315******************************************************************/ 316 317/* 318 * It is not possible or allowable to create a node of this type. 319 * Nodes get created when the interface is attached (or, when 320 * this node type's KLD is loaded). 321 */ 322static int 323ng_ether_constructor(node_p node) 324{ 325 return (EINVAL); 326} 327 328/* 329 * Check for attaching a new hook. 330 */ 331static int 332ng_ether_newhook(node_p node, hook_p hook, const char *name) 333{ 334 const priv_p priv = NG_NODE_PRIVATE(node); 335 hook_p *hookptr; 336 337 /* Divert hook is an alias for lower */ 338 if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 339 name = NG_ETHER_HOOK_LOWER; 340 341 /* Which hook? */ 342 if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 343 hookptr = &priv->upper; 344 else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) 345 hookptr = &priv->lower; 346 else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) 347 hookptr = &priv->orphan; 348 else 349 return (EINVAL); 350 351 /* Check if already connected (shouldn't be, but doesn't hurt) */ 352 if (*hookptr != NULL) 353 return (EISCONN); 354 355 /* Disable hardware checksums while 'upper' hook is connected */ 356 if (hookptr == &priv->upper) 357 priv->ifp->if_hwassist = 0; 358 359 /* OK */ 360 *hookptr = hook; 361 return (0); 362} 363 364/* 365 * Hooks are attached, adjust to force queueing. 366 * We don't really care which hook it is. 367 * they should all be queuing for outgoing data. 368 */ 369static int 370ng_ether_connect(hook_p hook) 371{ 372 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 373 return (0); 374} 375 376/* 377 * Receive an incoming control message. 378 */ 379static int 380ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 381{ 382 const priv_p priv = NG_NODE_PRIVATE(node); 383 struct ng_mesg *resp = NULL; 384 int error = 0; 385 struct ng_mesg *msg; 386 387 NGI_GET_MSG(item, msg); 388 switch (msg->header.typecookie) { 389 case NGM_ETHER_COOKIE: 390 switch (msg->header.cmd) { 391 case NGM_ETHER_GET_IFNAME: 392 NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 393 if (resp == NULL) { 394 error = ENOMEM; 395 break; 396 } 397 strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ + 1); 398 break; 399 case NGM_ETHER_GET_IFINDEX: 400 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 401 if (resp == NULL) { 402 error = ENOMEM; 403 break; 404 } 405 *((u_int32_t *)resp->data) = priv->ifp->if_index; 406 break; 407 case NGM_ETHER_GET_ENADDR: 408 NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 409 if (resp == NULL) { 410 error = ENOMEM; 411 break; 412 } 413 bcopy((IFP2AC(priv->ifp))->ac_enaddr, 414 resp->data, ETHER_ADDR_LEN); 415 break; 416 case NGM_ETHER_SET_ENADDR: 417 { 418 if (msg->header.arglen != ETHER_ADDR_LEN) { 419 error = EINVAL; 420 break; 421 } 422 error = if_setlladdr(priv->ifp, 423 (u_char *)msg->data, ETHER_ADDR_LEN); 424 break; 425 } 426 case NGM_ETHER_GET_PROMISC: 427 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 428 if (resp == NULL) { 429 error = ENOMEM; 430 break; 431 } 432 *((u_int32_t *)resp->data) = priv->promisc; 433 break; 434 case NGM_ETHER_SET_PROMISC: 435 { 436 u_char want; 437 438 if (msg->header.arglen != sizeof(u_int32_t)) { 439 error = EINVAL; 440 break; 441 } 442 want = !!*((u_int32_t *)msg->data); 443 if (want ^ priv->promisc) { 444 if ((error = ifpromisc(priv->ifp, want)) != 0) 445 break; 446 priv->promisc = want; 447 } 448 break; 449 } 450 case NGM_ETHER_GET_AUTOSRC: 451 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 452 if (resp == NULL) { 453 error = ENOMEM; 454 break; 455 } 456 *((u_int32_t *)resp->data) = priv->autoSrcAddr; 457 break; 458 case NGM_ETHER_SET_AUTOSRC: 459 if (msg->header.arglen != sizeof(u_int32_t)) { 460 error = EINVAL; 461 break; 462 } 463 priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 464 break; 465 default: 466 error = EINVAL; 467 break; 468 } 469 break; 470 default: 471 error = EINVAL; 472 break; 473 } 474 NG_RESPOND_MSG(error, node, item, resp); 475 NG_FREE_MSG(msg); 476 return (error); 477} 478 479/* 480 * Receive data on a hook. 481 */ 482static int 483ng_ether_rcvdata(hook_p hook, item_p item) 484{ 485 const node_p node = NG_HOOK_NODE(hook); 486 const priv_p priv = NG_NODE_PRIVATE(node); 487 struct mbuf *m;
| 99 100/* Netgraph node methods */ 101static ng_constructor_t ng_ether_constructor; 102static ng_rcvmsg_t ng_ether_rcvmsg; 103static ng_shutdown_t ng_ether_shutdown; 104static ng_newhook_t ng_ether_newhook; 105static ng_connect_t ng_ether_connect; 106static ng_rcvdata_t ng_ether_rcvdata; 107static ng_disconnect_t ng_ether_disconnect; 108static int ng_ether_mod_event(module_t mod, int event, void *data); 109 110/* List of commands and how to convert arguments to/from ASCII */ 111static const struct ng_cmdlist ng_ether_cmdlist[] = { 112 { 113 NGM_ETHER_COOKIE, 114 NGM_ETHER_GET_IFNAME, 115 "getifname", 116 NULL, 117 &ng_parse_string_type 118 }, 119 { 120 NGM_ETHER_COOKIE, 121 NGM_ETHER_GET_IFINDEX, 122 "getifindex", 123 NULL, 124 &ng_parse_int32_type 125 }, 126 { 127 NGM_ETHER_COOKIE, 128 NGM_ETHER_GET_ENADDR, 129 "getenaddr", 130 NULL, 131 &ng_parse_enaddr_type 132 }, 133 { 134 NGM_ETHER_COOKIE, 135 NGM_ETHER_SET_ENADDR, 136 "setenaddr", 137 &ng_parse_enaddr_type, 138 NULL 139 }, 140 { 141 NGM_ETHER_COOKIE, 142 NGM_ETHER_GET_PROMISC, 143 "getpromisc", 144 NULL, 145 &ng_parse_int32_type 146 }, 147 { 148 NGM_ETHER_COOKIE, 149 NGM_ETHER_SET_PROMISC, 150 "setpromisc", 151 &ng_parse_int32_type, 152 NULL 153 }, 154 { 155 NGM_ETHER_COOKIE, 156 NGM_ETHER_GET_AUTOSRC, 157 "getautosrc", 158 NULL, 159 &ng_parse_int32_type 160 }, 161 { 162 NGM_ETHER_COOKIE, 163 NGM_ETHER_SET_AUTOSRC, 164 "setautosrc", 165 &ng_parse_int32_type, 166 NULL 167 }, 168 { 0 } 169}; 170 171static struct ng_type ng_ether_typestruct = { 172 .version = NG_ABI_VERSION, 173 .name = NG_ETHER_NODE_TYPE, 174 .mod_event = ng_ether_mod_event, 175 .constructor = ng_ether_constructor, 176 .rcvmsg = ng_ether_rcvmsg, 177 .shutdown = ng_ether_shutdown, 178 .newhook = ng_ether_newhook, 179 .connect = ng_ether_connect, 180 .rcvdata = ng_ether_rcvdata, 181 .disconnect = ng_ether_disconnect, 182 .cmdlist = ng_ether_cmdlist, 183}; 184MODULE_VERSION(ng_ether, 1); 185NETGRAPH_INIT(ether, &ng_ether_typestruct); 186 187/****************************************************************** 188 ETHERNET FUNCTION HOOKS 189******************************************************************/ 190 191/* 192 * Handle a packet that has come in on an interface. We get to 193 * look at it here before any upper layer protocols do. 194 * 195 * NOTE: this function will get called at splimp() 196 */ 197static void 198ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 199{ 200 const node_p node = IFP2NG(ifp); 201 const priv_p priv = NG_NODE_PRIVATE(node); 202 int error; 203 204 /* If "lower" hook not connected, let packet continue */ 205 if (priv->lower == NULL) 206 return; 207 NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ 208} 209 210/* 211 * Handle a packet that has come in on an interface, and which 212 * does not match any of our known protocols (an ``orphan''). 213 * 214 * NOTE: this function will get called at splimp() 215 */ 216static void 217ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 218{ 219 const node_p node = IFP2NG(ifp); 220 const priv_p priv = NG_NODE_PRIVATE(node); 221 int error; 222 223 /* If "orphan" hook not connected, discard packet */ 224 if (priv->orphan == NULL) { 225 m_freem(m); 226 return; 227 } 228 NG_SEND_DATA_ONLY(error, priv->orphan, m); 229} 230 231/* 232 * Handle a packet that is going out on an interface. 233 * The Ethernet header is already attached to the mbuf. 234 */ 235static int 236ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 237{ 238 const node_p node = IFP2NG(ifp); 239 const priv_p priv = NG_NODE_PRIVATE(node); 240 int error = 0; 241 242 /* If "upper" hook not connected, let packet continue */ 243 if (priv->upper == NULL) 244 return (0); 245 246 /* Send it out "upper" hook */ 247 NG_SEND_DATA_ONLY(error, priv->upper, *mp); 248 return (error); 249} 250 251/* 252 * A new Ethernet interface has been attached. 253 * Create a new node for it, etc. 254 */ 255static void 256ng_ether_attach(struct ifnet *ifp) 257{ 258 priv_p priv; 259 node_p node; 260 261 /* Create node */ 262 KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 263 if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 264 log(LOG_ERR, "%s: can't %s for %s\n", 265 __func__, "create node", ifp->if_xname); 266 return; 267 } 268 269 /* Allocate private data */ 270 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 271 if (priv == NULL) { 272 log(LOG_ERR, "%s: can't %s for %s\n", 273 __func__, "allocate memory", ifp->if_xname); 274 NG_NODE_UNREF(node); 275 return; 276 } 277 NG_NODE_SET_PRIVATE(node, priv); 278 priv->ifp = ifp; 279 IFP2NG(ifp) = node; 280 priv->autoSrcAddr = 1; 281 priv->hwassist = ifp->if_hwassist; 282 283 /* Try to give the node the same name as the interface */ 284 if (ng_name_node(node, ifp->if_xname) != 0) { 285 log(LOG_WARNING, "%s: can't name node %s\n", 286 __func__, ifp->if_xname); 287 } 288} 289 290/* 291 * An Ethernet interface is being detached. 292 * REALLY Destroy its node. 293 */ 294static void 295ng_ether_detach(struct ifnet *ifp) 296{ 297 const node_p node = IFP2NG(ifp); 298 const priv_p priv = NG_NODE_PRIVATE(node); 299 300 if (node == NULL) /* no node (why not?), ignore */ 301 return; 302 NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 303 /* 304 * We can't assume the ifnet is still around when we run shutdown 305 * So zap it now. XXX We HOPE that anything running at this time 306 * handles it (as it should in the non netgraph case). 307 */ 308 IFP2NG(ifp) = NULL; 309 priv->ifp = NULL; /* XXX race if interrupted an output packet */ 310 ng_rmnode_self(node); /* remove all netgraph parts */ 311} 312 313/****************************************************************** 314 NETGRAPH NODE METHODS 315******************************************************************/ 316 317/* 318 * It is not possible or allowable to create a node of this type. 319 * Nodes get created when the interface is attached (or, when 320 * this node type's KLD is loaded). 321 */ 322static int 323ng_ether_constructor(node_p node) 324{ 325 return (EINVAL); 326} 327 328/* 329 * Check for attaching a new hook. 330 */ 331static int 332ng_ether_newhook(node_p node, hook_p hook, const char *name) 333{ 334 const priv_p priv = NG_NODE_PRIVATE(node); 335 hook_p *hookptr; 336 337 /* Divert hook is an alias for lower */ 338 if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 339 name = NG_ETHER_HOOK_LOWER; 340 341 /* Which hook? */ 342 if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 343 hookptr = &priv->upper; 344 else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) 345 hookptr = &priv->lower; 346 else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) 347 hookptr = &priv->orphan; 348 else 349 return (EINVAL); 350 351 /* Check if already connected (shouldn't be, but doesn't hurt) */ 352 if (*hookptr != NULL) 353 return (EISCONN); 354 355 /* Disable hardware checksums while 'upper' hook is connected */ 356 if (hookptr == &priv->upper) 357 priv->ifp->if_hwassist = 0; 358 359 /* OK */ 360 *hookptr = hook; 361 return (0); 362} 363 364/* 365 * Hooks are attached, adjust to force queueing. 366 * We don't really care which hook it is. 367 * they should all be queuing for outgoing data. 368 */ 369static int 370ng_ether_connect(hook_p hook) 371{ 372 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 373 return (0); 374} 375 376/* 377 * Receive an incoming control message. 378 */ 379static int 380ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 381{ 382 const priv_p priv = NG_NODE_PRIVATE(node); 383 struct ng_mesg *resp = NULL; 384 int error = 0; 385 struct ng_mesg *msg; 386 387 NGI_GET_MSG(item, msg); 388 switch (msg->header.typecookie) { 389 case NGM_ETHER_COOKIE: 390 switch (msg->header.cmd) { 391 case NGM_ETHER_GET_IFNAME: 392 NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 393 if (resp == NULL) { 394 error = ENOMEM; 395 break; 396 } 397 strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ + 1); 398 break; 399 case NGM_ETHER_GET_IFINDEX: 400 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 401 if (resp == NULL) { 402 error = ENOMEM; 403 break; 404 } 405 *((u_int32_t *)resp->data) = priv->ifp->if_index; 406 break; 407 case NGM_ETHER_GET_ENADDR: 408 NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 409 if (resp == NULL) { 410 error = ENOMEM; 411 break; 412 } 413 bcopy((IFP2AC(priv->ifp))->ac_enaddr, 414 resp->data, ETHER_ADDR_LEN); 415 break; 416 case NGM_ETHER_SET_ENADDR: 417 { 418 if (msg->header.arglen != ETHER_ADDR_LEN) { 419 error = EINVAL; 420 break; 421 } 422 error = if_setlladdr(priv->ifp, 423 (u_char *)msg->data, ETHER_ADDR_LEN); 424 break; 425 } 426 case NGM_ETHER_GET_PROMISC: 427 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 428 if (resp == NULL) { 429 error = ENOMEM; 430 break; 431 } 432 *((u_int32_t *)resp->data) = priv->promisc; 433 break; 434 case NGM_ETHER_SET_PROMISC: 435 { 436 u_char want; 437 438 if (msg->header.arglen != sizeof(u_int32_t)) { 439 error = EINVAL; 440 break; 441 } 442 want = !!*((u_int32_t *)msg->data); 443 if (want ^ priv->promisc) { 444 if ((error = ifpromisc(priv->ifp, want)) != 0) 445 break; 446 priv->promisc = want; 447 } 448 break; 449 } 450 case NGM_ETHER_GET_AUTOSRC: 451 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 452 if (resp == NULL) { 453 error = ENOMEM; 454 break; 455 } 456 *((u_int32_t *)resp->data) = priv->autoSrcAddr; 457 break; 458 case NGM_ETHER_SET_AUTOSRC: 459 if (msg->header.arglen != sizeof(u_int32_t)) { 460 error = EINVAL; 461 break; 462 } 463 priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 464 break; 465 default: 466 error = EINVAL; 467 break; 468 } 469 break; 470 default: 471 error = EINVAL; 472 break; 473 } 474 NG_RESPOND_MSG(error, node, item, resp); 475 NG_FREE_MSG(msg); 476 return (error); 477} 478 479/* 480 * Receive data on a hook. 481 */ 482static int 483ng_ether_rcvdata(hook_p hook, item_p item) 484{ 485 const node_p node = NG_HOOK_NODE(hook); 486 const priv_p priv = NG_NODE_PRIVATE(node); 487 struct mbuf *m;
|
488 meta_p meta;
| |
489 490 NGI_GET_M(item, m);
| 488 489 NGI_GET_M(item, m);
|
491 NGI_GET_META(item, meta);
| |
492 NG_FREE_ITEM(item);
| 490 NG_FREE_ITEM(item);
|
| 491
|
493 if (hook == priv->lower || hook == priv->orphan)
| 492 if (hook == priv->lower || hook == priv->orphan)
|
494 return ng_ether_rcv_lower(node, m, meta);
| 493 return ng_ether_rcv_lower(node, m);
|
495 if (hook == priv->upper)
| 494 if (hook == priv->upper)
|
496 return ng_ether_rcv_upper(node, m, meta);
| 495 return ng_ether_rcv_upper(node, m);
|
497 panic("%s: weird hook", __func__); 498#ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ 499 return NULL; 500#endif 501} 502 503/* 504 * Handle an mbuf received on the "lower" or "orphan" hook. 505 */ 506static int
| 496 panic("%s: weird hook", __func__); 497#ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ 498 return NULL; 499#endif 500} 501 502/* 503 * Handle an mbuf received on the "lower" or "orphan" hook. 504 */ 505static int
|
507ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta)
| 506ng_ether_rcv_lower(node_p node, struct mbuf *m)
|
508{ 509 const priv_p priv = NG_NODE_PRIVATE(node); 510 struct ifnet *const ifp = priv->ifp; 511
| 507{ 508 const priv_p priv = NG_NODE_PRIVATE(node); 509 struct ifnet *const ifp = priv->ifp; 510
|
512 /* Discard meta info */ 513 NG_FREE_META(meta); 514
| |
515 /* Check whether interface is ready for packets */ 516 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 517 NG_FREE_M(m); 518 return (ENETDOWN); 519 } 520 521 /* Make sure header is fully pulled up */ 522 if (m->m_pkthdr.len < sizeof(struct ether_header)) { 523 NG_FREE_M(m); 524 return (EINVAL); 525 } 526 if (m->m_len < sizeof(struct ether_header) 527 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 528 return (ENOBUFS); 529 530 /* Drop in the MAC address if desired */ 531 if (priv->autoSrcAddr) { 532 533 /* Make the mbuf writable if it's not already */ 534 if (!M_WRITABLE(m) 535 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 536 return (ENOBUFS); 537 538 /* Overwrite source MAC address */ 539 bcopy((IFP2AC(ifp))->ac_enaddr, 540 mtod(m, struct ether_header *)->ether_shost, 541 ETHER_ADDR_LEN); 542 } 543 544 /* Send it on its way */ 545 return ether_output_frame(ifp, m); 546} 547 548/* 549 * Handle an mbuf received on the "upper" hook. 550 */ 551static int
| 511 /* Check whether interface is ready for packets */ 512 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 513 NG_FREE_M(m); 514 return (ENETDOWN); 515 } 516 517 /* Make sure header is fully pulled up */ 518 if (m->m_pkthdr.len < sizeof(struct ether_header)) { 519 NG_FREE_M(m); 520 return (EINVAL); 521 } 522 if (m->m_len < sizeof(struct ether_header) 523 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 524 return (ENOBUFS); 525 526 /* Drop in the MAC address if desired */ 527 if (priv->autoSrcAddr) { 528 529 /* Make the mbuf writable if it's not already */ 530 if (!M_WRITABLE(m) 531 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 532 return (ENOBUFS); 533 534 /* Overwrite source MAC address */ 535 bcopy((IFP2AC(ifp))->ac_enaddr, 536 mtod(m, struct ether_header *)->ether_shost, 537 ETHER_ADDR_LEN); 538 } 539 540 /* Send it on its way */ 541 return ether_output_frame(ifp, m); 542} 543 544/* 545 * Handle an mbuf received on the "upper" hook. 546 */ 547static int
|
552ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta)
| 548ng_ether_rcv_upper(node_p node, struct mbuf *m)
|
553{ 554 const priv_p priv = NG_NODE_PRIVATE(node); 555
| 549{ 550 const priv_p priv = NG_NODE_PRIVATE(node); 551
|
556 /* Discard meta info */ 557 NG_FREE_META(meta); 558
| |
559 m->m_pkthdr.rcvif = priv->ifp; 560 561 /* Route packet back in */ 562 ether_demux(priv->ifp, m); 563 return (0); 564} 565 566/* 567 * Shutdown node. This resets the node but does not remove it 568 * unless the REALLY_DIE flag is set. 569 */ 570static int 571ng_ether_shutdown(node_p node) 572{ 573 const priv_p priv = NG_NODE_PRIVATE(node); 574 575 if (node->nd_flags & NG_REALLY_DIE) { 576 /* 577 * WE came here because the ethernet card is being unloaded, 578 * so stop being persistant. 579 * Actually undo all the things we did on creation. 580 * Assume the ifp has already been freed. 581 */ 582 NG_NODE_SET_PRIVATE(node, NULL); 583 FREE(priv, M_NETGRAPH); 584 NG_NODE_UNREF(node); /* free node itself */ 585 return (0); 586 } 587 if (priv->promisc) { /* disable promiscuous mode */ 588 (void)ifpromisc(priv->ifp, 0); 589 priv->promisc = 0; 590 } 591 priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 592 node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 593 return (0); 594} 595 596/* 597 * Hook disconnection. 598 */ 599static int 600ng_ether_disconnect(hook_p hook) 601{ 602 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 603 604 if (hook == priv->upper) { 605 priv->upper = NULL; 606 if (priv->ifp != NULL) /* restore h/w csum */ 607 priv->ifp->if_hwassist = priv->hwassist; 608 } else if (hook == priv->lower) 609 priv->lower = NULL; 610 else if (hook == priv->orphan) 611 priv->orphan = NULL; 612 else 613 panic("%s: weird hook", __func__); 614 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 615 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 616 ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 617 return (0); 618} 619 620/****************************************************************** 621 INITIALIZATION 622******************************************************************/ 623 624/* 625 * Handle loading and unloading for this node type. 626 */ 627static int 628ng_ether_mod_event(module_t mod, int event, void *data) 629{ 630 struct ifnet *ifp; 631 int error = 0; 632 int s; 633 634 s = splnet(); 635 switch (event) { 636 case MOD_LOAD: 637 638 /* Register function hooks */ 639 if (ng_ether_attach_p != NULL) { 640 error = EEXIST; 641 break; 642 } 643 ng_ether_attach_p = ng_ether_attach; 644 ng_ether_detach_p = ng_ether_detach; 645 ng_ether_output_p = ng_ether_output; 646 ng_ether_input_p = ng_ether_input; 647 ng_ether_input_orphan_p = ng_ether_input_orphan; 648 649 /* Create nodes for any already-existing Ethernet interfaces */ 650 IFNET_RLOCK(); 651 TAILQ_FOREACH(ifp, &ifnet, if_link) { 652 if (ifp->if_type == IFT_ETHER 653 || ifp->if_type == IFT_L2VLAN) 654 ng_ether_attach(ifp); 655 } 656 IFNET_RUNLOCK(); 657 break; 658 659 case MOD_UNLOAD: 660 661 /* 662 * Note that the base code won't try to unload us until 663 * all nodes have been removed, and that can't happen 664 * until all Ethernet interfaces are removed. In any 665 * case, we know there are no nodes left if the action 666 * is MOD_UNLOAD, so there's no need to detach any nodes. 667 */ 668 669 /* Unregister function hooks */ 670 ng_ether_attach_p = NULL; 671 ng_ether_detach_p = NULL; 672 ng_ether_output_p = NULL; 673 ng_ether_input_p = NULL; 674 ng_ether_input_orphan_p = NULL; 675 break; 676 677 default: 678 error = EOPNOTSUPP; 679 break; 680 } 681 splx(s); 682 return (error); 683} 684
| 552 m->m_pkthdr.rcvif = priv->ifp; 553 554 /* Route packet back in */ 555 ether_demux(priv->ifp, m); 556 return (0); 557} 558 559/* 560 * Shutdown node. This resets the node but does not remove it 561 * unless the REALLY_DIE flag is set. 562 */ 563static int 564ng_ether_shutdown(node_p node) 565{ 566 const priv_p priv = NG_NODE_PRIVATE(node); 567 568 if (node->nd_flags & NG_REALLY_DIE) { 569 /* 570 * WE came here because the ethernet card is being unloaded, 571 * so stop being persistant. 572 * Actually undo all the things we did on creation. 573 * Assume the ifp has already been freed. 574 */ 575 NG_NODE_SET_PRIVATE(node, NULL); 576 FREE(priv, M_NETGRAPH); 577 NG_NODE_UNREF(node); /* free node itself */ 578 return (0); 579 } 580 if (priv->promisc) { /* disable promiscuous mode */ 581 (void)ifpromisc(priv->ifp, 0); 582 priv->promisc = 0; 583 } 584 priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 585 node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 586 return (0); 587} 588 589/* 590 * Hook disconnection. 591 */ 592static int 593ng_ether_disconnect(hook_p hook) 594{ 595 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 596 597 if (hook == priv->upper) { 598 priv->upper = NULL; 599 if (priv->ifp != NULL) /* restore h/w csum */ 600 priv->ifp->if_hwassist = priv->hwassist; 601 } else if (hook == priv->lower) 602 priv->lower = NULL; 603 else if (hook == priv->orphan) 604 priv->orphan = NULL; 605 else 606 panic("%s: weird hook", __func__); 607 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 608 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 609 ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 610 return (0); 611} 612 613/****************************************************************** 614 INITIALIZATION 615******************************************************************/ 616 617/* 618 * Handle loading and unloading for this node type. 619 */ 620static int 621ng_ether_mod_event(module_t mod, int event, void *data) 622{ 623 struct ifnet *ifp; 624 int error = 0; 625 int s; 626 627 s = splnet(); 628 switch (event) { 629 case MOD_LOAD: 630 631 /* Register function hooks */ 632 if (ng_ether_attach_p != NULL) { 633 error = EEXIST; 634 break; 635 } 636 ng_ether_attach_p = ng_ether_attach; 637 ng_ether_detach_p = ng_ether_detach; 638 ng_ether_output_p = ng_ether_output; 639 ng_ether_input_p = ng_ether_input; 640 ng_ether_input_orphan_p = ng_ether_input_orphan; 641 642 /* Create nodes for any already-existing Ethernet interfaces */ 643 IFNET_RLOCK(); 644 TAILQ_FOREACH(ifp, &ifnet, if_link) { 645 if (ifp->if_type == IFT_ETHER 646 || ifp->if_type == IFT_L2VLAN) 647 ng_ether_attach(ifp); 648 } 649 IFNET_RUNLOCK(); 650 break; 651 652 case MOD_UNLOAD: 653 654 /* 655 * Note that the base code won't try to unload us until 656 * all nodes have been removed, and that can't happen 657 * until all Ethernet interfaces are removed. In any 658 * case, we know there are no nodes left if the action 659 * is MOD_UNLOAD, so there's no need to detach any nodes. 660 */ 661 662 /* Unregister function hooks */ 663 ng_ether_attach_p = NULL; 664 ng_ether_detach_p = NULL; 665 ng_ether_output_p = NULL; 666 ng_ether_input_p = NULL; 667 ng_ether_input_orphan_p = NULL; 668 break; 669 670 default: 671 error = EOPNOTSUPP; 672 break; 673 } 674 splx(s); 675 return (error); 676} 677
|