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