Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 31 Jan 1998 07:11:01 +0100
From:      Eivind Eklund <eivind@yes.no>
To:        hackers@FreeBSD.ORG
Subject:   On a new IPFW interface, w/potentially wider applications
Message-ID:  <19980131071101.11786@follo.net>

next in thread | raw e-mail | index | archive | help
Does the below (inspired by tags on the Amiga) sound like a good idea
for IPFW?

Does it sound like it is such a good idea that it should be extended
to a generic method of manipulating kernel objects instead of doing it
only for IPFW?  (Cost: At least one syscall number, up to four if we
want separate syscalls for add/delete/commit/cancel, as well as making
it approx twice as much work to implement as a special case for IPFW).

And speaking of kernel interfaces: I've been thinking of a way of
implementing capabilites (in the security sense) which would share a
lot of properties with this interface, and might be cheap to implement
along with it (I'd have to look more closely at the design issues to
be certain).  Do anybody but me feel that this (capabilites) would be
a good direction to go for FreeBSD, or do people think we'll become
'too weird' that way?


[Originally in a mail to Alex Nash]
Speaking of the IPFW interface: I am quite tired of seeing necessary
changes to that break other software.  What do you say about an
interface like the below?  If that's OK, I'll look into implementing
it or getting my mentoree to do it (unless you have enough time and
want to do it yourself to get it done quickly :-)

The basic idea behind the below is the make the interface create rules
by itself with default values first, and then change the created rule
by a set of setsockopt() calls until its parameters is correct.  The
new rule is then added to the list by a commit operation.
Implementation detail: The interface should limit the number of
non-committed rules to a fixed number, to stop kernel resource
starvation by a userland process.  Old non-committed rules should
probably be thrown away in preference to new ones being denied; that
will more easily protect against programming errors.

I think a new new userland library for easy access to this would be a
good idea; see bottom for details on how I envision this.

------------------------------------------------------------------

    New socket options:
IP_FW_START_ADD:
    struct {
       long version;            /* Interface version, below is version 0 */
       unsigned long list;      /* Which rule list to work with (0 is
                                   the only presently correct list
                                   number) */
       int number;              /* "Line number"/rule number for IPFW */

       unsigned long coookie;   /* Filled in by the kernel; used as a
                                   capability (in the security sense)
                                   to identify this add operation for
                                   later manipulation. */
    }

This socket option starts a new add request; the add request will be
filled with data and committed to the firewall lists later.  List is
allowed to be able to have more than one firewall list later, to allow
filtering the way Cisco implements it.  (If the structure is not
copied to the kernel for a getsockopt() operation, we'll just have to
make do with just returning a cookie.  The above implementation would
be preferable, as we won't have to specify as many parameters for to
IP_FW_SET, and can upgrade the interface at will).


IP_FW_SET:
    struct {
       unsigned long cookie;    /* Previously returned from
                                   IP_FW_START_ADD.  This identify
                                   which non-completed rule to
                                   manipulate.  */
       unsigned long field;     /* Which field of the rule to
                                   manipulate */

       unsigned char data[92];  /* The data for this field. 92 will
                                   allow it to fit in a 108 byte
                                   socket option together with a
                                   64-bit cookie and 'field' ;-) */
    }
Manipulate the field of an as-of-yet uncommitted rule.

IP_FW_COMMIT:
    struct {
       unsigned long cookie;    /* The capability of the rule to
                                   commit */
    }
Commit a new rule into the kernel.


IP_FW_CANCEL:
    struct {
       unsigned long cookie;    /* The capability of the rule to
                                   cancel */
    }
Drop a rule that has been built in the kernel.


IP_FW_START_GET:
    struct {
       long version;            /* Version of interface to use
                                   (presently 0) */
       unsigned long list;      /* Which list of rules to get
                                   (presently 0) */
       unsigned long cookie;    /* Capability; filled in by the
                                   kernel. */
    }
Get a capability for getting a list of firewall rules from the kernel.

IP_FW_GET_VALUE:
    struct ip_fw_get_value_struct {
       unsigned long cookie;    /* Capability for getting data */
       unsigned long field;     /* Which field to get */
       unsigned char data[92];  /* Data gotten from the kernel */
    }
Get the data for a field from the kernel.  If we can't pass this
structure to the kernel, we're in mucho, mucho trouble, and probably
will need to connect the state of reading a list to a particular
socket, which would be a pity.  The size of the data read from the
kernel is found by subtracting offsetof(struct
ip_fw_get_value_struct,data) from the amount of data the kernel says
it has written.

IP_FW_GET_NEXT:
    struct {
       unsigned long cookie;    /* Capability for reading operation in
                                   the kernel we're presently
                                   manipulating. */
    }
Proceed to next rule along a rule-list we're reading.


IP_FW_GET_DONE:
    struct {
       unsigned long cookie;    /* Capability for reading operation in
                                   the kernel we're presently
                                   manipulating. */
    }
Finish with the capability for a particular read operation.

-------------------------------------------

Now, to make the userland side of this easy, I would like an access
library:

int
ipfw_addrule(
unsigned long list_number,
unsigned long rule_number,
...);

where ... is filled with option/value pairs, like in
   (long)IPFW_SRCADDR, &inaddr,
   (long)IPFW_PORT, 6667,
   (long)0                      /* Termination */

For deletion,
int
ipfw_delrule(
unsigned long list_number,
unsigned long rule_number
);

For getting lists:
{
    unsigned long cookie;
    struct ip_fw rule;

    cookie = ipfw_getlist_start(0); /* List number */
    if (!cookie)
       return;

    while (ipfw_getlist_entry(cookie,
       IPFW_SRCADDR, &rule.fw_src,
       IPFW_DSTADDR, &rule.fw_dst,
       IPFW_SRCMASK, &rule.fw_smsk,
       IPFW_DSTMASK, &rule.fw_dmsk,
       (long)0)) {
       ... do stuff ...
    }
    ipfw_getlist_done(cookie);
}

The data don't have to go into the old ip_fw struct, of course -
that's just as an example.



Well, what do you think?  It covers our present needs, and as far as I
can see our future needs - if we need single fields of more than 92
bytes we can always set them as several operations or change the
interface by changing the version.  It allow us to add entries to the
data structures without killing old apps.  It should be fairly easy to
implement, and fairly easy for programmers to use, especially if we
add the library.

Eivind.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19980131071101.11786>