#define SMK_RECEIVING  1
 #define SMK_SENDING    2
 
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_PORT_LABELING
 LIST_HEAD(smk_ipv6_port_list);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
 static struct kmem_cache *smack_inode_cache;
 int smack_enabled;
 
 }
 
 /**
-* smack_host_label - check host based restrictions
+* smack_ipv4host_label - check host based restrictions
 * @sip: the object end
 *
 * looks for host based access restrictions
 *
 * Returns the label of the far end or NULL if it's not special.
 */
-static struct smack_known *smack_host_label(struct sockaddr_in *sip)
+static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
 {
-       struct smk_netlbladdr *snp;
+       struct smk_net4addr *snp;
        struct in_addr *siap = &sip->sin_addr;
 
        if (siap->s_addr == 0)
                return NULL;
 
-       list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list)
+       list_for_each_entry_rcu(snp, &smk_net4addr_list, list)
+               /*
+                * we break after finding the first match because
+                * the list is sorted from longest to shortest mask
+                * so we have found the most specific match
+                */
+               if (snp->smk_host.s_addr ==
+                   (siap->s_addr & snp->smk_mask.s_addr))
+                       return snp->smk_label;
+
+       return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * smk_ipv6_localhost - Check for local ipv6 host address
+ * @sip: the address
+ *
+ * Returns boolean true if this is the localhost address
+ */
+static bool smk_ipv6_localhost(struct sockaddr_in6 *sip)
+{
+       __be16 *be16p = (__be16 *)&sip->sin6_addr;
+       __be32 *be32p = (__be32 *)&sip->sin6_addr;
+
+       if (be32p[0] == 0 && be32p[1] == 0 && be32p[2] == 0 && be16p[6] == 0 &&
+           ntohs(be16p[7]) == 1)
+               return true;
+       return false;
+}
+
+/**
+* smack_ipv6host_label - check host based restrictions
+* @sip: the object end
+*
+* looks for host based access restrictions
+*
+* This version will only be appropriate for really small sets of single label
+* hosts.  The caller is responsible for ensuring that the RCU read lock is
+* taken before calling this function.
+*
+* Returns the label of the far end or NULL if it's not special.
+*/
+static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
+{
+       struct smk_net6addr *snp;
+       struct in6_addr *sap = &sip->sin6_addr;
+       int i;
+       int found = 0;
+
+       /*
+        * It's local. Don't look for a host label.
+        */
+       if (smk_ipv6_localhost(sip))
+               return NULL;
+
+       list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
                /*
                * we break after finding the first match because
                * the list is sorted from longest to shortest mask
                * so we have found the most specific match
                */
-               if ((&snp->smk_host.sin_addr)->s_addr ==
-                   (siap->s_addr & (&snp->smk_mask)->s_addr)) {
-                       /* we have found the special CIPSO option */
-                       if (snp->smk_label == &smack_cipso_option)
-                               return NULL;
-                       return snp->smk_label;
+               for (found = 1, i = 0; i < 8; i++) {
+                       /*
+                        * If the label is NULL the entry has
+                        * been renounced. Ignore it.
+                        */
+                       if (snp->smk_label == NULL)
+                               continue;
+                       if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
+                           snp->smk_host.s6_addr16[i]) {
+                               found = 0;
+                               break;
+                       }
                }
+               if (found)
+                       return snp->smk_label;
+       }
 
        return NULL;
 }
