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: stable/11/sys/netgraph/ng_atmllc.c 346817 2019-04-28 13:21:01Z dchagin $
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));
156227132Sfjoe	NGI_GET_M(item, m);
157126742Sbenno	outhook = NULL;
158126742Sbenno	padding = 0;
159126742Sbenno
160126742Sbenno	if (hook == priv->atm) {
161346817Sdchagin		/* Ditch the pseudoheader. */
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) {
173227132Sfjoe				NG_FREE_ITEM(item);
174126742Sbenno				return (ENOMEM);
175126742Sbenno			}
176126742Sbenno		}
177126742Sbenno
178126742Sbenno		/* Decode the LLC header. */
179126742Sbenno		hdr = mtod(m, struct atmllc *);
180126742Sbenno		if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_NOFCS) {
181126742Sbenno			m->m_flags &= ~M_HASFCS;
182126742Sbenno			outhook = priv->ether;
183126742Sbenno			padding = 2;
184126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_FCS) {
185126742Sbenno			m->m_flags |= M_HASFCS;
186126742Sbenno			outhook = priv->ether;
187126742Sbenno			padding = 2;
188126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_NOFCS) {
189126742Sbenno			m->m_flags &= ~M_HASFCS;
190126742Sbenno			outhook = priv->fddi;
191126742Sbenno			padding = 3;
192126742Sbenno		} else if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_FDDI_FCS) {
193126742Sbenno			m->m_flags |= M_HASFCS;
194126742Sbenno			outhook = priv->fddi;
195126742Sbenno			padding = 3;
196126742Sbenno		} else {
197126742Sbenno			printf("ng_atmllc: unknown type: %x\n",
198126742Sbenno			    ATM_LLC_TYPE(hdr));
199126742Sbenno		}
200126742Sbenno
201126742Sbenno		/* Remove the LLC header and any padding*/
202126742Sbenno		m_adj(m, sizeof(struct atmllc) + padding);
203126742Sbenno	} else if (hook == priv->ether) {
204126742Sbenno		/* Add the LLC header */
205243882Sglebius		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 2, M_NOWAIT);
206126742Sbenno		if (m == NULL) {
207126742Sbenno			printf("ng_atmllc: M_PREPEND failed\n");
208126742Sbenno			NG_FREE_ITEM(item);
209126742Sbenno			return (ENOMEM);
210126742Sbenno		}
211126742Sbenno		hdr = mtod(m, struct atmllc *);
212126742Sbenno		bzero((void *)hdr, sizeof(struct atmllc) + 2);
213126742Sbenno		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
214126742Sbenno		if ((m->m_flags & M_HASFCS) != 0) {
215126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_FCS);
216126742Sbenno		} else {
217126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_ETHERNET_NOFCS);
218126742Sbenno		}
219126742Sbenno		outhook = priv->atm;
220126742Sbenno	} else if (hook == priv->fddi) {
221126742Sbenno		/* Add the LLC header */
222243882Sglebius		M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 3, M_NOWAIT);
223126742Sbenno		if (m == NULL) {
224126742Sbenno			printf("ng_atmllc: M_PREPEND failed\n");
225126742Sbenno			NG_FREE_ITEM(item);
226126742Sbenno			return (ENOMEM);
227126742Sbenno		}
228126742Sbenno		hdr = mtod(m, struct atmllc *);
229126742Sbenno		bzero((void *)hdr, sizeof(struct atmllc) + 3);
230126742Sbenno		bcopy(NG_ATMLLC_HEADER, hdr->llchdr, 6);
231126742Sbenno		if ((m->m_flags & M_HASFCS) != 0) {
232126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_FCS);
233126742Sbenno		} else {
234126742Sbenno			ATM_LLC_SETTYPE(hdr, NG_ATMLLC_TYPE_FDDI_NOFCS);
235126742Sbenno		}
236126742Sbenno		outhook = priv->atm;
237126742Sbenno	}
238126742Sbenno
239126742Sbenno	if (outhook == NULL) {
240227132Sfjoe		NG_FREE_M(m);
241126742Sbenno		NG_FREE_ITEM(item);
242126742Sbenno		return (0);
243126742Sbenno	}
244126742Sbenno
245126742Sbenno	NG_FWD_NEW_DATA(error, item, outhook, m);
246126742Sbenno	return (error);
247126742Sbenno}
248126742Sbenno
249126742Sbennostatic int
250126742Sbennong_atmllc_disconnect(hook_p hook)
251126742Sbenno{
252126742Sbenno	node_p	node;
253126742Sbenno	struct	ng_atmllc_priv *priv;
254126742Sbenno
255126742Sbenno	node = NG_HOOK_NODE(hook);
256126742Sbenno	priv = NG_NODE_PRIVATE(node);
257126742Sbenno
258126742Sbenno	if (hook == priv->atm) {
259126742Sbenno		priv->atm = NULL;
260126742Sbenno	} else if (hook == priv->ether) {
261126742Sbenno		priv->ether = NULL;
262126742Sbenno	} else if (hook == priv->fddi) {
263126742Sbenno		priv->fddi = NULL;
264126742Sbenno	}
265126742Sbenno
266126742Sbenno	if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) {
267126742Sbenno		ng_rmnode_self(node);
268126742Sbenno	}
269126742Sbenno
270126742Sbenno	return (0);
271126742Sbenno}
272