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$
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>
55196019Srwatson#include <sys/proc.h>
5662143Sarchie#include <sys/syslog.h>
5762143Sarchie#include <sys/socket.h>
58224107Szec#include <sys/taskqueue.h>
5962143Sarchie
6062143Sarchie#include <net/if.h>
61141721Sglebius#include <net/if_dl.h>
6262143Sarchie#include <net/if_types.h>
6362143Sarchie#include <net/if_arp.h>
6462143Sarchie#include <net/if_var.h>
6562143Sarchie#include <net/ethernet.h>
66151305Sthompsa#include <net/if_bridgevar.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
74239429SemasteMODULE_VERSION(ng_ether, 1);
75239429Semaste
76152243Sru#define IFP2NG(ifp)  (IFP2AC((ifp))->ac_netgraph)
7762143Sarchie
78126035Spjd/* Per-node private data */
79126035Spjdstruct private {
80126035Spjd	struct ifnet	*ifp;		/* associated interface */
81126035Spjd	hook_p		upper;		/* upper hook connection */
82129281Sarchie	hook_p		lower;		/* lower hook connection */
83129281Sarchie	hook_p		orphan;		/* orphan hook connection */
84126035Spjd	u_char		autoSrcAddr;	/* always overwrite source address */
85126035Spjd	u_char		promisc;	/* promiscuous mode enabled */
86126035Spjd	u_long		hwassist;	/* hardware checksum capabilities */
87126035Spjd	u_int		flags;		/* flags e.g. really die */
88126035Spjd};
89126035Spjdtypedef struct private *priv_p;
9062143Sarchie
91106933Ssam/* Hook pointers used by if_ethersubr.c to callback to netgraph */
92106933Ssamextern	void	(*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
93106933Ssamextern	void	(*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
94106933Ssamextern	int	(*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
95106933Ssamextern	void	(*ng_ether_attach_p)(struct ifnet *ifp);
96106933Ssamextern	void	(*ng_ether_detach_p)(struct ifnet *ifp);
97139903Sglebiusextern	void	(*ng_ether_link_state_p)(struct ifnet *ifp, int state);
98106933Ssam
9962143Sarchie/* Functional hooks called from if_ethersubr.c */
100106933Ssamstatic void	ng_ether_input(struct ifnet *ifp, struct mbuf **mp);
101106933Ssamstatic void	ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m);
10262143Sarchiestatic int	ng_ether_output(struct ifnet *ifp, struct mbuf **mp);
10362143Sarchiestatic void	ng_ether_attach(struct ifnet *ifp);
10462143Sarchiestatic void	ng_ether_detach(struct ifnet *ifp);
105139903Sglebiusstatic void	ng_ether_link_state(struct ifnet *ifp, int state);
10662143Sarchie
10762143Sarchie/* Other functions */
108186488Sjulianstatic int	ng_ether_rcv_lower(hook_p node, item_p item);
109186488Sjulianstatic int	ng_ether_rcv_upper(hook_p node, item_p item);
11062143Sarchie
11162143Sarchie/* Netgraph node methods */
11262143Sarchiestatic ng_constructor_t	ng_ether_constructor;
11362143Sarchiestatic ng_rcvmsg_t	ng_ether_rcvmsg;
11470700Sjulianstatic ng_shutdown_t	ng_ether_shutdown;
11562143Sarchiestatic ng_newhook_t	ng_ether_newhook;
11662143Sarchiestatic ng_rcvdata_t	ng_ether_rcvdata;
11762143Sarchiestatic ng_disconnect_t	ng_ether_disconnect;
11862143Sarchiestatic int		ng_ether_mod_event(module_t mod, int event, void *data);
11962143Sarchie
120247886Savgstatic eventhandler_tag	ng_ether_ifnet_arrival_cookie;
121247886Savg
12262143Sarchie/* List of commands and how to convert arguments to/from ASCII */
12362143Sarchiestatic const struct ng_cmdlist ng_ether_cmdlist[] = {
12462143Sarchie	{
12562143Sarchie	  NGM_ETHER_COOKIE,
12662143Sarchie	  NGM_ETHER_GET_IFNAME,
12762143Sarchie	  "getifname",
12862143Sarchie	  NULL,
12962143Sarchie	  &ng_parse_string_type
13062143Sarchie	},
13162143Sarchie	{
13262143Sarchie	  NGM_ETHER_COOKIE,
13362143Sarchie	  NGM_ETHER_GET_IFINDEX,
13462143Sarchie	  "getifindex",
13562143Sarchie	  NULL,
13662143Sarchie	  &ng_parse_int32_type
13762143Sarchie	},
13864358Sarchie	{
13964358Sarchie	  NGM_ETHER_COOKIE,
14064358Sarchie	  NGM_ETHER_GET_ENADDR,
14164358Sarchie	  "getenaddr",
14264358Sarchie	  NULL,
143123600Sru	  &ng_parse_enaddr_type
14464358Sarchie	},
14564358Sarchie	{
14664358Sarchie	  NGM_ETHER_COOKIE,
14764653Sarchie	  NGM_ETHER_SET_ENADDR,
14864653Sarchie	  "setenaddr",
149123600Sru	  &ng_parse_enaddr_type,
15064653Sarchie	  NULL
15164653Sarchie	},
15264653Sarchie	{
15364653Sarchie	  NGM_ETHER_COOKIE,
15464653Sarchie	  NGM_ETHER_GET_PROMISC,
15564653Sarchie	  "getpromisc",
15664653Sarchie	  NULL,
15764653Sarchie	  &ng_parse_int32_type
15864653Sarchie	},
15964653Sarchie	{
16064653Sarchie	  NGM_ETHER_COOKIE,
16164358Sarchie	  NGM_ETHER_SET_PROMISC,
16264358Sarchie	  "setpromisc",
16364358Sarchie	  &ng_parse_int32_type,
16464358Sarchie	  NULL
16564358Sarchie	},
16664358Sarchie	{
16764358Sarchie	  NGM_ETHER_COOKIE,
16864653Sarchie	  NGM_ETHER_GET_AUTOSRC,
16964653Sarchie	  "getautosrc",
17064653Sarchie	  NULL,
17164653Sarchie	  &ng_parse_int32_type
17264653Sarchie	},
17364653Sarchie	{
17464653Sarchie	  NGM_ETHER_COOKIE,
17564358Sarchie	  NGM_ETHER_SET_AUTOSRC,
17664358Sarchie	  "setautosrc",
17764358Sarchie	  &ng_parse_int32_type,
17864358Sarchie	  NULL
17964358Sarchie	},
180141721Sglebius	{
181141721Sglebius	  NGM_ETHER_COOKIE,
182141721Sglebius	  NGM_ETHER_ADD_MULTI,
183141721Sglebius	  "addmulti",
184141721Sglebius	  &ng_parse_enaddr_type,
185141721Sglebius	  NULL
186141721Sglebius	},
187141721Sglebius	{
188141721Sglebius	  NGM_ETHER_COOKIE,
189141721Sglebius	  NGM_ETHER_DEL_MULTI,
190141721Sglebius	  "delmulti",
191141721Sglebius	  &ng_parse_enaddr_type,
192141721Sglebius	  NULL
193141721Sglebius	},
194141910Sglebius	{
195141910Sglebius	  NGM_ETHER_COOKIE,
196141910Sglebius	  NGM_ETHER_DETACH,
197141910Sglebius	  "detach",
198141910Sglebius	  NULL,
199141910Sglebius	  NULL
200141910Sglebius	},
20162143Sarchie	{ 0 }
20262143Sarchie};
20362143Sarchie
20462143Sarchiestatic struct ng_type ng_ether_typestruct = {
205129823Sjulian	.version =	NG_ABI_VERSION,
206129823Sjulian	.name =		NG_ETHER_NODE_TYPE,
207129823Sjulian	.mod_event =	ng_ether_mod_event,
208129823Sjulian	.constructor =	ng_ether_constructor,
209129823Sjulian	.rcvmsg =	ng_ether_rcvmsg,
210129823Sjulian	.shutdown =	ng_ether_shutdown,
211129823Sjulian	.newhook =	ng_ether_newhook,
212129823Sjulian	.rcvdata =	ng_ether_rcvdata,
213129823Sjulian	.disconnect =	ng_ether_disconnect,
214129823Sjulian	.cmdlist =	ng_ether_cmdlist,
21562143Sarchie};
21662143SarchieNETGRAPH_INIT(ether, &ng_ether_typestruct);
21762143Sarchie
21862143Sarchie/******************************************************************
219247886Savg		    UTILITY FUNCTIONS
220247886Savg******************************************************************/
221247886Savgstatic void
222247886Savgng_ether_sanitize_ifname(const char *ifname, char *name)
223247886Savg{
224247886Savg	int i;
225247886Savg
226247886Savg	for (i = 0; i < IFNAMSIZ; i++) {
227247886Savg		if (ifname[i] == '.' || ifname[i] == ':')
228247886Savg			name[i] = '_';
229247886Savg		else
230247886Savg			name[i] = ifname[i];
231247886Savg		if (name[i] == '\0')
232247886Savg			break;
233247886Savg	}
234247886Savg}
235247886Savg
236247886Savg/******************************************************************
23762143Sarchie		    ETHERNET FUNCTION HOOKS
23862143Sarchie******************************************************************/
23962143Sarchie
24062143Sarchie/*
24162143Sarchie * Handle a packet that has come in on an interface. We get to
24262143Sarchie * look at it here before any upper layer protocols do.
24362143Sarchie *
24462143Sarchie * NOTE: this function will get called at splimp()
24562143Sarchie */
24662143Sarchiestatic void
247106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp)
24862143Sarchie{
24962143Sarchie	const node_p node = IFP2NG(ifp);
25070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
251129281Sarchie	int error;
25262143Sarchie
25362143Sarchie	/* If "lower" hook not connected, let packet continue */
254129281Sarchie	if (priv->lower == NULL)
25562143Sarchie		return;
256129281Sarchie	NG_SEND_DATA_ONLY(error, priv->lower, *mp);	/* sets *mp = NULL */
25762143Sarchie}
25862143Sarchie
25962143Sarchie/*
26062143Sarchie * Handle a packet that has come in on an interface, and which
26162143Sarchie * does not match any of our known protocols (an ``orphan'').
26262143Sarchie *
26362143Sarchie * NOTE: this function will get called at splimp()
26462143Sarchie */
26562143Sarchiestatic void
266106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m)
26762143Sarchie{
26862143Sarchie	const node_p node = IFP2NG(ifp);
26970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
270129281Sarchie	int error;
27162143Sarchie
272129281Sarchie	/* If "orphan" hook not connected, discard packet */
273129281Sarchie	if (priv->orphan == NULL) {
27462143Sarchie		m_freem(m);
27562143Sarchie		return;
27662143Sarchie	}
277129281Sarchie	NG_SEND_DATA_ONLY(error, priv->orphan, m);
27862143Sarchie}
27962143Sarchie
28062143Sarchie/*
28162143Sarchie * Handle a packet that is going out on an interface.
28262143Sarchie * The Ethernet header is already attached to the mbuf.
28362143Sarchie */
28462143Sarchiestatic int
28562143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp)
28662143Sarchie{
28762143Sarchie	const node_p node = IFP2NG(ifp);
28870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
28962143Sarchie	int error = 0;
29062143Sarchie
29162143Sarchie	/* If "upper" hook not connected, let packet continue */
29262143Sarchie	if (priv->upper == NULL)
29362143Sarchie		return (0);
29462143Sarchie
29562143Sarchie	/* Send it out "upper" hook */
296194012Szec	NG_OUTBOUND_THREAD_REF();
29770700Sjulian	NG_SEND_DATA_ONLY(error, priv->upper, *mp);
298194012Szec	NG_OUTBOUND_THREAD_UNREF();
29962143Sarchie	return (error);
30062143Sarchie}
30162143Sarchie
30262143Sarchie/*
30362143Sarchie * A new Ethernet interface has been attached.
30462143Sarchie * Create a new node for it, etc.
30562143Sarchie */
30662143Sarchiestatic void
30762143Sarchieng_ether_attach(struct ifnet *ifp)
30862143Sarchie{
309247886Savg	char name[IFNAMSIZ];
31062143Sarchie	priv_p priv;
31162143Sarchie	node_p node;
31262143Sarchie
313191510Szec	/*
314191510Szec	 * Do not create / attach an ether node to this ifnet if
315191510Szec	 * a netgraph node with the same name already exists.
316191510Szec	 * This should prevent ether nodes to become attached to
317191510Szec	 * eiface nodes, which may be problematic due to naming
318191510Szec	 * clashes.
319191510Szec	 */
320191510Szec	if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) {
321191510Szec		NG_NODE_UNREF(node);
322191510Szec		return;
323191510Szec	}
324191510Szec
32562143Sarchie	/* Create node */
32687599Sobrien	KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__));
32762143Sarchie	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
32862143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
329121816Sbrooks		    __func__, "create node", ifp->if_xname);
33062143Sarchie		return;
33162143Sarchie	}
33262143Sarchie
33362143Sarchie	/* Allocate private data */
334184205Sdes	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
33562143Sarchie	if (priv == NULL) {
33662143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
337121816Sbrooks		    __func__, "allocate memory", ifp->if_xname);
33870784Sjulian		NG_NODE_UNREF(node);
33962143Sarchie		return;
34062143Sarchie	}
34170784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
34262143Sarchie	priv->ifp = ifp;
343152243Sru	IFP2NG(ifp) = node;
34490249Sarchie	priv->hwassist = ifp->if_hwassist;
34562143Sarchie
34662143Sarchie	/* Try to give the node the same name as the interface */
347247886Savg	ng_ether_sanitize_ifname(ifp->if_xname, name);
348247886Savg	if (ng_name_node(node, name) != 0)
349247886Savg		log(LOG_WARNING, "%s: can't name node %s\n", __func__, name);
35062143Sarchie}
35162143Sarchie
35262143Sarchie/*
35362143Sarchie * An Ethernet interface is being detached.
35471849Sjulian * REALLY Destroy its node.
35562143Sarchie */
35662143Sarchiestatic void
35762143Sarchieng_ether_detach(struct ifnet *ifp)
35862143Sarchie{
35962143Sarchie	const node_p node = IFP2NG(ifp);
36071849Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
36162143Sarchie
362224107Szec	taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
36371849Sjulian	NG_NODE_REALLY_DIE(node);	/* Force real removal of node */
36471849Sjulian	/*
36571849Sjulian	 * We can't assume the ifnet is still around when we run shutdown
36671849Sjulian	 * So zap it now. XXX We HOPE that anything running at this time
36771849Sjulian	 * handles it (as it should in the non netgraph case).
36871849Sjulian	 */
369152243Sru	IFP2NG(ifp) = NULL;
37071849Sjulian	priv->ifp = NULL;	/* XXX race if interrupted an output packet */
37171849Sjulian	ng_rmnode_self(node);		/* remove all netgraph parts */
37262143Sarchie}
37362143Sarchie
374139903Sglebius/*
375139903Sglebius * Notify graph about link event.
376139903Sglebius * if_link_state_change() has already checked that the state has changed.
377139903Sglebius */
378139903Sglebiusstatic void
379139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state)
380139903Sglebius{
381139903Sglebius	const node_p node = IFP2NG(ifp);
382139903Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
383139903Sglebius	struct ng_mesg *msg;
384139903Sglebius	int cmd, dummy_error = 0;
385139903Sglebius
386139903Sglebius	if (state == LINK_STATE_UP)
387139903Sglebius		cmd = NGM_LINK_IS_UP;
388139903Sglebius	else if (state == LINK_STATE_DOWN)
389139903Sglebius		cmd = NGM_LINK_IS_DOWN;
390139903Sglebius	else
391139903Sglebius		return;
392139903Sglebius
393201924Sfjoe	if (priv->lower != NULL) {
394201924Sfjoe		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
395201924Sfjoe		if (msg != NULL)
396201924Sfjoe			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0);
397201924Sfjoe	}
398201924Sfjoe	if (priv->orphan != NULL) {
399201924Sfjoe		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
400201924Sfjoe		if (msg != NULL)
401201924Sfjoe			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0);
402201924Sfjoe	}
403139903Sglebius}
404139903Sglebius
405247886Savg/*
406247886Savg * Interface arrival notification handler.
407247886Savg * The notification is produced in two cases:
408247886Savg *  o a new interface arrives
409247886Savg *  o an existing interface got renamed
410247886Savg * Currently the first case is handled by ng_ether_attach via special
411247886Savg * hook ng_ether_attach_p.
412247886Savg */
413247886Savgstatic void
414247886Savgng_ether_ifnet_arrival_event(void *arg __unused, struct ifnet *ifp)
415247886Savg{
416247886Savg	char name[IFNAMSIZ];
417247886Savg	node_p node;
418247886Savg
419247886Savg	/* Only ethernet interfaces are of interest. */
420247886Savg	if (ifp->if_type != IFT_ETHER
421247886Savg	    && ifp->if_type != IFT_L2VLAN)
422247886Savg		return;
423247886Savg
424247886Savg	/*
425247886Savg	 * Just return if it's a new interface without an ng_ether companion.
426247886Savg	 */
427247886Savg	node = IFP2NG(ifp);
428247886Savg	if (node == NULL)
429247886Savg		return;
430247886Savg
431247886Savg	/* Try to give the node the same name as the new interface name */
432247886Savg	ng_ether_sanitize_ifname(ifp->if_xname, name);
433247886Savg	if (ng_name_node(node, name) != 0)
434247886Savg		log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name);
435247886Savg}
436247886Savg
43762143Sarchie/******************************************************************
43862143Sarchie		    NETGRAPH NODE METHODS
43962143Sarchie******************************************************************/
44062143Sarchie
44162143Sarchie/*
44262143Sarchie * It is not possible or allowable to create a node of this type.
44362143Sarchie * Nodes get created when the interface is attached (or, when
44462143Sarchie * this node type's KLD is loaded).
44562143Sarchie */
44662143Sarchiestatic int
44770700Sjulianng_ether_constructor(node_p node)
44862143Sarchie{
44962143Sarchie	return (EINVAL);
45062143Sarchie}
45162143Sarchie
45262143Sarchie/*
45362143Sarchie * Check for attaching a new hook.
45462143Sarchie */
45562143Sarchiestatic	int
45662143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name)
45762143Sarchie{
45870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
45962143Sarchie	hook_p *hookptr;
46062143Sarchie
46162143Sarchie	/* Divert hook is an alias for lower */
46262143Sarchie	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
46362143Sarchie		name = NG_ETHER_HOOK_LOWER;
46462143Sarchie
46562143Sarchie	/* Which hook? */
466186488Sjulian	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) {
46762143Sarchie		hookptr = &priv->upper;
468186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper);
469194012Szec		NG_HOOK_SET_TO_INBOUND(hook);
470186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
47162143Sarchie		hookptr = &priv->lower;
472186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
473186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
474129281Sarchie		hookptr = &priv->orphan;
475186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
476186488Sjulian	} else
47762143Sarchie		return (EINVAL);
47862143Sarchie
47962143Sarchie	/* Check if already connected (shouldn't be, but doesn't hurt) */
48062143Sarchie	if (*hookptr != NULL)
48162143Sarchie		return (EISCONN);
48262143Sarchie
48390249Sarchie	/* Disable hardware checksums while 'upper' hook is connected */
48490249Sarchie	if (hookptr == &priv->upper)
48590249Sarchie		priv->ifp->if_hwassist = 0;
486194699Smav	NG_HOOK_HI_STACK(hook);
48762143Sarchie	/* OK */
48862143Sarchie	*hookptr = hook;
48962143Sarchie	return (0);
49062143Sarchie}
49162143Sarchie
49262143Sarchie/*
49362143Sarchie * Receive an incoming control message.
49462143Sarchie */
49562143Sarchiestatic int
49670700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook)
49762143Sarchie{
49870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
49962143Sarchie	struct ng_mesg *resp = NULL;
50062143Sarchie	int error = 0;
50170700Sjulian	struct ng_mesg *msg;
50262143Sarchie
50370700Sjulian	NGI_GET_MSG(item, msg);
50462143Sarchie	switch (msg->header.typecookie) {
50562143Sarchie	case NGM_ETHER_COOKIE:
50662143Sarchie		switch (msg->header.cmd) {
50762143Sarchie		case NGM_ETHER_GET_IFNAME:
508141195Sru			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
50962143Sarchie			if (resp == NULL) {
51062143Sarchie				error = ENOMEM;
51162143Sarchie				break;
51262143Sarchie			}
513141195Sru			strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
51462143Sarchie			break;
51562143Sarchie		case NGM_ETHER_GET_IFINDEX:
51662143Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
51762143Sarchie			if (resp == NULL) {
51862143Sarchie				error = ENOMEM;
51962143Sarchie				break;
52062143Sarchie			}
52162143Sarchie			*((u_int32_t *)resp->data) = priv->ifp->if_index;
52262143Sarchie			break;
52364358Sarchie		case NGM_ETHER_GET_ENADDR:
52464358Sarchie			NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT);
52564358Sarchie			if (resp == NULL) {
52664358Sarchie				error = ENOMEM;
52764358Sarchie				break;
52864358Sarchie			}
529152315Sru			bcopy(IF_LLADDR(priv->ifp),
53064358Sarchie			    resp->data, ETHER_ADDR_LEN);
53164358Sarchie			break;
53264653Sarchie		case NGM_ETHER_SET_ENADDR:
53364653Sarchie		    {
53464653Sarchie			if (msg->header.arglen != ETHER_ADDR_LEN) {
53564653Sarchie				error = EINVAL;
53664653Sarchie				break;
53764653Sarchie			}
53864653Sarchie			error = if_setlladdr(priv->ifp,
53964653Sarchie			    (u_char *)msg->data, ETHER_ADDR_LEN);
540202588Sthompsa			EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp);
54164653Sarchie			break;
54264653Sarchie		    }
54364653Sarchie		case NGM_ETHER_GET_PROMISC:
54464653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
54564653Sarchie			if (resp == NULL) {
54664653Sarchie				error = ENOMEM;
54764653Sarchie				break;
54864653Sarchie			}
54964653Sarchie			*((u_int32_t *)resp->data) = priv->promisc;
55064653Sarchie			break;
55164358Sarchie		case NGM_ETHER_SET_PROMISC:
55264358Sarchie		    {
55364358Sarchie			u_char want;
55464358Sarchie
55564358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
55664358Sarchie				error = EINVAL;
55764358Sarchie				break;
55864358Sarchie			}
55964358Sarchie			want = !!*((u_int32_t *)msg->data);
56064358Sarchie			if (want ^ priv->promisc) {
56164358Sarchie				if ((error = ifpromisc(priv->ifp, want)) != 0)
56264358Sarchie					break;
56364358Sarchie				priv->promisc = want;
56464358Sarchie			}
56564358Sarchie			break;
56664358Sarchie		    }
56764653Sarchie		case NGM_ETHER_GET_AUTOSRC:
56864653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
56964653Sarchie			if (resp == NULL) {
57064653Sarchie				error = ENOMEM;
57164653Sarchie				break;
57264653Sarchie			}
57364653Sarchie			*((u_int32_t *)resp->data) = priv->autoSrcAddr;
57464653Sarchie			break;
57564358Sarchie		case NGM_ETHER_SET_AUTOSRC:
57664358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
57764358Sarchie				error = EINVAL;
57864358Sarchie				break;
57964358Sarchie			}
58064358Sarchie			priv->autoSrcAddr = !!*((u_int32_t *)msg->data);
58164358Sarchie			break;
582141721Sglebius		case NGM_ETHER_ADD_MULTI:
583141721Sglebius		    {
584141721Sglebius			struct sockaddr_dl sa_dl;
585167729Sbms			struct ifmultiaddr *ifma;
586141721Sglebius
587141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
588141721Sglebius				error = EINVAL;
589141721Sglebius				break;
590141721Sglebius			}
591141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
592141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
593141721Sglebius			sa_dl.sdl_family = AF_LINK;
594141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
595141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
596141721Sglebius			    ETHER_ADDR_LEN);
597167729Sbms			/*
598167729Sbms			 * Netgraph is only permitted to join groups once
599167729Sbms			 * via the if_addmulti() KPI, because it cannot hold
600167729Sbms			 * struct ifmultiaddr * between calls. It may also
601167729Sbms			 * lose a race while we check if the membership
602167729Sbms			 * already exists.
603167729Sbms			 */
604195049Srwatson			if_maddr_rlock(priv->ifp);
605167729Sbms			ifma = if_findmulti(priv->ifp,
606167729Sbms			    (struct sockaddr *)&sa_dl);
607195049Srwatson			if_maddr_runlock(priv->ifp);
608167729Sbms			if (ifma != NULL) {
609167729Sbms				error = EADDRINUSE;
610167729Sbms			} else {
611167729Sbms				error = if_addmulti(priv->ifp,
612167729Sbms				    (struct sockaddr *)&sa_dl, &ifma);
613167729Sbms			}
614141721Sglebius			break;
615141721Sglebius		    }
616141721Sglebius		case NGM_ETHER_DEL_MULTI:
617141721Sglebius		    {
618141721Sglebius			struct sockaddr_dl sa_dl;
619141721Sglebius
620141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
621141721Sglebius				error = EINVAL;
622141721Sglebius				break;
623141721Sglebius			}
624141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
625141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
626141721Sglebius			sa_dl.sdl_family = AF_LINK;
627141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
628141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
629141721Sglebius			    ETHER_ADDR_LEN);
630141721Sglebius			error = if_delmulti(priv->ifp,
631141721Sglebius			    (struct sockaddr *)&sa_dl);
632141721Sglebius			break;
633141721Sglebius		    }
634141910Sglebius		case NGM_ETHER_DETACH:
635141910Sglebius			ng_ether_detach(priv->ifp);
636141910Sglebius			break;
63762143Sarchie		default:
63862143Sarchie			error = EINVAL;
63962143Sarchie			break;
64062143Sarchie		}
64162143Sarchie		break;
64262143Sarchie	default:
64362143Sarchie		error = EINVAL;
64462143Sarchie		break;
64562143Sarchie	}
64670700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
64770700Sjulian	NG_FREE_MSG(msg);
64862143Sarchie	return (error);
64962143Sarchie}
65062143Sarchie
65162143Sarchie/*
65262143Sarchie * Receive data on a hook.
653186488Sjulian * Since we use per-hook recveive methods this should never be called.
65462143Sarchie */
65562143Sarchiestatic int
65670700Sjulianng_ether_rcvdata(hook_p hook, item_p item)
65762143Sarchie{
65870700Sjulian	NG_FREE_ITEM(item);
659131155Sjulian
66087599Sobrien	panic("%s: weird hook", __func__);
66162143Sarchie}
66262143Sarchie
66362143Sarchie/*
664129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook.
66562143Sarchie */
66662143Sarchiestatic int
667186488Sjulianng_ether_rcv_lower(hook_p hook, item_p item)
66862143Sarchie{
669186488Sjulian	struct mbuf *m;
670186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
67170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
67296265Sarchie 	struct ifnet *const ifp = priv->ifp;
67362143Sarchie
674186488Sjulian	NGI_GET_M(item, m);
675186488Sjulian	NG_FREE_ITEM(item);
676186488Sjulian
67796265Sarchie	/* Check whether interface is ready for packets */
678186488Sjulian
679148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
680148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
68196265Sarchie		NG_FREE_M(m);
68296265Sarchie		return (ENETDOWN);
68396265Sarchie	}
68496265Sarchie
68562143Sarchie	/* Make sure header is fully pulled up */
68662143Sarchie	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
68770700Sjulian		NG_FREE_M(m);
68862143Sarchie		return (EINVAL);
68962143Sarchie	}
69062143Sarchie	if (m->m_len < sizeof(struct ether_header)
69197896Sarchie	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
69262143Sarchie		return (ENOBUFS);
69362143Sarchie
69464358Sarchie	/* Drop in the MAC address if desired */
69564358Sarchie	if (priv->autoSrcAddr) {
69697896Sarchie
69797896Sarchie		/* Make the mbuf writable if it's not already */
69897896Sarchie		if (!M_WRITABLE(m)
69997896Sarchie		    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
70097896Sarchie			return (ENOBUFS);
70197896Sarchie
70297896Sarchie		/* Overwrite source MAC address */
703152315Sru		bcopy(IF_LLADDR(ifp),
70464358Sarchie		    mtod(m, struct ether_header *)->ether_shost,
70564358Sarchie		    ETHER_ADDR_LEN);
70664358Sarchie	}
70762678Sjulian
70862143Sarchie	/* Send it on its way */
70996265Sarchie	return ether_output_frame(ifp, m);
71062143Sarchie}
71162143Sarchie
71262143Sarchie/*
71362143Sarchie * Handle an mbuf received on the "upper" hook.
71462143Sarchie */
71562143Sarchiestatic int
716186488Sjulianng_ether_rcv_upper(hook_p hook, item_p item)
71762143Sarchie{
718186488Sjulian	struct mbuf *m;
719186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
72070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
721151063Sglebius	struct ifnet *ifp = priv->ifp;
72262143Sarchie
723186488Sjulian	NGI_GET_M(item, m);
724186488Sjulian	NG_FREE_ITEM(item);
725186488Sjulian
726152001Sru	/* Check length and pull off header */
727152001Sru	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
728152001Sru		NG_FREE_M(m);
729152001Sru		return (EINVAL);
730152001Sru	}
731152001Sru	if (m->m_len < sizeof(struct ether_header) &&
732152001Sru	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
733152001Sru		return (ENOBUFS);
734152001Sru
735151063Sglebius	m->m_pkthdr.rcvif = ifp;
73662143Sarchie
737151305Sthompsa	/* Pass the packet to the bridge, it may come back to us */
738151063Sglebius	if (ifp->if_bridge) {
739151305Sthompsa		BRIDGE_INPUT(ifp, m);
740151063Sglebius		if (m == NULL)
741151063Sglebius			return (0);
742151063Sglebius	}
743151063Sglebius
74462143Sarchie	/* Route packet back in */
745151305Sthompsa	ether_demux(ifp, m);
74662143Sarchie	return (0);
74762143Sarchie}
74862143Sarchie
74962143Sarchie/*
75071849Sjulian * Shutdown node. This resets the node but does not remove it
75171849Sjulian * unless the REALLY_DIE flag is set.
75262143Sarchie */
75362143Sarchiestatic int
75470700Sjulianng_ether_shutdown(node_p node)
75562143Sarchie{
75670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
75764358Sarchie
758132464Sjulian	if (node->nd_flags & NGF_REALLY_DIE) {
75971849Sjulian		/*
76071849Sjulian		 * WE came here because the ethernet card is being unloaded,
76171849Sjulian		 * so stop being persistant.
76271849Sjulian		 * Actually undo all the things we did on creation.
76371849Sjulian		 * Assume the ifp has already been freed.
76471849Sjulian		 */
76571849Sjulian		NG_NODE_SET_PRIVATE(node, NULL);
766184205Sdes		free(priv, M_NETGRAPH);
76771849Sjulian		NG_NODE_UNREF(node);	/* free node itself */
76871849Sjulian		return (0);
76970700Sjulian	}
770124269Sgreen	if (priv->promisc) {		/* disable promiscuous mode */
771124269Sgreen		(void)ifpromisc(priv->ifp, 0);
772124269Sgreen		priv->promisc = 0;
773124269Sgreen	}
774132464Sjulian	NG_NODE_REVIVE(node);		/* Signal ng_rmnode we are persisant */
775132464Sjulian
77662143Sarchie	return (0);
77762143Sarchie}
77862143Sarchie
77962143Sarchie/*
78062143Sarchie * Hook disconnection.
78162143Sarchie */
78262143Sarchiestatic int
78362143Sarchieng_ether_disconnect(hook_p hook)
78462143Sarchie{
78570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
78662143Sarchie
78790249Sarchie	if (hook == priv->upper) {
78862143Sarchie		priv->upper = NULL;
789124270Sgreen		if (priv->ifp != NULL)		/* restore h/w csum */
790124270Sgreen			priv->ifp->if_hwassist = priv->hwassist;
791129281Sarchie	} else if (hook == priv->lower)
79262143Sarchie		priv->lower = NULL;
793129281Sarchie	else if (hook == priv->orphan)
794129281Sarchie		priv->orphan = NULL;
795129281Sarchie	else
79687599Sobrien		panic("%s: weird hook", __func__);
79770784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
79870784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
79970784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));	/* reset node */
80062143Sarchie	return (0);
80162143Sarchie}
80262143Sarchie
80362143Sarchie/******************************************************************
80462143Sarchie		    	INITIALIZATION
80562143Sarchie******************************************************************/
80662143Sarchie
80762143Sarchie/*
80862143Sarchie * Handle loading and unloading for this node type.
80962143Sarchie */
81062143Sarchiestatic int
81162143Sarchieng_ether_mod_event(module_t mod, int event, void *data)
81262143Sarchie{
81362143Sarchie	int error = 0;
81462143Sarchie	int s;
81562143Sarchie
81662143Sarchie	s = splnet();
81762143Sarchie	switch (event) {
81862143Sarchie	case MOD_LOAD:
81962143Sarchie
82062143Sarchie		/* Register function hooks */
82162143Sarchie		if (ng_ether_attach_p != NULL) {
82262143Sarchie			error = EEXIST;
82362143Sarchie			break;
82462143Sarchie		}
82562143Sarchie		ng_ether_attach_p = ng_ether_attach;
82662143Sarchie		ng_ether_detach_p = ng_ether_detach;
82762143Sarchie		ng_ether_output_p = ng_ether_output;
82862143Sarchie		ng_ether_input_p = ng_ether_input;
82962143Sarchie		ng_ether_input_orphan_p = ng_ether_input_orphan;
830139903Sglebius		ng_ether_link_state_p = ng_ether_link_state;
83162143Sarchie
832247886Savg		ng_ether_ifnet_arrival_cookie =
833247886Savg		    EVENTHANDLER_REGISTER(ifnet_arrival_event,
834247886Savg		    ng_ether_ifnet_arrival_event, NULL, EVENTHANDLER_PRI_ANY);
83562143Sarchie		break;
83662143Sarchie
83762143Sarchie	case MOD_UNLOAD:
83862143Sarchie
83962143Sarchie		/*
84062143Sarchie		 * Note that the base code won't try to unload us until
84162143Sarchie		 * all nodes have been removed, and that can't happen
84262143Sarchie		 * until all Ethernet interfaces are removed. In any
84362143Sarchie		 * case, we know there are no nodes left if the action
84462143Sarchie		 * is MOD_UNLOAD, so there's no need to detach any nodes.
84562143Sarchie		 */
84662143Sarchie
847247886Savg		EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
848247886Savg		    ng_ether_ifnet_arrival_cookie);
849247886Savg
85062143Sarchie		/* Unregister function hooks */
85162143Sarchie		ng_ether_attach_p = NULL;
85262143Sarchie		ng_ether_detach_p = NULL;
85362143Sarchie		ng_ether_output_p = NULL;
85462143Sarchie		ng_ether_input_p = NULL;
85562143Sarchie		ng_ether_input_orphan_p = NULL;
856139903Sglebius		ng_ether_link_state_p = NULL;
85762143Sarchie		break;
85862143Sarchie
85962143Sarchie	default:
86062143Sarchie		error = EOPNOTSUPP;
86162143Sarchie		break;
86262143Sarchie	}
86362143Sarchie	splx(s);
86462143Sarchie	return (error);
86562143Sarchie}
86662143Sarchie
867195837Srwatsonstatic void
868195837Srwatsonvnet_ng_ether_init(const void *unused)
869191510Szec{
870191510Szec	struct ifnet *ifp;
871191510Szec
872195837Srwatson	/* If module load was rejected, don't attach to vnets. */
873195837Srwatson	if (ng_ether_attach_p != ng_ether_attach)
874195837Srwatson		return;
875195837Srwatson
876191510Szec	/* Create nodes for any already-existing Ethernet interfaces. */
877191510Szec	IFNET_RLOCK();
878191510Szec	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
879191510Szec		if (ifp->if_type == IFT_ETHER
880191510Szec		    || ifp->if_type == IFT_L2VLAN)
881191510Szec			ng_ether_attach(ifp);
882191510Szec	}
883191510Szec	IFNET_RUNLOCK();
884191510Szec}
885195837SrwatsonVNET_SYSINIT(vnet_ng_ether_init, SI_SUB_PSEUDO, SI_ORDER_ANY,
886195837Srwatson    vnet_ng_ether_init, NULL);
887