874 lines
19 KiB
C
874 lines
19 KiB
C
|
/*
|
||
|
* if_ppp.c - a network interface connected to a STREAMS module.
|
||
|
*
|
||
|
* Copyright (c) 1994 Paul Mackerras. All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
*
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in
|
||
|
* the documentation and/or other materials provided with the
|
||
|
* distribution.
|
||
|
*
|
||
|
* 3. The name(s) of the authors of this software must not be used to
|
||
|
* endorse or promote products derived from this software without
|
||
|
* prior written permission.
|
||
|
*
|
||
|
* 4. Redistributions of any form whatsoever must retain the following
|
||
|
* acknowledgment:
|
||
|
* "This product includes software developed by Paul Mackerras
|
||
|
* <paulus@samba.org>".
|
||
|
*
|
||
|
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
|
||
|
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||
|
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*
|
||
|
* $Id: if_ppp.c,v 1.18 2002/12/06 09:49:15 paulus Exp $
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* This file is used under SunOS 4 and Digital UNIX.
|
||
|
*
|
||
|
* This file provides the glue between PPP and IP.
|
||
|
*/
|
||
|
|
||
|
#define INET 1
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/param.h>
|
||
|
#include <sys/errno.h>
|
||
|
#include <sys/mbuf.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <net/if.h>
|
||
|
#include <net/netisr.h>
|
||
|
#include <net/ppp_defs.h>
|
||
|
#include <net/pppio.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <netinet/in_var.h>
|
||
|
#ifdef __osf__
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <net/if_types.h>
|
||
|
#else
|
||
|
#include <sys/sockio.h>
|
||
|
#endif
|
||
|
#include "ppp_mod.h"
|
||
|
|
||
|
#include <sys/stream.h>
|
||
|
|
||
|
#ifdef SNIT_SUPPORT
|
||
|
#include <sys/time.h>
|
||
|
#include <net/nit_if.h>
|
||
|
#include <netinet/if_ether.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef __osf__
|
||
|
#define SIOCSIFMTU SIOCSIPMTU
|
||
|
#define SIOCGIFMTU SIOCRIPMTU
|
||
|
#define IFA_ADDR(ifa) (*(ifa)->ifa_addr)
|
||
|
#else
|
||
|
#define IFA_ADDR(ifa) ((ifa)->ifa_addr)
|
||
|
#endif
|
||
|
|
||
|
#define ifr_mtu ifr_metric
|
||
|
|
||
|
static int if_ppp_open __P((queue_t *, int, int, int));
|
||
|
static int if_ppp_close __P((queue_t *, int));
|
||
|
static int if_ppp_wput __P((queue_t *, mblk_t *));
|
||
|
static int if_ppp_rput __P((queue_t *, mblk_t *));
|
||
|
|
||
|
#define PPP_IF_ID 0x8021
|
||
|
static struct module_info minfo = {
|
||
|
PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128
|
||
|
};
|
||
|
|
||
|
static struct qinit rinit = {
|
||
|
if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
|
||
|
};
|
||
|
|
||
|
static struct qinit winit = {
|
||
|
if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
|
||
|
};
|
||
|
|
||
|
struct streamtab if_pppinfo = {
|
||
|
&rinit, &winit, NULL, NULL
|
||
|
};
|
||
|
|
||
|
typedef struct if_ppp_state {
|
||
|
int unit;
|
||
|
queue_t *q;
|
||
|
int flags;
|
||
|
} if_ppp_t;
|
||
|
|
||
|
/* Values for flags */
|
||
|
#define DBGLOG 1
|
||
|
|
||
|
static int if_ppp_count; /* Number of currently-active streams */
|
||
|
|
||
|
static int ppp_nalloc; /* Number of elements of ifs and states */
|
||
|
static struct ifnet **ifs; /* Array of pointers to interface structs */
|
||
|
static if_ppp_t **states; /* Array of pointers to state structs */
|
||
|
|
||
|
static int if_ppp_output __P((struct ifnet *, struct mbuf *,
|
||
|
struct sockaddr *));
|
||
|
static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t));
|
||
|
static struct mbuf *make_mbufs __P((mblk_t *, int));
|
||
|
static mblk_t *make_message __P((struct mbuf *, int));
|
||
|
|
||
|
#ifdef SNIT_SUPPORT
|
||
|
/* Fake ether header for SNIT */
|
||
|
static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP};
|
||
|
#endif
|
||
|
|
||
|
#ifndef __osf__
|
||
|
static void ppp_if_detach __P((struct ifnet *));
|
||
|
|
||
|
/*
|
||
|
* Detach all the interfaces before unloading.
|
||
|
* Not sure this works.
|
||
|
*/
|
||
|
int
|
||
|
if_ppp_unload()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (if_ppp_count > 0)
|
||
|
return EBUSY;
|
||
|
for (i = 0; i < ppp_nalloc; ++i)
|
||
|
if (ifs[i] != 0)
|
||
|
ppp_if_detach(ifs[i]);
|
||
|
if (ifs) {
|
||
|
FREE(ifs, ppp_nalloc * sizeof (struct ifnet *));
|
||
|
FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *));
|
||
|
}
|
||
|
ppp_nalloc = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* __osf__ */
|
||
|
|
||
|
/*
|
||
|
* STREAMS module entry points.
|
||
|
*/
|
||
|
static int
|
||
|
if_ppp_open(q, dev, flag, sflag)
|
||
|
queue_t *q;
|
||
|
int dev;
|
||
|
int flag, sflag;
|
||
|
{
|
||
|
if_ppp_t *sp;
|
||
|
|
||
|
if (q->q_ptr == 0) {
|
||
|
sp = (if_ppp_t *) ALLOC_SLEEP(sizeof (if_ppp_t));
|
||
|
if (sp == 0)
|
||
|
return OPENFAIL;
|
||
|
bzero(sp, sizeof (if_ppp_t));
|
||
|
q->q_ptr = (caddr_t) sp;
|
||
|
WR(q)->q_ptr = (caddr_t) sp;
|
||
|
sp->unit = -1; /* no interface unit attached at present */
|
||
|
sp->q = WR(q);
|
||
|
sp->flags = 0;
|
||
|
++if_ppp_count;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
if_ppp_close(q, flag)
|
||
|
queue_t *q;
|
||
|
int flag;
|
||
|
{
|
||
|
if_ppp_t *sp;
|
||
|
struct ifnet *ifp;
|
||
|
|
||
|
sp = (if_ppp_t *) q->q_ptr;
|
||
|
if (sp != 0) {
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp closed, q=%x sp=%x\n", q, sp);
|
||
|
if (sp->unit >= 0) {
|
||
|
if (sp->unit < ppp_nalloc) {
|
||
|
states[sp->unit] = 0;
|
||
|
ifp = ifs[sp->unit];
|
||
|
if (ifp != 0)
|
||
|
ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
|
||
|
#ifdef DEBUG
|
||
|
} else {
|
||
|
printf("if_ppp: unit %d nonexistent!\n", sp->unit);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
FREE(sp, sizeof (if_ppp_t));
|
||
|
--if_ppp_count;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
if_ppp_wput(q, mp)
|
||
|
queue_t *q;
|
||
|
mblk_t *mp;
|
||
|
{
|
||
|
if_ppp_t *sp;
|
||
|
struct iocblk *iop;
|
||
|
int error, unit;
|
||
|
struct ifnet *ifp;
|
||
|
|
||
|
sp = (if_ppp_t *) q->q_ptr;
|
||
|
switch (mp->b_datap->db_type) {
|
||
|
case M_DATA:
|
||
|
/*
|
||
|
* Now why would we be getting data coming in here??
|
||
|
*/
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp));
|
||
|
freemsg(mp);
|
||
|
break;
|
||
|
|
||
|
case M_IOCTL:
|
||
|
iop = (struct iocblk *) mp->b_rptr;
|
||
|
error = EINVAL;
|
||
|
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: got ioctl cmd=%x count=%d\n",
|
||
|
iop->ioc_cmd, iop->ioc_count);
|
||
|
|
||
|
switch (iop->ioc_cmd) {
|
||
|
case PPPIO_NEWPPA: /* well almost */
|
||
|
if (iop->ioc_count != sizeof(int) || sp->unit >= 0)
|
||
|
break;
|
||
|
if ((error = NOTSUSER()) != 0)
|
||
|
break;
|
||
|
unit = *(int *)mp->b_cont->b_rptr;
|
||
|
|
||
|
/* Check that this unit isn't already in use */
|
||
|
if (unit < ppp_nalloc && states[unit] != 0) {
|
||
|
error = EADDRINUSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Extend ifs and states arrays if necessary. */
|
||
|
error = ENOSR;
|
||
|
if (unit >= ppp_nalloc) {
|
||
|
int newn;
|
||
|
struct ifnet **newifs;
|
||
|
if_ppp_t **newstates;
|
||
|
|
||
|
newn = unit + 4;
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: extending ifs to %d\n", newn);
|
||
|
newifs = (struct ifnet **)
|
||
|
ALLOC_NOSLEEP(newn * sizeof (struct ifnet *));
|
||
|
if (newifs == 0)
|
||
|
break;
|
||
|
bzero(newifs, newn * sizeof (struct ifnet *));
|
||
|
newstates = (if_ppp_t **)
|
||
|
ALLOC_NOSLEEP(newn * sizeof (struct if_ppp_t *));
|
||
|
if (newstates == 0) {
|
||
|
FREE(newifs, newn * sizeof (struct ifnet *));
|
||
|
break;
|
||
|
}
|
||
|
bzero(newstates, newn * sizeof (struct if_ppp_t *));
|
||
|
bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *));
|
||
|
bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *));
|
||
|
if (ifs) {
|
||
|
FREE(ifs, ppp_nalloc * sizeof(struct ifnet *));
|
||
|
FREE(states, ppp_nalloc * sizeof(if_ppp_t *));
|
||
|
}
|
||
|
ifs = newifs;
|
||
|
states = newstates;
|
||
|
ppp_nalloc = newn;
|
||
|
}
|
||
|
|
||
|
/* Allocate a new ifnet struct if necessary. */
|
||
|
ifp = ifs[unit];
|
||
|
if (ifp == 0) {
|
||
|
ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet));
|
||
|
if (ifp == 0)
|
||
|
break;
|
||
|
bzero(ifp, sizeof (struct ifnet));
|
||
|
ifs[unit] = ifp;
|
||
|
ifp->if_name = "ppp";
|
||
|
ifp->if_unit = unit;
|
||
|
ifp->if_mtu = PPP_MTU;
|
||
|
ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
|
||
|
#ifndef __osf__
|
||
|
#ifdef IFF_MULTICAST
|
||
|
ifp->if_flags |= IFF_MULTICAST;
|
||
|
#endif
|
||
|
#endif /* __osf__ */
|
||
|
ifp->if_output = if_ppp_output;
|
||
|
#ifdef __osf__
|
||
|
ifp->if_version = "Point-to-Point Protocol, version 2.3.11";
|
||
|
ifp->if_mediamtu = PPP_MTU;
|
||
|
ifp->if_type = IFT_PPP;
|
||
|
ifp->if_hdrlen = PPP_HDRLEN;
|
||
|
ifp->if_addrlen = 0;
|
||
|
ifp->if_flags |= IFF_NOARP | IFF_SIMPLEX | IFF_NOTRAILERS;
|
||
|
#ifdef IFF_VAR_MTU
|
||
|
ifp->if_flags |= IFF_VAR_MTU;
|
||
|
#endif
|
||
|
#ifdef NETMASTERCPU
|
||
|
ifp->if_affinity = NETMASTERCPU;
|
||
|
#endif
|
||
|
#endif
|
||
|
ifp->if_ioctl = if_ppp_ioctl;
|
||
|
ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
|
||
|
if_attach(ifp);
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: created unit %d\n", unit);
|
||
|
} else {
|
||
|
ifp->if_mtu = PPP_MTU;
|
||
|
ifp->if_flags |= IFF_RUNNING;
|
||
|
}
|
||
|
|
||
|
states[unit] = sp;
|
||
|
sp->unit = unit;
|
||
|
|
||
|
error = 0;
|
||
|
iop->ioc_count = 0;
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
|
||
|
sp, sp->q);
|
||
|
break;
|
||
|
|
||
|
case PPPIO_DEBUG:
|
||
|
error = -1;
|
||
|
if (iop->ioc_count == sizeof(int)) {
|
||
|
if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
|
||
|
printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
|
||
|
sp->flags |= DBGLOG;
|
||
|
error = 0;
|
||
|
iop->ioc_count = 0;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: ioctl result %d\n", error);
|
||
|
if (error < 0)
|
||
|
putnext(q, mp);
|
||
|
else if (error == 0) {
|
||
|
mp->b_datap->db_type = M_IOCACK;
|
||
|
qreply(q, mp);
|
||
|
} else {
|
||
|
mp->b_datap->db_type = M_IOCNAK;
|
||
|
iop->ioc_count = 0;
|
||
|
iop->ioc_error = error;
|
||
|
qreply(q, mp);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
putnext(q, mp);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
if_ppp_rput(q, mp)
|
||
|
queue_t *q;
|
||
|
mblk_t *mp;
|
||
|
{
|
||
|
if_ppp_t *sp;
|
||
|
int proto, s;
|
||
|
struct mbuf *mb;
|
||
|
struct ifqueue *inq;
|
||
|
struct ifnet *ifp;
|
||
|
int len;
|
||
|
|
||
|
sp = (if_ppp_t *) q->q_ptr;
|
||
|
switch (mp->b_datap->db_type) {
|
||
|
case M_DATA:
|
||
|
/*
|
||
|
* Convert the message into an mbuf chain
|
||
|
* and inject it into the network code.
|
||
|
*/
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
|
||
|
msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
|
||
|
mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
|
||
|
mp->b_rptr[7]);
|
||
|
|
||
|
if (sp->unit < 0) {
|
||
|
freemsg(mp);
|
||
|
break;
|
||
|
}
|
||
|
if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
|
||
|
#ifdef DEBUG
|
||
|
printf("if_ppp: no unit %d!\n", sp->unit);
|
||
|
#endif
|
||
|
freemsg(mp);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((ifp->if_flags & IFF_UP) == 0) {
|
||
|
freemsg(mp);
|
||
|
break;
|
||
|
}
|
||
|
++ifp->if_ipackets;
|
||
|
|
||
|
proto = PPP_PROTOCOL(mp->b_rptr);
|
||
|
adjmsg(mp, PPP_HDRLEN);
|
||
|
len = msgdsize(mp);
|
||
|
mb = make_mbufs(mp, sizeof(struct ifnet *));
|
||
|
freemsg(mp);
|
||
|
if (mb == NULL) {
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
|
||
|
++ifp->if_ierrors;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#ifdef SNIT_SUPPORT
|
||
|
if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) {
|
||
|
struct nit_if nif;
|
||
|
|
||
|
nif.nif_header = (caddr_t) &snit_ehdr;
|
||
|
nif.nif_hdrlen = sizeof(snit_ehdr);
|
||
|
nif.nif_bodylen = len;
|
||
|
nif.nif_promisc = 0;
|
||
|
snit_intr(ifp, mb, &nif);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* For Digital UNIX, there's space set aside in the header mbuf
|
||
|
* for the interface info.
|
||
|
*
|
||
|
* For Sun it's smuggled around via a pointer at the front of the mbuf.
|
||
|
*/
|
||
|
#ifdef __osf__
|
||
|
mb->m_pkthdr.rcvif = ifp;
|
||
|
mb->m_pkthdr.len = len;
|
||
|
#else
|
||
|
mb->m_off -= sizeof(struct ifnet *);
|
||
|
mb->m_len += sizeof(struct ifnet *);
|
||
|
*mtod(mb, struct ifnet **) = ifp;
|
||
|
#endif
|
||
|
|
||
|
inq = 0;
|
||
|
switch (proto) {
|
||
|
case PPP_IP:
|
||
|
inq = &ipintrq;
|
||
|
schednetisr(NETISR_IP);
|
||
|
}
|
||
|
|
||
|
if (inq != 0) {
|
||
|
s = splhigh();
|
||
|
if (IF_QFULL(inq)) {
|
||
|
IF_DROP(inq);
|
||
|
++ifp->if_ierrors;
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: inq full, proto=%x\n", proto);
|
||
|
m_freem(mb);
|
||
|
} else {
|
||
|
IF_ENQUEUE(inq, mb);
|
||
|
}
|
||
|
splx(s);
|
||
|
} else {
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
|
||
|
++ifp->if_ierrors;
|
||
|
m_freem(mb);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
putnext(q, mp);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Network code wants to output a packet.
|
||
|
* Turn it into a STREAMS message and send it down.
|
||
|
*/
|
||
|
static int
|
||
|
if_ppp_output(ifp, m0, dst)
|
||
|
struct ifnet *ifp;
|
||
|
struct mbuf *m0;
|
||
|
struct sockaddr *dst;
|
||
|
{
|
||
|
mblk_t *mp;
|
||
|
int proto, s;
|
||
|
if_ppp_t *sp;
|
||
|
u_char *p;
|
||
|
|
||
|
if ((ifp->if_flags & IFF_UP) == 0) {
|
||
|
m_freem(m0);
|
||
|
return ENETDOWN;
|
||
|
}
|
||
|
|
||
|
if ((unsigned)ifp->if_unit >= ppp_nalloc) {
|
||
|
#ifdef DEBUG
|
||
|
printf("if_ppp_output: unit %d?\n", ifp->if_unit);
|
||
|
#endif
|
||
|
m_freem(m0);
|
||
|
return EINVAL;
|
||
|
}
|
||
|
sp = states[ifp->if_unit];
|
||
|
if (sp == 0) {
|
||
|
#ifdef DEBUG
|
||
|
printf("if_ppp_output: no queue?\n");
|
||
|
#endif
|
||
|
m_freem(m0);
|
||
|
return ENETDOWN;
|
||
|
}
|
||
|
|
||
|
if (sp->flags & DBGLOG) {
|
||
|
p = mtod(m0, u_char *);
|
||
|
printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
|
||
|
ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
|
||
|
p[5], p[6], p[7], sp->q);
|
||
|
}
|
||
|
|
||
|
switch (dst->sa_family) {
|
||
|
case AF_INET:
|
||
|
proto = PPP_IP;
|
||
|
#ifdef SNIT_SUPPORT
|
||
|
if (ifp->if_flags & IFF_PROMISC) {
|
||
|
struct nit_if nif;
|
||
|
struct mbuf *m;
|
||
|
int len;
|
||
|
|
||
|
for (len = 0, m = m0; m != NULL; m = m->m_next)
|
||
|
len += m->m_len;
|
||
|
nif.nif_header = (caddr_t) &snit_ehdr;
|
||
|
nif.nif_hdrlen = sizeof(snit_ehdr);
|
||
|
nif.nif_bodylen = len;
|
||
|
nif.nif_promisc = 0;
|
||
|
snit_intr(ifp, m0, &nif);
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
m_freem(m0);
|
||
|
return EAFNOSUPPORT;
|
||
|
}
|
||
|
|
||
|
++ifp->if_opackets;
|
||
|
mp = make_message(m0, PPP_HDRLEN);
|
||
|
m_freem(m0);
|
||
|
if (mp == 0) {
|
||
|
++ifp->if_oerrors;
|
||
|
return ENOBUFS;
|
||
|
}
|
||
|
mp->b_rptr -= PPP_HDRLEN;
|
||
|
mp->b_rptr[0] = PPP_ALLSTATIONS;
|
||
|
mp->b_rptr[1] = PPP_UI;
|
||
|
mp->b_rptr[2] = proto >> 8;
|
||
|
mp->b_rptr[3] = proto;
|
||
|
|
||
|
s = splstr();
|
||
|
if (sp->flags & DBGLOG)
|
||
|
printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
|
||
|
sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
|
||
|
putnext(sp->q, mp);
|
||
|
splx(s);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Socket ioctl routine for ppp interfaces.
|
||
|
*/
|
||
|
static int
|
||
|
if_ppp_ioctl(ifp, cmd, data)
|
||
|
struct ifnet *ifp;
|
||
|
u_int cmd;
|
||
|
caddr_t data;
|
||
|
{
|
||
|
int s, error;
|
||
|
struct ifreq *ifr = (struct ifreq *) data;
|
||
|
struct ifaddr *ifa = (struct ifaddr *) data;
|
||
|
u_short mtu;
|
||
|
|
||
|
error = 0;
|
||
|
s = splimp();
|
||
|
switch (cmd) {
|
||
|
case SIOCSIFFLAGS:
|
||
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
||
|
ifp->if_flags &= ~IFF_UP;
|
||
|
break;
|
||
|
|
||
|
case SIOCSIFADDR:
|
||
|
if (IFA_ADDR(ifa).sa_family != AF_INET)
|
||
|
error = EAFNOSUPPORT;
|
||
|
break;
|
||
|
|
||
|
case SIOCSIFDSTADDR:
|
||
|
if (IFA_ADDR(ifa).sa_family != AF_INET)
|
||
|
error = EAFNOSUPPORT;
|
||
|
break;
|
||
|
|
||
|
case SIOCSIFMTU:
|
||
|
if ((error = NOTSUSER()) != 0)
|
||
|
break;
|
||
|
#ifdef __osf__
|
||
|
/* this hack is necessary because ifioctl checks ifr_data
|
||
|
* in 4.0 and 5.0, but ifr_data and ifr_metric overlay each
|
||
|
* other in the definition of struct ifreq so pppd can't set both.
|
||
|
*/
|
||
|
bcopy(ifr->ifr_data, &mtu, sizeof (u_short));
|
||
|
ifr->ifr_mtu = mtu;
|
||
|
#endif
|
||
|
|
||
|
if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
|
||
|
error = EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
ifp->if_mtu = ifr->ifr_mtu;
|
||
|
break;
|
||
|
|
||
|
case SIOCGIFMTU:
|
||
|
ifr->ifr_mtu = ifp->if_mtu;
|
||
|
break;
|
||
|
|
||
|
case SIOCADDMULTI:
|
||
|
case SIOCDELMULTI:
|
||
|
switch(ifr->ifr_addr.sa_family) {
|
||
|
case AF_INET:
|
||
|
break;
|
||
|
default:
|
||
|
error = EAFNOSUPPORT;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error = EINVAL;
|
||
|
}
|
||
|
splx(s);
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Turn a STREAMS message into an mbuf chain.
|
||
|
*/
|
||
|
static struct mbuf *
|
||
|
make_mbufs(mp, off)
|
||
|
mblk_t *mp;
|
||
|
int off;
|
||
|
{
|
||
|
struct mbuf *head, **prevp, *m;
|
||
|
int len, space, n;
|
||
|
unsigned char *cp, *dp;
|
||
|
|
||
|
len = msgdsize(mp);
|
||
|
if (len == 0)
|
||
|
return 0;
|
||
|
prevp = &head;
|
||
|
space = 0;
|
||
|
cp = mp->b_rptr;
|
||
|
#ifdef __osf__
|
||
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
||
|
m->m_len = 0;
|
||
|
space = MHLEN;
|
||
|
*prevp = m;
|
||
|
prevp = &m->m_next;
|
||
|
dp = mtod(m, unsigned char *);
|
||
|
len -= space;
|
||
|
off = 0;
|
||
|
#endif
|
||
|
for (;;) {
|
||
|
while (cp >= mp->b_wptr) {
|
||
|
mp = mp->b_cont;
|
||
|
if (mp == 0) {
|
||
|
*prevp = 0;
|
||
|
return head;
|
||
|
}
|
||
|
cp = mp->b_rptr;
|
||
|
}
|
||
|
n = mp->b_wptr - cp;
|
||
|
if (space == 0) {
|
||
|
MGET(m, M_DONTWAIT, MT_DATA);
|
||
|
*prevp = m;
|
||
|
if (m == 0) {
|
||
|
if (head != 0)
|
||
|
m_freem(head);
|
||
|
return 0;
|
||
|
}
|
||
|
if (len + off > 2 * MLEN) {
|
||
|
#ifdef __osf__
|
||
|
MCLGET(m, M_DONTWAIT);
|
||
|
#else
|
||
|
MCLGET(m);
|
||
|
#endif
|
||
|
}
|
||
|
#ifdef __osf__
|
||
|
space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN);
|
||
|
#else
|
||
|
space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
|
||
|
m->m_off += off;
|
||
|
#endif
|
||
|
m->m_len = 0;
|
||
|
len -= space;
|
||
|
dp = mtod(m, unsigned char *);
|
||
|
off = 0;
|
||
|
prevp = &m->m_next;
|
||
|
}
|
||
|
if (n > space)
|
||
|
n = space;
|
||
|
bcopy(cp, dp, n);
|
||
|
cp += n;
|
||
|
dp += n;
|
||
|
space -= n;
|
||
|
m->m_len += n;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Turn an mbuf chain into a STREAMS message.
|
||
|
*/
|
||
|
#define ALLOCB_MAX 4096
|
||
|
|
||
|
static mblk_t *
|
||
|
make_message(m, off)
|
||
|
struct mbuf *m;
|
||
|
int off;
|
||
|
{
|
||
|
mblk_t *head, **prevp, *mp;
|
||
|
int len, space, n, nb;
|
||
|
unsigned char *cp, *dp;
|
||
|
struct mbuf *nm;
|
||
|
|
||
|
len = 0;
|
||
|
for (nm = m; nm != 0; nm = nm->m_next)
|
||
|
len += nm->m_len;
|
||
|
prevp = &head;
|
||
|
space = 0;
|
||
|
cp = mtod(m, unsigned char *);
|
||
|
nb = m->m_len;
|
||
|
for (;;) {
|
||
|
while (nb <= 0) {
|
||
|
m = m->m_next;
|
||
|
if (m == 0) {
|
||
|
*prevp = 0;
|
||
|
return head;
|
||
|
}
|
||
|
cp = mtod(m, unsigned char *);
|
||
|
nb = m->m_len;
|
||
|
}
|
||
|
if (space == 0) {
|
||
|
space = len + off;
|
||
|
if (space > ALLOCB_MAX)
|
||
|
space = ALLOCB_MAX;
|
||
|
mp = allocb(space, BPRI_LO);
|
||
|
*prevp = mp;
|
||
|
if (mp == 0) {
|
||
|
if (head != 0)
|
||
|
freemsg(head);
|
||
|
return 0;
|
||
|
}
|
||
|
dp = mp->b_rptr += off;
|
||
|
space -= off;
|
||
|
len -= space;
|
||
|
off = 0;
|
||
|
prevp = &mp->b_cont;
|
||
|
}
|
||
|
n = nb < space? nb: space;
|
||
|
bcopy(cp, dp, n);
|
||
|
cp += n;
|
||
|
dp += n;
|
||
|
nb -= n;
|
||
|
space -= n;
|
||
|
mp->b_wptr = dp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Digital UNIX doesn't allow for removing ifnet structures
|
||
|
* from the list. But then we're not using this as a loadable
|
||
|
* module anyway, so that's OK.
|
||
|
*
|
||
|
* Under SunOS, this should allow the module to be unloaded.
|
||
|
* Unfortunately, it doesn't seem to detach all the references,
|
||
|
* so your system may well crash after you unload this module :-(
|
||
|
*/
|
||
|
#ifndef __osf__
|
||
|
|
||
|
/*
|
||
|
* Remove an interface from the system.
|
||
|
* This routine contains magic.
|
||
|
*/
|
||
|
#include <net/route.h>
|
||
|
#include <netinet/in_pcb.h>
|
||
|
#include <netinet/ip_var.h>
|
||
|
#include <netinet/tcp.h>
|
||
|
#include <netinet/tcp_timer.h>
|
||
|
#include <netinet/tcp_var.h>
|
||
|
#include <netinet/udp.h>
|
||
|
#include <netinet/udp_var.h>
|
||
|
|
||
|
static void
|
||
|
ppp_if_detach(ifp)
|
||
|
struct ifnet *ifp;
|
||
|
{
|
||
|
int s;
|
||
|
struct inpcb *pcb;
|
||
|
struct ifaddr *ifa;
|
||
|
struct in_ifaddr **inap;
|
||
|
struct ifnet **ifpp;
|
||
|
|
||
|
s = splhigh();
|
||
|
|
||
|
/*
|
||
|
* Clear the interface from any routes currently cached in
|
||
|
* TCP or UDP protocol control blocks.
|
||
|
*/
|
||
|
for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
|
||
|
if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
|
||
|
in_losing(pcb);
|
||
|
for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
|
||
|
if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
|
||
|
in_losing(pcb);
|
||
|
|
||
|
/*
|
||
|
* Delete routes through all addresses of the interface.
|
||
|
*/
|
||
|
for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
|
||
|
rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
|
||
|
rtinit(ifa, ifa, SIOCDELRT, 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Unlink the interface's address(es) from the in_ifaddr list.
|
||
|
*/
|
||
|
for (inap = &in_ifaddr; *inap != 0; ) {
|
||
|
if ((*inap)->ia_ifa.ifa_ifp == ifp)
|
||
|
*inap = (*inap)->ia_next;
|
||
|
else
|
||
|
inap = &(*inap)->ia_next;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Delete the interface from the ifnet list.
|
||
|
*/
|
||
|
for (ifpp = &ifnet; (*ifpp) != 0; ) {
|
||
|
if (*ifpp == ifp)
|
||
|
break;
|
||
|
ifpp = &(*ifpp)->if_next;
|
||
|
}
|
||
|
if (*ifpp == 0)
|
||
|
printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
|
||
|
else
|
||
|
*ifpp = ifp->if_next;
|
||
|
|
||
|
splx(s);
|
||
|
}
|
||
|
|
||
|
#endif /* __osf__ */
|