home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR3
/
KA9Q212.ZIP
/
IPFILTER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-16
|
12KB
|
632 lines
/*
* Packet filtering code for KA9Q.
*
* Copyright 1992 David F. Mischler
* This code may be freely distributed as long as this copyright
* notice is preserved.
*/
/****************************************************************************
* $Id: ipfilter.c 1.2 93/07/16 11:45:46 ROOT_DOS Exp $
* 14 Jun 93 1.2 GT Fix warnings. *
****************************************************************************/
#include <limits.h>
#ifndef _FILTER_H
#include "filter.h"
#endif
#ifndef _IFACE_H
#include "iface.h"
#endif
#ifndef _ICMP_H
#include "icmp.h"
#endif
#ifndef _IP_H
#include "ip.h"
#endif
#ifndef _NETUSER_H
#include "netuser.h"
#endif
#include "socket.h"
#define SDWIDTH 21 /* Width of a displayed source or destination */
/*
* Information for TCP header kludges.
*/
#define CODEBITS_ACK 16
#define CODEBITS_OFFSET 13
#define CODEBITS_SYN 2
#define SRC_PORT_OFFSET 0 /* TCP & UDP */
#define DST_PORT_OFFSET 2 /* TCP & UDP */
/*
* Function to return a non-zero value if an IP packet
* matches a filter entry.
*/
static
int
pkt_ip( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
/*
* If source address doesn't match then get out.
*/
if ( fp->src.addr == ( ip->source & fp->src.mask ) ) {
if ( fp->src.exclude )
return 0;
}
else {
if ( fp->src.exclude == 0 )
return 0;
}
/*
* If destination address doesn't match then get out.
*/
if ( fp->dest.addr == ( ip->dest & fp->dest.mask ) ) {
if ( fp->dest.exclude )
return 0;
}
else {
if ( fp->dest.exclude == 0 )
return 0;
}
return 1;
}
/*
* Function to return a non-zero value if an ICMP packet
* matches a filter entry.
*/
static
int
pkt_icmp( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
/*
* Check protocol type.
*/
if ( ip->protocol != ICMP_PTCL )
return 0;
/*
* Check source & destination addresses.
*/
return pkt_ip( bp, ip, fp );
}
/*
* Function to return a non-zero value if an ICMP REDIRECT
* packet matches a filter entry.
*/
static
int
pkt_icmprd( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
/*
* Check protocol type and source & destination addresses.
*/
if ( pkt_icmp( bp, ip, fp ) == 0 )
return 0;
/*
* Check ICMP message type.
*/
if ( bp->data[ 0 ] == ICMP_REDIRECT )
return 1;
return 0;
}
/*
* Function to return a non-zero value if an ICMP packet
* other than a REDIRECT matches a filter entry.
*/
static
int
pkt_icmpxrd( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
/*
* Check protocol type and source & destination addresses.
*/
if ( pkt_icmp( bp, ip, fp ) == 0 )
return 0;
/*
* Check ICMP message type.
*/
if ( bp->data[ 0 ] != ICMP_REDIRECT )
return 1;
return 0;
}
/*
* Function to return a non-zero value if a TCP packet
* matches a filter entry.
*/
static
int
pkt_tcp( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
unsigned short port;
/*
* Check protocol type.
*/
if ( ip->protocol != TCP_PTCL )
return 0;
/*
* Check source & destination addresses.
*/
if ( pkt_ip( bp, ip, fp ) == 0 )
return 0;
/*
* Check source port.
*/
if ( fp->src.port ) {
port = get16( &bp->data[SRC_PORT_OFFSET] );
if ( fp->src.port > port || fp->src.hiport < port )
return 0;
}
/*
* Check destination port.
*/
if ( fp->dest.port ) {
port = get16( &bp->data[DST_PORT_OFFSET] );
if ( fp->dest.port > port || fp->dest.hiport < port )
return 0;
}
return 1;
}
/*
* Function to return a non-zero value if a TCP packet
* with SYN set and ACK clear matches a filter entry.
*/
static
int
pkt_tcpsyn( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
unsigned char codebits;
/*
* Check for TCP protocol type and matching addresses.
*/
if ( pkt_tcp( bp, ip, fp ) == 0 )
return 0;
/*
* Check that SYN is set and ACK is clear.
*/
codebits = bp->data[ CODEBITS_OFFSET ];
if ( ( codebits & CODEBITS_SYN ) && ( codebits & CODEBITS_ACK ) == 0 )
return 1;
return 0;
}
/*
* Function to return a non-zero value if a TCP packet
* with SYN clear or ACK set matches a filter entry.
*/
static
int
pkt_tcpxsyn( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
unsigned char codebits;
/*
* Check for TCP protocol type and matching addresses.
*/
if ( pkt_tcp( bp, ip, fp ) == 0 )
return 0;
/*
* Check that SYN is clear or ACK is set.
*/
codebits = bp->data[ CODEBITS_OFFSET ];
if ( ( codebits & CODEBITS_SYN ) == 0 || ( codebits & CODEBITS_ACK ) )
return 1;
return 0;
}
/*
* Function to return a non-zero value if a UDP packet
* matches a filter entry.
*/
static
int
pkt_udp( struct mbuf *bp, struct ip *ip, struct filter *fp )
{
unsigned short port;
/*
* Check protocol type.
*/
if ( ip->protocol != UDP_PTCL )
return 0;
/*
* Check source & destination addresses.
*/
if ( pkt_ip( bp, ip, fp ) == 0 )
return 0;
/*
* Check source port.
*/
if ( fp->src.port ) {
port = get16( &bp->data[SRC_PORT_OFFSET] );
if ( fp->src.port > port || fp->src.hiport < port )
return 0;
}
/*
* Check destination port.
*/
if ( fp->dest.port ) {
port = get16( &bp->data[DST_PORT_OFFSET] );
if ( fp->dest.port > port || fp->dest.hiport < port )
return 0;
}
return 1;
}
/*
* Table of filter actions (indexes must match FILTER_ACTION_*).
*/
static char *Acts[] = {
"?", "deny", "permit"
};
/*
* Table of packet type names and matching functions.
*/
static struct {
char *name;
int (*function) __ARGS((struct mbuf *bp, struct ip *ip, struct filter *fp));
} Types[] = {
{ "*", pkt_ip }, /* Any IP packet */
{ "icmp", pkt_icmp }, /* Any ICMP packet */
{ "icmprd", pkt_icmprd }, /* ICMP redirect */
{ "icmpxrd", pkt_icmpxrd }, /* ICMP except redirect */
{ "tcp", pkt_tcp }, /* Any TCP packet */
{ "tcpsyn", pkt_tcpsyn }, /* TCP SYN packet */
{ "tcpxsyn", pkt_tcpxsyn }, /* TCP except SYN */
{ "udp", pkt_udp }, /* Any UDP packet */
{ NULL, NULL }
};
/*
* Function to parse a filter source or destination spec.
* A non-zero return value indicates a syntax problem.
*/
static
int
sdparse( char *p, struct filtersd *sdp )
{
char *addrp; /* Pointer to address specification */
char *bitp; /* Pointer to bit count */
sdp->addr = 0L;
sdp->mask = ~0L;
sdp->bits = 32;
sdp->exclude = 0;
sdp->port = 0;
sdp->hiport = 0;
/*
* Look for '!' in address to specify address exclusion.
*/
if ( *p == '!' ) {
p += 1;
sdp->exclude = 1;
}
addrp = p;
/*
* Look for '/' in spec to separate number of bits.
*/
if ((bitp = strchr( p, '/' )) != 0) {
*bitp++ = '\0';
p = bitp;
}
/*
* Look for ':' in spec to separate port number.
*/
if ((p = strchr( p, ':' )) != 0) {
*p++ = '\0';
sdp->hiport = sdp->port = atoi( p );
/*
* '+' in port spec indicates >= port.
*/
if ( strchr( p, '+' ) )
sdp->hiport = USHRT_MAX;
/*
* '-' in port spec indicates port range.
*/
else if ((p = strchr( p, '-' )) != 0) {
p += 1;
sdp->hiport = atoi( p );
}
if ( sdp->port > sdp->hiport ) {
tprintf( "Bad port range\n" );
return -1;
}
}
/*
* Evaluate number of bits if necessary.
*/
if ( bitp )
sdp->mask <<= ( 32 - (sdp->bits = atoi( bitp )) );
/*
* Evaluate host/net address.
*/
if ( strcmp( "*", addrp ) == 0 ) {
sdp->bits = 0;
sdp->mask = 0L;
}
else {
if ( ( sdp->addr = resolve( addrp ) ) == 0 ) {
tprintf( Badhost, addrp );
return -1;
}
}
sdp->addr &= sdp->mask;
return 0;
}
/*
* Function to list a source or destination address.
*/
static
void
listaddr( struct filtersd *sdp )
{
int i = 0;
if ( sdp->exclude ) {
tputc( '!' );
i = 1;
}
if ( sdp->addr == 0L && sdp->bits == 0 ) {
tputc( '*' );
i += 1;
}
else {
i += tprintf( "%s", inet_ntoa( sdp->addr ) );
if ( sdp->bits != 32 )
i += tprintf( "/%d", sdp->bits );
}
if ( sdp->port ) {
i += tprintf( ":%u", sdp->port );
if ( sdp->hiport == USHRT_MAX ) {
tputc( '+' );
i += 1;
}
else if ( sdp->hiport > sdp->port )
i += tprintf( "-%u", sdp->hiport );
}
for ( ; i < SDWIDTH ; i++ )
tputc( ' ' );
}
/*
* Function to list a single filter entry.
*/
static
void
listfilter( struct filter *fp, char *iface, char *direct )
{
int i;
tprintf( "%s %-6s %-3s ", iface, Acts[fp->action], direct );
for ( i = 0 ; Types[i].name ; i++ ) {
if ( Types[i].function == fp->type ) {
tprintf( "%-7s ", Types[i].name );
break;
}
}
listaddr( &fp->src );
tputc( ' ' );
listaddr( &fp->dest );
tprintf( " %lu\n", fp->matches );
}
/*
* Function to process the IP FILTER command.
*/
int
doipfilter( int argc, char *argv[], void *p )
{
int action;
struct filter *fp; /* Filter entry pointer */
struct filter **fpp; /* Pointer to filter entry pointer */
struct iface *ifp; /* Interface structure pointer */
int i;
int (*type)();
struct filtersd src,dest;
/*
* Make sure interface is good.
*/
if (( ifp = if_lookup( argv[1] ) ) == NULLIF ) {
tprintf( "Interface \"%s\" unknown\n", argv[1] );
return 1;
}
/*
* Check action.
*/
if ( strcmp(argv[2],"delete") == 0 ) {
/*
* Delete entire filter set.
*/
while ( ifp->infilter ) {
fp = ifp->infilter;
ifp->infilter = fp->next;
free( (char *) fp );
}
while ( ifp->outfilter ) {
fp = ifp->outfilter;
ifp->outfilter = fp->next;
free( (char *) fp );
}
return 0;
}
else if ( strcmp(argv[2],"list") == 0 ) {
/*
* List entire filter set.
*/
fp = ifp->infilter;
while ( fp ) {
listfilter( fp, ifp->name, "in" );
fp = fp->next;
}
fp = ifp->outfilter;
while ( fp ) {
listfilter( fp, ifp->name, "out" );
fp = fp->next;
}
return 0;
}
else if ( strcmp(argv[2],"deny") == 0 ) {
action = FILTER_ACTION_DENY;
}
else if ( strcmp(argv[2],"permit") == 0 ) {
action = FILTER_ACTION_PERMIT;
}
else {
tprintf( "Unknown action \"%s\"\n", argv[2] );
return 2;
}
/*
* Complain if not enough arguments.
*/
if ( argc < 7 ) {
tprintf("ip filter <iface> <act> <dir> <type> <src> <dest>\n");
return -1;
}
/*
* Check direction.
*/
if ( strncmp(argv[3],"in",2) == 0 ) {
fpp = &ifp->infilter;
}
else if ( strncmp(argv[3],"out",3) == 0 ) {
fpp = &ifp->outfilter;
}
else {
tprintf( "Unknown direction \"%s\"\n", argv[3] );
return 3;
}
/*
* Check packet type.
*/
for ( i = 0 ; Types[i].name ; i++ ) {
if ( strcmp(argv[4],Types[i].name) == 0 ) {
type = Types[i].function;
break;
}
}
if ( Types[i].name == NULL ) {
tprintf( "Unknown packet type \"%s\"\n", argv[4] );
return 4;
}
/*
* Parse source specification.
*/
if ( sdparse( argv[5], &src ) )
return 5;
/*
* Parse destination specification.
*/
if ( sdparse( argv[6], &dest ) )
return 6;
/*
* Append filter entry to list.
*/
while ( *fpp )
fpp = &((*fpp)->next);
*fpp = fp = (struct filter *) callocw( 1, sizeof( struct filter ) );
fp->action = action;
fp->type = type;
fp->src = src;
fp->dest = dest;
return 0;
}
/*
* Function to apply a filter specification to a packet.
* A non-zero return indicates that the packet should be dropped.
*/
int
ip_filter(struct mbuf *bp, struct ip *ip, struct filter *fp)
{
/*
* Pass all IP fragments except the first.
*/
if ( ip->offset != 0 )
return 0;
/*
* Walk the filter list until an entry matches the packet.
*/
for ( ; fp ; fp = fp->next ) {
/*
* If packet doesn't match try the next entry.
*/
if ( (*fp->type)( bp, ip, fp ) == 0 )
continue;
fp->matches += 1;
/*
* Take specified action.
*/
if ( fp->action == FILTER_ACTION_PERMIT )
return 0;
return -1;
}
return 1; /* Deny all packets not explicitly permitted */
}