u-isdn/ip_mon/ip_mon.c

603 lines
12 KiB
C

/* Streams module to do logging+blocking of IP packet addresses */
#define UAREA
#include "f_module.h"
#include "primitives.h"
#include "f_ip.h"
#include <sys/types.h>
#include <sys/time.h>
#include "f_signal.h"
#include "f_malloc.h"
#include <sys/param.h>
#include <sys/sysmacros.h>
#include "streams.h"
#include <sys/stropts.h>
#ifdef DONT_ADDERROR
#include "f_user.h"
#endif
#include <sys/errno.h>
#include "ip_mon.h"
#include "streamlib.h"
#include "isdn_proto.h"
#include "vanj.h"
#include "ppp.h"
static struct module_info ip_mon_minfo =
{
0, "ip_mon", 0, INFPSZ, 4096,1024
};
static qf_open ip_mon_open;
static qf_close ip_mon_close;
static qf_put ip_mon_rput, ip_mon_wput;
static qf_srv ip_mon_rsrv, ip_mon_wsrv;
static struct qinit ip_mon_rinit =
{
ip_mon_rput, ip_mon_rsrv, ip_mon_open, ip_mon_close, NULL, &ip_mon_minfo, NULL
};
static struct qinit ip_mon_winit =
{
ip_mon_wput, ip_mon_wsrv, NULL, NULL, NULL, &ip_mon_minfo, NULL
};
struct streamtab ip_moninfo =
{&ip_mon_rinit, &ip_mon_winit, NULL, NULL};
struct streamtab ip_mon2info =
{&ip_mon_rinit, &ip_mon_winit, NULL, NULL};
#define NIP_MON 8
#define NIP_INFO 40
struct _ip_mon {
queue_t *qptr;
int timer;
#ifdef NEW_TIMEOUT
int timeout;
#endif
short nr;
unsigned hastimer:1;
unsigned encap:2;
#define ENCAP_NONE 0
#define ENCAP_ETHER 1
#define ENCAP_PPP 2
} ip_mon;
static mblk_t *ip_info[NIP_INFO];
static int last_index = -1;
static int nexti = 0;
static int
ip_mon_index (unsigned long local, unsigned long remote, int protocol, int *dosend, ushort_t localp, ushort_t remotep)
{
mblk_t *mb;
int i;
struct _monitor *mon;
#define CMP(_m) ( \
(_m)->local == local && \
(_m)->remote == remote && \
(protocol == 0 || _m->p_protocol == 0 || \
(_m->p_protocol == protocol && \
(_m->p_local == localp || _m->p_local == 0 || localp == 0) && \
(_m->p_remote == remotep || _m->p_remote == 0 || remotep == 0)) \
) \
)
if (last_index >= 0) {
mon = (struct _monitor *) ip_info[last_index]->b_rptr;
if (CMP(mon))
return last_index;
}
for (i = 0; i < NIP_INFO; i++) {
if(i == last_index)
continue;
if ((mb = ip_info[i]) == NULL) {
nexti = i;
break;
}
mon = (struct _monitor *) mb->b_rptr;
if (CMP(mon))
return last_index = i;
}
if ((mb = allocb (sizeof (struct _monitor), BPRI_MED)) == NULL)
return -1;
mon = ((struct _monitor *) mb->b_wptr)++;
bzero (mon, sizeof (struct _monitor));
mon->local = local;
mon->remote = remote;
mon->p_protocol = protocol;
mon->p_local = localp;
mon->p_remote = remotep;
mon->cap_b = ~0;
mon->cap_p = ~0;
if (ip_info[i = last_index = nexti] != NULL) {
struct _monitor *m2 = (struct _monitor *)(ip_info[i]->b_rptr);
if ((m2->sofar_b != 0 || m2->sofar_p != 0) && ip_mon.qptr != NULL && canput (ip_mon.qptr->q_next))
putnext (ip_mon.qptr, ip_info[i]);
else
freemsg (ip_info[i]);
}
ip_info[i] = mb;
if (++nexti >= NIP_INFO)
nexti = 0;
if (dosend != NULL)
*dosend = 1;
return i;
}
static void
ip_mon_sendup (int i)
{
mblk_t *mb, *mb2;
struct _monitor *m2;
if (ip_mon.qptr == NULL || !canput (ip_mon.qptr->q_next) || (mb = ip_info[i]) == NULL)
return;
m2 = (struct _monitor *)(mb->b_rptr);
if (m2->sofar_b == 0 && m2->sofar_p == 0)
return;
mb2 = copyb (mb);
if (mb2 != 0) {
struct _monitor *m = (struct _monitor *) mb->b_rptr;
putnext (ip_mon.qptr, mb2);
m->sofar_b = 0;
m->sofar_p = 0;
}
}
static void
ip_mon_proto (queue_t * q, mblk_t * mp, char senddown)
{
register struct _ip_mon *ipmon = (struct _ip_mon *) q->q_ptr;
char *origmp = mp->b_rptr;
ushort_t id;
int error = 0;
if (m_getid (mp, &id) != 0) {
mp->b_rptr = origmp;
putnext (q, mp);
return;
}
switch (id) {
default:
break;
case PROTO_DISCONNECT:{
int i;
for (i = 0; i < NIP_INFO; i++)
ip_mon_sendup (i);
} break;
case PROTO_MODULE:
if (strnamecmp (q, mp)) {
long x;
#if 0
if (ipmon->flags & IP_MON_CONN)
goto err;
#endif
while (mp != NULL && m_getsx (mp, &id) == 0) {
switch (id) {
default:
goto err;
case PROTO_MODULE:
break;
case PROTO_TYPE_NONE:
ipmon->encap = ENCAP_NONE;
break;
case PROTO_TYPE_PPP:
ipmon->encap = ENCAP_PPP;
break;
case PROTO_TYPE_ETHER:
ipmon->encap = ENCAP_ETHER;
break;
case IP_MON_TIMEOUT:
if ((error = m_geti (mp, &x)) != 0)
goto err;
if (x < 2 || x > 99999)
goto err;
ipmon->timer = x;
break;
}
}
if (mp != NULL) {
mp->b_rptr = origmp;
m_reply (q,mp,0);
mp = NULL;
}
}
}
if (mp != NULL) {
if (origmp != NULL)
mp->b_rptr = origmp;
putnext (q, mp);
}
return;
err:
mp->b_rptr = origmp;
m_reply (q, mp, error ? error : -EINVAL);
}
static void
ip_mon_timer (struct _ip_mon *ipmon)
{
long s;
if (ipmon->timer == 0)
return;
if (ip_mon.qptr == NULL) {
ipmon->timer = 0;
return;
}
s = splstr();
ip_mon_sendup (nexti++);
if (nexti >= NIP_INFO)
nexti = 0;
#ifdef NEW_TIMEOUT
ipmon->timeout =
#endif
timeout ((void *)ip_mon_timer, ipmon, ipmon->timer * HZ);
splx(s);
}
static int
ip_mon_open (queue_t * q, dev_t dev, int flag, int sflag ERR_DECL)
{
struct _ip_mon *ipmon;
static int nr = 1;
int do_timeout = 1;
dev = minor (dev);
if (sflag == MODOPEN) {
ipmon = malloc(sizeof(*ipmon));
if(ipmon == NULL)
ERR_RETURN(-ENOMEM);
} else if (ip_mon.qptr != NULL) {
printf ("IP_MON: Master already open\n");
ERR_RETURN(-EBUSY);
} else if (dev != 0 || sflag == CLONEOPEN) {
printf ("IP_MON: Bad minor number: dev %d, sflag %d\n", dev, sflag);
ERR_RETURN(-ENXIO);
} else {
ipmon = &ip_mon;
do_timeout = (ip_mon.qptr == NULL);
}
memset(ipmon,0,sizeof (*ipmon));
if (ipmon != &ip_mon)
ipmon->nr = nr++;
WR (q)->q_ptr = (char *) ipmon;
q->q_ptr = (char *) ipmon;
ipmon->qptr = q;
ipmon->timer = 10;
if(do_timeout) {
ipmon->hastimer = 1;
#ifdef NEW_TIMEOUT
ipmon->timeout =
#endif
timeout ((void *)ip_mon_timer, ipmon, HZ * ipmon->timer);
}
printf ("IP_MON driver %d opened.\n", ipmon->nr);
MORE_USE;
return 0;
}
static void
ip_mon_wput (queue_t * q, mblk_t * mp)
{
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
case CASE_DATA:
putq (q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq (q, FLUSHDATA);
putnext (q, mp);
break;
default:
putnext (q, mp);
break;
}
return;
}
static mblk_t *count_packet (struct _ip_mon *ipmon, mblk_t *mp, char doswap)
{
mblk_t *mq;
struct ip *ipp;
struct _monitor *mon;
int i, send = 0;
int ms;
ushort_t localp, remotep;
if(dsize(mp) < sizeof(struct ip)) {
printf("ip_%c: size %d\n",doswap?'r':'w',dsize(mp));
freemsg(mp);
return NULL;
}
if(ipmon->encap) {
short encap;
mq = pullupm (mp, 2); /* encap header */
if (mq == NULL)
return mp;
encap = *(short *)(mq->b_rptr);
if((ipmon->encap == ENCAP_PPP && encap != htons(PPP_IP)) ||
(ipmon->encap == ENCAP_ETHER && encap != htons(ETH_P_IP)))
return mq;
mp = mq;
}
mq = pullupm (mp, sizeof (struct ip) + 2); /* IP header */
if (mq == NULL) {
return mp; /* oh well -- don't count it */
}
ms = splstr();
ipp = (struct ip *) (mq->b_rptr+ipmon->encap ? 2 : 0);
switch (ipp->ip_p) {
default:
localp = remotep = 0;
break;
case IPPROTO_TCP:
mp = pullupm (mq, (ipp->ip_hl << 2) + sizeof (struct tcphdr)+2);
if (mp != NULL) {
struct tcphdr *tcp;
mq = mp;
ipp = (struct ip *) (mq->b_rptr+ipmon->encap ? 2 : 0);
tcp = (struct tcphdr *) (((char *)ipp) + (ipp->ip_hl << 2));
localp = tcp->th_sport;
remotep = tcp->th_dport;
} else
return mq;
break;
case IPPROTO_UDP:
mp = pullupm (mq, (ipp->ip_hl << 2) + sizeof (struct udphdr)+2);
if (mp != NULL) {
struct udphdr *udp;
mq = mp;
ipp = (struct ip *) (mq->b_rptr+ipmon->encap ? 2 : 0);
udp = (struct udphdr *) (((char *)ipp) + (ipp->ip_hl << 2));
localp = udp->uh_sport;
remotep = udp->uh_dport;
} else
return mq;
break;
case IPPROTO_ICMP:
mp = pullupm (mq, (ipp->ip_hl << 2) + sizeof (struct icmp) + 2);
if(mp != NULL) {
struct icmp *icm;
mq = mp;
ipp = (struct ip *) (mq->b_rptr+ipmon->encap ? 2 : 0);
icm = (struct icmp *) (((char *)ipp) + (ipp->ip_hl << 2));
if(doswap) {
localp = htons(icm->icmp_code);
remotep= htons(icm->icmp_type);
} else {
localp = htons(icm->icmp_type);
remotep= htons(icm->icmp_code);
}
} else
return mq;
break;
}
if(doswap)
i = ip_mon_index (ADR(ipp->ip_dst), ADR(ipp->ip_src), ipp->ip_p, &send,remotep,localp);
else
i = ip_mon_index (ADR(ipp->ip_src), ADR(ipp->ip_dst), ipp->ip_p, &send,localp,remotep);
if (i < 0) {
splx (ms);
return mq;
}
mon = (struct _monitor *) ip_info[i]->b_rptr;
mon->sofar_b += dsize (mq);
mon->sofar_p++;
if (mon->sofar_b > mon->cap_b || mon->sofar_p > mon->cap_p) {
splx (ms);
printf ("Drop packet %lx %lx\n", ntohl(ADR(ipp->ip_src)), ntohl(ADR(ipp->ip_dst)));
freemsg (mq);
return NULL;
}
splx (ms);
if (send)
ip_mon_sendup (i);
return mq;
}
static void
ip_mon_wsrv (queue_t * q)
{
mblk_t *mp;
struct _ip_mon *ipmon = (struct _ip_mon *) q->q_ptr;
if (q == ip_mon.qptr) {
while ((mp = getq (q)) != NULL) {
struct _monitor *mon;
int i;
int ms = splstr ();
if (mp->b_rptr - mp->b_wptr != sizeof (struct _monitor)
|| mp->b_cont != NULL) {
freemsg (mp);
putctlerr (RD (q), -EIO);
flushq (q, FLUSHDATA);
splx (ms);
return;
}
mon = (struct _monitor *) mp->b_rptr;
i = ip_mon_index (mon->local, mon->remote, mon->p_protocol, NULL,mon->p_local,mon->p_remote);
if (i < 0)
freemsg (mp);
else {
if (ip_info[i] != NULL)
qreply (q, ip_info[i]);
ip_info[i] = mp;
}
splx (ms);
}
} else {
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
ip_mon_proto (q, mp, 1);
break;
case CASE_DATA:
{
mblk_t *mq;
if(!canput(q->q_next)) {
putbq(q,mp);
return;
}
mq = count_packet(ipmon, mp,0);
if(mq != NULL)
putnext (q, mq);
}
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq (q, FLUSHDATA);
/* FALL THRU */
default:
putnext (q, mp);
continue;
}
}
}
return;
}
static void
ip_mon_close (queue_t * q, int dummy)
{
struct _ip_mon *ipmon = (struct _ip_mon *) q->q_ptr;
flushq (q, FLUSHALL);
flushq (WR (q), FLUSHALL);
printf ("IP_MON driver %d closed.\n", ipmon->nr);
if (ipmon->hastimer) {
#ifdef NEW_TIMEOUT
untimeout (ipmon->timeout);
#else
untimeout (ip_mon_timer, ipmon);
#endif
}
ipmon->qptr = NULL;
if(ipmon != &ip_mon)
free(ipmon);
LESS_USE;
return;
}
static void
ip_mon_rput (queue_t * q, mblk_t * mp)
{
switch (DATA_TYPE(mp)) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHR) {
flushq (q, FLUSHDATA);
}
putnext (q, mp); /* send it along too */
break;
case MSG_PROTO:
case CASE_DATA:
putq (q, mp); /* queue it for my service routine */
break;
case M_HANGUP:
case M_ERROR:
default:
putnext (q, mp); /* don't know what to do with this, so send
* it along */
}
return;
}
static void
ip_mon_rsrv (queue_t * q)
{
mblk_t *mp;
struct _ip_mon *ipmon = (struct _ip_mon *) q->q_ptr;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
ip_mon_proto (q, mp, 0);
break;
case CASE_DATA:
{
mblk_t *mq;
if(!canput(q->q_next)) {
putbq(q,mp);
return;
}
mq = count_packet(ipmon, mp,1);
if(mq != NULL)
putnext (q, mq);
}
break;
default:
putnext (q, mp);
continue;
}
}
return;
}
#ifdef MODULE
static int devmajor = 0;
static int do_init_module(void)
{
int err;
err = register_strmod(&ip_moninfo);
if (err < 0)
return err;
err = register_strdev(0,&ip_moninfo,0);
if(err < 0) {
unregister_strmod(&ip_moninfo);
return err;
}
devmajor = err;
return 0;
}
static int do_exit_module(void)
{
int err1 = unregister_strmod(&ip_moninfo);
int err2 = unregister_strdev(devmajor,&ip_moninfo,0);
return err1 || err2;
}
#endif