+#endif /* CONFIG_IPV6 */
 
 /**
  * smack_netlabel - Set the secattr on a socket
        struct smk_audit_info ad;
 
        rcu_read_lock();
-       hkp = smack_host_label(sap);
+       hkp = smack_ipv4host_label(sap);
        if (hkp != NULL) {
 #ifdef CONFIG_AUDIT
                struct lsm_network_audit net;
        return smack_netlabel(sk, sk_lbl);
 }
 
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * smk_ipv6_check - check Smack access
+ * @subject: subject Smack label
+ * @object: object Smack label
+ * @address: address
+ * @act: the action being taken
+ *
+ * Check an IPv6 access
+ */
+static int smk_ipv6_check(struct smack_known *subject,
+                               struct smack_known *object,
+                               struct sockaddr_in6 *address, int act)
+{
+#ifdef CONFIG_AUDIT
+       struct lsm_network_audit net;
+#endif
+       struct smk_audit_info ad;
+       int rc;
+
+#ifdef CONFIG_AUDIT
+       smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
+       ad.a.u.net->family = PF_INET6;
+       ad.a.u.net->dport = ntohs(address->sin6_port);
+       if (act == SMK_RECEIVING)
+               ad.a.u.net->v6info.saddr = address->sin6_addr;
+       else
+               ad.a.u.net->v6info.daddr = address->sin6_addr;
+#endif
+       rc = smk_access(subject, object, MAY_WRITE, &ad);
+       rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc);
+       return rc;
+}
+#endif /* CONFIG_IPV6 */
+
+#ifdef SMACK_IPV6_PORT_LABELING
 /**
  * smk_ipv6_port_label - Smack port access table management
  * @sock: socket
 static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
                                int act)
 {
-       __be16 *bep;
-       __be32 *be32p;
        struct smk_port_label *spp;
        struct socket_smack *ssp = sk->sk_security;
-       struct smack_known *skp;
-       unsigned short port = 0;
+       struct smack_known *skp = NULL;
+       unsigned short port;
        struct smack_known *object;
-       struct smk_audit_info ad;
-       int rc;
-#ifdef CONFIG_AUDIT
-       struct lsm_network_audit net;
-#endif
 
        if (act == SMK_RECEIVING) {
-               skp = smack_net_ambient;
+               skp = smack_ipv6host_label(address);
                object = ssp->smk_in;
        } else {
                skp = ssp->smk_out;
-               object = smack_net_ambient;
+               object = smack_ipv6host_label(address);
        }
 
        /*
-        * Get the IP address and port from the address.
+        * The other end is a single label host.
         */
-       port = ntohs(address->sin6_port);
-       bep = (__be16 *)(&address->sin6_addr);
-       be32p = (__be32 *)(&address->sin6_addr);
+       if (skp != NULL && object != NULL)
+               return smk_ipv6_check(skp, object, address, act);
+       if (skp == NULL)
+               skp = smack_net_ambient;
+       if (object == NULL)
+               object = smack_net_ambient;
 
        /*
         * It's remote, so port lookup does no good.
         */
-       if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1)
-               goto auditout;
+       if (!smk_ipv6_localhost(address))
+               return smk_ipv6_check(skp, object, address, act);
 
        /*
         * It's local so the send check has to have passed.
         */
-       if (act == SMK_RECEIVING) {
-               skp = &smack_known_web;
-               goto auditout;
-       }
+       if (act == SMK_RECEIVING)
+               return 0;
 
+       port = ntohs(address->sin6_port);
        list_for_each_entry(spp, &smk_ipv6_port_list, list) {
                if (spp->smk_port != port)
                        continue;
                break;
        }
 
