From owner-freebsd-hackers Thu Jun 20 17: 9:40 2002 Delivered-To: freebsd-hackers@freebsd.org Received: from mailsrv.otenet.gr (mailsrv.otenet.gr [195.170.0.5]) by hub.freebsd.org (Postfix) with ESMTP id A0B5337B401 for ; Thu, 20 Jun 2002 17:09:29 -0700 (PDT) Received: from hades.hell.gr (patr530-b140.otenet.gr [212.205.244.148]) by mailsrv.otenet.gr (8.12.3/8.12.3) with ESMTP id g5L09QMW016414 for ; Fri, 21 Jun 2002 03:09:27 +0300 (EEST) Received: from hades.hell.gr (hades [127.0.0.1]) by hades.hell.gr (8.12.4/8.12.4) with ESMTP id g5L09PLa002232 for ; Fri, 21 Jun 2002 03:09:25 +0300 (EEST) (envelope-from keramida@FreeBSD.org) Received: (from charon@localhost) by hades.hell.gr (8.12.4/8.12.4/Submit) id g5L09PaO002231 for hackers@freebsd.org; Fri, 21 Jun 2002 03:09:25 +0300 (EEST) (envelope-from keramida@FreeBSD.org) Date: Fri, 21 Jun 2002 03:09:25 +0300 From: Giorgos Keramidas To: hackers@FreeBSD.org Subject: Limiting clients per source IP address (ftpd, inetd, etc.) Message-ID: <20020621000924.GA2178@hades.hell.gr> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="DocE+STaALJfprDB" Content-Disposition: inline Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG --DocE+STaALJfprDB Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 macros. Now, what do you all think about this? Does it sound like a nice idea to pursue further? - Giorgos --DocE+STaALJfprDB Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.7 (FreeBSD) iD8DBQE9Em601g+UGjGGA7YRAmSgAJ44WU4C+skCQUbY8joL+no/wLvwqQCffNJW gJp6EhjnnMN91gZvP4J2jog= =atlJ -----END PGP SIGNATURE----- --DocE+STaALJfprDB-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message