xref: /freebsd/sys/netgraph/ng_ppp.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
1 
2 /*
3  * ng_ppp.c
4  *
5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6  * All rights reserved.
7  *
8  * Subject to the following obligations and disclaimer of warranty, use and
9  * redistribution of this software, in source or object code forms, with or
10  * without modifications are expressly permitted by Whistle Communications;
11  * provided, however, that:
12  * 1. Any and all reproductions of the source or object code must include the
13  *    copyright notice above and the following disclaimer of warranties; and
14  * 2. No rights are granted, in any manner or form, to use Whistle
15  *    Communications, Inc. trademarks, including the mark "WHISTLE
16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17  *    such appears in the above copyright notice or in the software.
18  *
19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35  * OF SUCH DAMAGE.
36  *
37  * Author: Archie Cobbs <archie@whistle.com>
38  *
39  * $FreeBSD$
40  * $Whistle: ng_ppp.c,v 1.22 1999/01/28 23:54:53 julian Exp $
41  */
42 
43 /*
44  * This node does PPP protocol multiplexing based on PPP protocol
45  * ID numbers. This node does not add address and control fields,
46  * as that is considered a ``device layer'' issue.
47  */
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/conf.h>
53 #include <sys/mbuf.h>
54 #include <sys/malloc.h>
55 #include <sys/errno.h>
56 #include <sys/socket.h>
57 #include <sys/syslog.h>
58 
59 #include <netgraph/ng_message.h>
60 #include <netgraph/netgraph.h>
61 #include <netgraph/ng_ppp.h>
62 
63 /* Protocol stuff */
64 #define PROT_DOWNLINK		0xffff
65 #define PROT_BYPASS		0x0000
66 
67 #define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
68 #define PROT_COMPRESSIBLE(p)	(((p) & 0xFF00) == 0x0000)
69 
70 /* Extract protocol from hook private pointer */
71 #define HOOK_PROTO(hook)	(*((u_int16_t *) &hook->private))
72 
73 /* Node private data */
74 struct private {
75 	struct	ng_ppp_stat stats;
76 	u_int   protocomp:1;
77 };
78 typedef struct private *priv_p;
79 
80 /* Protocol aliases */
81 struct protoalias {
82 	char   *name;
83 	u_int16_t proto;
84 };
85 
86 /* Netgraph node methods */
87 static int	ng_ppp_constructor(node_p *nodep);
88 static int	ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
89 		    const char *retaddr, struct ng_mesg **resp);
90 static int	ng_ppp_rmnode(node_p node);
91 static int	ng_ppp_newhook(node_p node, hook_p hook, const char *name);
92 static int	ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
93 static int	ng_ppp_disconnect(hook_p hook);
94 
95 /* Helper stuff */
96 static int	ng_ppp_decodehookname(const char *name);
97 static hook_p	ng_ppp_findhook(node_p node, int proto);
98 
99 /* Node type descriptor */
100 static struct ng_type typestruct = {
101 	NG_VERSION,
102 	NG_PPP_NODE_TYPE,
103 	NULL,
104 	ng_ppp_constructor,
105 	ng_ppp_rcvmsg,
106 	ng_ppp_rmnode,
107 	ng_ppp_newhook,
108 	NULL,
109 	NULL,
110 	ng_ppp_rcvdata,
111 	ng_ppp_rcvdata,
112 	ng_ppp_disconnect
113 };
114 NETGRAPH_INIT(ppp, &typestruct);
115 
116 /* Protocol aliases */
117 static const struct protoalias gAliases[] =
118 {
119 	{ NG_PPP_HOOK_DOWNLINK,		PROT_DOWNLINK	},
120 	{ NG_PPP_HOOK_BYPASS,		PROT_BYPASS	},
121 	{ NG_PPP_HOOK_LCP,		0xc021		},
122 	{ NG_PPP_HOOK_IPCP,		0x8021		},
123 	{ NG_PPP_HOOK_ATCP,		0x8029		},
124 	{ NG_PPP_HOOK_CCP,		0x80fd		},
125 	{ NG_PPP_HOOK_ECP,		0x8053		},
126 	{ NG_PPP_HOOK_IP,		0x0021		},
127 	{ NG_PPP_HOOK_VJCOMP,		0x002d		},
128 	{ NG_PPP_HOOK_VJUNCOMP,		0x002f		},
129 	{ NG_PPP_HOOK_MP,		0x003d		},
130 	{ NG_PPP_HOOK_COMPD,		0x00fd		},
131 	{ NG_PPP_HOOK_CRYPTD,		0x0053		},
132 	{ NG_PPP_HOOK_PAP,		0xc023		},
133 	{ NG_PPP_HOOK_CHAP,		0xc223		},
134 	{ NG_PPP_HOOK_LQR,		0xc025		},
135 	{ NULL,				0		}
136 };
137 
138 #define ERROUT(x)	do { error = (x); goto done; } while (0)
139 
140 /************************************************************************
141 			NETGRAPH NODE STUFF
142  ************************************************************************/
143 
144 /*
145  * Node constructor
146  */
147 static int
148 ng_ppp_constructor(node_p *nodep)
149 {
150 	priv_p priv;
151 	int error;
152 
153 	/* Allocate private structure */
154 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
155 	if (priv == NULL)
156 		return (ENOMEM);
157 	bzero(priv, sizeof(*priv));
158 
159 	/* Call generic node constructor */
160 	if ((error = ng_make_node_common(&typestruct, nodep))) {
161 		FREE(priv, M_NETGRAPH);
162 		return (error);
163 	}
164 	(*nodep)->private = priv;
165 
166 	/* Done */
167 	return (0);
168 }
169 
170 /*
171  * Give our OK for a hook to be added
172  */
173 static int
174 ng_ppp_newhook(node_p node, hook_p hook, const char *name)
175 {
176 	const priv_p priv = node->private;
177 	int proto;
178 
179 	/* Decode protocol number */
180 	if ((proto = ng_ppp_decodehookname(name)) < 0)
181 		return (EINVAL);
182 
183 	/* See if already connected */
184 	if (ng_ppp_findhook(node, proto) != NULL)
185 		return (EISCONN);
186 
187 	/* Clear stats when downstream hook reconnected */
188 	if (proto == PROT_DOWNLINK)
189 		bzero(&priv->stats, sizeof(priv->stats));
190 
191 	/* OK */
192 	HOOK_PROTO(hook) = proto;
193 	return (0);
194 }
195 
196 /*
197  * Receive a control message
198  */
199 static int
200 ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
201 	      const char *raddr, struct ng_mesg **rptr)
202 {
203 	const priv_p priv = node->private;
204 	struct ng_mesg *resp = NULL;
205 	int error = 0;
206 
207 	switch (msg->header.typecookie) {
208 	case NGM_PPP_COOKIE:
209 		switch (msg->header.cmd) {
210 		case NGM_PPP_SET_PROTOCOMP:
211 			if (msg->header.arglen < sizeof(int))
212 				ERROUT(EINVAL);
213 			priv->protocomp = !!*((int *) msg->data);
214 			break;
215 		case NGM_PPP_GET_STATS:
216 			NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT);
217 			if (resp == NULL)
218 				ERROUT(ENOMEM);
219 			*((struct ng_ppp_stat *) resp->data) = priv->stats;
220 			break;
221 		case NGM_PPP_CLR_STATS:
222 			bzero(&priv->stats, sizeof(priv->stats));
223 			break;
224 		default:
225 			error = EINVAL;
226 			break;
227 		}
228 		break;
229 	default:
230 		error = EINVAL;
231 		break;
232 	}
233 	if (rptr)
234 		*rptr = resp;
235 	else if (resp)
236 		FREE(resp, M_NETGRAPH);
237 
238 done:
239 	FREE(msg, M_NETGRAPH);
240 	return (error);
241 }
242 
243 /*
244  * Receive data on a hook
245  */
246 static int
247 ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
248 {
249 	const node_p node = hook->node;
250 	const priv_p priv = node->private;
251 	u_int16_t proto = HOOK_PROTO(hook);
252 	int error = 0;
253 
254 	switch (proto) {
255 
256 	/* Prepend the (possibly compressed) protocol number */
257 	default:
258 	    {
259 		int psize = (priv->protocomp
260 				&& PROT_COMPRESSIBLE(proto)) ? 1 : 2;
261 
262 		M_PREPEND(m, psize, M_NOWAIT);
263 		if (!m || !(m = m_pullup(m, psize)))
264 			ERROUT(ENOBUFS);
265 		if (psize == 1)
266 			*mtod(m, u_char *) = proto;
267 		else
268 			*mtod(m, u_short *) = htons(proto);
269 		hook = ng_ppp_findhook(node, PROT_DOWNLINK);
270 		break;
271 	    }
272 
273 	/* Extract the protocol number and direct to the corresponding hook */
274 	case PROT_DOWNLINK:
275 	    {
276 		/* Stats */
277 		priv->stats.recvFrames++;
278 		priv->stats.recvOctets += m->m_pkthdr.len;
279 
280 		/* Extract protocol number */
281 		for (proto = 0;
282 		     !PROT_VALID(proto);
283 		     proto = (proto << 8) + *mtod(m, u_char *), m_adj(m, 1)) {
284 			if (m == NULL) {
285 				priv->stats.badProto++;
286 				ERROUT(EINVAL);
287 			}
288 			if ((m = m_pullup(m, 1)) == NULL)
289 				ERROUT(ENOBUFS);
290 		}
291 
292 		/* Find corresponding hook; if none, use the "unhooked"
293 		   hook and leave the two-byte protocol prepended */
294 		if ((hook = ng_ppp_findhook(node, proto)) == NULL) {
295 			priv->stats.unknownProto++;
296 			hook = ng_ppp_findhook(node, PROT_BYPASS);
297 			M_PREPEND(m, 2, M_NOWAIT);
298 			if (m == NULL || (m = m_pullup(m, 2)) == NULL)
299 				ERROUT(ENOBUFS);
300 			*mtod(m, u_short *) = htons(proto);
301 		}
302 		break;
303 	    }
304 
305 	/* Send raw data from "unhooked" hook as-is; we assume the
306 	   protocol is already prepended */
307 	case PROT_BYPASS:
308 		hook = ng_ppp_findhook(node, PROT_DOWNLINK);
309 		break;
310 	}
311 
312 	/* Stats */
313 	if (m != NULL && hook != NULL && HOOK_PROTO(hook) == PROT_DOWNLINK) {
314 		priv->stats.xmitFrames++;
315 		priv->stats.xmitOctets += m->m_pkthdr.len;
316 	}
317 
318 	/* Forward packet on hook */
319 	NG_SEND_DATA(error, hook, m, meta);
320 	return (error);
321 
322 done:
323 	/* Something went wrong */
324 	NG_FREE_DATA(m, meta);
325 	return (error);
326 }
327 
328 /*
329  * Destroy node
330  */
331 static int
332 ng_ppp_rmnode(node_p node)
333 {
334 	const priv_p priv = node->private;
335 
336 	/* Take down netgraph node */
337 	node->flags |= NG_INVALID;
338 	ng_cutlinks(node);
339 	ng_unname(node);
340 	bzero(priv, sizeof(*priv));
341 	FREE(priv, M_NETGRAPH);
342 	node->private = NULL;
343 	ng_unref(node);		/* let the node escape */
344 	return (0);
345 }
346 
347 /*
348  * Hook disconnection
349  */
350 static int
351 ng_ppp_disconnect(hook_p hook)
352 {
353 	if (hook->node->numhooks == 0)
354 		ng_rmnode(hook->node);
355 	return (0);
356 }
357 
358 /************************************************************************
359 			HELPER STUFF
360  ************************************************************************/
361 
362 /*
363  * Decode ASCII protocol name
364  */
365 static int
366 ng_ppp_decodehookname(const char *name)
367 {
368 	int     k, proto;
369 
370 	for (k = 0; gAliases[k].name; k++)
371 		if (!strcmp(gAliases[k].name, name))
372 			return (gAliases[k].proto);
373 	if (strlen(name) != 6 || name[0] != '0' || name[1] != 'x')
374 		return (-1);
375 	for (proto = k = 2; k < 6; k++) {
376 		const u_char ch = name[k] | 0x20;
377 		int dig;
378 
379 		if (ch >= '0' && ch <= '9')
380 			dig = ch - '0';
381 		else if (ch >= 'a' && ch <= 'f')
382 			dig = ch - 'a' + 10;
383 		else
384 			return (-1);
385 		proto = (proto << 4) + dig;
386 	}
387 	if (!PROT_VALID(proto))
388 		return(-1);
389 	return (proto);
390 }
391 
392 /*
393  * Find a hook by protocol number
394  */
395 static hook_p
396 ng_ppp_findhook(node_p node, int proto)
397 {
398 	hook_p hook;
399 
400 	LIST_FOREACH(hook, &node->hooks, hooks) {
401 		if (HOOK_PROTO(hook) == proto)
402 			return (hook);
403 	}
404 	return (NULL);
405 }
406 
407