1139823Simp/*-
2126742Sbenno * Copyright (c) 2003-2004 Benno Rice <benno@eloquent.com.au>
3126742Sbenno * All Rights Reserved.
4126742Sbenno *
5126742Sbenno * Redistribution and use in source and binary forms, with or without
6126742Sbenno * modification, are permitted provided that the following conditions
7126742Sbenno * are met:
8126742Sbenno * 1. Redistributions of source code must retain the above copyright
9126742Sbenno *    notice, this list of conditions and the following disclaimer.
10126742Sbenno * 2. Redistributions in binary form must reproduce the above copyright
11126742Sbenno *    notice, this list of conditions and the following disclaimer in the
12126742Sbenno *    documentation and/or other materials provided with the distribution.
13126742Sbenno *
14126742Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR  ``AS IS'' AND
15126742Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16126742Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17126742Sbenno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR  BE LIABLE
18126742Sbenno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19126742Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20126742Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21126742Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22126742Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23126742Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24126742Sbenno * SUCH DAMAGE.
25126742Sbenno *
26126742Sbenno * $FreeBSD$
27126742Sbenno */
28126742Sbenno
29126742Sbenno#include <sys/param.h>
30126742Sbenno#include <sys/systm.h>
31126742Sbenno#include <sys/kernel.h>
32126742Sbenno#include <sys/malloc.h>
33126742Sbenno#include <sys/mbuf.h>
34126742Sbenno#include <sys/socket.h>
35126742Sbenno#include <sys/sockio.h>
36126742Sbenno
37126742Sbenno#include <netgraph/ng_message.h>
38126742Sbenno#include <netgraph/netgraph.h>
39126742Sbenno#include <netgraph/ng_atmllc.h>
40126742Sbenno
41126742Sbenno#include <net/if.h>
42126742Sbenno#include <net/ethernet.h>	/* for M_HASFCS and ETHER_HDR_LEN */
43126742Sbenno#include <net/if_atm.h>		/* for struct atmllc */
44126742Sbenno
45126742Sbenno#define	NG_ATMLLC_HEADER		"\252\252\3\0\200\302"
46126742Sbenno#define	NG_ATMLLC_HEADER_LEN		(sizeof(struct atmllc))
47126742Sbenno#define	NG_ATMLLC_TYPE_ETHERNET_FCS	0x0001
48126742Sbenno#define	NG_ATMLLC_TYPE_FDDI_FCS		0x0004
49126742Sbenno#define	NG_ATMLLC_TYPE_ETHERNET_NOFCS	0x0007
50126742Sbenno#define	NG_ATMLLC_TYPE_FDDI_NOFCS	0x000A
51126742Sbenno
52126742Sbennostruct ng_atmllc_priv {
53126742Sbenno	hook_p		atm;
54126742Sbenno	hook_p		ether;
55126742Sbenno	hook_p		fddi;
56126742Sbenno};
57126742Sbenno
58126742Sbenno/* Netgraph methods. */
59126742Sbennostatic ng_constructor_t		ng_atmllc_constructor;
60126742Sbennostatic ng_shutdown_t		ng_atmllc_shutdown;
61126742Sbennostatic ng_rcvmsg_t		ng_atmllc_rcvmsg;
62126742Sbennostatic ng_newhook_t		ng_atmllc_newhook;
63126742Sbennostatic ng_rcvdata_t		ng_atmllc_rcvdata;
64126742Sbennostatic ng_disconnect_t		ng_atmllc_disconnect;
65126742Sbenno
66126742Sbennostatic struct ng_type ng_atmllc_typestruct = {
67129823Sjulian	.version =	NG_ABI_VERSION,
68129823Sjulian	.name =		NG_ATMLLC_NODE_TYPE,
69129823Sjulian	.constructor =	ng_atmllc_constructor,
70129823Sjulian	.rcvmsg =	ng_atmllc_rcvmsg,
71129823Sjulian	.shutdown =	ng_atmllc_shutdown,
72129823Sjulian	.newhook =	ng_atmllc_newhook,
73129823Sjulian	.rcvdata =	ng_atmllc_rcvdata,
74129823Sjulian	.disconnect =	ng_atmllc_disconnect,
75126742Sbenno};
76126742SbennoNETGRAPH_INIT(atmllc, &ng_atmllc_typestruct);
77126742Sbenno
78126742Sbennostatic int
79126742Sbennong_atmllc_constructor(node_p node)
80126742Sbenno{
81126742Sbenno	struct	ng_atmllc_priv *priv;
82126742Sbenno
83220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
84126742Sbenno	NG_NODE_SET_PRIVATE(node, priv);
85126742Sbenno
86126742Sbenno	return (0);
87126742Sbenno}
88126742Sbenno
89126742Sbennostatic int
90126742Sbennong_atmllc_rcvmsg(node_p node, item_p item, hook_p lasthook)
91126742Sbenno{
92126742Sbenno	struct	ng_mesg *msg;
93126742Sbenno	int	error;
94126742Sbenno
95126742Sbenno	error = 0;
96126742Sbenno	NGI_GET_MSG(item, msg);
97126742Sbenno	msg->header.flags |= NGF_RESP;
98126742Sbenno	NG_RESPOND_MSG(error, node, item, msg);
99126742Sbenno	return (error);
100126742Sbenno}
101126742Sbenno
102126742Sbennostatic int
103126742Sbennong_atmllc_shutdown(node_p node)
104126742Sbenno{
105126742Sbenno	struct	ng_atmllc_priv *priv;
106126742Sbenno
107126742Sbenno	priv = NG_NODE_PRIVATE(node);
108126742Sbenno
109184205Sdes	free(priv, M_NETGRAPH);
110126742Sbenno
111126742Sbenno	NG_NODE_UNREF(node);
112126742Sbenno
113126742Sbenno	return (0);
114126742Sbenno}
115126742Sbenno
116126742Sbennostatic int
117126742Sbennong_atmllc_newhook(node_p node, hook_p hook, const char *name)
118126742Sbenno{
119126742Sbenno	struct	ng_atmllc_priv *priv;
120126742Sbenno
121126742Sbenno	priv = NG_NODE_PRIVATE(node);
122126742Sbenno
123126742Sbenno	if (strcmp(name, NG_ATMLLC_HOOK_ATM) == 0) {
124126742Sbenno		if (priv->atm != NULL) {
125126742Sbenno			return (EISCONN);
126126742Sbenno		}
127126742Sbenno		priv->atm = hook;
128126742Sbenno	} else if (strcmp(name, NG_ATMLLC_HOOK_ETHER) == 0) {
129126742Sbenno		if (priv->ether != NULL) {
130126742Sbenno			return (EISCONN);
131126742Sbenno		}
132126742Sbenno		priv->ether = hook;
133126742Sbenno	} else if (strcmp(name, NG_ATMLLC_HOOK_FDDI) == 0) {
134126742Sbenno		if (priv->fddi != NULL) {
135126742Sbenno			return (EISCONN);
136126742Sbenno		}
137126742Sbenno		priv->fddi = hook;
138126742Sbenno	} else {
139126742Sbenno		return (EINVAL);
140126742Sbenno	}
141126742Sbenno
142126742Sbenno	return (0);
143126742Sbenno}
144126742Sbenno
145126742Sbennostatic int
146126742Sbennong_atmllc_rcvdata(hook_p hook, item_p item)
147126742Sbenno{
148126742Sbenno	struct	ng_atmllc_priv *priv;
149126742Sbenno	struct	mbuf *m;
150126742Sbenno	struct	atmllc *hdr;
151126742Sbenno	hook_p	outhook;
152126742Sbenno	u_int	padding;
153126742Sbenno	int	error;
154126742Sbenno
155126742Sbenno	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
156126742Sbenno	m = NGI_M(item);
157126742Sbenno	outhook = NULL;
158126742Sbenno	padding = 0;
159126742Sbenno
160126742Sbenno	if (hook == priv->atm) {
161126742Sbenno		/* Ditch the psuedoheader. */
162126742Sbenno		hdr = mtod(m, struct atmllc *);
163126742Sbenno		/* m_adj(m, sizeof(struct atm_pseudohdr)); */
164126742Sbenno
165126742Sbenno		/*
166126742Sbenno		 * Make sure we have the LLC and ethernet headers.
167126742Sbenno		 * The ethernet header size is slightly larger than the FDDI
168126742Sbenno		 * header, which is convenient.
169126742Sbenno		 */
170126742Sbenno		if (m->m_len < sizeof(struct atmllc) + ETHER_HDR_LEN) {
171126742Sbenno			m = m_pullup(m, sizeof(struct atmllc) + ETHER_HDR_LEN);
172126742Sbenno			if (m == NULL) {
173126742Sbenno				return (ENOMEM);
174126742Sbenno			}
175126742Sbenno		}
176126742Sbenno
177126742Sbenno		/* Decode the LLC header. */
178126742Sbenno		hdr = mtod(m, struct atmllc *);
179126742Sbenno		if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_NOFCS) {
180126742Sbenno			m->m_flags &= ~M_HASFCS;
181126742Sbenno			outhook = priv->ether;
182126742Sbenno			padding = 2;
183126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_FCS) {
184126742Sbenno			m->m_flags |= M_HASFCS;
185126742Sbenno			outhook = priv->ether;
186126742Sbenno			padding = 2;
187126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_NOFCS) {
188126742Sbenno			m->m_flags &= ~M_HASFCS;
189126742Sbenno			outhook = priv->fddi;
190126742Sbenno			padding = 3;
191126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_FCS) {
192126742Sbenno			m->m_flags |= M_HASFCS;
193126742Sbenno			outhook = priv->fddi;
194126742Sbenno			padding = 3;
195126742Sbenno		} else {
196126742Sbenno			printf("ng_atmllc: unknown type: %x\n",
197126742Sbenno			    ATM_LLC_TYPE(hdr));
198126742Sbenno		}
199126742Sbenno
200126742Sbenno		/* Remove the LLC header and any padding*/
201126742Sbenno		m_adj(m, sizeof(struct atmllc) + padding);
202126742Sbenno	} else if (hook == priv->ether) {
203126742Sbenno		/* Add the LLC header */
204126742Sbenno		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 2, M_DONTWAIT);
205126742Sbenno		if (m == NULL) {
206126742Sbenno			printf("ng_atmllc: M_PREPEND failed\n");
207126742Sbenno			NG_FREE_ITEM(item);
208126742Sbenno			return (ENOMEM);
209126742Sbenno		}
210126742Sbenno		hdr = mtod(m, struct atmllc *);
211126742Sbenno		bzero((void *)hdr, sizeof(struct atmllc) + 2);
212126742Sbenno		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
213126742Sbenno		if ((m->m_flags & M_HASFCS) != 0) {
214126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_FCS);
215126742Sbenno		} else {
216126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_NOFCS);
217126742Sbenno		}
218126742Sbenno		outhook = priv->atm;
219126742Sbenno	} else if (hook == priv->fddi) {
220126742Sbenno		/* Add the LLC header */
221126742Sbenno		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 3, M_DONTWAIT);
222126742Sbenno		if (m == NULL) {
223126742Sbenno			printf("ng_atmllc: M_PREPEND failed\n");
224126742Sbenno			NG_FREE_ITEM(item);
225126742Sbenno			return (ENOMEM);
226126742Sbenno		}
227126742Sbenno		hdr = mtod(m, struct atmllc *);
228126742Sbenno		bzero((void *)hdr, sizeof(struct atmllc) + 3);
229126742Sbenno		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
230126742Sbenno		if ((m->m_flags & M_HASFCS) != 0) {
231126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_FCS);
232126742Sbenno		} else {
233126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_NOFCS);
234126742Sbenno		}
235126742Sbenno		outhook = priv->atm;
236126742Sbenno	}
237126742Sbenno
238126742Sbenno	if (outhook == NULL) {
239126742Sbenno		NG_FREE_ITEM(item);
240126742Sbenno		return (0);
241126742Sbenno	}
242126742Sbenno
243126742Sbenno	NG_FWD_NEW_DATA(error, item, outhook, m);
244126742Sbenno	return (error);
245126742Sbenno}
246126742Sbenno
247126742Sbennostatic int
248126742Sbennong_atmllc_disconnect(hook_p hook)
249126742Sbenno{
250126742Sbenno	node_p	node;
251126742Sbenno	struct	ng_atmllc_priv *priv;
252126742Sbenno
253126742Sbenno	node = NG_HOOK_NODE(hook);
254126742Sbenno	priv = NG_NODE_PRIVATE(node);
255126742Sbenno
256126742Sbenno	if (hook == priv->atm) {
257126742Sbenno		priv->atm = NULL;
258126742Sbenno	} else if (hook == priv->ether) {
259126742Sbenno		priv->ether = NULL;
260126742Sbenno	} else if (hook == priv->fddi) {
261126742Sbenno		priv->fddi = NULL;
262126742Sbenno	}
263126742Sbenno
264126742Sbenno	if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) {
265126742Sbenno		ng_rmnode_self(node);
266126742Sbenno	}
267126742Sbenno
268126742Sbenno	return (0);
269126742Sbenno}
270