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/10/sys/netgraph/ng_atmllc.c 243882 2012-12-05 08:04:20Z glebius $
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 
52 struct ng_atmllc_priv {
53 	hook_p		atm;
54 	hook_p		ether;
55 	hook_p		fddi;
56 };
57 
58 /* Netgraph methods. */
59 static ng_constructor_t		ng_atmllc_constructor;
60 static ng_shutdown_t		ng_atmllc_shutdown;
61 static ng_rcvmsg_t		ng_atmllc_rcvmsg;
62 static ng_newhook_t		ng_atmllc_newhook;
63 static ng_rcvdata_t		ng_atmllc_rcvdata;
64 static ng_disconnect_t		ng_atmllc_disconnect;
65 
66 static 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 };
76 NETGRAPH_INIT(atmllc, &ng_atmllc_typestruct);
77 
78 static int
ng_atmllc_constructor(node_p node)79 ng_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 
89 static int
ng_atmllc_rcvmsg(node_p node,item_p item,hook_p lasthook)90 ng_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 
102 static int
ng_atmllc_shutdown(node_p node)103 ng_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 
116 static int
ng_atmllc_newhook(node_p node,hook_p hook,const char * name)117 ng_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 
145 static int
ng_atmllc_rcvdata(hook_p hook,item_p item)146 ng_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 psuedoheader. */
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 
249 static int
ng_atmllc_disconnect(hook_p hook)250 ng_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