tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Patch: accept filters for NetBSD



On Mon, Jan 28, 2008 at 04:30:10PM -0500, Thor Lancelot Simon wrote:
>
> A coworker has ported the FreeBSD "accept filter" functionality to
> NetBSD (approximately 4.99.40 -- I think the attached patch should
> apply cleanly to HEAD today, however).

Oops!  Here's the patch.

--- /workspaces/vendor/nbsrc/sys/conf/files     2008-01-09 14:44:14.000000000 
-0500
+++ sys/conf/files      2008-01-28 16:01:14.000000000 -0500
@@ -1188,6 +1188,9 @@
 defpseudo carp:                ifnet, ether, arp, bpf_filter
 defpseudo etherip { }: ifnet, ether, arp, bpf_filter
 
+defpseudo accf_data
+defpseudo accf_http
+
 defpseudo sequencer
 defpseudo clockctl
 defpseudo irix_kmem
@@ -1422,6 +1425,7 @@
 file   kern/tty_ptm.c                  pty
 file   kern/tty_subr.c
 file   kern/tty_tty.c
+file   kern/uipc_accf.c                inet
 file   kern/uipc_domain.c
 file   kern/uipc_mbuf.c
 file   kern/uipc_mbuf2.c
@@ -1506,6 +1510,8 @@
 file   netinet/ip_etherip.c            etherip & inet
 file   netinet6/ip6_etherip.c          etherip & inet6
 file   netinet6/in6_gif.c              gif & inet6
+file   netinet/accf_data.c             inet & accf_data
+file   netinet/accf_http.c             inet & accf_http
 
 file   netisdn/i4b_ctl.c               isdnctl                 needs-flag
 file   netisdn/i4b_isppp.c             ippp                    needs-count
@@ -1559,3 +1565,6 @@
 # Pseudo audio device
 #
 include "dev/pad/files.pad"
