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