#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/sysctl.h>
#include <kern/locks.h>
#include <net/if.h>
#include <netinet/in.h>
#include "if_ppplink.h" // public link API
#include "ppp_domain.h"
#include "ppp_defs.h" // public ppp values
#include "if_ppp.h" // public ppp API
#include "ppp_if.h"
#include "ppp_link.h"
#define TYPE_IF 1
#define TYPE_LINK 2
int ppp_proto_attach(struct socket *, int, struct proc *);
int ppp_proto_detach(struct socket *so);
int ppp_proto_connect(struct socket *, struct sockaddr *, struct proc *);
int ppp_proto_ioctl(struct socket *, u_long cmd, caddr_t , struct ifnet *, struct proc *);
int ppp_proto_send(struct socket *, int , struct mbuf * , struct sockaddr *, struct mbuf *, struct proc *);
struct domain ppp_domain =
{ PF_PPP, PPP_NAME, NULL,
NULL, NULL, NULL, NULL, NULL,
0, 0, 0, 0
};
struct pr_usrreqs ppp_usr =
{ pru_abort_notsupp, pru_accept_notsupp, ppp_proto_attach, pru_bind_notsupp,
ppp_proto_connect, pru_connect2_notsupp, ppp_proto_ioctl, ppp_proto_detach,
pru_disconnect_notsupp, pru_listen_notsupp, pru_peeraddr_notsupp,
pru_rcvd_notsupp, pru_rcvoob_notsupp, ppp_proto_send,
pru_sense_null, pru_shutdown_notsupp, pru_sockaddr_notsupp,
sosend, soreceive, pru_sopoll_notsupp
};
struct protosw ppp_proto =
{ SOCK_RAW, &ppp_domain, PPPPROTO_CTL, PR_ATOMIC|PR_CONNREQUIRED|PR_PROTOLOCK,
NULL, NULL, NULL, NULL,
NULL, NULL,
NULL, NULL, NULL, NULL, &ppp_usr
};
u_char pppproto_inited = 0;
lck_mtx_t *ppp_domain_mutex;
SYSCTL_NODE(_net, PF_PPP, ppp, CTLFLAG_RW, 0, "");
int ppp_domain_init()
{
net_add_domain(&ppp_domain);
ppp_domain_mutex = ppp_domain.dom_mtx;
ppp_domain.dom_flags = DOM_REENTRANT;
sysctl_register_oid(&sysctl__net_ppp);
return 0;
}
int ppp_proto_add()
{
int ret;
ret = net_add_proto(&ppp_proto, &ppp_domain);
if (ret) {
net_del_domain(&ppp_domain);
LOGRETURN(ret, ret, "ppp_proto_add : can't add proto to PPP domain, error = 0x%x\n");
}
pppproto_inited = 1;
return 0;
}
int ppp_proto_remove()
{
int ret;
if (pppproto_inited) {
ret = net_del_proto(ppp_proto.pr_type, ppp_proto.pr_protocol, ppp_proto.pr_domain);
LOGRETURN(ret, ret, "ppp_domain_terminate : can't del proto from PPP domain, error = 0x%x\n");
pppproto_inited = 0;
}
return 0;
}
int ppp_domain_dispose()
{
int ret;
ret = net_del_domain(&ppp_domain);
LOGRETURN(ret, ret, "ppp_domain_terminate : can't del PPP domain, error = 0x%x\n");
sysctl_unregister_oid(&sysctl__net_ppp);
return 0;
}
int ppp_proto_attach (struct socket *so, int proto, struct proc *p)
{
int error = 0;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0)
error = soreserve(so, 8192, 8192);
return error;
}
int ppp_proto_detach(struct socket *so)
{
ppp_proto_free(so);
return 0;
}
int ppp_proto_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
{
soisconnected(so);
return 0;
}
int ppp_proto_ioctl(struct socket *so, u_long cmd, caddr_t data,
struct ifnet *ifp, struct proc *p)
{
int error = 0;
u_int16_t unit;
switch (cmd) {
case PPPIOCNEWUNIT:
unit = *(u_int32_t *)data;
error = ppp_if_attach(&unit);
if (error)
return error;
*(u_int32_t *)data = unit;
case PPPIOCATTACH:
unit = *(u_int32_t *)data;
error = ppp_if_attachclient(unit, so, (ifnet_t *)&so->so_pcb);
if (!error)
so->so_tpcb = (caddr_t)TYPE_IF;
break;
case PPPIOCATTCHAN:
unit = *(u_int32_t *)data;
error = ppp_link_attachclient(unit, so, (struct ppp_link **)&so->so_pcb);
if (!error)
so->so_tpcb = (caddr_t)TYPE_LINK;
break;
case PPPIOCDETACH:
ppp_proto_free(so);
break;
default:
switch ((u_int32_t)so->so_tpcb) {
case TYPE_IF:
error = ppp_if_control((ifnet_t)so->so_pcb, cmd, data);
break;
case TYPE_LINK:
error = ppp_link_control((struct ppp_link *)so->so_pcb, cmd, data);
break;
default:
error = EINVAL;
}
}
return error;
}
int ppp_proto_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *nam, struct mbuf *control, struct proc *p)
{
int error = 0;
switch ((u_int32_t)so->so_tpcb) {
case TYPE_IF:
error = ppp_if_send((ifnet_t)so->so_pcb, (mbuf_t)m);
break;
case TYPE_LINK:
error = ppp_link_send((struct ppp_link *)so->so_pcb, (mbuf_t)m);
break;
default:
error = EINVAL;
if (m)
mbuf_freem(m);
}
if (control)
mbuf_freem(control);
return error;
}
void ppp_proto_free(void *data)
{
struct socket *so = (struct socket *)data;
lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
if (!so)
return;
switch ((u_int32_t)so->so_tpcb) {
case TYPE_IF:
ppp_if_detachclient((ifnet_t)so->so_pcb, so);
break;
case TYPE_LINK:
ppp_link_detachclient((struct ppp_link *)so->so_pcb, so);
break;
}
so->so_pcb = 0;
so->so_tpcb = 0;
so->so_flags |= SOF_PCBCLEARING;
}
int ppp_proto_input(void *data, mbuf_t m)
{
struct socket *so = (struct socket *)data;
lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
mbuf_setflags(m, mbuf_flags(m) | MBUF_EOR);
if (!so || sbspace(&so->so_rcv) < mbuf_pkthdr_len(m)) {
if (so)
log(LOGVAL, "ppp_proto_input no space, so = 0x%x, len = %d\n", so, mbuf_pkthdr_len(m));
mbuf_freem(m);
return 0;
}
sbappendrecord(&so->so_rcv, (struct mbuf*)m);
sorwakeup(so);
return 0;
}
int ppp_qfull(struct pppqueue *pppq)
{
return pppq->len >= pppq->maxlen;
}
void ppp_drop(struct pppqueue *pppq)
{
pppq->drops++;
}
void ppp_enqueue(struct pppqueue *pppq, mbuf_t m)
{
mbuf_setnextpkt(m, 0);
if (pppq->tail == 0)
pppq->head = m;
else
mbuf_setnextpkt(pppq->tail, m);
pppq->tail = m;
pppq->len++;
}
mbuf_t ppp_dequeue(struct pppqueue *pppq)
{
mbuf_t m = pppq->head;
if (m) {
if ((pppq->head = mbuf_nextpkt(m)) == 0)
pppq->tail = 0;
mbuf_setnextpkt(m, 0);
pppq->len--;
}
return m;
}
void ppp_prepend(struct pppqueue *pppq, mbuf_t m)
{
mbuf_setnextpkt(m, pppq->head); \
if (pppq->tail == 0)
pppq->tail = m;
pppq->head = m;
pppq->len++;
}