+# Coyote Point Equalizer
+#
+include "l7lb/files.EQ"
--- /dev/null   2008-01-28 15:53:44.000000000 -0500
+++ sys/kern/uipc_accf.c        2008-01-28 16:01:30.000000000 -0500
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2000 Paycounter, Inc.
+ * Copyright (c) 2005 Robert N. M. Watson
+ * Author: Alfred Perlstein <alfred%paycounter.com@localhost>, 
<alfred%FreeBSD.org@localhost>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: uipc_accf.c $");
+
+#define ACCEPT_FILTER_MOD
+
+#include "opt_inet.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/lkm.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/queue.h>
+
+static SLIST_HEAD(, accept_filter) accept_filtlsthd =
+       SLIST_HEAD_INITIALIZER(&accept_filtlsthd);
+
+MALLOC_DEFINE(M_ACCF, "accf", "accept filter data");
+
+static int unloadable = 0;
+
+/*
+ * Names of Accept filter sysctl objects
+ */
+
+#define ACCFCTL_UNLOADABLE     1       /* Allow module to be unloaded */
+
+
+SYSCTL_SETUP(sysctl_net_inet_accf_setup, "sysctl net.inet.accf subtree setup")
+{
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "net", NULL,
+                      NULL, 0, NULL, 0,
+                      CTL_NET, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "inet", NULL,
+                      NULL, 0, NULL, 0,
+                      CTL_NET, PF_INET, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "accf",
+                      SYSCTL_DESCR("Accept filters"),
+                      NULL, 0, NULL, 0,
+                      CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                      CTLTYPE_INT, "unloadable",
+                      SYSCTL_DESCR("Allow unload of accept filters "
+                                   "(not recommended)"),
+                      NULL, 0, &unloadable, 0,
+                      CTL_NET, PF_INET, SO_ACCEPTFILTER,
+                      ACCFCTL_UNLOADABLE, CTL_EOL);
+}
+
+/*
+ * Must be passed a malloc'd structure so we don't explode if the kld is
+ * unloaded, we leak the struct on deallocation to deal with this, but if a
+ * filter is loaded with the same name as a leaked one we re-use the entry.
+ */
+int
+accept_filt_add(struct accept_filter *filt)
+{
+       struct accept_filter *p;
+
+       SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
+               if (strcmp(p->accf_name, filt->accf_name) == 0)  {
+                       if (p->accf_callback != NULL) {
+                               return (EEXIST);
+                       } else {
+                               p->accf_callback = filt->accf_callback;
+                               FREE(filt, M_ACCF);
+                               return (0);
+                       }
+               }
+                               
+       if (p == NULL)
+               SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next);
+       return (0);
+}
+
+int
+accept_filt_del(char *name)
+{
+       struct accept_filter *p;
+
+       p = accept_filt_get(name);
+       if (p == NULL)
+               return (ENOENT);
+
+       p->accf_callback = NULL;
+       return (0);
+}
+
+struct accept_filter *
+accept_filt_get(char *name)
+{
+       struct accept_filter *p;
+
+       SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
+               if (strcmp(p->accf_name, name) == 0)
+                       break;
+
+       return (p);
+}
+
+int
+accept_filt_generic_mod_event(struct lkm_table *lkmtp, int event, void *data)
+{
+       struct accept_filter *p;
+       struct accept_filter *accfp = (struct accept_filter *) data;
+       int error;
+
+       switch (event) {
+       case LKM_E_LOAD:
+               MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF,
+                   M_WAITOK);
+               bcopy(accfp, p, sizeof(*p));
+               error = accept_filt_add(p);
+               break;
+
+       case LKM_E_UNLOAD:
+               /*
+                * Do not support unloading yet. we don't keep track of
+                * refcounts and unloading an accept filter callback and then
+                * having it called is a bad thing.  A simple fix would be to
+                * track the refcount in the struct accept_filter.
+                */
+               if (unloadable != 0) {
+                       error = accept_filt_del(accfp->accf_name);
+               } else
+                       error = EOPNOTSUPP;
+               break;
+
+       case LKM_E_STAT:
+               error = 0;
+               break;
+
+       default:
+               error = EOPNOTSUPP;
+               break;
+       }
+
+       return (error);
+}
+
+int
+do_getopt_accept_filter(struct socket *so, struct mbuf *m)
+{
+       int error;
+
+       error = 0;
+       if ((so->so_options & SO_ACCEPTCONN) == 0) {
+               error = EINVAL;
+               goto out;
+       }
+       if ((so->so_options & SO_ACCEPTFILTER) == 0) {
+               error = EINVAL;
+               goto out;
+       }
+       m->m_len = sizeof(struct accept_filter_arg);
+       strcpy(mtod(m, struct accept_filter_arg *)->af_name, 
so->so_accf->so_accept_filter->accf_name);
+       if (so->so_accf->so_accept_filter_str != NULL)
+               strcpy(mtod(m, struct accept_filter_arg *)->af_arg, 
so->so_accf->so_accept_filter_str);
+out:
+       return (error);
+}
+
+int
+do_setopt_accept_filter(struct socket *so, struct mbuf *m)
+{
+       struct accept_filter_arg *afap;
+       struct accept_filter *afp;
+       struct so_accf *newaf;
+       int error = 0;
+
+       /*
+        * Handle the simple delete case first.
+        */
+       if (m == NULL || mtod(m, struct accept_filter_arg *) == NULL) {
+               if ((so->so_options & SO_ACCEPTCONN) == 0) {
+                       return (EINVAL);
+               }
+               if (so->so_accf != NULL) {
+                       struct so_accf *af = so->so_accf;
+                       if (af->so_accept_filter != NULL &&
+                               af->so_accept_filter->accf_destroy != NULL) {
+                               af->so_accept_filter->accf_destroy(so);
+                       }
+                       if (af->so_accept_filter_str != NULL)
+                               FREE(af->so_accept_filter_str, M_ACCF);
+                       FREE(af, M_ACCF);
+                       so->so_accf = NULL;
+               }
+               so->so_options &= ~SO_ACCEPTFILTER;
+               return (0);
+       }
+
+       /*
+        * Pre-allocate any memory we may need later to avoid blocking at
+        * untimely moments.  This does not optimize for invalid arguments.
+        */
+       if (m->m_len != sizeof(struct accept_filter_arg)) {
+               return (EINVAL);
+       }
+       afap = mtod(m, struct accept_filter_arg *);
+       afap->af_name[sizeof(afap->af_name)-1] = '\0';
+       afap->af_arg[sizeof(afap->af_arg)-1] = '\0';
+       if (error) {
+               return (error);
+       }
+       afp = accept_filt_get(afap->af_name);
+       if (afp == NULL) {
+               return (ENOENT);
+       }
+       /*
+        * Allocate the new accept filter instance storage.  We may
+        * have to free it again later if we fail to attach it.  If
+        * attached properly, 'newaf' is NULLed to avoid a free()
+        * while in use.
+        */
+       MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK |
+           M_ZERO);
+       if (afp->accf_create != NULL && afap->af_name[0] != '\0') {
+               int len = strlen(afap->af_name) + 1;
+               MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF,
+                   M_WAITOK);
+               strcpy(newaf->so_accept_filter_str, afap->af_name);
+       }
+
+       /*
+        * Require a listen socket; don't try to replace an existing filter
+        * without first removing it.
+        */
+       if (((so->so_options & SO_ACCEPTCONN) == 0) ||
+           (so->so_accf != NULL)) {
+               error = EINVAL;
+               goto out;
+       }
+
+       /*
+        * Invoke the accf_create() method of the filter if required.  The
+        * socket mutex is held over this call, so create methods for filters
+        * can't block.
+        */
+       if (afp->accf_create != NULL) {
+               newaf->so_accept_filter_arg =
+                   afp->accf_create(so, afap->af_arg);
+               if (newaf->so_accept_filter_arg == NULL) {
+                       error = EINVAL;
+                       goto out;
+               }
+       }
+       newaf->so_accept_filter = afp;
+       so->so_accf = newaf;
+       so->so_options |= SO_ACCEPTFILTER;
+       newaf = NULL;
+out:
+       if (newaf != NULL) {
+               if (newaf->so_accept_filter_str != NULL)
+                       FREE(newaf->so_accept_filter_str, M_ACCF);
+               FREE(newaf, M_ACCF);
+       }
+       return (error);
+}
--- /workspaces/vendor/nbsrc/sys/kern/uipc_socket.c     2008-01-09 
14:44:39.000000000 -0500
+++ sys/kern/uipc_socket.c      2008-01-28 16:01:30.000000000 -0500
@@ -70,6 +70,7 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: uipc_socket.c,v 1.149 2007/12/05 17:19:59 pooka 
Exp $");
 
