ng_atmllc.c revision 126742
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 126742 2004-03-08 10:54:35Z benno $
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	NG_ABI_VERSION,
68	NG_ATMLLC_NODE_TYPE,
69	NULL,
70	ng_atmllc_constructor,
71	ng_atmllc_rcvmsg,
72	ng_atmllc_shutdown,
73	ng_atmllc_newhook,
74	NULL,
75	NULL,
76	ng_atmllc_rcvdata,
77	ng_atmllc_disconnect,
78	NULL
79};
80NETGRAPH_INIT(atmllc, &ng_atmllc_typestruct);
81
82static int
83ng_atmllc_constructor(node_p node)
84{
85	struct	ng_atmllc_priv *priv;
86
87	MALLOC(priv, struct ng_atmllc_priv *, sizeof(*priv), M_NETGRAPH,
88	    M_NOWAIT | M_ZERO);
89	if (priv == NULL) {
90		return (ENOMEM);
91	}
92
93	NG_NODE_SET_PRIVATE(node, priv);
94
95	return (0);
96}
97
98static int
99ng_atmllc_rcvmsg(node_p node, item_p item, hook_p lasthook)
100{
101	struct	ng_mesg *msg;
102	int	error;
103
104	error = 0;
105	NGI_GET_MSG(item, msg);
106	msg->header.flags |= NGF_RESP;
107	NG_RESPOND_MSG(error, node, item, msg);
108	return (error);
109}
110
111static int
112ng_atmllc_shutdown(node_p node)
113{
114	struct	ng_atmllc_priv *priv;
115
116	priv = NG_NODE_PRIVATE(node);
117
118	FREE(priv, M_NETGRAPH);
119
120	NG_NODE_UNREF(node);
121
122	return (0);
123}
124
125static int
126ng_atmllc_newhook(node_p node, hook_p hook, const char *name)
127{
128	struct	ng_atmllc_priv *priv;
129
130	priv = NG_NODE_PRIVATE(node);
131
132	if (strcmp(name, NG_ATMLLC_HOOK_ATM) == 0) {
133		if (priv->atm != NULL) {
134			return (EISCONN);
135		}
136		priv->atm = hook;
137	} else if (strcmp(name, NG_ATMLLC_HOOK_ETHER) == 0) {
138		if (priv->ether != NULL) {
139			return (EISCONN);
140		}
141		priv->ether = hook;
142	} else if (strcmp(name, NG_ATMLLC_HOOK_FDDI) == 0) {
143		if (priv->fddi != NULL) {
144			return (EISCONN);
145		}
146		priv->fddi = hook;
147	} else {
148		return (EINVAL);
149	}
150
151	return (0);
152}
153
154static int
155ng_atmllc_rcvdata(hook_p hook, item_p item)
156{
157	struct	ng_atmllc_priv *priv;
158	struct	mbuf *m;
159	struct	atmllc *hdr;
160	hook_p	outhook;
161	u_int	padding;
162	int	error;
163
164	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
165	m = NGI_M(item);
166	outhook = NULL;
167	padding = 0;
168
169	if (hook == priv->atm) {
170		/* Ditch the psuedoheader. */
171		hdr = mtod(m, struct atmllc *);
172		/* m_adj(m, sizeof(struct atm_pseudohdr)); */
173
174		/*
175		 * Make sure we have the LLC and ethernet headers.
176		 * The ethernet header size is slightly larger than the FDDI
177		 * header, which is convenient.
178		 */
179		if (m->m_len < sizeof(struct atmllc) + ETHER_HDR_LEN) {
180			m = m_pullup(m, sizeof(struct atmllc) + ETHER_HDR_LEN);
181			if (m == NULL) {
182				return (ENOMEM);
183			}
184		}
185
186		/* Decode the LLC header. */
187		hdr = mtod(m, struct atmllc *);
188		if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_NOFCS) {
189			m->m_flags &= ~M_HASFCS;
190			outhook = priv->ether;
191			padding = 2;
192		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_FCS) {
193			m->m_flags |= M_HASFCS;
194			outhook = priv->ether;
195			padding = 2;
196		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_NOFCS) {
197			m->m_flags &= ~M_HASFCS;
198			outhook = priv->fddi;
199			padding = 3;
200		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_FCS) {
201			m->m_flags |= M_HASFCS;
202			outhook = priv->fddi;
203			padding = 3;
204		} else {
205			printf("ng_atmllc: unknown type: %x\n",
206			    ATM_LLC_TYPE(hdr));
207		}
208
209		/* Remove the LLC header and any padding*/
210		m_adj(m, sizeof(struct atmllc) + padding);
211	} else if (hook == priv->ether) {
212		/* Add the LLC header */
213		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 2, M_DONTWAIT);
214		if (m == NULL) {
215			printf("ng_atmllc: M_PREPEND failed\n");
216			NG_FREE_ITEM(item);
217			return (ENOMEM);
218		}
219		hdr = mtod(m, struct atmllc *);
220		bzero((void *)hdr, sizeof(struct atmllc) + 2);
221		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
222		if ((m->m_flags & M_HASFCS) != 0) {
223			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_FCS);
224		} else {
225			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_NOFCS);
226		}
227		outhook = priv->atm;
228	} else if (hook == priv->fddi) {
229		/* Add the LLC header */
230		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 3, M_DONTWAIT);
231		if (m == NULL) {
232			printf("ng_atmllc: M_PREPEND failed\n");
233			NG_FREE_ITEM(item);
234			return (ENOMEM);
235		}
236		hdr = mtod(m, struct atmllc *);
237		bzero((void *)hdr, sizeof(struct atmllc) + 3);
238		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
239		if ((m->m_flags & M_HASFCS) != 0) {
240			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_FCS);
241		} else {
242			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_NOFCS);
243		}
244		outhook = priv->atm;
245	}
246
247	if (outhook == NULL) {
248		NG_FREE_ITEM(item);
249		return (0);
250	}
251
252	NG_FWD_NEW_DATA(error, item, outhook, m);
253	return (error);
254}
255
256static int
257ng_atmllc_disconnect(hook_p hook)
258{
259	node_p	node;
260	struct	ng_atmllc_priv *priv;
261
262	node = NG_HOOK_NODE(hook);
263	priv = NG_NODE_PRIVATE(node);
264
265	if (hook == priv->atm) {
266		priv->atm = NULL;
267	} else if (hook == priv->ether) {
268		priv->ether = NULL;
269	} else if (hook == priv->fddi) {
270		priv->fddi = NULL;
271	}
272
273	if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) {
274		ng_rmnode_self(node);
275	}
276
277	return (0);
278}
279