freebsd-nq/sys/netgraph/ng_ppp.c
Julian Elischer 4cf49a4355 Whistle's Netgraph link-layer (sometimes more) networking infrastructure.
Been in production for 3 years now. Gives Instant Frame relay to if_sr
and if_ar drivers, and PPPOE support soon. See:
ftp://ftp.whistle.com/pub/archie/netgraph/index.html
for on-line manual pages.

Reviewed by: Doug Rabson (dfr@freebsd.org)
Obtained from:  Whistle CVS tree
1999-10-21 09:06:11 +00:00

407 lines
10 KiB
C

/*
* ng_ppp.c
*
* Copyright (c) 1996-1999 Whistle Communications, Inc.
* All rights reserved.
*
* Subject to the following obligations and disclaimer of warranty, use and
* redistribution of this software, in source or object code forms, with or
* without modifications are expressly permitted by Whistle Communications;
* provided, however, that:
* 1. Any and all reproductions of the source or object code must include the
* copyright notice above and the following disclaimer of warranties; and
* 2. No rights are granted, in any manner or form, to use Whistle
* Communications, Inc. trademarks, including the mark "WHISTLE
* COMMUNICATIONS" on advertising, endorsements, or otherwise except as
* such appears in the above copyright notice or in the software.
*
* THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
* INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
* WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
* REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
* SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
* IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
* RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
* WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* Author: Archie Cobbs <archie@whistle.com>
*
* $FreeBSD$
* $Whistle: ng_ppp.c,v 1.22 1999/01/28 23:54:53 julian Exp $
*/
/*
* This node does PPP protocol multiplexing based on PPP protocol
* ID numbers. This node does not add address and control fields,
* as that is considered a ``device layer'' issue.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <netgraph/ng_ppp.h>
/* Protocol stuff */
#define PROT_DOWNLINK 0xffff
#define PROT_BYPASS 0x0000
#define PROT_VALID(p) (((p) & 0x0101) == 0x0001)
#define PROT_COMPRESSIBLE(p) (((p) & 0xFF00) == 0x0000)
/* Extract protocol from hook private pointer */
#define HOOK_PROTO(hook) (*((u_int16_t *) &hook->private))
/* Node private data */
struct private {
struct ng_ppp_stat stats;
u_int protocomp:1;
};
typedef struct private *priv_p;
/* Protocol aliases */
struct protoalias {
char *name;
u_int16_t proto;
};
/* Netgraph node methods */
static int ng_ppp_constructor(node_p *nodep);
static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
const char *retaddr, struct ng_mesg **resp);
static int ng_ppp_rmnode(node_p node);
static int ng_ppp_newhook(node_p node, hook_p hook, const char *name);
static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
static int ng_ppp_disconnect(hook_p hook);
/* Helper stuff */
static int ng_ppp_decodehookname(const char *name);
static hook_p ng_ppp_findhook(node_p node, int proto);
/* Node type descriptor */
static struct ng_type typestruct = {
NG_VERSION,
NG_PPP_NODE_TYPE,
NULL,
ng_ppp_constructor,
ng_ppp_rcvmsg,
ng_ppp_rmnode,
ng_ppp_newhook,
NULL,
NULL,
ng_ppp_rcvdata,
ng_ppp_rcvdata,
ng_ppp_disconnect
};
NETGRAPH_INIT(ppp, &typestruct);
/* Protocol aliases */
static const struct protoalias gAliases[] =
{
{ NG_PPP_HOOK_DOWNLINK, PROT_DOWNLINK },
{ NG_PPP_HOOK_BYPASS, PROT_BYPASS },
{ NG_PPP_HOOK_LCP, 0xc021 },
{ NG_PPP_HOOK_IPCP, 0x8021 },
{ NG_PPP_HOOK_ATCP, 0x8029 },
{ NG_PPP_HOOK_CCP, 0x80fd },
{ NG_PPP_HOOK_ECP, 0x8053 },
{ NG_PPP_HOOK_IP, 0x0021 },
{ NG_PPP_HOOK_VJCOMP, 0x002d },
{ NG_PPP_HOOK_VJUNCOMP, 0x002f },
{ NG_PPP_HOOK_MP, 0x003d },
{ NG_PPP_HOOK_COMPD, 0x00fd },
{ NG_PPP_HOOK_CRYPTD, 0x0053 },
{ NG_PPP_HOOK_PAP, 0xc023 },
{ NG_PPP_HOOK_CHAP, 0xc223 },
{ NG_PPP_HOOK_LQR, 0xc025 },
{ NULL, 0 }
};
#define ERROUT(x) do { error = (x); goto done; } while (0)
/************************************************************************
NETGRAPH NODE STUFF
************************************************************************/
/*
* Node constructor
*/
static int
ng_ppp_constructor(node_p *nodep)
{
priv_p priv;
int error;
/* Allocate private structure */
MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
if (priv == NULL)
return (ENOMEM);
bzero(priv, sizeof(*priv));
/* Call generic node constructor */
if ((error = ng_make_node_common(&typestruct, nodep))) {
FREE(priv, M_NETGRAPH);
return (error);
}
(*nodep)->private = priv;
/* Done */
return (0);
}
/*
* Give our OK for a hook to be added
*/
static int
ng_ppp_newhook(node_p node, hook_p hook, const char *name)
{
const priv_p priv = node->private;
int proto;
/* Decode protocol number */
if ((proto = ng_ppp_decodehookname(name)) < 0)
return (EINVAL);
/* See if already connected */
if (ng_ppp_findhook(node, proto) != NULL)
return (EISCONN);
/* Clear stats when downstream hook reconnected */
if (proto == PROT_DOWNLINK)
bzero(&priv->stats, sizeof(priv->stats));
/* OK */
HOOK_PROTO(hook) = proto;
return (0);
}
/*
* Receive a control message
*/
static int
ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
const char *raddr, struct ng_mesg **rptr)
{
const priv_p priv = node->private;
struct ng_mesg *resp = NULL;
int error = 0;
switch (msg->header.typecookie) {
case NGM_PPP_COOKIE:
switch (msg->header.cmd) {
case NGM_PPP_SET_PROTOCOMP:
if (msg->header.arglen < sizeof(int))
ERROUT(EINVAL);
priv->protocomp = !!*((int *) msg->data);
break;
case NGM_PPP_GET_STATS:
NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT);
if (resp == NULL)
ERROUT(ENOMEM);
*((struct ng_ppp_stat *) resp->data) = priv->stats;
break;
case NGM_PPP_CLR_STATS:
bzero(&priv->stats, sizeof(priv->stats));
break;
default:
error = EINVAL;
break;
}
break;
default:
error = EINVAL;
break;
}
if (rptr)
*rptr = resp;
else if (resp)
FREE(resp, M_NETGRAPH);
done:
FREE(msg, M_NETGRAPH);
return (error);
}
/*
* Receive data on a hook
*/
static int
ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
{
const node_p node = hook->node;
const priv_p priv = node->private;
u_int16_t proto = HOOK_PROTO(hook);
int error = 0;
switch (proto) {
/* Prepend the (possibly compressed) protocol number */
default:
{
int psize = (priv->protocomp
&& PROT_COMPRESSIBLE(proto)) ? 1 : 2;
M_PREPEND(m, psize, M_NOWAIT);
if (!m || !(m = m_pullup(m, psize)))
ERROUT(ENOBUFS);
if (psize == 1)
*mtod(m, u_char *) = proto;
else
*mtod(m, u_short *) = htons(proto);
hook = ng_ppp_findhook(node, PROT_DOWNLINK);
break;
}
/* Extract the protocol number and direct to the corresponding hook */
case PROT_DOWNLINK:
{
/* Stats */
priv->stats.recvFrames++;
priv->stats.recvOctets += m->m_pkthdr.len;
/* Extract protocol number */
for (proto = 0;
!PROT_VALID(proto);
proto = (proto << 8) + *mtod(m, u_char *), m_adj(m, 1)) {
if (m == NULL) {
priv->stats.badProto++;
ERROUT(EINVAL);
}
if ((m = m_pullup(m, 1)) == NULL)
ERROUT(ENOBUFS);
}
/* Find corresponding hook; if none, use the "unhooked"
hook and leave the two-byte protocol prepended */
if ((hook = ng_ppp_findhook(node, proto)) == NULL) {
priv->stats.unknownProto++;
hook = ng_ppp_findhook(node, PROT_BYPASS);
M_PREPEND(m, 2, M_NOWAIT);
if (m == NULL || (m = m_pullup(m, 2)) == NULL)
ERROUT(ENOBUFS);
*mtod(m, u_short *) = htons(proto);
}
break;
}
/* Send raw data from "unhooked" hook as-is; we assume the
protocol is already prepended */
case PROT_BYPASS:
hook = ng_ppp_findhook(node, PROT_DOWNLINK);
break;
}
/* Stats */
if (m != NULL && hook != NULL && HOOK_PROTO(hook) == PROT_DOWNLINK) {
priv->stats.xmitFrames++;
priv->stats.xmitOctets += m->m_pkthdr.len;
}
/* Forward packet on hook */
NG_SEND_DATA(error, hook, m, meta);
return (error);
done:
/* Something went wrong */
NG_FREE_DATA(m, meta);
return (error);
}
/*
* Destroy node
*/
static int
ng_ppp_rmnode(node_p node)
{
const priv_p priv = node->private;
/* Take down netgraph node */
node->flags |= NG_INVALID;
ng_cutlinks(node);
ng_unname(node);
bzero(priv, sizeof(*priv));
FREE(priv, M_NETGRAPH);
node->private = NULL;
ng_unref(node); /* let the node escape */
return (0);
}
/*
* Hook disconnection
*/
static int
ng_ppp_disconnect(hook_p hook)
{
if (hook->node->numhooks == 0)
ng_rmnode(hook->node);
return (0);
}
/************************************************************************
HELPER STUFF
************************************************************************/
/*
* Decode ASCII protocol name
*/
static int
ng_ppp_decodehookname(const char *name)
{
int k, proto;
for (k = 0; gAliases[k].name; k++)
if (!strcmp(gAliases[k].name, name))
return (gAliases[k].proto);
if (strlen(name) != 6 || name[0] != '0' || name[1] != 'x')
return (-1);
for (proto = k = 2; k < 6; k++) {
const u_char ch = name[k] | 0x20;
int dig;
if (ch >= '0' && ch <= '9')
dig = ch - '0';
else if (ch >= 'a' && ch <= 'f')
dig = ch - 'a' + 10;
else
return (-1);
proto = (proto << 4) + dig;
}
if (!PROT_VALID(proto))
return(-1);
return (proto);
}
/*
* Find a hook by protocol number
*/
static hook_p
ng_ppp_findhook(node_p node, int proto)
{
hook_p hook;
LIST_FOREACH(hook, &node->hooks, hooks) {
if (HOOK_PROTO(hook) == proto)
return (hook);
}
return (NULL);
}