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
74238844SemasteMODULE_VERSION(ng_ether, 1);
75238844Semaste
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
120246245Savgstatic eventhandler_tag	ng_ether_ifnet_arrival_cookie;
121246245Savg
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/******************************************************************
219246245Savg		    UTILITY FUNCTIONS
220246245Savg******************************************************************/
221246245Savgstatic void
222246245Savgng_ether_sanitize_ifname(const char *ifname, char *name)
223246245Savg{
224246245Savg	int i;
225246245Savg
226246245Savg	for (i = 0; i < IFNAMSIZ; i++) {
227246245Savg		if (ifname[i] == '.' || ifname[i] == ':')
228246245Savg			name[i] = '_';
229246245Savg		else
230246245Savg			name[i] = ifname[i];
231246245Savg		if (name[i] == '\0')
232246245Savg			break;
233246245Savg	}
234246245Savg}
235246245Savg
236246245Savg/******************************************************************
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 */
24462143Sarchiestatic void
245106933Ssamng_ether_input(struct ifnet *ifp, struct mbuf **mp)
24662143Sarchie{
24762143Sarchie	const node_p node = IFP2NG(ifp);
24870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
249129281Sarchie	int error;
25062143Sarchie
25162143Sarchie	/* If "lower" hook not connected, let packet continue */
252129281Sarchie	if (priv->lower == NULL)
25362143Sarchie		return;
254129281Sarchie	NG_SEND_DATA_ONLY(error, priv->lower, *mp);	/* sets *mp = NULL */
25562143Sarchie}
25662143Sarchie
25762143Sarchie/*
25862143Sarchie * Handle a packet that has come in on an interface, and which
25962143Sarchie * does not match any of our known protocols (an ``orphan'').
26062143Sarchie */
26162143Sarchiestatic void
262106933Ssamng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m)
26362143Sarchie{
26462143Sarchie	const node_p node = IFP2NG(ifp);
26570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
266129281Sarchie	int error;
26762143Sarchie
268129281Sarchie	/* If "orphan" hook not connected, discard packet */
269129281Sarchie	if (priv->orphan == NULL) {
27062143Sarchie		m_freem(m);
27162143Sarchie		return;
27262143Sarchie	}
273129281Sarchie	NG_SEND_DATA_ONLY(error, priv->orphan, m);
27462143Sarchie}
27562143Sarchie
27662143Sarchie/*
27762143Sarchie * Handle a packet that is going out on an interface.
27862143Sarchie * The Ethernet header is already attached to the mbuf.
27962143Sarchie */
28062143Sarchiestatic int
28162143Sarchieng_ether_output(struct ifnet *ifp, struct mbuf **mp)
28262143Sarchie{
28362143Sarchie	const node_p node = IFP2NG(ifp);
28470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
28562143Sarchie	int error = 0;
28662143Sarchie
28762143Sarchie	/* If "upper" hook not connected, let packet continue */
28862143Sarchie	if (priv->upper == NULL)
28962143Sarchie		return (0);
29062143Sarchie
29162143Sarchie	/* Send it out "upper" hook */
292194012Szec	NG_OUTBOUND_THREAD_REF();
29370700Sjulian	NG_SEND_DATA_ONLY(error, priv->upper, *mp);
294194012Szec	NG_OUTBOUND_THREAD_UNREF();
29562143Sarchie	return (error);
29662143Sarchie}
29762143Sarchie
29862143Sarchie/*
29962143Sarchie * A new Ethernet interface has been attached.
30062143Sarchie * Create a new node for it, etc.
30162143Sarchie */
30262143Sarchiestatic void
30362143Sarchieng_ether_attach(struct ifnet *ifp)
30462143Sarchie{
305246245Savg	char name[IFNAMSIZ];
30662143Sarchie	priv_p priv;
30762143Sarchie	node_p node;
30862143Sarchie
309191510Szec	/*
310191510Szec	 * Do not create / attach an ether node to this ifnet if
311191510Szec	 * a netgraph node with the same name already exists.
312191510Szec	 * This should prevent ether nodes to become attached to
313191510Szec	 * eiface nodes, which may be problematic due to naming
314191510Szec	 * clashes.
315191510Szec	 */
316191510Szec	if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) {
317191510Szec		NG_NODE_UNREF(node);
318191510Szec		return;
319191510Szec	}
320191510Szec
32162143Sarchie	/* Create node */
32287599Sobrien	KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__));
32362143Sarchie	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
32462143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
325121816Sbrooks		    __func__, "create node", ifp->if_xname);
32662143Sarchie		return;
32762143Sarchie	}
32862143Sarchie
32962143Sarchie	/* Allocate private data */
330184205Sdes	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
33162143Sarchie	if (priv == NULL) {
33262143Sarchie		log(LOG_ERR, "%s: can't %s for %s\n",
333121816Sbrooks		    __func__, "allocate memory", ifp->if_xname);
33470784Sjulian		NG_NODE_UNREF(node);
33562143Sarchie		return;
33662143Sarchie	}
33770784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
33862143Sarchie	priv->ifp = ifp;
339152243Sru	IFP2NG(ifp) = node;
34090249Sarchie	priv->hwassist = ifp->if_hwassist;
34162143Sarchie
34262143Sarchie	/* Try to give the node the same name as the interface */
343246245Savg	ng_ether_sanitize_ifname(ifp->if_xname, name);
344246245Savg	if (ng_name_node(node, name) != 0)
345246245Savg		log(LOG_WARNING, "%s: can't name node %s\n", __func__, name);
34662143Sarchie}
34762143Sarchie
34862143Sarchie/*
34962143Sarchie * An Ethernet interface is being detached.
35071849Sjulian * REALLY Destroy its node.
35162143Sarchie */
35262143Sarchiestatic void
35362143Sarchieng_ether_detach(struct ifnet *ifp)
35462143Sarchie{
35562143Sarchie	const node_p node = IFP2NG(ifp);
35671849Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
35762143Sarchie
358224107Szec	taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
35971849Sjulian	NG_NODE_REALLY_DIE(node);	/* Force real removal of node */
36071849Sjulian	/*
36171849Sjulian	 * We can't assume the ifnet is still around when we run shutdown
36271849Sjulian	 * So zap it now. XXX We HOPE that anything running at this time
36371849Sjulian	 * handles it (as it should in the non netgraph case).
36471849Sjulian	 */
365152243Sru	IFP2NG(ifp) = NULL;
36671849Sjulian	priv->ifp = NULL;	/* XXX race if interrupted an output packet */
36771849Sjulian	ng_rmnode_self(node);		/* remove all netgraph parts */
36862143Sarchie}
36962143Sarchie
370139903Sglebius/*
371139903Sglebius * Notify graph about link event.
372139903Sglebius * if_link_state_change() has already checked that the state has changed.
373139903Sglebius */
374139903Sglebiusstatic void
375139903Sglebiusng_ether_link_state(struct ifnet *ifp, int state)
376139903Sglebius{
377139903Sglebius	const node_p node = IFP2NG(ifp);
378139903Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
379139903Sglebius	struct ng_mesg *msg;
380139903Sglebius	int cmd, dummy_error = 0;
381139903Sglebius
382139903Sglebius	if (state == LINK_STATE_UP)
383139903Sglebius		cmd = NGM_LINK_IS_UP;
384139903Sglebius	else if (state == LINK_STATE_DOWN)
385139903Sglebius		cmd = NGM_LINK_IS_DOWN;
386139903Sglebius	else
387139903Sglebius		return;
388139903Sglebius
389201924Sfjoe	if (priv->lower != NULL) {
390201924Sfjoe		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
391201924Sfjoe		if (msg != NULL)
392201924Sfjoe			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0);
393201924Sfjoe	}
394201924Sfjoe	if (priv->orphan != NULL) {
395201924Sfjoe		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
396201924Sfjoe		if (msg != NULL)
397201924Sfjoe			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0);
398201924Sfjoe	}
399139903Sglebius}
400139903Sglebius
401246245Savg/*
402246245Savg * Interface arrival notification handler.
403246245Savg * The notification is produced in two cases:
404246245Savg *  o a new interface arrives
405246245Savg *  o an existing interface got renamed
406246245Savg * Currently the first case is handled by ng_ether_attach via special
407246245Savg * hook ng_ether_attach_p.
408246245Savg */
409246245Savgstatic void
410246245Savgng_ether_ifnet_arrival_event(void *arg __unused, struct ifnet *ifp)
411246245Savg{
412246245Savg	char name[IFNAMSIZ];
413246324Savg	node_p node;
414246245Savg
415246324Savg	/* Only ethernet interfaces are of interest. */
416246324Savg	if (ifp->if_type != IFT_ETHER
417246324Savg	    && ifp->if_type != IFT_L2VLAN)
418246324Savg		return;
419246324Savg
420246245Savg	/*
421246245Savg	 * Just return if it's a new interface without an ng_ether companion.
422246245Savg	 */
423246324Savg	node = IFP2NG(ifp);
424246245Savg	if (node == NULL)
425246245Savg		return;
426246245Savg
427246245Savg	/* Try to give the node the same name as the new interface name */
428246245Savg	ng_ether_sanitize_ifname(ifp->if_xname, name);
429246245Savg	if (ng_name_node(node, name) != 0)
430246245Savg		log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name);
431246245Savg}
432246245Savg
43362143Sarchie/******************************************************************
43462143Sarchie		    NETGRAPH NODE METHODS
43562143Sarchie******************************************************************/
43662143Sarchie
43762143Sarchie/*
43862143Sarchie * It is not possible or allowable to create a node of this type.
43962143Sarchie * Nodes get created when the interface is attached (or, when
44062143Sarchie * this node type's KLD is loaded).
44162143Sarchie */
44262143Sarchiestatic int
44370700Sjulianng_ether_constructor(node_p node)
44462143Sarchie{
44562143Sarchie	return (EINVAL);
44662143Sarchie}
44762143Sarchie
44862143Sarchie/*
44962143Sarchie * Check for attaching a new hook.
45062143Sarchie */
45162143Sarchiestatic	int
45262143Sarchieng_ether_newhook(node_p node, hook_p hook, const char *name)
45362143Sarchie{
45470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
45562143Sarchie	hook_p *hookptr;
45662143Sarchie
45762143Sarchie	/* Divert hook is an alias for lower */
45862143Sarchie	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
45962143Sarchie		name = NG_ETHER_HOOK_LOWER;
46062143Sarchie
46162143Sarchie	/* Which hook? */
462186488Sjulian	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) {
46362143Sarchie		hookptr = &priv->upper;
464186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper);
465194012Szec		NG_HOOK_SET_TO_INBOUND(hook);
466186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
46762143Sarchie		hookptr = &priv->lower;
468186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
469186488Sjulian	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
470129281Sarchie		hookptr = &priv->orphan;
471186488Sjulian		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
472186488Sjulian	} else
47362143Sarchie		return (EINVAL);
47462143Sarchie
47562143Sarchie	/* Check if already connected (shouldn't be, but doesn't hurt) */
47662143Sarchie	if (*hookptr != NULL)
47762143Sarchie		return (EISCONN);
47862143Sarchie
47990249Sarchie	/* Disable hardware checksums while 'upper' hook is connected */
48090249Sarchie	if (hookptr == &priv->upper)
48190249Sarchie		priv->ifp->if_hwassist = 0;
482194699Smav	NG_HOOK_HI_STACK(hook);
48362143Sarchie	/* OK */
48462143Sarchie	*hookptr = hook;
48562143Sarchie	return (0);
48662143Sarchie}
48762143Sarchie
48862143Sarchie/*
48962143Sarchie * Receive an incoming control message.
49062143Sarchie */
49162143Sarchiestatic int
49270700Sjulianng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook)
49362143Sarchie{
49470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
49562143Sarchie	struct ng_mesg *resp = NULL;
49662143Sarchie	int error = 0;
49770700Sjulian	struct ng_mesg *msg;
49862143Sarchie
49970700Sjulian	NGI_GET_MSG(item, msg);
50062143Sarchie	switch (msg->header.typecookie) {
50162143Sarchie	case NGM_ETHER_COOKIE:
50262143Sarchie		switch (msg->header.cmd) {
50362143Sarchie		case NGM_ETHER_GET_IFNAME:
504141195Sru			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
50562143Sarchie			if (resp == NULL) {
50662143Sarchie				error = ENOMEM;
50762143Sarchie				break;
50862143Sarchie			}
509141195Sru			strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
51062143Sarchie			break;
51162143Sarchie		case NGM_ETHER_GET_IFINDEX:
51262143Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
51362143Sarchie			if (resp == NULL) {
51462143Sarchie				error = ENOMEM;
51562143Sarchie				break;
51662143Sarchie			}
51762143Sarchie			*((u_int32_t *)resp->data) = priv->ifp->if_index;
51862143Sarchie			break;
51964358Sarchie		case NGM_ETHER_GET_ENADDR:
52064358Sarchie			NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT);
52164358Sarchie			if (resp == NULL) {
52264358Sarchie				error = ENOMEM;
52364358Sarchie				break;
52464358Sarchie			}
525152315Sru			bcopy(IF_LLADDR(priv->ifp),
52664358Sarchie			    resp->data, ETHER_ADDR_LEN);
52764358Sarchie			break;
52864653Sarchie		case NGM_ETHER_SET_ENADDR:
52964653Sarchie		    {
53064653Sarchie			if (msg->header.arglen != ETHER_ADDR_LEN) {
53164653Sarchie				error = EINVAL;
53264653Sarchie				break;
53364653Sarchie			}
53464653Sarchie			error = if_setlladdr(priv->ifp,
53564653Sarchie			    (u_char *)msg->data, ETHER_ADDR_LEN);
536202588Sthompsa			EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp);
53764653Sarchie			break;
53864653Sarchie		    }
53964653Sarchie		case NGM_ETHER_GET_PROMISC:
54064653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
54164653Sarchie			if (resp == NULL) {
54264653Sarchie				error = ENOMEM;
54364653Sarchie				break;
54464653Sarchie			}
54564653Sarchie			*((u_int32_t *)resp->data) = priv->promisc;
54664653Sarchie			break;
54764358Sarchie		case NGM_ETHER_SET_PROMISC:
54864358Sarchie		    {
54964358Sarchie			u_char want;
55064358Sarchie
55164358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
55264358Sarchie				error = EINVAL;
55364358Sarchie				break;
55464358Sarchie			}
55564358Sarchie			want = !!*((u_int32_t *)msg->data);
55664358Sarchie			if (want ^ priv->promisc) {
55764358Sarchie				if ((error = ifpromisc(priv->ifp, want)) != 0)
55864358Sarchie					break;
55964358Sarchie				priv->promisc = want;
56064358Sarchie			}
56164358Sarchie			break;
56264358Sarchie		    }
56364653Sarchie		case NGM_ETHER_GET_AUTOSRC:
56464653Sarchie			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
56564653Sarchie			if (resp == NULL) {
56664653Sarchie				error = ENOMEM;
56764653Sarchie				break;
56864653Sarchie			}
56964653Sarchie			*((u_int32_t *)resp->data) = priv->autoSrcAddr;
57064653Sarchie			break;
57164358Sarchie		case NGM_ETHER_SET_AUTOSRC:
57264358Sarchie			if (msg->header.arglen != sizeof(u_int32_t)) {
57364358Sarchie				error = EINVAL;
57464358Sarchie				break;
57564358Sarchie			}
57664358Sarchie			priv->autoSrcAddr = !!*((u_int32_t *)msg->data);
57764358Sarchie			break;
578141721Sglebius		case NGM_ETHER_ADD_MULTI:
579141721Sglebius		    {
580141721Sglebius			struct sockaddr_dl sa_dl;
581167729Sbms			struct ifmultiaddr *ifma;
582141721Sglebius
583141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
584141721Sglebius				error = EINVAL;
585141721Sglebius				break;
586141721Sglebius			}
587141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
588141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
589141721Sglebius			sa_dl.sdl_family = AF_LINK;
590141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
591141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
592141721Sglebius			    ETHER_ADDR_LEN);
593167729Sbms			/*
594167729Sbms			 * Netgraph is only permitted to join groups once
595167729Sbms			 * via the if_addmulti() KPI, because it cannot hold
596167729Sbms			 * struct ifmultiaddr * between calls. It may also
597167729Sbms			 * lose a race while we check if the membership
598167729Sbms			 * already exists.
599167729Sbms			 */
600195049Srwatson			if_maddr_rlock(priv->ifp);
601167729Sbms			ifma = if_findmulti(priv->ifp,
602167729Sbms			    (struct sockaddr *)&sa_dl);
603195049Srwatson			if_maddr_runlock(priv->ifp);
604167729Sbms			if (ifma != NULL) {
605167729Sbms				error = EADDRINUSE;
606167729Sbms			} else {
607167729Sbms				error = if_addmulti(priv->ifp,
608167729Sbms				    (struct sockaddr *)&sa_dl, &ifma);
609167729Sbms			}
610141721Sglebius			break;
611141721Sglebius		    }
612141721Sglebius		case NGM_ETHER_DEL_MULTI:
613141721Sglebius		    {
614141721Sglebius			struct sockaddr_dl sa_dl;
615141721Sglebius
616141721Sglebius			if (msg->header.arglen != ETHER_ADDR_LEN) {
617141721Sglebius				error = EINVAL;
618141721Sglebius				break;
619141721Sglebius			}
620141755Sglebius			bzero(&sa_dl, sizeof(struct sockaddr_dl));
621141721Sglebius			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
622141721Sglebius			sa_dl.sdl_family = AF_LINK;
623141755Sglebius			sa_dl.sdl_alen = ETHER_ADDR_LEN;
624141721Sglebius			bcopy((void *)msg->data, LLADDR(&sa_dl),
625141721Sglebius			    ETHER_ADDR_LEN);
626141721Sglebius			error = if_delmulti(priv->ifp,
627141721Sglebius			    (struct sockaddr *)&sa_dl);
628141721Sglebius			break;
629141721Sglebius		    }
630141910Sglebius		case NGM_ETHER_DETACH:
631141910Sglebius			ng_ether_detach(priv->ifp);
632141910Sglebius			break;
63362143Sarchie		default:
63462143Sarchie			error = EINVAL;
63562143Sarchie			break;
63662143Sarchie		}
63762143Sarchie		break;
63862143Sarchie	default:
63962143Sarchie		error = EINVAL;
64062143Sarchie		break;
64162143Sarchie	}
64270700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
64370700Sjulian	NG_FREE_MSG(msg);
64462143Sarchie	return (error);
64562143Sarchie}
64662143Sarchie
64762143Sarchie/*
64862143Sarchie * Receive data on a hook.
649186488Sjulian * Since we use per-hook recveive methods this should never be called.
65062143Sarchie */
65162143Sarchiestatic int
65270700Sjulianng_ether_rcvdata(hook_p hook, item_p item)
65362143Sarchie{
65470700Sjulian	NG_FREE_ITEM(item);
655131155Sjulian
65687599Sobrien	panic("%s: weird hook", __func__);
65762143Sarchie}
65862143Sarchie
65962143Sarchie/*
660129281Sarchie * Handle an mbuf received on the "lower" or "orphan" hook.
66162143Sarchie */
66262143Sarchiestatic int
663186488Sjulianng_ether_rcv_lower(hook_p hook, item_p item)
66462143Sarchie{
665186488Sjulian	struct mbuf *m;
666186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
66770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
66896265Sarchie 	struct ifnet *const ifp = priv->ifp;
66962143Sarchie
670186488Sjulian	NGI_GET_M(item, m);
671186488Sjulian	NG_FREE_ITEM(item);
672186488Sjulian
67396265Sarchie	/* Check whether interface is ready for packets */
674186488Sjulian
675148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
676148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
67796265Sarchie		NG_FREE_M(m);
67896265Sarchie		return (ENETDOWN);
67996265Sarchie	}
68096265Sarchie
68162143Sarchie	/* Make sure header is fully pulled up */
68262143Sarchie	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
68370700Sjulian		NG_FREE_M(m);
68462143Sarchie		return (EINVAL);
68562143Sarchie	}
68662143Sarchie	if (m->m_len < sizeof(struct ether_header)
68797896Sarchie	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
68862143Sarchie		return (ENOBUFS);
68962143Sarchie
69064358Sarchie	/* Drop in the MAC address if desired */
69164358Sarchie	if (priv->autoSrcAddr) {
69297896Sarchie
69397896Sarchie		/* Make the mbuf writable if it's not already */
69497896Sarchie		if (!M_WRITABLE(m)
69597896Sarchie		    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
69697896Sarchie			return (ENOBUFS);
69797896Sarchie
69897896Sarchie		/* Overwrite source MAC address */
699152315Sru		bcopy(IF_LLADDR(ifp),
70064358Sarchie		    mtod(m, struct ether_header *)->ether_shost,
70164358Sarchie		    ETHER_ADDR_LEN);
70264358Sarchie	}
70362678Sjulian
70462143Sarchie	/* Send it on its way */
70596265Sarchie	return ether_output_frame(ifp, m);
70662143Sarchie}
70762143Sarchie
70862143Sarchie/*
70962143Sarchie * Handle an mbuf received on the "upper" hook.
71062143Sarchie */
71162143Sarchiestatic int
712186488Sjulianng_ether_rcv_upper(hook_p hook, item_p item)
71362143Sarchie{
714186488Sjulian	struct mbuf *m;
715186488Sjulian	const node_p node = NG_HOOK_NODE(hook);
71670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
717151063Sglebius	struct ifnet *ifp = priv->ifp;
71862143Sarchie
719186488Sjulian	NGI_GET_M(item, m);
720186488Sjulian	NG_FREE_ITEM(item);
721186488Sjulian
722152001Sru	/* Check length and pull off header */
723152001Sru	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
724152001Sru		NG_FREE_M(m);
725152001Sru		return (EINVAL);
726152001Sru	}
727152001Sru	if (m->m_len < sizeof(struct ether_header) &&
728152001Sru	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
729152001Sru		return (ENOBUFS);
730152001Sru
731151063Sglebius	m->m_pkthdr.rcvif = ifp;
73262143Sarchie
733151305Sthompsa	/* Pass the packet to the bridge, it may come back to us */
734151063Sglebius	if (ifp->if_bridge) {
735151305Sthompsa		BRIDGE_INPUT(ifp, m);
736151063Sglebius		if (m == NULL)
737151063Sglebius			return (0);
738151063Sglebius	}
739151063Sglebius
74062143Sarchie	/* Route packet back in */
741151305Sthompsa	ether_demux(ifp, m);
74262143Sarchie	return (0);
74362143Sarchie}
74462143Sarchie
74562143Sarchie/*
74671849Sjulian * Shutdown node. This resets the node but does not remove it
74771849Sjulian * unless the REALLY_DIE flag is set.
74862143Sarchie */
74962143Sarchiestatic int
75070700Sjulianng_ether_shutdown(node_p node)
75162143Sarchie{
75270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
75364358Sarchie
754132464Sjulian	if (node->nd_flags & NGF_REALLY_DIE) {
75571849Sjulian		/*
75671849Sjulian		 * WE came here because the ethernet card is being unloaded,
75771849Sjulian		 * so stop being persistant.
75871849Sjulian		 * Actually undo all the things we did on creation.
75971849Sjulian		 * Assume the ifp has already been freed.
76071849Sjulian		 */
76171849Sjulian		NG_NODE_SET_PRIVATE(node, NULL);
762184205Sdes		free(priv, M_NETGRAPH);
76371849Sjulian		NG_NODE_UNREF(node);	/* free node itself */
76471849Sjulian		return (0);
76570700Sjulian	}
766124269Sgreen	if (priv->promisc) {		/* disable promiscuous mode */
767124269Sgreen		(void)ifpromisc(priv->ifp, 0);
768124269Sgreen		priv->promisc = 0;
769124269Sgreen	}
770132464Sjulian	NG_NODE_REVIVE(node);		/* Signal ng_rmnode we are persisant */
771132464Sjulian
77262143Sarchie	return (0);
77362143Sarchie}
77462143Sarchie
77562143Sarchie/*
77662143Sarchie * Hook disconnection.
77762143Sarchie */
77862143Sarchiestatic int
77962143Sarchieng_ether_disconnect(hook_p hook)
78062143Sarchie{
78170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
78262143Sarchie
78390249Sarchie	if (hook == priv->upper) {
78462143Sarchie		priv->upper = NULL;
785124270Sgreen		if (priv->ifp != NULL)		/* restore h/w csum */
786124270Sgreen			priv->ifp->if_hwassist = priv->hwassist;
787129281Sarchie	} else if (hook == priv->lower)
78862143Sarchie		priv->lower = NULL;
789129281Sarchie	else if (hook == priv->orphan)
790129281Sarchie		priv->orphan = NULL;
791129281Sarchie	else
79287599Sobrien		panic("%s: weird hook", __func__);
79370784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
79470784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
79570784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));	/* reset node */
79662143Sarchie	return (0);
79762143Sarchie}
79862143Sarchie
79962143Sarchie/******************************************************************
80062143Sarchie		    	INITIALIZATION
80162143Sarchie******************************************************************/
80262143Sarchie
80362143Sarchie/*
80462143Sarchie * Handle loading and unloading for this node type.
80562143Sarchie */
80662143Sarchiestatic int
80762143Sarchieng_ether_mod_event(module_t mod, int event, void *data)
80862143Sarchie{
80962143Sarchie	int error = 0;
81062143Sarchie
81162143Sarchie	switch (event) {
81262143Sarchie	case MOD_LOAD:
81362143Sarchie
81462143Sarchie		/* Register function hooks */
81562143Sarchie		if (ng_ether_attach_p != NULL) {
81662143Sarchie			error = EEXIST;
81762143Sarchie			break;
81862143Sarchie		}
81962143Sarchie		ng_ether_attach_p = ng_ether_attach;
82062143Sarchie		ng_ether_detach_p = ng_ether_detach;
82162143Sarchie		ng_ether_output_p = ng_ether_output;
82262143Sarchie		ng_ether_input_p = ng_ether_input;
82362143Sarchie		ng_ether_input_orphan_p = ng_ether_input_orphan;
824139903Sglebius		ng_ether_link_state_p = ng_ether_link_state;
82562143Sarchie
826246245Savg		ng_ether_ifnet_arrival_cookie =
827246245Savg		    EVENTHANDLER_REGISTER(ifnet_arrival_event,
828246245Savg		    ng_ether_ifnet_arrival_event, NULL, EVENTHANDLER_PRI_ANY);
82962143Sarchie		break;
83062143Sarchie
83162143Sarchie	case MOD_UNLOAD:
83262143Sarchie
83362143Sarchie		/*
83462143Sarchie		 * Note that the base code won't try to unload us until
83562143Sarchie		 * all nodes have been removed, and that can't happen
83662143Sarchie		 * until all Ethernet interfaces are removed. In any
83762143Sarchie		 * case, we know there are no nodes left if the action
83862143Sarchie		 * is MOD_UNLOAD, so there's no need to detach any nodes.
83962143Sarchie		 */
84062143Sarchie
841246245Savg		EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
842246245Savg		    ng_ether_ifnet_arrival_cookie);
843246245Savg
84462143Sarchie		/* Unregister function hooks */
84562143Sarchie		ng_ether_attach_p = NULL;
84662143Sarchie		ng_ether_detach_p = NULL;
84762143Sarchie		ng_ether_output_p = NULL;
84862143Sarchie		ng_ether_input_p = NULL;
84962143Sarchie		ng_ether_input_orphan_p = NULL;
850139903Sglebius		ng_ether_link_state_p = NULL;
85162143Sarchie		break;
85262143Sarchie
85362143Sarchie	default:
85462143Sarchie		error = EOPNOTSUPP;
85562143Sarchie		break;
85662143Sarchie	}
85762143Sarchie	return (error);
85862143Sarchie}
85962143Sarchie
860195837Srwatsonstatic void
861195837Srwatsonvnet_ng_ether_init(const void *unused)
862191510Szec{
863191510Szec	struct ifnet *ifp;
864191510Szec
865195837Srwatson	/* If module load was rejected, don't attach to vnets. */
866195837Srwatson	if (ng_ether_attach_p != ng_ether_attach)
867195837Srwatson		return;
868195837Srwatson
869191510Szec	/* Create nodes for any already-existing Ethernet interfaces. */
870191510Szec	IFNET_RLOCK();
871191510Szec	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
872191510Szec		if (ifp->if_type == IFT_ETHER
873191510Szec		    || ifp->if_type == IFT_L2VLAN)
874191510Szec			ng_ether_attach(ifp);
875191510Szec	}
876191510Szec	IFNET_RUNLOCK();
877191510Szec}
878195837SrwatsonVNET_SYSINIT(vnet_ng_ether_init, SI_SUB_PSEUDO, SI_ORDER_ANY,
879195837Srwatson    vnet_ng_ether_init, NULL);
880