diff -urN kernel.orig/Documentation/Configure.help lsm/Documentation/Configure.help
--- kernel.orig/Documentation/Configure.help	Wed Dec 12 19:45:20 2001
+++ lsm/Documentation/Configure.help	Wed Dec 12 19:49:07 2001
@@ -23995,6 +23995,23 @@
   experiment with it, see the README.MLS file in the SELinux archive.  
   If unsure, say N.
   
+SELinux Labeled Networking Support
+CONFIG_SECURITY_SELINUX_NSID
+  This enables the Network SID API, which provides a framework for
+  labeled networking.  You will need this if you want to use labeled IPv4
+  networking with SELinux CIPSO/FIPS188 IP Options (selopt).
+  
+  If unsure, say N.
+
+CIPSO/FIPS188 IP Options
+CONFIG_SECURITY_SELINUX_SELOPT
+  This enables labeled IPv4 networking with SELinux CIPSO/FIPS188
+  IP options (also known as selopt).
+  
+  See http://www.intercode.com.au/jmorris/selopt/ for more information.
+  
+  If unsure, say N.
+
 LSM port of Openwall
 CONFIG_SECURITY_OWLSM
   This enables the LSM port of the Openwall kernel patch.  This is NOT
diff -urN kernel.orig/net/netsyms.c lsm/net/netsyms.c
--- kernel.orig/net/netsyms.c	Thu Nov  8 13:15:19 2001
+++ lsm/net/netsyms.c	Wed Dec 12 19:49:07 2001
@@ -292,7 +292,9 @@
 EXPORT_SYMBOL(register_inet6addr_notifier);
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
 #endif
-#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || \
+		defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_SECURITY_SELINUX_SELOPT_MODULE)
+extern struct tcp_func  ipv4_specific;
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_release);
 EXPORT_SYMBOL(inet_stream_connect);
diff -urN kernel.orig/security/Makefile lsm/security/Makefile
--- kernel.orig/security/Makefile	Wed Dec 12 19:45:20 2001
+++ lsm/security/Makefile	Wed Dec 12 19:49:07 2001
@@ -7,6 +7,7 @@
 
 # subdirectory list
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
+subdir-$(CONFIG_SECURITY_SELINUX_SELOPT)+= selinux/selopt
 subdir-$(CONFIG_SECURITY_DTE)		+= dte
 
 # Objects that export symbols
diff -urN kernel.orig/security/selinux/Config.in lsm/security/selinux/Config.in
--- kernel.orig/security/selinux/Config.in	Wed Dec 12 19:45:21 2001
+++ lsm/security/selinux/Config.in	Wed Dec 12 19:49:07 2001
@@ -4,4 +4,11 @@
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       bool '  NSA SELinux MLS policy (EXPERIMENTAL)' CONFIG_SECURITY_SELINUX_MLS 
    fi
+   
+   if [ "$CONFIG_SECURITY_IP" != "n" ]; then
+   	bool '  Labeled IP Networking Support' CONFIG_SECURITY_SELINUX_NSID
+   	dep_tristate '    CIPSO/FIPS188 IP Options' CONFIG_SECURITY_SELINUX_SELOPT $CONFIG_SECURITY_SELINUX_NSID $CONFIG_NETLINK
+   fi
 fi
+
+
diff -urN kernel.orig/security/selinux/Makefile lsm/security/selinux/Makefile
--- kernel.orig/security/selinux/Makefile	Wed Dec 12 19:45:21 2001
+++ lsm/security/selinux/Makefile	Wed Dec 12 19:49:07 2001
@@ -8,6 +8,16 @@
 
 selinux-objs := avc.o psid.o hooks.o syscalls.o ss.o
 
+ifeq ($(CONFIG_SECURITY_SELINUX_NSID),y)
+	selinux-objs += nsid.o
+	export-objs += nsid.o
+endif
+
+ifeq ($(CONFIG_SECURITY_SELINUX_SELOPT),y)
+	subdir-$(CONFIG_SECURITY_SELINUX) += selopt
+        selinux-objs += selopt/selopt.o
+endif
+
 obj-y := $(selinux-objs)
 obj-m := $(O_TARGET)
 
diff -urN kernel.orig/security/selinux/hooks.c lsm/security/selinux/hooks.c
--- kernel.orig/security/selinux/hooks.c	Wed Dec 12 19:45:39 2001
+++ lsm/security/selinux/hooks.c	Wed Dec 12 19:49:07 2001
@@ -24,6 +24,7 @@
 #include <linux/security.h>
 #include <linux/capability.h>
 #include <linux/flask/avc.h>
+#include <linux/flask/nsid.h>
 #include <linux/flask/psid.h>
 #include <linux/flask/syscalls.h>
 #include <linux/mm.h>
@@ -163,6 +164,7 @@
 static LIST_HEAD(msg_security_head);
 static LIST_HEAD(ipc_security_head);
 static LIST_HEAD(superblock_security_head);
+static LIST_HEAD(skb_security_head);
 static LIST_HEAD(netdev_security_head);
 
 /* Allocate and free functions for each kind of security blob. */
@@ -351,6 +353,43 @@
 	kfree(sbsec);
 }
 
+/*
+ * This is always called from the core skb allocator, so we don't need an
+ * allocation semaphore.
+ */
+static int skb_alloc_security(struct sk_buff *skb)
+{
+	struct skb_security_struct *ssec;
+	
+	ssec = kmalloc(sizeof(struct skb_security_struct), SAFE_ALLOC);
+	if (!ssec)
+		return -ENOMEM;
+	
+	memset(ssec, 0, sizeof(struct skb_security_struct));
+	atomic_set(&ssec->use, 1);
+	ssec->magic = SELINUX_MAGIC;
+	ssec->skb = skb;
+	ssec->ssid = ssec->msid = ssec->dsid = SECINITSID_UNLABELED;
+	list_add(&ssec->list, &skb_security_head);
+	skb->lsm_security = ssec;
+	return 0;
+}
+
+static void skb_free_security(struct sk_buff *skb)
+{
+	struct skb_security_struct *ssec = skb->lsm_security;
+
+	if (!ssec || ssec->magic != SELINUX_MAGIC)
+		return;
+
+	skb->lsm_security = NULL;
+	       
+	if (atomic_dec_and_test(&ssec->use)) {
+		list_del(&ssec->list);
+		kfree(ssec);
+	}
+}
+
 static struct semaphore netdev_alloc_semaphore;
 
 static int netdev_alloc_security(struct net_device *dev)
@@ -880,6 +919,25 @@
 	return 1;
 }
 
+int skb_precondition(struct sk_buff *skb)
+{
+	struct skb_security_struct *ssec = skb->lsm_security;
+	int rc;
+	
+	rc = ss_precondition();
+	if (rc <= 0)
+		return rc;
+		
+	if (ssec && ssec->magic == SELINUX_MAGIC)
+		return 1;
+	
+	rc = skb_alloc_security(skb);
+	if (rc)
+		return rc;
+	
+	return 1;
+}
+
 /* The network interface security attributes must be initialized before 
  * first use. */
 int netdev_precondition(struct net_device *dev) 
@@ -2544,6 +2602,17 @@
 	return;
 }
 
+static void skb_copy_security(struct skb_security_struct *new,
+                              struct skb_security_struct *old)
+{
+	new->serial    = old->serial;
+	new->ssid      = old->ssid;
+	new->msid      = old->msid;
+	new->dsid      = old->dsid;
+	new->opts      = old->opts;
+	new->mapped    = old->mapped;
+}
+
 static unsigned int selinux_ip_preroute_first(unsigned int hooknum, 
 					      struct sk_buff **pskb,
 					      const struct net_device *in, 
@@ -2628,7 +2697,7 @@
 					  const struct net_device *out,
 					  int (*okfn)(struct sk_buff *))
 {
-	return NF_ACCEPT;
+	return nsid_ip_map_input(hooknum, pskb, in, out, okfn);
 }
 
 static unsigned int selinux_ip_forward_first(unsigned int hooknum, 
@@ -2649,13 +2718,17 @@
 	return NF_ACCEPT;
 }
 
+/*
+ * Add labels here, so packet filter can see labeled packets in the output
+ * chain.  Note that IP header needs to be validated on the output hook.
+ */
 static unsigned int selinux_ip_output_first(unsigned int hooknum, 
 					    struct sk_buff **pskb,
 					    const struct net_device *in,
 					    const struct net_device *out,
 					    int (*okfn)(struct sk_buff *))
 {
-	return NF_ACCEPT;
+	return nsid_ip_label_output(hooknum, pskb, in, out, okfn);
 }
 
 static unsigned int selinux_ip_output_last(unsigned int hooknum, 
@@ -2775,12 +2848,13 @@
 static void selinux_ip_fragment(struct sk_buff *newskb, 
 				const struct sk_buff *oldskb)
 {
+	skb_copy_security(newskb->lsm_security, oldskb->lsm_security);
 	return;
 }
 
 static int selinux_ip_defragment(struct sk_buff *skb) 
 {
-	return 0;
+	return nsid_ip_defragment(skb);
 }
 
 static void selinux_ip_decapsulate(struct sk_buff *skb) 
@@ -2793,13 +2867,10 @@
 	return;
 }
 
-static int selinux_ip_decode_options(struct sk_buff *skb, const char *optptr, unsigned char **pp_ptr) 
+static int selinux_ip_decode_options(struct sk_buff *skb,
+                                     const char *optptr, unsigned char **pp_ptr)
 {
-	if (!skb && !capable(CAP_NET_RAW)) {
-		(const unsigned char *)*pp_ptr = optptr;
-		return -EPERM;
-	}
-	return 0;
+	return nsid_ip_decode_options(skb, optptr, pp_ptr);
 }
 
 static void selinux_netdev_unregister(struct net_device *dev)
@@ -2966,7 +3037,15 @@
 static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, 
 				  int size)
 {
-	return socket_has_perm(current, sock, SOCKET__WRITE);
+
+	int err;
+
+	err = socket_has_perm(current, sock, SOCKET__WRITE);
+	if (err)
+		return err;
+	
+	nsid_sock_sendmsg(sock->sk);
+	return 0;
 }
 
 static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, 
@@ -3876,34 +3955,66 @@
 
 static int selinux_skb_alloc_security(struct sk_buff *skb)
 {
-	/* Unused until we have packet labeling across the network. */
+	int rc;
+	
+	rc = skb_precondition(skb);
+	if (rc <= 0)
+		return rc;
+ 		
 	return 0;
 }
 
 static int selinux_skb_clone(struct sk_buff *newskb, 
 			      const struct sk_buff *oldskb) 
 {
-	/* Unused until we have packet labeling across the network. */
+	struct skb_security_struct *ssec = oldskb->lsm_security;
+
+	atomic_inc(&ssec->use);	
+	newskb->lsm_security = ssec;
+	
 	return 0;
 }
 
 static void selinux_skb_copy(struct sk_buff *newskb, 
 			     const struct sk_buff *oldskb)
 {
-	/* Unused until we have packet labeling across the network. */
+	skb_copy_security(newskb->lsm_security, oldskb->lsm_security);
 	return;
 }
 
+/*
+ * Copy security attributes from sending socket to skb, if appropriate.
+ */
 static void selinux_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
 {
-  	/* Unused until we have packet labeling across the network. */
+	struct skb_security_struct *ssec = skb->lsm_security;
+	
+	if (ssec->msid == SECINITSID_UNLABELED) {
+		struct inode_security_struct *isec;
+		int err;
+		
+		if (!sk->socket)
+			return;
+		
+		if (!sk->socket->inode)
+			return;
+			
+		err = inode_precondition(sk->socket->inode);
+		if (err <= 0)
+			return;
+			
+		isec = sk->socket->inode->i_security;
+		
+		ssec->ssid = isec->sid;
+		ssec->msid = ssec->ssid;
+		/* XXX ssec->dsid = isec->peer_sid; */
+	}
 	return;
 }
 
 static void selinux_skb_free_security(struct sk_buff *skb)
 {
-	/* Unused until we have packet labeling across the network. */
-	return;
+	skb_free_security(skb);
 }
 
 /* module stacking operations */
