ip_frag.c revision 8128a42d18f03617e333c5aa4e3219cb31a28f48
/*
* Copyright (C) 1993-2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
# define KERNEL 1
# define _KERNEL 1
#endif
#ifdef __hpux
#endif
#if !defined(_KERNEL)
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# define _KERNEL
# ifdef __OpenBSD__
struct file;
# endif
#endif
#else
#endif
#if !defined(linux)
#endif
#if defined(_KERNEL)
# endif
#endif
# endif
#else
# include <sys/byteorder.h>
# ifdef _KERNEL
# include <sys/dditypes.h>
# endif
#endif
#ifdef sun
#endif
#include <netinet/in_systm.h>
#if !defined(linux)
#endif
#include "netinet/ip_compat.h"
#include "netinet/ip_state.h"
#include "netinet/ipf_stack.h"
#if (__FreeBSD_version >= 300000)
# if defined(_KERNEL)
# ifndef IPFILTER_LKM
# endif
extern struct callout_handle fr_slowtimer_ch;
# endif
#endif
extern struct callout fr_slowtimer_ch;
#endif
#if defined(__OpenBSD__)
extern struct timeout fr_slowtimer_ch;
#endif
/* END OF INCLUDES */
#if !defined(lint)
#endif
/* ------------------------------------------------------------------------ */
/* Function: fr_fraginit */
/* Returns: int - 0 == success, -1 == error */
/* Parameters: Nil */
/* */
/* Initialise the hash tables for the fragment cache lookups. */
/* ------------------------------------------------------------------------ */
int fr_fraginit(ifs)
{
/* the IP frag related variables are set in ipftuneable_setdefs() to
* their default values
*/
return -1;
return -1;
return -1;
/* Initialise frblock with "block in all" */
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fragunload */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Free all memory allocated whilst running and from initialisation. */
/* ------------------------------------------------------------------------ */
void fr_fragunload(ifs)
{
ifs->ifs_fr_frag_init = 0;
}
}
}
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fragstats */
/* Returns: ipfrstat_t* - pointer to struct with current frag stats */
/* Parameters: Nil */
/* */
/* Updates ipfr_stats with current information and returns a pointer to it */
/* ------------------------------------------------------------------------ */
{
return &ifs->ifs_ipfr_stats;
}
/* ------------------------------------------------------------------------ */
/* Function: ipfr_newfrag */
/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */
/* Parameters: fin(I) - pointer to packet information */
/* table(I) - pointer to frag table to add to */
/* */
/* Add a new entry to the fragment cache, registering it as having come */
/* through this box, with the result of the filter operation. */
/* ------------------------------------------------------------------------ */
{
return NULL;
return NULL;
if (pass & FR_FRSTRICT)
return NULL;
idx *= 127;
/*
* first, make sure it isn't already there...
*/
IPFR_CMPSZ)) {
return NULL;
}
/*
* allocate some memory, if possible, if not, just record that we
* failed to do so.
*/
return NULL;
}
}
/*
* Insert the fragment into the fragment table, copy the struct used
* in the search using bcopy rather than reassign each field.
* Set the ttl to the default.
*/
/*
* Compute the offset of the expected start of the next packet.
*/
if (off == 0) {
} else {
fra->ipfr_seen0 = 0;
fra->ipfr_firstend = 0;
}
ifs->ifs_ipfr_inuse++;
return fra;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_newfrag */
/* Returns: int - 0 == success, -1 == error */
/* Parameters: fin(I) - pointer to packet information */
/* */
/* Add a new entry to the fragment cache table based on the current packet */
/* ------------------------------------------------------------------------ */
{
if (ifs->ifs_fr_frag_lock != 0)
return -1;
}
return fra ? 0 : -1;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_nat_newfrag */
/* Returns: int - 0 == success, -1 == error */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT structure */
/* */
/* Create a new NAT fragment cache entry based on the current packet and */
/* the NAT structure for this "session". */
/* ------------------------------------------------------------------------ */
{
return 0;
}
return fra ? 0 : -1;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_ipid_newfrag */
/* Returns: int - 0 == success, -1 == error */
/* Parameters: fin(I) - pointer to packet information */
/* ipid(I) - new IP ID for this fragmented packet */
/* */
/* Create a new fragment cache entry for this packet and store, as a data */
/* pointer, the new IP ID value. */
/* ------------------------------------------------------------------------ */
{
if (ifs->ifs_fr_frag_lock)
return 0;
}
return fra ? 0 : -1;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fraglookup */
/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */
/* matching entry in the frag table, else NULL */
/* Parameters: fin(I) - pointer to packet information */
/* table(I) - pointer to fragment cache table to search */
/* */
/* Check the fragment cache to see if there is already a record of this */
/* packet with its filter result known. */
/* ------------------------------------------------------------------------ */
{
return NULL;
/*
* For fragments, we record protocol, packet id, TOS and both IP#'s
* (these should all be the same for all fragments of a packet).
*
* build up a hash value to index the table with.
*/
idx *= 127;
/*
* check the table, careful to only compare the right amount of data
*/
IPFR_CMPSZ)) {
/*
* We don't want to let short packets match because
* they could be compromising the security of other
* rules that want to match on layer 4 fields (and
* can't because they have been fragmented off.)
* Why do this check here? The counter acts as an
* indicator of this kind of attack, whereas if it was
* elsewhere, it wouldn't know if other matching
* packets had been seen.
*/
continue;
}
/*
* XXX - We really need to be guarding against the
* retransmission of (src,dst,id,offset-range) here
* because a fragmented packet is never resent with
* the same IP ID# (or shouldn't).
*/
if (f->ipfr_seen0) {
if (off == 0) {
continue;
}
} else if (off == 0) {
f->ipfr_seen0 = 1;
}
/*
* Move fragment info. to the top of the list
* to speed up searches. First, delink...
*/
fp = f->ipfr_hprev;
(*fp) = f->ipfr_hnext;
if (f->ipfr_hnext != NULL)
/*
* Then put back at the top of the chain.
*/
}
}
/*
* If we've follwed the fragments, and this is the
* last (in order), shrink expiration time.
*/
} else if (f->ipfr_pass & FR_FRSTRICT)
continue;
return f;
}
return NULL;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_nat_knownfrag */
/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */
/* match found, else NULL */
/* Parameters: fin(I) - pointer to packet information */
/* */
/* Functional interface for NAT lookups of the NAT fragment cache */
/* ------------------------------------------------------------------------ */
{
return NULL;
/*
* This is the last fragment for this packet.
*/
}
} else
return nat;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_ipid_knownfrag */
/* Returns: u_32_t - IPv4 ID for this packet if match found, else */
/* return 0xfffffff to indicate no match. */
/* Parameters: fin(I) - pointer to packet information */
/* */
/* Functional interface for IP ID lookups of the IP ID fragment cache */
/* ------------------------------------------------------------------------ */
{
return 0xffffffff;
else
id = 0xffffffff;
return id;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_knownfrag */
/* Returns: frentry_t* - pointer to filter rule if a match is found in */
/* the frag cache table, else NULL. */
/* Parameters: fin(I) - pointer to packet information */
/* passp(O) - pointer to where to store rule flags resturned */
/* */
/* Functional interface for normal lookups of the fragment cache. If a */
/* match is found, return the rule pointer and flags from the rule, except */
/* that if FR_LOGFIRST is set, reset FR_LOG. */
/* ------------------------------------------------------------------------ */
{
return NULL;
if ((pass & FR_LOGFIRST) != 0)
}
}
*passp &= ~FR_CMDMASK;
}
return fr;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_forget */
/* Returns: Nil */
/* Parameters: ptr(I) - pointer to data structure */
/* */
/* Search through all of the fragment cache entries and wherever a pointer */
/* is found to match ptr, reset it to NULL. */
/* ------------------------------------------------------------------------ */
void *ptr;
{
}
/* ------------------------------------------------------------------------ */
/* Function: fr_forgetnat */
/* Returns: Nil */
/* Parameters: ptr(I) - pointer to data structure */
/* */
/* Search through all of the fragment cache entries for NAT and wherever a */
/* pointer is found to match ptr, reset it to NULL. */
/* ------------------------------------------------------------------------ */
void *ptr;
{
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fragdelete */
/* Returns: Nil */
/* Parameters: fra(I) - pointer to fragment structure to delete */
/* tail(IO) - pointer to the pointer to the tail of the frag */
/* list */
/* */
/* Remove a fragment cache table entry from the table & list. Also free */
/* the filter rule it is associated with it if it is no longer used as a */
/* result of decreasing the reference count. */
/* ------------------------------------------------------------------------ */
{
if (fra->ipfr_hnext)
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fragclear */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Free memory in use by fragment state information kept. Do the normal */
/* fragment state stuff first and then the NAT-fragment table. */
/* ------------------------------------------------------------------------ */
void fr_fragclear(ifs)
{
}
}
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_fragexpire */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Expire entries in the fragment cache table that have been there too long */
/* ------------------------------------------------------------------------ */
void fr_fragexpire(ifs)
{
SPL_INT(s);
if (ifs->ifs_fr_frag_lock)
return;
SPL_NET(s);
/*
* Go through the entire table, looking for entries to expire,
* which is indicated by the ttl being less than or equal to
* ifs_fr_ticks.
*/
break;
ifs->ifs_ipfr_inuse--;
}
break;
ifs->ifs_ipfr_inuse--;
}
/*
* Same again for the NAT table, except that if the structure also
* still points to a NAT structure, and the NAT structure points back
* at the one to be free'd, NULL the reference from the NAT struct.
* NOTE: We need to grab both mutex's early, and in this order so as
* to prevent a deadlock if both try to expire at the same time.
*/
break;
}
ifs->ifs_ipfr_inuse--;
}
SPL_X(s);
}
/* ------------------------------------------------------------------------ */
/* Function: fr_slowtimer */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Slowly expire held state for fragments. Timeouts are set * in */
/* expectation of this being called twice per second. */
/* ------------------------------------------------------------------------ */
# else
int fr_slowtimer(void *arg)
# endif
{
ifs->ifs_fr_ticks++;
if (ifs->ifs_fr_running <= 0)
goto done;
# ifdef _KERNEL
# else
# if defined(__OpenBSD__)
# else
# if (__FreeBSD_version >= 300000)
# else
# ifdef linux
;
# else
# endif
# endif /* FreeBSD */
# endif /* OpenBSD */
# endif /* NetBSD */
# endif
done:
return 0;
# endif
}
#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
/*ARGSUSED*/
{
int error = 0;
return ESRCH;
}
else
} else {
}
}
if (error != 0)
return error;
}
{
ifs->ifs_ipfr_inuse--;
}
}