xref: /freebsd/sys/netgraph/ng_ether.c (revision c1b8a9edabef3e1703b4ace3cdaadc833cf2465e)
1e1e1452dSArchie Cobbs 
2e1e1452dSArchie Cobbs /*
3e1e1452dSArchie Cobbs  * ng_ether.c
4c398230bSWarner Losh  */
5c398230bSWarner Losh 
6c398230bSWarner Losh /*-
7e1e1452dSArchie Cobbs  * Copyright (c) 1996-2000 Whistle Communications, Inc.
8e1e1452dSArchie Cobbs  * All rights reserved.
9e1e1452dSArchie Cobbs  *
10e1e1452dSArchie Cobbs  * Subject to the following obligations and disclaimer of warranty, use and
11e1e1452dSArchie Cobbs  * redistribution of this software, in source or object code forms, with or
12e1e1452dSArchie Cobbs  * without modifications are expressly permitted by Whistle Communications;
13e1e1452dSArchie Cobbs  * provided, however, that:
14e1e1452dSArchie Cobbs  * 1. Any and all reproductions of the source or object code must include the
15e1e1452dSArchie Cobbs  *    copyright notice above and the following disclaimer of warranties; and
16e1e1452dSArchie Cobbs  * 2. No rights are granted, in any manner or form, to use Whistle
17e1e1452dSArchie Cobbs  *    Communications, Inc. trademarks, including the mark "WHISTLE
18e1e1452dSArchie Cobbs  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
19e1e1452dSArchie Cobbs  *    such appears in the above copyright notice or in the software.
20e1e1452dSArchie Cobbs  *
21e1e1452dSArchie Cobbs  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
22e1e1452dSArchie Cobbs  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
23e1e1452dSArchie Cobbs  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
24e1e1452dSArchie Cobbs  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
25e1e1452dSArchie Cobbs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
26e1e1452dSArchie Cobbs  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
27e1e1452dSArchie Cobbs  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
28e1e1452dSArchie Cobbs  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
29e1e1452dSArchie Cobbs  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
30e1e1452dSArchie Cobbs  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
31e1e1452dSArchie Cobbs  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
32e1e1452dSArchie Cobbs  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
33e1e1452dSArchie Cobbs  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
34e1e1452dSArchie Cobbs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35e1e1452dSArchie Cobbs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36e1e1452dSArchie Cobbs  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
37e1e1452dSArchie Cobbs  * OF SUCH DAMAGE.
38e1e1452dSArchie Cobbs  *
39e1e1452dSArchie Cobbs  * Authors: Archie Cobbs <archie@freebsd.org>
40e1e1452dSArchie Cobbs  *	    Julian Elischer <julian@freebsd.org>
41e1e1452dSArchie Cobbs  *
42e1e1452dSArchie Cobbs  * $FreeBSD$
43e1e1452dSArchie Cobbs  */
44e1e1452dSArchie Cobbs 
45e1e1452dSArchie Cobbs /*
46e1e1452dSArchie Cobbs  * ng_ether(4) netgraph node type
47e1e1452dSArchie Cobbs  */
48e1e1452dSArchie Cobbs 
49e1e1452dSArchie Cobbs #include <sys/param.h>
50e1e1452dSArchie Cobbs #include <sys/systm.h>
51e1e1452dSArchie Cobbs #include <sys/kernel.h>
52e1e1452dSArchie Cobbs #include <sys/malloc.h>
53e1e1452dSArchie Cobbs #include <sys/mbuf.h>
54e1e1452dSArchie Cobbs #include <sys/errno.h>
55e1e1452dSArchie Cobbs #include <sys/syslog.h>
56e1e1452dSArchie Cobbs #include <sys/socket.h>
57603724d3SBjoern A. Zeeb #include <sys/vimage.h>
58e1e1452dSArchie Cobbs 
59e1e1452dSArchie Cobbs #include <net/if.h>
60810d5e89SGleb Smirnoff #include <net/if_dl.h>
61e1e1452dSArchie Cobbs #include <net/if_types.h>
62e1e1452dSArchie Cobbs #include <net/if_arp.h>
63e1e1452dSArchie Cobbs #include <net/if_var.h>
64e1e1452dSArchie Cobbs #include <net/ethernet.h>
65fd6238a6SAndrew Thompson #include <net/if_bridgevar.h>
6633553d6eSBjoern A. Zeeb #include <net/route.h>
674b79449eSBjoern A. Zeeb #include <net/vnet.h>
68e1e1452dSArchie Cobbs 
69e1e1452dSArchie Cobbs #include <netgraph/ng_message.h>
70e1e1452dSArchie Cobbs #include <netgraph/netgraph.h>
71e1e1452dSArchie Cobbs #include <netgraph/ng_parse.h>
72e1e1452dSArchie Cobbs #include <netgraph/ng_ether.h>
73e1e1452dSArchie Cobbs 
745240dcdbSRuslan Ermilov #define IFP2NG(ifp)  (IFP2AC((ifp))->ac_netgraph)
75e1e1452dSArchie Cobbs 
76aef8f344SMarko Zec static vnet_attach_fn ng_ether_iattach;
77aef8f344SMarko Zec 
78aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS
79aef8f344SMarko Zec static vnet_modinfo_t vnet_ng_ether_modinfo = {
80aef8f344SMarko Zec 	.vmi_id		= VNET_MOD_NG_ETHER,
81aef8f344SMarko Zec 	.vmi_name	= "ng_ether",
82aef8f344SMarko Zec 	.vmi_dependson	= VNET_MOD_NETGRAPH,
83aef8f344SMarko Zec 	.vmi_iattach	= ng_ether_iattach,
84aef8f344SMarko Zec };
85aef8f344SMarko Zec #endif
86aef8f344SMarko Zec 
873c976c3fSPawel Jakub Dawidek /* Per-node private data */
883c976c3fSPawel Jakub Dawidek struct private {
893c976c3fSPawel Jakub Dawidek 	struct ifnet	*ifp;		/* associated interface */
903c976c3fSPawel Jakub Dawidek 	hook_p		upper;		/* upper hook connection */
911a292b80SArchie Cobbs 	hook_p		lower;		/* lower hook connection */
921a292b80SArchie Cobbs 	hook_p		orphan;		/* orphan hook connection */
933c976c3fSPawel Jakub Dawidek 	u_char		autoSrcAddr;	/* always overwrite source address */
943c976c3fSPawel Jakub Dawidek 	u_char		promisc;	/* promiscuous mode enabled */
953c976c3fSPawel Jakub Dawidek 	u_long		hwassist;	/* hardware checksum capabilities */
963c976c3fSPawel Jakub Dawidek 	u_int		flags;		/* flags e.g. really die */
973c976c3fSPawel Jakub Dawidek };
983c976c3fSPawel Jakub Dawidek typedef struct private *priv_p;
99e1e1452dSArchie Cobbs 
100edbb5246SSam Leffler /* Hook pointers used by if_ethersubr.c to callback to netgraph */
101edbb5246SSam Leffler extern	void	(*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
102edbb5246SSam Leffler extern	void	(*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
103edbb5246SSam Leffler extern	int	(*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
104edbb5246SSam Leffler extern	void	(*ng_ether_attach_p)(struct ifnet *ifp);
105edbb5246SSam Leffler extern	void	(*ng_ether_detach_p)(struct ifnet *ifp);
1061c7899c7SGleb Smirnoff extern	void	(*ng_ether_link_state_p)(struct ifnet *ifp, int state);
107edbb5246SSam Leffler 
108e1e1452dSArchie Cobbs /* Functional hooks called from if_ethersubr.c */
109edbb5246SSam Leffler static void	ng_ether_input(struct ifnet *ifp, struct mbuf **mp);
110edbb5246SSam Leffler static void	ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m);
111e1e1452dSArchie Cobbs static int	ng_ether_output(struct ifnet *ifp, struct mbuf **mp);
112e1e1452dSArchie Cobbs static void	ng_ether_attach(struct ifnet *ifp);
113e1e1452dSArchie Cobbs static void	ng_ether_detach(struct ifnet *ifp);
1141c7899c7SGleb Smirnoff static void	ng_ether_link_state(struct ifnet *ifp, int state);
115e1e1452dSArchie Cobbs 
116e1e1452dSArchie Cobbs /* Other functions */
117f664dcdeSJulian Elischer static int	ng_ether_rcv_lower(hook_p node, item_p item);
118f664dcdeSJulian Elischer static int	ng_ether_rcv_upper(hook_p node, item_p item);
119e1e1452dSArchie Cobbs 
120e1e1452dSArchie Cobbs /* Netgraph node methods */
121e1e1452dSArchie Cobbs static ng_constructor_t	ng_ether_constructor;
122e1e1452dSArchie Cobbs static ng_rcvmsg_t	ng_ether_rcvmsg;
123069154d5SJulian Elischer static ng_shutdown_t	ng_ether_shutdown;
124e1e1452dSArchie Cobbs static ng_newhook_t	ng_ether_newhook;
125e1e1452dSArchie Cobbs static ng_rcvdata_t	ng_ether_rcvdata;
126e1e1452dSArchie Cobbs static ng_disconnect_t	ng_ether_disconnect;
127e1e1452dSArchie Cobbs static int		ng_ether_mod_event(module_t mod, int event, void *data);
128e1e1452dSArchie Cobbs 
129e1e1452dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
130e1e1452dSArchie Cobbs static const struct ng_cmdlist ng_ether_cmdlist[] = {
131e1e1452dSArchie Cobbs 	{
132e1e1452dSArchie Cobbs 	  NGM_ETHER_COOKIE,
133e1e1452dSArchie Cobbs 	  NGM_ETHER_GET_IFNAME,
134e1e1452dSArchie Cobbs 	  "getifname",
135e1e1452dSArchie Cobbs 	  NULL,
136e1e1452dSArchie Cobbs 	  &ng_parse_string_type
137e1e1452dSArchie Cobbs 	},
138e1e1452dSArchie Cobbs 	{
139e1e1452dSArchie Cobbs 	  NGM_ETHER_COOKIE,
140e1e1452dSArchie Cobbs 	  NGM_ETHER_GET_IFINDEX,
141e1e1452dSArchie Cobbs 	  "getifindex",
142e1e1452dSArchie Cobbs 	  NULL,
143e1e1452dSArchie Cobbs 	  &ng_parse_int32_type
144e1e1452dSArchie Cobbs 	},
1454b39c3c7SArchie Cobbs 	{
1464b39c3c7SArchie Cobbs 	  NGM_ETHER_COOKIE,
1474b39c3c7SArchie Cobbs 	  NGM_ETHER_GET_ENADDR,
1484b39c3c7SArchie Cobbs 	  "getenaddr",
1494b39c3c7SArchie Cobbs 	  NULL,
1508c7e4101SRuslan Ermilov 	  &ng_parse_enaddr_type
1514b39c3c7SArchie Cobbs 	},
1524b39c3c7SArchie Cobbs 	{
1534b39c3c7SArchie Cobbs 	  NGM_ETHER_COOKIE,
15456045c66SArchie Cobbs 	  NGM_ETHER_SET_ENADDR,
15556045c66SArchie Cobbs 	  "setenaddr",
1568c7e4101SRuslan Ermilov 	  &ng_parse_enaddr_type,
15756045c66SArchie Cobbs 	  NULL
15856045c66SArchie Cobbs 	},
15956045c66SArchie Cobbs 	{
16056045c66SArchie Cobbs 	  NGM_ETHER_COOKIE,
16156045c66SArchie Cobbs 	  NGM_ETHER_GET_PROMISC,
16256045c66SArchie Cobbs 	  "getpromisc",
16356045c66SArchie Cobbs 	  NULL,
16456045c66SArchie Cobbs 	  &ng_parse_int32_type
16556045c66SArchie Cobbs 	},
16656045c66SArchie Cobbs 	{
16756045c66SArchie Cobbs 	  NGM_ETHER_COOKIE,
1684b39c3c7SArchie Cobbs 	  NGM_ETHER_SET_PROMISC,
1694b39c3c7SArchie Cobbs 	  "setpromisc",
1704b39c3c7SArchie Cobbs 	  &ng_parse_int32_type,
1714b39c3c7SArchie Cobbs 	  NULL
1724b39c3c7SArchie Cobbs 	},
1734b39c3c7SArchie Cobbs 	{
1744b39c3c7SArchie Cobbs 	  NGM_ETHER_COOKIE,
17556045c66SArchie Cobbs 	  NGM_ETHER_GET_AUTOSRC,
17656045c66SArchie Cobbs 	  "getautosrc",
17756045c66SArchie Cobbs 	  NULL,
17856045c66SArchie Cobbs 	  &ng_parse_int32_type
17956045c66SArchie Cobbs 	},
18056045c66SArchie Cobbs 	{
18156045c66SArchie Cobbs 	  NGM_ETHER_COOKIE,
1824b39c3c7SArchie Cobbs 	  NGM_ETHER_SET_AUTOSRC,
1834b39c3c7SArchie Cobbs 	  "setautosrc",
1844b39c3c7SArchie Cobbs 	  &ng_parse_int32_type,
1854b39c3c7SArchie Cobbs 	  NULL
1864b39c3c7SArchie Cobbs 	},
187810d5e89SGleb Smirnoff 	{
188810d5e89SGleb Smirnoff 	  NGM_ETHER_COOKIE,
189810d5e89SGleb Smirnoff 	  NGM_ETHER_ADD_MULTI,
190810d5e89SGleb Smirnoff 	  "addmulti",
191810d5e89SGleb Smirnoff 	  &ng_parse_enaddr_type,
192810d5e89SGleb Smirnoff 	  NULL
193810d5e89SGleb Smirnoff 	},
194810d5e89SGleb Smirnoff 	{
195810d5e89SGleb Smirnoff 	  NGM_ETHER_COOKIE,
196810d5e89SGleb Smirnoff 	  NGM_ETHER_DEL_MULTI,
197810d5e89SGleb Smirnoff 	  "delmulti",
198810d5e89SGleb Smirnoff 	  &ng_parse_enaddr_type,
199810d5e89SGleb Smirnoff 	  NULL
200810d5e89SGleb Smirnoff 	},
201cefddd66SGleb Smirnoff 	{
202cefddd66SGleb Smirnoff 	  NGM_ETHER_COOKIE,
203cefddd66SGleb Smirnoff 	  NGM_ETHER_DETACH,
204cefddd66SGleb Smirnoff 	  "detach",
205cefddd66SGleb Smirnoff 	  NULL,
206cefddd66SGleb Smirnoff 	  NULL
207cefddd66SGleb Smirnoff 	},
208e1e1452dSArchie Cobbs 	{ 0 }
209e1e1452dSArchie Cobbs };
210e1e1452dSArchie Cobbs 
211e1e1452dSArchie Cobbs static struct ng_type ng_ether_typestruct = {
212f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
213f8aae777SJulian Elischer 	.name =		NG_ETHER_NODE_TYPE,
214f8aae777SJulian Elischer 	.mod_event =	ng_ether_mod_event,
215f8aae777SJulian Elischer 	.constructor =	ng_ether_constructor,
216f8aae777SJulian Elischer 	.rcvmsg =	ng_ether_rcvmsg,
217f8aae777SJulian Elischer 	.shutdown =	ng_ether_shutdown,
218f8aae777SJulian Elischer 	.newhook =	ng_ether_newhook,
219f8aae777SJulian Elischer 	.rcvdata =	ng_ether_rcvdata,
220f8aae777SJulian Elischer 	.disconnect =	ng_ether_disconnect,
221f8aae777SJulian Elischer 	.cmdlist =	ng_ether_cmdlist,
222e1e1452dSArchie Cobbs };
223e1e1452dSArchie Cobbs NETGRAPH_INIT(ether, &ng_ether_typestruct);
224e1e1452dSArchie Cobbs 
225e1e1452dSArchie Cobbs /******************************************************************
226e1e1452dSArchie Cobbs 		    ETHERNET FUNCTION HOOKS
227e1e1452dSArchie Cobbs ******************************************************************/
228e1e1452dSArchie Cobbs 
229e1e1452dSArchie Cobbs /*
230e1e1452dSArchie Cobbs  * Handle a packet that has come in on an interface. We get to
231e1e1452dSArchie Cobbs  * look at it here before any upper layer protocols do.
232e1e1452dSArchie Cobbs  *
233e1e1452dSArchie Cobbs  * NOTE: this function will get called at splimp()
234e1e1452dSArchie Cobbs  */
235e1e1452dSArchie Cobbs static void
236edbb5246SSam Leffler ng_ether_input(struct ifnet *ifp, struct mbuf **mp)
237e1e1452dSArchie Cobbs {
238e1e1452dSArchie Cobbs 	const node_p node = IFP2NG(ifp);
23930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
2401a292b80SArchie Cobbs 	int error;
241e1e1452dSArchie Cobbs 
242e1e1452dSArchie Cobbs 	/* If "lower" hook not connected, let packet continue */
2431a292b80SArchie Cobbs 	if (priv->lower == NULL)
244e1e1452dSArchie Cobbs 		return;
2451a292b80SArchie Cobbs 	NG_SEND_DATA_ONLY(error, priv->lower, *mp);	/* sets *mp = NULL */
246e1e1452dSArchie Cobbs }
247e1e1452dSArchie Cobbs 
248e1e1452dSArchie Cobbs /*
249e1e1452dSArchie Cobbs  * Handle a packet that has come in on an interface, and which
250e1e1452dSArchie Cobbs  * does not match any of our known protocols (an ``orphan'').
251e1e1452dSArchie Cobbs  *
252e1e1452dSArchie Cobbs  * NOTE: this function will get called at splimp()
253e1e1452dSArchie Cobbs  */
254e1e1452dSArchie Cobbs static void
255edbb5246SSam Leffler ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m)
256e1e1452dSArchie Cobbs {
257e1e1452dSArchie Cobbs 	const node_p node = IFP2NG(ifp);
25830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
2591a292b80SArchie Cobbs 	int error;
260e1e1452dSArchie Cobbs 
2611a292b80SArchie Cobbs 	/* If "orphan" hook not connected, discard packet */
2621a292b80SArchie Cobbs 	if (priv->orphan == NULL) {
263e1e1452dSArchie Cobbs 		m_freem(m);
264e1e1452dSArchie Cobbs 		return;
265e1e1452dSArchie Cobbs 	}
2661a292b80SArchie Cobbs 	NG_SEND_DATA_ONLY(error, priv->orphan, m);
267e1e1452dSArchie Cobbs }
268e1e1452dSArchie Cobbs 
269e1e1452dSArchie Cobbs /*
270e1e1452dSArchie Cobbs  * Handle a packet that is going out on an interface.
271e1e1452dSArchie Cobbs  * The Ethernet header is already attached to the mbuf.
272e1e1452dSArchie Cobbs  */
273e1e1452dSArchie Cobbs static int
274e1e1452dSArchie Cobbs ng_ether_output(struct ifnet *ifp, struct mbuf **mp)
275e1e1452dSArchie Cobbs {
276e1e1452dSArchie Cobbs 	const node_p node = IFP2NG(ifp);
27730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
278e1e1452dSArchie Cobbs 	int error = 0;
279e1e1452dSArchie Cobbs 
280e1e1452dSArchie Cobbs 	/* If "upper" hook not connected, let packet continue */
281e1e1452dSArchie Cobbs 	if (priv->upper == NULL)
282e1e1452dSArchie Cobbs 		return (0);
283e1e1452dSArchie Cobbs 
284e1e1452dSArchie Cobbs 	/* Send it out "upper" hook */
285f089869fSMarko Zec 	NG_OUTBOUND_THREAD_REF();
286069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, priv->upper, *mp);
287f089869fSMarko Zec 	NG_OUTBOUND_THREAD_UNREF();
288e1e1452dSArchie Cobbs 	return (error);
289e1e1452dSArchie Cobbs }
290e1e1452dSArchie Cobbs 
291e1e1452dSArchie Cobbs /*
292e1e1452dSArchie Cobbs  * A new Ethernet interface has been attached.
293e1e1452dSArchie Cobbs  * Create a new node for it, etc.
294e1e1452dSArchie Cobbs  */
295e1e1452dSArchie Cobbs static void
296e1e1452dSArchie Cobbs ng_ether_attach(struct ifnet *ifp)
297e1e1452dSArchie Cobbs {
298e1e1452dSArchie Cobbs 	priv_p priv;
299e1e1452dSArchie Cobbs 	node_p node;
300e1e1452dSArchie Cobbs 
301aef8f344SMarko Zec 	/*
302aef8f344SMarko Zec 	 * Do not create / attach an ether node to this ifnet if
303aef8f344SMarko Zec 	 * a netgraph node with the same name already exists.
304aef8f344SMarko Zec 	 * This should prevent ether nodes to become attached to
305aef8f344SMarko Zec 	 * eiface nodes, which may be problematic due to naming
306aef8f344SMarko Zec 	 * clashes.
307aef8f344SMarko Zec 	 */
308aef8f344SMarko Zec 	if ((node = ng_name2noderef(NULL, ifp->if_xname)) != NULL) {
309aef8f344SMarko Zec 		NG_NODE_UNREF(node);
310aef8f344SMarko Zec 		return;
311aef8f344SMarko Zec 	}
312aef8f344SMarko Zec 
313e1e1452dSArchie Cobbs 	/* Create node */
3146e551fb6SDavid E. O'Brien 	KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__));
315e1e1452dSArchie Cobbs 	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
316e1e1452dSArchie Cobbs 		log(LOG_ERR, "%s: can't %s for %s\n",
3179bf40edeSBrooks Davis 		    __func__, "create node", ifp->if_xname);
318e1e1452dSArchie Cobbs 		return;
319e1e1452dSArchie Cobbs 	}
320e1e1452dSArchie Cobbs 
321e1e1452dSArchie Cobbs 	/* Allocate private data */
3221ede983cSDag-Erling Smørgrav 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
323e1e1452dSArchie Cobbs 	if (priv == NULL) {
324e1e1452dSArchie Cobbs 		log(LOG_ERR, "%s: can't %s for %s\n",
3259bf40edeSBrooks Davis 		    __func__, "allocate memory", ifp->if_xname);
32630400f03SJulian Elischer 		NG_NODE_UNREF(node);
327e1e1452dSArchie Cobbs 		return;
328e1e1452dSArchie Cobbs 	}
32930400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
330e1e1452dSArchie Cobbs 	priv->ifp = ifp;
3315240dcdbSRuslan Ermilov 	IFP2NG(ifp) = node;
332a3e232d6SArchie Cobbs 	priv->hwassist = ifp->if_hwassist;
333e1e1452dSArchie Cobbs 
334e1e1452dSArchie Cobbs 	/* Try to give the node the same name as the interface */
3359bf40edeSBrooks Davis 	if (ng_name_node(node, ifp->if_xname) != 0) {
336e1e1452dSArchie Cobbs 		log(LOG_WARNING, "%s: can't name node %s\n",
3379bf40edeSBrooks Davis 		    __func__, ifp->if_xname);
338e1e1452dSArchie Cobbs 	}
339e1e1452dSArchie Cobbs }
340e1e1452dSArchie Cobbs 
341e1e1452dSArchie Cobbs /*
342e1e1452dSArchie Cobbs  * An Ethernet interface is being detached.
3431acb27c6SJulian Elischer  * REALLY Destroy its node.
344e1e1452dSArchie Cobbs  */
345e1e1452dSArchie Cobbs static void
346e1e1452dSArchie Cobbs ng_ether_detach(struct ifnet *ifp)
347e1e1452dSArchie Cobbs {
348e1e1452dSArchie Cobbs 	const node_p node = IFP2NG(ifp);
3491acb27c6SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
350e1e1452dSArchie Cobbs 
3511acb27c6SJulian Elischer 	NG_NODE_REALLY_DIE(node);	/* Force real removal of node */
3521acb27c6SJulian Elischer 	/*
3531acb27c6SJulian Elischer 	 * We can't assume the ifnet is still around when we run shutdown
3541acb27c6SJulian Elischer 	 * So zap it now. XXX We HOPE that anything running at this time
3551acb27c6SJulian Elischer 	 * handles it (as it should in the non netgraph case).
3561acb27c6SJulian Elischer 	 */
3575240dcdbSRuslan Ermilov 	IFP2NG(ifp) = NULL;
3581acb27c6SJulian Elischer 	priv->ifp = NULL;	/* XXX race if interrupted an output packet */
3591acb27c6SJulian Elischer 	ng_rmnode_self(node);		/* remove all netgraph parts */
360e1e1452dSArchie Cobbs }
361e1e1452dSArchie Cobbs 
3621c7899c7SGleb Smirnoff /*
3631c7899c7SGleb Smirnoff  * Notify graph about link event.
3641c7899c7SGleb Smirnoff  * if_link_state_change() has already checked that the state has changed.
3651c7899c7SGleb Smirnoff  */
3661c7899c7SGleb Smirnoff static void
3671c7899c7SGleb Smirnoff ng_ether_link_state(struct ifnet *ifp, int state)
3681c7899c7SGleb Smirnoff {
3691c7899c7SGleb Smirnoff 	const node_p node = IFP2NG(ifp);
3701c7899c7SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
3711c7899c7SGleb Smirnoff 	struct ng_mesg *msg;
3721c7899c7SGleb Smirnoff 	int cmd, dummy_error = 0;
3731c7899c7SGleb Smirnoff 
3741c7899c7SGleb Smirnoff 	if (priv->lower == NULL)
3751c7899c7SGleb Smirnoff                 return;
3761c7899c7SGleb Smirnoff 
3771c7899c7SGleb Smirnoff 	if (state == LINK_STATE_UP)
3781c7899c7SGleb Smirnoff 		cmd = NGM_LINK_IS_UP;
3791c7899c7SGleb Smirnoff 	else if (state == LINK_STATE_DOWN)
3801c7899c7SGleb Smirnoff 		cmd = NGM_LINK_IS_DOWN;
3811c7899c7SGleb Smirnoff 	else
3821c7899c7SGleb Smirnoff 		return;
3831c7899c7SGleb Smirnoff 
3841c7899c7SGleb Smirnoff 	NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
3851c7899c7SGleb Smirnoff 	if (msg != NULL)
3861c7899c7SGleb Smirnoff 		NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0);
3871c7899c7SGleb Smirnoff }
3881c7899c7SGleb Smirnoff 
389e1e1452dSArchie Cobbs /******************************************************************
390e1e1452dSArchie Cobbs 		    NETGRAPH NODE METHODS
391e1e1452dSArchie Cobbs ******************************************************************/
392e1e1452dSArchie Cobbs 
393e1e1452dSArchie Cobbs /*
394e1e1452dSArchie Cobbs  * It is not possible or allowable to create a node of this type.
395e1e1452dSArchie Cobbs  * Nodes get created when the interface is attached (or, when
396e1e1452dSArchie Cobbs  * this node type's KLD is loaded).
397e1e1452dSArchie Cobbs  */
398e1e1452dSArchie Cobbs static int
399069154d5SJulian Elischer ng_ether_constructor(node_p node)
400e1e1452dSArchie Cobbs {
401e1e1452dSArchie Cobbs 	return (EINVAL);
402e1e1452dSArchie Cobbs }
403e1e1452dSArchie Cobbs 
404e1e1452dSArchie Cobbs /*
405e1e1452dSArchie Cobbs  * Check for attaching a new hook.
406e1e1452dSArchie Cobbs  */
407e1e1452dSArchie Cobbs static	int
408e1e1452dSArchie Cobbs ng_ether_newhook(node_p node, hook_p hook, const char *name)
409e1e1452dSArchie Cobbs {
41030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
411e1e1452dSArchie Cobbs 	hook_p *hookptr;
412e1e1452dSArchie Cobbs 
413e1e1452dSArchie Cobbs 	/* Divert hook is an alias for lower */
414e1e1452dSArchie Cobbs 	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
415e1e1452dSArchie Cobbs 		name = NG_ETHER_HOOK_LOWER;
416e1e1452dSArchie Cobbs 
417e1e1452dSArchie Cobbs 	/* Which hook? */
418f664dcdeSJulian Elischer 	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) {
419e1e1452dSArchie Cobbs 		hookptr = &priv->upper;
420f664dcdeSJulian Elischer 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper);
421f089869fSMarko Zec 		NG_HOOK_SET_TO_INBOUND(hook);
422f664dcdeSJulian Elischer 	} else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
423e1e1452dSArchie Cobbs 		hookptr = &priv->lower;
424f664dcdeSJulian Elischer 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
425f664dcdeSJulian Elischer 	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
4261a292b80SArchie Cobbs 		hookptr = &priv->orphan;
427f664dcdeSJulian Elischer 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
428f664dcdeSJulian Elischer 	} else
429e1e1452dSArchie Cobbs 		return (EINVAL);
430e1e1452dSArchie Cobbs 
431e1e1452dSArchie Cobbs 	/* Check if already connected (shouldn't be, but doesn't hurt) */
432e1e1452dSArchie Cobbs 	if (*hookptr != NULL)
433e1e1452dSArchie Cobbs 		return (EISCONN);
434e1e1452dSArchie Cobbs 
435a3e232d6SArchie Cobbs 	/* Disable hardware checksums while 'upper' hook is connected */
436a3e232d6SArchie Cobbs 	if (hookptr == &priv->upper)
437a3e232d6SArchie Cobbs 		priv->ifp->if_hwassist = 0;
438c1b8a9edSAlexander Motin 	NG_HOOK_HI_STACK(hook);
439e1e1452dSArchie Cobbs 	/* OK */
440e1e1452dSArchie Cobbs 	*hookptr = hook;
441e1e1452dSArchie Cobbs 	return (0);
442e1e1452dSArchie Cobbs }
443e1e1452dSArchie Cobbs 
444e1e1452dSArchie Cobbs /*
445e1e1452dSArchie Cobbs  * Receive an incoming control message.
446e1e1452dSArchie Cobbs  */
447e1e1452dSArchie Cobbs static int
448069154d5SJulian Elischer ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook)
449e1e1452dSArchie Cobbs {
45030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
451e1e1452dSArchie Cobbs 	struct ng_mesg *resp = NULL;
452e1e1452dSArchie Cobbs 	int error = 0;
453069154d5SJulian Elischer 	struct ng_mesg *msg;
454e1e1452dSArchie Cobbs 
455069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
456e1e1452dSArchie Cobbs 	switch (msg->header.typecookie) {
457e1e1452dSArchie Cobbs 	case NGM_ETHER_COOKIE:
458e1e1452dSArchie Cobbs 		switch (msg->header.cmd) {
459e1e1452dSArchie Cobbs 		case NGM_ETHER_GET_IFNAME:
460bbb75d78SRuslan Ermilov 			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
461e1e1452dSArchie Cobbs 			if (resp == NULL) {
462e1e1452dSArchie Cobbs 				error = ENOMEM;
463e1e1452dSArchie Cobbs 				break;
464e1e1452dSArchie Cobbs 			}
465bbb75d78SRuslan Ermilov 			strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
466e1e1452dSArchie Cobbs 			break;
467e1e1452dSArchie Cobbs 		case NGM_ETHER_GET_IFINDEX:
468e1e1452dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
469e1e1452dSArchie Cobbs 			if (resp == NULL) {
470e1e1452dSArchie Cobbs 				error = ENOMEM;
471e1e1452dSArchie Cobbs 				break;
472e1e1452dSArchie Cobbs 			}
473e1e1452dSArchie Cobbs 			*((u_int32_t *)resp->data) = priv->ifp->if_index;
474e1e1452dSArchie Cobbs 			break;
4754b39c3c7SArchie Cobbs 		case NGM_ETHER_GET_ENADDR:
4764b39c3c7SArchie Cobbs 			NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT);
4774b39c3c7SArchie Cobbs 			if (resp == NULL) {
4784b39c3c7SArchie Cobbs 				error = ENOMEM;
4794b39c3c7SArchie Cobbs 				break;
4804b39c3c7SArchie Cobbs 			}
4814a0d6638SRuslan Ermilov 			bcopy(IF_LLADDR(priv->ifp),
4824b39c3c7SArchie Cobbs 			    resp->data, ETHER_ADDR_LEN);
4834b39c3c7SArchie Cobbs 			break;
48456045c66SArchie Cobbs 		case NGM_ETHER_SET_ENADDR:
48556045c66SArchie Cobbs 		    {
48656045c66SArchie Cobbs 			if (msg->header.arglen != ETHER_ADDR_LEN) {
48756045c66SArchie Cobbs 				error = EINVAL;
48856045c66SArchie Cobbs 				break;
48956045c66SArchie Cobbs 			}
49056045c66SArchie Cobbs 			error = if_setlladdr(priv->ifp,
49156045c66SArchie Cobbs 			    (u_char *)msg->data, ETHER_ADDR_LEN);
49256045c66SArchie Cobbs 			break;
49356045c66SArchie Cobbs 		    }
49456045c66SArchie Cobbs 		case NGM_ETHER_GET_PROMISC:
49556045c66SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
49656045c66SArchie Cobbs 			if (resp == NULL) {
49756045c66SArchie Cobbs 				error = ENOMEM;
49856045c66SArchie Cobbs 				break;
49956045c66SArchie Cobbs 			}
50056045c66SArchie Cobbs 			*((u_int32_t *)resp->data) = priv->promisc;
50156045c66SArchie Cobbs 			break;
5024b39c3c7SArchie Cobbs 		case NGM_ETHER_SET_PROMISC:
5034b39c3c7SArchie Cobbs 		    {
5044b39c3c7SArchie Cobbs 			u_char want;
5054b39c3c7SArchie Cobbs 
5064b39c3c7SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int32_t)) {
5074b39c3c7SArchie Cobbs 				error = EINVAL;
5084b39c3c7SArchie Cobbs 				break;
5094b39c3c7SArchie Cobbs 			}
5104b39c3c7SArchie Cobbs 			want = !!*((u_int32_t *)msg->data);
5114b39c3c7SArchie Cobbs 			if (want ^ priv->promisc) {
5124b39c3c7SArchie Cobbs 				if ((error = ifpromisc(priv->ifp, want)) != 0)
5134b39c3c7SArchie Cobbs 					break;
5144b39c3c7SArchie Cobbs 				priv->promisc = want;
5154b39c3c7SArchie Cobbs 			}
5164b39c3c7SArchie Cobbs 			break;
5174b39c3c7SArchie Cobbs 		    }
51856045c66SArchie Cobbs 		case NGM_ETHER_GET_AUTOSRC:
51956045c66SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
52056045c66SArchie Cobbs 			if (resp == NULL) {
52156045c66SArchie Cobbs 				error = ENOMEM;
52256045c66SArchie Cobbs 				break;
52356045c66SArchie Cobbs 			}
52456045c66SArchie Cobbs 			*((u_int32_t *)resp->data) = priv->autoSrcAddr;
52556045c66SArchie Cobbs 			break;
5264b39c3c7SArchie Cobbs 		case NGM_ETHER_SET_AUTOSRC:
5274b39c3c7SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int32_t)) {
5284b39c3c7SArchie Cobbs 				error = EINVAL;
5294b39c3c7SArchie Cobbs 				break;
5304b39c3c7SArchie Cobbs 			}
5314b39c3c7SArchie Cobbs 			priv->autoSrcAddr = !!*((u_int32_t *)msg->data);
5324b39c3c7SArchie Cobbs 			break;
533810d5e89SGleb Smirnoff 		case NGM_ETHER_ADD_MULTI:
534810d5e89SGleb Smirnoff 		    {
535810d5e89SGleb Smirnoff 			struct sockaddr_dl sa_dl;
536ec002feeSBruce M Simpson 			struct ifmultiaddr *ifma;
537810d5e89SGleb Smirnoff 
538810d5e89SGleb Smirnoff 			if (msg->header.arglen != ETHER_ADDR_LEN) {
539810d5e89SGleb Smirnoff 				error = EINVAL;
540810d5e89SGleb Smirnoff 				break;
541810d5e89SGleb Smirnoff 			}
542ba20540eSGleb Smirnoff 			bzero(&sa_dl, sizeof(struct sockaddr_dl));
543810d5e89SGleb Smirnoff 			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
544810d5e89SGleb Smirnoff 			sa_dl.sdl_family = AF_LINK;
545ba20540eSGleb Smirnoff 			sa_dl.sdl_alen = ETHER_ADDR_LEN;
546810d5e89SGleb Smirnoff 			bcopy((void *)msg->data, LLADDR(&sa_dl),
547810d5e89SGleb Smirnoff 			    ETHER_ADDR_LEN);
548ec002feeSBruce M Simpson 			/*
549ec002feeSBruce M Simpson 			 * Netgraph is only permitted to join groups once
550ec002feeSBruce M Simpson 			 * via the if_addmulti() KPI, because it cannot hold
551ec002feeSBruce M Simpson 			 * struct ifmultiaddr * between calls. It may also
552ec002feeSBruce M Simpson 			 * lose a race while we check if the membership
553ec002feeSBruce M Simpson 			 * already exists.
554ec002feeSBruce M Simpson 			 */
555ec002feeSBruce M Simpson 			IF_ADDR_LOCK(priv->ifp);
556ec002feeSBruce M Simpson 			ifma = if_findmulti(priv->ifp,
557ec002feeSBruce M Simpson 			    (struct sockaddr *)&sa_dl);
558ec002feeSBruce M Simpson 			IF_ADDR_UNLOCK(priv->ifp);
559ec002feeSBruce M Simpson 			if (ifma != NULL) {
560ec002feeSBruce M Simpson 				error = EADDRINUSE;
561ec002feeSBruce M Simpson 			} else {
562810d5e89SGleb Smirnoff 				error = if_addmulti(priv->ifp,
563ec002feeSBruce M Simpson 				    (struct sockaddr *)&sa_dl, &ifma);
564ec002feeSBruce M Simpson 			}
565810d5e89SGleb Smirnoff 			break;
566810d5e89SGleb Smirnoff 		    }
567810d5e89SGleb Smirnoff 		case NGM_ETHER_DEL_MULTI:
568810d5e89SGleb Smirnoff 		    {
569810d5e89SGleb Smirnoff 			struct sockaddr_dl sa_dl;
570810d5e89SGleb Smirnoff 
571810d5e89SGleb Smirnoff 			if (msg->header.arglen != ETHER_ADDR_LEN) {
572810d5e89SGleb Smirnoff 				error = EINVAL;
573810d5e89SGleb Smirnoff 				break;
574810d5e89SGleb Smirnoff 			}
575ba20540eSGleb Smirnoff 			bzero(&sa_dl, sizeof(struct sockaddr_dl));
576810d5e89SGleb Smirnoff 			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
577810d5e89SGleb Smirnoff 			sa_dl.sdl_family = AF_LINK;
578ba20540eSGleb Smirnoff 			sa_dl.sdl_alen = ETHER_ADDR_LEN;
579810d5e89SGleb Smirnoff 			bcopy((void *)msg->data, LLADDR(&sa_dl),
580810d5e89SGleb Smirnoff 			    ETHER_ADDR_LEN);
581810d5e89SGleb Smirnoff 			error = if_delmulti(priv->ifp,
582810d5e89SGleb Smirnoff 			    (struct sockaddr *)&sa_dl);
583810d5e89SGleb Smirnoff 			break;
584810d5e89SGleb Smirnoff 		    }
585cefddd66SGleb Smirnoff 		case NGM_ETHER_DETACH:
586cefddd66SGleb Smirnoff 			ng_ether_detach(priv->ifp);
587cefddd66SGleb Smirnoff 			break;
588e1e1452dSArchie Cobbs 		default:
589e1e1452dSArchie Cobbs 			error = EINVAL;
590e1e1452dSArchie Cobbs 			break;
591e1e1452dSArchie Cobbs 		}
592e1e1452dSArchie Cobbs 		break;
593e1e1452dSArchie Cobbs 	default:
594e1e1452dSArchie Cobbs 		error = EINVAL;
595e1e1452dSArchie Cobbs 		break;
596e1e1452dSArchie Cobbs 	}
597069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
598069154d5SJulian Elischer 	NG_FREE_MSG(msg);
599e1e1452dSArchie Cobbs 	return (error);
600e1e1452dSArchie Cobbs }
601e1e1452dSArchie Cobbs 
602e1e1452dSArchie Cobbs /*
603e1e1452dSArchie Cobbs  * Receive data on a hook.
604f664dcdeSJulian Elischer  * Since we use per-hook recveive methods this should never be called.
605e1e1452dSArchie Cobbs  */
606e1e1452dSArchie Cobbs static int
607069154d5SJulian Elischer ng_ether_rcvdata(hook_p hook, item_p item)
608e1e1452dSArchie Cobbs {
609069154d5SJulian Elischer 	NG_FREE_ITEM(item);
6103ca24c28SJulian Elischer 
6116e551fb6SDavid E. O'Brien 	panic("%s: weird hook", __func__);
6121a292b80SArchie Cobbs #ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */
6137ea5573cSDag-Erling Smørgrav 	return (0);
614b40ce416SJulian Elischer #endif
615e1e1452dSArchie Cobbs }
616e1e1452dSArchie Cobbs 
617e1e1452dSArchie Cobbs /*
6181a292b80SArchie Cobbs  * Handle an mbuf received on the "lower" or "orphan" hook.
619e1e1452dSArchie Cobbs  */
620e1e1452dSArchie Cobbs static int
621f664dcdeSJulian Elischer ng_ether_rcv_lower(hook_p hook, item_p item)
622e1e1452dSArchie Cobbs {
623f664dcdeSJulian Elischer 	struct mbuf *m;
624f664dcdeSJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
62530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
626a1479aa2SArchie Cobbs  	struct ifnet *const ifp = priv->ifp;
627a1479aa2SArchie Cobbs 
628f664dcdeSJulian Elischer 	NGI_GET_M(item, m);
629f664dcdeSJulian Elischer 	NG_FREE_ITEM(item);
630f664dcdeSJulian Elischer 
631a1479aa2SArchie Cobbs 	/* Check whether interface is ready for packets */
632f664dcdeSJulian Elischer 
63313f4c340SRobert Watson 	if (!((ifp->if_flags & IFF_UP) &&
63413f4c340SRobert Watson 	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
635a1479aa2SArchie Cobbs 		NG_FREE_M(m);
636a1479aa2SArchie Cobbs 		return (ENETDOWN);
637a1479aa2SArchie Cobbs 	}
638e1e1452dSArchie Cobbs 
639e1e1452dSArchie Cobbs 	/* Make sure header is fully pulled up */
640e1e1452dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
641069154d5SJulian Elischer 		NG_FREE_M(m);
642e1e1452dSArchie Cobbs 		return (EINVAL);
643e1e1452dSArchie Cobbs 	}
644e1e1452dSArchie Cobbs 	if (m->m_len < sizeof(struct ether_header)
6457b9f235fSArchie Cobbs 	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
646e1e1452dSArchie Cobbs 		return (ENOBUFS);
647e1e1452dSArchie Cobbs 
6484b39c3c7SArchie Cobbs 	/* Drop in the MAC address if desired */
6494b39c3c7SArchie Cobbs 	if (priv->autoSrcAddr) {
6507b9f235fSArchie Cobbs 
6517b9f235fSArchie Cobbs 		/* Make the mbuf writable if it's not already */
6527b9f235fSArchie Cobbs 		if (!M_WRITABLE(m)
6537b9f235fSArchie Cobbs 		    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
6547b9f235fSArchie Cobbs 			return (ENOBUFS);
6557b9f235fSArchie Cobbs 
6567b9f235fSArchie Cobbs 		/* Overwrite source MAC address */
6574a0d6638SRuslan Ermilov 		bcopy(IF_LLADDR(ifp),
6584b39c3c7SArchie Cobbs 		    mtod(m, struct ether_header *)->ether_shost,
6594b39c3c7SArchie Cobbs 		    ETHER_ADDR_LEN);
6604b39c3c7SArchie Cobbs 	}
661561e4fb9SJulian Elischer 
662e1e1452dSArchie Cobbs 	/* Send it on its way */
663a1479aa2SArchie Cobbs 	return ether_output_frame(ifp, m);
664e1e1452dSArchie Cobbs }
665e1e1452dSArchie Cobbs 
666e1e1452dSArchie Cobbs /*
667e1e1452dSArchie Cobbs  * Handle an mbuf received on the "upper" hook.
668e1e1452dSArchie Cobbs  */
669e1e1452dSArchie Cobbs static int
670f664dcdeSJulian Elischer ng_ether_rcv_upper(hook_p hook, item_p item)
671e1e1452dSArchie Cobbs {
672f664dcdeSJulian Elischer 	struct mbuf *m;
673f664dcdeSJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
67430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
6756512768bSGleb Smirnoff 	struct ifnet *ifp = priv->ifp;
676e1e1452dSArchie Cobbs 
677f664dcdeSJulian Elischer 	NGI_GET_M(item, m);
678f664dcdeSJulian Elischer 	NG_FREE_ITEM(item);
679f664dcdeSJulian Elischer 
680c60c00bcSRuslan Ermilov 	/* Check length and pull off header */
681c60c00bcSRuslan Ermilov 	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
682c60c00bcSRuslan Ermilov 		NG_FREE_M(m);
683c60c00bcSRuslan Ermilov 		return (EINVAL);
684c60c00bcSRuslan Ermilov 	}
685c60c00bcSRuslan Ermilov 	if (m->m_len < sizeof(struct ether_header) &&
686c60c00bcSRuslan Ermilov 	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
687c60c00bcSRuslan Ermilov 		return (ENOBUFS);
688c60c00bcSRuslan Ermilov 
6896512768bSGleb Smirnoff 	m->m_pkthdr.rcvif = ifp;
690e1e1452dSArchie Cobbs 
691fd6238a6SAndrew Thompson 	/* Pass the packet to the bridge, it may come back to us */
6926512768bSGleb Smirnoff 	if (ifp->if_bridge) {
693fd6238a6SAndrew Thompson 		BRIDGE_INPUT(ifp, m);
6946512768bSGleb Smirnoff 		if (m == NULL)
6956512768bSGleb Smirnoff 			return (0);
6966512768bSGleb Smirnoff 	}
697a176c2aeSGleb Smirnoff 
698e1e1452dSArchie Cobbs 	/* Route packet back in */
699fd6238a6SAndrew Thompson 	ether_demux(ifp, m);
700e1e1452dSArchie Cobbs 	return (0);
701e1e1452dSArchie Cobbs }
702e1e1452dSArchie Cobbs 
703e1e1452dSArchie Cobbs /*
7041acb27c6SJulian Elischer  * Shutdown node. This resets the node but does not remove it
7051acb27c6SJulian Elischer  * unless the REALLY_DIE flag is set.
706e1e1452dSArchie Cobbs  */
707e1e1452dSArchie Cobbs static int
708069154d5SJulian Elischer ng_ether_shutdown(node_p node)
709e1e1452dSArchie Cobbs {
71030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
7114b39c3c7SArchie Cobbs 
712be4252b3SJulian Elischer 	if (node->nd_flags & NGF_REALLY_DIE) {
7131acb27c6SJulian Elischer 		/*
7141acb27c6SJulian Elischer 		 * WE came here because the ethernet card is being unloaded,
7151acb27c6SJulian Elischer 		 * so stop being persistant.
7161acb27c6SJulian Elischer 		 * Actually undo all the things we did on creation.
7171acb27c6SJulian Elischer 		 * Assume the ifp has already been freed.
7181acb27c6SJulian Elischer 		 */
7191acb27c6SJulian Elischer 		NG_NODE_SET_PRIVATE(node, NULL);
7201ede983cSDag-Erling Smørgrav 		free(priv, M_NETGRAPH);
7211acb27c6SJulian Elischer 		NG_NODE_UNREF(node);	/* free node itself */
7221acb27c6SJulian Elischer 		return (0);
723069154d5SJulian Elischer 	}
724018df1c3SBrian Feldman 	if (priv->promisc) {		/* disable promiscuous mode */
725018df1c3SBrian Feldman 		(void)ifpromisc(priv->ifp, 0);
726018df1c3SBrian Feldman 		priv->promisc = 0;
727018df1c3SBrian Feldman 	}
7284b39c3c7SArchie Cobbs 	priv->autoSrcAddr = 1;		/* reset auto-src-addr flag */
729be4252b3SJulian Elischer 	NG_NODE_REVIVE(node);		/* Signal ng_rmnode we are persisant */
730be4252b3SJulian Elischer 
731e1e1452dSArchie Cobbs 	return (0);
732e1e1452dSArchie Cobbs }
733e1e1452dSArchie Cobbs 
734e1e1452dSArchie Cobbs /*
735e1e1452dSArchie Cobbs  * Hook disconnection.
736e1e1452dSArchie Cobbs  */
737e1e1452dSArchie Cobbs static int
738e1e1452dSArchie Cobbs ng_ether_disconnect(hook_p hook)
739e1e1452dSArchie Cobbs {
74030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
741e1e1452dSArchie Cobbs 
742a3e232d6SArchie Cobbs 	if (hook == priv->upper) {
743e1e1452dSArchie Cobbs 		priv->upper = NULL;
744b712e9ecSBrian Feldman 		if (priv->ifp != NULL)		/* restore h/w csum */
745b712e9ecSBrian Feldman 			priv->ifp->if_hwassist = priv->hwassist;
7461a292b80SArchie Cobbs 	} else if (hook == priv->lower)
747e1e1452dSArchie Cobbs 		priv->lower = NULL;
7481a292b80SArchie Cobbs 	else if (hook == priv->orphan)
7491a292b80SArchie Cobbs 		priv->orphan = NULL;
7501a292b80SArchie Cobbs 	else
7516e551fb6SDavid E. O'Brien 		panic("%s: weird hook", __func__);
75230400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
75330400f03SJulian Elischer 	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
75430400f03SJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));	/* reset node */
755e1e1452dSArchie Cobbs 	return (0);
756e1e1452dSArchie Cobbs }
757e1e1452dSArchie Cobbs 
758e1e1452dSArchie Cobbs /******************************************************************
759e1e1452dSArchie Cobbs 		    	INITIALIZATION
760e1e1452dSArchie Cobbs ******************************************************************/
761e1e1452dSArchie Cobbs 
762e1e1452dSArchie Cobbs /*
763e1e1452dSArchie Cobbs  * Handle loading and unloading for this node type.
764e1e1452dSArchie Cobbs  */
765e1e1452dSArchie Cobbs static int
766e1e1452dSArchie Cobbs ng_ether_mod_event(module_t mod, int event, void *data)
767e1e1452dSArchie Cobbs {
768e1e1452dSArchie Cobbs 	int error = 0;
769e1e1452dSArchie Cobbs 	int s;
770e1e1452dSArchie Cobbs 
771e1e1452dSArchie Cobbs 	s = splnet();
772e1e1452dSArchie Cobbs 	switch (event) {
773e1e1452dSArchie Cobbs 	case MOD_LOAD:
774e1e1452dSArchie Cobbs 
775e1e1452dSArchie Cobbs 		/* Register function hooks */
776e1e1452dSArchie Cobbs 		if (ng_ether_attach_p != NULL) {
777e1e1452dSArchie Cobbs 			error = EEXIST;
778e1e1452dSArchie Cobbs 			break;
779e1e1452dSArchie Cobbs 		}
780e1e1452dSArchie Cobbs 		ng_ether_attach_p = ng_ether_attach;
781e1e1452dSArchie Cobbs 		ng_ether_detach_p = ng_ether_detach;
782e1e1452dSArchie Cobbs 		ng_ether_output_p = ng_ether_output;
783e1e1452dSArchie Cobbs 		ng_ether_input_p = ng_ether_input;
784e1e1452dSArchie Cobbs 		ng_ether_input_orphan_p = ng_ether_input_orphan;
7851c7899c7SGleb Smirnoff 		ng_ether_link_state_p = ng_ether_link_state;
786e1e1452dSArchie Cobbs 
787aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS
788aef8f344SMarko Zec 		vnet_mod_register(&vnet_ng_ether_modinfo);
789aef8f344SMarko Zec #else
790aef8f344SMarko Zec 		error = ng_ether_iattach(NULL);
791aef8f344SMarko Zec #endif
792e1e1452dSArchie Cobbs 		break;
793e1e1452dSArchie Cobbs 
794e1e1452dSArchie Cobbs 	case MOD_UNLOAD:
795e1e1452dSArchie Cobbs 
796e1e1452dSArchie Cobbs 		/*
797e1e1452dSArchie Cobbs 		 * Note that the base code won't try to unload us until
798e1e1452dSArchie Cobbs 		 * all nodes have been removed, and that can't happen
799e1e1452dSArchie Cobbs 		 * until all Ethernet interfaces are removed. In any
800e1e1452dSArchie Cobbs 		 * case, we know there are no nodes left if the action
801e1e1452dSArchie Cobbs 		 * is MOD_UNLOAD, so there's no need to detach any nodes.
802e1e1452dSArchie Cobbs 		 */
803e1e1452dSArchie Cobbs 
804aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS
805aef8f344SMarko Zec 		vnet_mod_deregister(&vnet_ng_ether_modinfo);
806aef8f344SMarko Zec #endif
807aef8f344SMarko Zec 
808e1e1452dSArchie Cobbs 		/* Unregister function hooks */
809e1e1452dSArchie Cobbs 		ng_ether_attach_p = NULL;
810e1e1452dSArchie Cobbs 		ng_ether_detach_p = NULL;
811e1e1452dSArchie Cobbs 		ng_ether_output_p = NULL;
812e1e1452dSArchie Cobbs 		ng_ether_input_p = NULL;
813e1e1452dSArchie Cobbs 		ng_ether_input_orphan_p = NULL;
8141c7899c7SGleb Smirnoff 		ng_ether_link_state_p = NULL;
815e1e1452dSArchie Cobbs 		break;
816e1e1452dSArchie Cobbs 
817e1e1452dSArchie Cobbs 	default:
818e1e1452dSArchie Cobbs 		error = EOPNOTSUPP;
819e1e1452dSArchie Cobbs 		break;
820e1e1452dSArchie Cobbs 	}
821e1e1452dSArchie Cobbs 	splx(s);
822e1e1452dSArchie Cobbs 	return (error);
823e1e1452dSArchie Cobbs }
824e1e1452dSArchie Cobbs 
825aef8f344SMarko Zec static int ng_ether_iattach(const void *unused)
826aef8f344SMarko Zec {
827aef8f344SMarko Zec 	INIT_VNET_NET(curvnet);
828aef8f344SMarko Zec 	struct ifnet *ifp;
829aef8f344SMarko Zec 
830aef8f344SMarko Zec 	/* Create nodes for any already-existing Ethernet interfaces. */
831aef8f344SMarko Zec 	IFNET_RLOCK();
832aef8f344SMarko Zec 	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
833aef8f344SMarko Zec 		if (ifp->if_type == IFT_ETHER
834aef8f344SMarko Zec 		    || ifp->if_type == IFT_L2VLAN)
835aef8f344SMarko Zec 			ng_ether_attach(ifp);
836aef8f344SMarko Zec 	}
837aef8f344SMarko Zec 	IFNET_RUNLOCK();
838aef8f344SMarko Zec 
839aef8f344SMarko Zec 	return (0);
840aef8f344SMarko Zec }
841