+#include "opt_inet.h"
 #include "opt_sock_counters.h"
 #include "opt_sosend_loan.h"
 #include "opt_mbuftrace.h"
@@ -625,6 +626,11 @@
        sorflush(so);
        seldestroy(&so->so_rcv.sb_sel);
        seldestroy(&so->so_snd.sb_sel);
+#ifdef INET
+       /* remove acccept filter if one is present. */
+       if (so->so_accf != NULL)
+               do_setopt_accept_filter(so, NULL);
+#endif
        pool_put(&socket_pool, so);
 }
 
@@ -1460,13 +1466,25 @@
 static int
 sosetopt1(struct socket *so, int level, int optname, struct mbuf *m)
 {
+#ifdef INET
+       int error, optval, val;
+#else
        int optval, val;
+#endif
        struct linger   *l;
        struct sockbuf  *sb;
        struct timeval *tv;
 
        switch (optname) {
 
+#ifdef INET
+       case SO_ACCEPTFILTER:
+               error = do_setopt_accept_filter(so, m);
+               if (error)
+                       return error;
+               break;
+#endif
+
        case SO_LINGER:
                if (m == NULL || m->m_len != sizeof(struct linger))
                        return EINVAL;
@@ -1596,6 +1614,9 @@
 int
 sogetopt(struct socket *so, int level, int optname, struct mbuf **mp)
 {
+#ifdef INET
+       int error;
+#endif
        struct mbuf     *m;
 
        if (level != SOL_SOCKET) {
@@ -1610,6 +1631,14 @@
 
                switch (optname) {
 
+#ifdef INET
+               case SO_ACCEPTFILTER:
+                       error = do_getopt_accept_filter(so, m);
+                       if (error)
+                               return error;
+                       break;
+#endif
+
                case SO_LINGER:
                        m->m_len = sizeof(struct linger);
                        mtod(m, struct linger *)->l_onoff =
--- /workspaces/vendor/nbsrc/sys/kern/uipc_socket2.c    2008-01-09 
14:44:39.000000000 -0500
+++ sys/kern/uipc_socket2.c     2008-01-28 16:01:30.000000000 -0500
@@ -34,6 +34,7 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: uipc_socket2.c,v 1.86 2007/09/25 14:04:07 ad Exp 
$");
 
+#include "opt_inet.h"
 #include "opt_mbuftrace.h"
 #include "opt_sb_max.h"
 
@@ -110,10 +111,20 @@
        head = so->so_head;
        so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
        so->so_state |= SS_ISCONNECTED;
-       if (head && soqremque(so, 0)) {
-               soqinsque(head, so, 1);
-               sorwakeup(head);
-               wakeup((void *)&head->so_timeo);
+       if (head && so->so_onq == &head->so_q0) {
+               if ((so->so_options & SO_ACCEPTFILTER) == 0) {
+                       soqremque(so, 0);
+                       soqinsque(head, so, 1);
+                       sorwakeup(head);
+                       wakeup((void *)&head->so_timeo);
+               } else {
+                       so->so_upcall =
+                           head->so_accf->so_accept_filter->accf_callback;
+                       so->so_upcallarg = head->so_accf->so_accept_filter_arg;
+                       so->so_rcv.sb_flags |= SB_UPCALL;
+                       so->so_options &= ~SO_ACCEPTFILTER;
+                       so->so_upcall(so, so->so_upcallarg, M_DONTWAIT);
+               }
        } else {
                wakeup((void *)&so->so_timeo);
                sorwakeup(so);
@@ -157,6 +168,8 @@
        struct socket   *so;
        int             soqueue;
 
+       if ((head->so_options & SO_ACCEPTFILTER) != 0)
+               connstatus = 0;
        soqueue = connstatus ? 1 : 0;
        if (head->so_qlen + head->so_q0len > 3 * head->so_qlimit / 2)
                return ((struct socket *)0);
@@ -195,6 +208,11 @@
                (void) soqremque(so, soqueue);
                seldestroy(&so->so_rcv.sb_sel);
                seldestroy(&so->so_snd.sb_sel);
+#ifdef INET
+               /* remove acccept filter if one is present. */
+               if (so->so_accf != NULL)
+                       do_setopt_accept_filter(so, NULL);
+#endif
                pool_put(&socket_pool, so);
                return (NULL);
        }
--- /dev/null   2008-01-28 15:53:44.000000000 -0500
+++ sys/netinet/accept_filter.h 2008-01-28 16:01:33.000000000 -0500
@@ -0,0 +1,50 @@
+/*     $NetBSD: accept_filter.h $      */
+
+/*
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Rui Paulo.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the NetBSD
+ *     Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NETINET_ACCEPT_FILTER_H
+#define _NETINET_ACCEPT_FILTER_H
+
+/*
+ * Valid accept filter list
+ * Currently it is used for creting sysctl object
+ */
+
+#define ACCF_DATA      1               /* Data ready accept filter */
+#define ACCF_HTTP      2               /* HTTP ready accept filter */
+
+#endif /* _NETINET_ACCEPT_FILTER_H */
--- /dev/null   2008-01-28 15:53:44.000000000 -0500
+++ sys/netinet/accf_data.c     2008-01-28 16:01:33.000000000 -0500
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2000 Alfred Perlstein <alfred%FreeBSD.org@localhost>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: accf_data.c $");
+
+#define ACCEPT_FILTER_MOD
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lkm.h>
+#include <sys/sysctl.h>
+#include <sys/signalvar.h>
+#include <sys/socketvar.h>
+#include <netinet/accept_filter.h>
+
+/* accept filter that holds a socket until data arrives */
+
+static void    sohasdata(struct socket *so, void *arg, int waitflag);
+
+static struct accept_filter accf_data_filter = {
+       "dataready",
+       sohasdata,
+       NULL,
+       NULL,
+       {NULL,}
+};
+
+void accf_dataattach(int);
+void accf_dataattach(int num)
+{
+       accept_filt_generic_mod_event(NULL, LKM_E_LOAD, &accf_data_filter);
+}
+
+#ifdef _LKM
+static int accf_data_handle(struct lkm_table * lkmtp, int cmd);
+int accf_data_lkmentry(struct lkm_table * lkmtp, int cmd, int ver);
+
+MOD_MISC("accf_data");
+
+static int accf_data_handle(struct lkm_table * lkmtp, int cmd)
+{
+
+       printf("called this funtion\n");
+       return accept_filt_generic_mod_event(lkmtp, cmd, &accf_data_filter);
+}
+
+/*
+ * the module entry point.
+ */
+int
+accf_data_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
+{
+       DISPATCH(lkmtp, cmd, ver, accf_data_handle, accf_data_handle,
+           accf_data_handle)
+}
+#endif
+
+static void
+sohasdata(struct socket *so, void *arg, int waitflag)
+{
+
+       if (!soreadable(so))
+               return;
+
+       so->so_upcall = NULL;
+       so->so_rcv.sb_flags &= ~SB_UPCALL;
+       soisconnected(so);
+       return;
+}
--- /dev/null   2008-01-28 15:53:44.000000000 -0500
+++ sys/netinet/accf_http.c     2008-01-28 16:01:33.000000000 -0500
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 2000 Paycounter, Inc.
+ * Author: Alfred Perlstein <alfred%paycounter.com@localhost>, 
<alfred%FreeBSD.org@localhost>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: accf_data.c $");
+
+#define ACCEPT_FILTER_MOD
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/lkm.h>
+#include <sys/signalvar.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <netinet/accept_filter.h>
+
+/* check for GET/HEAD */
+static void sohashttpget(struct socket *so, void *arg, int waitflag);
+/* check for HTTP/1.0 or HTTP/1.1 */
+static void soparsehttpvers(struct socket *so, void *arg, int waitflag);
+/* check for end of HTTP/1.x request */
+static void soishttpconnected(struct socket *so, void *arg, int waitflag);
+/* strcmp on an mbuf chain */
+static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const 
char *cmp);
+/* strncmp on an mbuf chain */
+static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
+       int len, const char *cmp);
+/* socketbuffer is full */
+static int sbfull(struct sockbuf *sb);
+
+static struct accept_filter accf_http_filter = {
+       "httpready",
+       sohashttpget,
+       NULL,
+       NULL,
+       {NULL,}
+};
+
+/*
+ * Names of HTTP Accept filter sysctl objects
+ */
+
+#define ACCFCTL_PARSEVER       1       /* Parse HTTP version */
+
+static int parse_http_version = 1;
+
+SYSCTL_SETUP(sysctl_net_inet_accf__http_setup, "sysctl net.inet.accf.http 
subtree setup")
+{
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "net", NULL,
+                      NULL, 0, NULL, 0,
+                      CTL_NET, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "inet", NULL,
+                      NULL, 0, NULL, 0,
+                      CTL_NET, PF_INET, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "accf", NULL,
+                      NULL, 0, NULL, 0,
+                      CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT,
+                      CTLTYPE_NODE, "http",
+                      SYSCTL_DESCR("HTTP accept filter"),
+                      NULL, 0, NULL, 0,
+                      CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP, CTL_EOL);
+       sysctl_createv(clog, 0, NULL, NULL,
+                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                      CTLTYPE_INT, "parsehttpversion",
+                      SYSCTL_DESCR("Parse http version so that non "
+                                   "1.x requests work"),
+                      NULL, 0, &parse_http_version, 0,
+                      CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP,
+                      ACCFCTL_PARSEVER, CTL_EOL);
+}
+
+void accf_httpattach(int);
+void accf_httpattach(int num)
+{
+       accept_filt_generic_mod_event(NULL, LKM_E_LOAD, &accf_http_filter);
+}
+
+#ifdef _LKM
+static int accf_http_handle(struct lkm_table * lkmtp, int cmd);
+int accf_http_lkmentry(struct lkm_table * lkmtp, int cmd, int ver);
+
+MOD_MISC("accf_http");
+
+static int accf_http_handle(struct lkm_table * lkmtp, int cmd)
+{
+
+       return accept_filt_generic_mod_event(lkmtp, cmd, &accf_http_filter);
+}
+
+/*
+ * the module entry point.
+ */
+int
+accf_http_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
+{
+       DISPATCH(lkmtp, cmd, ver, accf_http_handle, accf_http_handle,
+           accf_http_handle)
+}
+#endif
+
+
+
+#ifdef ACCF_HTTP_DEBUG
+#define DPRINT(fmt, args...)                                           \
+       do {                                                            \
+               printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
+       } while (0)
+#else
+#define DPRINT(fmt, args...)
+#endif
+
+static int
+sbfull(struct sockbuf *sb)
+{
+
+       DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
+           "mbcnt(%ld) >= mbmax(%ld): %d",
+           sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
+           sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
+       return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
+}
+
+/*
+ * start at mbuf m, (must provide npkt if exists)
+ * starting at offset in m compare characters in mbuf chain for 'cmp'
+ */
+static int
+mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp)
+{
+       struct mbuf *n;
+
+       for (; m != NULL; m = n) {
+               n = npkt;
+               if (npkt)
+                       npkt = npkt->m_nextpkt;
+               for (; m; m = m->m_next) {
+                       for (; offset < m->m_len; offset++, cmp++) {
+                               if (*cmp == '\0')
+                                       return (1);
+                               else if (*cmp != *(mtod(m, char *) + offset))
+                                       return (0);
+                       }
+                       if (*cmp == '\0')
+                               return (1);
+                       offset = 0;
+               }
+       }
+       return (0);
+}
+
+/*
+ * start at mbuf m, (must provide npkt if exists)
+ * starting at offset in m compare characters in mbuf chain for 'cmp'
+ * stop at 'max' characters
+ */
+static int
+mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int len, const char 
*cmp)
+{
+       struct mbuf *n;
+
+       for (; m != NULL; m = n) {
+               n = npkt;
+               if (npkt)
+                       npkt = npkt->m_nextpkt;
+               for (; m; m = m->m_next) {
+                       for (; offset < m->m_len; offset++, cmp++, len--) {
+                               if (max == 0 || *cmp == '\0')
+                                       return (1);
+                               else if (*cmp != *(mtod(m, char *) + offset))
+                                       return (0);
+                       }
+                       if (len == 0 || *cmp == '\0')
+                               return (1);
+                       offset = 0;
+               }
+       }
+       return (0);
+}
+
+#define STRSETUP(sptr, slen, str)                                      \
+       do {                                                            \
+               sptr = str;                                             \
+               slen = sizeof(str) - 1;                                 \
+       } while(0)
+
+static void
+sohashttpget(struct socket *so, void *arg, int waitflag)
+{
+
+       if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
+               struct mbuf *m;
+               const char *cmp;
+               int     cmplen, cc;
+
+               m = so->so_rcv.sb_mb;
+               cc = so->so_rcv.sb_cc - 1;
+               if (cc < 1)
+                       return;
+               switch (*mtod(m, char *)) {
+               case 'G':
+                       STRSETUP(cmp, cmplen, "ET ");
+                       break;
+               case 'H':
+                       STRSETUP(cmp, cmplen, "EAD ");
+                       break;
+               default:
+                       goto fallout;
+               }
+               if (cc < cmplen) {
+                       if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
+                               DPRINT("short cc (%d) but mbufstrncmp ok", cc);
+                               return;
+                       } else {
+                               DPRINT("short cc (%d) mbufstrncmp failed", cc);
+                               goto fallout;
+                       }
+               }
+               if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
+                       DPRINT("mbufstrcmp ok");
+                       if (parse_http_version == 0)
+                               soishttpconnected(so, arg, waitflag);
+                       else
+                               soparsehttpvers(so, arg, waitflag);
+                       return;
+               }
+               DPRINT("mbufstrcmp bad");
+       }
+
+fallout:
+       DPRINT("fallout");
+       so->so_upcall = NULL;
+       so->so_rcv.sb_flags &= ~SB_UPCALL;
+       soisconnected(so);
+       return;
+}
+
+static void
+soparsehttpvers(struct socket *so, void *arg, int waitflag)
+{
+       struct mbuf *m, *n;
+       int     i, cc, spaces, inspaces;
+
+       if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
+               goto fallout;
+
+       m = so->so_rcv.sb_mb;
+       cc = so->so_rcv.sb_cc;
+       inspaces = spaces = 0;
+       for (m = so->so_rcv.sb_mb; m; m = n) {
+               n = m->m_nextpkt;
+               for (; m; m = m->m_next) {
+                       for (i = 0; i < m->m_len; i++, cc--) {
+                               switch (*(mtod(m, char *) + i)) {
+                               case ' ':
+                                       /* tabs? '\t' */
+                                       if (!inspaces) {
+                                               spaces++;
+                                               inspaces = 1;
+                                       }
+                                       break;
+                               case '\r':
+                               case '\n':
+                                       DPRINT("newline");
+                                       goto fallout;
+                               default:
+                                       if (spaces != 2) {
+                                               inspaces = 0;
+                                               break;
+                                       }
+
+                                       /*
+                                        * if we don't have enough characters
+                                        * left (cc < sizeof("HTTP/1.0") - 1)
+                                        * then see if the remaining ones
+                                        * are a request we can parse.
+                                        */
+                                       if (cc < sizeof("HTTP/1.0") - 1) {
+                                               if (mbufstrncmp(m, n, i, cc,
+                                                       "HTTP/1.") == 1) {
+                                                       DPRINT("ok");
+                                                       goto readmore;
+                                               } else {
+                                                       DPRINT("bad");
+                                                       goto fallout;
+                                               }
+                                       } else if (
+                                           mbufstrcmp(m, n, i, "HTTP/1.0") ||
+                                           mbufstrcmp(m, n, i, "HTTP/1.1")) {
+                                                       DPRINT("ok");
+                                                       soishttpconnected(so,
+                                                           arg, waitflag);
+                                                       return;
+                                       } else {
+                                               DPRINT("bad");
+                                               goto fallout;
+                                       }
+                               }
+                       }
+               }
+       }
+readmore:
+       DPRINT("readmore");
+       /*
+        * if we hit here we haven't hit something
+        * we don't understand or a newline, so try again
+        */
+       so->so_upcall = soparsehttpvers;
+       so->so_rcv.sb_flags |= SB_UPCALL;
+       return;
+
+fallout:
+       DPRINT("fallout");
+       so->so_upcall = NULL;
+       so->so_rcv.sb_flags &= ~SB_UPCALL;
+       soisconnected(so);
+       return;
+}
+
+
+#define NCHRS 3
+
+static void
+soishttpconnected(struct socket *so, void *arg, int waitflag)
+{
+       char a, b, c;
+       struct mbuf *m, *n;
+       int ccleft, copied;
+
+       DPRINT("start");
+       if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
+               goto gotit;
+
+       /*
+        * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
+        * copied - how much we've copied so far
+        * ccleft - how many bytes remaining in the socketbuffer
+        * just loop over the mbufs subtracting from 'ccleft' until we only
+        * have NCHRS left
+        */
+       copied = 0;
+       ccleft = so->so_rcv.sb_cc;
+       if (ccleft < NCHRS)
+               goto readmore;
+       a = b = c = '\0';
+       for (m = so->so_rcv.sb_mb; m; m = n) {
+               n = m->m_nextpkt;
+               for (; m; m = m->m_next) {
+                       ccleft -= m->m_len;
+                       if (ccleft <= NCHRS) {
+                               char *src;
+                               int tocopy;
+
+                               tocopy = (NCHRS - ccleft) - copied;
+                               src = mtod(m, char *) + (m->m_len - tocopy);
+
+                               while (tocopy--) {
+                                       switch (copied++) {
+                                       case 0:
+                                               a = *src++;
+                                               break;
+                                       case 1:
+                                               b = *src++;
+                                               break;
+                                       case 2:
+                                               c = *src++;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
+               /* we have all request headers */
+               goto gotit;
+       }
+
+readmore:
+       so->so_upcall = soishttpconnected;
+       so->so_rcv.sb_flags |= SB_UPCALL;
+       return;
+
+gotit:
+       so->so_upcall = NULL;
+       so->so_rcv.sb_flags &= ~SB_UPCALL;
+       soisconnected(so);
+       return;
+}
--- /workspaces/vendor/nbsrc/sys/sys/socket.h   2008-01-09 14:44:46.000000000 
-0500
+++ sys/sys/socket.h    2008-01-28 16:01:36.000000000 -0500
@@ -121,6 +121,7 @@
 #define        SO_OOBINLINE    0x0100          /* leave received OOB data in 
line */
 #define        SO_REUSEPORT    0x0200          /* allow local address & port 
reuse */
 #define        SO_TIMESTAMP    0x0400          /* timestamp received dgram 
traffic */
+#define        SO_ACCEPTFILTER 0x1000          /* there is an accept filter */
 
 
 /*
@@ -148,6 +149,11 @@
        int     l_linger;               /* linger time in seconds */
 };
 
+struct accept_filter_arg {
+       char    af_name[16];
+       char    af_arg[256-16];
+};
+
 /*
  * Level number for (get/set)sockopt() to apply to socket itself.
  */
--- /workspaces/vendor/nbsrc/sys/sys/socketvar.h        2008-01-09 
14:44:46.000000000 -0500
+++ sys/sys/socketvar.h 2008-01-28 16:01:36.000000000 -0500
@@ -134,6 +134,11 @@
                                        struct mbuf **, int *);
        struct mowner   *so_mowner;     /* who owns mbufs for this socket */
        struct uidinfo  *so_uidinfo;    /* who opened the socket */
+       struct so_accf {
+               struct  accept_filter *so_accept_filter;
+               void    *so_accept_filter_arg;  /* saved filter args */
+               char    *so_accept_filter_str;  /* saved user args */
+       } *so_accf;
 };
 
 #define        SB_EMPTY_FIXUP(sb)                                              
\
@@ -254,6 +259,17 @@
 } while (/* CONSTCOND */ 0)
 
 #ifdef _KERNEL
