4cf49a4355
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
407 lines
10 KiB
C
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);
|
|
}
|
|
|