-auditout:
-
-#ifdef CONFIG_AUDIT
-       smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
-       ad.a.u.net->family = sk->sk_family;
-       ad.a.u.net->dport = port;
-       if (act == SMK_RECEIVING)
-               ad.a.u.net->v6info.saddr = address->sin6_addr;
-       else
-               ad.a.u.net->v6info.daddr = address->sin6_addr;
-#endif
-       rc = smk_access(skp, object, MAY_WRITE, &ad);
-       rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
-       return rc;
+       return smk_ipv6_check(skp, object, address, act);
 }
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
 
 /**
  * smack_inode_setsecurity - set smack xattrs
        } else
                return -EOPNOTSUPP;
 
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_PORT_LABELING
        if (sock->sk->sk_family == PF_INET6)
                smk_ipv6_port_label(sock, NULL);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
 
        return 0;
 }
        return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
 }
 
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_PORT_LABELING
 /**
  * smack_socket_bind - record port binding information.
  * @sock: the socket
 static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
                                int addrlen)
 {
-#if IS_ENABLED(CONFIG_IPV6)
        if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
                smk_ipv6_port_label(sock, address);
-#endif
-
        return 0;
 }
-#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
 
 /**
  * smack_socket_connect - connect access check
                                int addrlen)
 {
        int rc = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+       struct smack_known *rsp;
+       struct socket_smack *ssp = sock->sk->sk_security;
+#endif
 
        if (sock->sk == NULL)
                return 0;
        case PF_INET6:
                if (addrlen < sizeof(struct sockaddr_in6))
                        return -EINVAL;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
-               rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap,
+#ifdef SMACK_IPV6_SECMARK_LABELING
+               rsp = smack_ipv6host_label(sip);
+               if (rsp != NULL)
+                       rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
                                                SMK_CONNECTING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+               rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
+#endif
                break;
        }
        return rc;
                                int size)
 {
        struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
        struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+       struct socket_smack *ssp = sock->sk->sk_security;
+       struct smack_known *rsp;
+#endif
        int rc = 0;
 
        /*
                rc = smack_netlabel_send(sock->sk, sip);
                break;
        case AF_INET6:
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_SECMARK_LABELING
+               rsp = smack_ipv6host_label(sap);
+               if (rsp != NULL)
+                       rc = smk_ipv6_check(ssp->smk_out, rsp, sap,
+                                               SMK_CONNECTING);
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
                rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
                break;
        }
        return rc;
                proto = smk_skb_to_addr_ipv6(skb, &sadd);
                if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
                        break;
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
                if (skb && skb->secmark != 0)
                        skp = smack_from_secid(skb->secmark);
                else
+                       skp = smack_ipv6host_label(&sadd);
+               if (skp == NULL)
                        skp = smack_net_ambient;
 #ifdef CONFIG_AUDIT
                smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
                rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
                rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
                                        MAY_WRITE, rc);
-#else /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_SECMARK_LABELING */
+#ifdef SMACK_IPV6_PORT_LABELING
                rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
                break;
 #endif /* CONFIG_IPV6 */
        }
                }
                netlbl_secattr_destroy(&secattr);
                break;
-#if IS_ENABLED(CONFIG_IPV6)
        case PF_INET6:
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
                s = skb->secmark;
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
                break;
-#endif /* CONFIG_IPV6 */
        }
        *secid = s;
        if (s == 0)
        hdr = ip_hdr(skb);
        addr.sin_addr.s_addr = hdr->saddr;
        rcu_read_lock();
-       hskp = smack_host_label(&addr);
+       hskp = smack_ipv4host_label(&addr);
        rcu_read_unlock();
 
        if (hskp == NULL)
        LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
 
        LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_PORT_LABELING
        LSM_HOOK_INIT(socket_bind, smack_socket_bind),
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
        LSM_HOOK_INIT(socket_connect, smack_socket_connect),
        LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),
        LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb),
                return -ENOMEM;
        }
 
-       printk(KERN_INFO "Smack:  Initializing.\n");
+       pr_info("Smack:  Initializing.\n");
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+       pr_info("Smack:  Netfilter enabled.\n");
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+       pr_info("Smack:  IPv6 port labeling enabled.\n");
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+       pr_info("Smack:  IPv6 Netfilter enabled.\n");
+#endif
 
        /*
         * Set the security state for the initial task.
 
 #include <linux/magic.h>
 #include "smack.h"
 
+#define BEBITS (sizeof(__be32) * 8)
 /*
  * smackfs pseudo filesystem.
  */
        SMK_DOI         = 5,    /* CIPSO DOI */
        SMK_DIRECT      = 6,    /* CIPSO level indicating direct label */
        SMK_AMBIENT     = 7,    /* internet ambient label */
-       SMK_NETLBLADDR  = 8,    /* single label hosts */
+       SMK_NET4ADDR    = 8,    /* single label hosts */
        SMK_ONLYCAP     = 9,    /* the only "capable" label */
        SMK_LOGGING     = 10,   /* logging */
        SMK_LOAD_SELF   = 11,   /* task specific rules */
 #ifdef CONFIG_SECURITY_SMACK_BRINGUP
        SMK_UNCONFINED  = 22,   /* define an unconfined label */
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+       SMK_NET6ADDR    = 23,   /* single label IPv6 hosts */
+#endif /* CONFIG_IPV6 */
 };
 
 /*
  */
 static DEFINE_MUTEX(smack_cipso_lock);
 static DEFINE_MUTEX(smack_ambient_lock);
-static DEFINE_MUTEX(smk_netlbladdr_lock);
+static DEFINE_MUTEX(smk_net4addr_lock);
+#if IS_ENABLED(CONFIG_IPV6)
+static DEFINE_MUTEX(smk_net6addr_lock);
+#endif /* CONFIG_IPV6 */
 
 /*
  * This is the "ambient" label for network traffic.
  * can write to the specified label.
  */
 
