ng_ether.c revision 194012
162143Sarchie
262143Sarchie/*
362143Sarchie * ng_ether.c
4139823Simp */
5139823Simp
6139823Simp/*-
762143Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc.
862143Sarchie * All rights reserved.
962143Sarchie *
1062143Sarchie * Subject to the following obligations and disclaimer of warranty, use and
1162143Sarchie * redistribution of this software, in source or object code forms, with or
1262143Sarchie * without modifications are expressly permitted by Whistle Communications;
1362143Sarchie * provided, however, that:
1462143Sarchie * 1. Any and all reproductions of the source or object code must include the
1562143Sarchie *    copyright notice above and the following disclaimer of warranties; and
1662143Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1762143Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1862143Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1962143Sarchie *    such appears in the above copyright notice or in the software.
2062143Sarchie *
2162143Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2262143Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2362143Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2462143Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2562143Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2662143Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2762143Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2862143Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2962143Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
3062143Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3162143Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3262143Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3362143Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3462143Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3562143Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3662143Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3762143Sarchie * OF SUCH DAMAGE.
3862143Sarchie *
3962143Sarchie * Authors: Archie Cobbs <archie@freebsd.org>
4062143Sarchie *	    Julian Elischer <julian@freebsd.org>
4162143Sarchie *
4262143Sarchie * $FreeBSD: head/sys/netgraph/ng_ether.c 194012 2009-06-11 16:50:49Z zec $
4362143Sarchie */
4462143Sarchie
4562143Sarchie/*
4662143Sarchie * ng_ether(4) netgraph node type
4762143Sarchie */
4862143Sarchie
4962143Sarchie#include <sys/param.h>
5062143Sarchie#include <sys/systm.h>
5162143Sarchie#include <sys/kernel.h>
5262143Sarchie#include <sys/malloc.h>
5362143Sarchie#include <sys/mbuf.h>
5462143Sarchie#include <sys/errno.h>
5562143Sarchie#include <sys/syslog.h>
5662143Sarchie#include <sys/socket.h>
57181803Sbz#include <sys/vimage.h>
5862143Sarchie
5962143Sarchie#include <net/if.h>
60141721Sglebius#include <net/if_dl.h>
6162143Sarchie#include <net/if_types.h>
6262143Sarchie#include <net/if_arp.h>
6362143Sarchie#include <net/if_var.h>
6462143Sarchie#include <net/ethernet.h>
65151305Sthompsa#include <net/if_bridgevar.h>
66189106Sbz#include <net/route.h>
67185571Sbz#include <net/vnet.h>
6862143Sarchie
6962143Sarchie#include <netgraph/ng_message.h>
7062143Sarchie#include <netgraph/netgraph.h>
7162143Sarchie#include <netgraph/ng_parse.h>
7262143Sarchie#include <netgraph/ng_ether.h>
7362143Sarchie
74152243Sru#define IFP2NG(ifp)  (IFP2AC((ifp))->ac_netgraph)
7562143Sarchie
76191510Szecstatic vnet_attach_fn ng_ether_iattach;
77191510Szec
78191510Szec#ifndef VIMAGE_GLOBALS
79191510Szecstatic vnet_modinfo_t vnet_ng_ether_modinfo = {
80191510Szec	.vmi_id		= VNET_MOD_NG_ETHER,
81191510Szec	.vmi_name	= "ng_ether",
82191510Szec	.vmi_dependson	= VNET_MOD_NETGRAPH,
83191510Szec	.vmi_iattach	= ng_ether_iattach,
84191510Szec};
85191510Szec#endif
86191510Szec
87126035Spjd/* Per-node private data */
88126035Spjdstruct private {
89126035Spjd	struct ifnet	*ifp;		/* associated interface */
90126035Spjd	hook_p		upper;		/* upper hook connection */
91129281Sarchie	hook_p		lower;		/* lower hook connection */
92129281Sarchie	hook_p		orphan;		/* orphan hook connection */
93126035Spjd	u_char		autoSrcAddr;	/* always overwrite source address */
94126035Spjd	u_char		promisc;	/* promiscuous mode enabled */
95126035Spjd	u_long		hwassist;	/* hardware checksum capabilities */
96126035Spjd	u_int		flags;		/* flags e.g. really die */
97126035Spjd};
98126035Spjdtypedef struct private *priv_p;
9962143Sarchie
100106933Ssam/* Hook pointers used by if_ethersubr.c to callback to netgraph */
101106933Ssamextern	void	(*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
102106933Ssamextern	void	(*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
103106933Ssamextern	int	(*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
104106933Ssamextern	void	(*ng_ether_attach_p)(struct ifnet *ifp);
105106933Ssamextern	void	(*ng_ether_detach_p)(struct ifnet *ifp);
106139903Sglebiusextern	void	(*ng_ether_link_state_p)(struct ifnet *ifp, int state);
107106933Ssam
10862143Sarchie/* Functional hooks called from if_ethersubr.c */
109106933Ssamstatic void	ng_ether_input(struct ifnet *ifp, struct mbuf **mp);
110106933Ssamstatic void	ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m);
11162143Sarchiestatic int	ng_ether_output(struct ifnet *ifp, struct mbuf **mp);
11262143Sarchiestatic void	ng_ether_attach(struct ifnet *ifp);
11362143Sarchiestatic void	ng_ether_detach(struct ifnet *ifp);
114139903Sglebiusstatic void	ng_ether_link_state(struct ifnet *ifp, int state);
11562143Sarchie
11662143Sarchie/* Other functions */
117186488Sjulianstatic int	ng_ether_rcv_lower(hook_p node, item_p item);
118186488Sjulianstatic int	ng_ether_rcv_upper(hook_p node, item_p item);
11962143Sarchie
12062143Sarchie/* Netgraph node methods */
12162143Sarchiestatic ng_constructor_t	ng_ether_constructor;
12262143Sarchiestatic ng_rcvmsg_t	ng_ether_rcvmsg;
12370700Sjulianstatic ng_shutdown_t	ng_ether_shutdown;
12462143Sarchiestatic ng_newhook_t	ng_ether_newhook;
12562143Sarchiestatic ng_rcvdata_t	ng_ether_rcvdata;
12662143Sarchiestatic ng_disconnect_t	ng_ether_disconnect;
12762143Sarchiestatic int		ng_ether_mod_event(module_t mod, int event, void *data);
12862143Sarchie
12962143Sarchie/* List of commands and how to convert arguments to/from ASCII */
13062143Sarchiestatic const struct ng_cmdlist ng_ether_cmdlist[] = {
13162143Sarchie	{
13262143Sarchie	  NGM_ETHER_COOKIE,
13362143Sarchie	  NGM_ETHER_GET_IFNAME,
13462143Sarchie	  "getifname",
13562143Sarchie	  NULL,
13662143Sarchie	  &ng_parse_string_type
13762143Sarchie	},
13862143Sarchie	{
13962143Sarchie	  NGM_ETHER_COOKIE,
14062143Sarchie	  NGM_ETHER_GET_IFINDEX,
14162143Sarchie	  "getifindex",
14262143Sarchie	  NULL,
14362143Sarchie	  &ng_parse_int32_type
14462143Sarchie	},
14564358Sarchie	{
14664358Sarchie	  NGM_ETHER_COOKIE,
14764358Sarchie	  NGM_ETHER_GET_ENADDR,
14864358Sarchie	  "getenaddr",
14964358Sarchie	  NULL,
150123600Sru	  &ng_parse_enaddr_type
15164358Sarchie	},
15264358Sarchie	{
15364358Sarchie	  NGM_ETHER_COOKIE,
15464653Sarchie	  NGM_ETHER_SET_ENADDR,
15564653Sarchie	  "setenaddr",
156123600Sru	  &ng_parse_enaddr_type,
15764653Sarchie	  NULL
15864653Sarchie	},
15964653Sarchie	{
16064653Sarchie	  NGM_ETHER_COOKIE,
16164653Sarchie	  NGM_ETHER_GET_PROMISC,
16264653Sarchie	  "getpromisc",
16364653Sarchie	  NULL,
16464653Sarchie	  &ng_parse_int32_type
16564653Sarchie	},
16664653Sarchie	{
16764653Sarchie	  NGM_ETHER_COOKIE,
16864358Sarchie	  NGM_ETHER_SET_PROMISC,
16964358Sarchie	  "setpromisc",
17064358Sarchie	  &ng_parse_int32_type,
17164358Sarchie	  NULL
17264358Sarchie	},
17364358Sarchie	{
17464358Sarchie	  NGM_ETHER_COOKIE,
17564653Sarchie	  NGM_ETHER_GET_AUTOSRC,
17664653Sarchie	  "getautosrc",
17764653Sarchie	  NULL,
17864653Sarchie	  &ng_parse_int32_type
17964653Sarchie	},
18064653Sarchie	{
18164653Sarchie	  NGM_ETHER_COOKIE,
18264358Sarchie	  NGM_ETHER_SET_AUTOSRC,
18364358Sarchie	  "setautosrc",
18464358Sarchie	  &ng_parse_int32_type,
18564358Sarchie	  NULL
18664358Sarchie	},
187141721Sglebius	{
188141721Sglebius	  NGM_ETHER_COOKIE,
189141721Sglebius	  NGM_ETHER_ADD_MULTI,
190141721Sglebius	  "addmulti",
191141721Sglebius	  &ng_parse_enaddr_type,
192141721Sglebius	  NULL
193141721Sglebius	},
194141721Sglebius	{
195141721Sglebius	  NGM_ETHER_COOKIE,
196141721Sglebius	  NGM_ETHER_DEL_MULTI,
197141721Sglebius	  "delmulti",
198141721Sglebius	  &ng_parse_enaddr_type,
199141721Sglebius	  NULL
200141721Sglebius	},
201141910Sglebius	{
202141910Sglebius	  NGM_ETHER_COOKIE,
203141910Sglebius	  NGM_ETHER_DETACH,
204141910Sglebius	  "detach",
205141910Sglebius	  NULL,
206141910Sglebius	  NULL
207141910Sglebius	},
20862143Sarchie	{ 0 }
20962143Sarchie};
21062143Sarchie
21162143Sarchiestatic struct ng_type ng_ether_typestruct = {
212129823Sjulian	.version =	NG_ABI_VERSION,
213129823Sjulian	.name =		NG_ETHER_NODE_TYPE,
214129823Sjulian	.mod_event =	ng_ether_mod_event,
215129823Sjulian	.constructor =	ng_ether_constructor,
216129823Sjulian	.rcvmsg =	ng_ether_rcvmsg,
217129823Sjulian	.shutdown =	ng_ether_shutdown,
218129823Sjulian	.newhook =	ng_ether_newhook,
219129823Sjulian	.rcvdata =	ng_ether_rcvdata,
220129823Sjulian	.disconnect =	ng_ether_disconnect,
221129823Sjulian	.cmdlist =	ng_ether_cmdlist,
22262143Sarchie};
22362143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct);
22462143Sarchie
22562143Sarchie/******************************************************************
22662143Sarchie		    ETHERNET FUNCTION HOOKS
22762143Sarchie******************************************************************/
22862143Sarchie
22962143Sarchie/*
23062143Sarchie * Handle a packet that has come in on an interface. We get to
23162143Sarchie * look at it here before any upper layer protocols do.
23262143Sarchie *
23362143Sarchie * NOTE: this function will get called at splimp()
23462143Sarchie */
23562143Sarchiestatic void
236106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp)
23762143Sarchie{
23862143Sarchie	const node_p node = IFP2NG(ifp);
23970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
240129281Sarchie	int error;
24162143Sarchie
24262143Sarchie	/* If "lower" hook not connected, let packet continue */
243129281Sarchie	if (priv->lower == NULL)
24462143Sarchie		return;
245129281Sarchie	NG_SEND_DATA_ONLY(error, priv->lower, *mp);	/* sets *mp = NULL */
24662143Sarchie}
24762143Sarchie
24862143Sarchie/*
24962143Sarchie * Handle a packet that has come in on an interface, and which
25062143Sarchie * does not match any of our known protocols (an ``orphan'').
25162143Sarchie *
25262143Sarchie * NOTE: this function will get called at splimp()
25362143Sarchie */
25462143Sarchiestatic void
255106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m)
25662143Sarchie{
25762143Sarchie	const node_p node = IFP2NG(ifp);
25870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
259129281Sarchie	int error;
26062143Sarchie
261129281Sarchie	/* If "orphan" hook not connected, discard packet */
262129281Sarchie	if (priv->orphan == NULL) {
26362143Sarchie		m_freem(m);
26462143Sarchie		return;
26562143Sarchie	}
266129281Sarchie	NG_SEND_DATA_ONLY(error, priv->orphan, m);
26762143Sarchie}
26862143Sarchie
26962143Sarchie/*
27062143Sarchie * Handle a packet that is going out on an interface.
27162143Sarchie * The Ethernet header is already attached to the mbuf.
27262143Sarchie */
27362143Sarchiestatic int
27462143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp)
27562143Sarchie{
27662143Sarchie	const node_p node = IFP2NG(ifp);
27770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
27862143Sarchie	int error = 0;
27962143Sarchie
28062143Sarchie	/* If "upper" hook not connected, let packet continue */
28162143Sarchie	if (priv->upper == NULL)
28262143Sarchie		return (0);
28362143Sarchie
28462143Sarchie	/* Send it out "upper" hook */
285194012Szec	NG_OUTBOUND_THREAD_REF();
28670700Sjulian	NG_SEND_DATA_ONLY(error, priv->upper, *mp);
287194012Szec	NG_OUTBOUND_THREAD_UNREF();
28862143Sarchie	return (error);
28962143Sarchie}
29062143Sarchie
29162143Sarchie/*
29262143Sarchie * A new Ethernet interface has been attached.
29362143Sarchie * Create a new node for it, etc.
29462143Sarchie */
29562143Sarchiestatic void
29662143Sarchieng_ether_attach(struct ifnet *ifp)
29762143Sarchie{
29862143Sarchie	priv_p priv;
29962143Sarchie	node_p node;
30062143Sarchie
301191510Szec	/*
302191510Szec	 * Do not create / attach an ether node to this ifnet if
303191510Szec	 * a netgraph node with the same name already exists.
304191510Szec	 * This should prevent ether nodes to become attached to
305191510Szec	 * eiface nodes, which may be problematic due to naming
306191510Szec	 * clashes.
307191510Szec	 */
308191510Szec	if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) {
309191510Szec		NG_NODE_UNREF(node);
310191510Szec		return;
311191510Szec	}
312191510Szec
31362143Sarchie	/* Create node */
31487599Sobrien	KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__));
31562143Sarchie	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
31662143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
317121816Sbrooks		    __func__, "create node", ifp->if_xname);
31862143Sarchie		return;
31962143Sarchie	}
32062143Sarchie
32162143Sarchie	/* Allocate private data */
322184205Sdes	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
32362143Sarchie	if (priv == NULL) {
32462143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
325121816Sbrooks		    __func__, "allocate memory", ifp->if_xname);
32670784Sjulian		NG_NODE_UNREF(node);
32762143Sarchie		return;
32862143Sarchie	}
32970784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
33062143Sarchie	priv->ifp = ifp;
331152243Sru	IFP2NG(ifp) = node;
33290249Sarchie	priv->hwassist = ifp->if_hwassist;
33362143Sarchie
33462143Sarchie	/* Try to give the node the same name as the interface */
335121816Sbrooks	if (ng_name_node(node, ifp->if_xname) != 0) {
33662143Sarchie		log(LOG_WARNING, "%s: can't name node %s\n",
337121816Sbrooks		    __func__, ifp->if_xname);
33862143Sarchie	}
33962143Sarchie}
34062143Sarchie
34162143Sarchie/*
34262143Sarchie * An Ethernet interface is being detached.
34371849Sjulian * REALLY Destroy its node.
34462143Sarchie */
34562143Sarchiestatic void
34662143Sarchieng_ether_detach(struct ifnet *ifp)
34762143Sarchie{
34862143Sarchie	const node_p node = IFP2NG(ifp);
34971849Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
35062143Sarchie
35171849Sjulian	NG_NODE_REALLY_DIE(node);	/* Force real removal of node */
35271849Sjulian	/*
35371849Sjulian	 * We can't assume the ifnet is still around when we run shutdown
35471849Sjulian	 * So zap it now. XXX We HOPE that anything running at this time
35571849Sjulian	 * handles it (as it should in the non netgraph case).
35671849Sjulian	 */
357152243Sru	IFP2NG(ifp) = NULL;
35871849Sjulian	priv->ifp = NULL;	/* XXX race if interrupted an output packet */
35971849Sjulian	ng_rmnode_self(node);		/* remove all netgraph parts */
36062143Sarchie}
36162143Sarchie
362139903Sglebius/*
363139903Sglebius * Notify graph about link event.
364139903Sglebius * if_link_state_change() has already checked that the state has changed.
365139903Sglebius */
366139903Sglebiusstatic void
367139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state)
368139903Sglebius{
369139903Sglebius	const node_p node = IFP2NG(ifp);
370139903Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
371139903Sglebius	struct ng_mesg *msg;
372139903Sglebius	int cmd, dummy_error = 0;
373139903Sglebius
374139903Sglebius	if (priv->lower == NULL)
375139903Sglebius                return;
376139903Sglebius
377139903Sglebius	if (state == LINK_STATE_UP)
378139903Sglebius		cmd = NGM_LINK_IS_UP;
379139903Sglebius	else if (state == LINK_STATE_DOWN)
380139903Sglebius		cmd = NGM_LINK_IS_DOWN;
381139903Sglebius	else
382139903Sglebius		return;
383139903Sglebius
384139903Sglebius	NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
385139903Sglebius	if (msg != NULL)
386139903Sglebius		NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0);
387139903Sglebius}
388139903Sglebius
38962143Sarchie/******************************************************************
39062143Sarchie		    NETGRAPH NODE METHODS
39162143Sarchie******************************************************************/
39262143Sarchie
39362143Sarchie/*
39462143Sarchie * It is not possible or allowable to create a node of this type.
39562143Sarchie * Nodes get created when the interface is attached (or, when
39662143Sarchie * this node type's KLD is loaded).
39762143Sarchie */
39862143Sarchiestatic int
39970700Sjulianng_ether_constructor(node_p node)
40062143Sarchie{
40162143Sarchie	return (EINVAL);
40262143Sarchie}
40362143Sarchie
40462143Sarchie/*
40562143Sarchie * Check for attaching a new hook.
40662143Sarchie */
40762143Sarchiestatic	int
40862143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name)
40962143Sarchie{
41070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
41162143Sarchie	hook_p *hookptr;
41262143Sarchie
41362143Sarchie	/* Divert hook is an alias for lower */
41462143Sarchie	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
41562143Sarchie		name = NG_ETHER_HOOK_LOWER;
41662143Sarchie
41762143Sarchie	/* Which hook? */
418186488Sjulian	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) {
41962143Sarchie		hookptr = &priv->upper;
420186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper);
421194012Szec		NG_HOOK_SET_TO_INBOUND(hook);
422186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
42362143Sarchie		hookptr = &priv->lower;
424186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
425186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
426129281Sarchie		hookptr = &priv->orphan;
427186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
428186488Sjulian	} else
42962143Sarchie		return (EINVAL);
43062143Sarchie
43162143Sarchie	/* Check if already connected (shouldn't be, but doesn't hurt) */
43262143Sarchie	if (*hookptr != NULL)
43362143Sarchie		return (EISCONN);
43462143Sarchie
43590249Sarchie	/* Disable hardware checksums while 'upper' hook is connected */
43690249Sarchie	if (hookptr == &priv->upper)
43790249Sarchie		priv->ifp->if_hwassist = 0;
43890249Sarchie
43962143Sarchie	/* OK */
44062143Sarchie	*hookptr = hook;
44162143Sarchie	return (0);
44262143Sarchie}
44362143Sarchie
44462143Sarchie/*
44562143Sarchie * Receive an incoming control message.
44662143Sarchie */
44762143Sarchiestatic int
44870700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook)
44962143Sarchie{
45070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
45162143Sarchie	struct ng_mesg *resp = NULL;
45262143Sarchie	int error = 0;
45370700Sjulian	struct ng_mesg *msg;
45462143Sarchie
45570700Sjulian	NGI_GET_MSG(item, msg);
45662143Sarchie	switch (msg->header.typecookie) {
45762143Sarchie	case NGM_ETHER_COOKIE:
45862143Sarchie		switch (msg->header.cmd) {
45962143Sarchie		case NGM_ETHER_GET_IFNAME:
460141195Sru			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
46162143Sarchie			if (resp == NULL) {
46262143Sarchie				error = ENOMEM;
46362143Sarchie				break;
46462143Sarchie			}
465141195Sru			strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
46662143Sarchie			break;
46762143Sarchie		case NGM_ETHER_GET_IFINDEX:
46862143Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
46962143Sarchie			if (resp == NULL) {
47062143Sarchie				error = ENOMEM;
47162143Sarchie				break;
47262143Sarchie			}
47362143Sarchie			*((u_int32_t *)resp->data) = priv->ifp->if_index;
47462143Sarchie			break;
47564358Sarchie		case NGM_ETHER_GET_ENADDR:
47664358Sarchie			NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT);
47764358Sarchie			if (resp == NULL) {
47864358Sarchie				error = ENOMEM;
47964358Sarchie				break;
48064358Sarchie			}
481152315Sru			bcopy(IF_LLADDR(priv->ifp),
48264358Sarchie			    resp->data, ETHER_ADDR_LEN);
48364358Sarchie			break;
48464653Sarchie		case NGM_ETHER_SET_ENADDR:
48564653Sarchie		    {
48664653Sarchie			if (msg->header.arglen != ETHER_ADDR_LEN) {
48764653Sarchie				error = EINVAL;
48864653Sarchie				break;
48964653Sarchie			}
49064653Sarchie			error = if_setlladdr(priv->ifp,
49164653Sarchie			    (u_char *)msg->data, ETHER_ADDR_LEN);
49264653Sarchie			break;
49364653Sarchie		    }
49464653Sarchie		case NGM_ETHER_GET_PROMISC:
49564653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
49664653Sarchie			if (resp == NULL) {
49764653Sarchie				error = ENOMEM;
49864653Sarchie				break;
49964653Sarchie			}
50064653Sarchie			*((u_int32_t *)resp->data) = priv->promisc;
50164653Sarchie			break;
50264358Sarchie		case NGM_ETHER_SET_PROMISC:
50364358Sarchie		    {
50464358Sarchie			u_char want;
50564358Sarchie
50664358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
50764358Sarchie				error = EINVAL;
50864358Sarchie				break;
50964358Sarchie			}
51064358Sarchie			want = !!*((u_int32_t *)msg->data);
51164358Sarchie			if (want ^ priv->promisc) {
51264358Sarchie				if ((error = ifpromisc(priv->ifp, want)) != 0)
51364358Sarchie					break;
51464358Sarchie				priv->promisc = want;
51564358Sarchie			}
51664358Sarchie			break;
51764358Sarchie		    }
51864653Sarchie		case NGM_ETHER_GET_AUTOSRC:
51964653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
52064653Sarchie			if (resp == NULL) {
52164653Sarchie				error = ENOMEM;
52264653Sarchie				break;
52364653Sarchie			}
52464653Sarchie			*((u_int32_t *)resp->data) = priv->autoSrcAddr;
52564653Sarchie			break;
52664358Sarchie		case NGM_ETHER_SET_AUTOSRC:
52764358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
52864358Sarchie				error = EINVAL;
52964358Sarchie				break;
53064358Sarchie			}
53164358Sarchie			priv->autoSrcAddr = !!*((u_int32_t *)msg->data);
53264358Sarchie			break;
533141721Sglebius		case NGM_ETHER_ADD_MULTI:
534141721Sglebius		    {
535141721Sglebius			struct sockaddr_dl sa_dl;
536167729Sbms			struct ifmultiaddr *ifma;
537141721Sglebius
538141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
539141721Sglebius				error = EINVAL;
540141721Sglebius				break;
541141721Sglebius			}
542141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
543141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
544141721Sglebius			sa_dl.sdl_family = AF_LINK;
545141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
546141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
547141721Sglebius			    ETHER_ADDR_LEN);
548167729Sbms			/*
549167729Sbms			 * Netgraph is only permitted to join groups once
550167729Sbms			 * via the if_addmulti() KPI, because it cannot hold
551167729Sbms			 * struct ifmultiaddr * between calls. It may also
552167729Sbms			 * lose a race while we check if the membership
553167729Sbms			 * already exists.
554167729Sbms			 */
555167729Sbms			IF_ADDR_LOCK(priv->ifp);
556167729Sbms			ifma = if_findmulti(priv->ifp,
557167729Sbms			    (struct sockaddr *)&sa_dl);
558167729Sbms			IF_ADDR_UNLOCK(priv->ifp);
559167729Sbms			if (ifma != NULL) {
560167729Sbms				error = EADDRINUSE;
561167729Sbms			} else {
562167729Sbms				error = if_addmulti(priv->ifp,
563167729Sbms				    (struct sockaddr *)&sa_dl, &ifma);
564167729Sbms			}
565141721Sglebius			break;
566141721Sglebius		    }
567141721Sglebius		case NGM_ETHER_DEL_MULTI:
568141721Sglebius		    {
569141721Sglebius			struct sockaddr_dl sa_dl;
570141721Sglebius
571141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
572141721Sglebius				error = EINVAL;
573141721Sglebius				break;
574141721Sglebius			}
575141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
576141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
577141721Sglebius			sa_dl.sdl_family = AF_LINK;
578141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
579141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
580141721Sglebius			    ETHER_ADDR_LEN);
581141721Sglebius			error = if_delmulti(priv->ifp,
582141721Sglebius			    (struct sockaddr *)&sa_dl);
583141721Sglebius			break;
584141721Sglebius		    }
585141910Sglebius		case NGM_ETHER_DETACH:
586141910Sglebius			ng_ether_detach(priv->ifp);
587141910Sglebius			break;
58862143Sarchie		default:
58962143Sarchie			error = EINVAL;
59062143Sarchie			break;
59162143Sarchie		}
59262143Sarchie		break;
59362143Sarchie	default:
59462143Sarchie		error = EINVAL;
59562143Sarchie		break;
59662143Sarchie	}
59770700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
59870700Sjulian	NG_FREE_MSG(msg);
59962143Sarchie	return (error);
60062143Sarchie}
60162143Sarchie
60262143Sarchie/*
60362143Sarchie * Receive data on a hook.
604186488Sjulian * Since we use per-hook recveive methods this should never be called.
60562143Sarchie */
60662143Sarchiestatic int
60770700Sjulianng_ether_rcvdata(hook_p hook, item_p item)
60862143Sarchie{
60970700Sjulian	NG_FREE_ITEM(item);
610131155Sjulian
61187599Sobrien	panic("%s: weird hook", __func__);
612129281Sarchie#ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */
613136312Sdes	return (0);
61483366Sjulian#endif
61562143Sarchie}
61662143Sarchie
61762143Sarchie/*
618129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook.
61962143Sarchie */
62062143Sarchiestatic int
621186488Sjulianng_ether_rcv_lower(hook_p hook, item_p item)
62262143Sarchie{
623186488Sjulian	struct mbuf *m;
624186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
62570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
62696265Sarchie 	struct ifnet *const ifp = priv->ifp;
62762143Sarchie
628186488Sjulian	NGI_GET_M(item, m);
629186488Sjulian	NG_FREE_ITEM(item);
630186488Sjulian
63196265Sarchie	/* Check whether interface is ready for packets */
632186488Sjulian
633148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
634148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
63596265Sarchie		NG_FREE_M(m);
63696265Sarchie		return (ENETDOWN);
63796265Sarchie	}
63896265Sarchie
63962143Sarchie	/* Make sure header is fully pulled up */
64062143Sarchie	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
64170700Sjulian		NG_FREE_M(m);
64262143Sarchie		return (EINVAL);
64362143Sarchie	}
64462143Sarchie	if (m->m_len < sizeof(struct ether_header)
64597896Sarchie	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
64662143Sarchie		return (ENOBUFS);
64762143Sarchie
64864358Sarchie	/* Drop in the MAC address if desired */
64964358Sarchie	if (priv->autoSrcAddr) {
65097896Sarchie
65197896Sarchie		/* Make the mbuf writable if it's not already */
65297896Sarchie		if (!M_WRITABLE(m)
65397896Sarchie		    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
65497896Sarchie			return (ENOBUFS);
65597896Sarchie
65697896Sarchie		/* Overwrite source MAC address */
657152315Sru		bcopy(IF_LLADDR(ifp),
65864358Sarchie		    mtod(m, struct ether_header *)->ether_shost,
65964358Sarchie		    ETHER_ADDR_LEN);
66064358Sarchie	}
66162678Sjulian
66262143Sarchie	/* Send it on its way */
66396265Sarchie	return ether_output_frame(ifp, m);
66462143Sarchie}
66562143Sarchie
66662143Sarchie/*
66762143Sarchie * Handle an mbuf received on the "upper" hook.
66862143Sarchie */
66962143Sarchiestatic int
670186488Sjulianng_ether_rcv_upper(hook_p hook, item_p item)
67162143Sarchie{
672186488Sjulian	struct mbuf *m;
673186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
67470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
675151063Sglebius	struct ifnet *ifp = priv->ifp;
67662143Sarchie
677186488Sjulian	NGI_GET_M(item, m);
678186488Sjulian	NG_FREE_ITEM(item);
679186488Sjulian
680152001Sru	/* Check length and pull off header */
681152001Sru	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
682152001Sru		NG_FREE_M(m);
683152001Sru		return (EINVAL);
684152001Sru	}
685152001Sru	if (m->m_len < sizeof(struct ether_header) &&
686152001Sru	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
687152001Sru		return (ENOBUFS);
688152001Sru
689151063Sglebius	m->m_pkthdr.rcvif = ifp;
69062143Sarchie
691151305Sthompsa	/* Pass the packet to the bridge, it may come back to us */
692151063Sglebius	if (ifp->if_bridge) {
693151305Sthompsa		BRIDGE_INPUT(ifp, m);
694151063Sglebius		if (m == NULL)
695151063Sglebius			return (0);
696151063Sglebius	}
697151063Sglebius
69862143Sarchie	/* Route packet back in */
699151305Sthompsa	ether_demux(ifp, m);
70062143Sarchie	return (0);
70162143Sarchie}
70262143Sarchie
70362143Sarchie/*
70471849Sjulian * Shutdown node. This resets the node but does not remove it
70571849Sjulian * unless the REALLY_DIE flag is set.
70662143Sarchie */
70762143Sarchiestatic int
70870700Sjulianng_ether_shutdown(node_p node)
70962143Sarchie{
71070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
71164358Sarchie
712132464Sjulian	if (node->nd_flags & NGF_REALLY_DIE) {
71371849Sjulian		/*
71471849Sjulian		 * WE came here because the ethernet card is being unloaded,
71571849Sjulian		 * so stop being persistant.
71671849Sjulian		 * Actually undo all the things we did on creation.
71771849Sjulian		 * Assume the ifp has already been freed.
71871849Sjulian		 */
71971849Sjulian		NG_NODE_SET_PRIVATE(node, NULL);
720184205Sdes		free(priv, M_NETGRAPH);
72171849Sjulian		NG_NODE_UNREF(node);	/* free node itself */
72271849Sjulian		return (0);
72370700Sjulian	}
724124269Sgreen	if (priv->promisc) {		/* disable promiscuous mode */
725124269Sgreen		(void)ifpromisc(priv->ifp, 0);
726124269Sgreen		priv->promisc = 0;
727124269Sgreen	}
72864358Sarchie	priv->autoSrcAddr = 1;		/* reset auto-src-addr flag */
729132464Sjulian	NG_NODE_REVIVE(node);		/* Signal ng_rmnode we are persisant */
730132464Sjulian
73162143Sarchie	return (0);
73262143Sarchie}
73362143Sarchie
73462143Sarchie/*
73562143Sarchie * Hook disconnection.
73662143Sarchie */
73762143Sarchiestatic int
73862143Sarchieng_ether_disconnect(hook_p hook)
73962143Sarchie{
74070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
74162143Sarchie
74290249Sarchie	if (hook == priv->upper) {
74362143Sarchie		priv->upper = NULL;
744124270Sgreen		if (priv->ifp != NULL)		/* restore h/w csum */
745124270Sgreen			priv->ifp->if_hwassist = priv->hwassist;
746129281Sarchie	} else if (hook == priv->lower)
74762143Sarchie		priv->lower = NULL;
748129281Sarchie	else if (hook == priv->orphan)
749129281Sarchie		priv->orphan = NULL;
750129281Sarchie	else
75187599Sobrien		panic("%s: weird hook", __func__);
75270784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
75370784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
75470784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));	/* reset node */
75562143Sarchie	return (0);
75662143Sarchie}
75762143Sarchie
75862143Sarchie/******************************************************************
75962143Sarchie		    	INITIALIZATION
76062143Sarchie******************************************************************/
76162143Sarchie
76262143Sarchie/*
76362143Sarchie * Handle loading and unloading for this node type.
76462143Sarchie */
76562143Sarchiestatic int
76662143Sarchieng_ether_mod_event(module_t mod, int event, void *data)
76762143Sarchie{
76862143Sarchie	int error = 0;
76962143Sarchie	int s;
77062143Sarchie
77162143Sarchie	s = splnet();
77262143Sarchie	switch (event) {
77362143Sarchie	case MOD_LOAD:
77462143Sarchie
77562143Sarchie		/* Register function hooks */
77662143Sarchie		if (ng_ether_attach_p != NULL) {
77762143Sarchie			error = EEXIST;
77862143Sarchie			break;
77962143Sarchie		}
78062143Sarchie		ng_ether_attach_p = ng_ether_attach;
78162143Sarchie		ng_ether_detach_p = ng_ether_detach;
78262143Sarchie		ng_ether_output_p = ng_ether_output;
78362143Sarchie		ng_ether_input_p = ng_ether_input;
78462143Sarchie		ng_ether_input_orphan_p = ng_ether_input_orphan;
785139903Sglebius		ng_ether_link_state_p = ng_ether_link_state;
78662143Sarchie
787191510Szec#ifndef VIMAGE_GLOBALS
788191510Szec		vnet_mod_register(&vnet_ng_ether_modinfo);
789191510Szec#else
790191510Szec		error = ng_ether_iattach(NULL);
791191510Szec#endif
79262143Sarchie		break;
79362143Sarchie
79462143Sarchie	case MOD_UNLOAD:
79562143Sarchie
79662143Sarchie		/*
79762143Sarchie		 * Note that the base code won't try to unload us until
79862143Sarchie		 * all nodes have been removed, and that can't happen
79962143Sarchie		 * until all Ethernet interfaces are removed. In any
80062143Sarchie		 * case, we know there are no nodes left if the action
80162143Sarchie		 * is MOD_UNLOAD, so there's no need to detach any nodes.
80262143Sarchie		 */
80362143Sarchie
804191510Szec#ifndef VIMAGE_GLOBALS
805191510Szec		vnet_mod_deregister(&vnet_ng_ether_modinfo);
806191510Szec#endif
807191510Szec
80862143Sarchie		/* Unregister function hooks */
80962143Sarchie		ng_ether_attach_p = NULL;
81062143Sarchie		ng_ether_detach_p = NULL;
81162143Sarchie		ng_ether_output_p = NULL;
81262143Sarchie		ng_ether_input_p = NULL;
81362143Sarchie		ng_ether_input_orphan_p = NULL;
814139903Sglebius		ng_ether_link_state_p = NULL;
81562143Sarchie		break;
81662143Sarchie
81762143Sarchie	default:
81862143Sarchie		error = EOPNOTSUPP;
81962143Sarchie		break;
82062143Sarchie	}
82162143Sarchie	splx(s);
82262143Sarchie	return (error);
82362143Sarchie}
82462143Sarchie
825191510Szecstatic int ng_ether_iattach(const void *unused)
826191510Szec{
827191510Szec	INIT_VNET_NET(curvnet);
828191510Szec	struct ifnet *ifp;
829191510Szec
830191510Szec	/* Create nodes for any already-existing Ethernet interfaces. */
831191510Szec	IFNET_RLOCK();
832191510Szec	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
833191510Szec		if (ifp->if_type == IFT_ETHER
834191510Szec		    || ifp->if_type == IFT_L2VLAN)
835191510Szec			ng_ether_attach(ifp);
836191510Szec	}
837191510Szec	IFNET_RUNLOCK();
838191510Szec
839191510Szec	return (0);
840191510Szec}
841