Hello all,

I've been thinking for quite some time to add per-client-IP limiting
to ftpd, and I had almost decided upon something like the following,
where each child of ftpd has two numbers associated with it.  The
client IP address, and the PID of the ftpd child that serves it.  The
hash at the beginning of the lists serves as a minor assistance in
splitting the 2^32 address space in smaller chunks so that we don't
end up with a singly linked list of a few thousand entries.

     addrhash
       %  %       .------------------.        .------------------.
    0 [    ]----->| struct childaddr |------->| struct childaddr |----- ...
    1 [    ]      |==================|        |==================|
    3 [    ]      | in_addr ca_addr  |        | in_addr ca_addr  |
    4 [    ]      | int ca_count     |        | int ca_count     |
    5 [    ]      | LIST(pids)       |--+     | LIST(pids)       |--+
  ... [    ]      `------------------'  |     `------------------'  |
65535 [    ]                            |                           |
                  .-----------------.   |     .-----------------.   |
                  | struct childpid |<--+     | struct childpid |<--+
               +--|=================|      +--|=================|
               |  | pid_t cp_pid    |      |  | pid_t cp_pid    |
               |  `-----------------'      |  `-----------------'
               |                           |
               +->.-----------------.      +-> ...
                  | struct childpid |
               +--|=================|
               |  | pid_t cp_pid    |
               |  `-----------------'
               |
               +-> ...

A simple hash function, can be used in selecting which entry of the
addrhash[] array points to the head of the proper list.  The first
level lists are lists of IP addresses, stored in ca_addr, and the
second level of lists contain just the process ID of the child that
serves a client from that address.  The ca_count field of the address
structure can be used to quickly find out if more clients are allowed
or not, without doing a LIST_FOREACH() every time we want to see if
another client socket can be accepted or dropped.

I am not sure if using simple lists instead of some more sophisticated
scheme is OK in this case, since I don't have a multi-thousand FTP
server handy to test the patches.  The inetd discussion brought this
up from old things I had been thinking about, and I wondered, is there
any case we could make ftpd limit client sockets per IP address too?
And if that is feasible, could inetd and ftpd be made to use the same
limiting code (instead of rolling two different versions), reducing
code duplication?

There is also the following problem, that I am not sure if I have
solved it correctly.  If the parent process plays around with those
hashes and structures, then a signal handler that calls reapchild() to
remove parts of those structures can probably do a lot of harm.  The
race condition seems easy to fix if I use something like:

        nosignals();
        /* Fool around with the structures. */
        signals();

Where signals() and nosignals() use sigaction() to block & unblock the
signals that ftpd already installs a handler for.  Does this all sound
reasonable?

Below is a prototype I'm playing the last few days with, trying to
make something that implements the above scheme using <queue.h>
macros.  Now, what do you all think about this?  Does it sound like a
nice idea to pursue further?

- Giorgos

Attachment: msg35116/pgp00000.pgp
Description: PGP signature

Reply via email to