ng_atmllc.c revision 184205
1/*-
2 * Copyright (c) 2003-2004 Benno Rice <benno@eloquent.com.au>
3 * All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR  ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR  BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/netgraph/ng_atmllc.c 184205 2008-10-23 15:53:51Z des $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#include <sys/mbuf.h>
34#include <sys/socket.h>
35#include <sys/sockio.h>
36
37#include <netgraph/ng_message.h>
38#include <netgraph/netgraph.h>
39#include <netgraph/ng_atmllc.h>
40
41#include <net/if.h>
42#include <net/ethernet.h>	/* for M_HASFCS and ETHER_HDR_LEN */
43#include <net/if_atm.h>		/* for struct atmllc */
44
45#define	NG_ATMLLC_HEADER		"\252\252\3\0\200\302"
46#define	NG_ATMLLC_HEADER_LEN		(sizeof(struct atmllc))
47#define	NG_ATMLLC_TYPE_ETHERNET_FCS	0x0001
48#define	NG_ATMLLC_TYPE_FDDI_FCS		0x0004
49#define	NG_ATMLLC_TYPE_ETHERNET_NOFCS	0x0007
50#define	NG_ATMLLC_TYPE_FDDI_NOFCS	0x000A
51
52struct ng_atmllc_priv {
53	hook_p		atm;
54	hook_p		ether;
55	hook_p		fddi;
56};
57
58/* Netgraph methods. */
59static ng_constructor_t		ng_atmllc_constructor;
60static ng_shutdown_t		ng_atmllc_shutdown;
61static ng_rcvmsg_t		ng_atmllc_rcvmsg;
62static ng_newhook_t		ng_atmllc_newhook;
63static ng_rcvdata_t		ng_atmllc_rcvdata;
64static ng_disconnect_t		ng_atmllc_disconnect;
65
66static struct ng_type ng_atmllc_typestruct = {
67	.version =	NG_ABI_VERSION,
68	.name =		NG_ATMLLC_NODE_TYPE,
69	.constructor =	ng_atmllc_constructor,
70	.rcvmsg =	ng_atmllc_rcvmsg,
71	.shutdown =	ng_atmllc_shutdown,
72	.newhook =	ng_atmllc_newhook,
73	.rcvdata =	ng_atmllc_rcvdata,
74	.disconnect =	ng_atmllc_disconnect,
75};
76NETGRAPH_INIT(atmllc, &ng_atmllc_typestruct);
77
78static int
79ng_atmllc_constructor(node_p node)
80{
81	struct	ng_atmllc_priv *priv;
82
83	priv = malloc(sizeof(*priv), M_NETGRAPH,
84	    M_NOWAIT | M_ZERO);
85	if (priv == NULL) {
86		return (ENOMEM);
87	}
88
89	NG_NODE_SET_PRIVATE(node, priv);
90
91	return (0);
92}
93
94static int
95ng_atmllc_rcvmsg(node_p node, item_p item, hook_p lasthook)
96{
97	struct	ng_mesg *msg;
98	int	error;
99
100	error = 0;
101	NGI_GET_MSG(item, msg);
102	msg->header.flags |= NGF_RESP;
103	NG_RESPOND_MSG(error, node, item, msg);
104	return (error);
105}
106
107static int
108ng_atmllc_shutdown(node_p node)
109{
110	struct	ng_atmllc_priv *priv;
111
112	priv = NG_NODE_PRIVATE(node);
113
114	free(priv, M_NETGRAPH);
115
116	NG_NODE_UNREF(node);
117
118	return (0);
119}
120
121static int
122ng_atmllc_newhook(node_p node, hook_p hook, const char *name)
123{
124	struct	ng_atmllc_priv *priv;
125
126	priv = NG_NODE_PRIVATE(node);
127
128	if (strcmp(name, NG_ATMLLC_HOOK_ATM) == 0) {
129		if (priv->atm != NULL) {
130			return (EISCONN);
131		}
132		priv->atm = hook;
133	} else if (strcmp(name, NG_ATMLLC_HOOK_ETHER) == 0) {
134		if (priv->ether != NULL) {
135			return (EISCONN);
136		}
137		priv->ether = hook;
138	} else if (strcmp(name, NG_ATMLLC_HOOK_FDDI) == 0) {
139		if (priv->fddi != NULL) {
140			return (EISCONN);
141		}
142		priv->fddi = hook;
143	} else {
144		return (EINVAL);
145	}
146
147	return (0);
148}
149
150static int
151ng_atmllc_rcvdata(hook_p hook, item_p item)
152{
153	struct	ng_atmllc_priv *priv;
154	struct	mbuf *m;
155	struct	atmllc *hdr;
156	hook_p	outhook;
157	u_int	padding;
158	int	error;
159
160	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
161	m = NGI_M(item);
162	outhook = NULL;
163	padding = 0;
164
165	if (hook == priv->atm) {
166		/* Ditch the psuedoheader. */
167		hdr = mtod(m, struct atmllc *);
168		/* m_adj(m, sizeof(struct atm_pseudohdr)); */
169
170		/*
171		 * Make sure we have the LLC and ethernet headers.
172		 * The ethernet header size is slightly larger than the FDDI
173		 * header, which is convenient.
174		 */
175		if (m->m_len < sizeof(struct atmllc) + ETHER_HDR_LEN) {
176			m = m_pullup(m, sizeof(struct atmllc) + ETHER_HDR_LEN);
177			if (m == NULL) {
178				return (ENOMEM);
179			}
180		}
181
182		/* Decode the LLC header. */
183		hdr = mtod(m, struct atmllc *);
184		if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_NOFCS) {
185			m->m_flags &= ~M_HASFCS;
186			outhook = priv->ether;
187			padding = 2;
188		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_FCS) {
189			m->m_flags |= M_HASFCS;
190			outhook = priv->ether;
191			padding = 2;
192		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_NOFCS) {
193			m->m_flags &= ~M_HASFCS;
194			outhook = priv->fddi;
195			padding = 3;
196		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_FCS) {
197			m->m_flags |= M_HASFCS;
198			outhook = priv->fddi;
199			padding = 3;
200		} else {
201			printf("ng_atmllc: unknown type: %x\n",
202			    ATM_LLC_TYPE(hdr));
203		}
204
205		/* Remove the LLC header and any padding*/
206		m_adj(m, sizeof(struct atmllc) + padding);
207	} else if (hook == priv->ether) {
208		/* Add the LLC header */
209		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 2, M_DONTWAIT);
210		if (m == NULL) {
211			printf("ng_atmllc: M_PREPEND failed\n");
212			NG_FREE_ITEM(item);
213			return (ENOMEM);
214		}
215		hdr = mtod(m, struct atmllc *);
216		bzero((void *)hdr, sizeof(struct atmllc) + 2);
217		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
218		if ((m->m_flags & M_HASFCS) != 0) {
219			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_FCS);
220		} else {
221			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_NOFCS);
222		}
223		outhook = priv->atm;
224	} else if (hook == priv->fddi) {
225		/* Add the LLC header */
226		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 3, M_DONTWAIT);
227		if (m == NULL) {
228			printf("ng_atmllc: M_PREPEND failed\n");
229			NG_FREE_ITEM(item);
230			return (ENOMEM);
231		}
232		hdr = mtod(m, struct atmllc *);
233		bzero((void *)hdr, sizeof(struct atmllc) + 3);
234		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
235		if ((m->m_flags & M_HASFCS) != 0) {
236			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_FCS);
237		} else {
238			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_NOFCS);
239		}
240		outhook = priv->atm;
241	}
242
243	if (outhook == NULL) {
244		NG_FREE_ITEM(item);
245		return (0);
246	}
247
248	NG_FWD_NEW_DATA(error, item, outhook, m);
249	return (error);
250}
251
252static int
253ng_atmllc_disconnect(hook_p hook)
254{
255	node_p	node;
256	struct	ng_atmllc_priv *priv;
257
258	node = NG_HOOK_NODE(hook);
259	priv = NG_NODE_PRIVATE(node);
260
261	if (hook == priv->atm) {
262		priv->atm = NULL;
263	} else if (hook == priv->ether) {
264		priv->ether = NULL;
265	} else if (hook == priv->fddi) {
266		priv->fddi = NULL;
267	}
268
269	if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) {
270		ng_rmnode_self(node);
271	}
272
273	return (0);
274}
275