+struct accept_filter {
+       char    accf_name[16];
+       void    (*accf_callback)
+               (struct socket *so, void *arg, int waitflag);
+       void *  (*accf_create)
+               (struct socket *so, char *arg);
+       void    (*accf_destroy)
+               (struct socket *so);
+       SLIST_ENTRY(accept_filter) accf_next;
+};
+
 extern u_long          sb_max;
 extern int             somaxkva;
 extern int             sock_loan_thresh;
@@ -404,6 +420,21 @@
 #define SB_PRIO_OVERDRAFT      2
 #define SB_PRIO_BESTEFFORT     3
 
+/*
+ * Accept filter functions (duh).
+ */
+int    do_getopt_accept_filter(struct socket *, struct mbuf *);
+int    do_setopt_accept_filter(struct socket *, struct mbuf *);
+int    accept_filt_add(struct accept_filter *);
+int    accept_filt_del(char *);
+struct accept_filter *accept_filt_get(char *);
+#ifdef ACCEPT_FILTER_MOD
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_inet_accf);
+#endif
+int   accept_filt_generic_mod_event(struct lkm_table *lkmtp, int event, void 
*data);
+#endif
+
 #endif /* _KERNEL */
 
 #endif /* !_SYS_SOCKETVAR_H_ */



Home | Main Index | Thread Index | Old Index