@@ -4158,6 +4269,8 @@
 
 static int __init selinux_plug_init (void)
 {
+	int rc = 0;
+	
 	init_MUTEX(&task_alloc_semaphore);
 	init_MUTEX(&inode_alloc_semaphore);
 	init_MUTEX(&file_alloc_semaphore);
@@ -4174,6 +4287,12 @@
 
 	avc_init();
 
+	rc = nsid_init();
+	if (rc) {
+		printk(KERN_INFO "SELinux: failed to initialize NSID API\n");
+		return rc;
+	}
+	
 	/* Replace the LSM security syscall with our own entrypoint 
 	   function so that the registers on the stack are available
 	   for the execve_secure system call.  If we didn't need the
@@ -4183,15 +4302,16 @@
 	orig_syscall = sys_call_table[__NR_security];
 	sys_call_table[__NR_security] = sys_security_selinux;
 
-	if (register_security (&selinux_ops)) {
+	rc = register_security (&selinux_ops);
+	if (rc) {
 		printk (KERN_INFO "Failure registering SELinux with the kernel\n");
 		sys_call_table[__NR_security] = orig_syscall;
-		return -EINVAL;
+		return rc;
 	}
 
 	printk (KERN_INFO "SELinux:  module inserted\n");
 
-	return 0;
+	return rc;
 }
 
 
@@ -4251,6 +4371,14 @@
 		msg_msg_free_security(msgsec->msg);
 	}
 
+	/* TBD: assert or override refcounts, synchronize with BR_NETPROTO_LOCK */
+	p = skb_security_head.next;
+	while (p != &skb_security_head) {
+		struct skb_security_struct *ssec = list_entry(p, struct skb_security_struct, list);
+		p = p->next;
+		skb_free_security(ssec->skb);
+	}
+
 	p = netdev_security_head.next;
 	while (p != &netdev_security_head) {
 		struct netdev_security_struct *nsec = list_entry(p, struct netdev_security_struct, list);
@@ -4260,6 +4388,8 @@
 
 	/* XXX:  Need AVC and security server interfaces for cleaning up. */
 		
+	nsid_exit();
+
 	printk (KERN_INFO "SELinux:  module removed\n");
 }
 
diff -urN kernel.orig/security/selinux/include/linux/flask/flnetlink.h lsm/security/selinux/include/linux/flask/flnetlink.h
--- kernel.orig/security/selinux/include/linux/flask/flnetlink.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/include/linux/flask/flnetlink.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,90 @@
+/* -*- linux-c -*- */
+/*
+ * Flask Netlink Interface
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _LINUX_FLASK_FLNETLINK_H_
+#define _LINUX_FLASK_FLNETLINK_H_
+
+/* This probably needs to be in <linux/netlink.h> */
+#define NETLINK_FLASK		6
+
+/* Message types. */
+#define FLMSG_BASE		0x10
+enum {
+	FLMSG_PERIM_ADD = FLMSG_BASE,
+	FLMSG_PERIM_GET,
+	FLMSG_PERIM_DEL,
+	FLMSG_PERIM_FLUSH,
+	
+	FLMSG_CACHE_ADD,
+	FLMSG_CACHE_GET,
+	FLMSG_CACHE_DEL,
+	FLMSG_CACHE_FLUSH,
+	
+	FLMSG_CACHE_MAP_ADD,
+	FLMSG_CACHE_MAP_GET,
+	FLMSG_CACHE_MAP_DEL,
+	FLMSG_CACHE_MAP_REQ,
+	FLMSG_CACHE_MAP_RES,
+	FLMSG_CACHE_MAP_FLUSH,
+	
+	FLMSG_MAX
+};
+
+/* Multicast groups. */
+#define FLN_G_NONE		0x00000000
+#define FLN_G_PERIM		0x00000001
+#define FLN_G_CACHE		0x00000002
+#define FLN_G_ALL		0xffffffff
+
+struct flmsg_base {
+	u_int32_t serial;
+	u_int32_t peer;
+	u_int16_t count;
+};
+
+struct flmsg_attr_perim {
+	u_int32_t addr;
+	u_int32_t mask;
+};
+
+struct flmsg_attr_map {
+	security_id_t lsid;
+	security_id_t rsid;
+};
+
+struct flmsg_perim_entry {
+	struct flmsg_base base;
+	struct flmsg_attr_perim entry;
+};
+
+struct flmsg_map_req {
+	struct flmsg_base base;
+	security_id_t sid[0];
+};
+
+struct flmsg_map_res {
+	struct flmsg_base base;
+	struct flmsg_attr_map map[0];
+};
+
+#ifdef __KERNEL__
+
+extern struct sock *flnl;
+
+typedef int (*fln_dumpfn)(struct sk_buff * skb, struct netlink_callback *cb);
+
+int flnetlink_init(void) __init;
+void flnetlink_exit(void) __exit;
+
+#endif	/* __KERNEL__ */
+                                                                          
+#endif	/* _LINUX_FLASK_FLNETLINK_H_ */
diff -urN kernel.orig/security/selinux/include/linux/flask/nsid.h lsm/security/selinux/include/linux/flask/nsid.h
--- kernel.orig/security/selinux/include/linux/flask/nsid.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/include/linux/flask/nsid.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,110 @@
+/* -*- linux-c -*- */
+/*
+ * Network SID API.
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _LINUX_FLASK_NSID_H_
+#define _LINUX_FLASK_NSID_H_
+
+#if defined(CONFIG_SECURITY_SELINUX_NSID) || defined(CONFIG_SECURITY_SELINUX_NSID_MODULE)
+
+struct nsid_operations {
+	unsigned int (*ip_label_output)(unsigned int hooknum,
+	                                struct sk_buff **pskb,
+	                                const struct net_device *in,
+	                                const struct net_device *out,
+	                                int (*okfn)(struct sk_buff *));
+	                                
+	unsigned int (*ip_map_input)(unsigned int hooknum,
+	                             struct sk_buff **pskb,
+	                             const struct net_device *in,
+	                             const struct net_device *out,
+	                             int (*okfn)(struct sk_buff *));
+
+	int (*ip_decode_options)(struct sk_buff *skb,
+	                         const char *optptr,
+	                         unsigned char **pp_ptr);
+	                         
+	int (*ip_defragment)(struct sk_buff *skb);
+
+	void (*sock_sendmsg)(struct sock *sk);
+};
+
+unsigned int nsid_ip_label_output(unsigned int hooknum,
+                                  struct sk_buff **pskb,
+                                  const struct net_device *in,
+                                  const struct net_device *out,
+                                  int (*okfn)(struct sk_buff *));
+
+unsigned int nsid_ip_map_input(unsigned int hooknum,
+                               struct sk_buff **pskb,
+                               const struct net_device *in,
+                               const struct net_device *out,
+                               int (*okfn)(struct sk_buff *));
+
+int nsid_ip_decode_options(struct sk_buff *skb,
+                           const char *optptr, unsigned char **pp_ptr);
+
+int nsid_ip_defragment(struct sk_buff *skb);
+
+void nsid_sock_sendmsg(struct sock *sk);
+
+int nsid_register_ops(struct nsid_operations *ops);
+int nsid_unregister_ops(struct nsid_operations *ops);
+
+int nsid_init(void) __init;
+void nsid_exit(void) __exit;
+
+#else
+
+#include <linux/netfilter.h>
+
+static inline unsigned int nsid_ip_label_output(unsigned int hooknum,
+                                                struct sk_buff **pskb,
+                                                const struct net_device *in,
+                                                const struct net_device *out,
+                                                int (*okfn)(struct sk_buff *))
+{ return NF_ACCEPT; }
+
+static inline unsigned int nsid_ip_map_input(unsigned int hooknum,
+                                             struct sk_buff **pskb,
+                                             const struct net_device *in,
+                                             const struct net_device *out,
+                                             int (*okfn)(struct sk_buff *))
+{ return NF_ACCEPT; }
+
+
+static inline int nsid_ip_decode_options(struct sk_buff *skb,
+                                         const char *optptr,
+                                         unsigned char **pp_ptr)
+{
+	if (!skb && !capable(CAP_NET_RAW)) {
+		(const unsigned char *)*pp_ptr = optptr;
+		return -EPERM;
+	}
+	return 0;
+}
+
+static inline int nsid_ip_defragment(struct sk_buff *skb)
+{ return 0; }
+
+static inline void nsid_sock_sendmsg(struct sock *sk)
+{ return; }
+
+static inline __init int nsid_init(void)
+{ return 0; }
+
+static inline __exit void nsid_exit(void)
+{ return; }
+
+#endif	/* defined CONFIG_SECURITY_SELINUX_NSID */
+      
+#endif /* _LINUX_FLASK_NSID_H_ */
+
diff -urN kernel.orig/security/selinux/include/linux/flask/selopt.h lsm/security/selinux/include/linux/flask/selopt.h
--- kernel.orig/security/selinux/include/linux/flask/selopt.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/include/linux/flask/selopt.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,108 @@
+/* -*- linux-c -*- */
+/*
+ * SELinux CIPSO/FIPS188 IP Labeling
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _LINUX_FLASK_SELOPT_H_
+#define _LINUX_FLASK_SELOPT_H_
+
+#define SELOPT_DOI		0x10001000	/* Made up */
+#define SELOPT_FREEFORM		7		/* FIPS188, type 7 tag */
+#define SELOPT_BASE_LEN		8		/* type+len+doi+tagtype+taglen*/
+#define SELOPT_NULL_LEN		2		/* Length of null parameter */
+#define SELOPT_U32_LEN		6		/* Length of 32-bit parameter */
+#define SELOPT_BYPASS		1		/* Bypass label */
+#define SELOPT_BYPASS_LEN	SELOPT_NULL_LEN	/* Bypass label length */
+#define SELOPT_SERIAL		2		/* Policy serial label */
+#define SELOPT_SERIAL_LEN	SELOPT_U32_LEN	/* Policy serial length */
+#define SELOPT_SSID		3		/* Source SID label */
+#define SELOPT_SSID_LEN		SELOPT_U32_LEN	/* Source SID length */
+#define SELOPT_MSID		4		/* Message SID label */
+#define SELOPT_MSID_LEN		SELOPT_U32_LEN	/* Message SID length */
+#define SELOPT_DSID		5		/* Destination SID label */
+#define SELOPT_DSID_LEN		SELOPT_U32_LEN	/* Destination SID length */
+
+#define SCMP_PORT		40000		/* IANA unassigned */
+
+#ifdef __KERNEL__
+
+#define DEBUG_LABELING
+
+#define SELOPT_ALIGN(val)		((val + 3) & ~3)
+#define SELOPT_MIN_LEN			SELOPT_BASE_LEN + SELOPT_BYPASS_LEN
+#define SELOPT_STD_LEN			(SELOPT_BASE_LEN \
+                                         + SELOPT_SERIAL_LEN + SELOPT_SSID_LEN)
+#define SELOPT_MAX_TCP_LEN		(SELOPT_BASE_LEN + SELOPT_SERIAL_LEN \
+                                         + SELOPT_SSID_LEN + SELOPT_DSID_LEN)
+
+static inline int selopt_get(struct skb_security_struct *ssec, __u8 val)
+{
+	return ssec->opts & (1 << val);
+}
+
+static inline void selopt_set(struct skb_security_struct *ssec, __u8 val)
+{
+	ssec->opts |= (1 << val);
+}
+
+static inline void selopt_map(struct skb_security_struct *ssec, __u8 val)
+{
+	ssec->mapped |= (1 << val);
+}
+
+static inline void selopt_map_all(struct skb_security_struct *ssec)
+{
+	ssec->mapped = ssec->opts & ~(1 << SELOPT_SERIAL);
+}
+
+static inline int selopt_mapped(struct skb_security_struct *ssec, __u8 val)
+{
+	return ssec->mapped & (1 << val);
+}
+
+static inline int selopt_mapped_all(struct skb_security_struct *ssec)
+{
+	return ssec->mapped == (ssec->opts & ~(1 << SELOPT_SERIAL));
+}
+
+static inline int selopt_map_needed(struct skb_security_struct *ssec, __u8 val)
+{
+	return selopt_get(ssec, val) && !selopt_mapped(ssec, val);
+}
+
+static inline int selopt_maps_needed(struct skb_security_struct *ssec)
+{
+	int i = 0;
+	
+	if (selopt_map_needed(ssec, SELOPT_SSID)) i++;
+	if (selopt_map_needed(ssec, SELOPT_MSID)) i++;
+	if (selopt_map_needed(ssec, SELOPT_DSID)) i++;
+	
+	return i;
+}
+
+#define ip_warn(iph, fmt, args...)                                     \
+                                                                       \
+       do { printk(KERN_WARNING __FUNCTION__ ": " fmt, ##args);        \
+            printk(" [%u.%u.%u.%u->%u.%u.%u.%u]\n",                    \
+            NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));                 \
+       } while (0);
+
+
+#ifdef DEBUG_LABELING
+#define SDBG(x)		do { x; } while (0);
+#else
+#define SDBG(x)		do { } while (0);
+#endif	/* DEBUG_LABELING */
+
+#endif	/* __KERNEL__ */
+
+#endif /* _LINUX_FLASK_SELOPT_H_ */
+
diff -urN kernel.orig/security/selinux/nsid.c lsm/security/selinux/nsid.c
--- kernel.orig/security/selinux/nsid.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/nsid.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,176 @@
+/* -*- linux-c -*- */
+/*
+ * Network SID API
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/netfilter.h>
+#include <linux/flask/nsid.h>
+#include <linux/smp_lock.h>
+#include <linux/brlock.h>
+
+#if defined(CONFIG_SECURITY_SELINUX_NSID) || defined (CONFIG_SECURITY_SELINUX_NSID_MODULE)
+
+static spinlock_t nsid_ops_lock = SPIN_LOCK_UNLOCKED;
+static struct nsid_operations *nsid_ops;
+
+static unsigned int dummy_ip_label_output(unsigned int hooknum,
+                                          struct sk_buff **pskb,
+                                          const struct net_device *in,
+                                          const struct net_device *out,
+                                          int (*okfn)(struct sk_buff *))
+{
+	return NF_ACCEPT;
+}
+
+static unsigned int dummy_ip_map_input(unsigned int hooknum,
+                                       struct sk_buff **pskb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+	return NF_ACCEPT;
+}
+
+
+static int dummy_ip_decode_options(struct sk_buff *skb,
+                                   const char *optptr, unsigned char **pp_ptr)
+{
+	if (!skb && !capable(CAP_NET_RAW)) {
+		(const unsigned char *)*pp_ptr = optptr;
+		return -EPERM;
+	}
+	
+	return 0;
+}
+
+static int dummy_ip_defragment(struct sk_buff *skb)
+{
+	return 0;
+}
+
+static void dummy_sock_sendmsg(struct sock *sk)
+{
+	return;
+}
+
+static struct nsid_operations dummy_ops =
+{
+	ip_label_output:	dummy_ip_label_output,
+	ip_map_input:		dummy_ip_map_input,
+	ip_decode_options:	dummy_ip_decode_options,
+	ip_defragment:		dummy_ip_defragment,
+	sock_sendmsg:		dummy_sock_sendmsg,
+};
+
+unsigned int nsid_ip_label_output(unsigned int hooknum,
+                                  struct sk_buff **pskb,
+                                  const struct net_device *in,
+                                  const struct net_device *out,
+                                  int (*okfn)(struct sk_buff *))
+{
+	return nsid_ops->ip_label_output(hooknum, pskb, in, out, okfn);
+}
+
+unsigned int nsid_ip_map_input(unsigned int hooknum,
+                               struct sk_buff **pskb,
+                               const struct net_device *in,
+                               const struct net_device *out,
+                               int (*okfn)(struct sk_buff *))
+{
+	return nsid_ops->ip_map_input(hooknum, pskb, in, out, okfn);
+}
+
+
+int nsid_ip_decode_options(struct sk_buff *skb,
+                           const char *optptr, unsigned char **pp_ptr)
+{
+	return nsid_ops->ip_decode_options(skb, optptr, pp_ptr);
+}
+
+int nsid_ip_defragment(struct sk_buff *skb)
+{
+	return nsid_ops->ip_defragment(skb);
+}
+
+void nsid_sock_sendmsg(struct sock *sk)
+{
+	nsid_ops->sock_sendmsg(sk);
+}
+
+int nsid_register_ops(struct nsid_operations *ops)
+{
+	spin_lock_bh(&nsid_ops_lock);
+	
+	if (nsid_ops && nsid_ops != &dummy_ops) {
+		spin_unlock_bh(&nsid_ops_lock);
+		printk(KERN_WARNING "nsid ops already registered\n");
+		return -EBUSY;
+	}
+	
+	nsid_ops = ops;
+	spin_unlock_bh(&nsid_ops_lock);
+	
+	return 0;
+}
+
+int nsid_unregister_ops(struct nsid_operations *ops)
+{
+	spin_lock_bh(&nsid_ops_lock);
+	
+	if (nsid_ops != ops) {
+		spin_unlock_bh(&nsid_ops_lock);
+		printk(KERN_WARNING "failed to unregister other nsid ops\n");
+		return -EINVAL;
+	}
+	
+	/* Synchronize with net_rx_action() */
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	nsid_ops = &dummy_ops;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	
+	spin_unlock_bh(&nsid_ops_lock);
+	
+	return 0;
+}
+
+__init int nsid_init(void)
+{
+	int rc;
+	
+	rc = nsid_register_ops(&dummy_ops);
+	if (rc)
+		return rc;
+		
+	printk(KERN_INFO "SELinux: NSID API initialized\n");
+	
+	return 0;
+}
+
+__exit void nsid_exit(void)
+{
+	return;
+}
+
+EXPORT_SYMBOL(nsid_ip_label_output);
+EXPORT_SYMBOL(nsid_ip_map_input);
+EXPORT_SYMBOL(nsid_ip_decode_options);
+EXPORT_SYMBOL(nsid_ip_defragment);
+EXPORT_SYMBOL(nsid_sock_sendmsg);
+EXPORT_SYMBOL(nsid_register_ops);
+EXPORT_SYMBOL(nsid_unregister_ops);
+
+#endif	/* CONFIG_SECURITY_SELINUX_NSID */
diff -urN kernel.orig/security/selinux/selinux_plug.h lsm/security/selinux/selinux_plug.h
--- kernel.orig/security/selinux/selinux_plug.h	Wed Dec 12 19:45:39 2001
+++ lsm/security/selinux/selinux_plug.h	Wed Dec 12 19:49:07 2001
@@ -96,6 +96,20 @@
         avc_entry_ref_t avcr;		/* reference to permissions */
 };
 