-LIST_HEAD(smk_netlbladdr_list);
+LIST_HEAD(smk_net4addr_list);
+#if IS_ENABLED(CONFIG_IPV6)
+LIST_HEAD(smk_net6addr_list);
+#endif /* CONFIG_IPV6 */
 
 /*
  * Rule lists are maintained for each label.
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 
-struct smack_known smack_cipso_option = {
-       .smk_known      = SMACK_CIPSO_OPTION,
-       .smk_secid      = 0,
-};
-
 /*
  * Values for parsing cipso rules
  * SMK_DIGITLEN: Length of a digit field in a rule.
  * Seq_file read operations for /smack/netlabel
  */
 
-static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
+static void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
 {
-       return smk_seq_start(s, pos, &smk_netlbladdr_list);
+       return smk_seq_start(s, pos, &smk_net4addr_list);
 }
 
-static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       return smk_seq_next(s, v, pos, &smk_netlbladdr_list);
+       return smk_seq_next(s, v, pos, &smk_net4addr_list);
 }
-#define BEBITS (sizeof(__be32) * 8)
 
 /*
  * Print host/label pairs
  */
-static int netlbladdr_seq_show(struct seq_file *s, void *v)
+static int net4addr_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
-       struct smk_netlbladdr *skp =
-                       list_entry_rcu(list, struct smk_netlbladdr, list);
-       unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
-       int maskn;
-       u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
-
-       for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
+       struct smk_net4addr *skp =
+                       list_entry_rcu(list, struct smk_net4addr, list);
+       char *kp = SMACK_CIPSO_OPTION;
 
-       seq_printf(s, "%u.%u.%u.%u/%d %s\n",
-               hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known);
+       if (skp->smk_label != NULL)
+               kp = skp->smk_label->smk_known;
+       seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
+                       skp->smk_masks, kp);
 
        return 0;
 }
 
-static const struct seq_operations netlbladdr_seq_ops = {
-       .start = netlbladdr_seq_start,
-       .next  = netlbladdr_seq_next,
-       .show  = netlbladdr_seq_show,
+static const struct seq_operations net4addr_seq_ops = {
+       .start = net4addr_seq_start,
+       .next  = net4addr_seq_next,
+       .show  = net4addr_seq_show,
        .stop  = smk_seq_stop,
 };
 
 /**
- * smk_open_netlbladdr - open() for /smack/netlabel
+ * smk_open_net4addr - open() for /smack/netlabel
  * @inode: inode structure representing file
  * @file: "netlabel" file pointer
  *
- * Connect our netlbladdr_seq_* operations with /smack/netlabel
+ * Connect our net4addr_seq_* operations with /smack/netlabel
  * file_operations
  */
-static int smk_open_netlbladdr(struct inode *inode, struct file *file)
+static int smk_open_net4addr(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &netlbladdr_seq_ops);
+       return seq_open(file, &net4addr_seq_ops);
 }
 
 /**
- * smk_netlbladdr_insert
+ * smk_net4addr_insert
  * @new : netlabel to insert
  *
- * This helper insert netlabel in the smack_netlbladdrs list
+ * This helper insert netlabel in the smack_net4addrs list
  * sorted by netmask length (longest to smallest)
- * locked by &smk_netlbladdr_lock in smk_write_netlbladdr
+ * locked by &smk_net4addr_lock in smk_write_net4addr
  *
  */
