nf_conntrack_put(&ct->ct_general);
 }
 
+/* Protocol module loading */
+extern int nf_ct_l3proto_try_module_get(unsigned short l3proto);
+extern void nf_ct_l3proto_module_put(unsigned short l3proto);
+
 extern struct nf_conntrack_tuple_hash *
 __nf_conntrack_find(const struct nf_conntrack_tuple *tuple,
                    const struct nf_conn *ignored_conntrack);
 
        return ret;
 }
 
+MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET));
 MODULE_LICENSE("GPL");
 
 static int __init init(void)
 
        return ret;
 }
 
+MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
 
 
  * 26 Jan 2006: Harald Welte <laforge@netfilter.org>
  *     - restructure nf_conn (introduce nf_conn_help)
  *     - redesign 'features' how they were originally intended
+ * 26 Feb 2006: Pablo Neira Ayuso <pablo@eurodev.net>
+ *     - add support for L3 protocol module load on demand.
  *
  * Derived from net/ipv4/netfilter/ip_conntrack_core.c
  */
        module_put(p->me);
 }
 
+int
+nf_ct_l3proto_try_module_get(unsigned short l3proto)
+{
+       int ret;
+       struct nf_conntrack_l3proto *p;
+
+retry: p = nf_ct_l3proto_find_get(l3proto);
+       if (p == &nf_conntrack_generic_l3proto) {
+               ret = request_module("nf_conntrack-%d", l3proto);
+               if (!ret)
+                       goto retry;
+
+               return -EPROTOTYPE;
+       }
+
+       return 0;
+}
+
+void nf_ct_l3proto_module_put(unsigned short l3proto)
+{
+       struct nf_conntrack_l3proto *p;
+
+       preempt_disable();
+       p = __nf_ct_l3proto_find(l3proto);
+       preempt_enable();
+
+       module_put(p->me);
+}
+
 static int nf_conntrack_hash_rnd_initted;
 static unsigned int nf_conntrack_hash_rnd;
 
 
 EXPORT_PER_CPU_SYMBOL_GPL(nf_conntrack_ecache);
 EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
 #endif
+EXPORT_SYMBOL(nf_ct_l3proto_try_module_get);
+EXPORT_SYMBOL(nf_ct_l3proto_module_put);
 EXPORT_SYMBOL(nf_conntrack_l3proto_register);
 EXPORT_SYMBOL(nf_conntrack_l3proto_unregister);
 EXPORT_SYMBOL(nf_conntrack_protocol_register);
 
                printk(KERN_WARNING "connmark: only support 32bit mark\n");
                return 0;
        }
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+               printk(KERN_WARNING "can't load nf_conntrack support for "
+                                   "proto=%d\n", match->family);
+               return 0;
+       }
+#endif
        return 1;
 }
 
+static void
+destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       nf_ct_l3proto_module_put(match->family);
+#endif
+}
+
 static struct xt_match connmark_match = {
        .name           = "connmark",
        .match          = match,
        .matchsize      = sizeof(struct xt_connmark_info),
        .checkentry     = checkentry,
+       .destroy        = destroy,
        .family         = AF_INET,
        .me             = THIS_MODULE
 };
        .match          = match,
        .matchsize      = sizeof(struct xt_connmark_info),
        .checkentry     = checkentry,
+       .destroy        = destroy,
        .family         = AF_INET6,
        .me             = THIS_MODULE
 };
 
 
 #endif /* CONFIG_NF_IP_CONNTRACK */
 
+static int
+checkentry(const char *tablename,
+          const void *ip,
+          const struct xt_match *match,
+          void *matchinfo,
+          unsigned int matchsize,
+          unsigned int hook_mask)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+               printk(KERN_WARNING "can't load nf_conntrack support for "
+                                   "proto=%d\n", match->family);
+               return 0;
+       }
+#endif
+       return 1;
+}
+
+static void
+destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       nf_ct_l3proto_module_put(match->family);
+#endif
+}
+
 static struct xt_match conntrack_match = {
        .name           = "conntrack",
        .match          = match,
+       .checkentry     = checkentry,
+       .destroy        = destroy,
        .matchsize      = sizeof(struct xt_conntrack_info),
        .family         = AF_INET,
        .me             = THIS_MODULE,
 
 {
        struct xt_helper_info *info = matchinfo;
 
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+               printk(KERN_WARNING "can't load nf_conntrack support for "
+                                   "proto=%d\n", match->family);
+               return 0;
+       }
+#endif
        info->name[29] = '\0';
        return 1;
 }
 
+static void
+destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       nf_ct_l3proto_module_put(match->family);
+#endif
+}
+
 static struct xt_match helper_match = {
        .name           = "helper",
        .match          = match,
        .matchsize      = sizeof(struct xt_helper_info),
        .checkentry     = check,
+       .destroy        = destroy,
        .family         = AF_INET,
        .me             = THIS_MODULE,
 };
        .match          = match,
        .matchsize      = sizeof(struct xt_helper_info),
        .checkentry     = check,
+       .destroy        = destroy,
        .family         = AF_INET6,
        .me             = THIS_MODULE,
 };
 
        return (sinfo->statemask & statebit);
 }
 
+static int check(const char *tablename,
+                const void *inf,
+                const struct xt_match *match,
+                void *matchinfo,
+                unsigned int matchsize,
+                unsigned int hook_mask)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+               printk(KERN_WARNING "can't load nf_conntrack support for "
+                                   "proto=%d\n", match->family);
+               return 0;
+       }
+#endif
+       return 1;
+}
+
+static void
+destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize)
+{
+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       nf_ct_l3proto_module_put(match->family);
+#endif
+}
+
 static struct xt_match state_match = {
        .name           = "state",
        .match          = match,
+       .checkentry     = check,
+       .destroy        = destroy,
        .matchsize      = sizeof(struct xt_state_info),
        .family         = AF_INET,
        .me             = THIS_MODULE,
 static struct xt_match state6_match = {
        .name           = "state",
        .match          = match,
+       .checkentry     = check,
+       .destroy        = destroy,
        .matchsize      = sizeof(struct xt_state_info),
        .family         = AF_INET6,
        .me             = THIS_MODULE,