+struct skb_security_struct {
+	unsigned long magic;            /* magic number for this module */
+	struct sk_buff *skb;            /* back pointer */
+	struct list_head list;          /* list of skb_security_struct */
+	__u8 opts;                      /* Bitmap of current options */
+	__u8 mapped;			/* Bitmap of mapped SIDs */
+	atomic_t use;                   /* reference count */
+	__u32 serial;                   /* Policy ID used to label datagram */
+	security_id_t ssid;             /* Source SID */
+	security_id_t msid;             /* Message SID  */
+	security_id_t dsid;             /* Destination SID */
+	void *data;			/* Implementation specific data */
+};
+
 struct netdev_security_struct {
         unsigned long magic;		/* magic number for this module */
 	struct net_device *dev;		/* back pointer to network device */
diff -urN kernel.orig/security/selinux/selopt/Makefile lsm/security/selinux/selopt/Makefile
--- kernel.orig/security/selinux/selopt/Makefile	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/Makefile	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,18 @@
+#
+# Makefile for building selopt (SELinux labeled networking with CIPSO/FIPS188
+# IP options).
+#
+
+EXTRA_CFLAGS += -I../include -I..
+
+O_TARGET := selopt.o
+
+selopt-objs := selopt_core.o perimtab.o flnetlink.o cache.o queue.o
+
+obj-y := $(selopt-objs)
+obj-m := $(O_TARGET)
+
+all: $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff -urN kernel.orig/security/selinux/selopt/cache.c lsm/security/selinux/selopt/cache.c
--- kernel.orig/security/selinux/selopt/cache.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/cache.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,616 @@
+/* -*- linux-c -*- */
+/*
+ * Peer mapping cache.
+ *
+ * This structure contains a list of peers within our security perimeter
+ * with which we exchange labeled packets.  Each entry contains the address
+ * of the peer, a remote policy serial number, and a list of local to remote
+ * SID mappings.  If any SCMP message is received from a peer with a different
+ * policy serial number, the entire map for that peer is invalidated.
+ *
+ * The selopt_cache_lock protects the list containing the peer entries, as well
+ * as preventing peers from being destroyed while their refcounts are being
+ * incremented and decremented.  The only exception to this is when the entry
+ * is created as it is not yet linked into the list.
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/netlink.h>
+#include <linux/flask/flask_types.h>
+#include <linux/flask/flnetlink.h>
+#include "selinux_plug.h"
+#include <linux/flask/selopt.h>
+#include "cache.h"
+#include "queue.h"
+
+#define SELOPT_CACHE_PEER_MAX	1024	/* Max. total peers. */
+#define SELOPT_CACHE_MAP_MAX	2048	/* Max. maps per peer. */
+
+static LIST_HEAD(selopt_cache_list);
+static rwlock_t selopt_cache_lock = RW_LOCK_UNLOCKED;
+volatile __u32 global_map_total;
+volatile __u32 peer_total;
+
+/*
+ * Increment peer reference count.
+ */
+inline void selopt_cache_peer_get(struct selopt_cache_peer *peer)
+{
+	atomic_inc(&peer->use);
+}
+  
+/*
+ * Decrement peer reference count.
+ */      
+inline void selopt_cache_peer_put(struct selopt_cache_peer *peer)
+{
+	atomic_dec(&peer->use);
+}
+
+/*
+ * Find a peer in the cache by address.
+ */
+static inline struct selopt_cache_peer *__selopt_cache_peer_find(__u32 addr)
+{
+	struct list_head *i;
+	
+	for (i = selopt_cache_list.next; i != &selopt_cache_list; i = i->next) {
+		struct selopt_cache_peer *peer = (struct selopt_cache_peer *)i;
+		
+		if (addr == peer->addr)
+			return peer;
+	}
+	return NULL;
+}
+
+/*
+ * Add a peer entry to the cache, possibly returning an existing
+ * entry via old instead if someone raced us here.
+ */
+static inline int __selopt_cache_peer_append(struct selopt_cache_peer *new,
+                                             struct selopt_cache_peer **old)
+{
+	struct selopt_cache_peer *peer;
+	
+	peer = __selopt_cache_peer_find(new->addr);
+	if (peer) {
+		if (old)
+			*old = peer;
+		return -EEXIST;
+	}
+	
+	if (peer_total >= SELOPT_CACHE_PEER_MAX) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "SELinux: clamping cache "
+			       "at %u entries\n", SELOPT_CACHE_PEER_MAX);
+		return -ENOSPC;
+	}
+	
+	list_add_tail(&new->list, &selopt_cache_list);
+	peer_total++;
+	
+	return 0;
+}
+
+/*
+ * Lookup the SID map for a peer, given the remote SID.
+ */
+static inline struct selopt_cache_map *
+__selopt_cache_map_lookup(struct selopt_cache_peer *peer, security_id_t rsid)
+{
+	struct list_head *i;
+	struct selopt_cache_map *map = peer->map;
+
+	if (rsid == SECSID_NULL)
+		return NULL;
+		
+	for (i = map->list.next; i != &map->list; i = i->next) {
+		struct selopt_cache_map *entry = (struct selopt_cache_map *)i;
+		
+		if (entry->rsid == rsid)
+			return entry;
+	}
+	
+	return NULL;
+}
+
+/*
+ * Return an equivalent local SID given a remote SID, for the specified peer.
+ */
+static inline security_id_t
+__selopt_cache_map_sid(struct selopt_cache_peer *peer, security_id_t rsid)
+{
+	struct selopt_cache_map *map;
+
+	map = __selopt_cache_map_lookup(peer, rsid);
+	if (map == NULL)
+		return SECSID_NULL;
+		
+	return map->lsid;
+}
+
+/*
+ * Create a new map entry for a peer given a remote SID.
+ * TBD: try and move the kmalloc out of the spinlock.
+ */
+static inline struct selopt_cache_map *
+__selopt_cache_map_create(struct selopt_cache_peer *peer,
+                          security_id_t rsid, int *err)
+{
+	struct selopt_cache_map *map;
+
+	if (peer->map_total >= SELOPT_CACHE_MAP_MAX) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "SELinux: clamping maps "
+			       "at %u entries for peer %u.%u.%u.%u\n",
+			       SELOPT_CACHE_MAP_MAX, NIPQUAD(peer->addr));
+		*err = -ENOSPC;
+		return NULL;
+	}
+
+	map = kmalloc(sizeof(struct selopt_cache_map), GFP_ATOMIC);
+	if (map == NULL) {
+		printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+		*err = -ENOMEM;
+		return NULL;
+	}
+	
+	memset(map, 0, sizeof(struct selopt_cache_map));
+	
+	map->rsid = rsid;
+	map->lsid = SECSID_NULL;
+	
+	list_add_tail(&map->list, &peer->map->list);
+	
+	peer->map_total++;
+	global_map_total++;
+	
+	*err = 0;
+	return map;
+}
+
+/*
+ * Flush all map entries for a peer.
+ */
+static inline void __selopt_cache_map_flush(struct selopt_cache_peer *peer)
+{
+	struct selopt_cache_map *map = peer->map;
+	
+	while (map->list.next != &map->list) {
+		struct list_head *i = map->list.next;
+		
+		list_del(i);
+		kfree(i);
+		peer->map_total--;
+		global_map_total--;
+	}
+}
+
+/*
+ * Destroy a peer entry and it's mappings.
+ */
+static inline void __selopt_cache_peer_destroy(struct selopt_cache_peer *peer)
+{
+	write_lock_bh(&peer->map_lock);
+	
+	if (peer->map) {
+		__selopt_cache_map_flush(peer);
+		kfree(peer->map);
+		peer->map = NULL;
+	}
+	
+	write_unlock_bh(&peer->map_lock);
+	
+	list_del(&peer->list);
+}
+
+/*
+ * Peer destructor, protected by refcount.
+ */
+static void selopt_cache_peer_destroy(struct selopt_cache_peer *peer)
+{
+	write_lock_bh(&selopt_cache_lock);
+	
+	if (atomic_dec_and_test(&peer->use)) {
+		__selopt_cache_peer_destroy(peer);
+		kfree(peer);
+	}
+	
+	write_unlock_bh(&selopt_cache_lock);
+}
+
+/*
+ * XXX: reschedule for refcounts
+ */
+void selopt_cache_flush(void)
+{
+	write_lock_bh(&selopt_cache_lock);
+	
+	while (selopt_cache_list.next != &selopt_cache_list) {
+		struct selopt_cache_peer *peer =
+			(struct selopt_cache_peer *)selopt_cache_list.next;
+		
+		if (atomic_read(&peer->use) != 1)
+			printk(KERN_ERR "BUG: Invalid refcount for peer!\n");
+	
+		__selopt_cache_peer_destroy(peer);
+		kfree(peer);
+	}	
+	
+	write_unlock_bh(&selopt_cache_lock);
+}
+
+/*
+ * Fill an skb with an flnetlink peer message.  Called during a netlink dump.
+ */
+static int selopt_cache_fill_map(struct sk_buff *skb,
+                                 struct selopt_cache_peer *peer,
+                                 struct selopt_cache_map *map,
+                                 int type, u32 pid, u32 seq)
+{
+	__u16 datalen;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+	struct flmsg_map_res *msg;
+
+	datalen = sizeof(struct flmsg_map_res) + sizeof(struct flmsg_attr_map);
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, datalen);
+	
+	if (pid)
+		nlh->nlmsg_flags |= NLM_F_MULTI;
+
+	msg = NLMSG_DATA(nlh);
+	
+	msg->base.count = 1;
+	msg->base.peer = peer->addr;
+	msg->base.serial = peer->addr;
+	msg->map[0].lsid = map->lsid;
+	msg->map[0].rsid = map->rsid;
+	
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+/*
+ * Netlink callback for dumping the entire cache to userspace.  Callback args[0]
+ * is the peer index and args[1] is the map index.
+ */
+int selopt_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int peer_idx, map_idx;
+	int s_peer_idx = cb->args[0];
+	int s_map_idx = cb->args[1];
+	struct list_head *i, *j;
+
+	read_lock_bh(&selopt_cache_lock);
+
+	for (i = selopt_cache_list.next, peer_idx = map_idx = 0;
+	     i != &selopt_cache_list; i = i->next, peer_idx++) {
+	     	struct selopt_cache_map *map;
+		struct selopt_cache_peer *peer = (struct selopt_cache_peer *)i;
+		
+		if (peer_idx < s_peer_idx)
+			continue;
+		
+		/*
+		 * Found the current peer, start dumping it's map.
+		 */
+		map = peer->map;
+		
+		read_lock_bh(&peer->map_lock);
+		
+		for (j = map->list.next, map_idx = 0;
+		     j != &map->list; j = j->next, map_idx++) {
+			struct selopt_cache_map *entry = (struct selopt_cache_map *)j;
+			
+			if (map_idx < s_map_idx)
+				continue;
+				
+			if (selopt_cache_fill_map(skb, peer, entry, FLMSG_CACHE_GET,
+		    	    NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0) {
+				read_unlock_bh(&peer->map_lock);
+				goto escape;
+			}
+		}
+		
+		read_unlock_bh(&peer->map_lock);
+	}
+
+escape:
+	read_unlock_bh(&selopt_cache_lock);
+	
+	cb->args[0] = peer_idx;
+	cb->args[1] = map_idx;
+	
+	return skb->len;
+}
+
+/*
+ * Find or create a peer entry in the cache.
+ *
+ * Returns the specified peer entry with refcount incremented, sets rc to
+ * SELOPT_CACHE_NEW if the entry was created by this request.
+ *
+ * Caller must decrement refcount after it is finished using the entry
+ * returned from here.
+ */
+struct selopt_cache_peer *selopt_cache_peer_lookup(__u32 addr, int *err)
+{
+	int rc;
+	struct selopt_cache_peer *new, *old;
+	
+	read_lock_bh(&selopt_cache_lock);
+	
+	old = __selopt_cache_peer_find(addr);
+	if (old)
+		selopt_cache_peer_get(old);
+		
+	read_unlock_bh(&selopt_cache_lock);
+	
+	if (old)
+		return old;
+	
+	/*
+	 * Slow path, create new entry and add it to the cache.
+	 */
+	new = kmalloc(sizeof(struct selopt_cache_peer), SAFE_ALLOC);
+	if (new == NULL) {
+		*err = -ENOMEM;
+		return NULL;
+	}
+	
+	memset(new, 0, sizeof(struct selopt_cache_peer));
+	
+	new->map = kmalloc(sizeof(struct flmsg_attr_map), SAFE_ALLOC);
+	if (new->map == NULL) {
+		kfree(new);
+		*err = -ENOMEM;
+		return NULL;
+	}
+	
+	memset(new->map, 0, sizeof(struct flmsg_attr_map));
+	INIT_LIST_HEAD(&new->map->list);
+	
+	INIT_LIST_HEAD(&new->list);
+	atomic_set(&new->use, 1);
+	new->map_lock = RW_LOCK_UNLOCKED;
+	new->addr = addr;
+	
+	write_lock_bh(&selopt_cache_lock);
+	
+	rc = __selopt_cache_peer_append(new, &old);
+	
+	if (rc >= 0) {
+		*err = SELOPT_CACHE_NEW;
+		selopt_cache_peer_get(new);
+		write_unlock_bh(&selopt_cache_lock);
+		return new;
+	}
+
+	/*
+	 * If someone raced us to this, return their entry and destroy ours.
+	 */
+	if (rc == -EEXIST) {
+		selopt_cache_peer_get(old);
+		write_unlock_bh(&selopt_cache_lock);
+		selopt_cache_peer_destroy(new);
+		return old;
+	}
+	
+	write_unlock_bh(&selopt_cache_lock);
+	selopt_cache_peer_destroy(new);
+	*err = rc;
+	return NULL;
+}
+
+/*
+ * Attempt to map a SID from the cache.  If it's not there, the caller
+ * can schedule a remote lookup via SCMP.
+ */
+static int selopt_cache_map_sid(struct selopt_cache_peer *peer,
+                                security_id_t rsid)
+{
+	security_id_t lsid;
+	
+	read_lock_bh(&peer->map_lock);
+	lsid = __selopt_cache_map_sid(peer, rsid);
+	read_unlock_bh(&peer->map_lock);
+	
+	return lsid;
+}
+
+/*
+ * Map SIDs from cache, caller can determine which SIDs need SCMP lookups.
+ */
+void selopt_cache_map_sids(struct selopt_cache_peer *peer,
+                          struct sk_buff *skb, int status)
+{
+	struct skb_security_struct *ssec = skb->lsm_security;
+
+	if (status != SELOPT_CACHE_NEW) {
+	
+		if (selopt_get(ssec, SELOPT_SSID)) {
+			security_id_t lsid;
+			
+			lsid = selopt_cache_map_sid(peer, ssec->ssid);
+			if (lsid != SECSID_NULL) {
+				selopt_map(ssec, SELOPT_SSID);
+				ssec->ssid = lsid;
+			}
+		}
+		
+		if (selopt_get(ssec, SELOPT_MSID)) {
+			security_id_t lsid;
+			
+			lsid = selopt_cache_map_sid(peer, ssec->msid);
+			if (lsid != SECSID_NULL) {
+				selopt_map(ssec, SELOPT_MSID);
+				ssec->msid = lsid;
+			}
+		}
+		
+		if (selopt_get(ssec, SELOPT_DSID)) {
+			security_id_t lsid;
+			
+			lsid = selopt_cache_map_sid(peer, ssec->dsid);
+			if (lsid != SECSID_NULL) {
+				selopt_map(ssec, SELOPT_DSID);
+				ssec->dsid = lsid;
+			}
+		}
+	}
+	
+	return;
+}
+
+/*
+ * Update and possibly create a new peer map entry.
+ */
+int selopt_cache_map_update(struct selopt_cache_peer *peer,
+                            security_id_t rsid, security_id_t lsid)
+{
+	int err = 0;
+	struct selopt_cache_map *map;
+
+	write_lock_bh(&peer->map_lock);
+	
+	map = __selopt_cache_map_lookup(peer, rsid);
+	
+	if (map == NULL) {
+		map = __selopt_cache_map_create(peer, rsid, &err);
+		if (map == NULL) {
+			write_unlock_bh(&peer->map_lock);
+			return err;
+		}
+	}
+
+	if (map->lsid != lsid)
+		map->lsid = lsid;
+
+	write_unlock_bh(&peer->map_lock);
+	
+	return err;
+}
+
+/*
+ * Handle mapping response, add to cache then run packet queue evictor.
+ */
+int selopt_cache_map_response(struct flmsg_map_res *res)
+{
+	int i, err;
+	struct selopt_cache_peer *peer;
+
+	/*
+	 * Get the cache entry for this peer.
+	 */
+	peer = selopt_cache_peer_lookup(res->base.peer, &err);
+	if (peer == NULL)
+		return err;
+
+	/*
+	 * Update the mappings from the map response.
+	 */
+	for (i = 0; i < res->base.count; i++) {
+		int rc;
+		struct flmsg_attr_map map = res->map[i];
+		
+		rc = selopt_cache_map_update(peer, map.rsid, map.lsid);
+		if (rc) {
+			selopt_cache_peer_put(peer);
+			return rc;
+		}
+	}
+
+	selopt_queue_evict(peer);
+	selopt_cache_peer_put(peer);
+	
+	return 0;
+}
+
+/*
+ * Build and broadcast a flnetlink map request message.
+ */
+int selopt_cache_request_mappings(struct sk_buff *skb)
+{
+	__u16 datalen, nsids;
+	unsigned char *oldtail;
+	struct nlmsghdr *nlh;
+	struct sk_buff *nskb;
+	struct iphdr *iph = skb->nh.iph;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	struct flmsg_map_req *req;
+	
+	SDBG(ip_warn(iph, "ssid=%u msid=%u dsid=%u",
+	             ssec->ssid, ssec->msid, ssec->dsid));
+	
+	nsids = selopt_maps_needed(ssec);
+	datalen = sizeof(struct flmsg_map_req) + nsids * sizeof(security_id_t);
+	
+	nskb = alloc_skb(NLMSG_SPACE(datalen), SAFE_ALLOC);
+	if (nskb == NULL) {
+		printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+	
+	oldtail = nskb->tail;
+	
+	nlh = NLMSG_PUT(nskb, 0, 0, FLMSG_CACHE_MAP_REQ, datalen);
+	req = NLMSG_DATA(nlh);
+	
+	memset(req, 0, datalen);
+	
+	req->base.count = nsids;
+	req->base.peer = iph->saddr;
+	req->base.serial = ssec->serial;
+	
+	if (selopt_map_needed(ssec, SELOPT_SSID))
+		req->sid[0] = ssec->ssid;
+	
+	if (selopt_map_needed(ssec, SELOPT_MSID))
+		req->sid[1] = ssec->msid;
+		
+	if (selopt_map_needed(ssec, SELOPT_DSID))
+		req->sid[2] = ssec->dsid;
+	
+	nlh->nlmsg_len = nskb->tail - oldtail;
+	netlink_broadcast(flnl, nskb, 0, FLN_G_CACHE, SAFE_ALLOC);
+	        
+	return 0;
+
+nlmsg_failure:
+	kfree_skb(nskb);
+	return -ENOBUFS;
+}
+
+__init int selopt_cache_init(void)
+{
+	return 0;
+}
+
+__exit void selopt_cache_exit(void)
+{
+	selopt_cache_flush();
+	return;
+}
diff -urN kernel.orig/security/selinux/selopt/cache.h lsm/security/selinux/selopt/cache.h
--- kernel.orig/security/selinux/selopt/cache.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/cache.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,56 @@
+/* -*- linux-c -*- */
+/*
+ * Peer NSID mapping cache.
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _SELOPT_CACHE_H_
+#define _SELOPT_CACHE_H_
+
+#include <linux/flask/flnetlink.h>
+
+#define SELOPT_CACHE_NEW	1
+
+struct selopt_cache_map {
+	struct list_head list;
+	security_id_t lsid;
+	security_id_t rsid;
+};
+
+struct selopt_cache_peer {
+	struct list_head list;
+	rwlock_t map_lock;
+	struct selopt_cache_map *map;
+	__u32 map_total;
+	__u32 addr;
+	__u32 serial;
+	atomic_t use;
+};
+
+struct selopt_cache_peer *selopt_cache_peer_lookup(__u32 addr, int *err);
+
+inline void selopt_cache_peer_get(struct selopt_cache_peer *peer);
+
+inline void selopt_cache_peer_put(struct selopt_cache_peer *peer);
+
+void selopt_cache_map_sids(struct selopt_cache_peer *peer,
+                           struct sk_buff *skb, int status);
+
+int selopt_cache_request_mappings(struct sk_buff *skb);
+
+int selopt_cache_map_response(struct flmsg_map_res *res);
+
+int selopt_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
+
+void selopt_cache_flush(void);
+
+int selopt_cache_init(void) __init;
+void selopt_cache_exit(void) __exit;
+
+#endif	/* _SELOPT_CACHE_H_ */
diff -urN kernel.orig/security/selinux/selopt/flnetlink.c lsm/security/selinux/selopt/flnetlink.c
--- kernel.orig/security/selinux/selopt/flnetlink.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/flnetlink.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,283 @@
+/* -*- linux-c -*- */
+/*
+ * Flask Netlink User/Kernel Interface
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/sock.h>
+#include <linux/flask/flask_types.h>
+#include <linux/flask/flnetlink.h>
+#include "selinux_plug.h"
+#include <linux/flask/selopt.h>
+#include "perimtab.h"
+#include "cache.h"
+
+static DECLARE_MUTEX(flnl_sem);
+struct sock *flnl = NULL;
+
+/*
+ * Parse a (non-dump) flnetlink message from userspace.
+ */
+static inline int flnetlink_parse_msg(unsigned char *msg, unsigned char type,
+                                      __u32 seq, unsigned int len)
+{
+	int rc = 0;
+
+	switch (type) {
+	case FLMSG_PERIM_ADD:
+	{
+		struct flmsg_perim_entry *flmsg;
+		
+		if (len != sizeof(struct flmsg_perim_entry)) {
+			rc = -EINVAL;
+			break;
+		}
+		
+		flmsg = (struct flmsg_perim_entry *)msg;
+		rc = selopt_perimtab_append(flmsg->entry.addr, flmsg->entry.mask);
+		break;
+	}
+	
+	case FLMSG_PERIM_DEL:
+	{
+		struct flmsg_perim_entry *flmsg;
+		
+		if (len != sizeof(struct flmsg_perim_entry)) {
+			rc = -EINVAL;
+			break;
+		}
+		
+		flmsg = (struct flmsg_perim_entry *)msg;
+		rc = selopt_perimtab_delete(flmsg->entry.addr, flmsg->entry.mask);
+		break;
+	}
+	
+	case FLMSG_PERIM_FLUSH:
+		selopt_perimtab_flush();
+		break;
+		
+	case FLMSG_CACHE_MAP_RES:
+	{
+		struct flmsg_map_res *flmsg = (struct flmsg_map_res *)msg;
+		
+		if (len != sizeof(struct flmsg_map_res) +
+		    flmsg->base.count * sizeof(struct flmsg_attr_map)) {
+			rc = -EINVAL;
+			break;
+		}
+
+		rc = selopt_cache_map_response(flmsg);
+		break;
+	}
+	
+	case FLMSG_CACHE_FLUSH:
+		selopt_cache_flush();
+		break;
+		
+	case FLMSG_PERIM_GET:
+	
+		rc = -ENOSYS;
+		break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	};
+	
+	return rc;
+}
+
+/*
+ * Called when a netlink dump completes.
+ */
+static int flnetlink_done(struct netlink_callback *cb)
+{
+	return 0;
+}
+
+/*
+ * Process one flnetlink message, brodcast any changes made.
+ */
+static inline int flnetlink_rcv_msg(struct sk_buff *skb,
+                                    struct nlmsghdr *nlh, int *errp)
+{
+	int type, rc;
+	
+	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
+		return 0;
+
+	type = nlh->nlmsg_type;
+	
+	if (type < FLMSG_BASE)
+		return 0;
+	
+	if (type >= FLMSG_MAX) {
+		*errp = -EINVAL;
+		return -1;
+	}
+
+	if (security_ops->netlink_recv(skb)) {
+		*errp = -EPERM;
+		return -1;
+	}
+
+	/*
+	 * Netlink dump starter.
+	 */
+	if (nlh->nlmsg_flags&NLM_F_DUMP) {
+		__u32 rlen;
+		fln_dumpfn dumpfn = NULL;
+		
+		switch (type) {
+		case FLMSG_PERIM_GET:
+			dumpfn = selopt_perimtab_dump;
+			break;
+			
+		case FLMSG_CACHE_GET:
+			dumpfn = selopt_cache_dump;
+			break;
+		
+		default:
+			*errp = -EINVAL;
+			return -1;
+		}
+		
+		*errp = netlink_dump_start(flnl, skb, nlh,
+		                           dumpfn, flnetlink_done);
+		if (*errp != 0)
+			return -1;
+		
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		
+		if (rlen > skb->len)
+			rlen = skb->len;
+			
+		skb_pull(skb, rlen);
+		return -1;
+	}
+
+	/*
+	 * Non-dump requests.
+	 */
+	rc = flnetlink_parse_msg(NLMSG_DATA(nlh), type,
+	                         nlh->nlmsg_seq, skb->len - NLMSG_LENGTH(0));
+	if (rc) {
+		*errp = rc;
+		return -1;
+	}
+
+	switch (type) {
+	case FLMSG_PERIM_ADD:
+	case FLMSG_PERIM_DEL:
+	case FLMSG_PERIM_FLUSH:
+		atomic_inc(&skb->users);
+		netlink_broadcast(flnl, skb, 0, FLN_G_PERIM, GFP_KERNEL);
+		break;
+	
+	case FLMSG_CACHE_FLUSH:
+		atomic_inc(&skb->users);
+		netlink_broadcast(flnl, skb, 0, FLN_G_CACHE, GFP_KERNEL);
+		break;
+		
+	default:
+		break;
+	}
+	
+	return 0;
+}
+
+/*
+ * Process one skb from userspace, possibly containing multiple
+ * flnetlink messages.
+ */
+static inline int flnetlink_rcv_skb(struct sk_buff *skb)
+{
+	int err;
+	struct nlmsghdr *nlh;
+	
+	while (skb->len >= NLMSG_SPACE(0)) {
+		u32 rlen;
+		
+		nlh = (struct nlmsghdr *)skb->data;
+		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+			return 0;
+		
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (rlen > skb->len)
+			rlen = skb->len;
+			
+		if (flnetlink_rcv_msg(skb, nlh, &err)) {
+			if (err == 0)
+				return -1;
+			netlink_ack(skb, nlh, err);
+		
+		} else if (nlh->nlmsg_flags&NLM_F_ACK)
+			netlink_ack(skb, nlh, 0);
+		
+		skb_pull(skb, rlen);
+	}
+	
+	return 0;
+}
+
+/*
+ * Socket receive routine.
+ */
+static void flnetlink_rcv(struct sock *sk, int len)
+{
+	do {
+		struct sk_buff *skb;
+		
+		if (down_trylock(&flnl_sem))
+			return;
+
+		while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+			if (flnetlink_rcv_skb(skb)) {
+				if(skb->len)
+					skb_queue_head(&sk->receive_queue, skb);
+				else
+					kfree_skb(skb);
+				break;
+			}
+			kfree_skb(skb);
+		}
+		up(&flnl_sem);
+		
+	} while (flnl && flnl->receive_queue.qlen);
+
+	return;
+}
+
+__init int flnetlink_init(void)
+{
+	flnl = netlink_kernel_create(NETLINK_FLASK, flnetlink_rcv);
+	if (flnl == NULL) {
+		printk(KERN_WARNING "SELinux: flnetlink init failed\n");
+		return -EADDRNOTAVAIL; 
+	}
+	
+	return 0;
+}
+
+__exit void flnetlink_exit(void)
+{
+	sock_release(flnl->socket);
+}
diff -urN kernel.orig/security/selinux/selopt/perimtab.c lsm/security/selinux/selopt/perimtab.c
--- kernel.orig/security/selinux/selopt/perimtab.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/perimtab.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,279 @@
+/* -*- linux-c -*- */
+/*
+ * Perimeter Table
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/flask/flask_types.h>
+#include <linux/flask/flnetlink.h>
+
+#include "perimtab.h"
+
+#define SELOPT_PERIMTAB_MAX 1024
+
+struct selopt_perimtab_entry {
+	struct list_head list;
+	__u32 addr;
+	__u32 mask;
+};
+
+static LIST_HEAD(selopt_perimtab_list);
+static rwlock_t selopt_perimtab_lock = RW_LOCK_UNLOCKED;
+static volatile __u32 selopt_perimtab_total;
+
+static inline struct selopt_perimtab_entry *__selopt_perimtab_find(__u32 addr,
+                                                                   __u32 mask)
+{
+	struct list_head *i;
+	
+	for (i = selopt_perimtab_list.next;
+	     i != &selopt_perimtab_list; i = i->next) {
+		struct selopt_perimtab_entry *entry =
+			(struct selopt_perimtab_entry *)i;
+		
+		if (addr == entry->addr && mask == entry->mask)
+			return entry;
+	}
+	
+	return NULL;
+}
+
+static inline int __selopt_perimtab_append(struct selopt_perimtab_entry *entry)
+{
+	if (selopt_perimtab_total >= SELOPT_PERIMTAB_MAX) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "SELinux: clamping perimeter table "
+			       "at %u entries\n", SELOPT_PERIMTAB_MAX);
+		return -ENOSPC;
+	}
+	
+	if (__selopt_perimtab_find(entry->addr, entry->mask))
+		return -EEXIST;
+
+	list_add_tail(&entry->list, &selopt_perimtab_list);
+	selopt_perimtab_total++;
+	
+	return 0;
+}
+
+static inline int __selopt_perimtab_match(__u32 addr)
+{
+	struct list_head *i;
+
+	for (i = selopt_perimtab_list.next;
+	     i != &selopt_perimtab_list; i = i->next) {
+		struct selopt_perimtab_entry *entry =
+			(struct selopt_perimtab_entry *)i;
+	
+		if (entry->addr == (addr & entry->mask))
+			return 1;
+	}
+	return 0;
+}
+
+static inline int __selopt_perimtab_delete(__u32 addr, __u32 mask)
+{
+	struct selopt_perimtab_entry *entry;
+	
+	entry = __selopt_perimtab_find(addr, mask);
+	if (entry == NULL)
+		return -ENOENT;
+	
+	list_del(&entry->list);
+	kfree(entry);
+	selopt_perimtab_total--;
+	
+	return 0;
+}
+
+static inline void __selopt_perimtab_flush(void)
+{
+	while (selopt_perimtab_list.next != &selopt_perimtab_list) {
+		struct list_head *i = selopt_perimtab_list.next;
+		
+		list_del(i);
+		kfree(i);
+		selopt_perimtab_total--;
+	}
+}
+
+/*
+ * Notify userspace that the perimeter table is being flushed.
+ */
+static void selopt_perimtab_flush_notify(void)
+{
+	unsigned char *oldtail;
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	
+	skb = alloc_skb(NLMSG_SPACE(0), SAFE_ALLOC);
+	if (skb == NULL) {
+		printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+		return;
+	}
+	
+	oldtail = skb->tail;
+	nlh = NLMSG_PUT(skb, 0, 0, FLMSG_PERIM_FLUSH, NLMSG_SPACE(0));
+	nlh->nlmsg_len = skb->tail - oldtail;
+	netlink_broadcast(flnl, skb, 0, FLN_G_PERIM, GFP_KERNEL);
+
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+	printk(KERN_WARNING "%s: netlink message failure\n", __FUNCTION__);
+}
+
+
+/*
+ * Flush all entries from the table.
+ */
+void selopt_perimtab_flush(void)
+{
+	selopt_perimtab_flush_notify();
+	
+	write_lock_bh(&selopt_perimtab_lock);
+	__selopt_perimtab_flush();
+	write_unlock_bh(&selopt_perimtab_lock);
+	
+	return;
+}
+
+/*
+ * Add a new entry to the table.
+ */
+int selopt_perimtab_append(__u32 addr, __u32 mask)
+{
+	int rc;
+	struct selopt_perimtab_entry *entry;
+
+	entry = kmalloc(sizeof(struct selopt_perimtab_entry), GFP_KERNEL);
+	if (entry == NULL)
+		return -ENOMEM;
+	
+	entry->addr = addr;
+	entry->mask = mask;
+
+	write_lock_bh(&selopt_perimtab_lock);
+	rc = __selopt_perimtab_append(entry);
+	write_unlock_bh(&selopt_perimtab_lock);
+	
+	if (rc)
+		kfree(entry);
+
+	return rc;
+}
+
+/*
+ * Match an address in the table.
+ */
+int selopt_perimtab_match(__u32 addr)
+{
+	int rc;
+	
+	read_lock_bh(&selopt_perimtab_lock);
+	rc = __selopt_perimtab_match(addr);
+	read_unlock_bh(&selopt_perimtab_lock);
+	return rc;
+}
+
+/*
+ * Delete an entry from the table.
+ */
+int selopt_perimtab_delete(__u32 addr, __u32 mask)
+{
+	int rc;
+
+	write_lock_bh(&selopt_perimtab_lock);
+	rc = __selopt_perimtab_delete(addr, mask);
+	write_unlock_bh(&selopt_perimtab_lock);
+	return rc;
+}
+
+
+/*
+ * Fill an skb with a perimtab entry.  Called during a netlink dump.
+ */
+static int selopt_perimtab_fill_entry(struct sk_buff *skb,
+                                      struct selopt_perimtab_entry *entry,
+                                      int type, u32 pid, u32 seq)
+{
+	struct flmsg_perim_entry *r;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(struct flmsg_perim_entry));
+	
+	if (pid)
+		nlh->nlmsg_flags |= NLM_F_MULTI;
+
+	r = NLMSG_DATA(nlh);
+	
+	r->entry.addr = entry->addr;
+	r->entry.mask = entry->mask;
+	
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+/*
+ * Netlink callback for dumping the entire table to userspace.
+ */
+int selopt_perimtab_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx;
+	int s_idx = cb->args[0];
+	struct list_head *i;
+
+	read_lock_bh(&selopt_perimtab_lock);
+	
+	for (i = selopt_perimtab_list.next, idx = 0;
+	     i != &selopt_perimtab_list; i = i->next, idx++) {
+		struct selopt_perimtab_entry *entry =
+			(struct selopt_perimtab_entry *)i;
+		
+		if (idx < s_idx)
+			continue;
+			
+		if (selopt_perimtab_fill_entry(skb, entry, FLMSG_PERIM_GET,
+		    NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
+			break;
+	}
+	
+	read_unlock_bh(&selopt_perimtab_lock);
+	
+	cb->args[0] = idx;
+	
+	return skb->len;
+}
+
+__init int selopt_perimtab_init(void)
+{
+	return 0;
+}
+
+__exit void selopt_perimtab_exit(void)
+{
+	selopt_perimtab_flush();
+}
diff -urN kernel.orig/security/selinux/selopt/perimtab.h lsm/security/selinux/selopt/perimtab.h
--- kernel.orig/security/selinux/selopt/perimtab.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/perimtab.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,29 @@
+/* -*- linux-c -*- */
+/*
+ * Perimeter Table
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _SELOPT_PERIMTAB_H_
+#define _SELOPT_PERIMTAB_H_
+
+int selopt_perimtab_append(__u32 addr, __u32 mask);
+int selopt_perimtab_match(__u32 addr);
+int selopt_perimtab_delete(__u32 addr, __u32 mask);
+void selopt_perimtab_flush(void);
+
+int selopt_perimtab_init(void) __init;
+void selopt_perimtab_exit(void) __exit;
+
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+
+int selopt_perimtab_dump(struct sk_buff *skb, struct netlink_callback *cb);
+
+#endif	/* _SELOPT_PERIMTAB_H_ */
diff -urN kernel.orig/security/selinux/selopt/queue.c lsm/security/selinux/selopt/queue.c
--- kernel.orig/security/selinux/selopt/queue.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/queue.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,311 @@
+/* -*- linux-c -*- */
+/*
+ * Packet queue, for deferred SCMP lookups.
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/netlink.h>
+#include <linux/flask/flask_types.h>
+#include "selinux_plug.h"
+#include <linux/flask/selopt.h>
+#include "queue.h"
+
+#define SELOPT_QUEUE_NAME	"selopt_queue"
+#define SELOPT_QUEUE_MAX	1024
+
+struct selopt_queue_entry {
+	struct list_head list;
+	struct sk_buff *skb;
+	struct nf_info *info;
+	void *data;
+};
+
+typedef int (*queue_matchfn)(struct selopt_queue_entry *entry,
+                             unsigned long data);
+
+static LIST_HEAD(selopt_queue_list);
+static spinlock_t selopt_queue_lock = SPIN_LOCK_UNLOCKED;
+static volatile __u32 selopt_queue_total = 0;
+
+/*
+ * Append an entry to the queue.
+ */
+static int inline __selopt_queue_append(struct selopt_queue_entry *entry)
+{
+	if (selopt_queue_total >= SELOPT_QUEUE_MAX) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "SELinux: clamping queue "
+			       "at %u entries\n", SELOPT_QUEUE_MAX);
+		return -ENOSPC;
+	}
+	
+	list_add_tail(&entry->list, &selopt_queue_list);
+	selopt_queue_total++;
+
+	return 0;
+}
+
+/*
+ * Find and return a queued packet matched by matchfn, or the first entry if
+ * matchfn is NULL.
+ */
+static struct selopt_queue_entry *
+__selopt_queue_find(queue_matchfn matchfn, unsigned long data)
+{
+	struct list_head *i;
+	
+	for (i = selopt_queue_list.next; i != &selopt_queue_list; i = i->next) {
+		struct selopt_queue_entry *entry = (struct selopt_queue_entry *)i;
+		
+		if (!matchfn || matchfn(entry, data))
+			return entry;
+	}
+	return NULL;
+}
+
+/*
+ * Dequeue entry with verdict.
+ */
+static void __selopt_queue_dequeue(struct selopt_queue_entry *entry, int verdict)
+{
+	list_del(&entry->list);
+	selopt_queue_total--;
+	nf_reinject(entry->skb, entry->info, verdict);
+	kfree(entry);
+}
+
+/*
+ * Flush entire queue with verdict.
+ */
+static inline void __selopt_queue_flush(int verdict)
+{
+	struct selopt_queue_entry *entry;
+	
+	while ((entry = __selopt_queue_find(NULL, 0)) != NULL)
+		__selopt_queue_dequeue(entry, verdict);
+
+	return;
+}
+
+/*
+ * Add a new packet to the queue.
+ */
+static int selopt_queue_enqueue(struct sk_buff *skb,
+                                struct nf_info *info, void *data)
+{
+	int rc;
+	struct selopt_queue_entry *entry;
+	
+	entry = kmalloc(sizeof(struct selopt_queue_entry), SAFE_ALLOC);
+	if (entry == NULL) {
+		printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+		return NF_DROP;
+	}
+	
+	memset(entry, 0, sizeof(struct selopt_queue_entry));
+	
+	entry->skb = skb;
+	entry->info = info;
+	entry->data = data;
+
+	spin_lock_bh(&selopt_queue_lock);
+	rc = __selopt_queue_append(entry);
+	spin_unlock_bh(&selopt_queue_lock);
+	
+	if (rc) {
+		kfree(entry);
+		return rc;
+	}
+	
+	return 0;
+}
+
+/*
+ * Netfilter queue handler.  Packets arrive here if a Netfilter hook has
+ * returned NF_QUEUE, and must be returned to the stack via nf_reinject().
+ */
+static int selopt_queue_handler(struct sk_buff *skb,
+                                struct nf_info *info, void *data)
+{
+	return selopt_queue_enqueue(skb, info, data);
+}
+
+
+
+static inline int
+match_saddr(struct selopt_queue_entry *entry, unsigned long addr)
+{
+	return (entry->skb->nh.iph->saddr == addr);
+}
+
+static inline int
+match_dev(struct selopt_queue_entry *entry, unsigned long ifindex)
+{
+	if (entry->info->indev)
+		if (entry->info->indev->ifindex == ifindex)
+			return 1;
+			
+	if (entry->info->outdev)
+		if (entry->info->outdev->ifindex == ifindex)
+			return 1;
+			
+	return 0;
+}
+
+/*
+ * Attempt to evict queue entries for a peer, typically called after
+ * a map response has been received.
+ */
+void selopt_queue_evict(struct selopt_cache_peer *peer)
+{
+	struct list_head *i;
+	
+	spin_lock_bh(&selopt_queue_lock);
+
+	for (i = selopt_queue_list.next; i != &selopt_queue_list; ) {
+		struct selopt_queue_entry *entry = (struct selopt_queue_entry *)i;
+		struct sk_buff *skb = entry->skb;
+	
+		i = i->next;
+		
+		if (peer->addr == skb->nh.iph->saddr) {
+			struct skb_security_struct *ssec = skb->lsm_security;
+
+			selopt_cache_map_sids(peer, skb, 0);
+
+			if (selopt_mapped_all(ssec))
+				__selopt_queue_dequeue(entry, NF_ACCEPT);
+		}		
+	}
+	
+	spin_unlock_bh(&selopt_queue_lock);
+	return;
+}
+
+/*
+ * Flush all entries from the queue.
+ */
+static void selopt_queue_flush(int verdict)
+{
+	spin_lock_bh(&selopt_queue_lock);
+	__selopt_queue_flush(verdict);
+	spin_unlock_bh(&selopt_queue_lock);
+	return;
+}
+
+/*
+ * Flush entries associated with the device.
+ */
+static void selopt_queue_dev_flush(struct net_device *dev, int verdict)
+{
+	struct selopt_queue_entry *entry;
+
+	spin_lock_bh(&selopt_queue_lock);
+	
+	while ((entry = __selopt_queue_find(match_dev, dev->ifindex)) != NULL)
+		__selopt_queue_dequeue(entry, verdict);
+	
+	spin_unlock_bh(&selopt_queue_lock);
+}
+
+/*
+ * The device notifier is used to flush queue entries referencing a particular
+ * device when it goes down.
+ */
+static int selopt_queue_receive_event(struct notifier_block *this,
+                                      unsigned long event, void *ptr)
+{
+	struct net_device *dev = ptr;
+	
+	if (event == NETDEV_DOWN)
+		selopt_queue_dev_flush(dev, NF_DROP);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block selopt_queue_notifier = {
+	selopt_queue_receive_event,
+	NULL,
+	0
+};
+
+/* Temporary debugging */
+static int selopt_queue_get_info(char *buffer, char **start,
+                                 off_t offset, int length)
+{
+	int len;
+	
+	spin_lock_bh(&selopt_queue_lock);
+	
+	len = sprintf(buffer, "Queue Statistics\n"
+	              "Max entries   : %u\n"
+	              "Total entries : %u\n",
+	              SELOPT_QUEUE_MAX,
+	              selopt_queue_total);
+
+	spin_unlock_bh(&selopt_queue_lock);
+	
+	*start = buffer + offset;
+	len -= offset;
+	if (len > length)
+		len = length;
+	else if (len < 0)
+		len = 0;
+	return len;
+}
+
+__init int selopt_queue_init(void)
+{
+	int rc;
+	struct proc_dir_entry *proc;
+	
+	
+	proc = proc_net_create(SELOPT_QUEUE_NAME, 0, selopt_queue_get_info);
+	if (proc)
+		proc->owner = THIS_MODULE;
+	else {
+		printk(KERN_WARNING "SELinux: proc_net_create(%s) failed\n",
+		       SELOPT_QUEUE_NAME);
+		return -ENOMEM;
+	}
+
+	register_netdevice_notifier(&selopt_queue_notifier);
+	
+	rc = nf_register_queue_handler(PF_INET, selopt_queue_handler, NULL);
+	if (rc) {
+		printk(KERN_WARNING "SELinux: selopt queue reg. failed\n");
+		unregister_netdevice_notifier(&selopt_queue_notifier);
+		proc_net_remove(SELOPT_QUEUE_NAME);
+		return rc;
+	}
+	
+	return 0;
+}
+
+__exit void selopt_queue_exit(void)
+{
+	nf_unregister_queue_handler(PF_INET);
+	unregister_netdevice_notifier(&selopt_queue_notifier);
+	selopt_queue_flush(NF_DROP);
+	proc_net_remove(SELOPT_QUEUE_NAME);
+	return;
+}
diff -urN kernel.orig/security/selinux/selopt/queue.h lsm/security/selinux/selopt/queue.h
--- kernel.orig/security/selinux/selopt/queue.h	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/queue.h	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,23 @@
+/* -*- linux-c -*- */
+/*
+ * Packet queue, for deferred SCMP lookups.
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#ifndef _SELOPT_QUEUE_H_
+#define _SELOPT_QUEUE_H_
+
+#include "cache.h"
+
+void selopt_queue_evict(struct selopt_cache_peer *peer);
+
+int selopt_queue_init(void) __init;
+void selopt_queue_exit(void) __exit;
+
+#endif	/* _SELOPT_QUEUE_H_ */
diff -urN kernel.orig/security/selinux/selopt/selopt_core.c lsm/security/selinux/selopt/selopt_core.c
--- kernel.orig/security/selinux/selopt/selopt_core.c	Thu Jan  1 10:00:00 1970
+++ lsm/security/selinux/selopt/selopt_core.c	Wed Dec 12 19:49:07 2001
@@ -0,0 +1,607 @@
+/* -*- linux-c -*- */
+/*
+ * SELinux Labeled IP Networking via CIPSO/FIPS188 Options
+ *
+ * Copyright (c) 2001 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/netfilter.h>
+#include <linux/flask/nsid.h>
+#include <linux/flask/flask_types.h>
+#include <linux/flask/flnetlink.h>
+#include "selinux_plug.h"
+#include <linux/flask/selopt.h>
+#include "perimtab.h"
+#include "cache.h"
+#include "queue.h"
+
+/*
+ * Map remote SIDS to local SIDS via the peer cache.  If we get
+ * a cache miss or invalidation caused by policy serial number change,
+ * the packet is queued during the SCMP lookup, then reinjected.
+ */
+static unsigned int selopt_map_sids(unsigned int hooknum,
+                                    struct sk_buff **pskb,
+                                    const struct net_device *in,
+                                    const struct net_device *out,
+                                    int (*okfn)(struct sk_buff *))
+{
+	int rc = 0;
+	struct sk_buff *skb = *pskb;
+	struct iphdr *iph = skb->nh.iph;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	struct selopt_cache_peer *peer;
+
+	SDBG(ip_warn(iph, "serial=%u ssid=%u msid=%u dsid=%u bypass=%u "
+	             "opts=%x", ssec->serial, ssec->ssid, ssec->msid,
+	             ssec->dsid, selopt_get(ssec, SELOPT_BYPASS), ssec->opts));
+
+	/* Implicit mapping for local packets */
+	if (inet_addr_type(iph->saddr) == RTN_LOCAL) {
+		selopt_map_all(ssec);
+		return NF_ACCEPT;
+	}	
+
+	if (selopt_get(ssec, SELOPT_BYPASS))
+		return NF_ACCEPT;
+
+	peer = selopt_cache_peer_lookup(iph->saddr, &rc);
+	if (peer == NULL)
+		return NF_DROP;
+
+	selopt_cache_map_sids(peer, skb, rc);
+	selopt_cache_peer_put(peer);
+
+	if (selopt_mapped_all(ssec)) {
+
+		SDBG(ip_warn(iph, "mapped: serial=%u ssid=%u msid=%u dsid=%u",
+	                     ssec->serial, ssec->ssid, ssec->msid, ssec->dsid));
+
+		return NF_ACCEPT;
+	}
+	
+	/*
+	 * Slow path, send map request and queue packet.
+	 */
+	rc = selopt_cache_request_mappings(skb);
+	if (rc)
+		return NF_DROP;
+
+	return NF_QUEUE;
+}
+
+
+/*
+ * Return true if this packet needs to bypass normal labeling.  Currently, this
+ * means unfragmented SCMP packets, and may later include ISAKMP packets.
+ */
+static int selopt_bypass_output(struct sk_buff *skb)
+{
+	struct iphdr *iph = skb->nh.iph;
+	
+	if (iph->protocol == IPPROTO_UDP) {
+		struct udphdr *udph;
+		__u8 ihlen = iph->ihl * 4;
+	
+		if (iph->frag_off & __constant_htons(IP_MF|IP_OFFSET))
+			return 0;
+			
+		if ((skb->len - ihlen) < sizeof(struct udphdr))
+			return 0;
+		
+		udph = (void *)iph + ihlen;
+		if (udph->dest == __constant_htons(SCMP_PORT)
+		    || udph->source == __constant_htons(SCMP_PORT))
+			return 1;
+	}
+	return 0;
+}
+
+/* 
+ * This is critical for TCP to operate with our CIPSO options.  The effective
+ * MSS is the maximum size of segments which are transmitted, and it must
+ * take the size of the options into account
+ */
+static void selopt_adjust_effective_mss(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+	if (tp->ext_header_len < SELOPT_ALIGN(SELOPT_MAX_TCP_LEN)) {
+		tp->ext_header_len = SELOPT_ALIGN(SELOPT_MAX_TCP_LEN);
+		tcp_sync_mss(sk, tp->pmtu_cookie);
+	}
+	return;
+}
+
+/*
+ * Returns true if the address is within our security perimeter.
+ */
+static int selopt_within_perimeter(__u32 addr)
+{
+	return selopt_perimtab_match(addr);
+}
+
+/*
+ * If not a bypass label, always include serial and ssid.  The msid and
+ * dsid tags are only included if they have been explicitly specified via
+ * the extended socket API.
+ */
+static inline __u8 selopt_calculate_optlen(__u8 protocol,
+                                           struct skb_security_struct *ssec)
+{
+	__u8 len = SELOPT_BASE_LEN;
+
+	if (selopt_get(ssec, SELOPT_BYPASS))
+		len += SELOPT_BYPASS_LEN;
+	else {
+		len += SELOPT_SERIAL_LEN + SELOPT_SSID_LEN;
+		
+		if (selopt_get(ssec, SELOPT_MSID)) {
+			if (protocol != IPPROTO_TCP)
+				len += SELOPT_MSID_LEN;
+		}
+		
+		if (selopt_get(ssec, SELOPT_DSID))
+			len += SELOPT_DSID_LEN;
+	}
+	return len;
+}
+
+
+/*
+ * Fill optptr buffer with IP options.  Note that optlen is not 32-bit
+ * aligned here.
+ */
+static void selopt_options_fill(struct sk_buff *skb,
+                                unsigned char *optptr, __u8 optlen)
+{
+	__u8 i;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	struct iphdr *iph = skb->nh.iph;
+	
+	memset(optptr, IPOPT_NOOP, SELOPT_ALIGN(optlen));
+	
+	optptr[0] = IPOPT_CIPSO;
+	optptr[1] = optlen;
+	
+	*(__u32 *)&optptr[2] = __constant_htonl(SELOPT_DOI);
+	
+	i = SELOPT_BASE_LEN;
+		
+	if (selopt_get(ssec, SELOPT_BYPASS)) {
+		optptr[i] = SELOPT_BYPASS;
+		optptr[i+1] = SELOPT_BYPASS_LEN;
+		i += SELOPT_BYPASS_LEN;
+		goto out_tag;
+	}
+	
+	optptr[i] = SELOPT_SERIAL;
+	optptr[i+1] = SELOPT_SERIAL_LEN;
+	*(__u32 *)&optptr[i+2] = htonl(ssec->serial);
+	i += SELOPT_SERIAL_LEN;
+	
+	optptr[i] = SELOPT_SSID;
+	optptr[i+1] = SELOPT_SSID_LEN;
+	*(__u32 *)&optptr[i+2] = htonl(ssec->ssid);
+	i += SELOPT_SSID_LEN;
+	
+	if (selopt_get(ssec, SELOPT_MSID)) {
+		if (iph->protocol != IPPROTO_TCP) {
+			optptr[i] = SELOPT_MSID;
+			optptr[i+1] = SELOPT_MSID_LEN;
+			*(__u32 *)&optptr[i+2] = htonl(ssec->msid);
+			i += SELOPT_MSID_LEN;
+		}
+	}
+	
+	if (selopt_get(ssec, SELOPT_DSID)) {
+		optptr[i] = SELOPT_DSID;
+		optptr[i+1] = SELOPT_DSID_LEN;
+		*(__u32 *)&optptr[i+2] = htonl(ssec->dsid);
+		i += SELOPT_DSID_LEN;
+	}
+	
+out_tag:
+	optptr[6] = SELOPT_FREEFORM;
+	optptr[7] = i - SELOPT_BASE_LEN + 2;
+	return;
+}
+
+/*
+ * Add labels to outgoing packets by inserting CIPSO/FIPS188 options.
+ * Note that packets on the output hook need to be length validated.
+ */
+static unsigned int selopt_ip_label_output(unsigned int hooknum,
+                                           struct sk_buff **pskb,
+                                           const struct net_device *in,
+                                           const struct net_device *out,
+                                           int (*okfn)(struct sk_buff *))
+{
+	__u8 optlen, ihlen, *optptr;
+	__u16 datalen, totlen;
+	struct sk_buff *skb = *pskb;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	struct iphdr *iph = skb->nh.iph;
+	
+	totlen = skb->len;
+	if (totlen < sizeof(struct iphdr))
+		return NF_DROP;
+	
+	if (totlen != ntohs(iph->tot_len))
+		return NF_DROP;
+	
+	if (!selopt_within_perimeter(iph->daddr))
+		return NF_ACCEPT;
+	
+	ihlen = iph->ihl * 4;
+	if (ihlen != sizeof(struct iphdr)) {
+		ip_warn(iph, "unable to label packet with ihlen %u", ihlen);
+		return NF_DROP;
+	}
+
+	if (selopt_bypass_output(skb))
+		selopt_set(ssec, SELOPT_BYPASS);
+		
+	/* Temporary hack until policy interleave implemented. */
+	selopt_set(ssec, SELOPT_SERIAL);
+	ssec->serial = iph->saddr;
+	
+	/* SSID is always set */
+	selopt_set(ssec, SELOPT_SSID);
+	
+	/* Packets with local destination do not need CIPSO labels. */
+	if (inet_addr_type(iph->daddr) == RTN_LOCAL)
+		return NF_ACCEPT;
+
+	totlen = skb->len;
+	datalen = totlen - ihlen;
+	
+	optlen = selopt_calculate_optlen(iph->protocol, ssec);
+	totlen += SELOPT_ALIGN(optlen);
+
+	if (totlen > 0xFFFF) {
+		ip_warn(iph, "oversize packet %u\n", totlen);
+		return NF_DROP;
+	}
+	
+	if (skb_tailroom(skb) < SELOPT_ALIGN(optlen)
+	    || skb_cloned(skb) || skb_shared(skb)) {
+		struct sk_buff *newskb;
+
+		newskb = skb_copy_expand(skb, skb_headroom(skb),
+		                         SELOPT_ALIGN(optlen), SAFE_ALLOC);
+		if (!newskb) {
+			ip_warn(iph, "out of memory expanding skb\n");
+			return NF_DROP;
+		}
+		
+		if (skb->sk)
+			skb_set_owner_w(newskb, skb->sk);
+			
+		kfree_skb(skb);
+		
+		*pskb = skb = newskb;
+		iph = skb->nh.iph;
+	}
+	
+	skb_put(skb, SELOPT_ALIGN(optlen));
+	optptr = (__u8 *)iph + sizeof(struct iphdr);
+	memmove(optptr + SELOPT_ALIGN(optlen), optptr, datalen);
+	skb->h.raw += SELOPT_ALIGN(optlen);
+	
+	selopt_options_fill(skb, optptr, optlen);
+	
+	iph->ihl = (ihlen + SELOPT_ALIGN(optlen)) >> 2;
+	iph->tot_len = htons(totlen);
+	iph->check = 0;
+	ip_send_check(iph);
+	
+	return NF_ACCEPT;
+}
+
+/*
+ * Map remote SIDs on incoming packets to local SIDs.
+ */
+static unsigned int selopt_ip_map_input(unsigned int hooknum,
+                                        struct sk_buff **pskb,
+                                        const struct net_device *in,
+                                        const struct net_device *out,
+                                        int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	struct iphdr *iph = skb->nh.iph;
+	
+	if (selopt_within_perimeter(iph->saddr)) {
+		
+		if (!ssec->opts) {
+			/*
+			 * CIPSO 5.1.2: Respond with a type 12 code 1 ICMP
+			 * message with a pointer value of 134. (todo).
+			 */
+			ip_warn(iph, "unlabeled packet from within perimeter\n");
+			return NF_DROP;
+		}
+		
+		return selopt_map_sids(hooknum, pskb, in, out, okfn);
+	}
+	
+	if (ssec->opts) {
+		ip_warn(iph, "labeled packet from outside perimeter\n");
+		return NF_DROP;
+	}
+
+	return NF_ACCEPT;
+}
+
+static inline int selopt_param(struct skb_security_struct *ssec,
+                              __u8 paramtype, __u8 optlen, int i,
+                              __u8 reqlen, const char *optptr,
+                              unsigned char **pp_ptr)
+{
+	__u8 paramlen;
+	
+	if (selopt_get(ssec, paramtype)) {
+		(const unsigned char *)*pp_ptr = &optptr[i];
+		return -EINVAL;
+	}
+	
+	paramlen = optptr[i + 1];
+	if (paramlen != reqlen || paramlen > optlen - i) {
+		(const unsigned char *)*pp_ptr = &optptr[i] + 1;
+		return -EINVAL;
+	}
+
+	selopt_set(ssec, paramtype);
+	return paramlen;
+}
+	
+static inline int selopt_parse_null_param(struct skb_security_struct *ssec,
+                                          __u8 paramtype, __u8 optlen, int i,
+                                          const char *optptr,
+                                          unsigned char **pp_ptr)
+{
+
+	return selopt_param(ssec, paramtype, optlen, i,
+	                    SELOPT_NULL_LEN, optptr, pp_ptr);
+}
+
+static inline int selopt_parse_u32_param(struct skb_security_struct *ssec,
+                                         __u8 paramtype, __u8 optlen, int i,
+                                         const char *optptr,
+                                         unsigned char **pp_ptr, __u32 *param)
+{
+	int rc;
+	
+	rc = selopt_param(ssec, paramtype, optlen, i,
+	                 SELOPT_U32_LEN, optptr, pp_ptr);
+	if (rc)
+		*param = ntohl(*(__u32 *)&optptr[i + 2]);
+	return rc;
+}
+
+/*
+ * Decode IP options on incoming packets into labels.
+ *
+ * Note: we're guaranteed to have a minimum of 2 bytes at optptr, with
+ * optptr[1] being a valid length of data to access.
+ */
+static int selopt_ip_decode_options(struct sk_buff *skb,
+                                    const char *optptr,
+                                    unsigned char **pp_ptr)
+{
+	__u8 opttype = optptr[0];
+	__u8 optlen = optptr[1];
+	__u8 i, tagtype, taglen;
+	__u32 doi;
+	struct iphdr *iph;
+	struct skb_security_struct *ssec;
+
+	if (!skb)
+		return 0;
+	
+	iph = skb->nh.iph;
+	ssec = skb->lsm_security;
+	
+	/* This is the only type of security option which we accept */
+	if (opttype != IPOPT_CIPSO) {
+		(const unsigned char *)*pp_ptr = optptr;
+		return -EPERM;
+	}
+
+	if (ssec->opts) {
+		if (inet_addr_type(iph->saddr) != RTN_LOCAL) {
+			ip_warn(iph, "packet already labeled %#x\n", ssec->opts)
+			return 0;
+		}
+	}
+	
+	if (optlen < SELOPT_MIN_LEN) {
+		(const unsigned char *)*pp_ptr = optptr;
+		return -EINVAL;
+	}
+
+	doi = *(__u32 *)&optptr[2];
+	if (doi != __constant_htonl(SELOPT_DOI)) {
+		(const unsigned char *)*pp_ptr = optptr + 3;
+		return -EINVAL;
+	}
+
+	tagtype = optptr[6];
+	if (tagtype != SELOPT_FREEFORM) {
+		(const unsigned char *)*pp_ptr = optptr + 7;
+		return -EINVAL;
+	}
+
+	taglen = optptr[7];
+	if (taglen != (optlen - 6)) {
+		(const unsigned char *)*pp_ptr = optptr + 8;
+		return -EINVAL;
+	}
+
+	for (i = 8; i < optlen; ) {
+		__u8 paramtype = optptr[i];
+		int rc = 0;
+		
+		switch (paramtype) {
+		case SELOPT_BYPASS:
+			rc = selopt_parse_null_param(ssec, paramtype, optlen,
+			                             i, optptr, pp_ptr);
+			break;
+
+		case SELOPT_SERIAL:
+			rc = selopt_parse_u32_param(ssec, paramtype, optlen,
+			                            i, optptr, pp_ptr,
+			                            &ssec->serial);
+			break;
+
+		case SELOPT_SSID:
+			rc = selopt_parse_u32_param(ssec, paramtype, optlen,
+			                            i, optptr, pp_ptr,
+			                            &ssec->ssid);
+			break;
+
+		case SELOPT_MSID:
+			rc = selopt_parse_u32_param(ssec, paramtype, optlen,
+			                            i, optptr, pp_ptr,
+			                            &ssec->msid);
+			break;
+
+		case SELOPT_DSID:
+			rc = selopt_parse_u32_param(ssec, paramtype, optlen,
+			                            i, optptr, pp_ptr,
+			                            &ssec->dsid);
+			break;
+	
+		default:
+			(const unsigned char *)*pp_ptr = &optptr[i] + 1;
+			return -EINVAL;
+		}
+		
+		if (rc < 0)
+			return rc;
+		
+		i += rc;
+	}
+
+	if (i != optlen) {
+		(const unsigned char *)*pp_ptr = &optptr[2];
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Validate labels on incoming fragments as they are about to be added
+ * to a reassembly queue.
+ */
+static int selopt_ip_defragment(struct sk_buff *skb)
+{
+	struct iphdr *iph = skb->nh.iph;
+	struct skb_security_struct *ssec = skb->lsm_security;
+	
+	if (ssec->opts)
+		ip_warn(iph, "labeled fragments yet supported\n");
+	return 0;
+}
+
+/*
+ * Adjust effective MSS for outgoing TCP data segments.
+ */
+static void selopt_sock_sendmsg(struct sock *sk)
+{
+	if (sk->family == AF_INET && sk->type == SOCK_STREAM)
+		if (selopt_within_perimeter(sk->daddr))
+			selopt_adjust_effective_mss(sk);
+	return;
+}
+
+static struct nsid_operations selopt_ops =
+{
+	ip_label_output:	selopt_ip_label_output,
+	ip_map_input:		selopt_ip_map_input,
+	ip_decode_options:	selopt_ip_decode_options,
+	ip_defragment:		selopt_ip_defragment,
+	sock_sendmsg:		selopt_sock_sendmsg,
+};
+
+
+__init int selopt_init(void)
+{
+	int rc;
+	
+	rc = flnetlink_init();
+	if (rc)
+		return rc;
+	
+	rc = selopt_perimtab_init();
+	if (rc) {
+		flnetlink_exit();
+		return rc;
+	}
+	
+	rc = selopt_cache_init();
+	if (rc) {
+		selopt_perimtab_exit();
+		flnetlink_exit();
+		return rc;
+	}
+
+	rc = selopt_queue_init();
+	if (rc) {
+		selopt_cache_exit();
+		selopt_perimtab_exit();
+		flnetlink_exit();
+		return rc;
+	}
+
+	rc = nsid_register_ops(&selopt_ops);
+	if (rc) {
+		selopt_queue_exit();
+		selopt_cache_exit();
+		selopt_perimtab_exit();
+		flnetlink_exit();
+		return rc;
+	}
+	
+	printk(KERN_INFO "SELinux: CIPSO/FIPS188 IP labeling initialized\n");
+	
+	return 0;
+}
+
+__exit void selopt_exit(void)
+{
+	nsid_unregister_ops(&selopt_ops);
+	selopt_queue_exit();
+	selopt_cache_exit();
+	selopt_perimtab_exit();
+	flnetlink_exit();
+	
+	return;
+}
+
+module_init(selopt_init);
+module_exit(selopt_exit);
+
+MODULE_DESCRIPTION("SELinux CIPSO/FIPS188 IP Labeling");
+MODULE_LICENSE("GPL");
+
+