-static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
+static void smk_net4addr_insert(struct smk_net4addr *new)
 {
-       struct smk_netlbladdr *m, *m_next;
+       struct smk_net4addr *m;
+       struct smk_net4addr *m_next;
 
-       if (list_empty(&smk_netlbladdr_list)) {
-               list_add_rcu(&new->list, &smk_netlbladdr_list);
+       if (list_empty(&smk_net4addr_list)) {
+               list_add_rcu(&new->list, &smk_net4addr_list);
                return;
        }
 
-       m = list_entry_rcu(smk_netlbladdr_list.next,
-                          struct smk_netlbladdr, list);
+       m = list_entry_rcu(smk_net4addr_list.next,
+                          struct smk_net4addr, list);
 
        /* the comparison '>' is a bit hacky, but works */
-       if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
-               list_add_rcu(&new->list, &smk_netlbladdr_list);
+       if (new->smk_masks > m->smk_masks) {
+               list_add_rcu(&new->list, &smk_net4addr_list);
                return;
        }
 
-       list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) {
-               if (list_is_last(&m->list, &smk_netlbladdr_list)) {
+       list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
+               if (list_is_last(&m->list, &smk_net4addr_list)) {
                        list_add_rcu(&new->list, &m->list);
                        return;
                }
                m_next = list_entry_rcu(m->list.next,
-                                       struct smk_netlbladdr, list);
-               if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) {
+                                       struct smk_net4addr, list);
+               if (new->smk_masks > m_next->smk_masks) {
                        list_add_rcu(&new->list, &m->list);
                        return;
                }
 
 
 /**
- * smk_write_netlbladdr - write() for /smack/netlabel
+ * smk_write_net4addr - write() for /smack/netlabel
  * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
  *
- * Accepts only one netlbladdr per write call.
+ * Accepts only one net4addr per write call.
  * Returns number of bytes written or error code, as appropriate
  */
-static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
+static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
                                size_t count, loff_t *ppos)
 {
-       struct smk_netlbladdr *snp;
+       struct smk_net4addr *snp;
        struct sockaddr_in newname;
        char *smack;
-       struct smack_known *skp;
+       struct smack_known *skp = NULL;
        char *data;
        char *host = (char *)&newname.sin_addr.s_addr;
        int rc;
        struct netlbl_audit audit_info;
        struct in_addr mask;
        unsigned int m;
+       unsigned int masks;
        int found;
        u32 mask_bits = (1<<31);
        __be32 nsa;
        data[count] = '\0';
 
        rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
-               &host[0], &host[1], &host[2], &host[3], &m, smack);
+               &host[0], &host[1], &host[2], &host[3], &masks, smack);
        if (rc != 6) {
                rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
                        &host[0], &host[1], &host[2], &host[3], smack);
                        goto free_out;
                }
                m = BEBITS;
+               masks = 32;
        }
-       if (m > BEBITS) {
+       if (masks > BEBITS) {
                rc = -EINVAL;
                goto free_out;
        }
                        goto free_out;
                }
        } else {
-               /* check known options */
-               if (strcmp(smack, smack_cipso_option.smk_known) == 0)
-                       skp = &smack_cipso_option;
-               else {
+               /*
+                * Only the -CIPSO option is supported for IPv4
+                */
+               if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
                        rc = -EINVAL;
                        goto free_out;
                }
        }
 
-       for (temp_mask = 0; m > 0; m--) {
+       for (m = masks, temp_mask = 0; m > 0; m--) {
                temp_mask |= mask_bits;
                mask_bits >>= 1;
        }
         * Only allow one writer at a time. Writes should be
         * quite rare and small in any case.
         */
-       mutex_lock(&smk_netlbladdr_lock);
+       mutex_lock(&smk_net4addr_lock);
 
        nsa = newname.sin_addr.s_addr;
        /* try to find if the prefix is already in the list */
        found = 0;
-       list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
-               if (snp->smk_host.sin_addr.s_addr == nsa &&
-                   snp->smk_mask.s_addr == mask.s_addr) {
+       list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
+               if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
                        found = 1;
                        break;
                }
                        rc = -ENOMEM;
                else {
                        rc = 0;
-                       snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
+                       snp->smk_host.s_addr = newname.sin_addr.s_addr;
                        snp->smk_mask.s_addr = mask.s_addr;
                        snp->smk_label = skp;
-                       smk_netlbladdr_insert(snp);
+                       snp->smk_masks = masks;
+                       smk_net4addr_insert(snp);
                }
        } else {
-               /* we delete the unlabeled entry, only if the previous label
-                * wasn't the special CIPSO option */
-               if (snp->smk_label != &smack_cipso_option)
+               /*
+                * Delete the unlabeled entry, only if the previous label
+                * wasn't the special CIPSO option
+                */
+               if (snp->smk_label != NULL)
                        rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
-                                       &snp->smk_host.sin_addr, &snp->smk_mask,
+                                       &snp->smk_host, &snp->smk_mask,
                                        PF_INET, &audit_info);
                else
                        rc = 0;
         * this host so that incoming packets get labeled.
         * but only if we didn't get the special CIPSO option
         */
-       if (rc == 0 && skp != &smack_cipso_option)
+       if (rc == 0 && skp != NULL)
                rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
-                       &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET,
+                       &snp->smk_host, &snp->smk_mask, PF_INET,
                        snp->smk_label->smk_secid, &audit_info);
 
        if (rc == 0)
                rc = count;
 
-       mutex_unlock(&smk_netlbladdr_lock);
+       mutex_unlock(&smk_net4addr_lock);
+
+free_out:
+       kfree(smack);
+free_data_out:
+       kfree(data);
+
+       return rc;
+}
+
+static const struct file_operations smk_net4addr_ops = {
+       .open           = smk_open_net4addr,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .write          = smk_write_net4addr,
+       .release        = seq_release,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * Seq_file read operations for /smack/netlabel6
+ */
+
+static void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
+{
+       return smk_seq_start(s, pos, &smk_net6addr_list);
+}
+
+static void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       return smk_seq_next(s, v, pos, &smk_net6addr_list);
+}
+
+/*
+ * Print host/label pairs
+ */
+static int net6addr_seq_show(struct seq_file *s, void *v)
+{
+       struct list_head *list = v;
+       struct smk_net6addr *skp =
+                        list_entry(list, struct smk_net6addr, list);
+
+       if (skp->smk_label != NULL)
+               seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
+                               skp->smk_label->smk_known);
+
+       return 0;
+}
+
+static const struct seq_operations net6addr_seq_ops = {
+       .start = net6addr_seq_start,
+       .next  = net6addr_seq_next,
+       .show  = net6addr_seq_show,
+       .stop  = smk_seq_stop,
+};
+
+/**
+ * smk_open_net6addr - open() for /smack/netlabel
+ * @inode: inode structure representing file
+ * @file: "netlabel" file pointer
+ *
+ * Connect our net6addr_seq_* operations with /smack/netlabel
+ * file_operations
+ */
+static int smk_open_net6addr(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &net6addr_seq_ops);
+}
+
+/**
+ * smk_net6addr_insert
+ * @new : entry to insert
+ *
+ * This inserts an entry in the smack_net6addrs list
+ * sorted by netmask length (longest to smallest)
+ * locked by &smk_net6addr_lock in smk_write_net6addr
+ *
+ */
+static void smk_net6addr_insert(struct smk_net6addr *new)
+{
+       struct smk_net6addr *m_next;
+       struct smk_net6addr *m;
+
+       if (list_empty(&smk_net6addr_list)) {
+               list_add_rcu(&new->list, &smk_net6addr_list);
+               return;
+       }
+
+       m = list_entry_rcu(smk_net6addr_list.next,
+                          struct smk_net6addr, list);
+
+       if (new->smk_masks > m->smk_masks) {
+               list_add_rcu(&new->list, &smk_net6addr_list);
+               return;
+       }
+
+       list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
+               if (list_is_last(&m->list, &smk_net6addr_list)) {
+                       list_add_rcu(&new->list, &m->list);
+                       return;
+               }
+               m_next = list_entry_rcu(m->list.next,
+                                       struct smk_net6addr, list);
+               if (new->smk_masks > m_next->smk_masks) {
+                       list_add_rcu(&new->list, &m->list);
+                       return;
+               }
+       }
+}
+
+
+/**
+ * smk_write_net6addr - write() for /smack/netlabel
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one net6addr per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       struct smk_net6addr *snp;
+       struct in6_addr newname;
+       struct in6_addr fullmask;
+       struct smack_known *skp = NULL;
+       char *smack;
+       char *data;
+       int rc = 0;
+       int found = 0;
+       int i;
+       unsigned int scanned[8];
+       unsigned int m;
+       unsigned int mask = 128;
+
+       /*
+        * Must have privilege.
+        * No partial writes.
+        * Enough data must be present.
+        * "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
+        * "<addr, as a:b:c:d:e:f:g:h><space><label>"
+        */
+       if (!smack_privileged(CAP_MAC_ADMIN))
+               return -EPERM;
+       if (*ppos != 0)
+               return -EINVAL;
+       if (count < SMK_NETLBLADDRMIN)
+               return -EINVAL;
+
+       data = kzalloc(count + 1, GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(data, buf, count) != 0) {
+               rc = -EFAULT;
+               goto free_data_out;
+       }
+
+       smack = kzalloc(count + 1, GFP_KERNEL);
+       if (smack == NULL) {
+               rc = -ENOMEM;
+               goto free_data_out;
+       }
+
+       data[count] = '\0';
+
+       i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
+                       &scanned[0], &scanned[1], &scanned[2], &scanned[3],
+                       &scanned[4], &scanned[5], &scanned[6], &scanned[7],
+                       &mask, smack);
+       if (i != 10) {
+               i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
+                               &scanned[0], &scanned[1], &scanned[2],
+                               &scanned[3], &scanned[4], &scanned[5],
+                               &scanned[6], &scanned[7], smack);
+               if (i != 9) {
+                       rc = -EINVAL;
+                       goto free_out;
+               }
+       }
+       if (mask > 128) {
+               rc = -EINVAL;
+               goto free_out;
+       }
+       for (i = 0; i < 8; i++) {
+               if (scanned[i] > 0xffff) {
+                       rc = -EINVAL;
+                       goto free_out;
+               }
+               newname.s6_addr16[i] = htons(scanned[i]);
+       }
+
+       /*
+        * If smack begins with '-', it is an option, don't import it
+        */
+       if (smack[0] != '-') {
+               skp = smk_import_entry(smack, 0);
+               if (skp == NULL) {
+                       rc = -EINVAL;
+                       goto free_out;
+               }
+       } else {
+               /*
+                * Only -DELETE is supported for IPv6
+                */
+               if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
+                       rc = -EINVAL;
+                       goto free_out;
+               }
+       }
+
+       for (i = 0, m = mask; i < 8; i++) {
+               if (m >= 16) {
+                       fullmask.s6_addr16[i] = 0xffff;
+                       m -= 16;
+               } else if (m > 0) {
+                       fullmask.s6_addr16[i] = (1 << m) - 1;
+                       m = 0;
+               } else
+                       fullmask.s6_addr16[i] = 0;
+               newname.s6_addr16[i] &= fullmask.s6_addr16[i];
+       }
+
+       /*
+        * Only allow one writer at a time. Writes should be
+        * quite rare and small in any case.
+        */
+       mutex_lock(&smk_net6addr_lock);
+       /*
+        * Try to find the prefix in the list
+        */
+       list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
+               if (mask != snp->smk_masks)
+                       continue;
+               for (found = 1, i = 0; i < 8; i++) {
+                       if (newname.s6_addr16[i] !=
+                           snp->smk_host.s6_addr16[i]) {
+                               found = 0;
+                               break;
+                       }
+               }
+               if (found == 1)
+                       break;
+       }
+       if (found == 0) {
+               snp = kzalloc(sizeof(*snp), GFP_KERNEL);
+               if (snp == NULL)
+                       rc = -ENOMEM;
+               else {
+                       snp->smk_host = newname;
+                       snp->smk_mask = fullmask;
+                       snp->smk_masks = mask;
+                       snp->smk_label = skp;
+                       smk_net6addr_insert(snp);
+               }
+       } else {
+               snp->smk_label = skp;
+       }
+
+       if (rc == 0)
+               rc = count;
+
+       mutex_unlock(&smk_net6addr_lock);
 
 free_out:
        kfree(smack);
        return rc;
 }
 
-static const struct file_operations smk_netlbladdr_ops = {
-       .open           = smk_open_netlbladdr,
+static const struct file_operations smk_net6addr_ops = {
+       .open           = smk_open_net6addr,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .write          = smk_write_netlbladdr,
+       .write          = smk_write_net6addr,
        .release        = seq_release,
 };
+#endif /* CONFIG_IPV6 */
 
 /**
  * smk_read_doi - read() for /smack/doi
                        "direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
                [SMK_AMBIENT] = {
                        "ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
-               [SMK_NETLBLADDR] = {
-                       "netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
+               [SMK_NET4ADDR] = {
+                       "netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
                [SMK_ONLYCAP] = {
                        "onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
                [SMK_LOGGING] = {
                [SMK_UNCONFINED] = {
                        "unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+               [SMK_NET6ADDR] = {
+                       "ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
+#endif /* CONFIG_IPV6 */
                /* last one */
                        {""}
        };